Click here to Skip to main content
15,886,799 members
Articles / Desktop Programming / Win32

Inter-Process Communication (IPC) Introduction and Sample Code

Rate me:
Please Sign up or sign in to vote.
4.91/5 (57 votes)
19 Dec 2009Ms-PL8 min read 251.3K   12.2K   195  
This article will cover general IPC technologies in All-In-One Code Framework. The IPC technologies include Named Pipes, File Mapping, MailSlot, etc.
/****************************** Module Header ******************************\
* Module Name:	Program.cs
* Project:		CSNamedPipeServer
* Copyright (c) Microsoft Corporation.
* 
* Named pipe is a mechanism for one-way or bi-directional inter-process 
* communication between the pipe server and one or more pipe clients in the
* local machine or across the computers in the intranet:
* 
* PIPE_ACCESS_INBOUND:
* Client (GENERIC_WRITE) ---> Server (GENERIC_READ)
* 
* PIPE_ACCESS_OUTBOUND:
* Client (GENERIC_READ) <--- Server (GENERIC_WRITE)
* 
* PIPE_ACCESS_DUPLEX:
* Client (GENERIC_READ or GENERIC_WRITE, or both) <--> 
* Server (GENERIC_READ and GENERIC_WRITE)
* 
* .NET supports named pipes in two ways:
* 
* 1. P/Invoke the native APIs.
* 
* By P/Invoke-ing the native APIs from .NET, we can mimic the code logic  
* in CppNamedPipeServer to create the named pipe server, \\.\pipe\HelloWorld,   
* that supports PIPE_ACCESS_DUPLEX.
* 
* PInvokeNativePipeServer first creates such a named pipe, then it listens to 
* the client's connection. When a client is connected, the server attempts to 
* read the client's requests from the pipe and write a response.
* 
* 2. System.IO.Pipes Namespace
* 
* In .NET Framework 3.5, the namespace System.IO.Pipes and a set of classes 
* (e.g. PipeStream, NamedPipeServerStream) are added to .NET BCL. These 
* classes make the programming of named pipe in .NET much easier and safer 
* than P/Invoke-ing the native APIs directly.
* 
* BCLSystemIOPipeServer first creates such a named pipe, then it listens to  
* the client's connection. When a client is connected, the server attempts to 
* read the client's requests from the pipe and write a response.
* 
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
* 
* History:
* * 1/27/2009 8:31 PM Jialiang Ge Created
* * 3/3/2009 10:10 AM Riquel Dong Added BCLSystemIOPipe
* * 3/3/2009 1:24 PM Jialiang Ge Reviewed
\***************************************************************************/

#region Using directives
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO.Pipes;
using System.IO;
using System.Security.AccessControl;
#endregion


class Program
{
    const int BUFFER_SIZE = 4096;  // 4 KB

    static void Main(string[] args)
    {
        PInvokeNativePipeServer();

        // [-or-]

        //BCLSystemIOPipeServer();
    }

