实现每显示器高DPI识别(Per-Monitor DPI Aware)的注意事项

转载自: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

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值