Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting an DPI aware correct RECT from GetWindowRect from a external window

I'm in the process of making an application DPI Aware but I have a need to do a GetWindowRect on HWNDs from other applications. My problem is this works fine on applications that are also DPI Aware but how do I detect if the HWND handle is DPI virtualized e.g. scaled so I can scale it myself? Or are there other APIs I've missed which will give me the size of the window in a DPI aware way from a HWND from another process?

I've tried the LogicalToPhysicalPoint but that always seem to fail, possibly because the HWND doesn't belong to my application.

like image 440
Karl Edwall Avatar asked Nov 09 '11 04:11

Karl Edwall


People also ask

How do I make my application DPI-aware?

To make your application dpi-aware, you must cancel automatic dpi scaling, and then adjust user interface elements to scale appropriately to the system dpi.

What is DPI awareness mode?

The DPI Awareness Mode specifies the way an application is displayed when shown on a high resolution screen. Unaware. This mode works on all Windows versions prior to Windows Vista. In this mode, applications assume that all displays have the default 96 DPI (100%) scaling.

How do you use high DPI scaling override?

Select Display > Change the size of text, apps, and other items, and then adjust the slider for each monitor. Right-click the application, select Properties, select the Compatibility tab, and then select the Disable display scaling on high DPI settings check box.


1 Answers

This is not an actual problem. If you mark your process as high-DPI aware, then the system will no longer do any sort of DPI virtualization and the APIs will no longer lie to you about the actual values.

In particular, if you call GetWindowRect or GetClientRect from a high-DPI aware application, you will get the actual values in screen coordinates. This will be true not only for windows belonging to your application's process, but also for windows belonging to other processes, regardless of that other process's DPI awareness setting.

As of Windows 8.1, the PhysicalToLogicalPoint and LogicalToPhysicalPoint functions are no longer necessary and don't actually do anything. The documentation for these two functions calls this out explicitly:

In Windows 8.1, the additional virtualization of the system and inter-process communications means that for the majority of applications, you do not need these APIs. As a result, in Windows 8.1, PhysicalToLogicalPoint and LogicalToPhysicalPoint no longer transform points. The system returns all points to an application in its own coordinate space.

The last sentence is just a different way of phrasing what I said above. The system returns values according to the DPI awareness of the caller. If your process is high-DPI aware, then you will get the real values. You do not need to scale the values yourself. If you are not high-DPI aware, then you are subject to being lied to about the actual values. But that makes sense, because it is assumed that you cannot handle the truth and will not react appropriately.

Just to be clear, I should point out that there are actually two levels of high-DPI awareness now, as of Windows 8.1 (and continued in Windows 10):

  1. There is the first level, introduced way back with Windows Vista, of high-DPI awareness. This is indicated by a setting of true in the application's manifest file, and it just means that you (the application) is able to deal with a system DPI that is set to something other than the classic default of 96 DPI.

    Based on the above knowledge, then, we know that if a process with this DPI-awareness setting calls an API function that returns screen coordinates, it will receive the values in terms of the system DPI.

  2. Then there is the new level, introduced with Windows 8.1, of per-monitor high-DPI awareness. This is indicated by a setting of True/PM in the application's manifest, and it means that you (the application) is able to deal with different monitors having different DPI settings. In other words, while there is still a system default DPI (and it may be 96 DPI or it may be something else), there may be monitors attached to the system that use a different DPI setting (something other than the system DPI).

    Again, based on the above understanding, we know that if a process that is per-monitor high-DPI aware calls an API function that returns screen coordinates, it will receive the actual coordinates relative to the DPI of the monitor that contains the window in question.

If your process is not DPI aware at all (no setting in the manifest, or false), then when you call API functions that return screen coordinates, you will receive the coordinates scaled/virtualized based on a system-wide DPI of 96 DPI.

like image 141
Cody Gray Avatar answered Oct 27 '22 13:10

Cody Gray