Tuesday 30 April 2013

Interprocess Communication with WCF - Part 1

With the introduction of Microsoft's Windows Communication Foundation from .NET 3.0 onwards we have been provided with a powerful framework for communicating across process boundaries, whether they are on the same machine, the same network or even across the internet. A huge range of configuration options are available for the many scenarios that WCF can be used as part of our solution and they can seem overwhelming to begin with.

In this tutorial I will be using a simple Hello World example to show how we can use WCF to communicate between two processes running on the same machine. One application will act as our server and contain the WCF service definition. A service needs to comprise of three things, an Address, a Binding and a Contract, so remember your ABC! I'll show how we can define each of these three things in code. The other application will be our client and I will show the code needed to call our service.

First we need to create our solution and add a Windows Forms Application, this is going to be the server part of our solution. Next we add two class files, HelloWorldService.cs and IHelloWorldService.cs, along with references to System.Runtime.Serialization and System.ServiceModel. In IHelloWorldService.cs we are going to put an interface, this is our service Contract. In HelloWorldService.cs we will implement this interface and provide the functionality for our service.

Here's what our interface will look like.

  1. using System.ServiceModel;
  2.  
  3. namespace Server
  4. {
  5.     [ServiceContract]
  6.     public interface IHelloWorldService
  7.     {
  8.         [OperationContract]
  9.         void SendMessage(string message);
  10.     }
  11. }

We apply a ServiceContractAttribute to our interface to indicate it is a service contract. We also apply the OperationContractAttribute to any method we want to expose on our service. In this example we will have only the one method, ShowMessage.

Next we need to implement the service interface in HelloWorldService.cs. In our ShowMessage method we are simply going to display a message box containing the value of the message parameter.

  1. using System.Windows.Forms;
  2.  
  3. namespace Server
  4. {
  5.     public class HelloWorldService : IHelloWorldService
  6.     {
  7.         public void SendMessage(string message)
  8.         {
  9.             MessageBox.Show(message);
  10.         }
  11.     }
  12. }

The final part of our server application is the code we need to actually instantiate and expose our service to other applications.

  1. using System;
  2. using System.ServiceModel;
  3. using System.Windows.Forms;
  4.  
  5. namespace Server
  6. {
  7.     public partial class Form1 : Form
  8.     {
  9.         public Form1()
  10.         {
  11.             InitializeComponent();
  12.         }
  13.  
  14.         private void Form1_Load(object sender, EventArgs e)
  15.         {
  16.             ServiceHost host = new ServiceHost(typeof(HelloWorldService), new Uri("net.pipe://localhost"));
  17.             host.AddServiceEndpoint(typeof(IHelloWorldService), new NetNamedPipeBinding(), "HelloWorld");
  18.             host.Open();
  19.         }
  20.     }
  21. }

We use a ServiceHost object to expose our service to other applications. The ServiceHost requires those three things we discussed earlier, Address, Contract and Binding. 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 good to go.

A few more words on our binding and address. WCF provides several built in binding types as well as the ability to use our own custom binding types. I'm not going to cover those options here as it's a fairly vast topic on it's own. You will see from the code example I'm using the NetNamedPipeBinding, this binding is suitable for same machine, cross process communication so fits our scenario perfectly. It uses the NamedPipe protocol therefore our base address has to specify the correct scheme (net.pipe://).

Now all we need to do is create our client so add another Windows Forms Application to our solution. the client is going to need access to our service contract interface so add a reference in the client to our server application. Put a button on the form and in the click event handler we add the code to call our service.

  1. using System;
  2. using System.Windows.Forms;
  3. using Server;
  4. using System.ServiceModel;
  5.  
  6. namespace Client
  7. {
  8.     public partial class Form1 : Form
  9.     {
  10.         public Form1()
  11.         {
  12.             InitializeComponent();
  13.         }
  14.  
  15.         private void btnSendMessage_Click(object sender, EventArgs e)
  16.         {
  17.             ChannelFactory<IHelloWorldService> factory = new ChannelFactory<IHelloWorldService>(
  18.               new NetNamedPipeBinding(),
  19.               new EndpointAddress("net.pipe://localhost/HelloWorld"));
  20.  
  21.             IHelloWorldService client = factory.CreateChannel();
  22.  
  23.             client.SendMessage("Hello World!");
  24.         }
  25.     }
  26. }

We use the generic ChannelFactory<TChannel> class to create our client object. The ChannelFactory needs the type of service contract, the binding type, and the endpoint address of our service. Once we have created our client object it is a simple case of calling a method on our service.

In part 2 I will show how we can build on this example to send messages from our server back to the client.

No comments:

Post a Comment