在C#中复制MsgWaitForMultipleObjectsEx

时间:2015-03-13 19:34:04

标签: c# c++ .net wpf

我需要在C#中复制以下C ++函数:

void TComThread::CommWaitForAndHandleMessages(int minimum_poll)
{
     // Wait for:
     //  Any of the HANDLES in the WaitFor array, OR
     //  Any Windows message, OR
     //  Any Asynchronous Procedure Call (APC) to complete OR
     //  A timeout after minimum_poll milliseconds         
     const DWORD ret = MsgWaitForMultipleObjectsEx(0,
         NULL, minimum_poll, QS_ALLINPUT, MWMO_ALERTABLE);
     // APC: it was already called, so we just return
     if (ret == WAIT_IO_COMPLETION)
       return;
     // Timeout: we just return
     else if (ret == WAIT_TIMEOUT)
       return;
     // Windows message: process messages and return
     else if (ret == WAIT_OBJECT_0)
       Application->ProcessMessages();
     // Error of some kind
     else
       RaiseLastWin32Error();
}

我相信这个功能的关键是找到一个等同于MsgWaitForMultipleObjectsEx的C#。到目前为止,我在C#中发现的最接近的模拟此行为的方法是WaitHandle.WaitAny()方法。使用WaitAny()我可以设置一个计时器,并指定当前实例将等待的WaitHandle个对象。但是,我的问题在于 dwWakeMask dwFlags 值,因为我似乎无法将它们与System.Threading中的任何对象匹配。我还关注WAIT_IO_COMPLETIONWAIT_OBJECT_0

到目前为止,这是我的C#代码:

private void CommWaitForAndHandleMessages(int minimum_poll)
{
    Thread.WaitHandle[] waitHandles = new Thread.WaitHandle[]
    {
        //This is where I would create my WaitHandle array
    };

    int ret = Thread.WaitHandle.WaitAny(/* BLANK **/, new TimeSpan(minimum_poll), false);

    //Timeout -- return 
    if(ret == Thread.WaitHandle.WaitTimeout)
        return;
}

我已经阅读了WaitHandle.SafeWaitHandle并且它已经获取或设置了本机操作系统句柄"。我是否可以使用SafeWaitHandle来获取对QS_ALLINPUT等原生值的访问权限?如果没有,我如何添加到我的方法来复制C ++函数的行为?

注意:我必须在没有P / Invoke或任何类型的包装器的情况下执行此操作。

1 个答案:

答案 0 :(得分:0)

"纯粹" .NET完全相同。但是由于你没有使用p / invoke,你没有真正的Win32 APC,因此不需要完全相同,只是效果相同。 (特别是,你不需要警报等待)

为此,通过WPF SynchronizationContext转发您的I / O回调,它将通过现有的WPF消息循环传递它们。

同样,如果您正在翻译实际传递HANDLE数组的用法,那么您希望使用Tasks而不是WaitHandleTask的延续也使用WPF SynchronizationContext。但是你的C ++代码并没有使用MWFMO的句柄等待功能。

最后,超时可以使用await Task.Delay(timeout),使用延续,按上述。

对于TCP / IP通信,它可能看起来像这样(基于 https://stackoverflow.com/a/4036260/103167):

var cts = new CancellationTokenSource();

var recvTask = tcpSocket.ReadAsync(buffer, offset, length, cts.Token);

if (!await recvTask.Wait(3000, cts.Token)) {
    cts.Cancel();
    uiStatus("MessageTimeout");
    return;
}

int bytesReceived = recvTask.Result;
// buffer has been filled in, go ahead and process the message