{"id":4279,"date":"2021-05-19T09:24:19","date_gmt":"2021-05-19T07:24:19","guid":{"rendered":"https:\/\/mariusbancila.ro\/blog\/?p=4279"},"modified":"2021-05-20T23:33:18","modified_gmt":"2021-05-20T21:33:18","slug":"how-to-build-high-dpi-aware-native-desktop-applications","status":"publish","type":"post","link":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/","title":{"rendered":"How to build high DPI aware native Windows desktop applications"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">If you&#8217;re developing native applications for Windows using Win32 or MFC and you want to support high DPIs so that the application looks crisp on any display, you have to do a lot of things by hand. That is because the technologies for building native UIs, that is MFC, GDI, GDI+, do not provide DPI scaling support. In this article, I will walk through some of the problems of supporting DPI scaling and the solutions for them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Overview<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every display has a different value for the DPI (dots per inch), which, by the way, for displays is rather called PPI (pixels per inch); DPI is a term originated from printers. However, Windows allows virtualizing this value by enabling users to set a scaling factor for text and UI elements. This is possible from the <strong>Display Settings<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/displays.png\"><img loading=\"lazy\" decoding=\"async\" width=\"700\" height=\"298\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/displays-700x298.png\" alt=\"\" class=\"wp-image-4280\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/displays-700x298.png 700w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/displays-300x128.png 300w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/displays-768x327.png 768w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/displays.png 880w\" sizes=\"auto, (max-width: 700px) 100vw, 700px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/scaling.png\"><img loading=\"lazy\" decoding=\"async\" width=\"371\" height=\"376\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/scaling.png\" alt=\"\" class=\"wp-image-4281\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/scaling.png 371w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/scaling-296x300.png 296w\" sizes=\"auto, (max-width: 371px) 100vw, 371px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The scaling value is given as a percentage value, such as 100%, 125%, 150%, 175%, 200%, etc. Custom percentages (between 100 &#8211; 500) are also possible from <em>Advanced scaling settings<\/em>, although not recommended. These scaling factors correspond to DPI values as follows:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Scaling<\/th><th>DPI<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\">100%<\/td><td>96<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">125%<\/td><td>120<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">150%<\/td><td>144<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">175%<\/td><td>168<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">200%<\/td><td>192<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Keep in mind that this effective DPI value may be different than the native (raw) DPI value of the display. You could have a 140 DPI display with the effective value reported as 96 when set to the 100% scaling.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;re application may be running on a display with a scaling factor greater than the default 100%. This is especially reasonable to assume for newer laptop devices or displays (such as 4K ones, for instance). It is also more likely in multi-monitor environments, where different monitors are likely to be set up with different scaling.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The system can provide some automatic scaling for applications that can&#8217;t handle it by themselves (such as old applications that cannot or will not be updated). This is possible by changing the &#8220;Override High DPI scaling behavior&#8221; option in the Compatibility tab of the application&#8217;s (executable&#8217;s) properties. You can read more about this here:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/blogs.windows.com\/windowsdeveloper\/2017\/05\/19\/improving-high-dpi-experience-gdi-based-desktop-apps\/\" target=\"_blank\" rel=\"noreferrer noopener\">Improving the high-DPI experience in GDI based Desktop Apps<\/a><\/li><li><a href=\"https:\/\/blogs.windows.com\/windowsdeveloper\/2017\/04\/04\/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update\/\" target=\"_blank\" rel=\"noreferrer noopener\">High-DPI Scaling Improvements for Desktop Applications in the Windows 10 Creators Update (1703)<\/a><\/li><li><a href=\"https:\/\/www.howtogeek.com\/175664\/how-to-make-the-windows-desktop-work-well-on-high-dpi-displays-and-fix-blurry-fonts\/\" target=\"_blank\" rel=\"noreferrer noopener\">How to Make Windows Work Better on High-DPI Displays and Fix Blurry Fonts<\/a><\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you want to ensure your application&#8217;s user interface is crisp no matter the DPI, that text fonts, controls, windows, and others are adjusted appropriately (increase or decrease in size with the scaling) you need to make it adjust based on the settings on the monitor it&#8217;s running on. To do so, you must make it per-monitor DPI aware.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Per Monitor DPI Scaling Support<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An application can run with one of four different DPI awareness modes:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Unaware<\/strong>, views all displays as having 96 DPI<\/li><li><strong>System<\/strong>, introduced in Windows Vista, views all displays as having the DPI value of the primary display<\/li><li><strong>Per-Monitor<\/strong>, introduced in Windows 8.1, views the DPI of the display that the application window is primary located on. When the DPI changes, top-level windows are notified of the change, but there is no DPI scaling of any UI elements.<\/li><li><strong>Per-Monitor V2<\/strong>, introduced in Windows 10 1703, similar to Per-Monitor, but support automatic scaling of non-client area, theme-drawn bitmaps in common controls, and dialogs.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">However, as already mentioned, GDI\/GDI+ and MFC do not provide any per-monitor DPI awareness support. That means, if you&#8217;re using these technologies, you&#8217;re on your own to provide this support and appropriate scaling. Part of the solution is to replace Win32 APIs that only support a single DPI (the primary display DPI) with ones that support per-monitor settings, where available, or write your own, where that&#8217;s not available.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is a list of such APIs. If you&#8217;re using these in your code, then your app needs changes.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>System (primary monitor) DPI<\/th><th>Per-monitor DPI<\/th><\/tr><\/thead><tbody><tr><td>GetDeviceCaps<\/td><td>GetDpiForMonitor \/ GetDpiForWindow<\/td><\/tr><tr><td>GetSystemMetrics<\/td><td>GetSystemMetricsForDpi<\/td><\/tr><tr><td>SystemParametersInfo<\/td><td>SystemParametersInfoForDpi<\/td><\/tr><tr><td>AdjustWindowRectEx<\/td><td>AdjustWindowRectExForDpi<\/td><\/tr><tr><td>CWnd::CalcWindowRect<\/td><td>AdjustWindowRectExForDpi<\/td><\/tr><tr><td><\/td><td><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">GetDpiForMonitor vs GetDpiForWindow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/shellscalingapi\/nf-shellscalingapi-getdpiformonitor\" target=\"_blank\" rel=\"noreferrer noopener\">GetDpiForMonitor<\/a> function returns the DPI value of a monitor. The <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-getdpiforwindow\" target=\"_blank\" rel=\"noreferrer noopener\">GetDpiForWindow<\/a> function returns the DPI value for a window. However, their behavior, and, therefore, return value, depends on some settings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>GetDpiForMonitor<\/code> returns a different value based on the <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/shellscalingapi\/ne-shellscalingapi-process_dpi_awareness\" target=\"_blank\" rel=\"noreferrer noopener\">PROCESS_DPI_AWARENESS<\/a> value. This is value set per application that indicates how much scaling is provided by the system and how much is done by the application. The behavior of <code>GetDpiForMonitor<\/code> is described in the following table:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>PROCESS_DPI_AWARENESS<\/th><th>Description<\/th><th>GetDpiForMonitor return value<\/th><\/tr><\/thead><tbody><tr><td><code>PROCESS_DPI_UNAWARE<\/code><\/td><td>DPI unaware<\/td><td>96<\/td><\/tr><tr><td><code>PROCESS_SYSTEM_DPI_AWARE<\/code><\/td><td>System DPI aware (all monitors have the DPI value of the primary monitor)<\/td><td>System DPI (the DPI value of the primary monitor)<\/td><\/tr><tr><td><code>PROCESS_PER_MONITOR_DPI_AWARE<\/code><\/td><td>Per monitor DPI aware<\/td><td>The actual DPI value set by the user for the specified monitor<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><code>GetDpiForWindow<\/code> also returns a different value based on the <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/windef\/ne-windef-dpi_awareness\" target=\"_blank\" rel=\"noreferrer noopener\">DPI_AWARENESS<\/a> value. This is a value per thread, process, or window. This was introduced in Windows 10 1607 as an improvement over the per-application setting provided by <code>PROCESS_DPI_AWARENESS<\/code>. The behavior of <code>GetDpiForWindow<\/code> is described in the following table:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>DPI_AWARENESS<\/th><th>Description<\/th><th>GetDpiForWindow return value<\/th><\/tr><\/thead><tbody><tr><td><code>DPI_AWARENESS_UNAWARE<\/code><\/td><td>DPI unaware<\/td><td>96<\/td><\/tr><tr><td><code>DPI_AWARENESS_SYSTEM_AWARE<\/code><\/td><td>System DPI aware<\/td><td>System DPI (the DPI value of the primary monitor)<\/td><\/tr><tr><td><code>DPI_AWARENESS_PER_MONITOR_AWARE<\/code><\/td><td>Per monitor DPI aware<\/td><td>The actual DPI value set by the user for the monitor where the window is located.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You can change the values for <code>PROCESS_DPI_AWARENESS<\/code> and <code>DPI_AWARENESS<\/code> either programmatically or with a manifest.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>API<\/th><th>Modify API<\/th><th>Manifest<\/th><\/tr><\/thead><tbody><tr><td><code>PROCESS_DPI_AWARENESS<\/code><\/td><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/shellscalingapi\/nf-shellscalingapi-setprocessdpiawareness\" target=\"_blank\" rel=\"noreferrer noopener\">SetProcessDpiAwareness<\/a><\/td><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/hidpi\/setting-the-default-dpi-awareness-for-a-process\" target=\"_blank\" rel=\"noreferrer noopener\">App manifest<\/a><\/td><\/tr><tr><td><code>DPI_AWARENESS<\/code><\/td><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-setthreaddpiawarenesscontext\" target=\"_blank\" rel=\"noreferrer noopener\">SetThreadDpiAwarenessContext<\/a><br><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-setprocessdpiawarenesscontext\" target=\"_blank\" rel=\"noreferrer noopener\">SetProcessDpiAwarenessContext<\/a><\/td><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/hidpi\/setting-the-default-dpi-awareness-for-a-process\" target=\"_blank\" rel=\"noreferrer noopener\">App manifest<\/a><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The application manifest should contain the following (for details, see the link from the above table):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n&lt;assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\" xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">\n   &lt;asmv3:application xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">\n      &lt;asmv3:windowsSettings xmlns=\"http:\/\/schemas.microsoft.com\/SMI\/2005\/WindowsSettings\">\n         &lt;dpiAware xmlns=\"http:\/\/schemas.microsoft.com\/SMI\/2005\/WindowsSettings\">True\/PM&lt;\/dpiAware>\n         &lt;dpiAwareness xmlns=\"http:\/\/schemas.microsoft.com\/SMI\/2016\/WindowsSettings\">PerMonitorV2,PerMonitor&lt;\/dpiAwareness>\n      &lt;\/asmv3:windowsSettings>\n   &lt;\/asmv3:application>\n&lt;\/assembly><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Although in this example both <code>&lt;dpiAware&gt;<\/code> and <code>&lt;dpiAwareness&gt;<\/code> are set, the former is ignored in Windows 10 1607 or newer if the latter is present.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Retrieving monitor information<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You can retrieve monitor information using <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-enumdisplaymonitors\" target=\"_blank\" rel=\"noreferrer noopener\">EnumDisplayMonitors<\/a> to enumerate the available monitors and functions such as <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-getmonitorinfoa\" target=\"_blank\" rel=\"noreferrer noopener\">GetMonitorInfo<\/a>, <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/shellscalingapi\/nf-shellscalingapi-getdpiformonitor\" target=\"_blank\" rel=\"noreferrer noopener\">GetDpiForMonitor<\/a>, and <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-enumdisplaysettingsa\" target=\"_blank\" rel=\"noreferrer noopener\">EnumDisplaySettings<\/a> to retrieve various monitor information. An example is shown in the following listing:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int main()\n{\n   ::EnumDisplayMonitors(\n      nullptr,\n      nullptr,\n      [](HMONITOR Arg1,\n         HDC Arg2,\n         LPRECT Arg3,\n         LPARAM Arg4)\n      {\n         MONITORINFOEXA mif;\n         mif.cbSize = sizeof(MONITORINFOEXA);\n\n         if (::GetMonitorInfoA(Arg1, &amp;mif) != 0)\n         {\n            std::cout &lt;&lt; mif.szDevice &lt;&lt; '\\n';\n            std::cout\n               &lt;&lt; \"monitor rect:    \"\n               &lt;&lt; '(' &lt;&lt; mif.rcMonitor.left &lt;&lt; ',' &lt;&lt; mif.rcMonitor.top &lt;&lt; \")-\"\n               &lt;&lt; '(' &lt;&lt; mif.rcMonitor.right &lt;&lt; ',' &lt;&lt; mif.rcMonitor.bottom &lt;&lt; \")\\n\";\n            std::cout\n               &lt;&lt; \"work rect:       \"\n               &lt;&lt; '(' &lt;&lt; mif.rcWork.left &lt;&lt; ',' &lt;&lt; mif.rcWork.top &lt;&lt; \")-\"\n               &lt;&lt; '(' &lt;&lt; mif.rcWork.right &lt;&lt; ',' &lt;&lt; mif.rcWork.bottom &lt;&lt; \")\\n\";\n         }\n\n         UINT xdpi, ydpi;\n         LRESULT success = ::GetDpiForMonitor(Arg1, MDT_EFFECTIVE_DPI, &amp;xdpi, &amp;ydpi);\n         if (success == S_OK)\n         {\n            std::cout &lt;&lt; \"DPI (effective): \" &lt;&lt; xdpi &lt;&lt; ',' &lt;&lt; ydpi &lt;&lt; '\\n';\n         }\n\n         success = ::GetDpiForMonitor(Arg1, MDT_ANGULAR_DPI, &amp;xdpi, &amp;ydpi);\n         if (success == S_OK)\n         {\n            std::cout &lt;&lt; \"DPI (angular):   \" &lt;&lt; xdpi &lt;&lt; ',' &lt;&lt; ydpi &lt;&lt; '\\n';\n         }\n\n         success = ::GetDpiForMonitor(Arg1, MDT_RAW_DPI, &amp;xdpi, &amp;ydpi);\n         if (success == S_OK)\n         {\n            std::cout &lt;&lt; \"DPI (raw):       \" &lt;&lt; xdpi &lt;&lt; ',' &lt;&lt; ydpi &lt;&lt; '\\n';\n         }\n\n         DEVMODEA dm;\n         dm.dmSize = sizeof(DEVMODEA);\n         if (::EnumDisplaySettingsA(mif.szDevice, ENUM_CURRENT_SETTINGS, &amp;dm) != 0)\n         {\n            std::cout &lt;&lt; \"BPP:             \" &lt;&lt; dm.dmBitsPerPel &lt;&lt; '\\n';\n            std::cout &lt;&lt; \"resolution:      \" &lt;&lt; dm.dmPelsWidth &lt;&lt; ',' &lt;&lt; dm.dmPelsHeight &lt;&lt; '\\n';\n            std::cout &lt;&lt; \"frequency:       \" &lt;&lt; dm.dmDisplayFrequency &lt;&lt; '\\n';\n         }\n\n         std::cout &lt;&lt; '\\n';\n         return TRUE;\n      },\n      0);\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With three monitors set at 100%, 125% and 150% scaling, this code displays the following for me:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\\\\.\\DISPLAY1\nmonitor rect:    (-1920,0)-(0,1080)\nwork rect:       (-1920,0)-(0,1040)\nDPI (effective): 96,96\nDPI (angular):   123,123\nDPI (raw):       141,141\nBPP:             32\nresolution:      1920,1080\nfrequency:       60\n\n\\\\.\\DISPLAY2\nmonitor rect:    (0,0)-(2560,1440)\nwork rect:       (0,0)-(2560,1390)\nDPI (effective): 120,120\nDPI (angular):   108,108\nDPI (raw):       108,108\nBPP:             32\nresolution:      2560,1440\nfrequency:       59\n\n\\\\.\\DISPLAY3\nmonitor rect:    (2560,0)-(4480,1200)\nwork rect:       (2560,0)-(4480,1140)\nDPI (effective): 144,144\nDPI (angular):   93,93\nDPI (raw):       94,94\nBPP:             32\nresolution:      1920,1200\nfrequency:       59<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The value of the scaling is the ratio between the effective DPI (seen above) and 96. For instance, on the second display, 120 \/ 96 is 1.25, therefore the scaling for that display is set to 125%.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">GetDeviceCaps<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The use of <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/wingdi\/nf-wingdi-getdevicecaps\" target=\"_blank\" rel=\"noreferrer noopener\">GetDeviceCaps<\/a> to retrieve the value of the DPI is a clear code smell that your code is not DPI aware. Typically, you could see code like the following to get the DPI:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int GetDpi(HWND hWnd)\n{\n   HDC hDC = ::GetDC(hWnd);\n   INT ydpi = ::GetDeviceCaps(hDC, LOGPIXELSY);\n   ::ReleaseDC(hWnd, hDC);\n   return ydpi;\n}<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int dpi = GetDpi(GetDesktopWindow());\nint scaling = static_cast&lt;int>(100.0 * dpi \/ 96);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is what the docs are saying about <code>LOGPIXELSX<\/code> and <code>LOGPIXELSY<\/code>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><strong>LOGPIXELSX<\/strong><br>Number of pixels per logical inch along the screen width. In a system with multiple display monitors, this value is the same for all monitors.<br><strong>LOGPIXELSY<\/strong><br>Number of pixels per logical inch along the screen height. In a system with multiple display monitors, this value is the same for all monitors.<\/p><\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Therefore, this function is not able to return a per-monitor DPI. For that, you should use <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-getdpiforwindow\" target=\"_blank\" rel=\"noreferrer noopener\">GetDpiForWindow<\/a>, available since Windows 10 1607.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int GetDpi(HWND hWnd)\n{\n   return static_cast&lt;int>(::GetDpiForWindow(hWnd));\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re targeting an earlier version, you can also use <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/shellscalingapi\/nf-shellscalingapi-getdpiformonitor\" target=\"_blank\" rel=\"noreferrer noopener\">GetDpiForMonitor<\/a>, which is available since Windows 8.1.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int GetDpi(HWND hWnd)\n{\n   bool v81 = IsWindows8Point1OrGreater();\n   bool v10 = IsWindows10OrGreater();\n\n   if (v81 || v10)\n   {\n      HMONITOR hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);\n      UINT xdpi, ydpi;\n      LRESULT success = ::GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &amp;xdpi, &amp;ydpi);\n      if (success == S_OK)\n      {\n         return static_cast&lt;int>(ydpi);\n      }\n\n      return 96;\n   }\n   else\n   {\n      HDC hDC = ::GetDC(hWnd);\n      INT ydpi = ::GetDeviceCaps(hDC, LOGPIXELSY);\n      ::ReleaseDC(NULL, hDC);\n\n      return ydpi;\n   }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This implementation calls <code>GetDpiForMonitor<\/code> if the code is running on Windows 8.1 or newer, and falls back to <code>GetDeviceCaps<\/code> for older systems. This is, most likely, not necessarily, since prior to Windows 10 1607 it&#8217;s unlikely you can do truly per-monitor DPI aware native apps.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>IsWindows8Point1OrGreater<\/code> and <code>IsWindows10OrGreater<\/code> depend on the application manifest to specify the application support a particular operating system. Otherwise, they return false even if the application is running on Windows 8.1 or Windows 10. Notice these two functions are available since Windows 2000. You can setup the application manifest as following:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n&lt;assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\" xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">\n   &lt;compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n      &lt;application>\n         &lt;!-- Windows 10, Windows Server 2016 and Windows Server 2019 -->\n         &lt;supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"\/>\n         &lt;!-- Windows 8.1 and Windows Server 2012 R2 -->\n         &lt;supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\"\/>\n         &lt;!-- Windows 8 and Windows Server 2012 -->\n         &lt;supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\"\/>\n         &lt;!-- Windows 7 and Windows Server 2008 R2 -->\n         &lt;supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\"\/>\n         &lt;!-- Windows Vista and Windows Server 2008 -->\n         &lt;supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\"\/>\n      &lt;\/application>\n   &lt;\/compatibility>\n&lt;\/assembly><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Remember both <code>GetDpiForWindow<\/code> and <code>GetDpiForMonitor<\/code> depend on the DPI awareness set in the application manifest or programmatically, as described above.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">AdjustWindowRect, AdjustWindowRectEx, CWnd::CalcWindowRect<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-adjustwindowrect\" title=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-adjustwindowrect\" target=\"_blank\" rel=\"noreferrer noopener\">AdjustWindowRect<\/a> and its sister API, <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/winuser\/nf-winuser-adjustwindowrectex\" target=\"_blank\" rel=\"noreferrer noopener\">AdjustWindowRectEx<\/a>, calculate the required size of the window rectangle based on the desired size of the client rectangle. Similarly, the MFC counter-part, <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/mfc\/reference\/cwnd-class?view=msvc-160#calcwindowrect\" target=\"_blank\" rel=\"noreferrer noopener\">CWnd::CalcWindowRect<\/a>, does the same, by calling <code>AdjustWindowRectEx<\/code>. However, these Windows APIs are not DPI aware and should be replaced with <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/desktop\/api\/winuser\/nf-winuser-adjustwindowrectexfordpi\" target=\"_blank\" rel=\"noreferrer noopener\">AdjustWindowsRectExForDPI<\/a>. This function was introduced in Windows 10 1607.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The MFC implementation of <code>CalcWindowRect<\/code> is the following:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void CWnd::CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType)\n{\n\tDWORD dwExStyle = GetExStyle();\n\tif (nAdjustType == 0)\n\t\tdwExStyle &amp;= ~WS_EX_CLIENTEDGE;\n\t::AdjustWindowRectEx(lpClientRect, GetStyle(), FALSE, dwExStyle);\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This should be replaced with the following implementation:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using AdjustWindowRectExForDpi_fn = BOOL(WINAPI *)(LPRECT, DWORD, BOOL, DWORD, UINT);\n\nBOOL CalcWindowRectForDpi(\n   LPRECT lpRect,\n   DWORD  dwStyle,\n   BOOL   bMenu,\n   DWORD  dwExStyle,\n   UINT   dpi,\n   UINT   nAdjustType = CWnd::adjustBorder)\n{\n   if (nAdjustType == 0)\n      dwExStyle &amp;= ~WS_EX_CLIENTEDGE;\n\n   HMODULE hModule = ::LoadLibrary(_T(\"User32.dll\")); \/\/ don't call FreeLibrary() with this handle; the module was already loaded up, it would break the app\n   if (hModule != nullptr)\n   {\n      AdjustWindowRectExForDpi_fn addr = (AdjustWindowRectExForDpi_fn)::GetProcAddress(hModule, _T(\"AdjustWindowRectExForDpi\"));\n      if (addr != nullptr)\n      {\n         return addr(lpRect, dwStyle, bMenu, dwExStyle, dpi);\n      }\n   }\n   return ::AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For calling this function, you need to the pass the DPI, after priory retrieving it as explained earlier.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Therefore, you should do the following replacements in your code:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>Function<\/th><th>Replacement<\/th><\/tr><\/thead><tbody><tr><td><code>AdjustWindowRect<\/code><\/td><td><code>AdjustWindowRectExForDpi<\/code><\/td><\/tr><tr><td><code>AdjustWindowRectEx<\/code><\/td><td><code>AdjustWindowRectExForDpi<\/code><\/td><\/tr><tr><td><code>CWnd::CalcWindowRect<\/code><\/td><td><code>CalcWindowRectForDpi<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">CDC pixel conversion functions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The MFC&#8217;s CDC class contains several functions that perform conversions:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>Function<\/th><th>Description<\/th><\/tr><\/thead><tbody><tr><td><code>DPtoHIMETRIC<\/code><\/td><td>Converts device units into HIMETRIC units.<\/td><\/tr><tr><td><code>HIMETRICtoDP<\/code><\/td><td>Converts HIMETRIC units into device units.<\/td><\/tr><tr><td><code>LPtoHIMETRIC<\/code><\/td><td>Converts logical units into HIMETRIC units.<\/td><\/tr><tr><td><code>HIMETRICtoLP<\/code><\/td><td>Converts HIMETRIC units into logical units.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">These functions need the DPI to perform the conversion, but depend on the <code>GetDeviceCaps<\/code> function. Here is their implementation:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#define HIMETRIC_INCH   2540    \/\/ HIMETRIC units per inch\n\nvoid CDC::DPtoHIMETRIC(LPSIZE lpSize) const\n{\n\tASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));\n\n\tint nMapMode;\n\tif (this != NULL &amp;&amp; (nMapMode = GetMapMode()) &lt; MM_ISOTROPIC &amp;&amp;\n\t\tnMapMode != MM_TEXT)\n\t{\n\t\t\/\/ when using a constrained map mode, map against physical inch\n\t\t((CDC*)this)->SetMapMode(MM_HIMETRIC);\n\t\tDPtoLP(lpSize);\n\t\t((CDC*)this)->SetMapMode(nMapMode);\n\t}\n\telse\n\t{\n\t\t\/\/ map against logical inch for non-constrained mapping modes\n\t\tint cxPerInch, cyPerInch;\n\t\tif (this != NULL)\n\t\t{\n\t\t\tASSERT_VALID(this);\n\t\t\tASSERT(m_hDC != NULL);  \/\/ no HDC attached or created?\n\t\t\tcxPerInch = GetDeviceCaps(LOGPIXELSX);\n\t\t\tcyPerInch = GetDeviceCaps(LOGPIXELSY);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcxPerInch = afxData.cxPixelsPerInch;\n\t\t\tcyPerInch = afxData.cyPixelsPerInch;\n\t\t}\n\t\tASSERT(cxPerInch != 0 &amp;&amp; cyPerInch != 0);\n\t\tlpSize->cx = MulDiv(lpSize->cx, HIMETRIC_INCH, cxPerInch);\n\t\tlpSize->cy = MulDiv(lpSize->cy, HIMETRIC_INCH, cyPerInch);\n\t}\n}\n\nvoid CDC::HIMETRICtoDP(LPSIZE lpSize) const\n{\n\tASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));\n\n\tint nMapMode;\n\tif (this != NULL &amp;&amp; (nMapMode = GetMapMode()) &lt; MM_ISOTROPIC &amp;&amp;\n\t\tnMapMode != MM_TEXT)\n\t{\n\t\t\/\/ when using a constrained map mode, map against physical inch\n\t\t((CDC*)this)->SetMapMode(MM_HIMETRIC);\n\t\tLPtoDP(lpSize);\n\t\t((CDC*)this)->SetMapMode(nMapMode);\n\t}\n\telse\n\t{\n\t\t\/\/ map against logical inch for non-constrained mapping modes\n\t\tint cxPerInch, cyPerInch;\n\t\tif (this != NULL)\n\t\t{\n\t\t\tASSERT_VALID(this);\n\t\t\tASSERT(m_hDC != NULL);  \/\/ no HDC attached or created?\n\t\t\tcxPerInch = GetDeviceCaps(LOGPIXELSX);\n\t\t\tcyPerInch = GetDeviceCaps(LOGPIXELSY);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcxPerInch = afxData.cxPixelsPerInch;\n\t\t\tcyPerInch = afxData.cyPixelsPerInch;\n\t\t}\n\t\tASSERT(cxPerInch != 0 &amp;&amp; cyPerInch != 0);\n\t\tlpSize->cx = MulDiv(lpSize->cx, cxPerInch, HIMETRIC_INCH);\n\t\tlpSize->cy = MulDiv(lpSize->cy, cyPerInch, HIMETRIC_INCH);\n\t}\n}\n\nvoid CDC::LPtoHIMETRIC(LPSIZE lpSize) const\n{\n\tASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));\n\n\tLPtoDP(lpSize);\n\tDPtoHIMETRIC(lpSize);\n}\n\nvoid CDC::HIMETRICtoLP(LPSIZE lpSize) const\n{\n\tASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));\n\n\tHIMETRICtoDP(lpSize);\n\tDPtoLP(lpSize);\n}\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These functions can be rewritten as following, to be DPI aware. However, the DPI is actually provided as an argument, just as in the case of <code>CalcWindowRectForDpi<\/code> above.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">constexpr int HIMETRIC_INCH = 2540;    \/\/ HIMETRIC units per inch\n\nvoid Win32Utils::DPtoHIMETRIC(HDC hDC, LPSIZE lpSize, int const iDpi)\n{\n   ASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));\n\n   int nMapMode;\n   if ((nMapMode = ::GetMapMode(hDC)) &lt; MM_ISOTROPIC &amp;&amp; nMapMode != MM_TEXT)\n   {\n      \/\/ when using a constrained map mode, map against physical inch\n      ::SetMapMode(hDC, MM_HIMETRIC);\n      ::DPtoLP(hDC, reinterpret_cast&lt;LPPOINT>(lpSize), 1);\n      ::SetMapMode(hDC, nMapMode);\n   }\n   else\n   {\n      lpSize->cx = MulDiv(lpSize->cx, HIMETRIC_INCH, iDpi);\n      lpSize->cy = MulDiv(lpSize->cy, HIMETRIC_INCH, iDpi);\n   }\n}\n\nvoid Win32Utils::HIMETRICtoDP(HDC hDC, LPSIZE lpSize, int const iDpi)\n{\n   ASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));\n\n   int nMapMode;\n   if ((nMapMode = ::GetMapMode(hDC)) &lt; MM_ISOTROPIC &amp;&amp; nMapMode != MM_TEXT)\n   {\n      \/\/ when using a constrained map mode, map against physical inch\n      ::SetMapMode(hDC, MM_HIMETRIC);\n      ::LPtoDP(hDC, reinterpret_cast&lt;LPPOINT>(lpSize), 1);\n      ::SetMapMode(hDC, nMapMode);\n   }\n   else\n   {\n      lpSize->cx = MulDiv(lpSize->cx, iDpi, HIMETRIC_INCH);\n      lpSize->cy = MulDiv(lpSize->cy, iDpi, HIMETRIC_INCH);\n   }\n}\n\nvoid Win32Utils::LPtoHIMETRIC(HDC hDC, LPSIZE lpSize, int const iDpi)\n{\n   ASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));\n\n   ::LPtoDP(hDC, reinterpret_cast&lt;LPPOINT>(lpSize), 1);\n   DPtoHIMETRIC(hDC, lpSize, iDpi);\n}\n\nvoid Win32Utils::HIMETRICtoLP(HDC hDC, LPSIZE lpSize, int const iDpi)\n{\n   ASSERT(AfxIsValidAddress(lpSize, sizeof(SIZE)));\n\n   HIMETRICtoDP(hDC, lpSize, iDpi);\n   ::DPtoLP(hDC, reinterpret_cast&lt;LPPOINT>(lpSize), 1);\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">More of MFC<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you search for <code>GetDeviceCaps<\/code> (or for <code>LOGPIXELSY<\/code>) in the MFC source code, you&#8217;ll see there are other places where it&#8217;s used. These include <code>CFrameWnd::RecalcLayout<\/code> and <code>CWnd::RepositionBars<\/code>. You will have to look out for all these functions and replace them. <code>CFrameWnd::RecalcLayout<\/code>, for instance, is a virtual method so you can override it. As for <code>CWnd::RepositionBars<\/code>, you just need to replace it. You could copy the implementation from MFC and replace the parts that deal with DPI.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Working with fonts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The GDI <code>CreateFont<\/code> API, the MFC&#8217;s <code>CFont<\/code> class (based on the previous) and the GDI+ <code>Font<\/code> class are not DPI aware. You can specify the height in various units, but the height is not adjusted based on the DPI. This is something you have to do explicitly. Let&#8217;s take a look at these functions and classes.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>API<\/th><th>Library<\/th><th>Description<\/th><\/tr><\/thead><tbody><tr><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/wingdi\/nf-wingdi-createfonta\" target=\"_blank\" rel=\"noreferrer noopener\">CreateFont<\/a><\/td><td>GDI<\/td><td>Creates a logical font with the specified characteristics. The height is given in logical units and indicates either the height of the character cell (if it&#8217;s a positive number) or the height of the character (if it&#8217;s a negative number).<\/td><\/tr><tr><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/wingdi\/ns-wingdi-logfonta\" target=\"_blank\" rel=\"noreferrer noopener\">LOGFONTA<\/a> \/ <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/wingdi\/ns-wingdi-logfontw\" target=\"_blank\" rel=\"noreferrer noopener\">LOGFONTW<\/a><\/td><td>GDI<\/td><td>A structure that defines the attributes of a font. The height has the the same meaning as above.<\/td><\/tr><tr><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/mfc\/reference\/cfont-class?view=msvc-160#createfont\" target=\"_blank\" rel=\"noreferrer noopener\">CFont::CreateFont<\/a><\/td><td>MFC<\/td><td>A wrapper of the GDI&#8217;s <code>CreateFont<\/code> function.<\/td><\/tr><tr><td><a href=\"CreateFontIndirect\" target=\"_blank\" rel=\"noreferrer noopener\">CFont::CreateFontIndirect<\/a><\/td><td>MFC<\/td><td>Similar to <code>CreateFont<\/code> but takes a pointer to <code>LOGFONT<\/code> structure to describe the attributes of the font to be created.<\/td><\/tr><tr><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/gdiplusheaders\/nf-gdiplusheaders-font-font(constfont_)\" target=\"_blank\" rel=\"noreferrer noopener\">Font::Font<\/a><\/td><td>GDI+<\/td><td>A set of overloaded constructors. Some take a pointer to a <code>LOGFONT<\/code> structure. Others take multiple arguments including the height in a specified unit. The default unit is the point (1\/72 of an inch), but various <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/gdiplusenums\/ne-gdiplusenums-unit?redirectedfrom=MSDN\" target=\"_blank\" rel=\"noreferrer noopener\">other units<\/a> are available.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This is what the documentation is saying about the height of the GDI fonts:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>For the <code>MM_TEXT<\/code> mapping mode, you can use the following formula to specify a height for a font with a specified point size:<\/p><p><code>nHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);<\/code><\/p><\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Therefore, we often see code that looks like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int pointSize = 12;\nint height = -MulDiv(pointSize, ::GetDeviceCaps(hDC, LOGPIXELSY), 72);\n\nHFONT hFont = CreateFont(\n   height,                   \/\/ nHeight\n   0,                        \/\/ nWidth\n   0,                        \/\/ nEscapement\n   0,                        \/\/ nOrientation\n   FW_DONTCARE,              \/\/ nWeight\n   FALSE,                    \/\/ bItalic\n   FALSE,                    \/\/ bUnderline\n   FALSE,                    \/\/ cStrikeOut\n   ANSI_CHARSET,             \/\/ nCharSet\n   OUT_DEFAULT_PRECIS,       \/\/ nOutPrecision\n   CLIP_DEFAULT_PRECIS,      \/\/ nClipPrecision\n   DEFAULT_QUALITY,          \/\/ nQuality\n   DEFAULT_PITCH | FF_SWISS, \/\/ nPitchAndFamily\n   \"Tahoma\")<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The part that needs to be changed here is the computation of the height. <code>GetDeviceCaps<\/code> needs to be replaced with one of the functions that can return the proper DPI of the window or monitor.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int pointSize = 12;\nint height = -MulDiv(pointSize, ::GetDpiForWindow(hWnd), 72);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re working with GDI+ and creating fonts by specifying a unit points you need to consider that the library is using the system DPI, which is the DPI of the primary monitor or just 96. Therefore, you need to adjust your font size with a factor that is the ratio between the DPI of the monitor on which the window that uses the font is displayed and the DPI of the primary monitor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Therefore, if you have code that looks like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Gdiplus::Font font(L\"Tahoma\", 12, Gdiplus::FontStyleRegular);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You need to modify it as follows:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int primaryMonitorDpi = ::GetDpiForWindow(::GetDesktopWindow()); \/\/ or GetDeviceCaps(), or GetDpiForMonitor()\nint currentMonitorDpi = ::GetDpiForWindow(hwnd);\nGdiplus::REAL emSize = 12.0 * currentMonitorDpi \/ primaryMonitorDpi;\nGdiplus::Font font(L\"Tahoma\", emSize, Gdiplus::FontStyleRegular);<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Reacting to DPI changes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you want your app to change on the fly to changes in DPI then you need to handle some windows messages and trigger the appropriate updates in your code. There are several messages related to DPI changes:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th>Message<\/th><th>Description<\/th><\/tr><\/thead><tbody><tr><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/hidpi\/wm-dpichanged\" target=\"_blank\" rel=\"noreferrer noopener\">WM_DPICHANGED<\/a><\/td><td>Received by top-level windows when the effective DPI has changed. This message is only relevant for per-monitor DPI aware applications or threads. This was introduced in Windows 8.1.<\/td><\/tr><tr><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/hidpi\/wm-dpichanged-beforeparent\" target=\"_blank\" rel=\"noreferrer noopener\">WM_DPICHANGED_BEFOREPARENT<\/a><\/td><td>For top-level windows that are per-monitor v2 DPI aware, this message is sent (from bottom-up) to all the windows in the child HWND tree of the window that is undergoing the DPI change. This is sent <em>before<\/em> the top-level window receives the <code>WM_DPICHANGED<\/code> message.<\/td><\/tr><tr><td><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/hidpi\/wm-dpichanged-afterparent\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/hidpi\/wm-dpichanged-afterparent\">WM_DPICHANGED_AFTERPARENT<\/a><\/td><td>For top-level windows that are per-monitor v2 DPI aware, this message is sent (from top-down) to all the windows in the child HWND tree of the window that is undergoing the DPI change. This is sent <em>after<\/em> the top-level window receives the <code>WM_DPICHANGED<\/code> message.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Here I show you an example of a dialog application reacting to DPI changes and resizing and repositioning all the controls on the dialog. This is how the dialog resource looks like:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><a href=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpirc.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpirc.png\" alt=\"\" class=\"wp-image-4299\" width=\"627\" height=\"379\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpirc.png 627w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpirc-300x181.png 300w\" sizes=\"auto, (max-width: 627px) 100vw, 627px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">First, you need to register the handler for the <code>WM_DPICHANGED<\/code> message.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">BEGIN_MESSAGE_MAP(CDemoDlg, CDialogEx)\n   \/\/ ...\n   ON_MESSAGE(WM_DPICHANGED, OnDpiChanged)\nEND_MESSAGE_MAP()<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The implementation of this handler should do the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>resize and reposition the dialog (the top-level window that received the message); notice that the new window rectangle is received with the <code>LPARAM<\/code> argument. <\/li><li>enumerate all child windows and execute a callback that resizes and repositions each child window.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">To do the latter step above, you have to:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>determine the relative position of the child window to the parent window; this is needed to adjust the top-left corner of the child window based on the new DPI<\/li><li>know both the previous value of the DPI and the new DPI so that the position (left and top) and the size (width and height) can be adjusted accordingly (if you go from 100% to 125% the sizes must increase, but from 125% to 100% they have to decrease).<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">All these can be implemented as follows:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">LRESULT CDemoDlg::OnDpiChanged(WPARAM wParam, LPARAM lParam)\n{\n   if (m_dpi != 0)\n   {\n      RECT* const prcNewWindow = reinterpret_cast&lt;RECT*>(lParam);\n      ::SetWindowPos(\n         m_hWnd,\n         nullptr,\n         prcNewWindow->left,\n         prcNewWindow->top,\n         prcNewWindow->right - prcNewWindow->left,\n         prcNewWindow->bottom - prcNewWindow->top,\n         SWP_NOZORDER | SWP_NOACTIVATE);\n\n      ::EnumChildWindows(\n         m_hWnd, \n         [](HWND hWnd, LPARAM lParam)\n         {\n            int const dpi = ::GetDpiForWindow(hWnd);\n            int const previousDpi = static_cast&lt;int>(lParam);\n\n            CRect rc;\n            ::GetWindowRect(hWnd, rc);             \/\/ child window rect in screen coordinates\n\n            HWND parentWnd = ::GetParent(hWnd);\n            CRect rcParent;\n            ::GetWindowRect(parentWnd, rcParent);  \/\/ parent window rect in screen coordinates\n\n            POINT ptPos = { rc.left, rc.top };\n            ::ScreenToClient(parentWnd, &amp;ptPos);   \/\/ transforming the child window pos\n                                                   \/\/ from screen space to parent window space\n            int dpiScaledX = ::MulDiv(ptPos.x, dpi, previousDpi);\n            int dpiScaledY = ::MulDiv(ptPos.y, dpi, previousDpi);\n            int dpiScaledWidth = ::MulDiv(rc.Width(), dpi, previousDpi);\n            int dpiScaledHeight = ::MulDiv(rc.Height(), dpi, previousDpi);\n\n            ::SetWindowPos(\n               hWnd,\n               nullptr,\n               dpiScaledX,\n               dpiScaledY,\n               dpiScaledWidth,\n               dpiScaledHeight,\n               SWP_NOZORDER | SWP_NOACTIVATE);\n\n            return TRUE;\n         },\n         m_dpi);\n   }\n\n   m_dpi = HIWORD(wParam);\n\n   return 0;\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Notice that <code>m_dpi<\/code> is a class member, initialized with 0, that stores the value of the current DPI of the window.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The result is that if you move the window from one screen to another, with different DPIs, the application automatically adjust accordingly to the new DPI. Here are several screenshots from displays with 100%, 125%, and 150% scaling.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi100.png\"><img loading=\"lazy\" decoding=\"async\" width=\"450\" height=\"262\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi100.png\" alt=\"\" class=\"wp-image-4300\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi100.png 450w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi100-300x175.png 300w\" sizes=\"auto, (max-width: 450px) 100vw, 450px\" \/><\/a><figcaption>100% scaling<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi125.png\"><img loading=\"lazy\" decoding=\"async\" width=\"564\" height=\"329\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi125.png\" alt=\"\" class=\"wp-image-4301\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi125.png 564w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi125-300x175.png 300w\" sizes=\"auto, (max-width: 564px) 100vw, 564px\" \/><\/a><figcaption>125% scaling<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi150.png\"><img loading=\"lazy\" decoding=\"async\" width=\"676\" height=\"394\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi150.png\" alt=\"\" class=\"wp-image-4302\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi150.png 676w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/dpi150-300x175.png 300w\" sizes=\"auto, (max-width: 676px) 100vw, 676px\" \/><\/a><figcaption>150% scaling<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Making a native Windows application to be per-monitor DPI aware requires a certain amount of extra work. It is also going to work only for Windows 10 but at this point you shouldn&#8217;t be supporting any previous operating systems. To accomplish this task you need to loop up all those APIs (mentioned in this article) that are related to the DPI (return the DPI or fetch the DPI for various calculations) and replace them with other functions (either system ones or user-defined ones) that handle the DPI in a proper way.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;re developing native applications for Windows using Win32 or MFC and you want to support high DPIs so that the application looks crisp on any display, you have to do a lot of things by hand. That is because the technologies for building native UIs, that is MFC, GDI, GDI+, do not provide DPI scaling support. In this article, I will walk through some of the problems of supporting DPI scaling and the solutions for them.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[7,12,214,8],"tags":[451,636,453,291,459],"class_list":["post-4279","post","type-post","status-publish","format-standard","hentry","category-c","category-mfc","category-ui","category-windows-programming","tag-c","tag-dpi","tag-mfc","tag-win32-2","tag-windows"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Marius Bancila\"\/>\n\t<meta name=\"keywords\" content=\"c++,dpi,mfc,win32,windows\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Marius Bancila&#039;s Blog | About code. Mostly on C++\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"How to build high DPI aware native Windows desktop applications\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2021-05-19T07:24:19+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2021-05-20T21:33:18+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"How to build high DPI aware native Windows desktop applications\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#article\",\"name\":\"How to build high DPI aware native Windows desktop applications\",\"headline\":\"How to build high DPI aware native Windows desktop applications\",\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/wp-content\\\/uploads\\\/2021\\\/05\\\/displays.png\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#articleImage\",\"width\":880,\"height\":375},\"datePublished\":\"2021-05-19T09:24:19+02:00\",\"dateModified\":\"2021-05-20T23:33:18+02:00\",\"inLanguage\":\"en-US\",\"commentCount\":3,\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#webpage\"},\"articleSection\":\"C++, MFC, UI, Windows Programming, C++, DPI, MFC, win32, Windows\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"position\":2,\"name\":\"IT\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"position\":3,\"name\":\"Software\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"position\":4,\"name\":\"C++\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/mfc\\\/#listItem\",\"name\":\"MFC\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/mfc\\\/#listItem\",\"position\":5,\"name\":\"MFC\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/mfc\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#listItem\",\"name\":\"How to build high DPI aware native Windows desktop applications\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#listItem\",\"position\":6,\"name\":\"How to build high DPI aware native Windows desktop applications\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/mfc\\\/#listItem\",\"name\":\"MFC\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/\",\"name\":\"Marius Bancila\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Marius Bancila\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#webpage\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/\",\"name\":\"How to build high DPI aware native Windows desktop applications\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2021\\\/05\\\/19\\\/how-to-build-high-dpi-aware-native-desktop-applications\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"datePublished\":\"2021-05-19T09:24:19+02:00\",\"dateModified\":\"2021-05-20T23:33:18+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"How to build high DPI aware native Windows desktop applications","description":"","canonical_url":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/","robots":"max-image-preview:large","keywords":"c++,dpi,mfc,win32,windows","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#article","name":"How to build high DPI aware native Windows desktop applications","headline":"How to build high DPI aware native Windows desktop applications","author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2021\/05\/displays.png","@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#articleImage","width":880,"height":375},"datePublished":"2021-05-19T09:24:19+02:00","dateModified":"2021-05-20T23:33:18+02:00","inLanguage":"en-US","commentCount":3,"mainEntityOfPage":{"@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#webpage"},"isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#webpage"},"articleSection":"C++, MFC, UI, Windows Programming, C++, DPI, MFC, win32, Windows"},{"@type":"BreadcrumbList","@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","position":1,"name":"Home","item":"https:\/\/mariusbancila.ro\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","position":2,"name":"IT","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","position":3,"name":"Software","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","position":4,"name":"C++","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/mfc\/#listItem","name":"MFC"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/mfc\/#listItem","position":5,"name":"MFC","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/mfc\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#listItem","name":"How to build high DPI aware native Windows desktop applications"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#listItem","position":6,"name":"How to build high DPI aware native Windows desktop applications","previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/mfc\/#listItem","name":"MFC"}}]},{"@type":"Organization","@id":"https:\/\/mariusbancila.ro\/blog\/#organization","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","url":"https:\/\/mariusbancila.ro\/blog\/"},{"@type":"Person","@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author","url":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/","name":"Marius Bancila","image":{"@type":"ImageObject","@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g","width":96,"height":96,"caption":"Marius Bancila"}},{"@type":"WebPage","@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#webpage","url":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/","name":"How to build high DPI aware native Windows desktop applications","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/#breadcrumblist"},"author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"creator":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"datePublished":"2021-05-19T09:24:19+02:00","dateModified":"2021-05-20T23:33:18+02:00"},{"@type":"WebSite","@id":"https:\/\/mariusbancila.ro\/blog\/#website","url":"https:\/\/mariusbancila.ro\/blog\/","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","inLanguage":"en-US","publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Marius Bancila's Blog | About code. Mostly on C++","og:type":"article","og:title":"How to build high DPI aware native Windows desktop applications","og:url":"https:\/\/mariusbancila.ro\/blog\/2021\/05\/19\/how-to-build-high-dpi-aware-native-desktop-applications\/","article:published_time":"2021-05-19T07:24:19+00:00","article:modified_time":"2021-05-20T21:33:18+00:00","twitter:card":"summary","twitter:title":"How to build high DPI aware native Windows desktop applications"},"aioseo_meta_data":{"post_id":"4279","title":null,"description":null,"keywords":[],"keyphrases":{"focus":[],"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":[],"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":{"id":"aioseo-article-636966901b47e","slug":"article","graphName":"Article","label":"Article","properties":{"type":"BlogPosting","name":"#post_title","headline":"#post_title","description":"#post_excerpt","image":"","keywords":"","author":{"name":"#author_name","url":"#author_url"},"dates":{"include":true,"datePublished":"","dateModified":""}}},"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":"{\"article\":{\"articleType\":\"BlogPosting\"},\"course\":{\"name\":\"\",\"description\":\"\",\"provider\":\"\"},\"faq\":{\"pages\":[]},\"product\":{\"reviews\":[]},\"recipe\":{\"ingredients\":[],\"instructions\":[],\"keywords\":[]},\"software\":{\"reviews\":[],\"operatingSystems\":[]},\"webPage\":{\"webPageType\":\"WebPage\"}}","pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","location":null,"local_seo":{"locations":{"business":{"name":"","businessType":"","image":"","areaServed":"","urls":{"website":"","aboutPage":"","contactPage":""},"address":{"streetLine1":"","streetLine2":"","zipCode":"","city":"","state":"","country":"","addressFormat":"#streetLineOne\n#streetLineTwo\n#city, #state #zipCode"},"contact":{"phone":"","email":"","fax":""},"ids":{"vat":"","tax":"","chamberOfCommerce":""},"payment":{"priceRange":"","currenciesAccepted":"","methods":""}}},"openingHours":{"useDefaults":true,"show":true,"alwaysOpen":false,"use24hFormat":false,"timezone":"","labels":{"closed":"","alwaysOpen":""},"days":{"monday":{"open24h":false,"closed":false,"openTime":"09:00","closeTime":"17:00"},"tuesday":{"open24h":false,"closed":false,"openTime":"09:00","closeTime":"17:00"},"wednesday":{"open24h":false,"closed":false,"openTime":"09:00","closeTime":"17:00"},"thursday":{"open24h":false,"closed":false,"openTime":"09:00","closeTime":"17:00"},"friday":{"open24h":false,"closed":false,"openTime":"09:00","closeTime":"17:00"},"saturday":{"open24h":false,"closed":false,"openTime":"09:00","closeTime":"17:00"},"sunday":{"open24h":false,"closed":false,"openTime":"09:00","closeTime":"17:00"}}}},"breadcrumb_settings":null,"limit_modified_date":false,"ai":null,"created":"2021-05-14 13:56:56","updated":"2025-12-12 07:56:25","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"author_meta":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"featured_img":null,"jetpack_publicize_connections":[],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"uagb_comment_info":3,"uagb_excerpt":"If you're developing native applications for Windows using Win32 or MFC and you want to support high DPIs so that the application looks crisp on any display, you have to do a lot of things by hand. That is because the technologies for building native UIs, that is MFC, GDI, GDI+, do not provide DPI&hellip;","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/mfc\/\" class=\"advgb-post-tax-term\">MFC<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/ui\/\" class=\"advgb-post-tax-term\">UI<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/windows-programming\/\" class=\"advgb-post-tax-term\">Windows Programming<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">MFC<\/span>","<span class=\"advgb-post-tax-term\">UI<\/span>","<span class=\"advgb-post-tax-term\">Windows Programming<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/windows-programming\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/windows-programming\/\" class=\"advgb-post-tax-term\">DPI<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/windows-programming\/\" class=\"advgb-post-tax-term\">MFC<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/windows-programming\/\" class=\"advgb-post-tax-term\">win32<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/windows-programming\/\" class=\"advgb-post-tax-term\">Windows<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">DPI<\/span>","<span class=\"advgb-post-tax-term\">MFC<\/span>","<span class=\"advgb-post-tax-term\">win32<\/span>","<span class=\"advgb-post-tax-term\">Windows<\/span>"]}},"comment_count":"3","relative_dates":{"created":"Posted 5 years ago","modified":"Updated 5 years ago"},"absolute_dates":{"created":"Posted on May 19, 2021","modified":"Updated on May 20, 2021"},"absolute_dates_time":{"created":"Posted on May 19, 2021 9:24 am","modified":"Updated on May 20, 2021 11:33 pm"},"featured_img_caption":"","series_order":"","jetpack_shortlink":"https:\/\/wp.me\/pYNdv-171","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4279","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/comments?post=4279"}],"version-history":[{"count":23,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4279\/revisions"}],"predecessor-version":[{"id":4311,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4279\/revisions\/4311"}],"wp:attachment":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/media?parent=4279"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/categories?post=4279"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/tags?post=4279"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}