Using .Net Remoting for Inter-Process Communication The Basics
by Jon Wojtowicz

.Net Remoting
Microsoft created remoting in .Net for communication across applications. .NET objects are exposed to remote processes, thus allowing inter-process communication. The processes can be on the same computer or across the network.
Event though remoting is useful in certain situations, the focus has now shifted to using web services for exposing remote functionality.
There are circumstances where the use of web services does not fit. The particular scenario I had was to allow two applications residing on the same machine to communicate. This was an ideal situation for the use of remoting while using web services was impractical.
Remoting Basics
Remoting consists of a server and a client. A remotable object is passed between the client and the server to expose the desired functionality. There are several well written articles on the details of remoting that I will not cover. I'm going to cover the basics of setting up a client, server and remotable object.
To start we needed to create our remotable object. This creates a particular issue since the object needs to be shared between the client and server. I generally create a shared assembly for use with the actual types that will be passed in the methods. I maintain two copies of the remotable class both marked as internal. This allows me to provide the stub code for the methods to the client programmer while hiding the actual implementation in the server code. This produces two classes like the following.


Server Class
 
namespace RemotingShared
{
   
   internal class ServiceRequestAgent: MarshalByRefObject
   {

       private static string data;
       public static string DataFromForm
       {
         get{return data;}
         set{data = value;}
       }		
       public string GetData()
       {
         return data;
       }
       public string Concat(string[] strings)
       {
         return string.Concat(strings);
       }
   }
}
Shared Client Code
namespace RemotingShared
{
   internal class ServiceRequestAgent: MarshalByRefObject
   {		
     public string GetData()
     {
       throw new NotImplementedException("Calling local object.");
     }
     public string Concat(string[] strings)
     {
       throw new NotImplementedException("Calling local object.");
     }
   }
}			
A couple of items to note in the classes. They must both inherit from MarshalByRefObject and match types exactly, namespace and class name. This is a requirement for remoting. You can also notice that the implementations are completely different. The server version has an additional property to hold data. It's internal implementation details like this that can be hidden from the client developer by separating the classes.
Now we need to set up the server listener. Remoting uses the concept of channels for communication. The channel can be tcp or http. We can also choose the formatting type for our remotable class, binary or SOAP. Note that the SOAP formatter does not use standard SOAP so it is only useful for remoting and not interoperability. Since we want the best performance we will use a binary formatter with a tcp channel. One thing we need to ensure is that we only register a single particular remoting channel once. If we attempt to register the same channel a second time we will get an error. this also holds true for registering the remotable object. In my example I've accomplished this by creating a registration class with a static method and flag field to prevent multiple registrations. The code is as follows.
public sealed class RemotingAgent
{
   private static bool isRegistered = false;
   private RemotingAgent(){}
   public static void RegisterRemotingObjects()
   {		
     if( isRegistered )
        return;
     isRegistered = true;	
     ListDictionary sd = new ListDictionary();
     sd.Add("rejectRemoteRequests","true");
     sd.Add("port", 8085);
     TcpChannel chan = new TcpChannel(sd, 
                       new BinaryClientFormatterSinkProvider(), 
                       new BinaryServerFormatterSinkProvider());
     ChannelServices.RegisterChannel(chan);
     RemotingConfiguration.RegisterWellKnownServiceType(
                   typeof(ServiceRequestAgent), "ServiceServer", 
                   WellKnownObjectMode.SingleCall);
   }
}			
Since we want to only allow local calls to the listener we add the dictionary so we can pass in some additional arguments when we register the channel. We tell it to reject remote requests and set the port to listen on. This is useful for creating the remoting channel in code as many of the properties are exposed through the configuration file and not directly through properties or parameters. I've also coded this into the application to prevent the need for managing a configuration file. The server channel registration can be specified in a configuration file if that flexibility is required.
For the client we need a similar arrangement. I added a static method to get a remotable object when needed. The static constructor only fires when we reference the class for the first time and we only create the remoting channel when needed. The client code is the following.
 
internal sealed class RemoteConnectionClient
{
   private static bool channelCreated = false;
   static RemoteConnectionClient()
   {
     RegisterRemoting();
   }

   private RemoteConnectionClient(){}

   private static void RegisterRemoting()
   {
      if(channelCreated)
         return;
      channelCreated = true;
      ListDictionary sd = new ListDictionary();
      sd.Add("name","ServiceChannel");
      sd.Add("port", 0);
      TcpChannel chan = new TcpChannel(sd, 
                new BinaryClientFormatterSinkProvider(), 
                new BinaryServerFormatterSinkProvider());
      ChannelServices.RegisterChannel(chan);		
   }
   
   public static ServiceRequestAgent GetServiceRequestAgent()
   {
        return (ServiceRequestAgent)Activator.GetObject
         (typeof(ServiceRequestAgent), "tcp://localhost:8085/ServiceServer");
   }
}	
It is worth noting that any objects we pass as arguments or return values must be serializable. If you create your own classes to be passed then you can either implement ISerializable or use the Serializble attribute on your class. Since I used strings for this example, I should not have any issues with serialization.
To complete our shared component we will add it to the GAC. This is not necessary but since we are sharing this assembly it makes sense to add it. To do this we need to provide a strong name. To create the string name we use the sn.exe utility to create our snk file. We add the generated snk file to our project and add the following to the AssemblyInfo.cs file to sign the assembly.
 
Creating strong name key
sn -k remotingShared.snk
------
Inside AssemblyInfo.cs
[assembly: AssemblyKeyFile(@"..\..\RemotingShared.snk")]
To test our remoting we can add a reference to the RemotingShared assembly and program to the SharedData class. We then start the RemotingServer application as it must be running for the remoting to work. We can then start the RemotingSharedTest application and verify that everything is working correctly.

This has been a simple example but it demonstrates how you can communicate between processes. I used a Windows application but the server could be a console application or a service. You can also host it in IIS although this is not practical for my scenario. This example also limits the communication to the same machine. If you are looking at communicating to a remote server I would suggest looking at web services. But as you can see, using web services cannot answer all the problems that we may face with applications.

Download the code that accompanies this article

Jon Wojtowicz is a C# MVP and a Systems Analyst at a large insurance company in Chattanooga, TN where he currently provides developer support and internal training. He has worked as a consultant working with Microsoft Technologies. This includes ASP, COM, VB6 and .Net, both C# and VB.Net since Beta 1. He has been an MCSD since 1999 and an MCT since 2000. Prior to getting a degree in Computer science he worked as a process engineer focusing on process automation, programmable controllers and equipment installations. In his spare time he likes woodworking and gardening.
Article Discussion: