Click here to Skip to main content
15,867,978 members
Articles / Programming Languages / C#
Article

Inter-Process Communication in .NET Using Named Pipes, Part 2

Rate me:
Please Sign up or sign in to vote.
4.77/5 (41 votes)
14 Jun 20044 min read 272.5K   4.2K   132   57
This article explores a way of implementing Named Pipes based Inter-Process Communication between .NET applications

1. Introduction

Named Pipes are sections of shared memory used by separate processes to communicate with one another. The application that creates a pipe is the pipe server. A process that connects to the pipe server is a client.

In this article we will demonstrate how to create Named Pipe server and client applications that exchange text messages. Both applications use the Named Pipes .NET implementation outlined in Part 1.

The Named Pipes server is a multithreaded engine that serves concurrent requests by creating on demand new threads and pipe connections. In addition to this, the server pipes are reused across requests in order to reduce the resource consumption on the server.

It is important to note that the situations where this solution would be most beneficial is when one application is exchanging frequent short text messages with another, located on the same machine or within the same LAN. For structured data exchange those text messages can also be XML documents or serialized .NET objects.

2. Pipe Connections

Pipe connections are classes that encapsulate common Named Pipes operations like creating pipes, writing and reading data and others. The AppModule.NamedPipes assembly contains a base class for pipe connections, APipeConnection, which defines the common methods for reading and writing of data.

The APipeConnection abstract class also implements the IDisposable interface through the IInterProcessConnection interface, which is intended to ensure proper cleaning of unmanaged resources, namely the native pipe handles.

There are two other pipe connection classes that inherit from APipeConnection - ClientPipeConnection and ServerPipeConnection. They override some of the methods, like Connect and Close, to provide implementation specific to the client and server Named Pipes respectively. Both ClientPipeConnection and ServerPipeConnection have destructors, which call the Dispose method to clean the unmanaged resources.

The pipe connection classes also contain a PipeHandle class, which holds the operating system native handle and the current state of the pipe connection.

3. ServerNamedPipe Class

The Named Pipes server is a multithreaded engine that creates Named Pipes and handles client connections. There are two main classes that provide the server functionality PipeManager and ServerNamedPipe.

The ServerNamedPipe Class wraps a ServerPipeConnection and a separate Thread to keep it alive. Below is the ServerNamedPipe constrictor.

C#
internal ServerNamedPipe(string name, uint outBuffer, 
     uint inBuffer, int maxReadBytes) {
  PipeConnection = new ServerPipeConnection(name, outBuffer, 
     inBuffer, maxReadBytes);
  PipeThread = new Thread(new ThreadStart(PipeListener));
  PipeThread.IsBackground = true;
  PipeThread.Name = "Pipe Thread " + 
     this.PipeConnection.NativeHandle.ToString();
  LastAction = DateTime.Now;
}

The constructor creates a new ServerPipeConnection and starts a new Thread that calls the PipeListener method. The main part of the latter is a loop, which listens to client connections, reads and writes data:

C#
private void PipeListener() {
    CheckIfDisposed();
  try {
    Listen = Form1.PipeManager.Listen;
    Form1.ActivityRef.AppendText("Pipe " + 
       this.PipeConnection.NativeHandle.ToString() + 
       ": new pipe started" + Environment.NewLine);
    while (Listen) {
      LastAction = DateTime.Now;

Read data from the client pipe:

C#
string request = PipeConnection.Read();
LastAction = DateTime.Now;
if (request.Trim() != "") {

The PipeManager.HandleRequest method receives the client request, processes it and returns a response. This response is then written to the pipe:

C#
  PipeConnection.Write(Form1.PipeManager.HandleRequest(request));
  Form1.ActivityRef.AppendText("Pipe " +
    this.PipeConnection.NativeHandle.ToString() +
    ": request handled" + Environment.NewLine);
}
else {
  PipeConnection.Write("Error: bad request");
}
LastAction = DateTime.Now;

Disconnect from the client pipe:

C#
PipeConnection.Disconnect();
if (Listen) {
  Form1.ActivityRef.AppendText("Pipe " +
     this.PipeConnection.NativeHandle.ToString() +
     ": listening" + Environment.NewLine);

Start listening for a new client connection:

C#
        Connect();
      }
      Form1.PipeManager.WakeUp();
    }
  } 
  catch (System.Threading.ThreadAbortException ex) { }
  catch (System.Threading.ThreadStateException ex) { }
  catch (Exception ex) { 
    // Log exception
  }
  finally {
    this.Close();
  }
}

