Win10RS1/2/3的DPI支持改进
acmilan2017/03/31软件综合 IP:四川

Win10RS1/2/3的DPI支持改进简介

Win10RS1(1607/周年更新):新增一套API,使得每个窗口可以具有不同的DPI缩放兼容性,并大大简化了多显示器DPI编程。WPF可选支持多显示器DPI编程。

Win10RS2(1703/创意者更新):新增PerMonitorV2模式,简化Win32和WinForms的多显示器DPI编程。新增gdiScaling选项,改善DPI虚拟化画质。程序兼容型选项卡新增DPI兼容性设置,允许用户强行打开DPI虚拟化。

Win10RS3(秋季创意者更新,待发布):更改系统级DPI不再需要注销并重新登录,只需重新打开未响应DPI变化的程序即可。

WPF编程

.NET 4.6.2已经支持Win10 RS1的多显示器DPI支持模式,不过,为了使用这个功能,你还需要作以下操作:

1、声明程序支持Win10 RS1的多显示器DPI支持模式,但不支持Win8.1的多显示器DPI支持模式

编写XXXXXXnifest,在assembly标签下添加如下声明:

<code class="language-xml"><application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowssettings>
    
    <dpiawareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiawareness>
    <dpiaware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiaware>
  </windowssettings>
</application>
</code>

2、设置目标框架为4.6.2以上,或者在XXXXXXnfig的configuration标签下添加如下声明:

<code class="language-cpp"><runtime>
  <appcontextswitchoverrides value="Switch.System.Windows.DoNotScaleForDpiChanges=false">
</appcontextswitchoverrides></runtime>
</code>

Windows Forms编程

Windows Forms已经在.NET Framework 4.7支持Win10 RS2的多显示器DPI第二版。不过,由于Windows Forms的架构限制,实际上有一些bug,比如在频繁调整下,ToolStrip控件会越来越大。

**编写支持多显示器DPI的应用程序,建议使用WPF。**因为WPF基于浮点坐标和矢量绘图,能够更好地支持DPI的动态变化(Windows Forms基于整数坐标和像素绘图)。

如果你想尝试一下这个新功能,可以按照下面的方法声明。

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/dotnet/2017/04/05/announcing-the-net-framework-4-7/

XXXXXXXXXXXXXXXXXXXXXXXXX/Blogs/dotnet/Introducing-Windows-Forms-HDPI-Improvements-in-NET-Framework-47

XXXXXXXXXXXXXXXXXXXXXXXXXX/library/XXXXXXXXXXXpx

首先在XXXXXXnifest中允许.NET检测Win10系统版本

<code class="language-xml"><compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  <application>
    
    
    
    
    
    
    
    
    
    
    <supportedos id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}">
  </supportedos></application>
</compatibility>
</code>

然后在XXXXXXnfig中指定目标版本为4.7,并开启Windows Forms的多显示器DPI支持模式第二版

<code class="language-xml"><?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup> 
      <supportedruntime version="v4.0" sku=".NETFramework,Version=v4.7">
  </supportedruntime></startup>
  <system.windows.forms.applicationconfigurationsection>
    <add key="DpiAwareness" value="PerMonitorV2">
  </add></system.windows.forms.applicationconfigurationsection>
</configuration>
</code>

GDI缩放技术

Win10 RS2支持GDI缩放技术,可以在大多数情况下取代原有的像素缩放技术,优化一些内容的显示。

XXXXXXnifest声明方法:

<code class="language-xml"><?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestversion="1.0">
<assemblyidentity type="win32" name="MyApp" version="1.0.0.0">
<asmv3:application>
   <asmv3:windowssettings>
        <dpiaware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiaware>
        <gdiscaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiscaling>
   </asmv3:windowssettings>
</asmv3:application>
</assemblyidentity></assembly>
</code>

Win32编程

Win10RS1添加了一些动态DPI函数,这些函数比Win8.1的那些使用起来更容易。

Win10RS2添加了多显示器DPI支持模式第二版,增加了对话框、菜单栏、非客户区的自动缩放支持,以及子控件的DPI消息通知。

经过实测,其它通用控件可以近乎完美缩放,但是工具栏图标不能自动缩放,还是需要处理一下WM_DPICHANGED,然后重新给工具栏指定一组更大的位图。

