Make Your Apps Talk to Each Other: Asynchronous Named Pipes Library

An asynchronous .NET NamedPipes library to enable applications to talk to each other.

Now that, as of .NET 3.5, Named Pipes is a full-fledged citizen of the .NET Framework, it is a good exercise to learn how this transport can be useful.

In Windows, the design of named pipes is based towards client-server communication, and they work much like sockets, other than the usual read and write operations. Windows named pipes also support an explicit "passive" mode for server applications. The named pipe can be accessed much like a file. Win32 SDK functions such as CreateFile, ReadFile, WriteFile and CloseHandle can be used to open, read from, write to, and close a pipe. In the .NET Framework, the NamedPipeServerStream and NamedPipeClientStream wrap this underlying OS API, exposing native Named Pipes as managed code. .NET Named Pipes are bidirectional, meaning that two applications can both send and receive over the same pipe. Among other protocols, Named Pipes is a primary transport mode used by SQL Server and other RDBMS systems.

At the office, I heard some talk about making separate applications be able to send messages to each other, and that's what sparked my interest at this time. I've written several articles about Named Pipes in the past, in particular an implementation of a Named Pipe Cache here.

In keeping with my credo of "Don't reinvent the wheel" when approaching a programming problem, I spent some time Googling and Binging around for a decent .NET asynchronous implementation of a Named Pipes library, and was unable to find anything except your typical synchronous Console Application samples, which are essentially useless for the task at hand. But finally, I ran into something by Rudi Grobler at codeproject.com who was using something from Mario Lionello, who apparently was at one time, at least, an MVP who had implemented a reasonably decent async server and client pipes library. Unable to find any original source code with my Nimble Ninja Search TechniquesTM, I eventually Reflectored (is that a verb?) and disassembled the assembly and reconstructed my own implementation using it as a guide. Since it was originally only designed to handle string messages, I needed to "soup it up" to to make it useful for what I wanted to do. The original author did what I think is an admirable job but forgot that people might actually want to connect to a pipe on another machine, so in the process I took care of that, too.

The first step after getting the library assembly into a state consistent with my needs was to implement an IMessage interface. Thinking about being able to send a message containing any object you want over a pipe, what would be some of the kinds of properties you would want to have? This is what I came up with:

using System;
namespace AsyncPipes
{
/// <summary>
/// Interface for Async Pipes Message objects.
/// </summary>
public interface IMessage
{
Guid MessageId { get; set; }
String Originator { get; set; }
String Recipient { get; set;}
DateTime MessageDateTime { get; set; }
Type MessageType { get; set;}
byte[] Payload { get; set; }
}
}


Because one could have multiple application instances all talking on the same PipeStream, it would be a good idea to have a MessageId. This could also be used as a Primary Key should messages need to be persisted to a database. Also, since different instances of an application could receive pipe messages that weren't intended specifically for them, we have an Originator and a Recipient property that they can check. The MessageDateTime is obvious. Finally we have a Type property that tells the receiver what Type the Payload property represents. It could be a string message, a DataSet, or anything else. As long as we can serialize it to a byte array (e.g. BinaryFormatter or your favorite alternate type serializer) we can store the instance in the Payload property of the Message class.

I then implemented a GenericMessage class extending the IMessage interface:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;

namespace Messages
{
[Serializable]
public class GenericMessage :AsyncPipes.IMessage
{
#region IMessage Members
private Guid _messageId;
private string _originator;
private string _recipient;
private DateTime _messageDateTime;
private Type _messageType;
private byte[] _payload;

public Guid MessageId
{
get
{
return _messageId;
}
set
{
_messageId =
value;
}
}

public string Originator
{
get
{
return _originator;
}
set
{
_originator =
value;
}
}

public string Recipient
{
get
{
return _recipient;
}
set
{
_recipient =
value;
}
}

public DateTime MessageDateTime
{
get
{
return _messageDateTime;
}
set
{
_messageDateTime =
value;
}
}

public Type MessageType
{
get
{
return _messageType;
}
set
{
_messageType =
value;
}
}

public byte[] Payload
{
get
{
return _payload;
}
set
{
_payload =
value;
}
}

#endregion

public GenericMessage()
{
}

public GenericMessage(Guid messageId,string originator,string recipient,DateTime messageDateTime, Type messageType,byte[] payload )
{
this._messageId = messageId;
this._originator = originator;
this._recipient = recipient;
this._messageDateTime = messageDateTime;
this._messageType = messageType;
this._payload = payload;
}
}
}


So the Message class is what we actually serialize and send over the pipe after assigning the serialized Payload and setting the properties. You could also create specialized Message classes that implement IMessage and one or more additional interfaces. For example, you might want a Message class that implements the Command pattern and sports an Execute method. The final missing pieces of the puzzle are utility methods to serialize and deserialize the types used, and so I implemented a MessageSerializers class with static utility methods that is included in the AsyncPipes library project:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace AsyncPipes
{
public static class MessageSerializers
{
public static byte[] SerializeMessage(IMessage message)
{
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
formatter.
Serialize(ms, message);
return ms.ToArray();
}

public static IMessage DeserializeMessage(byte[] bMessage)
{
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream ms = new MemoryStream(bMessage);
Object message= formatter.Deserialize(ms);
return (IMessage) message;
}

public static byte[] SerializeObject( object item)
{
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
formatter.
Serialize(ms, item);
return ms.ToArray();
}

public static object DeserializeObject(byte[] bObject)
{
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream ms = new MemoryStream(bObject);
Object message = formatter.Deserialize(ms);
return message;
}
}
}

The rest of the prototype / demo implementation is to have two separate Windows Forms applications each of which is pretty much a mirror image of the other:



Each of these Apps has textboxes for a message to send, and to display a received message, as well as a DataGridView to display a DataTable from a DataSet. There is a dropdown that enables the user to select what kind of message they want to experiment with - either a string, or a DataSet. The Create DataSet button creates a DataSet and binds the single DataTable to the grid. When you press the Send button on either application, a GenericMessage object is created, the DataSet (or the string message) is assigned to the Payload property, the message is serialized to a byte array, and sent over the pipe. It will then appear in the other application - in the lower TextBox if it is a string message, or it populates the DataGridView if it was a DataSet message.

This is easy to "hook up" to your own apps because all they need is a reference to the AsyncPipes assembly. To prepare any application to use the library, this is all you need:

private AsyncPipes.NamedPipeStreamServer pipeServer = new NamedPipeStreamServer("test2");
private AsyncPipes.NamedPipeStreamClient pipeClient = new NamedPipeStreamClient("test");

private void Form1_Load(object sender, EventArgs e)
{
pipeServer.
MessageReceived += new MessageEventHandler(pipeServer_MessageReceived);
pipeClient.
MessageReceived += new MessageEventHandler(pipeClient_MessageReceived);

}

The pipe names on the other participating application would, of course, be reversed. You can see the full implementation code in the downloadable solution. You can use this AsyncPipes Library as the basis for most any type of IPC (Inter-Process Communication) need you may have. It is fast, easy - to - use, and efficient.

NOTE: To connect a client to a server on ANOTHER MACHINE, use the following syntax:
private AsyncPipes.NamedPipeStreamClient pipeClient = new NamedPipeStreamClient("\\othercomputername\\pipename");

My code will automatically parse the two parts out using the "\\" as a delimiter, and make the correct pipe connection.
As with all code samples published on eggheadcafe.com, this code comes under the Dr. Dotnetsky license: "Do whatever you want with it, but you can't sue me". Enjoy!

You can download the full Visual Studio 2008 solution here.



By Peter Bromberg   Popularity  (7566 Views)