当使用
LoadCursor
函数从资源加载光标时,生成的 HCURSOR
可以在不同的显示器上使用,并且始终以正确的大小显示。
即:通常:
但是,当以编程方式从内存创建光标时(例如使用
LookupIconIdFromDirectoryEx
和 CreateIconFromResourceEx
),生成的光标具有固定的分辨率。这意味着它在混合 DPI 多显示器设置中的至少一台显示器上显示为错误的尺寸。
我还检查了
LoadCursorFromFile
,它也提供了像LoadCursor
这样的动态分辨率行为。
有没有一种方法可以以编程方式创建一个光标,根据它显示的显示器动态切换?让加载了
LoadCursor
的光标以不同的方式工作,幕后发生了什么魔法?
经过大量实验,我终于发现,如果使用
scaleWithDpi
选项,WPF 可以从资源和内存流加载游标并获得正确的 DPI 行为:
public Cursor(Stream cursorStream, bool scaleWithDpi)
查看参考源,它最终出现在函数
LoadFromStream
中,该函数通过将流写入临时文件并从文件加载来加载流。 查看来源
总结一下:
LR_DEFAULTSIZE
函数的 LoadImage
标志有关。提供使用 LoadCursor 加载的光标以不同方式工作的幕后发生了什么魔法?
hInst
/MAKEINTRESOURCE(resId)
或
LR_LOADFROMFILE
标志的
LoadImage和 LoadCursorFromFile API 确实在返回的
HCURSOR
/HICON
中保存附加信息。
DI_DEFAULTSIZE
和 CopyImage 与 LR_COPYFROMRESOURCE
,也许其他 API 正在使用此信息从资源源获取最适合当前监视器/窗口的 DPI 相关大小的图标图像。
可以使用自 Windows Vista 起可用的 szModName
结构的
wResID
/szResName
/ICONINFOEX
成员中的 GetIconInfoEx手动获取此图标源信息。
CreateIconFromResourceEx 不会设置此信息,因为它只接收指向图标/光标位的内存指针。在这种情况下,简单的图像缩放应用于
DrawIconEx
调用。
除了使用文件/exe/dll 的加载之外,似乎没有其他方法可以使用这种 DPI 感知绘制机制。
Raymond Chen 博客中有关此主题的一些信息:
PS:作为解决方法我认为可以处理 WM_SETCURSOR 窗口消息,计算窗口的 DPI 感知光标大小(使用
wParam
与 GetDpiForWindow 和 GetSystemMetricsForDpi 与 SM_CXCURSOR
/ SM_CYCURSOR
)加载相应的HCURSOR
并用它调用SetCursor函数。
Windows 10 中还有一个新的用户光标大小设置也可能会被考虑在内。它保存在
CursorBaseSize
注册表下的 HKEY_CURRENT_USER\Control Panel\Cursors
中。 这是一些 Qt 代码,它处理它并返回正确的光标大小。
从 Windows 10 1607 开始有一个新的 API 可能会有所帮助:SetThreadCursorCreationScaling