duilib是win32一个不错的界面库,有人维护的版本也支持DPI
DPI是什么
先了解什么是DPI,DPI全名是Dots Per Inch 就是每英寸中像素点的个数。Windows中默认为96。
在以前,电脑的屏幕一般分辨率都不高(1366x768),随着屏幕技术进步,逐渐高分屏也多了起来,到目前新出产品最低都是1080p,也就是1920x1080的分辨率,将来可能最低配还得上升,参见iMac的分辨率。分辨率提高了,但是屏幕的尺寸却没变,所以看起来颗粒感减少,画面细腻,是个好事啊。
可是,1080p的普及,对于很多还停留在老分辨率的软件,如果放到1080p或者更高的显示器上,如果还使用原来的96DPI,界面会看起来很小,眼睛很累,所以win8以上都有调整DPI缩放的设置,推荐一档是125%,也就是缩放1.25倍,原来的程序界面就被系统放大到1.25倍。
Windows采用DWM(Desktop Window Manager)来负责窗口显示,当DPI较高时,而软件又没有自己处理,这时系统帮你适配,如前面设置的125%的缩放。类似放大图片,所以你的程序会看起来模糊,这就你的界面库要支持DPI的重要性。
所谓界面库支持DPI,就是根据当前的缩放比例,使用相对应的分辨率的资源(图片和文字等)。
科普太多。直接进入主题吧。
DPI在Duilib中使用
有两种方法,一种是函数实现,一种是清单文件设置
1、函数实现
直接在你的InitWindows()里调用,
m_pm.GetDPIObj()->SetDPIAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
内部是调用WindowsAPI
HRESULT WINAPI SetProcessDpiAwareness(_In_ PROCESS_DPI_AWARENESSvalue);
typedef enum _PROCESS_DPI_AWARENESS {
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2
} PROCESS_DPI_AWARENESS;
该函数告诉系统,要不要你帮我适配,就是前面讲的给我强行拉伸。
- ** PROCESS_DPI_UNAWARE** 告诉系统改程序自己无法处理不同DPI,获取的数据都是被处理过的。
- PROCESS_SYSTEM_DPI_AWARE 告诉系统该程序会在启动的显示器上自己支持不同的DPI,但是如果程序被拖动到了其他DPI不一样的显示器上时,需要DWM的帮助。
-
PROCESS_PER_MONITOR_DPI_AWARE 告诉系统任何时候该程序都能适应不同的DPI,不需要任何帮助。
如果你的程序支持 PROCESS_PER_MONITOR_DPI_AWARE,当你的窗口移动到DPI不同的显示器上时,会收到 WM_DPICHANGED 消息。
主要相关API:
SetProcessDpiAwareness :设置当前进程的DPI感知等级。
GetDpiForMonitor :查询显示的DPI。
MonitorFromPoint :获取指定点的显示器的句柄。
MonitorFromRect :获取与指定矩形相交面积最大的显示器句柄。
二、清单文件配置
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
设置dpi感知为ture,即自己处理适配。
配置完成后,你的程序不会随着系统设置的缩放而被拉伸。
PS:刚发现清单文件配置的duilib不能动态设置DPI,why?暂时先使用第一种方法吧!-_-
动态设置DPI显示:
//m_pm为窗口管理类,有的维护库为m_pManager
m_pm.SetDPI(120); //当前窗口
m_pm.SetAllDPI(120);//所有窗口
注意120是DPI,96x125%=120,duilib默认是96,就是不缩放。
一般跟随系统的缩放比例可以写成:
int sysDPI = GetDeviceCaps(m_pm.GetPaintDC(), LOGPIXELSX); //获取系统DPI
m_pm.SetAllDPI(sysDPI);
LOGPIXELSX: Logical pixels/inch in X
LOGPIXELSY: Logical pixels/inch in Y
兼容win10
发现,duilib并不支持win10的DPI。方法一里的设置一直不成功,看了会源码发现,是SetDPIAwareness里调用IsWindows8Point1OrGreater()一直失败,该函数顾名思义就是判断系统版是否8.1及以上版本。
注意 win8.1及以上才支持DPI Aware,所以这里要判断。
发现函数是拷贝了win10SDK的VersionHelpers.h里的源码。
内部调用
VerifyVersionInfo(...)
MSDN关于这个解释了一波
Targeting your application for Windows
GetVersion, GetVersionEx, VerifyVersionInfo这些函数最高只能识别出Win8,IsWindowsVersionOrGreater系列函数只是VerifyVersionInfo的一个封装,所以也存在同样的问题。
MSDN里说需要在配置清单里说明程序兼容win10
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<!-- OSVersion -->
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
</application>
</compatibility>
</assembly>
程序清单加入上述。
visual studio里配置清单文件。
上述代码拷贝进xml文件,建议软件名.xml
项目属性-清单工具-输入输出-附加清单文件,加入$(TargetName).xml。
可以选择是否嵌入清单选项,否的话,程序exe目录下还会生成响应的xxxx.manifest,软件运行需要带上它,是的话就不用。
附常用清单文件:
支持Windows 6.0界面库、支持管理员权限、兼容WIN8/WIN10下取系统版本、兼容DPI Aware
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<!-- Windows 6.0 Style -->
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
</dependentAssembly>
</dependency>
<!-- Administrator -->
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<!-- DPI Aware -->
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<!-- OSVersion -->
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1/Windows Blue/Server 2012 R2 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows Vista/Server 2008 -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7/Server 2008 R2 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8/Server 2012 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
</application>
</compatibility>
</assembly>