不一定要支持Windows 8.1/10TH1/10TH2的多显示器DPI支持模式,甚至连10RS1的都不一定要支持,因为多显示器动态DPI的API是逐渐完善的,而且需要使用多显示器DPI支持的用户一般倾向于升级到最新版Windows 10。

另外要注意,SetProcessDpiAwarenessContext函数,虽然MSDN上写的Win10RS2,实际上在Win10RS1已经导出了,所以如果以-4调用,一定要检查是否返回FALSE,返回FALSE的话需要再以-2调用一次。

XXXXXXinfest多显示器DPI支持模式第二版声明方法:

<code class="language-xml"><application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowssettings>
    
    <dpiawareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,System</dpiawareness>
    <dpiaware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiaware>
  </windowssettings>
</application>
</code>

这里的声明取自Windows SDK 10.0.15063.0头文件,而非MSDN Library开发者文档。(2017/4/6更新)

<code class="language-cpp">#pragma once

#include <windows.h>
#include <uxtheme.h>

#define DefineDLLEntry(name) T_##name *name
#define LoadDLLEntry(hmod, name) name = (T_##name *)GetProcAddress(hmod, #name)
#define _Inout_
#define _In_
#define _Out_
#define _In_opt_

// Windows 7加入的窗口消息
#define WM_DPICHANGED       0x02E0

// Windows 10 RS2加入的窗口消息
#define WM_DPICHANGED_BEFOREPARENT      0x02E2
#define WM_DPICHANGED_AFTERPARENT       0x02E3
#define WM_GETDPISCALEDSIZE             0x02E4

#if NTDDI_VERSION < 0x0A000002
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);

typedef enum DPI_AWARENESS {
	DPI_AWARENESS_INVALID = -1,
	DPI_AWARENESS_UNAWARE = 0,
	DPI_AWARENESS_SYSTEM_AWARE = 1,
	DPI_AWARENESS_PER_MONITOR_AWARE = 2
} DPI_AWARENESS;
#endif

#if NTDDI_VERSION < 0x0A000003
typedef enum DIALOG_DPI_CHANGE_BEHAVIORS {
	DDC_DEFAULT = 0x0000,
	DDC_DISABLE_ALL = 0x0001,
	DDC_DISABLE_RESIZE = 0x0002,
	DDC_DISABLE_CONTROL_RELAYOUT = 0x0004,
} DIALOG_DPI_CHANGE_BEHAVIORS;

typedef enum DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS {
	DCDC_DEFAULT = 0x0000,
	DCDC_DISABLE_FONT_UPDATE = 0x0001,
	DCDC_DISABLE_RELAYOUT = 0x0002,
} DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS;
#endif

// 不支持DPI
#define DPI_AWARENESS_CONTEXT_UNAWARE              ((DPI_AWARENESS_CONTEXT)-1)
// 系统级DPI(传统DPI模式)
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE         ((DPI_AWARENESS_CONTEXT)-2)
// 逐显示器DPI第一版(Win8.1/Win10RS1模式)
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE    ((DPI_AWARENESS_CONTEXT)-3)
// 逐显示器DPI第二版(Win10RS2模式)
// 支持对话框、菜单、非客户区自动缩放,支持子控件DPI通知
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)

// Windows 10 RS1、Windows 10 RS2加入的高DPI支持API
struct Win10DPI
{
	typedef BOOL WINAPI T_AdjustWindowRectExForDpi(
		_Inout_ LPRECT lpRect,
		_In_    DWORD  dwStyle,
		_In_    BOOL   bMenu,
		_In_    DWORD  dwExStyle,
		_In_    UINT   dpi
	);

	typedef BOOL WINAPI T_AreDpiAwarenessContextsEqual(
		_In_ DPI_AWARENESS_CONTEXT dpiContextA,
		_In_ DPI_AWARENESS_CONTEXT dpiContextB
	);

	typedef BOOL WINAPI T_EnableNonClientDpiScaling(
		_In_ HWND hwnd
	);

	typedef DPI_AWARENESS WINAPI T_GetAwarenessFromDpiAwarenessContext(
		_In_ DPI_AWARENESS_CONTEXT value
	);

	typedef UINT WINAPI T_GetDpiForSystem(void);

	typedef UINT WINAPI T_GetDpiForWindow(
		_In_ HWND hwnd
	);

	typedef int WINAPI T_GetSystemMetricsForDpi(
		_In_ int  nIndex,
		_In_ UINT dpi
	);

