Making DPI-aware applications for Windows Vista

Log-in or register.
rsrc/DPIa.png image

DPI-aware applications in Windows Vista and Windows 7

February 21st 2007

This article explains how to optimize your application for high-DPI modes popularized by Windows Vista. Find out, what problems are you likely to encounter during the process and how to solve them.

DPI mode in Vista and XP

Microsoft has supported high DPI modes in previous versions of Windows, but until now, they were used only seldom. There were good reasons to avoid them, but most of the reasons do not apply anymore in Windows Vista. In Vista and Win 7, selecting DPI mode is easy, the OS itself is optimized for these modes and delivers a superior user experience. Vista can deal with legacy applications that ignore DPI setting. The advances in computer hardware - LCDs, faster graphics adapters, larger memory chips - made it feasible to create DPI-independent applications and it is only logical that Microsoft encourages their development.

Selecting DPI in Vista

Switching DPI in Windows Vista

To switch DPI in Vista:

  • Right-click on an empty space on desktop and select "Properties" from context menu.
  • Click on "Adjust font size (DPI)".
  • Select one of the recently used resolutions.

To define your own resolution or to switch between XP scaling mode and Vista scaling mode, click the "Advanced" button.

Selecting DPI in XP

Switching DPI in Windows XP

To switch DPI in XP and previous systems, you need to:

  • Right-click on an empty space on desktop and select "Properties" from context menu.
  • Switch to the "Settings tab".
  • Click "Advanced" button at the bottom of the property page.
  • Select new value in the combo-box.

DPI-aware and legacy applications

Depending on the application and on user settings, there are 3 possible ways how would Vista deal with the situation:

DPI aware applications

If an application declares itself DPI-aware, Vista will make no attempt to interfere with the application behavior and the application must take care of its scaling. Default fonts and GUI elements are using scaled values.

Not DPI aware - Vista mode

This mode is the default one when resolution is set to more than 120 DPI.

The operating system pretends that the application is running at 96 DPI. All fonts and metrics are using 96 DPI values. When displaying the application window, the graphics is zoomed to fit the actual resolution. As a result, the graphics will not be as sharp as it could be on the current display.

Not DPI aware - XP mode

This mode is the default one when resolution is set to 120 DPI or less.

Default fonts and GUI elements are using scaled value. If the application ignores the system settings and uses hardcoded values instead, the user experience will suffer. But since 96 DPI and 120 DPI are not that far away and many legacy applications work correctly in 120 DPI, Microsoft decided to use this mode instead of the Vista mode for not-so-high DPI modes.

Note: it is ultimately the user's decision whether they will use XP or Vista scaling for not DPI-aware apps.

Making your application DPI aware

Declaring an application DPI-aware is easy - just call the SetProcessDPIAware API function. But this is only the first step. The real work is to update your application to properly use scaled fonts and images. The following paragraphs will direct your attention to the most common issues related to scaling.

Calling SetProcessDPIAware

This is a new API in Vista's user32.dll. If you want your applications to be compatible with previous systems, you cannot link to is statically. Instead you should use for example the following code:

HMODULE hUser32 = LoadLibrary(_T("user32.dll"));
typedef BOOL (*SetProcessDPIAwareFunc)();
SetProcessDPIAwareFunc setDPIAware = (SetProcessDPIAwareFunc)GetProcAddress(hUser32, "SetProcessDPIAware");
if (setDPIAware)
    setDPIAware();
FreeLibrary(hUser32);

SetProcessDPIAware must be called as soon as possible. That may be a problem for MFC applications, which execute their own code before InitInstance is called.

Using manifest to declare DPI awareness

Using manifests is the preferred way to mark applications DPI aware, because it is simpler and safer. The following manifest fragment demonstrates how to use the dpiAware element.

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <asmv3:windowsSettings
         xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

You can simply save the above code to a file called "DPIaware.manifest", add it to your projects and use the MT.exe tool to merge it with your other manifest fragments. If you are using Visual Studio 2005, go to your application settings, switch to Manifest Tool, then Input and Output and add "$(ProjectDir)DPIaware.manifest" to the Additional Manifest Files field.

Merging manifests in VS 2005.
Merging DPIaware.manifest with other (generated) manifests in VS 2005.

Dialogs

Dialogs use "dialog units" instead of pixels to measure size of controls and are therefore DPI independent. Except if you use dialogs with custom fonts - don't do that!

Still, it pays off to test all your dialogs in higher DPI. Expect problems with static text controls - labels. If you are using Visual Studio to design dialog resources, you have probably noticed that it is able to automatically size your labels according to the typed text. Sadly, the minimum size that works at 96 DPI is not good in higher resolutions. Sometimes the last word would not fit by a few pixels and will wrap on next line (which is clipped) resulting in missing words. The solution is to increase the width of all your static controls by ~ 3-4 dialog units to be on the safe side.

Another possible problem related to dialogs is the size of images or icons. You should preferably use higher resolution or at least up-scaled images. Fortunately, the image control can be set to scale your images automatically.

Custom controls

If you are using you own controls, you should scale graphical elements and fonts to the current resolution. Consider for example a control that displays points as tiny filled circles and draws lines. Both the diameter of points and the thickness of lines should scale with resolution. You can detect the current resolution and set you scale level using the following code. (The value can be cached, because resolution switch requires restart.)

// in your WM_CREATE handler (or later)
HDC hdc = GetDC(hWnd);
float fScale = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f;
ReleaseDC(hdc);

