Click here to Skip to main content
15,860,972 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.1K   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

 
GeneralProblem with Inter-Process Communication on Windows 7 X64 Machine Pin
Ameno0113-Jan-11 3:01
Ameno0113-Jan-11 3:01 
GeneralClient Connect() stuck Pin
Markclee15-Nov-10 6:52
Markclee15-Nov-10 6:52 
GeneralRe: Client Connect() stuck Pin
Markclee17-Nov-10 11:18
Markclee17-Nov-10 11:18 
GeneralThank you very much! Pin
Tsuda Kageyu28-Nov-09 21:21
Tsuda Kageyu28-Nov-09 21:21 
GeneralVery Good Example Pin
Tamal Saha5-Apr-09 8:39
Tamal Saha5-Apr-09 8:39 
GeneralVery good! Pin
Eugene Plokhov14-Jul-08 0:51
Eugene Plokhov14-Jul-08 0:51 
GeneralConcurrent use by Multiple client Pin
rpawnikar10-Sep-07 22:41
rpawnikar10-Sep-07 22:41 
Generalit is crashing in debug mode Pin
muharrem2-Aug-07 23:42
muharrem2-Aug-07 23:42 
GeneralInternal error:1326 Pin
tprakash18-Mar-07 20:26
tprakash18-Mar-07 20:26 
GeneralRe: Internal error:1326 Pin
tprakash18-Mar-07 22:49
tprakash18-Mar-07 22:49 
GeneralRe: Internal error:1326 Pin
mikepc14-Dec-08 8:26
mikepc14-Dec-08 8:26 
GeneralRe: Internal error:1326 Pin
J Abraham24-Sep-09 19:34
professionalJ Abraham24-Sep-09 19:34 
GeneralRe: Internal error:1326 Pin
mikepc24-Sep-09 22:49
mikepc24-Sep-09 22:49 
GeneralRe: Internal error:1326 Pin
osmadey1-Mar-11 21:12
osmadey1-Mar-11 21:12 
GeneralUsing Class in FCL directly Pin
ada cai23-Dec-06 16:40
ada cai23-Dec-06 16:40 
General(c) Pin
Alexey_i21-Oct-06 3:11
Alexey_i21-Oct-06 3:11 
GeneralSend message to assign client Pin
roy198117-Aug-06 20:59
roy198117-Aug-06 20:59 
GeneralError reading from pipe. Internal error: 109 Pin
pchana3-Jul-06 7:27
pchana3-Jul-06 7:27 
I'm getting this error from the client when debugging the server in VS 2005. Both the client and server have been built using VS 8.0. Any ideas?


P.S. great article
GeneralRe: Error reading from pipe. Internal error: 109 Pin
SakthiSurya23-Jan-07 18:10
SakthiSurya23-Jan-07 18:10 
GeneralRe: Error reading from pipe. Internal error: 109 Pin
n8_dawg2-May-07 4:33
n8_dawg2-May-07 4:33 
GeneralRe: Error reading from pipe. Internal error: 109 Pin
KTJones14-Nov-07 1:56
KTJones14-Nov-07 1:56 
GeneralRe: Error reading from pipe. Internal error: 109 Pin
jorbuy13-Dec-07 16:32
jorbuy13-Dec-07 16:32 
GeneralRe: Error reading from pipe. Internal error: 109 Pin
User 296361519-Jun-12 2:44
User 296361519-Jun-12 2:44 
AnswerRe: Error reading from pipe. Internal error: 109 Pin
Autoeng1-Aug-14 9:16
Autoeng1-Aug-14 9:16 
GeneralRe: Error reading from pipe. Internal error: 109 Pin
dmbrider15-Feb-08 3:00
dmbrider15-Feb-08 3:00 

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.