WINAPI postquitmessage leaves window hanging - c++

i'm using a Dialog to ask the user for some input, but my window 'hangs' after the user is done (controls don't respond anymore, but it's still visible) and disappears only when the application quits.
Here's my code:
LRESULT CALLBACK Level2Auth(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
switch (Message)
{
case WM_CREATE:
{
{... do stuff ...}
CreateWindow(L"BUTTON", L"Connect",
WS_VISIBLE | WS_CHILD | WS_BORDER,
370, 10, 70, 20,
hwnd, (HMENU)1, NULL, NULL);
break;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case 1:
{
{... retrieve input ...}
Level2Auth(NULL, WM_DESTROY, NULL, NULL);
break;
}
default:
{
break;
}
}
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
And the message loop:
INT WINAPI launchLevel2Auth()
{
MSG Msg; HWND hwnd;
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, L"Level2AuthPopUp", L"Remote PKCS#11 PIN entry", WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
640,
100,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
return 0;
}
while (GetMessage(&Msg, NULL, 0, 0) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
My intention was to retrieve some data in a text field as the "Connect" button was clicked, and then to close the window.
Thanks a lot for your answers.

When you are calling your own message handler with WM_DESTROY message directly, you are bypassing any cleanup, that needs to be done by WINAPI framework. Instead, use DestroyWindow:
...
switch (LOWORD(wParam))
{
case 1:
{
{... retrieve input ...}
DestroyWindow (hwnd);
// Level2Auth(NULL, WM_DESTROY, NULL, NULL);
break;
}
...
Article for further reading: Destroying Windows in WINAPI.

Related

Closing an app not sending WM_QUIT message?

Having such a simple Win32 app:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR pCmdLine, int nCmdShow) {
...
HWND hwnd = CreateWindowEx(0, CLASS_NAME, L"Learn to Program Windows", WS_POPUP | WS_BORDER, 0, 0, 190, 110, nHwnd, NULL, hInstance, NULL);
if (hwnd != NULL) {
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT) {
swprintf_s(msgbuf, _T("WM_QUIT (main)\n"));
OutputDebugString(msgbuf);
}
}
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
swprintf_s(msgbuf, _T("WM_DESTROY\n"));
OutputDebugString(msgbuf);
PostQuitMessage(0);
return 0;
case WM_CLOSE:
swprintf_s(msgbuf, _T("WM_CLOSE\n"));
OutputDebugString(msgbuf);
DestroyWindow(hwnd);
return 0;
case WM_QUIT:
swprintf_s(msgbuf, _T("WM_QUIT\n"));
OutputDebugString(msgbuf);
return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
Example_DrawImage9(hdc);
EndPaint(hwnd, &ps);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
When i do Alt+F4 to close it i get WM_CLOSE, WM_DESTROY but NOT WM_QUIT? Why don't I get the WM_QUIT message?
Reading documentation at https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-quit:
The WM_QUIT message is not associated with a window and therefore will
never be received through a window's window procedure. It is retrieved
only by the GetMessage or PeekMessage functions.
Do not post the WM_QUIT message using the PostMessage function; use
PostQuitMessage.
=== Portion after this was added after answer accepted for further clarification ===
As others have noted, there are sort of two things going on in the original code submitted. In the message map, there is a switch case entry for WM_QUIT. The documentation I quoted shows that the message is not for windows and so the case statement will never get processed.
However, there is another issue going on. Look at the message pumping:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT) {
swprintf_s(msgbuf, _T("WM_QUIT (main)\n"));
OutputDebugString(msgbuf);
}
}
When GetMessage() is called and the WM_QUIT message is the message retrieved in the queue, then the return value from GetMessage() will be zero and so it will immediately exit the while loop.
This is documented at https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage
If the function retrieves a message other than WM_QUIT, the return
value is nonzero.
If the function retrieves the WM_QUIT message, the return value is
zero.

How to correctly destroy window that is running from another thread?

The goal that I am trying to achieve is to listen session notifications about lock, unlock and so on. I have to do it in another thread due to architecture and avoiding blocking main thread. Here is what I am doing:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_WTSSESSION_CHANGE:
switch (wParam) {
case WTS_CONSOLE_CONNECT:
case WTS_SESSION_LOGON:
case WTS_REMOTE_CONNECT:
case WTS_SESSION_UNLOCK:
case WTS_CONSOLE_DISCONNECT:
case WTS_REMOTE_DISCONNECT:
case WTS_SESSION_LOGOFF:
case WTS_SESSION_LOCK:
case WTS_SESSION_REMOTE_CONTROL:
case WTS_SESSION_CREATE:
case WTS_SESSION_TERMINATE:
break;
default:
break;
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
void startListeningNotifications()
{
const wchar_t g_szClassName[] = L"myWindowClass";
WNDCLASSEX wc = {};
wc.lpfnWndProc = WndProc;
wc.lpszClassName = g_szClassName;
wc.cbSize = sizeof(WNDCLASSEX);
if (!RegisterClassEx(&wc))
{
return;
}
HWND hwnd = CreateWindowEx(NULL, g_szClassName, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL);
if (hwnd == NULL)
{
return;
}
if (!WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_ALL_SESSIONS))
{
return;
}
ShowWindow(hwnd, SW_HIDE);
MSG Msg = {};
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
int main()
{
std::thread listener(startListeningNotifications);
listener.join();
return 0;
}
The problem that I am facing right now is that I do not know how to correctly write stopListeningNotifications() method. I have to somehow destroy my window and quit message loop. Please advice how to do that safely. Thanks in advance.
Provided WndProc function handles WM_CLOSE and WM_DESTROY.
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
So you could send WM_CLOSE message with hwnd of the window created in the thread:
PostMessage(hwnd, WM_CLOSE, 0, 0)

Win32Api Window Menu Activating issue

Running my program it runs and due to me having a menu with EXIT to Destroy the window it runs and immediately exits the window. Unsure how to fix my issue here on compiling the program to have it not run the WindowProcedure function and passing the argument EXITMENU resulting in the Window being destroyed.
*.CPP
#include <windows.h>
#define HELPMENU 1
#define HIGHSCROREMENU 2
#define EXITMENU 3
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow) {
WNDCLASS wc = { 0 }; // WNDCLASSW is a structure
LPCWSTR title = L"Window"; // Long Pointer Constant Wide (UTF-16) String
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; // Background
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_HAND); // Sets Cursor
wc.hInstance = hInst; // Instance of window
wc.lpszClassName = L"windowClass"; // Class name
wc.lpfnWndProc = WindowProcedure; // Pointer to the function // Controller of window handle
if (!RegisterClassW(&wc)) { // Registers the window class
return -1;
}
// | binary combination value, posX, posY, Width, Height
// Creates the window
CreateWindow(wc.lpszClassName, title, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_BORDER, 100, 100, 800, 600, NULL, NULL, NULL, NULL);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, NULL, NULL) > 0) { // Keeps the window running
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
/* Event Paths */
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_CREATE: // On window creation
AddControls(hWnd);
AddMenu(hWnd);
break;
case WM_LBUTTONDOWN: // Left Mouse button
break;
case WM_DESTROY: // Makes GetMessage Function return false, closing the window
PostQuitMessage(0);
return 0;
case EXITMENU:
DestroyWindow(hWnd); // This part of the code shouldn't run on creation
break;
default:
return DefWindowProc(hWnd, msg, wp, lp);
}
}
/* Creates menu */
void AddMenu(HWND hWnd) {
hMenu = CreateMenu(); // Creates menu object
// AppendMenu(Menu Instance, Usage Type, Argument, String info);
AppendMenu(hMenu, MF_STRING, HELPMENU, L"Help - F1");
AppendMenu(hMenu, MF_STRING, HIGHSCROREMENU, L"Highscores - F2"); // Menu Created
AppendMenu(hMenu, MF_STRING, EXITMENU, L"Exit - ESC");
// SetMenu(Window Handle , Menu Instance);
SetMenu(hWnd, hMenu); // Sets menu for window //
}
You are not handling the menu commands correctly in your WindowProcedure().
You have defined EXITMENU as 3, which is the same value as the WM_MOVE message. So, in your switch, you are destroying your window as soon as it receives a WM_MOVE message during window creation.
You need to instead handle the menu commands via the WM_COMMAND message, per the documentation:
About Menus: Messages Used With Menus
When the user chooses a command item from a menu, the system sends a WM_COMMAND message to the window procedure. The low-order word of the WM_COMMAND message's wParam parameter contains the identifier of the chosen item. The window procedure should examine the identifier and process the message accordingly.
Try this instead:
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
...
case WM_COMMAND:
switch (wp) {
case HELPMENU: {
...
return 0;
}
case HIGHSCROREMENU: {
...
return 0;
}
case EXITMENU: {
DestroyWindow(hWnd);
return 0;
}
}
break;
}
...
}
return DefWindowProc(hWnd, msg, wp, lp);
}
UPDATE: That being said, consider having your EXITMENU handler use SendMessage(WM_CLOSE) instead of DestroyWindow(). If your app maintains data that should be saved when the app is closed by the user, you can add a WM_CLOSE handler to perform that action regardless of how the window is being closed (your exit menu, X close button, Alt-F4, etc). DefWindowProc() destroys a window when processing WM_CLOSE.
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
...
case WM_CLOSE: {
if (data has been modified) {
prompt user to save data...
if (cancelled) {
return 0;
}
if (should save) {
save data ...
}
}
break;
}
case WM_COMMAND:
switch (wp) {
...
case EXITMENU: {
SendMessage(hWnd, WM_CLOSE, 0, 0);
return 0;
}
}
break;
}
...
}
return DefWindowProc(hWnd, msg, wp, lp);
}

