转载自:https://www.52pojie.cn/thread-512713-1-1.html
为了在Win10下随时随地程序窗口都不模糊都能正常缩放,你必须在你的程序中加入Per-Monitor DPI Aware支持
因为Win10开始,用户在设置应用中调整DPI是不需要注销的!于是一些System Aware和Unaware的应用会被DWM虚拟化技术进行粗暴缩放,于是就模糊了
如果你定义你的程序支持Per-Monitor DPI Aware,但又没实现Per-Monitor DPI Aware支持的话,就会无法动态缩放(体验比模糊更加糟糕)
实现Per-Monitor DPI Aware支持,首先在程序清单中加入<dpiAware>True/PM</dpiAware>;或者你也可以在VS的项目属性里面设置即可
有人绝对会问,为何不调用API设置,因为对应API要求Windows 8.1以上;所以为了方便起见,我建议还是在清单文件添加
接着,获取DPI的部分需要重写
因为我们惯用的GetDeviceCaps只能获取系统DPI(MSDN指明是与显卡驱动的实现有关)
要获取真实DPI,你需要使用GetDpiForMonitor;但这个API至少要8.1起步
我在下面提供一些参考
重写的GetDpiForMonitor
[C++] 纯文本查看 复制代码
?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | #include <ShellScalingApi.h> static HRESULT WINAPI GetDpiForMonitor( _In_ HMONITOR hmonitor, _In_ MONITOR_DPI_TYPE dpiType, _Out_ UINT *dpiX, _Out_ UINT *dpiY) { HINSTANCE hInstWinSta = LoadLibraryW(L "SHCore.dll" ); if (hInstWinSta == nullptr) return E_NOINTERFACE; typedef HRESULT (WINAPI * PFN_GDFM)( HMONITOR , MONITOR_DPI_TYPE, UINT *, UINT *); PFN_GDFM pGetDpiForMonitor = (PFN_GDFM)GetProcAddress(hInstWinSta, "GetDpiForMonitor" ); if (pGetDpiForMonitor == nullptr) return E_NOINTERFACE; return pGetDpiForMonitor(hmonitor, dpiType, dpiX, dpiY); } |
获取DPI比例的代码逻辑片段
[C++] 纯文本查看 复制代码
?
01 02 03 04 05 06 07 08 09 10 11 12 | // 获取DPI比例 HRESULT hr = E_FAIL; hr = GetDpiForMonitor( MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), MDT_EFFECTIVE_DPI, ( UINT *)&g_xDPI, ( UINT *)&g_yDPI); if (hr != S_OK) { g_xDPI = GetDeviceCaps(GetDC(hWnd), LOGPIXELSX); g_yDPI = GetDeviceCaps(GetDC(hWnd), LOGPIXELSY); } |
然后实现窗口的WM_DPICHANGED消息,该消息有用的参数
[C++] 纯文本查看 复制代码
?
1 2 3 | // LOWORD(wParam); // x轴DPI // HIWORD(wParam); // y轴DPI // (RECT*)lParam /// 系统建议的新窗口位置 |
你应该在该消息实现对界面的调整或者通知你的UI框架
到这里,MSDN文档的Sample基本就结束了
但是你要真的碰到不同DPI缩放的显示器或者调整DPI后没有注销,然后你就傻了
因为标题栏和菜单不支持缩放
要实现这个问题,貌似只有一种方式,那就是隐藏非客户区,自己绘制一个标题栏并且自己绘制菜单
但是如果你不要自绘标题栏和菜单(太麻烦了,而且浪费时间)
你可以学习Windows 10的命令提示符窗口,调用(未公开)API让系统自动帮你缩放标题栏和菜单
[C++] 纯文本查看 复制代码
?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 开启对话框Per-Monitor DPI Aware支持(至少Win10) inline BOOL EnablePerMonitorDialogScaling() { typedef BOOL (WINAPI *PFN_EnablePerMonitorDialogScaling)(); PFN_EnablePerMonitorDialogScaling pEnablePerMonitorDialogScaling = (PFN_EnablePerMonitorDialogScaling)GetProcAddress( GetModuleHandleW(L "user32.dll" ), ( LPCSTR )2577); if (pEnablePerMonitorDialogScaling) return pEnablePerMonitorDialogScaling(); return -1; } // 开启子窗体DPI消息(至少Win10) inline BOOL EnableChildWindowDpiMessage( _In_ HWND hWnd, _In_ BOOL bEnable) { typedef BOOL (WINAPI *PFN_EnableChildWindowDpiMessage)( HWND , BOOL ); PFN_EnableChildWindowDpiMessage pEnableChildWindowDpiMessage = (PFN_EnableChildWindowDpiMessage)GetProcAddress( GetModuleHandleW(L "user32.dll" ), "EnableChildWindowDpiMessage" ); if (pEnableChildWindowDpiMessage) return pEnableChildWindowDpiMessage(hWnd, bEnable); return -1; } |
以上API定义通过IDA查看Win10的ConhostV2.dll得到
可用于Windows 10 Threshold 1及以后(如果微软不去掉的话)
在显示对话框前调用下EnablePerMonitorDialogScaling();不用实现WM_DPICHANGED消息,系统会自动帮你缩放对话框
窗体应用,需要用CreateWindowEx创建窗体后;调用EnableChildWindowDpiMessage(窗口hWnd,TRUE);
然后适当在WM_DPICHANGED添加调整代码即可完美Per-Monitor DPI Aware
效果图,仅供参考(调整DPI后没有注销,对话框应用)
希望我的资料能够对开发应用的人有帮助
毛利, 2016/7/6