Closing Window and Thread Generates Invalid Window Handle Error - C++ - 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.

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.)

Related

PeekMessage always returns FALSE

I have written a small test application that inserts files (with hardcoded paths) into the currently active folder/application via delayed rendering. It works as expected. But I have a question - why does PeekMessage always return FALSE? But if you remove the PeekMessage call, Wndproc will never be called. I read a similar post, but I'm creating a window in the same thread in which I'm trying to process messages.
Code:
static LRESULT CALLBACK WindProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_RENDERALLFORMATS: {
OpenClipboard(hWnd);
EmptyClipboard();
}
case WM_RENDERFORMAT: {
printf("WM_RENDERFORMAT received");
<Here the file paths are copied to the clipboard>
if (Msg == WM_RENDERALLFORMATS)
CloseClipboard();
return 0;
}
case WM_DESTROYCLIPBOARD:
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
HWND hwnd_;
void thread_(void* ignored) {
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = WindProc;
wcx.hInstance = GetModuleHandle(NULL);
wcx.lpszClassName = TEXT("my_class");
RegisterClassEx(&wcx);
hwnd_ = CreateWindowEx(0, TEXT("my_class"), TEXT(""), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
MSG msg;
while (true) {
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
printf("PeekMessage returned TRUE\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
break;
}
Sleep(1000);
}
}
void main() {
CloseHandle((HANDLE)_beginthread(thread_, 0, NULL));
// let's give some time to thread to create msg window
Sleep(100);
if (OpenClipboard(hwnd_)) {
EmptyClipboard();
SetClipboardData(CF_HDROP, NULL);
CloseClipboard();
}
while (true) {
Sleep(100);
}
}
PeekMessage() (and GetMessage()) only returns messages that are posted via PostMessage() or PostThreadMessage() to the calling thread's message queue. PeekMessage() returns FALSE when there is no posted message available at the moment it is called. GetMessage() blocks until a posted message becomes available.
However, the messages in question (WM_RENDERFORMAT and WM_RENDERALLFORMATS) are instead being sent via SendMessage...() directly to the target window. But, the messages are being sent by another thread, so PeekMessage() (or GetMessage()) is still needed, as they internally dispatch messages that are sent across thread boundaries.
This is stated in PeekMessage()'s documentation:
Dispatches incoming nonqueued messages, checks the thread message queue for a posted message, and retrieves the message (if any exist).
...
During this call, the system dispatches (DispatchMessage) pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. Then the first queued message that matches the specified filter is retrieved. The system may also process internal events. If no filter is specified, messages are processed in the following order:
Sent messages
Posted messages
Input (hardware) messages and system internal events
Sent messages (again)
WM_PAINT messages
WM_TIMER messages
As well as the GetMessage() documentation:
Retrieves a message from the calling thread's message queue. The function dispatches incoming sent messages until a posted message is available for retrieval.
...
During this call, the system delivers pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. Then the first queued message that matches the specified filter is retrieved. The system may also process internal events. If no filter is specified, messages are processed in the following order:
Sent messages
Posted messages
Input (hardware) messages and system internal events
Sent messages (again)
WM_PAINT messages
WM_TIMER messages
And SendMessage()'s documentation:
If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed. To prevent this, use SendMessageTimeout with SMTO_BLOCK set. For more information on nonqueued messages, see Nonqueued Messages.
With that said, your handling of the WM_RENDERALLFORMATS message is wrong. For one thing, it must not call EmptyClipboard(), and for another, it is not checking whether your app is still the clipboard owner before rendering its data. See What is the proper handling of WM_RENDERFORMAT and WM_RENDERALLFORMATS? for why these points are important.
Also, you have a race condition in your main thread. Before it calls OpenClipboard(), it sleeps for only 100ms to wait for the window to be created first, but there is no guarantee that hwnd_ will have been assigned within that 100ms. It could take that long just for the worker thread to start running, for instance.
A better option is to have the worker thread signal an event when the window is created, and then have the main thread wait on that event (even better would be to simply move the initial SetClipboardData() call into the worker thread itself after creating the window, but you have dismissed that option).
Try something more like this instead:
static LRESULT CALLBACK WindProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
/*
case WM_CREATE:
if (OpenClipboard(hWnd)) {
EmptyClipboard();
SetClipboardData(CF_HDROP, NULL);
CloseClipboard();
}
return 0;
}
*/
case WM_RENDERALLFORMATS: {
if (OpenClipboard(hWnd)) {
if (GetClipboardOwner() == hWnd) {
SendMessage(hWnd, WM_RENDERFORMAT, CF_HDROP, 0);
}
CloseClipboard();
}
return 0;
}
case WM_RENDERFORMAT: {
printf("WM_RENDERFORMAT received");
if (wParam == CF_HDROP) {
// <Here the file paths are copied to the clipboard>
}
return 0;
}
case WM_DESTROYCLIPBOARD:
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
HWND hwnd_ = NULL;
void thread_(void* arg) {
HANDLE hEvent = (HANDLE)arg;
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = WindProc;
wcx.hInstance = GetModuleHandle(NULL);
wcx.lpszClassName = TEXT("my_class");
RegisterClassEx(&wcx);
hwnd_ = CreateWindowEx(0, TEXT("my_class"), TEXT(""), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
SetEvent(hEvent);
if (hwnd_ == NULL) {
return 0;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
printf("GetMessage returned a message\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
int main() {
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hEvent)
return 1;
uintptr_t res = _beginthread(thread_, 0, hEvent);
if (res == -1L) {
CloseHandle(hEvent);
return 1;
}
WaitForSingleObject(hEvent, INFINITE);
CloseHandle(hEvent);
if (hwnd_ != NULL) {
if (OpenClipboard(hwnd_)) {
EmptyClipboard();
SetClipboardData(CF_HDROP, NULL);
CloseClipboard();
}
}
HANDLE hThread = (HANDLE)res;
WaitForSingleObject(hThread, INIFINTE);
CloseHandle(hThread);
return 0;
}
why does PeekMessage always return FALSE?
Assuming that the window handle that you pass to PeekMessage is valid, then the reason for PeekMessage returning FALSE is simply that there are no messages in the queue.
That this is the case can be discerned from the documentation which says:
If a message is available, the return value is nonzero.
If no messages are available, the return value is zero.

How to handle WM_ENDSESSION in a console app

I wrote a program to register mouse events, and I want it to terminate when the computer is shutdown (and then perform a flush and a final print).
I tried with a CtrlHandler, but it works only with Ctrl-C and not when the system is shutdown, because I am using a Win32 library, according to MSDN:
If a console application loads the gdi32.dll or user32.dll library, the HandlerRoutine function that you specify when you call SetConsoleCtrlHandler does not get called for the CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT events. The operating system recognizes processes that load gdi32.dll or user32.dll as Windows applications rather than console applications. This behavior also occurs for console applications that do not call functions in gdi32.dll or user32.dll directly, but do call functions such as Shell functions that do in turn call functions in gdi32.dll or user32.dll.
To receive events when a user signs out or the device shuts down in these circumstances, create a hidden window in your console application, and then handle the WM_QUERYENDSESSION and WM_ENDSESSION window messages that the hidden window receives. You can create a hidden window by calling the CreateWindowEx method with the dwExStyle parameter set to 0.
So, first I have to create a hidden window, and then I have to intercept the the WM_ENDSESSION message. But how?
I tried to read some examples, but I can't figure out how to do this.
Here is my code:
BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
{
switch (fdwCtrlType)
{
// Handle the CTRL-C signal.
/*case CTRL_C_EVENT:
printf("Ctrl-C event\n\n");
Beep(750, 300);
return FALSE; //TRUE
// CTRL-CLOSE: confirm that the user wants to exit.
case CTRL_CLOSE_EVENT:
Beep(600, 200);
printf("Ctrl-Close event\n\n");
return FALSE; //TRUE
// Pass other signals to the next handler.
case CTRL_BREAK_EVENT:
Beep(900, 200);
printf("Ctrl-Break event\n\n");
return FALSE;
*/case CTRL_LOGOFF_EVENT:
Beep(1000, 200);
printf("Ctrl-Logoff event\n\n");
myfile << "totale :" << tot;
myfile.flush();
myfile.close();
return TRUE; //FALSE
case CTRL_SHUTDOWN_EVENT:
Beep(750, 500);
printf("Ctrl-Shutdown event\n\n");
myfile << "totale :" << tot;
myfile.flush();
myfile.close();
return TRUE; //FALSE
default:
return FALSE;
}
}
int main(){
if (SetConsoleCtrlHandler(CtrlHandler, TRUE))
{
printf("\nThe Control Handler is installed.\n");
for(;;)
{
code that print the mouse event(........)
}
}
else
{
printf("\nERROR: Could not set control handler");
return 1;
}
return 0;
}
You can just create a hidden GUI window in console application and handle WM_ENDSESSION in the window procedure as shown below
#include <Windows.h>
HWND g_hidden_window = nullptr;
LRESULT CALLBACK wnd_proc(HWND, UINT, WPARAM, LPARAM);
// Main entry point of your app
int main() {
HMODULE current_instance = ::GetModuleHandle(L"");
// Register the window class
WNDCLASSEX window_class_ex = { 0 };
window_class_ex.cbSize = sizeof(WNDCLASSEX);
window_class_ex.lpfnWndProc = wnd_proc;
window_class_ex.lpszClassName = L"Foo";
window_class_ex.hInstance = current_instance;
if (!::RegisterClassEx(&window_class_ex)) {
return 1;
}
// Create an overlapped window
g_hidden_window = ::CreateWindow(
L"Foo",
L"",
WS_OVERLAPPED,
0, 0, 0, 0,
nullptr,
nullptr,
current_instance,
0);
if (!g_hidden_window) {
return 1;
}
MSG message;
// Main message loop
while (::GetMessage(&message, nullptr, 0, 0)) {
::DispatchMessage(&message);
}
}
Now, in your main window procedure, you should handle WM_ENDSESSION. In your case, I see no reason to handle WM_QUERYENDSESSION. You should also handle WM_CLOSE and/or WM_DESTROY to quit the main message loop:
// Main window procedure
LRESULT CALLBACK wnd_proc(HWND window_handle, UINT window_message, WPARAM wparam, LPARAM lparam) {
switch (window_message) {
case WM_ENDSESSION:
if(wparam) {
// According to MSDN this value will be 1 when the system is about to shut down: https://learn.microsoft.com/en-us/windows/win32/shutdown/wm-endsession
// Invoke your function here
CtrlHandler(CTRL_SHUTDOWN_EVENT);
}
break;
case WM_CLOSE:
DestroyWindow(window_handle);
break;
case WM_DESTROY:
::PostQuitMessage(0);
break;
default:
return ::DefWindowProc(window_handle, window_message, wparam, lparam);
}
return 0;
}
To gracefully shut down the app, you will have to break that message loop. To do so, you will have to send a WM_CLOSE message:
SendMessage(g_hidden_window, WM_CLOSE, 0, 0);
Or, explicitly destroy the window by calling:
DestroyWindow(g_hidden_window);
Let me know if it works. I have not tested it because I'm on a Mac right now, but it should work.

SendNotifyMessage do not send correct messages

From one thread I send the message to main thread in window procedure.
But it is unsuccessful. When I send messages from the same thread - all is ok
include "stdafx.h"
#include <Windows.h>
#include <atlbase.h>
#define MAX_THREADS 1
HWND m_wnd;
enum
{
EVENT_CALL = (WM_APP + 0x30),
};
static LRESULT CALLBACK function_call()
{
//some code
int test = 0;
return 0;
}
static LRESULT CALLBACK http_message_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case EVENT_CALL:
function_call();
return 0;
}
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void CreateNotifyWnd()
{
WNDCLASSEX w = { 0 };
w.cbSize = sizeof(w);
w.hInstance = (HINSTANCE)&__ImageBase;
w.lpszClassName = L"uistone_http_event_wnd";
w.lpfnWndProc = http_message_proc;
::RegisterClassEx(&w);
int error = GetLastError();
m_wnd = ::CreateWindowEx(0, w.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, w.hInstance, 0);
error = GetLastError();
}
DWORD WINAPI SendThread(void* request_param)
{
::SendNotifyMessage(m_wnd, EVENT_CALL, 11, 12);
int error = GetLastError();
return 0;
}
int main()
{
CreateNotifyWnd();
HANDLE hThreadArray[MAX_THREADS];
hThreadArray[0] = CreateThread(nullptr, 0, SendThread, nullptr, 0, nullptr);
//::SendNotifyMessage(m_wnd, EVENT_CALL, 11, 12);
WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);
return 0;
}
Why I can not catch messages from another thread?
Thanks.
This is documented behavior. This is the relevant part from the SendNotifyMessage documentation:
If the window was created by the calling thread, SendNotifyMessage calls the window procedure for the window and does not return until the window procedure has processed the message. If the window was created by a different thread, SendNotifyMessage passes the message to the window procedure and returns immediately; it does not wait for the window procedure to finish processing the message.
This appears to work when used with a window created on the same thread, because when you call SendNotifyMessage, the function synchronously calls into the window procedure associated with the target window before returning.
If the call crosses threads, on the other hand, you'd have to run a message loop for the - now queued - message to get picked up and passed to the window procedure1). Your application doesn't run a message loop, and it exits before the message ever reaches the target window.
To fix this you'd have to run a message loop. This may or may not be the right approach to your problem. Since we don't know, what problem you are trying to solve, we cannot suggest potentially superior approaches and solutions.
1) See About Messages and Message Queues: Message Routing.

Properly closing window created on a separate thread using ATL

I have a multithreaded application and on certain threads, I'm creating windows using ATL's CWindowImpl<>. I have a static method that I'm using as the thread procedure. I need to create a window on the thread, because I need some of my communication with the thread to be synchronous, and PostThreadMessage() is expressly asynchronous. When my window receives the WM_DESTROY message (handler defined by the MESSAGE_HANDLER macro), it calls PostQuitMessage(), as shown in this method:
LRESULT MyATLWindowClass::OnDestroy(UINT uMsg,
WPARAM wParam,
LPARAM lParam,
BOOL& bHandled) {
::PostQuitMessage(0);
return 0;
}
I'm using a custom message to the thread using PostThreadMessage() to indicate to the thread that it's time to terminate itself. Handling that custom message, I call the CWindowImpl::DestroyWindow() method, which does appear to properly destroy the window, as my OnDestroy message handler is getting called. However, it doesn't appear that the owning thread ever receives a WM_QUIT message for processing. Included below is a simplified version of my thread procedure.
unsigned int WINAPI MyATLWindowClass::ThreadProc(LPVOID lpParameter) {
// Initialize COM on the thread
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
// Create the window using ATL
MyATLWindowClass new_window;
HWND session_window_handle = new_window.Create(
/* HWND hWndParent */ HWND_MESSAGE,
/* _U_RECT rect */ CWindow::rcDefault,
/* LPCTSTR szWindowName */ NULL,
/* DWORD dwStyle */ NULL,
/* DWORD dwExStyle */ NULL,
/* _U_MENUorID MenuOrID */ 0U,
/* LPVOID lpCreateParam */ NULL);
// Initialize the message pump on the thread.
MSG msg;
::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
// Run the message loop
BOOL get_message_return_value;
while ((get_message_return_value = ::GetMessage(&msg, NULL, 0, 0)) != 0) {
if (get_message_return_value == -1) {
// GetMessage handling logic taken from MSDN documentation
break;
} else {
if (msg.message == WD_SIGNAL_THREAD_SHUTDOWN) {
// Requested thread shutdown, so destroy the window
new_window.DestroyWindow();
} else if (msg.message == WM_QUIT) {
// Process the quit message and exit the message loop
// to terminate the thread
break;
} else {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
}
// Uninitialize COM on the thread before exiting
::CoUninitialize();
return 0;
}
Note that it doesn't seem to matter if I call DestroyWindow() or if I send a WM_CLOSE message to the window. The thread's message pump is not receiving WM_QUIT in either case. Should the owning thread's message pump be receiving such a message? Where is my misunderstanding about how the thread's message pump and the window's message pump interact? Or what am I missing about how ATL's window classes create and manage windows?
GetMessage() never returns WM_QUIT. That message forces it to return 0 instead, designed to terminate your message loop.
Beware of the considerable hazards of using PostThreadMessage(). It should never be used on a thread that also displays windows, like the one you are using. The issue is that it doesn't take a HWND argument. So only your message loop can see the message, it won't be delivered to any window with DispatchMessage(). This goes wrong when a modal message loop is entered, the kind that are outside of your control. Like the modal loop that makes MessageBox work. Or the one that Windows uses to allow the user to resize a window. Or the one that DialogBox() uses. Etcetera. Always use PostMessage(), use your own message number.
Some late additional thoughts. You could probably safely terminate your message loop as soon as you have discovered WD_SIGNAL_THREAD_SHUTDOWN:
if (msg.message == WD_SIGNAL_THREAD_SHUTDOWN) {
// Requested thread shutdown, so destroy the window
new_window.DestroyWindow();
break; // exit the message loop
}
DestroyWindow is a synchronous call, the window will be fully destroyed before it returns and you can exit the loop. So, posting WM_QUIT would be redundant.
Also, you could use message-only window, if the windows is invisible and its only purpose is to process messages.

Thread Fails to Exit On Application Exit - C++

My application creates a thread that polls for Windows messages. When it is time to close down, my application sends the WM_QUIT message.
In the application thread, this is how I am attempting to shut things down:
if ( _hNotifyWindowThread != NULL )
{
ASSERT(_pobjNotifyWindow != NULL);
::SendMessage( _pobjNotifyWindow->m_hWnd, WM_QUIT, 0, 0 );
::WaitForSingleObject( _hNotifyWindowThread, 50000L );
::CloseHandle( _hNotifyWindowThread ); // <-- PC never gets here.
_hNotifyWindowThread = NULL;
}
This is the message pump running in my thread function:
// Start the message pump...
while ( (bRetVal = ::GetMessage(
&msg, // message structure
_pobjNotifyWindow->m_hWnd, // handle to window whose messages are to be retrieved
WM_DEVICECHANGE, // lowest message value to retrieve
WM_DEVICECHANGE // 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();
break;
default: // Other message received.
::TranslateMessage( &msg );
::DispatchMessage( &msg );
break;
}
}
delete _pobjNotifyWindow; // Delete the notify window.
return msg.wParam; // Return exit code.
The Microsoft documentation for GetMessage states:
If the function retrieves the WM_QUIT message, the return value is zero.
Note that GetMessage always retrieves WM_QUIT messages, no matter which values you specify for wMsgFilterMin and wMsgFilterMax.
If this is the case, then I would expect a call to GetMessage that retrieves the WM_QUIT message to return 0. However, debugging leaves me to believe that the message is not received properly. What is odd is that I can place a breakpoint in the WndProc function, and it seems to get the WM_QUIT message.
What am I doing wrong? Should I be using a different function for posting messages between threads? Thanks.
This the complete answer (I'm almost sure):
replace
::SendMessage( _pobjNotifyWindow->m_hWnd, WM_QUIT, 0, 0 );
with
::PostMessage( _pobjNotifyWindow->m_hWnd, WM_CLOSE, 0, 0 );
replace
( (bRetVal = ::GetMessage( &msg, _pobjNotifyWindow->m_hWnd, WM_DEVICECHANGE, WM_DEVICECHANGE )) != 0 )
with ( (bRetVal = ::GetMessage( &msg, NULL ,0 ,0 )) != 0 )
In your WindowsProcedure :
case WM_CLOSE : DestroyWindow( hWnd ); break; //can be return
case WM_DESTROY : PostQuitMessage( 0 );
While my knowledge of the WinAPI has limits, it seems WM_QUIT is special and not meant to be posted like other messages.
According to Raymond Chen:
Like the WM_PAINT, WM_MOUSEMOVE, and WM_TIMER messages, the WM_QUIT message is not a "real" posted message. Rather, it is one of those messages that the system generates as if it were posted, even though it wasn't.
.
When a thread calls PostQuitMessage, a flag in the queue state is set that says, "If somebody asks for a message and there are no posted messages, then manufacture a WM_QUIT message." This is just like the other "virtually posted" messages.
.
PostThreadMessage just places the message in the thread queue (for real, not virtually), and therefore it does not get any of the special treatment that a real PostQuitMessage triggers.
So you should probably be using PostQuitMessage.
Of course, there may be ways to work around the current odd behavior (as per other answers). But given the description of WM_QUIT being special, you may want to use PostQuitMessage anyway.
There are 2 problems with this code.
::GetMessage() doesn't stop because you're using the hWnd parameter with something else than NULL. You need to fetch the thread messages to get ::GetMessage() to return 0.
Following on the logic in (1), you need to post the message using ::PostThreadMessage() to put it in the thread's message queue.
All of this is rather well illustrated by the fact the ::PostQuitMessage(status) is a shorthand for
::PostThreadMessage(::GetCurrentThreadId(), WM_QUIT, status, 0);
EDIT:
It seems that people have been led into thinking that ::PostThreadMessage(...,WM_QUIT,...); doesn't work because it doesn't get the special treatement of setting the QS_QUIT flag that is set by ::PostQuitMessage(). If that was the case, then there would be no way to send WM_QUIT to another thread's message queue. Here is proof that it works anyways.
In particular, pay attention to the constants Use_PostQuitMessage and GetMessage_UseWindowHandle. Feel free to change the values and play around with the code. It works just as advertised in my answer, except that I mistakenly used ::GetCurrentThread() rather than ::GetCurrentThreadId() before trying it out.
#include <Windows.h>
#include <iomanip>
#include <iostream>
namespace {
// Doesn't matter if this is 'true' or 'false'.
const bool Use_PostQuitMessage = false;
// Setting this to 'true' prevents the application from closing.
const bool GetMessage_UseWindowHandle = false;
void post_quit_message ()
{
if ( Use_PostQuitMessage ) {
::PostQuitMessage(0);
}
else {
::PostThreadMessageW(::GetCurrentThreadId(), WM_QUIT, 0, 0);
}
}
::BOOL get_message ( ::HWND window, ::MSG& message )
{
if ( GetMessage_UseWindowHandle ) {
return (::GetMessageW(&message, window, 0, 0));
}
else {
return (::GetMessageW(&message, 0, 0, 0));
}
}
::ULONG __stdcall background ( void * )
{
// Allocate window in background thread that is to be interrupted.
::HWND window = ::CreateWindowW(L"STATIC", 0, WS_OVERLAPPEDWINDOW,
0, 0, 512, 256, 0, 0, ::GetModuleHandleW(0), 0);
if ( window == 0 ) {
std::cerr << "Could not create window." << std::endl;
return (EXIT_FAILURE);
}
// Process messages for this thread's windows.
::ShowWindow(window, SW_NORMAL);
::MSG message;
::BOOL result = FALSE;
while ((result = get_message(window,message)) > 0)
{
// Handle 'CloseWindow()'.
if ( message.message == WM_CLOSE )
{
post_quit_message(); continue;
}
// Handling for 'ALT+F4'.
if ((message.message == WM_SYSCOMMAND) &&
(message.wParam == SC_CLOSE))
{
post_quit_message(); continue;
}
// Dispatch message to window procedure.
::TranslateMessage(&message);
::DispatchMessageW(&message);
}
// Check for error in 'GetMessage()'.
if ( result == -1 )
{
std::cout << "GetMessage() failed with error: "
<< ::GetLastError() << "." << std::endl;
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
}
int main ( int, char ** )
{
// Launch window & message pump in background thread.
::DWORD id = 0;
::HANDLE thread = ::CreateThread(0, 0, &::background, 0, 0, &id);
if ( thread == INVALID_HANDLE_VALUE ) {
std::cerr << "Could not launch thread." << std::endl;
return (EXIT_FAILURE);
}
// "do something"...
::Sleep(1000);
// Decide to close application.
::PostThreadMessageW(id, WM_QUIT, 0, 0);
// Wait for everything to shut down.
::WaitForSingleObject(thread, INFINITE);
// Return background thread's success code.
::DWORD status = EXIT_FAILURE;
::GetExitCodeThread(thread,&status);
return (status);
}
P.S.:
To actually test the single-threaded use of ::PostThreadMessage(::GetCurrentThreadId(),...); invoke ::background(0); in main instead of launching the thread.
Just a guess. Your quit code is called from within a message loop call from another window which has its own message pump? According to MSDN for WaitForSingleObject you block the current UI thread indefinitely and prevent processing its own messages.
From
http://msdn.microsoft.com/en-us/library/ms687032%28VS.85%29.aspx
Use caution when calling the wait
functions and code that directly or
indirectly creates windows. If a
thread creates any windows, it must
process messages. Message broadcasts
are sent to all windows in the system.
A thread that uses a wait function
with no time-out interval may cause
the system to become deadlocked. Two
examples of code that indirectly
creates windows are DDE and the
CoInitialize function. Therefore, if
you have a thread that creates
windows, use MsgWaitForMultipleObjects
or MsgWaitForMultipleObjectsEx, rather
than WaitForSingleObject.
It could be that the WM_Quit message is broadcast to your own window which does not process any messages due to your WaitForSingleObject call. Try instead MsgWaitForMultipleOjbects which tries to call your message loop from time to time.
Yours,
Alois Kraus
WM_QUIT is not a window message, so you shouldn't be sending it to a window. Try using PostThreadMessage instead:
PostThreadMessage(GetThreadId(_hNotifyWindowThread), WM_QUIT, 0, 0);
If that doesn't work, try posting a dummy message to your window:
::PostMessage( _pobjNotifyWindow->m_hWnd, WM_APP, 0, 0 );
and use it as a signal to quit in your window procedure:
case WM_APP:
PostQuitMessage(0);
Your GetMessage(...) retrieves only message of the window you supplied. However WM_QUIT does not have a window associated with it. You Need to call GetMessage without a window handle (i.e. NULL) which retrieve any message in the Message-Queue.
To elaborate on what TheUndeadFish said; there's a "secret" undocumented QS_QUIT flag set in the wake flags of the thread's message queue when PostQuitMessage is called. GetMessage looks at its wake flags in a particular order to determine what message to process next.
If it finds that QS_QUIT is set, it generates a WM_QUIT message and causes GetMessage to return FALSE.
You can get a thread's documented wake flags that are currently set with GetQueueStatus.
The details can be found in Programming Applications for Microsoft Windows, 4th Edition (unfortunately, the newest edition removed these topics).