    /// <summary>
    /// Named pipe server through P/Invoke-ing the native APIs
    /// </summary>
    static void PInvokeNativePipeServer()
    {
        /////////////////////////////////////////////////////////////////////
        // Create a named pipe.
        // 

        // Prepare the pipe name
        String strPipeName = String.Format(@"\\{0}\pipe\{1}",
            ".",                // Server name
            "HelloWorld"        // Pipe name
            );

        // Prepare the security attributes

        IntPtr pSa = IntPtr.Zero;   // NULL
        SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();

        SECURITY_DESCRIPTOR sd;
        SecurityNative.InitializeSecurityDescriptor(out sd, 1);
        // DACL is set as NULL to allow all access to the object.
        SecurityNative.SetSecurityDescriptorDacl(ref sd, true, IntPtr.Zero, false);
        sa.lpSecurityDescriptor = Marshal.AllocHGlobal(Marshal.SizeOf(
            typeof(SECURITY_DESCRIPTOR)));
        Marshal.StructureToPtr(sd, sa.lpSecurityDescriptor, false);
        sa.bInheritHandle = false;              // Not inheritable
        sa.nLength = Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES));

        pSa = Marshal.AllocHGlobal(sa.nLength);
        Marshal.StructureToPtr(sa, pSa, false);

        // Create the named pipe.
        IntPtr hPipe = PipeNative.CreateNamedPipe(
            strPipeName,                        // The unique pipe name.
            PipeOpenMode.PIPE_ACCESS_DUPLEX,    // The pipe is bi-directional
            PipeMode.PIPE_TYPE_MESSAGE |        // Message type pipe 
            PipeMode.PIPE_READMODE_MESSAGE |    // Message-read mode 
            PipeMode.PIPE_WAIT,                 // Blocking mode is on
            PipeNative.PIPE_UNLIMITED_INSTANCES,// Max server instances
            BUFFER_SIZE,                        // Output buffer size
            BUFFER_SIZE,                        // Input buffer size
            PipeNative.NMPWAIT_USE_DEFAULT_WAIT,// Time-out interval
            pSa                                 // Pipe security attributes
            );

        if (hPipe.ToInt32() == PipeNative.INVALID_HANDLE_VALUE)
        {
            Console.WriteLine("Unable to create named pipe {0} w/err 0x{1:X}",
                strPipeName, PipeNative.GetLastError());
            return;
        }
        Console.WriteLine("The named pipe, {0}, is created.", strPipeName);


        /////////////////////////////////////////////////////////////////////
        // Wait for the client to connect.
        // 

        Console.WriteLine("Waiting for the client's connection...");

        bool bConnected = PipeNative.ConnectNamedPipe(hPipe, IntPtr.Zero) ?
            true : PipeNative.GetLastError() == PipeNative.ERROR_PIPE_CONNECTED;

        if (!bConnected)
        {
            Console.WriteLine(
                "Error occurred while connecting to the client: 0x{0:X}",
                PipeNative.GetLastError());
            PipeNative.CloseHandle(hPipe);      // Close the pipe handle.
            return;
        }


        /////////////////////////////////////////////////////////////////////
        // Read client requests from the pipe and write the response.
        // 

        // A byte buffer of BUFFER_SIZE bytes. The buffer should be big 
        // enough for ONE request from a client.

        string strMessage;
        byte[] bRequest = new byte[BUFFER_SIZE];// Client -> Server
        uint cbBytesRead, cbRequestBytes;
        byte[] bReply;                          // Server -> Client
        uint cbBytesWritten, cbReplyBytes;

        bool bResult;

        while (true)
        {
            // Receive one message from the pipe.

            cbRequestBytes = BUFFER_SIZE;
            bResult = PipeNative.ReadFile(      // Read from the pipe.
                hPipe,                          // Handle of the pipe
                bRequest,                       // Buffer to receive data
                cbRequestBytes,                 // Size of buffer in bytes
                out cbBytesRead,                // Number of bytes read
                IntPtr.Zero);                   // Not overlapped I/O

            if (!bResult/*Failed*/ || cbBytesRead == 0/*Finished*/)
                break;

            // Unicode-encode the byte array and trim all the '\0' chars at 
            // the end.
            strMessage = Encoding.Unicode.GetString(bRequest).TrimEnd('\0');
            Console.WriteLine("Receives {0} bytes; Message: \"{1}\"",
                cbBytesRead, strMessage);

            // Prepare the response.

            // '\0' is appended in the end because the client may be a native
            // C++ program.
            strMessage = "Default response from server\0";
            bReply = Encoding.Unicode.GetBytes(strMessage);
            cbReplyBytes = (uint)bReply.Length;

            // Write the response to the pipe.

            bResult = PipeNative.WriteFile(     // Write to the pipe.
                hPipe,                          // Handle of the pipe
                bReply,                         // Buffer to write to 
                cbReplyBytes,                   // Number of bytes to write 
                out cbBytesWritten,             // Number of bytes written 
                IntPtr.Zero);                   // Not overlapped I/O 

            if (!bResult/*Failed*/ || cbReplyBytes != cbBytesWritten/*Failed*/)
            {
                Console.WriteLine("WriteFile failed w/err 0x{0:X}",
                    PipeNative.GetLastError());
                break;
            }

            Console.WriteLine("Replies {0} bytes; Message: \"{1}\"",
                cbBytesWritten, strMessage.TrimEnd('\0'));
        }


        /////////////////////////////////////////////////////////////////////
        // Flush the pipe to allow the client to read the pipe's contents 
        // before disconnecting. Then disconnect the pipe, and close the 
        // handle to this pipe instance.
        // 

        PipeNative.FlushFileBuffers(hPipe);
        PipeNative.DisconnectNamedPipe(hPipe);
        PipeNative.CloseHandle(hPipe);
    }

    /// <summary>
    /// Named pipe server through BCL System.IO.Pipes
    /// </summary>
    static void BCLSystemIOPipeServer()
    {
        NamedPipeServerStream pipeServer = null;

        try
        {
            /////////////////////////////////////////////////////////////////
            // Create a named pipe.
            // 

            // Prepare the pipe name       
            String strPipeName = "HelloWorld";

            // Prepare the security attributes
            // Granting everyone the full control of the pipe is just for 
            // demo purpose, though it creates a security hole.
            PipeSecurity pipeSa = new PipeSecurity();
            pipeSa.SetAccessRule(new PipeAccessRule("Everyone", 
                PipeAccessRights.ReadWrite, AccessControlType.Allow));

            // Create the named pipe
            pipeServer = new NamedPipeServerStream(
                strPipeName,                    // The unique pipe name.
                PipeDirection.InOut,            // The pipe is bi-directional
                NamedPipeServerStream.MaxAllowedServerInstances,
                PipeTransmissionMode.Message,   // Message type pipe 
                PipeOptions.None,               // No additional parameters
                BUFFER_SIZE,                    // Input buffer size
                BUFFER_SIZE,                    // Output buffer size
                pipeSa,                         // Pipe security attributes
                HandleInheritability.None       // Not inheritable
                );

            Console.WriteLine("The named pipe, {0}, is created", strPipeName);
            

            /////////////////////////////////////////////////////////////////
            // Wait for the client to connect.
            // 

            Console.WriteLine("Waiting for the client's connection...");
            pipeServer.WaitForConnection();

            /////////////////////////////////////////////////////////////////
            // Read client requests from the pipe and write the response.
            // 

            // A byte buffer of BUFFER_SIZE bytes. The buffer should be big 
            // enough for ONE request from a client.

            string strMessage;
            byte[] bRequest = new byte[BUFFER_SIZE];// Client -> Server
            int cbBytesRead, cbRequestBytes;
            byte[] bReply;                          // Server -> Client
            int cbBytesWritten, cbReplyBytes;

            do
            {
                // Receive one message from the pipe.

                cbRequestBytes = BUFFER_SIZE;
                cbBytesRead = pipeServer.Read(bRequest, 0, cbRequestBytes);

                // Unicode-encode the byte array and trim all the '\0' chars 
                // at the end.
                strMessage = Encoding.Unicode.GetString(bRequest).TrimEnd('\0');
                Console.WriteLine("Receives {0} bytes; Message: \"{1}\"",
                    cbBytesRead, strMessage);

                // Prepare the response.

                // '\0' is appended in the end because the client may be a  
                // native C++ program.
                strMessage = "Default response from server\0";
                bReply = Encoding.Unicode.GetBytes(strMessage);
                cbReplyBytes = bReply.Length;

                // Write the response to the pipe.

                pipeServer.Write(bReply, 0, cbReplyBytes);
                // If no IO exception is thrown from Write, number of bytes 
                // written (cbBytesWritten) != -1.
                cbBytesWritten = cbReplyBytes;

                Console.WriteLine("Replies {0} bytes; Message: \"{1}\"",
                    cbBytesWritten, strMessage.TrimEnd('\0'));

            }
            while (!pipeServer.IsMessageComplete);


            /////////////////////////////////////////////////////////////////
            // Flush the pipe to allow the client to read the pipe's contents 
            // before disconnecting. Then disconnect the pipe.
            // 

            pipeServer.Flush();
            pipeServer.Disconnect();

        }
        catch (Exception ex)
        {
            Console.WriteLine("The server throws the error: {0}", ex.Message);
        }
        finally
        {
            if (pipeServer != null)
            {
                // Close the stream.
                pipeServer.Close();
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
China China
Microsoft All-In-One Code Framework delineates the framework and skeleton of Microsoft development techniques through typical sample codes in three popular programming languages (Visual C#, VB.NET, Visual C++). Each sample is elaborately selected, composed, and documented to demonstrate one frequently-asked, tested or used coding scenario based on our support experience in MSDN newsgroups and forums. If you are a software developer, you can fill the skeleton with blood, muscle and soul. If you are a software tester or a support engineer like us, you may extend the sample codes a little to fit your specific test scenario or refer your customer to this project if the customer's question coincides with what we collected.
http://cfx.codeplex.com/

Comments and Discussions