Looking at some .net remoting code recently reminded me of just how confusing and frustrating using it can be.
We were trying to diagnose why remoted events hooked up to a remoted object were failing after the standard 5 minutes initial lease time despite the fact that the remoted object had an overridden InitializeLifetimeService method that returned null - the standard way to setup an infinite lease on a remoted object.
Using debug tracing and explicit lease management with a sponsor class, we could prove that the remoted object was not dying, but nevertheless the events that it was firing seemed to be disconnected after the initial 5 minutes and we were getting a standard remoting infrastructure exception stating that the remote object didn't exist or was disconnected. How could it be that the remote object had an infinite lease but the events it was firing didn't?
The answer, as always with remoting, lay in understanding the hoops that the remoting infrastructure goes through in supporting cross-domain marshaling of calls, i.e. how it manages to make each side of the remoting relationship behave like it is communicating with a local instance of what is to either side, the remote object.
The curious thing is that even when a object that is remotable (i.e. derives from MarshalByRefObject) is started by a client that is not itself remotable, if the remoted object calls back to the client (as it must in order to fire an event back), then the remoted object must do so via the remoting infrastructure by communicating with a transparent proxy/real proxy local to itself which then forward the call via remoting back to the client.
In other words the client and server roles are reversed for this callback and there is effectively a remoted instance at the client to receive the call and it was this that was being created with the default 5 minute initial lease time.
This of course begs a couple of questions - firstly: what is being created on the client side as the receiver of the events? It can't be the client itself because it already exists, stays alive after the 5 minutes are up and doesn't derive from MarshalByRefObject and so cannot have a leased lifetime anyway. Secondly: how could we avoid whatever it was from dying after its 5 minute lease expired?
As it turned out the second question was easier to answer than the first, albeit with a somewhat inelegant solution.
A simple call to set the LifetimeServices.LeaseTime static property to a very large figure (say 1000 days) within the client app's initialization, sets the initial lease time for any MarshalByRefObject subsequently created in its AppDomain (including the dynamically created proxy that was previously timing out at the default lease time of 5 minutes) to what is effectively an infinite lease (unless you want to go 27 years without shutting the client app down!)
Of course a more elegant solution would be to actually have created our own client side MarshalByRefObject derived class to serve up the events to the client app and then to have overriden the InitializeLifetimeService method to return null as we do on the server side, but that really wasn't necessary as the only remoted functionality on the client end was the servicing of these events and we could safety define a long default lease for all remoted objects created in the AppDomain and achieve the same effect without another layer of indirection and the effort of creating and testing more code.
As for the answer to the first question...that's more difficult to answer; from debugging, it is clear that there is a TransparentProxy created on the server side which is used to fire events back to the client side, but what it is connected to, through the Remoting framework, down at the client side is difficult to establish (although I must admit I didn't spend too much time trying, after establishing the easy fix solution) - all the client is attached to, is an event that may or may not fire depending on whether it is connected to a valid source or not.
Perhaps someone more familiar with the Remoting framework knows more about the mechanics that go on under the hood when "doing events" on remotable objects and how non-remotable objects can effectively work as remote servers when the remote objects they create need to fire events back to them; it would be interesting to know...