Note that we do not close the server pipe. Creating a server Named Pipe is a relatively expensive operation so reusing the server pipes improves the performance and reduces the resource consumption on the server.

Our Named Pipes are created in blocking mode therefore the Connect method will block the current thread until a client connection comes through.

4. PipeManager Class

The PipeManager class is responsible for creating server pipes when necessary, manage the threads and also generate the response to the client requests. Below are displayed and described parts of the code in the PipeManager Class.

The Initialize method creates a new thread, which calls the Start method:

C#
public void Initialize() {
  Pipes = Hashtable.Synchronized(_pipes);
  Mre = new ManualResetEvent(false);
  MainThread = new Thread(new ThreadStart(Start));
  MainThread.IsBackground = true;
  MainThread.Name = "Main Pipe Thread";
  MainThread.Start();
  Thread.Sleep(1000);
}

The PipeManager class creates new pipe connections and threads only on demand. This means that ServerPipeConnection objects are created only when no connections exist or all connection are busy responding to other requests. Normally 2-3 server Named Pipe instances can handle quite a big load of concurrent client requests. This however depends also on the time necessary for processing a client request and generating the response.

References to the created ServerPipeConnection objects are kept in the Pipes Hashtable.

C#
private void Start() {
  try {
    while (_listen) {
      int[] keys = new int[Pipes.Keys.Count];
      Pipes.Keys.CopyTo(keys,0);

Loop through the existing ServerPipeConnection objects and check if they are still functional:

C#
foreach (int key in keys) {
  ServerNamedPipe serverPipe = (ServerNamedPipe)Pipes[key];
  if (serverPipe != null &&
       DateTime.Now.Subtract(serverPipe.LastAction).Milliseconds >
       PIPE_MAX_STUFFED_TIME && serverPipe.PipeConnection.GetState()
       != InterProcessConnectionState.WaitingForClient) {
    serverPipe.Listen = false;
    serverPipe.PipeThread.Abort();
    RemoveServerChannel(serverPipe.PipeConnection.NativeHandle);
  }
}

This is determined by checking how long the pipe has been in a state other than "WaitingForClient".

The NumberPipes field contains the maximum number of server Named Pipes we want to have on the server:

C#
if (numChannels <= NumberPipes) {
  ServerNamedPipe pipe = new ServerNamedPipe(PipeName, OutBuffer,
     InBuffer, MAX_READ_BYTES);
  try {

The Connect method puts the newly created pipe in listening mode, which blocks the thread until a client attempts to make a connection:

C#
pipe.Connect();
pipe.LastAction = DateTime.Now;
System.Threading.Interlocked.Increment(ref numChannels);

Start the ServerPipeConnection thread:

C#
          pipe.Start();
          Pipes.Add(pipe.PipeConnection.NativeHandle, pipe);
        }
        catch (InterProcessIOException ex) {
          RemoveServerChannel(pipe.PipeConnection.NativeHandle);
          pipe.Dispose();
        }
      }
      else {
        Mre.Reset();
        Mre.WaitOne(1000, false);
      }
    }
  }
  catch {
    // Log exception
  }
}

5. Client Pipe Connections

To connect a client application to the server using Named Pipes we have to create an instance of the ClientPipeConnection class and use its methods for reading and writing data. The following code illustrates that:

C#
IInterProcessConnection clientConnection = null;
  try {
    clientConnection = new ClientPipeConnection("MyPipe", ".");
    clientConnection.Connect();
    clientConnection.Write(textBox1.Text);
    clientConnection.Close();
  }
  catch {
    clientConnection.Dispose();
  }

