c++winapidpi-aware

Winapi scaling in different machines


I've been trying to design a small interface with winapi (no MFC) for some weeks and there is a problem I've not been able to solve. The creation of my window is specified in pixels, but the size of my controls is in logical units.

I thought the solution would be to use the MulDiv function when I create the font for my window:

#include <windows.h>

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;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

const char * g_szClassName = "SampleWindow";


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
    //Let's register our window class
    WNDCLASSEX wc;
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = wndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = CreateSolidBrush(RGB(240,240,240));
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm       = NULL;

    if(!RegisterClassEx(&wc)) {
        return 0;
    }

    DWORD dwStyle=( (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX)&~WS_MAXIMIZEBOX);
    RECT rect;
    rect.left = rect.top = 0;
    rect.right = 300;
    rect.bottom = 100;
    ::AdjustWindowRect(&rect, dwStyle, false);

    HWND mWindow = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "Sample Window",
        dwStyle,
        CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,// (x,y,width,height)
        NULL, NULL, hInstance, NULL);

    if(mWindow == NULL) {
        return 0;
    }

    HDC hdc = GetDC(mWindow);

    NONCLIENTMETRICS ncm;
    ncm.cbSize = sizeof(NONCLIENTMETRICS);
    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
    ncm.lfMessageFont.lfWidth = 0;
    ncm.lfMessageFont.lfHeight = -MulDiv(16, GetDeviceCaps(hdc, LOGPIXELSY), 72);
    HFONT normalFont = CreateFontIndirect(&ncm.lfMessageFont);

    HWND someText = CreateWindow( "Static", "This text will scale badly", 
        WS_VISIBLE | WS_CHILD | SS_LEFT,  // Styles 
        10, 10, 500, 30, // (x, y, width, height)
        mWindow, (HMENU) NULL, hInstance, NULL);

    SendMessage(someText, WM_SETFONT, (WPARAM) normalFont, MAKELPARAM(TRUE, 0));

    ShowWindow(mWindow, SW_SHOW);
    UpdateWindow(mWindow);

    //Run the window
    MSG Msg;
    while(GetMessage(&Msg, NULL, 0, 0) > 0) {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

    DeleteObject(normalFont);
    ReleaseDC(hdc);
}

Normally I compile the code with MSVC2010, but this minimal example was compiled with gcc 7.2.0 (MinGW 64 bit). This works in my computer, also playing with the scaling settings of windows 10. However, I've noticed in some computers the text looks bigger. I just can't get a window with a control to preserve the ratio between the window and the elements across all test machines. My application is not DPI aware, so every element should be scaled the same way, but it is every Edit and Static control which just seems to disobey the 16pt Font I'm asking for.

What is the correct way to get a ratio-preserving window with winapi?

Here is an example of what happens. The correct picture is as it looks in my machine (changing the scaling does scale the whole window, and it preserves the ratio between the font and the window) and the right is a Dell computer running the same program (Changing the scaling parameters modifies nothing, the whole window gets scaled but the text still looks bad). Normal case Badly scaled case

Thank you.


Solution

  • Do not adjust text size yourself in a non-DPI aware application.

    Either let the OS take care of all adjustments, or else manifest your application as DPI-aware and calculate size changes yourself.

    Now, this doesn't mean that you should hard-code sizes in a non-aware application. The best thing to do is to use SystemParametersInfo to query for the user's preferred font size, just as you are already doing for the font face. Do not apply adjustments based on LOGPIXELSX and LOGPIXELSY on top of that.