Jim Fell
Jim Fell

Reputation: 14254

Closing Window and Thread Generates Invalid Window Handle Error - C++

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

Answers (1)

Leo Davidson
Leo Davidson

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

Related Questions