简体   繁体   中英

What's the correct way to retrieve the DPI of secondary monitors?

Problem
On windows 8.1 (and newer) the DPI setting is per-monitor. I can clearly see in the system settings that my primary display (laptop) is set to 125% dpi, and my alternate display is set to 100% dpi. My application is written entirely in C#, and every API call I've tried results in the incorrect DPI for the secondary monitor. Keep in mind, that I am calling this method in the initialization of my process and have specified DPI aware in my manifest:

(shcore.dll)
SetProcessDpiAwareness(PROCESS_DPI_AWARENESS::PROCESS_PER_MONITOR_DPI_AWARE);

Attempts

(shcore.dll) 
GetDpiForMonitor(hMonitor, MONITOR_DPI_TYPE::MDT_EFFECTIVE_DPI, &dpiX, &dpiY)

(result 120 regardless of hMonitor handle)

(user32.dll)
IntPtr dC = CreateDC(...);
double logX = (double)GetDeviceCaps(dC, LOGPIXELSX);
double logY = (double)GetDeviceCaps(dC, LOGPIXELSY);

(result 120 regardless of device name for CreateDC)

Monitor Layout
One other thing that seriously confuses me, is that the first monitor is at rect {0,0,1920,1080} but the second monitor is at {2400,0,2400,1350} , which essentially makes a actual desktop size of {4800x1350} and a virtual desktop size of {3840x1080} , but there is missing pixels between the monitors that I can't account for and I don't understand.

                 Screen 1    Screen 2
Actual Pixels:   1920x1080   2400x1350
Virtual Pixels:  1536x864    1920x1080
Top-Left Coord:  0x0         2400x0
Actual DPI:      120 (125%)   96 (100%)
Returned DPI:    120 (125%)  120 (125%)

Another way that I can confirm that the displays are different DPI, is dragging the center point of any DPI aware application across the boundary between monitors will cause the window to change it's size to be consistent across the displays (WPF does this by default).

I've been fighting with this for a day already, and less than an hour after I post a question I found the answer. I'll leave the question here if someone runs into this issue in the future.

The correct method to get alternate monitor DPI's is shcore.dll 's GetDpiForMonitor method, as I tried above. The problem is that SetProcessDpiAwareness is not called early enough to make a difference in a windows/wpf application. I tested this by creating a new console application and a new wpf application and reproducing it with minimal code. The console application returned the correct values, while the wpf application returned incorrect values.

The solution is to add the following to your manifest file:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
  </windowsSettings>
</application>

The key here is the value of the dpiAware tag, it's True/PM instead of just True , this is what makes the difference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM