winapiwindows-10retina-displaywin32guihighdpi

Win32 API window application Retina high DPI screen issues on Windows 10


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;
}

Solution

  • 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