	typedef DPI_AWARENESS_CONTEXT WINAPI T_GetThreadDpiAwarenessContext(void);

	typedef DPI_AWARENESS_CONTEXT WINAPI T_GetWindowDpiAwarenessContext(
		_In_ HWND hwnd
	);

	typedef BOOL WINAPI T_IsValidDpiAwarenessContext(
		_In_ DPI_AWARENESS_CONTEXT  value
	);

	// Windows 8.1加入,但仍然适用
	typedef BOOL WINAPI T_LogicalToPhysicalPointForPerMonitorDPI(
		_In_    HWND    hwnd,
		_Inout_ LPPOINT lpPoint
	);

	// Windows 8.1加入,但仍然适用
	typedef BOOL WINAPI T_PhysicalToLogicalPointForPerMonitorDPI(
		_In_    HWND    hwnd,
		_Inout_ LPPOINT lpPoint
	);

	typedef DPI_AWARENESS_CONTEXT WINAPI T_SetThreadDpiAwarenessContext(
		_In_  DPI_AWARENESS_CONTEXT  dpiContext
	);

	typedef BOOL WINAPI T_SystemParametersInfoForDpi(
		_In_    UINT  uiAction,
		_In_    UINT  uiParam,
		_Inout_ PVOID pvParam,
		_In_    UINT  fWinIni,
		_In_    UINT  dpi
	);

	// Windows 10 RS2加入

	// 这个其实Windows 10 RS1已经导出了
	typedef BOOL WINAPI T_SetProcessDpiAwarenessContext(
		_In_ DPI_AWARENESS_CONTEXT value
	);

	typedef BOOL WINAPI T_SetDialogDpiChangeBehavior(
		_In_ HWND hDlg,
		_In_ DIALOG_DPI_CHANGE_BEHAVIORS mask,
		_In_ DIALOG_DPI_CHANGE_BEHAVIORS values
	);

	typedef DIALOG_DPI_CHANGE_BEHAVIORS WINAPI T_GetDialogDpiChangeBehavior(
		_In_ HWND hDlg
	);

	typedef BOOL WINAPI T_SetDialogControlDpiChangeBehavior(
		_In_ HWND hWnd,
		_In_ DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS mask,
		_In_ DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS values
	);

	typedef DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS WINAPI T_GetDialogControlDpiChangeBehavior(
		_In_ HWND hWnd
	);

	typedef HTHEME WINAPI T_OpenThemeDataForDpi(
		_In_opt_ HWND hwnd,
		_In_ LPCWSTR pszClassList,
		_In_ UINT dpi
	);

	DefineDLLEntry(AdjustWindowRectExForDpi);
	DefineDLLEntry(AreDpiAwarenessContextsEqual);
	DefineDLLEntry(EnableNonClientDpiScaling);
	DefineDLLEntry(GetAwarenessFromDpiAwarenessContext);
	DefineDLLEntry(GetDpiForSystem);
	DefineDLLEntry(GetDpiForWindow);
	DefineDLLEntry(GetSystemMetricsForDpi);
	DefineDLLEntry(GetThreadDpiAwarenessContext);
	DefineDLLEntry(GetWindowDpiAwarenessContext);
	DefineDLLEntry(IsValidDpiAwarenessContext);
	DefineDLLEntry(LogicalToPhysicalPointForPerMonitorDPI);
	DefineDLLEntry(PhysicalToLogicalPointForPerMonitorDPI);
	DefineDLLEntry(SetThreadDpiAwarenessContext);
	DefineDLLEntry(SystemParametersInfoForDpi);
	DefineDLLEntry(SetProcessDpiAwarenessContext);
	DefineDLLEntry(SetDialogDpiChangeBehavior);
	DefineDLLEntry(GetDialogDpiChangeBehavior);
	DefineDLLEntry(SetDialogControlDpiChangeBehavior);
	DefineDLLEntry(GetDialogControlDpiChangeBehavior);
	Win10DPI()
	{
		HMODULE huser32 = GetModuleHandleA("user32");
		LoadDLLEntry(huser32, AdjustWindowRectExForDpi);
		LoadDLLEntry(huser32, AreDpiAwarenessContextsEqual);
		LoadDLLEntry(huser32, EnableNonClientDpiScaling);
		LoadDLLEntry(huser32, GetAwarenessFromDpiAwarenessContext);
		LoadDLLEntry(huser32, GetDpiForSystem);
		LoadDLLEntry(huser32, GetDpiForWindow);
		LoadDLLEntry(huser32, GetSystemMetricsForDpi);
		LoadDLLEntry(huser32, GetThreadDpiAwarenessContext);
		LoadDLLEntry(huser32, GetWindowDpiAwarenessContext);
		LoadDLLEntry(huser32, IsValidDpiAwarenessContext);
		LoadDLLEntry(huser32, LogicalToPhysicalPointForPerMonitorDPI);
		LoadDLLEntry(huser32, PhysicalToLogicalPointForPerMonitorDPI);
		LoadDLLEntry(huser32, SetThreadDpiAwarenessContext);
		LoadDLLEntry(huser32, SystemParametersInfoForDpi);
		LoadDLLEntry(huser32, SetProcessDpiAwarenessContext);
		LoadDLLEntry(huser32, SetDialogDpiChangeBehavior);
		LoadDLLEntry(huser32, GetDialogDpiChangeBehavior);
		LoadDLLEntry(huser32, SetDialogControlDpiChangeBehavior);
		LoadDLLEntry(huser32, GetDialogControlDpiChangeBehavior);
	}
};

