一、缩放比例和DPI的关系DPI本意指的是“每英寸点数”,但是由于各大显示器厂商并不理会系统设置,因此它的值现在并不具有任何实际意义,仅仅是一个缩放因子而已。在Windows中,默认的DPI值为96,定义为100%缩放(即不缩放)。一般来说,缩放比例的步进值是25%,对应DPI的步进值为24,当然用户也可以定义任意缩放值。
缩放比例和DPI的关系:
100% - 96 DPI
125% - 120 DPI
150% - 144 DPI
175% - 168 DPI
200% - 192 DPI
依次类推
500% - 480 DPI
二、支持的缩放比例Windows NT 3.x / 4.0 —— 支持100%和125%两个缩放比例
Windows 95 / 98 / Me / 2000 —— 支持最高到500%
Windows XP / 2003 / Vista —— 最高可调到500%,但支持最高到155%(超过这个比例鼠标指针会糊掉)
Windows 7 / 8 —— 最高可调到500%,但支持最高到199%(超过这个比例鼠标指针会糊掉)
Windows 8.1 / 10 —— 支持最高到500%
三、如何调整DPIWindows NT 3.x —— 主群组,控制面板,显示
Windows 95 / NT 4.0 —— 右键桌面,属性,设置
Windows 98 / Me / 2000 / XP / 2003 —— 右键桌面,属性,设置,高级
Windows Vista —— 右键桌面,个性化,调整字体大小(DPI)
以上系统应用新DPI需重新启动
Windows 7 / 8 / 8.1 —— 右键桌面,屏幕分辨率,放大或缩小文本和其它项目
以上系统应用新DPI需重新登录
Windows 10 —— 右键桌面,显示设置
以上系统应用新DPI不需重新登录,但部分程序需重新登录才能响应新DPI设置
四、DPI如何获取Windows 8及之前版本仅支持系统级DPI——
在C#程序中,可使用XXXXXXXXXXXomHwnd(IntPtr.Zero).DpiX和DpiY获取。在C/C++程序中,可以先GetDC(NULL)获取桌面hDC,然后GetDeviceCaps(hDC, LOGPIXELSX和LOGPIXELSY)获取,最后别忘了ReleaseDC(hDC)。
Windows 8.1及之后版本支持逐显示器级DPI——
在C/C++程序中,可以使用MonitorFromPoint和GetDpiForMonitor获取指定显示器的DPI,也可以从WM_DPICHANGED消息的lParam参数中获取。
五、关于Windows Vista+的系统级DPI虚拟化由于长期以来屏幕的分辨率都没有上去,导致大多数第三方的老旧Windows软件并不理会高DPI,这导致了很多软件在高DPI下,出现排版错乱、界面元素过小等问题。为了解决这个问题,Windows Vista引入了DPI虚拟化机制,通过DWM对界面进行强制缩放来解决界面错乱问题。
在超过125%比例时,Windows Vista会开启DPI虚拟化。Windows 10使用滑杆调整DPI的话,在125%也会进行DPI虚拟化。但使用自定义缩放比例对话框设置的话,目前与之前版本表现一样。
这种机制不是万能的,它仅仅解决了界面错乱的问题。它仅对客户区进行缩放,也就是说,如果程序对非客户区进行操作(如截屏),DPI虚拟化是无能为力的。DPI虚拟化还会引起界面的模糊(整数倍缩放例外,如200%)。
如果用户认为这种缩放引起了兼容性问题,可以在自定义对话框中勾选“使用Windows XP风格DPI缩放比例”关闭DPI虚拟化。Windows 8.1把它放到了exe属性中,兼容性选项卡勾选“高DPI时禁用显示缩放”,可对程序单独关闭DPI虚拟化。
如果程序已经支持高DPI,Windows Vista允许程序关闭DPI虚拟化,方法是调用user32.dll中的SetProcessDPIAware()函数,声明程序支持DPI缩放。这个函数对整个进程起作用,也就是说DLL是不应该调用这个函数的。
在调用这个函数之前,通过GetDeviceCaps获得的DPI是96;调用这个函数之后,可通过GetDeviceCaps获得真实的DPI值。
获取程序的DPI支持状态,可使用user32.dll中的IsProcessDPIAware()函数。
Windows Vista还允许在程序的manifest(清单)资源(一个嵌入exe的xml配置文件)中声明程序支持DPI缩放,声明<dpiAware>true</dpiAware>相当于在程序启动时运行SetProcessDPIAware()函数。
<code class="lang-xml"><?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestversion="1.0">
<description>A free .NET IDE</description>
<dependency>
<dependentassembly>
<assemblyidentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorarchitecture="*" publickeytoken="6595b64144ccf1df" language="*" >
</assemblyidentity></dependentassembly>
</dependency>
<trustinfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedprivileges>
<requestedexecutionlevel level="asInvoker" uiaccess="false">
</requestedexecutionlevel></requestedprivileges>
</security>
</trustinfo>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowssettings xmlns:ws="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<ws:dpiaware>true</ws:dpiaware>
</windowssettings>
</application>
</assembly></code>
六、关于Windows 8.1+的逐显示器DPI虚拟化在Windows 8.1中,引入了另一个DPI缩放级别:逐显示器DPI。程序窗口在拖动到其它DPI不同的显示器时(或Windows 10中DPI设置实时变化时),会收到WM_DPICHANGED消息,程序需要响应这个消息以对DPI改变做出响应,重新进行界面布局缩放。
由于以前的程序只支持在启动时进行布局缩放,并不响应WM_DPICHANGED消息,所以在拖动到其它DPI不同的显示器时(或Windows 10中DPI设置实时变化时),系统会对它们进行DPI虚拟化。
Windows 8.1引入了一个新的函数进行DPI缩放级别声明。调用shcore.dll中的SetProcessDpiAwareness(int level)可以声明DPI缩放级别:0表示不支持DPI缩放,1表示支持系统级DPI,2表示支持逐显示器DPI。
这个函数调用一次之后,进程的DPI缩放级别即被锁定无法更改,即使使用老的SetProcessDPIAware函数也不行。老版本的SetProcessDPIAware()调用等价于SetProcessDpiAwareness(1),它也会锁定DPI缩放级别。所以SetProcessDpiAwareness必须在任何指定DPI缩放级别的操作之前调用,否则无效。
在级别0,无论是GetDeviceCaps还是GetDpiForMonitor都会返回96;在级别1,两个函数都会返回系统级DPI的值;在级别2,GetDeviceCaps返回系统级DPI,而GetDpiForMonitor返回指定显示器的DPI,同时窗口还会收到WM_DPICHANGED消息。
获得某个进程的DPI缩放级别,可以使用shcore.dll中的GetProcessDpiAwareness函数。
Windows 8.1和之前的操作系统中,manifest资源中的<dpiAware>声明的等效函数调用也不同:
false —— SetProcessDpiAwareness(0) —— 什么也不做
true —— SetProcessDpiAwareness(1) —— SetProcessDPIAware()
true/pm —— SetProcessDpiAwareness(2) —— SetProcessDPIAware()
per monitor —— SetProcessDpiAwareness(2) —— 什么也不做
200字以内,仅用于支线交流,主线讨论请采用回复功能。