Edit control doesn't work correctly

I started to learning winapi c++ today. And i wrote this simple program that have to change window name to the name that i write in text box:
#include <Windows.h>
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
void AddMenu(HWND);
void AddControl(HWND);
HWND hEdit;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrewInst, LPSTR args, int ncmdshow)
{
WNDCLASSW wc = { 0 };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hInstance = hInst;
wc.lpszClassName = L"class";
wc.lpfnWndProc = WindowProcedure;
if (!RegisterClassW(&wc))
return -1;
CreateWindowW(L"class", L"Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 500, 500, NULL, NULL, NULL, NULL);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
case WM_COMMAND:
switch (wp)
{
case 1:
MessageBeep(MB_SERVICE_NOTIFICATION); break;
case 2:
DestroyWindow(hWnd); break;
case 3:
wchar_t text[100];
GetWindowTextW(hEdit, text, 100);
SetWindowTextW(hWnd, text);
break;
}
case WM_CREATE:
AddMenu(hWnd);
AddControl(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hWnd, msg, wp, lp);
}
}
void AddMenu(HWND hWnd)
{
HMENU hMenu;
hMenu = CreateMenu();
HMENU file;
file = CreateMenu();
HMENU Sub;
Sub = CreateMenu();
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)file, "File");
AppendMenu(file, MF_STRING, 1, "Open");
AppendMenu(file, MF_POPUP, (UINT_PTR)Sub, "Sub");
AppendMenu(file, MF_STRING, 2, "Exit");
AppendMenu(Sub, MF_STRING, 3, "Change");
SetMenu(hWnd, hMenu);
}
void AddControl(HWND hWnd)
{
CreateWindowW(L"Static", L"Enter text: ", WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER, 200, 100, 100, 20, hWnd, NULL, NULL, NULL);
hEdit = CreateWindowW(L"Edit", L"...", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL, 200, 152, 100, 50, hWnd, NULL, NULL, NULL);
}
By default in text box i have .... So i run my program, starting to type hello world for expample but nothing changes. I highlited my 3 dots that had to change and boom, my text appeared. I go to the menu choose File -> Sub -> Change and my window name changes to 3 dots ....
BUT! If i delete these: | ES_MULTILINE | ES_AUTOVSCROLL then text box working correctly but menu doesnt show up in my window.
I found that i have to do smth with EN_CHANGE but idk what. I tried to change WM_COMMAND to EN_CHANGE and my textbox worked correctly, but other menu buttons didnt work at all...
Your failure is caused by handling all the edit control notifications the same way. You need to test the notification code (found in HIWORD(wParam)) to see whether it is EN_CHANGE, and let notifications you aren't handling pass through to DefWindowProc.
You also have an even bigger bug in that you reach the closing brace of a function with a non-void return type. You need to always return something; when you aren't calling DefWindowProc the system still expects a return value. The reason things start working when you change WM_COMMAND to EN_CHANGE is not because you are handling EN_CHANGE, it is because you no longer have incorrect handling of WM_COMMAND, and instead let the DefWindowProc take them.
The fall-through from WM_COMMAND into WM_CREATE isn't helping things either.
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
switch (HIWORD(wParam)) /* command kind */
{
case 0: // menus
switch (LOWORD(wParam)) /* child ID */
{
case 1:
MessageBeep(MB_SERVICE_NOTIFICATION);
return 0;
case 2:
DestroyWindow(hWnd);
return 0;
case 3:
wchar_t text[100];
GetWindowTextW(hEdit, text, 100);
SetWindowTextW(hWnd, text);
return 0;
}
break;
}
break;
case WM_CREATE:
AddMenu(hWnd);
AddControl(hWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
// break actions also reach here
return DefWindowProcW(hWnd, msg, wParam, lParam);
}

