Roman Korol
Roman Korol

Reputation: 21

WinApi task killing

I have super simple WinApi program. Close button does not destroy process of the program. What should be added?

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE hINSTANCE;

int WINAPI WinMain(HINSTANCE hInstance,
               HINSTANCE hPrevInstance,
               LPSTR lpstr,
               int nCmdShow) {

// VARIABLES
    TCHAR className[] = _T("win32api");
    TCHAR windowName[] = _T("Protected Window");

    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(NULL, IDI_INFORMATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = className;
    wcex.hIconSm = LoadIcon(NULL, IDI_SHIELD);

    if (!RegisterClassEx(&wcex)) {
        MessageBox(NULL, _T("Cannot register window"), _T("Error"), MB_OK | MB_ICONERROR);
        return 0;
    }

     HWND hWnd = CreateWindow(className,
                              windowName,
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              300,
                              300,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);

    if (!hWnd) {
        MessageBox(hWnd, _T("Call to register windows isn't working"), _T("Error"), NULL);
        return 1;
    }

    hINSTANCE = hInstance;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;

    while (GetMessage(&msg, hWnd, 0, 0) != 0 || GetMessage(&msg, hWnd, 0, 0) != -1) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.lParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uInt, WPARAM wParam, LPARAM lParam) {
    PAINTSTRUCT ps;
    HDC hdc;
    switch (uInt) {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, 10, 20, _T("Text sample right here boyz."), _tcsclen(_T("Text sample right here boyz.")));
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, uInt, wParam, lParam);
        break;
    }

    return 0;
}

If I add one more case

case WM_CLOSE:
    PostQuitMessage(0);
    break;

It doesnt close at all (pressing close button is not doing anything). Any other tips are very appreciated.

Upvotes: 0

Views: 133

Answers (2)

Jim Mischel
Jim Mischel

Reputation: 134045

To close the app, you need to destroy the window. Typically that's done in one of two ways:

  1. Have your WM_CLOSE handler call DestroyWindow.
  2. Have the DefWindowProc handle the message after you do your thing.

Your WndProc is a little out of the ordinary. I usually see them written as:

{    
    switch(msg) 
    {  
        case WM_CLOSE:
            // prompt user, clean stuff up, etc.
            break;

        case WM_DESTROY:
           PostQuitMessage(0);
           break;

        // Other cases here. Most will fall out of the switch so that
        // DefWindowProc can handle them (for other system notifications).
        // Only return from the case if you really don't want anybody else
        // to handle the message.
    }

    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

For specifics on WM_CLOSE, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms632617(v=vs.85).aspx, which specifically says that you must call DestroyWindow or let DefWindowProc do it.

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 597111

Your message loop is wrong, for two reasons:

  1. you are calling GetMessage() twice. Don't do that! Think of what happens if you do. If the GetMessage() call on the left-hand side of || were to detect WM_QUIT (which it can't, see further below), it would return 0. Which would cause || to call the GetMessage() on the right-hand side, ignoring the WM_QUIT from the previous call and blocking the loop until a new message arrives later. You should call GetMessage() only once per loop iteration, and then act on the return value as needed:

    BOOL bRet;
    
    do
    { 
        bRet = GetMessage(&msg, hWnd, 0, 0);
        if (bRet == 0) break;
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        }
    }
    while (1);
    
  2. However, you are also filtering messages by HWND (don't do that!), and so it will only return messages that are posted to the specified HWND via PostMessage(). PostQuitMessage() posts its WM_QUIT message to the input queue of the calling thread itself, not to the HWND. So the filtering will never see the WM_QUIT message. In order for WM_QUIT to break the loop, you need to stop filtering by HWND altogether, or at least make an unfiltered call periodically.

Your message loop should look like this instead. This is the standard message loop:

while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

Note in this case, there is no need to handle -1 from GetMessage()!.

Upvotes: 4

Related Questions