Well, in theory, X and Y resolutions can be different, but I doubt this will ever be true for a screen.

Rotation custom control (96DPI)
Rotation angle custom control at 96 DPI.
Rotation custom control (120DPI)
Rotation angle custom control at 120 DPI.

Application configuration

Store setting related to window size in DPI-independent manner.

Consider an application that splits its window into 2 parts (for example Internet Explorer with Favorites pane visible). The width of the pane should be stored in an DPI independent way. Or, at the very least, your default value for the pane width should be DPI independent. If you do not take DPI into account, the default configuration will look awkward at high DPI and you'll probably lose the trust of new users, because of the bad first impression of the application.

Menu and toolbars

The fonts used in menus and toolbars should not be hardcoded. Use either the system font for menus and toolbars or, if you are using custom fonts, count in the scale factor.

Images in toolbars (and in menu if you are using them) should be scaled as well. The problem with toolbar and menu images is similar to the one with window caption icons (read below). What size to use and how to avoid quality loss due to scaling?

My solution was to design toolbar and menu graphics in three sizes and pick the closest one based on current resolution:

// see the 'Custom control' section to learn how to obtain fScale value
// images available in sizes 16x16, 20x20, and 24x24
int nToolbarImageSize = (16*fScale+0.5f) >= 24 ? 24 : ((16*fScale+0.5f) >= 20 ? 20 : 16);

Application icon

Vista Explorer is keen on high-resolution icons and can rescales them quite nicely. Your application should have its main icon available in 256x256 format. But...that's not the whole story. The small versions of your icon used in start menu or window captions are important too.

For optimal look and feel under 96, 120, and 144 DPI consider creating your main icon in these sizes: 16, 20, 22, 24, 26, 32, 48, and 256. Why so many? ...read the following section on window captions.

Icon in windows' captions

Size of icons used in window captions must be equal to the value returned by GetSystemMetrics(SM_CXSMICON). Otherwise, they will be stretched and quality will suffer. Value returned by GetSystemMetrics equals 16 at 96 DPI, 22 at 120 DPI on Vista, 26 at 144 DPI on Vista. On XP and previous, it is the expected 20 at 120 DPI and 24 at 144 DPI. That's right, the size of caption icons is different on XP and Vista in high-DPI modes. So...what should you do to solve this mess? There are several ways, but none of them is optimal:

  • You can ignore high DPI modes and let Windows scale the icons. Low quality, but almost no work to be done.
  • You could design icons that are to be used in window caption at high-enough resolution (say 64x64) and resample them to the required size at runtime. This solution is very flexible, delivers good quality in all resolutions, but is quite non-standard. It requires that all graphic resources are under your control, and requires custom code for icon loading.
  • You can design icons for selected resolutions (for example 16, 20, 22, 24, 26) and use default Windows scaling for non-standard resolutions. This solution delivers best quality (icons can be fine-tuned) in selected resolutions and has no additional requirements.
  • Use a hybrid solution. Create graphics in multiple sizes, for example 16, 20, and 24 pixels. When an icon is to be used in window caption and the exact size is not available, use the closest available size and create a derived icon with empty borders. This solution delivers best quality (although sometimes it wastes a pixel or two by using smaller images). It does not require that all resources are under your control (a 3rd party icon with just 16x16 pixels will still be usable), but requires a custom function to be used when creating the derived icons.

If you choose the last solution (as did I), you may find this function useful. Given an icon, it creates another icon suitable for use in window caption. The original icon is destroyed (or returned if it already has the correct size or if the original icon did not have 32-bit color depth). You can then set caption icons using this code:

// in your WM_CREATE or ON_INITDIALOG handler
// nSize must be set to closest (<= GetSystemMetrics(SM_CXSMICON))
// size existing in your icon
HICON = (HICON)::LoadImage(hInstance, MAKEINTRESOURCE(IDI_YOURICON), IMAGE_ICON,
                           nSize, nSize, LR_DEFAULTCOLOR);
::SendMessage(hWnd, WM_SETICON, FALSE,
              (LPARAM)DPIUtils::PrepareIconForCaption(hIcon));
// do not forget to call DestroyIcon when the window is destroyed

The result

The following screenshot shows an application running in 96, 120 and 144 DPI. Note the scaled toolbar and menu graphics, caption icons and fonts.

Application running in 96, 120, and 144 DPI
Application running in 96, 120, and 144 DPI.

Summary

Modifying applications for high DPI modes is a time consuming job, but the result is worth the effort. If you plan to make the next version of your software compatible with high DPI modes, be ready to allocate enough resources to testing, graphic design and programming.

Resources

Recent comments

user icon Anonymous on March 16th 2007

Great article on High DPI! For setting Icons, instead of writing a function to pick the best icon sizes for you, there are two NEW APIs in Vista that can do this work for you.

LoadIconMetric() & LoadIconWithScaleDown() are both new APIs in the Vista ComCtl32.dll. Look them up on MSDN, they'll do what you need better than LoadIcon() or LoadImage().

user icon Anonymous on September 29th 2011

Thank you for this article!
mouser from donationcoder.com

user icon Anonymous on June 4th 2013

Nice overview. Small point: non-square screen pixels date back to pre-VGA video adapters and could come back to haunt us any day. This is why there are separate x and y resolutions and it is good practice to treat them separately.

user icon Anonymous
Select background
I wish there were...