Creating a Window in WinAPI after pressing a button

I'm making an auto clicker for a game in WinAPI and I have 4 simple buttons on the main window. When the user presses the 'start' button I want another window to open asking them for settings such as number of times to click and time between clicks. When I try to create a new window, nothing is happening, but everything else works perfectly.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_COMMAND:
{
switch (wParam)
{
case ID_START:
{
HINSTANCE hInstance = GetModuleHandle(CLASS_NAME);
HWND settings = CreateWindowEx(
0,
L"Settings",
L"Settings",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CHILD,
100, 100, 600, 200,
NULL,
(HMENU) ID_SETTINGS,
hInstance,
NULL
);
MSG msg = { };
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
case ID_QUIT:
{
PostQuitMessage(0);
return 0;
}
case ID_CALIB:
{
if (MessageBox(hwnd, L"You pressed Calibrate", L"Calibrate", MB_OK))
{
return 0;
}
}
case ID_INFO:
{
if (MessageBox(hwnd, L"You pressed about", L"About", MB_OK))
{
return 0;
}
}
}
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
I just started WinAPI today, so I am extremely new. Thanks for any help in advance!
The second parameter to CreateWindowEx must be a class name that you registered earlier by calling RegisterClass.
You are specifying WS_CHILD. But a child must have a parent. Pass the parent HWND into the hwndParent parameter.