I have the following mouse hook procedure (simplified for the explanation).
SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc, GetModuleHandle(NULL), 0) ;
LRESULT mouseHookProc(int code, WPARAM wParam, LPARAM lParam){
if(code==HC_ACTION){
const auto& data = *(MSLLHOOKSTRUCT*)lParam ;
data.pt ; //This point gives physical coordinates. It ignores the monitor's scaling factor.
//https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-msllhookstruct
}
return CallNextHookEx(NULL, code, wParam, lParam) ;
}
The mouse coordinates I get from the hook are not adjusted by the monitor's scaling factor.
Whether I set my monitor's scaling factor to 100% or 200%, the mouse hook always gives me physical pixels.
On the other hand, the GetCursorPos
winapi function gives the coordinates in logical pixels. i.e. If the scaling factor is 200%, GetCursorPos
will give the coordinates divided by 2, whereas the mouse hook will give the numbers unadjusted.
According to this:
https://docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/ne-shellscalingapi-process_dpi_awareness
if a program is NOT DPI-aware, the system compensates the scaling factor so that the program keeps working as if nothing happened.
That's exactly what's happening with the returned value of GetCursorPos
. It's giving logical pixels rather than physical ones.
The mouse hook procedure, on the other hand, is not being adjusted by the system.
I tried setting my program as DPI-unaware in the manifest, with this:
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">unaware</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
But it makes no difference. I also tried declaring it as DPI-aware, with this:
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<dpiAware>True/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
And that, also, made no difference.
Is there any setting I can add to my program, either via the manifest or otherwise, that will make the mouse hook procedure to give logical coordinates as GetCursorPos
does?
I'm testing all this in Windows 10.
FOLLOW UP:
I found the problem. My manifest file was incorrect. The XML namespaces were not set properly.
Here is the one that works.
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="AppName" type="win32"/>
<asmv3:application>
<asmv3:windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
In order for the system to give logical coordinates to a program, the program has to be DPI-aware. Programs that don't manifest DPI-awareness will handle coordinates inconsistently all over, not just in the mouse hook, also in BitBlt, IAccesibility, UIAutomation, etc.
I don't understand why Microsoft decided to do it backwards. Programs that don't manifest DPI-awareness should behave as usual, not the other way around.
This means that whenever a scaling factor is set on a monitor, most programs will break by default. In order to unbreak them, they have to become DPI-aware and... just like that... they will work again.