11

I'm trying to use named pipes for the first time. In the MS documentation found here, it states that:

EndWaitForConnection must be called exactly once for every call to BeginWaitForConnection.

So I'm trying to be a good little programmer and follow documentation, but the EndWaitForConnection() just hangs indefinitely when I use it.

So I stripped down my code to a bare minimum so see if I could isolate the problem but no dice. I've pulled the following code out of a class I've written. I've modified it so that it begins waiting on a pipe connection then immediately tries to stop waiting on that pipe connection:

private void WaitForConnectionCallBack(IAsyncResult result)
{

}

public void Start()
{
    var tempPipe = new NamedPipeServerStream("TempPipe",
                                             PipeDirection.In,
                                             254, 
                                             PipeTransmissionMode.Message,
                                             PipeOptions.Asynchronous);

    IAsyncResult result = tempPipe.BeginWaitForConnection(
                                    new AsyncCallback(WaitForConnectionCallBack), this);

    tempPipe.EndWaitForConnection(result);  // <----- Hangs on this line right here
}

1) Why does it hang on EndWaitForConnection()? If I want to shut down my server before I've received a connection, how can I essentially cancel this BeginWaitForConnection() callback?

2) Let's suppose that I did not have the above mentioned problem. What happens if 2 clients try to connect to my named pipe very quickly?

Do I get a callback invocation for each of them, or do I have to wait to receive the first connection notification then quickly call EndWaitForConnection() then WaitForConnectionCallBack() again to start listening for the next client again?

The latter seems like a race condition to me, because I may not set up the connection listener fast enough.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Ultratrunks
  • 2,464
  • 5
  • 28
  • 48
  • By design, that call should only be used in your callback method (WaitForConnectionCallBack). You cancel it by calling tempPipe.Close(). – Hans Passant Feb 07 '12 at 02:00
  • Yes, I sort of happened upon that conclusion myself. I basically found that calling tempPipe.Close() gets the callback routine moving immediately, problem is, I did have it set up to immediately call EndWaitForConnection, but since the pipe is closed by then, it throws an exception. So i had to wrap a try statement around that and do nothing in the catch statement. Is this the correct solution? It seems a little sloppy to me to close the pipe, knowing it will force an exception in your callback that you'll have to catch. – Ultratrunks Feb 07 '12 at 06:10
  • @HansPassant I was just coding up essentially this same thing, and I too, was surprised by that `ObjectDisposedException` in the callback. Since when is handling an exception the "expected" way of doing things? I'm not saying you're wrong, but does this make sense? Is this the way it *should* work? – Jonathon Reinhart Dec 16 '12 at 05:12
  • 1
    Yes, it is entirely normal. The async call completed abnormally, the exception says why. Don't catch them all like the answer did, only catch ODE. – Hans Passant Dec 16 '12 at 07:05

1 Answers1

10

So, a basic skeleton of the solution that is working for me is as follows:

private void WaitForConnectionCallBack(IAsyncResult result)
{
    try
    {
        PipeServer.EndWaitForConnection(result);

        /// ...
        /// Some arbitrary code
        /// ...
    }
    catch
    {
        // If the pipe is closed before a client ever connects,
        // EndWaitForConnection() will throw an exception.

        // If we are in here that is probably the case so just return.
        return;
    }
}

Here is the Server code.

public void Start()
{
    var server= new NamedPipeServerStream("TempPipe", 
                                          PipeDirection.In,
                                          254, 
                                          PipeTransmissionMode.Message, 
                                          PipeOptions.Asynchronous);

    // If nothing ever connects, the callback will never be called.
    server.BeginWaitForConnection(new AsyncCallback(WaitForConnectionCallBack), this);

    // ... arbitrary code

// EndWaitForConnection() was not the right answer here, it would just wait indefinitely
// if you called it.  As Hans Passant mention, its meant to be used in the callback. 
// Which it now is. Instead, we are going to close the pipe.  This will trigger 
// the callback to get called.  

// However, the EndWaitForConnection() that will excecute in the callback will fail
// with an exception since the pipe is closed by time it gets invoked, 
// thus you must capture it with a try/catch

    server.Close(); // <--- effectively closes our pipe and gets our 
                        //       BeginWaitForConnection() moving, even though any future 
                        //       operations on the pipe will fail.
}
ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Ultratrunks
  • 2,464
  • 5
  • 28
  • 48
  • FYI See [C# UnauthorizedAccessException when enabling MessageMode for read-only named pipe (NamedPipeClientStream class)](http://stackoverflow.com/questions/32739224/c-sharp-unauthorizedaccessexception-when-enabling-messagemode-for-read-only-name) for other information on using `Message` mode. – ΩmegaMan Apr 01 '17 at 19:19