Jim Fell
Jim Fell

Reputation: 14256

Trouble Closing Message-Only Window - C++

My application has a message-only window that is launched from a newly created thread. The thread function creates the message-only window and runs the message pump. The problem I am having is that the message pump never seems to get the WM_CLOSE message. I've removed error-handling to simplify the posted code. Does anyone know what I'm doing wrong?

Constructor:

this->m_hInstance = ::GetModuleHandle( NULL );

this->m_wcx.cbSize = sizeof(WNDCLASSEX);  // size of structure
this->m_wcx.style = CS_HREDRAW | CS_VREDRAW; // initially minimized
this->m_wcx.lpfnWndProc = &WndProc;       // points to window procedure
this->m_wcx.cbClsExtra = 0;               // no extra class memory
this->m_wcx.cbWndExtra = 0;               // no extra window memory
this->m_wcx.hInstance = m_hInstance;      // handle to instance
this->m_wcx.hIcon = ::LoadIcon( NULL, IDI_APPLICATION ); // default app icon
this->m_wcx.hCursor = ::LoadCursor( NULL, IDC_ARROW ); // standard arrow cursor
this->m_wcx.hbrBackground = NULL;         // no background to paint
this->m_wcx.lpszMenuName = NULL;          // no menu resource
this->m_wcx.lpszClassName = s_pwcWindowClass; // name of window class
this->m_wcx.hIconSm = NULL;               // search system resources for sm icon

this->m_atom = ::RegisterClassEx( &m_wcx );

this->m_hNotifyWindowThread = ::CreateThread(
    NULL,                           // no security attributes
    0,                              // use default initial stack size
    reinterpret_cast<LPTHREAD_START_ROUTINE>(NotifyWindowThreadFn), // function to execute in new thread
    NULL,                           // thread parameters
    0,                              // use default creation settings
    NULL                            // thread ID is not needed
    );

Destructor:

::DestroyWindow( this->m_hWnd );
::WaitForSingleObject( this->m_hNotifyWindowThread, NW_DEFAULT_TIMEOUT ); // <-- Seems to get stuck here.
::UnregisterClass( s_pwcWindowClass, this->m_hInstance );

Thread function:

s_ptInterface->pobjNotifyWindow->m_hWnd = ::CreateWindow(
    s_pwcWindowClass,               // window class name
    s_pwcWindowName,                // window name
    WS_ICONIC,                      // window style is minimized
    0,                              // initial horizontal position
    0,                              // initial vertical position
    CW_USEDEFAULT,                  // window width
    0,                              // window height
    NULL,                           // no parent window
    NULL,                           // no menu
    s_ptInterface->pobjNotifyWindow->GetInstanceHandle(), // associated instance
    NULL                            // no additional info for WM_CREATE
    );

::ShowWindow( s_ptInterface->pobjNotifyWindow->GetWindowHandle(), SW_HIDE );
::UpdateWindow( s_ptInterface->pobjNotifyWindow->GetWindowHandle();

dbt.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
dbt.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
dbt.dbcc_classguid = s_guidForCP210xDevices;

m_hNotify = RegisterDeviceNotification( m_hWnd, &dbt, DEVICE_NOTIFY_WINDOW_HANDLE );

while ( (blRetVal = ::GetMessage(
    &msg,                           // message structure
    NULL,                           // retrieve messages for all windows on this thread
    0,                              // lowest message value to retrieve
    0                               // highest message value to retrieve
    )) != 0 )
{
    if ( blRetVal == -1 )
    {
        return ::GetLastError();
    }
    else
    {
        ::TranslateMessage( &msg );
        ::DispatchMessage( &msg );
    }
}

Window procedure:

switch ( uMsg )
{
case WM_CLOSE:
    if ( m_hNotify != NULL )
    {
        ::UnregisterDeviceNotification( m_hNotify );
        m_hNotify = NULL;
    }

    ::DestroyWindow( hWnd );
    break;

case WM_DESTROY:
    ::PostQuitMessage( 0 );
    break;

case WM_DEVICECHANGE:
    if ( pHeader != NULL )
    {
        if ( pHeader->dbch_devicetype == DBT_DEVTYP_PORT )
        {
            switch ( wParam)
            {
            case DBT_DEVICEREMOVECOMPLETE:      // Device is gone
                ::EnterCriticalSection( &(s_ptInterface->csSerialPort) );
                s_ptInterface->pobjSerialPort->Close();
                ::LeaveCriticalSection( &(s_ptInterface->csSerialPort) );
                break;

            case DBT_DEVICEARRIVAL:             // System detected device
                ::EnterCriticalSection( &(s_ptInterface->csSerialPort) );
                s_ptInterface->pobjSerialPort->Open();
                ::LeaveCriticalSection( &(s_ptInterface->csSerialPort) );
                break;

            default:
                // Do nothing.
                break;
            }
        }
    }
    break;

default:
    // Do nothing.
    break;
}

return ::DefWindowProc( hWnd, uMsg, wParam, lParam );

Upvotes: 1

Views: 1084

Answers (1)

Hans Passant
Hans Passant

Reputation: 941218

WM_CLOSE is sent when Windows asks your app to close the window. When the user clicks the upper right Close button or presses Alt+F4 for example. None of that is going to happen, you called DestroyWindow(). You need to use WM_DESTROY instead. Which is fine, no need to veto the close request.

Upvotes: 3

Related Questions