Я должен найти, что путь уведомляется когда Система. IO.Pipe. NamedPipeServerStream, открытый в асинхронном режиме, имеет больше доступных данных для чтения на нем - WaitHandle был бы идеален. Я не могу просто использовать BeginRead () для получения такого дескриптора, потому что возможно, что я мог бы быть сообщен другим потоком, который хочет записать в канал - таким образом, я должен выпустить блокировку на канале и ожидать записи, чтобы быть завершенным, и NamedPipeServerStream does не имеют метод CancelAsync. Я также пытался назвать BeginRead (), затем вызывая функцию win32 CancelIO на канале, если поток сообщен, но я не думаю, что это - идеальное решение, потому что, если CancelIO называют так же, как, данные прибывают и обрабатываются, он будет отброшен - я все еще хочу сохранить эти данные, но обработать их в более позднее время, после записи. Я подозреваю функцию win32, PeekNamedPipe мог бы быть полезным, но я хотел бы избежать необходимости непрерывно опрос для новых данных с ним.
В вероятном событии, что вышеупомянутый текст немного неясен, вот примерно, что я хотел бы смочь сделать...
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;
}
}
Хорошо, я просто вырезал это из своего кода, надеюсь, я удалил всю логику приложения. Идея состоит в том, что вы пробуете чтение нулевой длины с помощью 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);
Просматривая MSDN, я не вижу никакого механизма, позволяющего делать то, что вы хотите. Возможно, самым быстрым решением является использование взаимодействия для доступа к PeekNamedPipe
. Если вы не хотите использовать interop
, вы можете абстрагировать конвейер внутри настраиваемого класса и обеспечить функциональность просмотра внутри абстракции. Абстракция будет обрабатывать всю сигнализацию и должна координировать чтение и запись в канал. Очевидно, задача нетривиальная.
Другой альтернативой, если это возможно в вашей ситуации, является использование WCF, который в значительной степени является абстракцией.