Jack
Jack

Reputation: 16724

How do I cancel a PIPE operation?

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.

Upvotes: 2

Views: 1296

Answers (2)

Jack
Jack

Reputation: 16724

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, ...)) {
             // ...
           }
}

Upvotes: 0

mksteve
mksteve

Reputation: 13073

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.

Upvotes: 1

Related Questions