Jim*_*ell 0 c++ multithreading exception visual-studio-2008
我的控制台应用程序在自己的线程中生成一个新的(不可见)窗口.在退出应用程序之前,它会尝试清理,并且GetMessage
窗口消息泵中的最后一次调用失败. GetLastError
返回1400,"无效的窗口句柄."
这是清理在应用程序线程中的进展:
if ( s_hNotifyWindowThread != NULL )
{
ASSERT(s_pobjNotifyWindow != NULL);
::PostMessage( s_pobjNotifyWindow->m_hWnd, WM_CLOSE, 0, 0 );
::WaitForSingleObject( s_hNotifyWindowThread, 50000L ); // Step 1: breakpoint here
::CloseHandle( s_hNotifyWindowThread ); // Step 4: breakpoint here
s_hNotifyWindowThread = NULL;
}
Run Code Online (Sandbox Code Playgroud)
这WndProc
存在于为窗口创建的新线程中:
static LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch ( uMsg )
{
case WM_CLOSE:
::DestroyWindow( hWnd ); // Step 2: breakpoint here
break;
case WM_DESTROY:
::PostQuitMessage( 0 ); // Step 3: breakpoint here
break;
case WM_DEVICECHANGE:
/* Handle device change. */
break;
default:
// Do nothing.
break;
}
return ::DefWindowProc( hWnd, uMsg, wParam, lParam );
}
Run Code Online (Sandbox Code Playgroud)
这是在窗口的线程函数中创建新窗口并且我的消息泵位于:
s_pobjNotifyWindow = new CNotifyWindow( pParam );
while ( (bRetVal = ::GetMessage(
&msg, // message structure
s_pobjNotifyWindow->m_hWnd, // handle to window whose messages are to be retrieved
0, // lowest message value to retrieve
0 // highest message value to retrieve
)) != 0 )
{
switch ( bRetVal )
{
case -1: // Error generated in GetMessage.
TRACE(_T("NotifyWindowThreadFn : Failed to get notify window message.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError(); // Step 5: breakpoint here: Returns error 1400
break;
default: // Other message received.
::TranslateMessage( &msg );
::DispatchMessage( &msg );
break;
}
}
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?谢谢.
您正在向GetMessage传递无效的窗口句柄,这就是它失败并报告窗口句柄无效的原因.
如果您使用虚拟窗口句柄运行此代码,您将看到相同的错误:
MSG msg = {0};
BOOL b = ::GetMessage(&msg, (HWND)(0x123000), 0, 0);
if (b == -1)
{
DWORD dwErr = ::GetLastError();
wprintf(L"%lu\n", dwErr);
}
Run Code Online (Sandbox Code Playgroud)
问题是窗口被破坏后仍在使用窗口句柄.一旦窗口被销毁,其句柄就无效(或者更糟糕的是,被其他窗口重用).消息泵在处理退出消息之前不会退出.由于在窗口销毁期间发布了退出消息,因此将在窗口销毁完成后处理它.即您的消息泵在窗口被破坏后会持续运行一小段时间.
只需将NULL传递给GetMessage获取窗口参数,以便它检索该线程上所有窗口的消息.由于线程只存在于那个窗口,因此它只会为该窗口获取消息.(加上发布到线程本身的退出消息,以及其他窗口的消息,如果你在该线程上使用COM,那么像COM这样的东西会创建......你肯定想要处理这些消息,所以告诉GetMessage按窗口过滤除了你看到GetMessage返回的错误之外,它最多什么都不做,可能会阻止某些事情在最坏的情况下工作.)