2009-12-17 13 views
9

Necesito encontrar una forma de recibir notificaciones cuando un System.IO.Pipe.NamedPipeServerStream abierto en modo asíncrono tiene más datos disponibles para leer en él, un WaitHandle sería ideal. No puedo usar BeginRead() para obtener un identificador de este tipo porque es posible que alguien me señale otro hilo que quiera escribir en el conducto, así que tengo que soltar el bloqueo en el tubo y esperar a que la escritura esté completa. y NamedPipeServerStream no tiene un método CancelAsync. También traté de llamar a BeginRead(), y luego llamar a la función de win32 CancelIO en el conducto si el hilo se señaliza, pero no creo que esta sea una solución ideal porque si se llama a CancelIO justo cuando los datos están llegando y se están procesando, ser descartado, aún deseo conservar estos datos, pero procesarlos en un momento posterior, después de la escritura. Sospecho que la función win32 PeekNamedPipe podría ser útil, pero me gustaría evitar tener que buscar continuamente nuevos datos con ella.Canalizaciones con nombre: asimetría

En el caso likley que el texto anterior es un poco claro, aquí es más o menos lo que me gustaría ser capaz de hacer ...

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

Respuesta

9

Ok, así que acabo de extraer esto de mi código, con suerte borré todas las cosas de la lógica de la aplicación. La idea es que pruebe una lectura de longitud cero con ReadFile y espere tanto lpOverlapped.EventHandle (activada cuando finaliza la lectura) como WaitHandle cuando otro hilo desea escribir en el conducto. Si la lectura se va a interrumpir debido a un hilo de escritura, use CancelIoEx para cancelar la lectura de longitud cero.

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

+1: el uso de lecturas de 0 bytes con E/S superpuestas como una forma de recibir una señal cuando se escriben datos sin leer realmente ningún dato es muy útil y no se evidencia en la documentación. –

1

Mirando a través de MSDN, no veo ningún mecanismo para hacer lo que quieras La solución más rápida es, por favor, utilizar la interoperabilidad para acceder al PeekNamedPipe. Si no desea utilizar interop, puede abstraer la tubería dentro de una clase personalizada y proporcionar la funcionalidad de vista previa dentro de la abstracción. La abstracción manejaría toda la señalización y tiene que coordinar la lectura y la escritura en la tubería. Obviamente, no es una tarea trivial.

Otra alternativa, si es posible en su caso, es estudiar el uso de WCF que es más o menos esa abstracción.

+0

Oh wow, una respuesta! Gracias :) En verdad resolví esto hace siglos, pondré la solución ahora. –

Cuestiones relacionadas