The name of the pipe "MyPipe" needs to be the same as the name used to create the server pipe. If the Named Pipes server is on the same machine ,then the second parameter of the ClientPipeConnection constructor is ".". Otherwise it needs to be the network name of the server machine.

It is important to always call the Dispose method when we have finished working with the client pipe. This will release the related unmanaged resources, but more importantly, will allow the server pipe to disconnect from the client and start waiting for new connections. If the client pipe is not closed the server one will be indefinitely blocked until the Named Pipes server is shut down.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
Ivan Latunov is a Software Architect with long term commercial experience in the areas of software architecture and engineering, design and development of web and distributed applications and business and technical analysis. Technical blog. Website: ivanweb.com.

Comments and Discussions

 
GeneralRe: Send Data from the Server Pipe Pin
Ivan L13-Mar-05 22:20
Ivan L13-Mar-05 22:20 
GeneralRequired User Permissions Pin
MikeGoatly19-Jan-05 10:48
MikeGoatly19-Jan-05 10:48 
GeneralExcellent Article! Pin
sheppe8-Nov-04 7:48
sheppe8-Nov-04 7:48 
GeneralNamed pipE wrapper blocks on dispose Pin
tschneringer8-Oct-04 12:33
tschneringer8-Oct-04 12:33 
GeneralRe: Named pipE wrapper blocks on dispose Pin
Ivan L10-Oct-04 4:07
Ivan L10-Oct-04 4:07 
Generalinterface to C++ unmanaged Client Pin
jeffr1234527-Sep-04 9:33
jeffr1234527-Sep-04 9:33 
GeneralRe: interface to C++ unmanaged Client Pin
Ivan L10-Oct-04 3:51
Ivan L10-Oct-04 3:51 
QuestionC++ version of this? Pin
jeffr1234521-Sep-04 1:24
jeffr1234521-Sep-04 1:24 
Do you have a C++ version of this code, which is quite nice by the way? Thanks.
AnswerRe: C++ version of this? Pin
Alex Korchemniy20-Oct-04 19:48
Alex Korchemniy20-Oct-04 19:48 
GeneralC++ client code right here!! Pin
derek_penguin6-Mar-06 3:24
derek_penguin6-Mar-06 3:24 
GeneralServer write waits forever Pin
ErikPhilips15-Aug-04 1:08
ErikPhilips15-Aug-04 1:08 
GeneralRe: Server write waits forever Pin
ErikPhilips15-Aug-04 11:38
ErikPhilips15-Aug-04 11:38 
GeneralSecurity Pin
Anonymous15-Jun-04 3:16
Anonymous15-Jun-04 3:16 
GeneralRe: Security Pin
Ivan L15-Jun-04 11:15
Ivan L15-Jun-04 11:15 
GeneralRe: Security Pin
Anonymous17-Jun-04 6:04
Anonymous17-Jun-04 6:04 
GeneralRe: Security Pin
Ivan L18-Jun-04 15:11
Ivan L18-Jun-04 15:11 
GeneralRe: Security Pin
blackbytes21-Nov-04 3:01
blackbytes21-Nov-04 3:01 
QuestionHow to stop pipes blocking Pin
Peter Kohout14-Jun-04 23:45
Peter Kohout14-Jun-04 23:45 
AnswerRe: How to stop pipes blocking Pin
Ivan L15-Jun-04 11:25
Ivan L15-Jun-04 11:25 
GeneralRe: How to stop pipes blocking Pin
Peter Kohout15-Jun-04 12:07
Peter Kohout15-Jun-04 12:07 
GeneralRe: How to stop pipes blocking Pin
Ivan L15-Jun-04 12:17
Ivan L15-Jun-04 12:17 
GeneralExcellent piece of code. Pin
Anonymous11-Jun-04 6:57
Anonymous11-Jun-04 6:57 
GeneralRe: Excellent piece of code. Pin
Ivan L12-Jun-04 14:23
Ivan L12-Jun-04 14:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.