Home About

November 20th, 2008

Using Named Pipes in C# / Windows - 1

Communication between different threads in a process is trivial as they share the same objects in memory. But what if you would like to communicate with a different process (program) on the same computer? You could open a TCP/IP port to share data but the added overhead of this would slow down your program if you send a lot of data.

Traditionally you would use a pipe: a pseudo file that allows one process to write and another to process to read. This is very similar to reading and writing from a file. This is more efficient as the information is passed straight through the kernel between processes and avoids the network stack overhead.

There are two kinds of pipes:

Anonymous pipes

These are typically used for communication between a parent and child process. As the pipe does not have a name we need to be able to pass a handle to it from one process to another. Typically this is done by passing this handle as an argument when starting the child process.

Named pipes

Named pipes are more generally useful. If a process is aware of the name of a pipe it can connect to it. There is no need for an immediate parent / child relationship. As long as the parent process makes the pipe available, other processes can connect to it.

Pipes and Windows

The Windows OS offers a solid set of named and unnamed pipe function calls in the kernel. But for some reason Microsoft didn’t actually include pipes in C# until .NET 3.5. The support for pipes is now included in the System.IO.Pipes class. In the intervening years enterprising programmers created their own set of non-portable bindings to the Kernel32 calls and on Linux the Mono teambuild their own set of calls to support Unix sockets in the Mono.Unix.UnixPipes class.

As a result there is currently no portable way to implement pipes under both .NET/Windows and Linux/Mono. In the following example I show you how to create a named pipe for Windows.

Because of the nature of a named pipe — we are going to need two programs: one that establishes the pipe, and another one that reads from the pipe.

Example: Named Pipes in Visual C# / .NET 3.5

With the introduction of System.IO.Pipes in .NET 3.5 creating and connecting to a pipe has become very straightforward.

  • NamedPipeServerStream creates a pipe
  • NamedPipeClientStream connects to an existing pipe

In the below example we simulate the communication between two processes by creating two threads instead. This makes debugging this example easier but in reality this could also have been an example of communication between two separate processes on the same computer.

We also make implicity use of the fact that NamedPipeClientStream.Connect() will wait for the pipe to be created if it can’t find it on its first try. If this is not acceptable it is also possible to call Connect with a time-out in milliseconds — if the pipe is not created in the time specified the function will fail.

using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;

namespace PipeApplication1
{
    class ProgramPipeTest
    {

        public void ThreadStartServer()
        {
            // Create a name pipe
            using (NamedPipeServerStream pipeStream = new NamedPipeServerStream("mytestpipe"))
            {
                Console.WriteLine("[Server] Pipe created {0}", pipeStream.GetHashCode());
               
                // Wait for a connection
                pipeStream.WaitForConnection();
                Console.WriteLine("[Server] Pipe connection established");

                using (StreamReader sr = new StreamReader(pipeStream))
                {
                    string temp;
                    // We read a line from the pipe and print it together with the current time
                    while ((temp = sr.ReadLine()) != null)
                    {
                        Console.WriteLine("{0}: {1}", DateTime.Now, temp);
                    }
                }
            }

            Console.WriteLine("Connection lost");
        }

        public void ThreadStartClient(object obj)
        {
            // Ensure that we only start the client after the server has created the pipe
            ManualResetEvent SyncClientServer = (ManualResetEvent)obj;

            // Only continue after the server was created -- otherwise we just fail badly
            // SyncClientServer.WaitOne();

            using (NamedPipeClientStream pipeStream = new NamedPipeClientStream("mytestpipe"))
            {
                // The connect function will indefinately wait for the pipe to become available
                // If that is not acceptable specify a maximum waiting time (in ms)
                pipeStream.Connect();

                Console.WriteLine("[Client] Pipe connection established");
                using (StreamWriter sw = new StreamWriter(pipeStream))
                {
                    sw.AutoFlush = true;
                    string temp;
                    Console.WriteLine("Please type a message and press [Enter], or type 'quit' to exit the program");
                    while ((temp = Console.ReadLine()) != null)
                    {
                        if (temp == "quit") break;
                        sw.WriteLine(temp);
                    }
                }
            }
        }

        static void Main(string[] args)
        {

            // To simplify debugging we are going to create just one process, and have two tasks
            // talk to each other. (Which is a bit like me sending an e-mail to my co-workers)
           
            ProgramPipeTest Server = new ProgramPipeTest();
            ProgramPipeTest Client = new ProgramPipeTest();
           
            Thread ServerThread = new Thread( Server.ThreadStartServer );
            Thread ClientThread = new Thread(Client.ThreadStartClient);

            ServerThread.Start();
            ClientThread.Start();
        }
    }
}

Image credit: TanakaWho

Be Sociable, Share!

Tags: , ,

One Response to “Using Named Pipes in C# / Windows”

  1. Tomas Says:

    A big THANK YOU for your example, which showed me a way to get rid of a hanging-up problem, probably caused by using the Peak() method. The solution simply was not to use it :)


Most popular
Recent Comments
  • ARS: great plugin! I love it! but, it will be so nice if you can add attribute ‘title’ as one of...
  • Nelson: Saved me from doing it myself. Good article.
  • andy: i am currently playing taiwanese server wow in 奈辛瓦里(PVP) and i would like to realm transfer to somewhere there...
  • berties: any english speaking playing on a taiwanese server?
  • web application development: has C# search volume really so constant over the years? really surprising.