Named Pipes - Asynchronous Peeking

前端 未结 2 1275
挽巷
挽巷 2021-02-08 17:45

I need to find a way to be notified when a System.IO.Pipe.NamedPipeServerStream opened in asynchronous mode has more data available for reading on it- a WaitHandle would be idea

2条回答
  •  日久生厌
    2021-02-08 18:20

    Ok, so I just ripped this out of my code, hopefully I deleted all the application logic stuff. The idea is that you try a zero-length read with ReadFile and wait on both the lpOverlapped.EventHandle (fired when the read completes) and a WaitHandle set when another thread wants to write to the pipe. If the read is to be interrupted due to a writing thread, use CancelIoEx to cancel the zero-length read.

    NativeOverlapped lpOverlapped;
    ManualResetEvent DataReadyHandle = new ManualResetEvent(false);
    lpOverlapped.InternalHigh = IntPtr.Zero;
    lpOverlapped.InternalLow = IntPtr.Zero;
    lpOverlapped.OffsetHigh = 0;
    lpOverlapped.OffsetLow = 0;
    lpOverlapped.EventHandle = DataReadyHandle.SafeWaitHandle.DangerousGetHandle();
    IntPtr x = Marshal.AllocHGlobal(1); //for some reason, ReadFile doesnt like passing NULL in as a buffer
    bool rval = ReadFile(SerialPipe.SafePipeHandle, x, 0, IntPtr.Zero,
       ref lpOverlapped);
    int BreakCause;
    if (!rval) //operation is completing asynchronously
    {
       if (GetLastError() != 997) //ERROR_IO_PENDING, which is in fact good
          throw new IOException();
       //So, we have a list of conditions we are waiting for
       WaitHandle[] BreakConditions = new WaitHandle[3];
       //We might get some input to read from the serial port...
       BreakConditions[0] = DataReadyHandle;
        //we might get told to yield the lock so that CPU can write...
       BreakConditions[1] = WriteRequiredSignal;
       //or we might get told that this thread has become expendable
       BreakConditions[2] = ThreadKillSignal;
       BreakCause = WaitHandle.WaitAny(BreakConditions, timeout);
    }
    else //operation completed synchronously; there is data available
    {
       BreakCause = 0; //jump into the reading code in the switch below
    }
    switch (BreakCause)
    {
       case 0:
          //serial port input
          byte[] Buffer = new byte[AttemptReadSize];
          int BRead = SerialPipe.Read(Buffer, 0, AttemptReadSize);
          //do something with your bytes.
          break;
       case 1:
          //asked to yield
          //first kill that read operation
          CancelIoEx(SerialPipe.SafePipeHandle, ref lpOverlapped);
          //should hand over the pipe mutex and wait to be told to tkae it back
          System.Threading.Monitor.Exit(SerialPipeLock);
          WriteRequiredSignal.Reset();
          WriteCompleteSignal.WaitOne();
          WriteCompleteSignal.Reset();
          System.Threading.Monitor.Enter(SerialPipeLock);
          break;
       case 2:
          //asked to die
          //we are the ones responsible for cleaning up the pipe
          CancelIoEx(SerialPipe.SafePipeHandle, ref lpOverlapped);
          //finally block will clean up the pipe and the mutex
          return; //quit the thread
    }
    Marshal.FreeHGlobal(x);
    

提交回复
热议问题