1
votes

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.

1
A low-level mouse hook runs before the system has even started to determine the target window. Consequently, the system cannot apply any sort of virtualization to the coordinates.IInspectable

1 Answers

-1
votes

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.

In fact, Microsoft's doc explains this problem.

From High DPI Desktop Application Development on Windows,

Desktop applications using older Windows programming technologies (raw Win32 programming, Windows Forms, Windows Presentation Framework (WPF), etc.) are unable to automatically handle DPI scaling without additional developer work. Without such work, applications will appear blurry or incorrectly-sized in many common usage scenarios.

If you want to avoid DPI awareness problems, please create a UWP application.

To start, if you're creating a new Windows app from scratch, it is highly recommended that you create a Universal Windows Platform (UWP) application. UWP applications automatically—and dynamically—scale for each display that they're running on.