Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

.NET Remoting Spied On

0.00/5 (No votes)
5 Feb 2001 3  
Microsoft's RemSpy sample's implementation has been explained and more remoting explanations.
  • Download demo project - 32 Kb
  • This article is a modest attempt to share the experience that I gained while going through Microsoft’s article on Remoting and the documentation that has been provided with .NET SDK Beta 1. As I went along on my quest, I found some interesting stuff on how Remoting works. I can say that things have now started to make some sense. Then I looked at some of the samples that have been provided with the SDK. The samples helped in implementing some basic stuff like creating remote server objects and having a client access them. Interestingly, there is a sample, RemSpy, that is supposed to spy on the calls that are made on a remote object. Looking at the code for RemSpy did not help make sense of things. I ended up with a lot of questions. What is this object? Why is this derived from that interface? why do I have to register some of the objects with Remoting services? So I had to go down into the bowls of the Remoting architecture. It was an interesting and worthwhile experience. Now I have some understanding of how the calls get moved from the client to the server and then back. In this article I will not go into too much detail about the basics. There are some nice articles by Microsoft that you can refer too for that.

    Where To Look For The Basics

    • Microsoft .NET Remoting: A Technical Overview
    • .NET SDK Documentation
    • Remoting Samples

    Basics

    Before I explain how this spy sample works, I will briefly explain how the Remoting of calls work. The basic building block of communication between two applications is the message object defined by the IMessage interface. In a very simple way this communication can be described as follows:

    1. Application A creates a message envelope and puts all the information in that message.
    2. Then application A hands over the message envelope to a carrier.
    3. The carrier takes the message envelope over to application B.
    4. Application B opens the envelope, reads the message and takes the necessary action described by the message.
    5. If Application A is expecting a reply, then application B puts that information or reply in the message envelope.
    6. Application B hands over the envelop to the carrier.
    7. Finally, the carrier delivers the message back to application A.

    Looking at the above flow, if we can intercept various calls, then we can see inside the message envelopes and other related tasks.

    How To Intercept The Calls

    When a client creates a remote object, it is handed over an actual instance or a proxy depending on the AppDomain the client is running in. If the client is running in an AppDomain different than the server, it will be handed over a proxy. Now, when the client makes a method call on the proxy, all the information is packed in a message object, IMessage. Then it is passed over to a message sink. The message sink uses the channel to transport the message from the source to the destination. When the server has to return the results, it does follows the same proces. The out parameters or results are put in the message and sent to the message sink. Then the sink uses the channel to propagate the results back to the source, i.e. the client. The whole mechanism is based on sending a message from one message sink to another. Each message sink completes its work and send the message to the next sink. If we can insert a sink in this chain of sinks, we can intercept the calls and see what is being passed around at various stages of an operation.

    .NET Remoting provides a couple of interfaces that can be used and extended to monitor the calls to and from objects. Dynamic Properties and Dynamic Sinks are the two main interfaces that help in intercepting the calls. What a server needs to do is register a Dynamic Property. The Dynamic Property will implement the IDynamicProperty and IContributeDynamicSink interfaces.

    public class NKRemoteServerDynamicProperty : IDynamicProperty, IContributeDynamicSink
    .
    .
    dynamicProp = new NKRemoteServerDynamicProperty ();
    Context.RegisterDynamicProperty (dynamicProp, null, null);

    The first parameter to the RegisterDynamicProperty call is the DynamicProperty that will be registered with the framework. The second parameter is the object for which the property is registered. The last parameter is the context for which the property is registered. Look in the documentation for more details on this call.

    The IContributeDynamicSink interface has only one method, GetDynamicSink. This method is supposed to return the sink that will be notified of Remoting calls made on the object. Therefore, in this method we will create a sink and return it to the framework.

    public IDynamicMessageSink GetDynamicSink ()
    {
       return new NKRemoteServerDynamicMessageSink ();
    }

    To provide our own sink, we will have to create a custom sink object derived from IDynamicMessageSink.

    public class NKRemoteServerDynamicMessageSink : IDynamicMessageSink

    It is this message sink object that helps in intercepting all the calls that are made to and from the objects. This interface has only two methods, ProcessMessageStart and ProcessMessageFinish. As the names signify, the sink is notified when a message starts and finishes. The signature of these methods is very simple, they have only three parameters.

    public void ProcessMessageStart (IMessage reqMsg, bool bClientSide, bool bAsync)
    public void ProcessMessageFinish (IMessage replyMsg, bool bClientSide, bool bAsync)

    The first parameter is the IMessage object. If this is a MessageStart call then this object contains the information sent from the client side. You can look at the parameters and properties of the IMessage object to see how the client has put info into the message so that the stack can be recreated on the server side. The IMessage object contains the properties like InArgs, OutArgs, MethodName, MethodSignature, Context, etc. From these properties we can get all the details for the method call. If the call is MessageFinish then the IMessage object contains the response from the server side. Again, we can peek inside the message object to see how the server has returned the stack back to the client. The second parameter tells whether the call is on the server or client side. And the last parameter tells if the call is in asynchronous mode or not. The implementation of all these calls has been provided in the source file NKRemoteServer.cs.

    How To Find Out When An Object Gets Marshaled And Unmarshaled

    We can find out when an object gets marshaled, unmarshaled and disconnected. This information is provided by TrackingServices. We can register a tracking handler with TrackingServices to intercept these calls. We can provide our own object to handle tracking. This is accomplished by creating a class derived from the ITrackingHandler interface.

    public class NKRemoteServerTrackingHandler : ITrackingHandler

    This class has three methods that need to be implemented, MarshaledObject, UnmarshaledObject, DisconnectedObject. These methods get called when an object has been marshaled, unmarshaled and disconnected respectively. We can simply dump the information of the Object and ObjRef objects that get passed to these methods to see what information is contained in each.

    To register the tracking handler, call the RegisterTrackingHandler function on TrackingServices.

    trkHndlr = new NKRemoteServerTrackingHandler ();
    TrackingServices.RegisterTrackingHandler (trkHndlr);

    When we are done with the tracking handler, unregister it from TrackingServices by calling UnregisterTrackingHandler.

    TrackingServices.UnregisterTrackingHandler (trkHndlr);

    Although the SDK documentation does not mention it, the UnregisterTrackingHandler API has not been implemented. When you call this function, an exception gets thrown saying that this method is not implemented yet.

    Utility Library

    While going through various Microsoft Remoting samples, I came across various utility functions that are very helpful in dumping information about Remoting objects. I have modified these functions and added some more into a utility class. This class library project, RemoteUtils is included with the source code for this article. You will require this library to compile the server and client code included with this article. The methods that are implemented by this utility lib are as follows.

    DumpMessageObj - Dump IMessage Object Info
    DumpObjectRef - Dumps ObjRef Info
    DumpChannelObj - Dumps IChannel Object Info
    DumpLeaseObj - Dumps ILease Object Info
    DumpChannelSinkProperies

    Overtime I will be adding more functions into this utility class.

    Source Code And Compilation

    The source code for the server object is in NKRemoteServer.cs. The client application code is in RemoteClientApp.cs. The utility library code is in RemoteUtils.cs. The client configuration file is RemoteClient.cfg. This file needs to be copied to where ever you run the client. The project has been created using VS.Net Beta 1. The main project file, UtilServer.sln, is in the UtilServer folder. Make sure that you start the server before you run the client. Otherwise, an exception will be thrown by the client application. To test the sample, bring up two DOS windows and start the server and client applications. You will see how the calls get intercepted and all the information that gets emitted at various stages on the client as well as the server side. It is really an interesting piece of information to watch. Most of the implementation has been inspired by the RemSpy sample included in the SDK. I have extended this sample to spy on Proxy objects, also. I will be writing about that in a separate article. If you want to run the server on a separate machine then change the RemoteClient.cfg file’s entry in the RemoteApplication section as follows.

    RemoteApplication#NKRemoteServer#tcp://localhost:8085/NKRemoteServer
    
    
    To
    
    RemoteApplication#NKRemoteServer#tcp://127.0.0.1:8085/NKRemoteServer

    Believe me, it works. It is needless to say that you will require the .NET Framework installed to compile and run these samples. The implementation has been done in C#.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here