Reputation: 14254
My console application spawns a new (invisible) window in its own thread. Prior to exiting the application, it attempts to clean up, and the last call to GetMessage
in the window's message pump fails. GetLastError
returns 1400, "Invalid window handle."
This is how the clean up proceeds in the application thread:
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;
}
This WndProc
exists in the new thread created for the window:
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 );
}
This is in the window's thread function where the new window is created and my message pump is located:
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;
}
}
What am I doing wrong? Thanks.
Upvotes: 0
Views: 2401
Reputation: 6143
You are passing an invalid window handle to GetMessage, which is why it's failing and reporting that a window handle is invalid.
You'll see the same error if you run this code with a made-up window handle:
MSG msg = {0};
BOOL b = ::GetMessage(&msg, (HWND)(0x123000), 0, 0);
if (b == -1)
{
DWORD dwErr = ::GetLastError();
wprintf(L"%lu\n", dwErr);
}
The problem is that you are still using the window handle after the window has been destroyed. As soon as a window is destroyed its handle is invalid (or, worse, re-used by some other window). The message pump won't exit until it processes the quit-message. Since the quit-message is posted during window destruction it will be processed after window destruction is complete. i.e. Your message pump keeps running for a short time after your window is destroyed.
Just pass NULL to GetMessage for the window argument, so that it retrieves messages for all windows on that thread. Since the thread only exists for that one window it's only going to get messages for that window anyway. (Plus the quit-message posted to the thread itself, and potentially messages for other windows which things like COM creates if you use COM on that thread... You'd definitely want to process those messages, so telling GetMessage to filter by window is doing nothing at best and could prevent something from working at worst, in addition to the error you're seeing GetMessage return.)
Upvotes: 6