Click here to Skip to main content
Click here to Skip to main content
Go to top

Asynchronous Named Pipe Server Using Overlapped I/O in C#

, 20 Jul 2008
Rate this:
Please Sign up or sign in to vote.
Asynchronous named pipe server in C#.

Introduction

I recently read CLR via C# by J. Richter dedicated to the internals of .NET 2.0 and the C# peculiarities deriving from it. I’ve decided to practice a little and write my new application in C#. The part of this application I present here. It’s an asynchronous named pipe server using overlapped I/O plus a synchronous (or they also say blocking) named pipe client.

Problems encountered

The first problem encountered was the fact that the only example by John Korres of using overlapped structure written in VB.NET provides asynchronous calls for two Win32 API functions ConnectNamedPipe and DisconnectNamedPipe. Read and write from and to a named pipe server implemented in a blocking mode using the System.IO.FileStream .NET class. I tried to use the BeginRead and BeginWrite methods but it remained unclear how these two interact with the NativeOverlapped structure if they do interact at all. So eventually, I adopted this sample code written in C from MSDN.

And that’s how the second problem came up. After the code was re-written, I found out that the GetOverlappedResult Win32 API function works incorrectly due to the fact that the pointer to the overlapped structure is not fixed, i.e., you have to work with the unmanaged memory of the process to make it work like in plain old C. Investigating farther, I understood that all buffers for the ReadFile and WriteFile Win32 API functions must also use unmanaged memory. I decided to use pointers and compile the application with the unsafe option; that upset me a bit because my code looked like almost plain old C to me now. And that was not just the case. Byte* to IntPtr cast did not work correctly either. So I switched to using IntPtr instead for the overlapped structure and buffers that allowed me to remove this odious unsafe parameter. And I was rewarded. Now the adopted code worked exactly like the sample from MSDN.

The last but not the least problem which I have to mention here is permissions for the named pipe server. Using the default permissions won’t allow interprocess communication between two computers, so look up buildSecurityAttributes in NamedPipeServer.cs. I won’t dwell on it here since it’s not the main point of the article.

Using the code

The following code extract shows the main server loop and exposes the peculiarities of asynchronous programming. The actual overlapped magic happens when the ReadFile Win32 API inside of the selectOperation method returns FALSE and GetLastError returns ERROR_IO_PENDING which is not an error but a signal that a ReadFile asynchronous call has not succeeded to complete reading so we have to call the GetOverlappedResult Win32 API to find out if the Operating System has put cbRet bytes into mPipeServer[i].Pipe.chRequest. If it hasn’t, we call the DisconnectAndReconnect method.

Another scenario happens if the application runs under Visual Studio 2005, no matter if it’s the release or debug configuration. To actually witness the difference, uncomment MessageBox.Show(“GetOverlappedResult:...”) altogether with the line above and run the executable. Afterwards, run it under Visual Studio in the debug configuration. You might not encounter it though.

while (mListenning)
{
    i = (UInt32)WaitHandle.WaitAny(hEvents);
    // determines which pipe instance

    if (i < 0 || i > (mNumOfInstances - 1))
    {
        ThrowWin32Exception ("Index out of range.");
        return;
    }

    Debug.WriteLine("Instance -> " + i.ToString());

    #region ERROR_IO_PENDING == TRUE
    
    if (mPipeServer[i].Pipe.fPendingIO)
    {
        fSuccess = NativeMethods.GetOverlappedResult(
            mPipeServer[i].Pipe.hPipeInst,
            mPipeServer[i].Pipe.OverlappedPtr,
            out cbRet,
            false);
           //if (cbRet > 0)
           //     MessageBox.Show("GetOverlappedResult: " +
           //     Marshal.PtrToStringAnsi(mPipeServer[i].Pipe.chRequest));

        switch (mPipeServer[i].Pipe.dwState)
        {
            // Pending connect operation 
            case CONNECTING_STATE:
                Debug.WriteLine("CONNECTING_STATE");
                if (!fSuccess)
                    ThrowWin32Exception("CONNECTING_STATE_ERROR");
                //SetEventAsync(CONNECTING_STATE, mPipeServer[i].Pipe.chRequest,
                //              mPipeServer[i].Pipe.cbRead);
                //STEP #0
                mPipeServer[i].Pipe.dwState = READING_STATE;
                break;
            // Pending read operation 
            case READING_STATE:
                Debug.WriteLine("READING_STATE " + cbRet.ToString());
                if (!fSuccess || cbRet == 0)
                {
                    DebugWin32Exception("READING_STATE");
                    DisconnectAndReconnect(i);
                    continue;
                }
                //STEP #1
                //NB! If we got here and cbRet > 0
                //    the mPipeServer[i].Pipe.chRequest already has client data 
                mPipeServer[i].Pipe.dwState = WRITING_STATE;
                break;
            // Pending write operation 
            case WRITING_STATE:
                Debug.WriteLine("WRITING_STATE " + cbRet.ToString());
                if (!fSuccess || cbRet != mPipeServer[i].Pipe.cbToWrite)
                {
                    DebugWin32Exception("WRITING_STATE");
                    DisconnectAndReconnect(i);
                    continue;
                }
                mPipeServer[i].Pipe.dwState = READING_STATE;
                break;
            default:
                ThrowWin32Exception("runInstance - INVALID PIPE STATE");
                break;
        }

    } //IO_PENDING
    #endregion

    if (!selectOperation((Int32) i) )
    //does ReadFile or WriteFile depending on the pipe instance state
    {
        // An error occurred; disconnect from the client. 
        DisconnectAndReconnect(i);
    }
}

Points of interest

While testing the application, it turned out that it behaves differently when run under Visual Studio 2005.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Boris Kolesnikov
Software Developer (Senior) Unipost company (http://apeha.ru project)
Russian Federation Russian Federation
Perl, C/C++ developer

Comments and Discussions

 
QuestionException occurs when starting pipe server. PinmemberDuojiao Yan7-Dec-11 0:52 
GeneralThere is a bug with the encoding of the message. Pinmemberconsolejoker6-Feb-10 5:06 
GeneralProcess Hang impossible to detach or even Stop [modified] PinmemberAndyHo24-Jan-10 6:11 
GeneralFirst Read is missing?! PinmemberDirar H.21-Aug-09 8:44 
GeneralRe: First Read is missing?! PinmemberBoris Kolesnikov21-Aug-09 15:14 
GeneralThank you!! PinmemberDirar H.19-Aug-09 11:14 
QuestionServer --&gt; Client communication? PinmemberGreg Cadmes10-Sep-08 9:49 
AnswerRe: Server --&gt; Client communication? PinmemberBoris Kolesnikov10-Sep-08 21:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140905.1 | Last Updated 21 Jul 2008
Article Copyright 2008 by Boris Kolesnikov
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid