Article: Q154649
Product(s): Microsoft C Compiler
Version(s): winnt:4.2
Operating System(s):
Keyword(s): kbnetwork kbMFC kbVC420fix kbWinsock
Last Modified: 06-MAY-2001
-------------------------------------------------------------------------------
The information in this article applies to:
- The Microsoft Foundation Classes (MFC), used with:
- Microsoft Visual C++, 32-bit Enterprise Edition, version 4.2
- Microsoft Visual C++, 32-bit Professional Edition, version 4.2
-------------------------------------------------------------------------------
SYMPTOMS
========
A call to CSocket Connect, Accept, Send, or Receive hangs if a timer is active.
CAUSE
=====
CSocket operations are written using "pseudo-blocking." The WinSock API call is
not a true blocking call, but if it doesn't return immediately then CSocket will
call PumpMessages. PumpMessages looks for the FD_xxx notification and the
CSocket function will not return until the FD_xxx notification arrives.
PumpMessages will not detect the socket notification if a timer is active because
it looks for WM_TIMER messages first. When a WM_TIMER message arrives, the
function doesn't look any further for other messages.
The PumpMessages function is looking for WM_TIMER messages because it sets up its
own timer to cause the application to retry its socket calls and do periodic
idle-time processing while waiting for the socket notification.
RESOLUTION
==========
There are two possible resolutions:
1. Disable all timers while making CSocket calls.
2. Override the PumpMessages function so that it only looks for its own WM_TIMER
messages and does not pick up other timer messages. This can be done by
borrowing the CSocket::PumpMessages function from SOCKCORE.CPP and changing
it as follows:
#if _MFC_VER == 0x0420
#define _AFX_SOCK_THREAD_STATE AFX_MODULE_THREAD_STATE
#define WM_SOCKET_NOTIFY 0x0373
#define WM_SOCKET_DEAD 0x0374
#define _afxSockThreadState AfxGetModuleThreadState()
#endif
BOOL CMySocket::PumpMessages(UINT uStopFlag)
{
#if _MFC_VER != 0x0420
return CSocket::PumpMessages(uStopFlag);
#else
// The same socket better not be blocking in more than one place.
ASSERT(m_pbBlocking == NULL);
_AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
ASSERT(pState->m_hSocketWindow != NULL);
BOOL bBlocking = TRUE;
m_pbBlocking = &bBlocking;
CWinThread* pThread = AfxGetThread();
// This is not a timeout in the WinSock sense, but more
// like a WM_KICKIDLE to keep message pumping alive
UINT nTimerID = ::SetTimer(pState->m_hSocketWindow, 0,
m_nTimeOut, NULL);
if (nTimerID == 0)
AfxThrowResourceException();
BOOL bPeek = TRUE;
while (bBlocking)
{
TRY
{
MSG msg;
if (::PeekMessage(&msg, pState->m_hSocketWindow,
WM_SOCKET_NOTIFY, WM_SOCKET_DEAD, PM_REMOVE))
{
if (msg.message == WM_SOCKET_NOTIFY &&
(SOCKET)msg.wParam == m_hSocket)
{
if (WSAGETSELECTEVENT(msg.lParam) == FD_CLOSE)
{
break;
}
if (WSAGETSELECTEVENT(msg.lParam) == uStopFlag)
{
if (uStopFlag == FD_CONNECT)
m_nConnectError = WSAGETSELECTERROR(msg.lParam);
break;
}
}
if (msg.wParam != 0 || msg.lParam != 0)
CSocket::AuxQueueAdd(msg.message, msg.wParam, msg.lParam);
bPeek = TRUE;
}
else if (::PeekMessage(&msg, pState->m_hSocketWindow,
WM_TIMER, WM_TIMER, PM_REMOVE))
{
break;
}
if (bPeek && ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if (OnMessagePending())
{
// allow user-interface updates
pThread->OnIdle(-1);
}
else
{
bPeek = FALSE;
}
}
else
{
// no work to do -- allow CPU to sleep
WaitMessage();
bPeek = TRUE;
}
}
CATCH_ALL(e)
{
TRACE0("Error: caught exception in PumpMessage - continuing.\n");
bPeek = TRUE;
}
END_CATCH_ALL
}
::KillTimer(pState->m_hSocketWindow, nTimerID);
if (!bBlocking)
{
WSASetLastError(WSAEINTR);
return FALSE;
}
m_pbBlocking = NULL;
::PostMessage(pState->m_hSocketWindow,WM_SOCKET_NOTIFY,0,0);
return TRUE;
#endif // _MFC_VER
}
STATUS
======
Microsoft has confirmed this to be a bug in the Microsoft products listed at the
beginning of this article. This bug has been fixed with the Visual C++ 4.2b
patch. For more information on this patch, please see the following articles in
the Microsoft Knowledge Base:
Q156934 PATCH: Visual C++ 4.2b Patch
Q160491 Information on Files Modified by VC42b Patch: Part 1 of 4
Q160496 Information on Files Modified by VC42b Patch: Part 2 of 4
Q160505 Information on Files Modified by VC42b Patch: Part 3 of 4
Q160506 Information on Files Modified by VC42b Patch: Part 4 of 4
Additional query words: kbVC420bug kbDSupport CAsyncSocket CSocket hangs timer
======================================================================
Keywords : kbnetwork kbMFC kbVC420fix kbWinsock
Technology : kbAudDeveloper kbMFC
Version : winnt:4.2
Issue type : kbbug
Solution Type : kbfix
=============================================================================