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. By request, I have added a VB.NET Windows Forms sample.

Running the Sample

The PipeClient and PipeServer are two separate projects in the NamedPipes solution sample. Run the PipeServer.exe, before sending strings from PipeClient.

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.

Visual BasicC#
Edit|Remove
Imports System 
Imports System.IO.Pipes 
Imports System.Text 
Imports System.Threading 
 
''' <summary> 
''' PipeServer creates a listener thread and waits for messages from clients. 
''' Received messages are displayed in Textbox "tbox" 
''' </summary> 
''' <remarks></remarks> 
Public Class Form1 
    Private Sub Form1_Load(ByVal sender As Object, _ 
                           ByVal e As System.EventArgsHandles MyBase.Load 
        tbox.Text = "" 
        PipeServer.pipeName = "testpipe" 
        PipeServer.owner = Me 
        Dim pipeThread As ThreadStart = _ 
                         New ThreadStart(AddressOf PipeServer.createPipeServer) 
        Dim listenerThread As Thread = New Thread(pipeThread) 
        listenerThread.SetApartmentState(ApartmentState.STA) 
        listenerThread.IsBackground = True 
        listenerThread.Start() 
    End Sub 
 
End Class

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.

Visual BasicC#
Edit|Remove
Public Class PipeServer 
    Public Shared owner As Form1 
    Public Shared pipeName As String 
    Private Shared pipeServer As NamedPipeServerStream 
    Private Shared ReadOnly BufferSize As Integer = 256 
 
    Delegate Sub SetTextboxDelegate(ByVal text As String) 
    Private Shared doSetTextBox As SetTextboxDelegate 
 
    Private Shared Sub SetTextbox(ByVal text As String) 
        Dim tb As TextBox = owner.tbox 
 
        tb.Text = String.Concat(tb.Text, text) 
        ' Scroll to the bottom of the textbox 
        tb.SelectionStart = tb.Text.Length - 2 
        tb.ScrollToCaret() 
    End Sub 
 
    Public Shared Sub createPipeServer() 
        Dim decdr As Decoder = Encoding.Default.GetDecoder() 
        Dim bytes(BufferSize) As Byte 
        Dim chars(BufferSize) As Char 
        Dim numBytes As Integer = 0 
        Dim msg As StringBuilder = New StringBuilder() 
        doSetTextBox = New SetTextboxDelegate(AddressOf SetTextbox) 
        Try 
            pipeServer = New NamedPipeServerStream(pipeName, PipeDirection.In1, _ 
                                                   PipeTransmissionMode.Message, _ 
                                                   PipeOptions.Asynchronous) 
            While True 
                pipeServer.WaitForConnection() 
                Do 
                    msg.Length = 0 
                    Do 
                        numBytes = pipeServer.Read(bytes, 0, BufferSize) 
                        If numBytes > 0 Then 
                            Dim numChars As Integer = _ 
                                         decdr.GetCharCount(bytes, 0, numBytes) 
                            decdr.GetChars(bytes, 0, numBytes, chars, 0False) 
                            msg.Append(chars, 0, numChars) 
                        End If 
                    Loop Until (numBytes = 0Or (pipeServer.IsMessageComplete) 
                    decdr.Reset() 
                    If numBytes > 0 Then 
                        owner.Invoke(doSetTextBox, New Object() {msg.ToString()}) 
                    End If 
                Loop Until numBytes = 0 
                pipeServer.Disconnect() 
            End While 
 
        Catch ex As Exception 
            MessageBox.Show(ex.Message) 
        End Try 
    End Sub 
 
End Class

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.

Visual BasicC#
Edit|Remove
Imports System.IO 
Imports System.IO.Pipes 
 
Public Class Form1 
 
    Private Sub button1_Click(ByVal sender As System.ObjectByVal e As System.EventArgsHandles button1.Click 
        Using pipeClient As NamedPipeClientStream = New NamedPipeClientStream(".""testpipe", _ 
                                                                        PipeDirection.Out, _ 
                                                                 PipeOptions.Asynchronous) 
            tbStatus.Text = "Attempting to connect to pipe..." 
            Try 
                pipeClient.Connect(2000) 
            Catch ex As Exception 
                MessageBox.Show("The Pipe server must be started in order to send data to it.") 
                Return 
            End Try 
            tbStatus.Text = "Connected to pipe." 
            Using sw As StreamWriter = New StreamWriter(pipeClient) 
                sw.WriteLine(tbClientText.Text) 
            End Using 
        End Using 
        tbStatus.Text = "" 
    End Sub 
 
    Private Sub btnClose_Click(ByVal sender As System.ObjectByVal e As System.EventArgsHandles btnClose.Click 
        Me.Close() 
    End Sub 
 
End Class
 

Source Code Files

WPF C# sample

Windows Forms VB.NET sample

More Information

NamedPipeServerStream Class - http://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeserverstream.aspx

NamedPipeClientStream Class - http://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeclientstream.aspx