重写的WSARecv()回调未在MFC应用程序中调用

时间:2010-10-25 13:52:52

标签: c++ com mfc winapi winsockets

我有一个COM组件,用C ++用ATL实现,它使用重叠的套接字I / O.在连接到服务器之后,它会在套接字上启动重叠读取,代码如下:

// Pass pointer to this instance as hEvent parameter, for use by callback
m_recvOverlapped.hEvent = reinterpret_cast<HANDLE>(this);

int rc = ::WSARecv(m_s, &wsabuf, 1, &m_recvNumberOfBytes, &m_recvFlags, &m_recvOverlapped, RecvCallback);
if (rc == SOCKET_ERROR)
{
    // If error is WSA_IO_PENDING, then the I/O is still in progress.  Otherwise, something bad happened.
    int error = ::WSAGetLastError();
    if (error != WSA_IO_PENDING)
    {
        ReceiveError(error);
    }
}

我有一个看起来像这样的回调函数:

void CALLBACK CMySocket::RecvCallback(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
CMySocket* socket = reinterpret_cast<CMySocket*>(lpOverlapped->hEvent);
ATLASSERT(socket != 0);
if (!socket)
    return;

socket->ReceiveCompleted(dwError, cbTransferred, lpOverlapped, dwFlags);
}

此COM组件在单元测试中工作正常,在命令行应用程序中使用时,以及在.NET GUI应用程序中使用时(通过COM interop)。但是,当我在MFC应用程序中使用此组件时,RecvCallback在服务器向其发送数据时永远不会被调用。

WSARecv()返回SOCKET_ERRORWSAGetLastError()返回WSA_IO_PENDING,正如预期的异步重叠读取一样。

当我使用SysInternals TcpView应用程序观察正在发生的事情时,它表明客户端正在接收数据。但回调永远不会被调用。

通过连接的套接字向服务器发送数据工作正常。

我在MFC应用的CoInitializeEx()方法中调用WSAStartup()InitInstance()

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

是的,确实如此。仅当线程进入“可警告”等待状态时才处理APC - 调用SleepExWaitForMultipleObjectsExMsgWaitForMultipleObjectsEx函数。

我想纠正你的一点是,使用OVERLAPPED的{​​{1}}成员作为“用户”数据的占位符是一个坏主意。因为操作系统将在您的I / O完成时尝试设置此“事件”。

将一些“用户”信息传递给回调路由的常用方法实际上是使用取代hEvent的自定义结构,根据需要添加更多成员(a.k.a。OVERLAPPED)。然后,您的回调路由可以将OVERLAPPED_PLUS投射到您的OVERLAPPED,您将看到所有成员。

另一点:既然你正在写一个COM对象 - 你可能没有能力编写自己的消息循环,因此可能很难保证进入可警告的等待。

答案 1 :(得分:0)

好的,我通过搜索有关WSARecv的其他Stack Overflow问题找到答案。

从Len Holgate回答Win32 Overlapped I/O - Completion routines or WaitForMultipleObjects?

  

。 。 。您可以传递完成例程,该例程在完成时调用。这称为“可警告I / O”,并要求发出WSARecv()调用的线程处于“可警告”状态,以便调用完成例程。线程可以通过多种方式将自己置于可警告状态(调用SleepEx()或等待函数的各种EX版本等)。 。 。

事实证明,如果我在MFC应用程序中启动定时调用SleepEx(0, TRUE)的计时器,则会调用接收回调。所以问题是调用我的COM对象调用WSARecv()的方法的主MFC线程永远不会自己进入可警告状态。

所以,我可能需要更改我的COM对象的实现,以便它使用I / O完成端口而不是回调,或者启动自己的调用WSARecv()的线程并使其自身保持警报。