Named Pipes – Peeking asincrono

Ho bisogno di trovare un modo per essere avvisato quando un System.IO.Pipe.NamedPipeServerStream aperto in modalità asincrona ha più dati disponibili per la lettura su di esso- un WaitHandle sarebbe l’ideale. Non posso semplicemente usare BeginRead () per ottenere un handle di questo tipo perché è ansible che io possa essere segnalato da un altro thread che vuole scrivere sulla pipe, quindi devo rilasciare il blocco sulla pipe e aspettare che la scrittura sia completa, e NamedPipeServerStream non ha un metodo CancelAsync. Ho anche provato a chiamare BeginRead (), quindi chiamando la funzione win32 CancelIO sulla pipe se il thread viene segnalato, ma non penso che questa sia una soluzione ideale perché se CancelIO viene chiamato proprio mentre i dati arrivano e vengono elaborati, essere lasciato cadere – desidero ancora conservare questi dati, ma elaborarli in un secondo momento, dopo la scrittura. Sospetto che la funzione win32 PeekNamedPipe potrebbe essere utile, ma mi piacerebbe evitare di dover continuamente eseguire il polling di nuovi dati con esso.

Nel caso in cui il testo sopra sia un po ‘oscuro, ecco approssimativamente cosa mi piacerebbe essere in grado di fare …

NamedPipeServerStream pipe; ManualResetEvent WriteFlag; //initialise pipe lock (pipe) { //I wish this method existed WaitHandle NewDataHandle = pipe.GetDataAvailableWaithandle(); Waithandle[] BreakConditions = new Waithandle[2]; BreakConditions[0] = NewDataHandle; BreakConditions[1] = WriteFlag; int breakcode = WaitHandle.WaitAny(BreakConditions); switch (breakcode) { case 0: //do a read on the pipe break; case 1: //break so that we release the lock on the pipe break; } } 

Ok, l’ho appena strappato dal mio codice, spero di aver cancellato tutte le cose della logica delle applicazioni. L’idea è di provare una lettura a lunghezza zero con ReadFile e attendere sia lpOverlapped.EventHandle (triggersto quando la lettura è completa) sia un WaitHandle impostato quando un altro thread vuole scrivere nella pipe. Se la lettura deve essere interrotta a causa di un thread di scrittura, utilizzare CancelIoEx per annullare la lettura a lunghezza zero.

 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); 

Guardando attraverso MSDN, non vedo alcun meccanismo per fare ciò che vuoi. La soluzione più veloce è probabilmente quella di utilizzare l’interoperabilità per accedere a PeekNamedPipe . Se non si desidera utilizzare interop , è ansible astrarre la pipe all’interno di una class personalizzata e fornire la funzionalità di spunto all’interno dell’astrazione. L’astrazione gestirà tutta la segnalazione e dovrà coordinare la lettura e la scrittura sul tubo. Ovviamente, non è un compito banale.

Un’altra alternativa, se ansible nella tua situazione, è quella di esaminare l’uso di WCF che è più o meno un’astrazione.