Share via


Named Pipes IO for Inter-process Communication

Introduction

Use the .NET 3.5 Named Pipes IO for Inter-process communication by implementing a pipe listener on a separate thread. The first sample is a WPF project. It can also be done just as easily in Windows forms.

Description

The sample demonstrates how to pass string data from clients to a pipe server application that contains a pipe listener that runs on a separate thread within the server application. The main requirement here is that the clients are collecting string data and the other application wants to use the string data too, but it needs to collect the information in a background thread.

The code snippet below shows the main window class that starts the Pipeserver thread in the Window_Loaded event handler. In Forms, it is the Form1_Load event.

I like to package the thread-owner attributes into a class of all static members. Then it is easy for this class to operate on owner objects.

I create an Invoker object for WPF. In Windows Forms this is already implemented for each Form.

      namespace PipeServer 
{ 
  /// <summary> 
  /// PipeServer creates a listener thread and waits for messages from clients. 
  /// Received messages are displayed in Textbox 
  /// </summary> 
  public partial  class Window1 : Window 
  { 
    public Window1() 
    { 
      InitializeComponent(); 
    } 
  
    private void  Window_Loaded(object  sender, RoutedEventArgs e) 
    { 
      tbox.Text = ""; 
      Pipeserver.pipeName = "testpipe";  
      Pipeserver.owner = this; 
      Pipeserver.ownerInvoker = new  Invoker(this); 
      ThreadStart pipeThread = new  ThreadStart(Pipeserver.createPipeServer); 
      Thread listenerThread = new  Thread(pipeThread); 
      listenerThread.SetApartmentState(ApartmentState.STA); 
      listenerThread.IsBackground = true; 
      listenerThread.Start(); 
    } 
  }

Pipeserver.createPipeServer is the static thread method that is attached to the pipeThread delegate. Once the thread starts, createPipeServer runs in a continuous while loop which waits for a connection and processes data coming in on the pipe's stream.

The NamedPipeServerStream class has a complex set of constructors and properties. I'm using the 6th constructor and setting all the properties in this constructor. Here are the details on the class: http://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeserverstream.aspx

SetTextbox is the delegate that gets invoked on the main thread to update Textbox control.

I'm using a low-level read hear for flexibility. You could just as well be processing binary data (such as images) with it instead of text.

Note that it is important to Disconnect the PipeServerStream after processing each incomming message. Otherwise an error will be thrown when the process loops back to wait for the next connection.

After all of the bytes are collected as chars in the StringBuilder, the message is posted as a string using the Invoker class. Invoker has an Action<string> delegate (sDel) that runs a method delegate that takes a string parameter and is set to SetTexbox. In Windows Forms the doSetTextBox delegate object does the callback to run SetTexbox on the owner thread.

public class  Pipeserver 
{ 
  public static  Window1 owner; 
  public static  Invoker ownerInvoker; 
  public static  string pipeName; 
  private static  NamedPipeServerStream pipeServer; 
  private static  readonly int  BufferSize = 256; 
 
  private static  void SetTextbox(String text) 
  { 
    owner.tbox.Text = String.Concat(owner.tbox.Text, text); 
    if (owner.tbox.ExtentHeight > owner.tbox.ViewportHeight) 
    { 
      owner.tbox.ScrollToEnd(); 
    } 
  } 
 
  public static  void createPipeServer() 
  { 
    Decoder decoder = Encoding.Default.GetDecoder(); 
    Byte[] bytes = new  Byte[BufferSize]; 
    char[] chars = new  char[BufferSize]; 
    int numBytes = 0; 
    StringBuilder msg = new  StringBuilder(); 
    ownerInvoker.sDel = SetTextbox; 
 
    try
    { 
      pipeServer = new  NamedPipeServerStream(pipeName, PipeDirection.In, 1, 
                                      PipeTransmissionMode.Message,  
                                      PipeOptions.Asynchronous); 
      while (true) 
      { 
        pipeServer.WaitForConnection(); 
 
        do
        { 
          msg.Length = 0; 
          do
          { 
            numBytes = pipeServer.Read(bytes, 0, BufferSize); 
            if (numBytes > 0) 
            { 
              int numChars = decoder.GetCharCount(bytes, 0, numBytes);  
              decoder.GetChars(bytes, 0, numBytes, chars, 0, false); 
              msg.Append(chars, 0, numChars); 
            } 
          } while  (numBytes > 0 && !pipeServer.IsMessageComplete); 
          decoder.Reset(); 
          if (numBytes > 0) 
          { 
            ownerInvoker.Invoke(msg.ToString()); 
          } 
        } while  (numBytes != 0); 
        pipeServer.Disconnect(); 
      } 
    } 
    catch (Exception ex) 
    { 
      MessageBox.Show(ex.Message); 
    } 
  } 
}

PipeClient

Here is the button1_Click code snippet from the client project. All of the work in the demo client is done on the button click. A StreamWriter is simply connected to the NamedPipeClientStream, and the contents of the Textblock are written to the pipe stream for each button click.

private void  button1_Click(object  sender, RoutedEventArgs e) 
{ 
  using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "testpipe",  
                                              PipeDirection.Out, 
                                                 PipeOptions.Asynchronous)) 
  { 
    tbStatus.Text = "Attempting to connect to pipe..."; 
    try
    { 
      pipeClient.Connect(2000); 
    } 
    catch
    { 
      MessageBox.Show("The Pipe server must be started in order to send data to it."); 
      return; 
    } 
    tbStatus.Text = "Connected to pipe."; 
    using (StreamWriter sw = new StreamWriter(pipeClient)) 
    { 
      sw.WriteLine(tbClientText.Text); 
    } 
  } 
  tbStatus.Text = ""; 
}

For More Information

MSDN Code Samples Source Code
NamedPipeServerStream Class
NamedPipeClientStream Class