2009-12-17 17 views
9

Ich muss einen Weg finden, benachrichtigt zu werden, wenn ein System.IO.Pipe.NamedPipeServerStream, das im asynchronen Modus geöffnet wurde, mehr Daten zum Lesen hat - ein WaitHandle wäre ideal. Ich kann BeginRead() nicht einfach verwenden, um ein solches Handle zu erhalten, weil es möglich ist, dass ich von einem anderen Thread signalisiert werde, der in die Pipe schreiben will. Also muss ich die Sperre für die Pipe freigeben und warten, bis der Schreibvorgang abgeschlossen ist. und NamedPipeServerStream verfügt nicht über eine CancelAsync-Methode. Ich versuchte auch, BeginRead() aufzurufen, dann die win32-Funktion CancelIO auf der Pipe aufzurufen, wenn der Thread signalisiert wird, aber ich denke nicht, dass dies eine ideale Lösung ist, wenn CancelIO gerade aufgerufen wird, während Daten ankommen und verarbeitet werden fallen lassen - Ich möchte diese Daten noch behalten, aber zu einem späteren Zeitpunkt, nach dem Schreiben, verarbeiten. Ich vermute, dass die Win32-Funktion PeekNamedPipe nützlich sein könnte, aber ich möchte vermeiden, dass ich ständig nach neuen Daten damit suchen muss.Named Pipes - Asynchronous Peeking

Im likley Fall, dass der obige Text ein wenig unklar ist, ist hier in etwa, was i ...

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

Antwort

9

Ok, also habe ich das einfach aus meinem Code rausgerissen, hoffentlich habe ich alle Anwendungslogik gelöscht. Die Idee ist, dass Sie mit ReadFile einen Lesevorgang der Länge Null versuchen und sowohl auf lpOverlapped.EventHandle (ausgelöst, wenn der Lesevorgang abgeschlossen ist) als auch WaitHandle auf einen anderen Thread warten, der in die Pipe schreiben möchte. Wenn der Lesevorgang aufgrund eines Schreib-Threads unterbrochen werden soll, verwenden Sie CancelIoEx, um den Lesevorgang der Länge Null abzubrechen.

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: Die Verwendung von 0-Byte-Lesevorgängen mit überlappenden E/A als Möglichkeit, ein Signal zu empfangen, wenn Daten geschrieben werden, ohne tatsächlich Daten zu lesen, ist sehr nützlich und in der Dokumentation nicht ersichtlich. –

1

über MSDN Suche zu tun in der Lage sein möchte, ich sehe keinen Mechanismus zu tun, was du willst. Die schnellste Lösung ist die Verwendung von Interop, um auf PeekNamedPipe zuzugreifen. Wenn Sie interop nicht verwenden möchten, können Sie die Pipe in einer benutzerdefinierten Klasse abstrahieren und die Peek-Funktion innerhalb der Abstraktion bereitstellen. Die Abstraktion würde die gesamte Signalisierung übernehmen und das Lesen und Schreiben an die Pipe koordinieren müssen. Offensichtlich keine triviale Aufgabe.

Eine andere Alternative, wenn möglich in Ihrer Situation, ist die Verwendung von WCF, die ziemlich genau diese Abstraktion ist.

+0

Oh wow, eine Antwort! Danke :) Ich löste dieses Alter wirklich, obwohl ich die Lösung jetzt aufstellen werde. –