9

非同期モードで開かれたSystem.IO.Pipe.NamedPipeServerStreamに、より多くのデータを読み取ることができる場合に通知を受ける方法を見つける必要があります。WaitHandleが理想的です。パイプに書き込みたい別のスレッドからシグナルが送信される可能性があるため、BeginRead()を使用してそのようなハンドルを取得することはできません。そのため、パイプのロックを解除して、書き込みが完了するのを待つ必要があります。 NamedPipeServerStreamにはCancelAsyncメソッドがありません。また、BeginRead()を呼び出してから、スレッドが通知された場合にパイプでwin32関数CancelIOを呼び出してみましたが、データが到着して処理されているときにCancelIOが呼び出された場合、これは理想的な解決策ではないと思います。ドロップされます-私はまだこのデータを保持したいのですが、書き込み後に後で処理します。

上記のテキストが少し不明瞭なリクレイイベントでは、これが私ができることを大まかに示しています...

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

2 に答える 2

9

さて、これをコードから削除しました。うまくいけば、すべてのアプリケーションロジックを削除しました。アイデアは、ReadFileを使用して長さゼロの読み取りを試行し、lpOverlapped.EventHandle(読み取りが完了したときに発生)と別のスレッドがパイプに書き込みを行うときに設定されたWaitHandleの両方を待機することです。書き込みスレッドが原因で読み取りが中断される場合は、CancelIoExを使用して長さゼロの読み取りをキャンセルします。

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);
于 2010-03-21T00:25:39.987 に答える
1

MSDNを調べてみると、あなたが望むことを実行するメカニズムがわかりません。最も手っ取り早い解決策は、おそらく相互運用機能を使用してにアクセスすることですPeekNamedPipe。を使用したくない場合はinterop、カスタムクラス内でパイプを抽象化し、抽象化内でピーク機能を提供できます。抽象化はすべてのシグナリングを処理し、パイプへの読み取りと書き込みを調整する必要があります。明らかに、些細な作業ではありません。

別の代替手段は、可能であれば、その抽象化であるWCFの使用を検討することです。

于 2010-03-19T21:09:11.657 に答える