设置进程 DPI 感知,以便系统补偿缩放因子
Posted
技术标签:
【中文标题】设置进程 DPI 感知,以便系统补偿缩放因子【英文标题】:Setting process DPI awareness so that the system compensates for scaling factors 【发布时间】:2020-10-11 10:16:59 【问题描述】:我有以下鼠标钩子程序(为解释而简化)。
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) ;
我从钩子上得到的鼠标坐标没有被显示器的比例因子调整。 无论我将显示器的缩放系数设置为 100% 还是 200%,鼠标挂钩总是给我物理像素。
另一方面,GetCursorPos
winapi 函数以逻辑像素给出坐标。即如果缩放因子为 200%,GetCursorPos
将给出除以 2 的坐标,而鼠标钩将给出未经调整的数字。
据此:https://docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/ne-shellscalingapi-process_dpi_awareness
如果程序不支持 DPI,则系统会补偿缩放因子,以便程序像什么都没发生一样继续工作。
这正是GetCursorPos
的返回值所发生的情况。它提供的是逻辑像素而不是物理像素。
另一方面,鼠标钩子程序并没有被系统调整。
我尝试在清单中将我的程序设置为不支持 DPI,如下:
<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>
但这并没有什么区别。 我还尝试将其声明为可识别 DPI,如下所示:
<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>
这也没什么区别。
我是否可以通过清单或其他方式将任何设置添加到我的程序中,这将使鼠标钩子程序像GetCursorPos
一样提供逻辑坐标?
我正在 Windows 10 中测试所有这些。
跟进:
我发现了问题。我的清单文件不正确。 XML 命名空间设置不正确。
这是一个有效的方法。
<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>
为了让系统为程序提供逻辑坐标,程序必须能够识别 DPI。 不具备 DPI 感知能力的程序在处理坐标时会不一致,不仅在鼠标钩子中,在 BitBlt、IAccesibility、UIAutomation 等中也是如此。
我不明白为什么微软决定倒退。没有表现出 DPI 意识的程序应该像往常一样运行,而不是相反。
这意味着每当在监视器上设置缩放因子时,大多数程序都会默认中断。为了破解它们,它们必须具备 DPI 意识,并且......就像那样......它们会再次工作。
【问题讨论】:
一个低级鼠标钩子在系统开始确定目标窗口之前运行。因此,系统无法对坐标应用任何类型的虚拟化。 【参考方案1】:我不明白为什么微软决定倒退。没有表现出 DPI 意识的程序应该像往常一样运行,而不是相反。
事实上,微软的文档解释了这个问题。
来自High DPI Desktop Application Development on Windows,
使用旧版 Windows 编程技术(原始 Win32 编程、Windows 窗体、Windows 演示框架 (WPF) 等) 无法自动处理 DPI 缩放 额外的开发人员工作。没有这样的工作,应用程序就会出现 在许多常见的使用场景中模糊或大小不正确。
如果您想避免 DPI 感知问题,请创建一个UWP
应用程序。
首先,如果您要从头开始创建一个新的 Windows 应用程序,它是 强烈建议您创建一个Universal Windows Platform (UWP) 应用。 UWP 应用程序自动且动态地扩展 他们正在运行的每个显示器。
【讨论】:
对不起,这甚至没有试图解决这个问题。显然,调用SetWindowsHookEx 仅限于桌面应用程序。告诉 OP 编写一个 UWP 应用程序,只是为了让他们不会遇到 DPI 意识问题,并没有帮助。最重要的是,低级鼠标钩子完全超出了 DPI 感知业务。最重要的是,面对 Project Reunion 推荐 UWP 有点难以消化。以上是关于设置进程 DPI 感知,以便系统补偿缩放因子的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 DPI 缩放因子 (float) 设置边距 (int)