2

I have a C++ and C# applications where I send command to each one other by using named pipes. It was working well until I noticied I couldn't cancel the Read() call, I was using a stop variable but didn't notice this wasn't all I need because it couldn't read the stop variable state until get off the Read() call. I found I would use PIPE_NOWAIT attribute in the CreateNamedPipe() call. When I added it the C# throw an System.NullReferenceException because the FileStream was null, it's created from new FileStream(clientHandle, FileAccess.ReadWrite, BUFFER_SIZE, true); where clientHandle is created as following:

   private void Listen()
        {
            while (!exitLoop)
            {
                clientHandle = CreateNamedPipe(this.pipeName,
                                                DUPLEX | FILE_FLAG_OVERLAPPED,
                                                PIPE_NOWAIT,
                                                255,
                                                BUFFER_SIZE,
                                                BUFFER_SIZE,
                                                0,
                                                IntPtr.Zero);

                if (clientHandle.IsInvalid)
                {
                    return;
                }

                int ok = ConnectNamedPipe(clientHandle, IntPtr.Zero);

                //could not connect client
                if (ok == 0) // here's the error, getLastError() = ERROR_PIPE_LISTENING
                {
                    return;
                }
                 stream = new FileStream(clientHandle, FileAccess.ReadWrite, BUFFER_SIZE, true);
              // ....
        }

If matter, in C++ the pipe is created like this:

 hPipe1 = CreateFile(lpszPipename1,
                       GENERIC_WRITE,
                       0,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_OVERLAPPED,
                       NULL);
    if (!IsValidPipe(hPipe1))
    {
         openError();
         return;
    }

    hPipe2 = CreateFile(lpszPipename2,
                       GENERIC_READ,
                       0,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_OVERLAPPED,
                       NULL);

So my question is: the error is ERROR_PIPE_LISTENING after ConnectNamedPipe() call, happend after I did add PIPE_NOWAIT. Why is that error? how do I fix this? and this the right way to add support to cancel a named-pipe operation? I would kill the theread where Listen() is running in but I read it isn't a good practive (it doesn't even work either).

NOTE: I'm working on existing code base and I would like to avoid rewrite everything using NamedPipeClientStream for time reasons.

Jack
  • 16,276
  • 55
  • 159
  • 284
  • In non-blocking mode, ERROR_PIPE_LISTENING is the normal, documented behaviour; it means "no client is attempting to connect at present, try again later". But you're not supposed to use both nonblocking mode and asynchronous mode at the same time anyway. Asynchronous mode is the preferred choice, even more so in this case since the FileStream class is unlikely to support nonblocking mode. – Harry Johnston Oct 21 '15 at 20:56
  • The C# application, the client, is opened. That's why I don't get the `ConnectNamedPipe()` return value. This works just fine if I remove the `PIPE_NOWAIT` atribute and use 0 (same as `PIPE_WAIT`, default value). I tried this (http://pastebin.com/03GhwRnJ) just to see if it gets connect after a while trying, but it didn't. I'll try to clean up the mess of using asynchronous mode and nonblocking mode at same time... – Jack Oct 21 '15 at 21:25
  • That pastebin code won't work because the call returns zero either way, either with ERROR_PIPE_LISTENING (no client yet) or ERROR_PIPE_CONNECTED (success!) and you aren't distinguishing between the two cases. But it's pretty much moot; you really do need to remove `PIPE_NOWAIT`. Hopefully mksteve's answer will provide you with an alternative. – Harry Johnston Oct 21 '15 at 21:29
  • Just to see what was last Windows error code after each `ConnectNamedPipe()` I ran this(http://pastebin.com/Umk4R4hg). I get `ERROR_PIPE_LISTENING` for a while then `ERROR_PIPE_CONNECTED`. If break the loop if windows last error code is `ERROR_PIPE_CONNECTED` I get an exception later on `stream.Read()` call. I'm remove the `PIPE_NOWAIT` and try mksteve's answer. – Jack Oct 21 '15 at 21:56

2 Answers2

1

In C++ you need to create the file with overlapped io, then WaitForMultipleObjects(..., INFINITE); for a stop event and the IO.

If you get the stop event, then you CancelIO();.

For C# msdn : Overlapped allows you to create an overlapped object (necessary for the read).

stackoverflow : pinvoke ReadFile. This shows how to natively call ReadFile with the overlapped option.

stackoverflow : waitformultipleobjects functionality Explains how to call WaitForMultipleObjects.

When solving this sort of problem I created a stand-alone function

ReadFileWithEvent( HANDLE file, VOID * pData, size_t data, HANDLE exitEvent, BOOL & signalled );

which packaged creating an overlapped object, waiting and explaining to the caller that the stop had occurred, and the Read had not completed. This simplified the code I needed.

Community
  • 1
  • 1
mksteve
  • 12,614
  • 3
  • 28
  • 50
0

I solved this with PeekNamedPipe(), I get the number total of bytes available and call ReadFile() only if it's > 0. This is a simple approach to emulate nonblocking mode and I can exit the loop running inside a thread just setting done to true.

Something like this:

   while(!done) {
            DWORD total_available_bytes = 0;
            if(!PeekNamedPipe(hPipe2, NULL, 0, NULL, &total_available_bytes, NULL)) {
                /* error handling goes here */
                break;
            }

            // for avoid overuse of the CPU, sleep for a while until next check.
            if(total_available_bytes == 0) {
                Sleep(500);
                continue;
            }

           if(ReadFile(hPipe2, ...)) {
             // ...
           }
}
Jack
  • 16,276
  • 55
  • 159
  • 284