In the Windows API, I learn how the GetMessage function works. I have seen 3 Windows message loop implementations and would like to learn them.
one)
At the time of this writing, this MSDN article describes what I think is the right way to implement a message loop.
MSG msg; BOOL bRet; while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { if (bRet == -1) {
2)
On the GetMessage page, I see this implementation:
MSG msg; BOOL bRet; while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0) { if (bRet == -1) {
3)
Finally, Visual Studio Documentation has this implementation as part of a demonstration of Win32 applications.
MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
Discussion
In short, implementation # 3 ignores errors returned from GetMessage , but otherwise works the same way as the first implementation. That is, they process all messages for the current thread. And when the GetMessage function returns 0 , the loops end.
Since I found implementation # 2 to # 1, I thought it was complete. However, I noticed that GetMessage does not return 0 when a WM_QUIT message WM_QUIT sent via PostQuitMessage
This led to some confusion until I found implementation # 1 and tested it. The difference between the first two implementations is the second GetMessage parameter. In # 2, it points to hWnd , which according to the GetMessage documentation:
handle to the window whose messages should be received. The window must belong to the current thread.
In # 1, this is NULL related to this excerpt:
If hWnd is NULL, GetMessage retrieves messages for any window belonging to the current thread, and any messages in the current thread's message queue whose hwnd is NULL (see MSG structure). Therefore, if hWnd is NULL, both window messages and stream messages are processed.
When testing using NULL , the GetMessage function returns 0 when processing the WM_QUIT message, successfully completing the loop.
Questions
Even if PostQuitMessage is called from a specific window callback function, does WM_QUIT belong to the window or the current thread? Based on testing these three implementations, it seems to be related to the current thread.
If it is thread related, when is it useful or advisable to use a valid hWnd as a parameter for GetMessage ? Such a message loop will not be able to return 0 in response to WM_QUIT , so is there any other way that the message loop should end?
References
The code
#include <Windows.h> #include <tchar.h> #include <strsafe.h> LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int nCmdShow) { LPCTSTR wndClassName =_T("Class_SHTEST"); LPCTSTR wndName = _T("SHTest"); WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW|CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); wcex.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); wcex.hbrBackground = (HBRUSH) COLOR_WINDOW+1; wcex.lpszMenuName = NULL; wcex.lpszClassName = wndClassName; wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), wndName, MB_OK|MB_ICONERROR); } HWND window = CreateWindow(wndClassName, wndName, WS_OVERLAPPEDWINDOW | WS_MAXIMIZE, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); if (!window) { MessageBox(NULL, _T("Call to CreateWindow failed!"), wndName, MB_OK|MB_ICONERROR); } ShowWindow(window, SW_SHOW); UpdateWindow(window); //Message loop (using implementation #1) MSG msg; BOOL bRet; while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { if (bRet == -1) { //Handle error and possibly exit. } else { TranslateMessage(&msg); DispatchMessage(&msg); } } //Return the exit code in the WM_QUIT message. return (int) msg.wParam; }