I have a basic window program, the problem is when i try to create a window in a new thread after the message loop has already started the window displays for a second and disappears. does anyone no the reason for this? can a window be created in a separate thread?
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
::hInstance =hInstance; // initialize global variables
::nCmdShow =nCmdShow;
// start thread
HANDLE threadHandle = startThread(StartUp);
MSG msg;
while(GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
::CloseHandle(threadHandle);
return static_cast<int>(msg.wParam);
}
DWORD WINAPI StartUp(LPVOID lpParam) // new thread runs here
{
//code to create a new window...
}
what i figured out so far is that the GetMessage(&msg, 0, 0, 0) returns false if there are no windows in the current thread that it is in... is there a way to get a round this?
GetMessage() does not return FALSE if there are no windows. It is only looking for messages in the calling thread's message queue. You are specifying NULL for its hWnd parameter, so it will not care how messages get queued, whether by PostMessage() to a window, or by PostThreadMessage() to the thread's ID.
Each thread has its own local message queue and thus requires its own message loop. You can most certainly create a new window in a worker thread after the main thread has started its message loop. They are independent of each other. So whatever problem you are having in the main thread is not related to creating a window in a worker thread. Something else is going on.
With that said, keep in mind that GetMessage() returns a BOOL, which is actually an int, not a true bool. GetMessage() can return one of 3 different return values:
-1 if an error occurs
0 if a WM_QUIT message is retrieved
>0 if any other message is retrieved
You are only checking for 0 and != 0, so if GetMessage() returns -1 on error, you are treating it as a success instead of a failure. Even MSDN says not to do that:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644936.aspx
Because the return value can be nonzero, zero, or -1, avoid code like this:
while (GetMessage( lpMsg, hWnd, 0, 0)) ...
The possibility of a -1 return value means that such code can lead to fatal application errors. Instead, use code like this:
BOOL bRet;
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Related
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.
I have a problem with my code, and after several hours, I can't seem to figure it out...
Problem: I'm trying to attempt to connect to a server every ten seconds. When the timer first elapses, the callback function is called just fine, but then it is called again immediately (without waiting ten seconds), and it keeps being called again and again and again repeatedly, as if the timer message is not getting removed from the queue. Can anyone help?
The timer is set here:
SConnect::SConnect()
{
hSimConnect = NULL;
Attempt();
SetTimer(hMainWindow, reinterpret_cast<UINT_PTR>(this), 10000, (TIMERPROC)TimerProc);
}
The (only) application message loop is here:
while (true)
{
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
if (msg.message == WM_QUIT)
break;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//UPDATES
pSCObj->Update();
manager.Update();
Sleep(50);
}
And the timer callback function is here:
void CALLBACK SConnect::TimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime){
SConnect *pSC = reinterpret_cast<SConnect *>(idEvent);
MSG wMsg;
if (!pSC->connected)
pSC->Attempt();
else{
pSC->connected = false;
}
}
I really appreciate any help... Please let me know if you need more info...
Sincerely,
Farley
I think this is happening because you are calling PeekMessage() in your message loop instead of GetMessage().
PeekMessage() will return FALSE if there are no messages in the queue. I don't know what the implementation of PeekMessage() is, but I could see it leaving the contents of the msg parameter alone if there are no messages in the queue. This means that when PeekMessage() returns FALSE, msg will contain the previous message in the queue. msg is then blindly passed to DispatchMessage(), which then dutifully will pass that to your window's window procedure. So as long as the WM_TIMER message is the last processed message, your timer callback will be called until another message is added to the queue.
You can fix this by using a more traditional message loop:
BOOL bRet;
while( (bRet = GetMessage( &msg, nullptr, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
pSCObj->Update();
manager.Update();
}
(Message loop adapted from the sample in the GetMessage() documentation.)
Since GetMessage() will block until there is a message in the queue, you don't need to call Sleep(), and your process will not use any CPU when there is nothing for it to do.
Problem is that the return value of PeekMessage() is not checked. On each loop, if there is no new message, PeekMessage leaves the contents of msg unchanged, and it is simply dispatched again.
Dispatching the messages only when PeekMessage() returns true fixes the problem:
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
if (msg.message == WM_QUIT)
break;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//UPDATES
pSCObj->Update();
manager.Update();
Sleep(50);
}
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.
I have a single window created with the CS_OWNDC style for an engine I'm coding and it's showing properly, but when I'm closing the program and I call the ReleaseDC(hWnd, hDC) the function returns 0 always (and according to MSDN it means it couldn't be released).
The DestroyWindow(hWnd) is also returning 0.
I am calling GetDC(hWnd) during the window creation.
What is weird is that I only get this error if I pass through the messaging function, even if it has nothing coded inside.
My main code is looking like this:
WindowManager windowManager;
HWND mainWindow = windowManager.Initialize("Title", "ClassName", 1024, 768, 32, WindowProc);
MSG msg;
while(true)
{
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT) { break; }
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
windowManager.Clear(); //this calls ReleaseDC(hWnd, DC) and DestroyWindow(hWnd) among other release codes
What I don't understand is that if I comment the whole while loop, I don't get the release or destroy window errors.
Calling GetLastError() gives me a 0 too.
On MSDN it says the DestroyWindow must be called from the same thread, but I'm not coding any multi thread functions so I don't think that's the case.
What could be causing this?
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).