0

I had practised winapi apps before, but never got this problem.

I have tried making a custom WindowProc to override default behaviour for WM_QUIT/WM_CLOSE/WM_DESTROY messages to do nothing, but the window still closes when I press ALT!

This is the entire code:

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
    const LPCSTR className = "Class name";
    WNDCLASS wc = {};
    wc.style = 0;
    wc.lpfnWndProc = DefWindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(nullptr);
    wc.hIcon = nullptr;
    wc.hCursor = nullptr;
    wc.hbrBackground = nullptr;
    wc.lpszMenuName = nullptr;
    wc.lpszClassName = className;

    RegisterClass(&wc);
    HWND window = CreateWindow(className, "Title", WS_OVERLAPPEDWINDOW, 200, 200, 600, 400, nullptr, nullptr, nullptr, nullptr);

    ShowWindow(window, SW_SHOW);

    MSG msg = {};

    while (msg.wParam != WM_QUIT)
    {
        while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return msg.wParam;
}

I believe the window shouldn't close when I press ALT, but it does.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
KijeviGombooc
  • 304
  • 2
  • 13
  • 1
    When you press ALT, condition `msg.wParam != WM_QUIT` result in `0` so the while loop end and execution go to `return msg.wParam;` then program exit. You can set a breakpoint at `return msg.wParam;` line to see what happened. – Rita Han Oct 21 '19 at 02:07

2 Answers2

4

You're mixing the message field with the wParam field:

while (msg.wParam != WM_QUIT)

When you press ALT, the window receives a WM_KEYDOWN message whose wParam is the virtual keycode. It so happens that ALT has the same VK code as the constant for WM_QUIT (0x12).

In addition, you're also translating and dispatching WM_QUIT before quitting. You can deal with both of these by having a single level of indentation with an appropriate check:

while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) && msg.message != WM_QUIT)

As pointed out in the comment, this is cleaner with GetMessage:

while (GetMessage(&msg, nullptr, 0, 0) > 0)
chris
  • 60,560
  • 13
  • 143
  • 205
3

There are a few problems with your code.

First, you should be using GetMessage() and not PeekMessage(). The fundamental difference is that PeekMessage() is non-blocking, and therefore your program will use 100% of a CPU running that loop.

Also, because the inner loop may or may not loop through multiple messages in a row, checking things on the outer loop like you're doing will be a hit or miss and won't check all messages. You should be checking every message in the inner loop instead.

But in this case it's not necessary since this loop will end if the Window is closed anyway. This check you're doing is pointless.

There are a few other inconsistencies with your code, wc.hInstance should be hInstance, that one you received as parameter in WinMain(). There's no need to go looking for this information using APIs.

Also CreateWindow() should receive hInstance again as it's second to last parameter, not nullptr.

And finally as pointed out by chris' answer, you should be looking for the message type in msg.message and not in msg.wParam.

In addition to all that, this is no place to do a "custom WindowProc". If you want to do a custom WndProc then you should set wc.lpfnWndProc to your own function instead of DefWindowProc, and there you can define custom behavior to your Window.

For example:

LRESULT CALLBACK MyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CLOSE:
    case WM_DESTROY:
        // do nothing
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

Note there's no need to test WM_QUIT as this message will only happen if your application calls PostQuitMessage().

Havenard
  • 27,022
  • 5
  • 36
  • 62
  • Hi, thanks for the answer!I use `PeekMessage()` instead of `GetMessage()` because I want to update my code even when there are no messagesand if I know well, I should use `PeekMessage()` for that. As for the WindowProc, I know that I must create my ownand put that in `wc.lpfnWndProc`, I just used DefWindowProc for this example, to make it as simple as possible. – KijeviGombooc Oct 20 '19 at 09:49
  • I use an inner loop because if I know well If I do it like this: `while(IS_RUNNING) { if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } }` then it will read a message, do the update stuff I want, then this again, reading messages one by one.If I do it like I did originally in the post, then it reads all messages in the queue, then does update my other stuff, then repeats itself.Or doesn't it? – KijeviGombooc Oct 20 '19 at 09:49
  • @KijeviGombooc There's no need for any of that, you can set up all sorts of triggers for your code that are processed through messages. Timers that are triggered periodically (`WM_TIMER`), Asynchronous Sockets (`WM_WSAASYNC`) and probably others, but those should cover most use cases. I never had a program where it needed more than that simple `GetMessage()` loop in it, everything else being processed in the `WndProc`. – Havenard Oct 20 '19 at 15:01