Question Details

No question body available.

Tags

c++ winapi

Answers (2)

Accepted Answer Available
Accepted Answer
October 9, 2025 Score: 6 Rep: 612,923 Quality: Expert Completeness: 60%

Your WMDESTROY handler is creating a deadlock situation. It is waiting for the thread to finish, and it is blocking the main message loop from receiving the thread's message, and so it gets stuck waiting for a condition that can never happen.

If WM
DESTROY must wait for the message then it needs to run a secondary message loop to allow the message to be processed, eg:

case WMDESTROY:
{
    // Set Stop Event for thread
    HANDLE hStopEvent = ..;
    SetEvent(hStopEvent); // this works, and is successfully detected by thread

while (appHandleMap.getKeysStartingWith(L"thread").size() > 0) { // Do nothing, give it some time to close // We proceed when the thread is no longer in the handle Map

// Pump the message queue to handle pending messages if (MsgWaitForMultipleObjects(0, NULL, FALSE, INFINITE, QS
POSTMESSAGE) == WAITOBJECT0) { MSG msg; while (PeekMessage(&msg, NULL, WMTHREADDONE, WMTHREADDONE, PM_REMOVE)) { DispatchMessage(&msg); } } }

//.. etc. other Destroy code appHandleMap.clear(); PostQuitMessage(0); break; }
October 9, 2025 Score: 3 Rep: 50,200 Quality: Medium Completeness: 80%

The explanation given by others is correct: Until you return from processing WMDESTROY, the message loop isn't going to dispatch the WMTHREADDONE messages.

There are several ways to solve the problem. There are local fixes, as Remy Lebeau showed with pumping messages while processing WMDESTROY. But there are several other ways to approach it, some of which might improve your architecture to help you avoid other problems as well.

Let's recap what happens when a typical Windows program ends.

  1. The user hits the X in the corner of the main window, which causes a WMCLOSE message to be sent.
  2. The WMCLOSE handler probably just calls DestroyWindow.
  3. WMDESTROY will be sent as destruction begins. The handler will probably post the WMQUIT pseudo-message.
  4. WMNCDESTROY will be sent right at the end of the window's life.
  5. When the dust settles, the message dispatch loop will encounter the WMQUIT that tells it to break.
  6. The rest of the program runs.

Option 1: Signal earlier but postpone destruction

In your WMCLOSE handler:

  • If there are any threads running, raise the stop event, and also set a "closing" flag.
  • Call DestroyWindow only if there are no threads.

In your WMTHREADDONE handler:

  • If the closing flag is set and you've just cleaned up the last thread, call DestroyWindow.

Note: Make sure things like File > Exit send WMCLOSE rather than skipping ahead to DestroyWindow.

Option 2: Make the threads clean up after themselves.

If the sole reason for WMTHREADDONE is to release resources, consider whether that's actually necessary. If possible, have the threads release the resources themselves instead of sending or posting a message. Then you don't have to worry whether there's still a window to send to.

The caveat here is that sometimes a resource allocated on one thread might need to be released on that same thread. The next option covers that.

Option 3: Decouple the application from its window.

It's common to treat the main window as the application, in the sense that the window owns the data and other resources. This is not ideal because it forces you to use the UI's messaging model for business logic unrelated to UI.

It's often better to have an application object own the resources, including a window object and the worker threads. When the window needs access to a resource owned by the application, it has to go through the application object's interface, which is always there because the application owns everything else.

It looks like you already have some decoupling in the form of appHandleMap, so you have a headstart.

Instead of posting WMTHREADDONE messages to the window, threads should call an application method. That method should:

  • Do the necessary cleanup (or queue up the work to be done later, especially if it must happen on the main thread)
  • Post a message to the window to if the UI needs to update.

Now that the threads and the window no longer know about each other, the UI shutdown can happen in the traditional way.

And, once the message loop is done, the application can raise the stop event, wait for the threads to finish up, perform any queued cleanup work, and then exit gracefully.