struct WinVistaDPI
{
	typedef BOOL WINAPI T_SetProcessDPIAware(VOID);
	typedef BOOL WINAPI T_IsProcessDPIAware(VOID);
	typedef BOOL WINAPI T_LogicalToPhysicalPoint(
		_In_ HWND hWnd,
		_Inout_ LPPOINT lpPoint
	);
	typedef BOOL WINAPI T_PhysicalToLogicalPoint(
		_In_ HWND hWnd,
		_Inout_ LPPOINT lpPoint
	);
	DefineDLLEntry(SetProcessDPIAware);
	DefineDLLEntry(IsProcessDPIAware);
	DefineDLLEntry(LogicalToPhysicalPoint);
	DefineDLLEntry(PhysicalToLogicalPoint);
	WinVistaDPI()
	{
		HMODULE huser32 = GetModuleHandleA("user32");
		LoadDLLEntry(huser32, SetProcessDPIAware);
		LoadDLLEntry(huser32, IsProcessDPIAware);
		LoadDLLEntry(huser32, LogicalToPhysicalPoint);
		LoadDLLEntry(huser32, PhysicalToLogicalPoint);
	}
};

// Win8.1加入,但已不适用的API
#ifdef WIN81DPI_DEPRECATED
typedef enum _MONITOR_DPI_TYPE {
	MDT_EFFECTIVE_DPI = 0,
	MDT_ANGULAR_DPI = 1,
	MDT_RAW_DPI = 2,
	MDT_DEFAULT = MDT_EFFECTIVE_DPI
} Monitor_DPI_Type;

typedef enum _PROCESS_DPI_AWARENESS {
	PROCESS_DPI_UNAWARE = 0,
	PROCESS_SYSTEM_DPI_AWARE = 1,
	PROCESS_PER_MONITOR_DPI_AWARE = 2
} PROCESS_DPI_AWARENESS;

typedef enum _SHELL_UI_COMPONENT {
	SHELL_UI_COMPONENT_TASKBARS = 0,
	SHELL_UI_COMPONENT_NOTIFICATIONAREA = 1,
	SHELL_UI_COMPONENT_DESKBAND = 2
} SHELL_UI_COMPONENT;

typedef HRESULT WINAPI T_GetDpiForMonitor(
	_In_  HMONITOR         hmonitor,
	_In_  MONITOR_DPI_TYPE dpiType,
	_Out_ UINT             *dpiX,
	_Out_ UINT             *dpiY
);

typedef UINT WINAPI T_GetDpiForShellUiComponent(
	_In_ SHELL_UI_COMPONENT component
);

typedef HRESULT WINAPI T_GetProcessDpiAwareness(
	_In_  HANDLE                hprocess,
	_Out_ PROCESS_DPI_AWARENESS *value
);

typedef HRESULT WINAPI T_SetProcessDpiAwareness(
	_In_ PROCESS_DPI_AWARENESS value
);
#endif
</uxtheme.h></windows.h></code>

(未完待续)

[修改于 6年10个月前 - 2017/07/18 22:22:09]

来自:计算机科学 / 软件综合
0
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年3个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}