Recently I have been working on a project that involves sending and receiving messages from one Windows application to another application which is running in a separate process.

Initially I was going to use .Net Remoting as there was some code in the project that already did something similar however that was only one-way communication from client to server. What I needed was two-way communication (or duplex) so I decided that rather than modifiy the existing code I would write some new code using Windows Communication Foundation (WCF).


WCF replaces the much older, deprecated .Net Remoting and has many more advantages over the older technology. .NET Remoting applications can use the HTTP, TCP, and SMTP protocols whereas WCF can use named pipes and MSMQ as well along with all these protocols. WCF abstracts the idea of a service from the transport technology that is used to implement that service. With a WCF service you can simply change the transport technology through configuration. There are many other benefits to upgrading to WCF which could be a separate blog post in itself.


In this post I want to show you how to setup WCF to allow interprocess communication between two process on the same machine. Before I show some code let me explain exactly what I want to achieve.

I need two applications running in different processes on the same machine to send messages to each other. One will be the client and the other the server. However it may not always be the client initiating the communication. I need the server to have the ability to independently send a message to the client.

So lets see some code to achieve this.

Server

First we will need to create a solution and add a class library project, this is going hold the interfaces. Next we add two interface files, ITestService.cs and ICallbackService.cs, along with the reference needed to work with WCF, System.ServiceModel. ITestService.cs will contain our interface which will define our service contract for methods for the client. ICallbackService.cs will define the interface for the methods that the server can call back into to communicate to the client.

ITestService.cs

using System.ServiceModel;
            
            namespace TestInterfaces
               {
                 [ServiceContract(SessionMode = SessionMode.Required,
                 CallbackContract = typeof(ICallbackService))]
                 public interface ITestService
                 {
                  [OperationContract(IsOneWay = true)]
                  void Connect();
            
                  [OperationContract(IsOneWay = true)]
                  void ShowMessage(string message);
                }
            }
            

I apply a ServiceContractAttribute to my interface to indicate it is a service contract. I also apply the OperationContractAttribute to any method I want to expose on my service. In this example I will have two methods, Connect and ShowMessage.

You can see in the OperationContractAttribute I have set the IsOneWay property to true. In WCF, even when you declare a method as void, a response message is returned to the party invoking the method. In duplex (or two-way) service-oriented applications in which the client and server communicate with each other independently, a client channel can use the IsOneWay property on its methods to indicate that the service can make one-way calls to the client that the client can treat as events. No return call or message is generated because the service does not expect any response message and hence avoids potential deadlock.

The Connect method will be used to access the callback channel which I will show later.

ICallbackService.cs

using System.ServiceModel;
            
            namespace TestInterfaces
            {
               public interface ICallbackService
               {
                  [OperationContract(IsOneWay = true)]
                  void SendCallbackMessage(string message);
               }
            }
            

This interface contains the definition of the method that the server can call to send a message to the client.

Now that we have defined our interfaces we can add a Windows Forms application which will be our server. Add a reference to the interfaces project. Next add a class TestService.cs to implement the ITestService interface.

TestService.cs

using System.ServiceModel;    
            using System.Windows.Forms;
            
            namespace TestServer
            {
                [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
               public class TestService : ITestService
               { 
                   public void Connect()
                   {
                       Callback = OperationContext.Current.GetCallbackChannel<ICallbackService>();
                   }
            
                   public static ICallbackService Callback { get; set; }
            
            
                   public void ShowMessage(string message)
                   {
                      MessageBox.Show(message);
                   }
               }
            }
            

You can see here that the implementation of the Connect method sets the property Callback to the current operation context callback channel. This is needed so that the server can access this callback channel if it wishes to send a message to the client. The ShowMessage method just displays the passed in message in a message box.

The last part of the server application is the code to initiate the service and expose it.

Add a windows form to the project and call it TestServerForm.cs

TestServerForm.cs

using System;
            using System.ServiceModel;
            using System.Windows.Forms;
            using TestInterfaces;
            
            namespace TestServer
            {
                public partial class TestServerForm : Form
                {
                    public TestServerForm()
                    {
                        InitializeComponent();
                    }
            
                    private void TestServerForm_Load(object sender, EventArgs e)
                    {
                        var host = new ServiceHost(typeof(TestService), 
                                   new Uri("net.pipe://localhost"));
            
                        host.AddServiceEndpoint(typeof(ITestService), 
                                                new NetNamedPipeBinding(), "Test");
                        host.Open();
                    }
                }
            }
            

We use a ServiceHost object to expose our service to other applications. We instantiate the ServiceHost object by passing the type of our service implementation and the base address of our service to the constructor. We add a service endpoint to the service host and specify our contract, binding and address. The address we specify in the end point is added to the base address we used in the ServiceHost constructor. Then finally we call the Open method of ServiceHost and our service is ready.

I'm using the NetNamedPipeBinding, this binding is suitable for same machine, cross process communication so fits my needs perfectly.

Now its time to create the client.

Client

Add another Windows Form Application to our solution. The client project will also need a reference to our TestInterfaces project. In the constructor method of the form you can add the code to call the service.

TestClientForm.cs

using TestInterfaces;
            using System.ServiceModel;
            
            namespace TestClient
            {
                public partial class TestClientForm : Form
                {
            
                    ITestService service;
            
                    public TestClientForm()
                    {
                       var callback = new CallbackService();
                       var context = new InstanceContext(callback);
                       var pipeFactory =
                            new DuplexChannelFactory<ITestService>(context,
                            new NetNamedPipeBinding(),
                            new EndpointAddress("net.pipe://localhost/Test"));
            
                        service = pipeFactory.CreateChannel();
            
                        service.Connect();
                    }
                }
            }
            

You can see here that I first create a new CallBack object. I will show you this class next. It's basically the implementation of the ICallBackService.cs interface.

I am using the DuplexChannelFactory here allow two-way communication. Two way communications require duplex channels and these require a client to provide a channel for the service to send messages back through. I pass in an instance of the InstanceContext object which was instantiated with an instance of our callback implementation.

I am calling the Connect method which as seen previously gets the callback channel.

Here is the implementation of the ICallbackService.cs interface which is also in the client project.

CallbackService.cs

using TestInterfaces;
            
            namespace TestClient
            {
                public class TestCallback: ICallbackSerice
                {
                    public void SendCallbackMessage(string message)
                    { 
                        MessageBox.Show(message);            
                    }
                }
            }
            

So everything is now in place. We just need to update our server form and client form to actually call the service methods. I will add a button to each form which on click will call the appropriate method.

TestServerForm.cs - updated

using System;
            using System.ServiceModel;
            using System.Windows.Forms;
            using TestInterfaces;
            
            namespace TestServer
            {
                public partial class TestServerForm : Form
                {
                    ...
                    ...
                    ...
            
                 private void btnSend_Click(object sender, EventArgs e)
                 {  
                    TestService.Callback.ShowCallbackMessage("Hi, I'm the server);
                 }
            
                }
            }
            

TestClientForm.cs - updated

using TestInterfaces;
            using System.ServiceModel;
            
            namespace TestClient
            {
                public partial class TestClientForm : Form
                {
                    ITestService service;
            
                    ...
                    ...
                    ...
            
                    private void btnSend_Click(object sender, EventArgs e)
                    {  
                        service.SendMessage("Hi, I'm the client);
                    }
                }
            }
            

Feel free to let me know what you think.

Fin.