Creating a Per-Monitor DPI Aware Win32 Application

If you move the window to a monitor with a different DPI setting, DWM will scale up or down the application and you will see blurry text on the new display. If I want this simple application to adapt itself to a different display or to dynamic changes in the DPI settings, it is necessary to change its manifest file to convert it to a per-monitor DPI aware application and make a few small changes in the code to listen for changes in the DPI settings or scale factor. The use of a scale helper class will make it easy to perform the conversion.

The following lines add the code that has to be executed with the WM_DPICHANGED message in the WndProc function. This message tells the program that most of its window is on a monitor with a new DPI setting. The wParam contains the new DPI setting and the lParam contains a window rectangle scaled for the new DPI. It is necessary to ask the scale helper to recalculate the scale based on the new DPI setting, resize the window, create scaled fonts, and render again the window content.

case WM_DPICHANGED:
// wParam = New DPI setting
// lParam = Window rectangle scaled for the new DPI
// Use the new DPI retrieved from the wParam to calculate the new scale factor
g_ScaleHelper.SetScaleFactor(LOWORD(wParam));
// Get the window rectangle scaled for the new DPI, retrieved from the lParam
LPRECT lprcNewScale;
lprcNewScale = (LPRECT)lParam;
// Resize the window for the new DPI setting
SetWindowPos(hWnd, HWND_TOP, lprcNewScale->left, lprcNewScale->top, RectWidth(*lprcNewScale), RectHeight(*lprcNewScale), SWP_NOZORDER | SWP_NOACTIVATE);
// Create new scaled fonts for the new DPI setting
CreateFonts(hWnd);
// Render the window contents again
RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
break;

Right-click on the project name (DPIAware) within Solution Explorer and select Properties. Visual Studio will show the Property Pages dialog box for the project. Then, select Configuration Properties | Manifest Tool | Input and Output | DPI Awareness, select Per Monitor High DPI Aware from the dropdown menu, and click OK. Execute the application, and perform one of the following actions:

Change the DPI settings for the display in which the application is showing the window.

Move the application window to another display with a different DPI setting than the display in which the application started.

You will see how the application changes its window, button, and text sizes and keeps displaying crisp text, taking full advantage of the new DPI setting (see Figure 2).

However, there is a small problem with the value generated for the manifest file. Now, the application is per-monitor DPI-aware, but in Windows versions prior to 8.1, that value isn't recognized. Thus, Windows Vista, Windows 7, and Windows 8 will consider the application as a DPI-unaware application and the DWM will scale it. Unless you only target Windows 8.1, you won't want this to happen.

You can solve this problem creating an XML manifest file with a special value for dpiAware: True/PM. This value sets the application to per-monitor DPI-aware on Windows 8.1, but sets the application to system DPI-aware on Windows Vista through Windows 8. This way, your application will behave as a system DPI-aware one on Windows versions lower than 8.1. The following lines show the XML code for the manifest you have to create:

You need to save the XML manifest and tell Visual Studio to merge it. Right-click on the project name (DPIAware) within Solution Explorer and select Properties. Visual Studio will show the Property Pages dialog box for the project. Then, select Configuration Properties | Manifest Tool | Input and Output | DPI Awareness, and select None. You don't want Visual Studio to specify and DPI awareness configuration in the manifest because you provide it with the manifest file. Then, specify the full path for the XML manifest file you saved in Additional Manifest Files and click OK. Now, the application will work as a per-monitor DPI aware application in Windows 8.1, and as a system DPI aware application in previous Windows versions.

As you can see from this simple example, if you code a scale helper class, it is pretty easy for you to convert a system DPI-aware application into a per-monitor DPI aware one. You can use either the retrieved DPI value or the calculated scale factor to load the necessary resources that have been prepared for the different scale factors you want to support. End users will definitely welcome your investment in providing crisp and clear text and images for their different high-DPI displays.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!