Enterprise Software

Using IPC channels and .NET Framework 2.0 to communicate between processes

With the introduction of .NET Framework 2.0, Microsoft has included a new remoting channel that is built on named pipes which developers can use for interprocess communication. Zach Smith explains how to use these new channels. A sample solution is also provided that demonstrates the concepts described.

This article is also available as a TechRepublic download, which includes a sample solution in the form of a Visual Studio 2005 Project.

Interprocess communication, or "IPC", allows one process to communicate with another process that is running on the same computer. Since the calls don't have to travel over the network, IPC calls have much less overhead and are typically faster than calls that do travel over the network. There are many different types of IPC calls, but for Windows systems a lot of IPC calls travel over named pipes.

In the .NET world, there is no direct support for named pipes in the framework class library (FCL). If a developer needs to use named pipes to communicate with an existing system, they can hook into the COM layer and write a wrapper to access the named pipes functionality. However, if the processes a developer needs to communicate with one another are built with .NET Framework 2.0, they can use IPC channels for the communication.

IPC channels are remoting channels built on top of the Windows IPC system. If you are familiar with writing remoting applications, then the new IPC channels should be an easy step for you. While the IPC channels are very similar to the other remoting channels such as HTTP and TCP, there are some functional differences. The most notable difference is that IPC channels are only useful when the processes that need to communicate reside on the same machine. While this is certainly a limitation it comes with the advantage of much less overhead and better performance.

How to use the new IPC channels

To use the new IPC channels you first need to look at the architecture that is required to make the communication possible. You will need at least the following layers/assemblies:

  • Shared Objects – These are objects that are shared by both the IPC Server and IPC Client. This should be a separate project or assembly, and should not reference either the Server or Client. This layer is implemented in the sample solution by the SharedObjects project.
  • IPC Client – This project/assembly will consume services and functionality from the server. The IPC Client will need to reference the SharedObjects. This layer is implemented in the sample solution by the Client project.
  • IPC Server – This project/assembly will host the IpcServerChannel and expose certain functionality to be consumed by the client application. This layer is implemented in the sample solution by the Server project.

You also need to make sure that your client and server projects have a reference set to System.Runtime.Remoting, and that the classes you're going to have channels in have the following using directive setup:

using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;

The SharedObjects layer

In the sample solution there are two objects contained in the SharedObjects project: ServerData and ServerMethods. ServerData is used to store current information about the server, and is not marshaled back and forth from the server and client. ServerMethods is the object that we're most interested in since it is the object that is marshaled. The code for the ServerMethods object is shown in Listing A.

Listing A

    public class ServerMethods : MarshalByRefObject
    {
        public string Status
        {
            get { return ServerData.Status; }
        }

        public DateTime StartTime
        {
            get { return ServerData.StartTime; }
        }

        public string IsProcessing
        {
            get { return ServerData.IsProcessing; }
        }
    }

Notice the inheritance from MarshalByRefObject, this allows the ServerMethods class to be passed across application boundaries. Without this inheritance the ServerMethods object won't be passed from one domain to the other, it will simply be instantiated in the domain where it is called. To read more on MarshalByRefObject click here.

The ServerLayer

In the sample solutionthe server layer is implemented by the Server project. Within this is a single windows form, which handles setting up the channel when it loads. Within the Form1_Load event you will find the code in Listing B.

Listing B

    //Instantiate our server channel.
    channel = newIpcServerChannel("ServerChannel");

    //Register the server channel.
    ChannelServices.RegisterChannel(channel, true);

    //Register this service type.
    RemotingConfiguration.RegisterWellKnownServiceType(
                                typeof(ServerMethods),
                                "ServerMethods",
                                WellKnownObjectMode.Singleton);

    //Set the server's status to Good.
    ServerData.Status = "Good";

    //Set the server's start time to now.
    ServerData.StartTime = DateTime.Now;

This code instantiates a new IpcServerChannel named "ServerChannel", registers the channel with ChannelServices, registers the service, and then sets a couple properties on the ServerData class.

The RegisterWellKnownServiceType call is very important because that is what tells the remoting libraries which object(s) you plan to provice, and the URI of those objects. In the call shown above, we are providing a ServerMethods object on the ServerMethods URI. The full URI for this would look similar to the address below, and is in fact the exact URI the client uses to contact the server:

