Share via


Easy peasy IPC-sy

Windows provides a wide range of inter process communication mechanisms, from very low level sockets (which can easily be used from managed applications too) to much more sophisticated WCF. However, the lighter weight ones tend to require a lot of fiddling with message structures and processing, and the heavier ones are, well, rather costly for simple messaging between applications on the same machine. One extremely simple mechanism introduced early in .NET's history is .NET Remoting. Alas it's now deprecated, which is a shame and I can't really recommend it for any future work - it does have limitations but it's so easy to use that it's still my first choice in almost any research prototype (but definitely not for production code) which requires one-way communications between cooperating .NET processes, even though it is "old" and frowned upon technology.

There are three components in a typical remoting setup: a class definition for whatever it is I need to communicate between the two processes, a client library and a server mechanism at the other end.

The following is a (much simplified) version of the library I use to communicate between Project Colletta add-ins and the main process:

 public interface IToolbarManager
{
    Guid CreateToolbar(IntPtr hwnd, string path, string title);

    [System.Runtime.Remoting.Messaging.OneWay]
    void Move(IntPtr hwnd, Guid docId);
}

The first method requests that the main process create a tool bar associated with the window (HWND) and document (path and title) specified. The second method is used for the add-in to notify the main process that the window has moved, and thus the tool bar needs to be moved too - this can be marked as OneWay, to gain a slight efficiency improvement, because the client doesn't need to wait for the server to do anything before it can move on (as also indicated by the method's void return). This is built into a separate assembly that can be shared between client and server.

The server provides a concrete implementation of this interface:

 internal class ToolbarManager : MarshalByRefObject, IToolbarManager
{
    public Guid CreateToolbar(IntPtr hwnd, string path, string title)
    {
        // Create the tool bar
    }

    public void Move(IntPtr hwnd, Guid docId)
    {
        // Move the tool bar
    }
}

Note that it derives from MarshalByRefObject, a signal that this is exposed for remote access. The only other thing the server has to do is expose this to the outside world - there are several ways to do this, but I tend to use the following:

 var provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
var props = new System.Collections.Hashtable();
props["port"] = SOME_PORT_NUMBER;
props["bindsTo"] = "localhost";
props["rejectRemoteRequests"] = "true";
ChannelServices.RegisterChannel(new System.Runtime.Remoting.Channels.Tcp.TcpServerChannel(props, provider), false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(ToolbarManager), "SOME_UNIQUE_NAME", WellKnownObjectMode.Singleton);

Only the last pair of calls is actually essential since the system accepts defaults for most of the communication properties, but I tend to take the opportunity to define some non-default values. First, I explicitly request binary serialization (XML is an alternative). Second, by setting "rejectRemoteRequests" I ensure that only processes on the same machine can invoke functions on the server process. The last line means that any process asking for the IToolbarManager interface on the specified port will create or reuse an instance of the ToolbarManager concrete class - and that's all that is required on the server. Do note that requests can come in on arbitrary threads, and I usually end up having methods such as those in ToolbarManager marshal across to the main thread or provide some other means of thread synchronization.

On the client side, I make sure that the TCP channel is available:

 ChannelServices.RegisterChannel(new System.Runtime.Remoting.Channels.Tcp.TcpClientChannel(), false);

and specify the connection with:

 var manager = (IToolbarManager)Activator.GetObject(typeof(IToolbarManager),
                 "tcp://localhost:" + SOME_PORT_NUMBER + "/" + SOME_PORT_NAME);

Now I can invoke manager.CreateToolbar and manager.Move to perform those operations.

And that's all there is to it - it really is so simple that, even though this is deprecated technology, it's still my favourite way to get a couple of processes to talk to each other. If you want to know more about .NET Remoting, I can recommend a couple of really good books - you can tell from their publication dates (2005 and 2002, respectively) that this is old technology!