1
votes

The subject: simple C++ Win32 API based single window application. See the code below. Computer is MacBook Retina with Windows 10 installed natively.

The problem: minimize/maximize/close buttons in the title bar (non client area of the window) behave incorrectly on mouse hover event. Each button is highlighted only when the mouse cursor moves while the button should be highlighted all the time until the mouse pointer leaves the area of the button.

The question: what is the problem? Win10 manifest?

The code:

#include <Windows.h>
#include <tchar.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    TCHAR msgGreeting[] = _T("Hello World from MyWindowsApp!");

    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        TextOut(hdc, 10, 10, msgGreeting, (int)_tcsclen(msgGreeting));
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }

    return 0;
}

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    // 1. Initialize the application
    // 2. Display the main window
    WNDCLASSEX wndClassWindowMain;
    wndClassWindowMain.cbSize = sizeof(WNDCLASSEX);
    wndClassWindowMain.style = CS_VREDRAW | CS_HREDRAW;
    wndClassWindowMain.lpfnWndProc = WndProc;
    wndClassWindowMain.cbClsExtra = 0;
    wndClassWindowMain.cbWndExtra = 0;
    wndClassWindowMain.hInstance = hInstance;
    wndClassWindowMain.hIcon = LoadIcon(wndClassWindowMain.hInstance, IDI_APPLICATION);
    wndClassWindowMain.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClassWindowMain.hbrBackground = (HBRUSH)(COLOR_WINDOW);
    wndClassWindowMain.lpszMenuName = NULL;
    wndClassWindowMain.lpszClassName = TEXT("MyMainWindowClass");
    wndClassWindowMain.hIconSm = LoadIcon(wndClassWindowMain.hInstance, IDI_APPLICATION);

    if (0 == RegisterClassEx(&wndClassWindowMain))
    {
        MessageBox(NULL
            , _T("Call to RegisterClassEx failed!")
            , _T("MyWindowsApplication")
            , MB_ICONERROR | MB_OK);
        return FALSE;
    }

    HWND hwndWindowMain = CreateWindowEx(WS_EX_APPWINDOW
        , _T("MyMainWindowClass")
        , _T("My Window")
        , WS_OVERLAPPEDWINDOW
        , 100, 100
        , 640, 480
        , NULL
        , NULL
        , hInstance
        , NULL);
    if (NULL == hwndWindowMain)
    {
        MessageBox(NULL
            , _T("Call to CreateWindowEx failed!")
            , _T("MyWindowsApplication")
            , MB_ICONERROR | MB_OK);
        return FALSE;
    }
    ShowWindow(hwndWindowMain, SW_SHOWDEFAULT);
    UpdateWindow(hwndWindowMain);
    // 3. Go to the message retrieval-and-dispatch loop
    MSG msg;
    BOOL bRet;

    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRet == -1)
        {
            // handle the error and possibly exit
            return FALSE;
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int)msg.wParam;
}
1
C and C++ are different languages. Please use only the relevant tag.kaylum
@kaylum For this case there is no difference as the case is about platform and not the language.nickolay
@kaylum the code that I showed is C++ but easily portable to C. WinAPI used by C and C++ programmers so having both tags will help the target audience finding this thread. BTW I already found the solution. Please enjoy.nickolay
@kaylum: The code is valid C and valid C++. It is not valid Pascal, Delphi, Rust, Go, or Python. Why limit the scope of discoverability by arbitrarily picking C over C++, or vice versa? How would that make this Q&A any more discoverable? Do you have a convincing answer to this?IInspectable
The language is not pertinent to the question. It's a question about winapi. It's best to tag it as such. A Delphi programmer that knows winapi will be just as well placed to answer as a C or c++ programmer.David Heffernan

1 Answers

5
votes

Solution:

The problem was in DPI (dots per inch) awareness of the app from the example above. What you need to do in order to make your native C++/Win32 API code compatible with different screens (including 4K and Retina) is to follow these steps:

  1. include the appropriate header #include <ShellScalingApi.h>
  2. tell the linker where the compiled function code is #pragma comment(lib,"Shcore.lib")
  3. finally in WinMain add the following call SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);

Voila!

Now on Retina I see my app window scaled appropriately. Minimize/Maximize/Close buttons work as expected.

Further reading is here: https://msdn.microsoft.com/en-us/library/windows/desktop/dn302122(v=vs.85).aspx