ipc://ServerChannel/ServerMethods

The format for the URI is ipc://[channel_name]/[method_name]. The channel name of "ServerChannel" is defined in the code above by the following line:

    //Instantiate our server channel.    channel = newIpcServerChannel("ServerChannel");

The Client Layer

In the sample solutionthe Client layer is implemented by the Client project. Similarly to the Server project, you will find a single windows form in the client project that sets up the client channel and registers the types to use for IPC. Within the Form1_Load event you will see the code shown in Listing C.

Listing C

    //Create an IPC client channel.
    IpcClientChannel channel = newIpcClientChannel();

    //Register the channel with ChannelServices.
    ChannelServices.RegisterChannel(channel, true);

    //Register the client type.
    RemotingConfiguration.RegisterWellKnownClientType(
                        typeof(ServerMethods),
                        "ipc://ServerChannel/ServerMethods");

    //Instantiate a new ServerMethods object. This is
    // being instantiated in the current AppDomain, but
    // since the ServerMethods object inherits from
    // MarshalByRefObject, a proxy is created and calls
    // on the ServerMethods object are passed to the
    // server.
    server = newServerMethods();

This code is very close to the code on the server class, except here we instantiate an IpcClientChannel instead of an IpcServerChannel, and instead of using RegisterWellKnownServiceType, we use RegisterWellKnownClientType. RegisterWellKnownClientType tells the remoting infrastructure the type (class) you want to remote, and the URI of the object to remote from.

In the case of our client example, we are remoting the type ServerMethods from the URI ipc://ServerChannel/ServerMethods. Notice that this URI is the same URI that we setup in the server project.

Once the channel and types are setup, you can instantiate that type like you would any other C# type:

server = newServerMethods();

When this statement is executed the remoting infrastructure contacts the server to create a new object and further calls to that object are proxied to and from the server to the client. This is demonstrated in the btnCallServer_Click method which contains the code shown in Listing D.

Listing D

    txtServerData.Clear();

    txtServerData.Text = "Status: "+server.Status+"\r\n";

    txtServerData.Text += "Start: "+server.StartTime.ToString()+"\r\n";

    txtServerData.Text += "Is Processing: "+server.IsProcessing+"\r\n";

Each call to the "server" object is passed to the IPC server and the data is returned to the client through the "server" object. This allows the server to modify data in its AppDomain and the client is able to view that data from the client's AppDomain.

A good example of this is the ServerMethods.IsProcessing property. This property is updated on the IPC server every 2 seconds so that it changes from "Yes" to "No", or visa versa. Since this value is accessible from the client, the client will see the value change even though the client did not change the value itself.

Running the example solution

To run the example solution, open the InterprocessCommunication.sln solution file in Visual Studio 2005 and right click the solution to Rebuild Solution. After the solution is done building, open up both the Client and Server build directories. You will need to execute the server first by double clicking on "Server.exe", then open the client by double clicking on "Client.exe".

When the client comes up, click the button labeled "Call Server", and you should see results such as these in the textbox below the button:

Status: Good
Start: 11/16/2006 3:17:19 PM
Is Processing: Yes

Try waiting a second or so and then click the "Call Server" button again. After clicking a couple times you should see the "Is Processing" status turn to "No". You can also click the "Set Status to Bad" button on the server application, and after clicking "Call Server" you should see the "Status" turn to "Bad".

In closing

As you can see, IPC channels are very useful for monitoring one application from another. This is especially useful in the case of Windows Services when there is no user interface associated with the server application. If you have any trouble getting the sample solutionup and running, or have any questions, please don't hesitate to contact me.

3 comments
dave94
dave94

Can you use IPC on the same machine but between processes running as different users (e.g. SYSTEM and normal account). My code worked when both processes were in same logon session. When client was a service running as system and server was a normal process it failed to connect. Thx Dave

Mark W. Kaelin
Mark W. Kaelin

Do you use named pipes for interprocess communication in the .NET Framework? How effective is it?

dipapadi2000
dipapadi2000

How effective is it compared to what? If you mean compared to standard tcp channels, I guess it is more effective. Don't you think?

Editor's Picks