Delphi 高 DPI 在自缩放和 Windows 缩放之间切换
Delphi High DPI switch between own scaling and Windows scaling
我的一些客户希望能够手动缩放我的应用程序(当 Windows dpi 设置为 96 时),因此我必须实施缩放。不幸的是,这些客户无法将 Windows DPI 设置为其他值并让 WIndows 扩展我的应用程序,因为他们使用的一些非常重要的应用程序在分辨率 <> 96 DPI 时根本表现不佳。
我设法使我的 Delphi 10.1 应用程序规模相当好,即使在 200% 上也是如此,但因子越高,某些比例变得越多 "not so nice looking"。许多第 3 方组件需要特殊处理以进行缩放,即使这样也不能 100% 准确缩放。虽然按 windows 缩放的应用程序在高分辨率下看起来有点模糊,但所有比例都是 100% 准确的,恕我直言,该应用程序看起来更专业。
所以我问自己是否可以创建一个设置,允许告诉 Windows 默认进行缩放,如果客户想要与当前 Windows 缩放比例。此设置托管在应用程序启动时读取的可执行文件的 Windows 清单中。有没有办法在运行时更改它(应用程序的早期启动)?创建两个具有不同清单的可执行文件肯定不是好的解决方案。
感谢您的帮助
感谢 Sertac Akyuz,我找到了解决问题的办法。在包含缩放代码的单元的初始化部分,我可以在 DPI 感知和非 DPI 感知之间切换。重要的是不要在应用程序清单中进行此设置,这可以通过提供这样的自定义清单来实现(使用控件装饰和 运行 以及当前用户的权限):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
publicKeyToken="6595b64144ccf1df"
language="*"
processorArchitecture="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
这是根据注册表项进行的实际代码切换:
// Set DPI Awareness depending on a registry setting
with TRegIniFile.create('SOFTWARE\' + SRegName) do
begin
setting := readInteger('SETTINGS', 'scale', 0);
Free;
end;
handle := LoadLibrary('shcore.dll');
if handle <> 0 then
begin
setProcessDPIAwareness := GetProcAddress(handle, 'SetProcessDpiAwareness');
if Assigned(setProcessDPIAwareness) then
begin
if setting < 2 then
// setting <2 means no scaling vs Windows
setProcessDPIAwareness(0)
else
// setting 2: 120%, 3: 140% vs. Windows
// The actual used scaling factor multiplies by windows DPI/96
setProcessDPIAwareness(1);
end;
FreeLibrary(handle);
// Get windows scaling as Screen.PixelsPerInch was read before swiching DPI awareness
// Our scaling routines now work with WinDPI instead of Screen.PixelsPerInch
WinDPI:= Screen.MonitorFromWindow(application.handle).PixelsPerInch;
end;
此代码段的最后一行检索当前监视器的当前 DPI,因为 screen.pixelsperinch 似乎之前已初始化,并且对于非 dpi 感知应用程序总是 return 96。我在所有后续的缩放计算中都使用了 winDPI 的值,并且效果很好。
我的一些客户希望能够手动缩放我的应用程序(当 Windows dpi 设置为 96 时),因此我必须实施缩放。不幸的是,这些客户无法将 Windows DPI 设置为其他值并让 WIndows 扩展我的应用程序,因为他们使用的一些非常重要的应用程序在分辨率 <> 96 DPI 时根本表现不佳。
我设法使我的 Delphi 10.1 应用程序规模相当好,即使在 200% 上也是如此,但因子越高,某些比例变得越多 "not so nice looking"。许多第 3 方组件需要特殊处理以进行缩放,即使这样也不能 100% 准确缩放。虽然按 windows 缩放的应用程序在高分辨率下看起来有点模糊,但所有比例都是 100% 准确的,恕我直言,该应用程序看起来更专业。
所以我问自己是否可以创建一个设置,允许告诉 Windows 默认进行缩放,如果客户想要与当前 Windows 缩放比例。此设置托管在应用程序启动时读取的可执行文件的 Windows 清单中。有没有办法在运行时更改它(应用程序的早期启动)?创建两个具有不同清单的可执行文件肯定不是好的解决方案。
感谢您的帮助
感谢 Sertac Akyuz,我找到了解决问题的办法。在包含缩放代码的单元的初始化部分,我可以在 DPI 感知和非 DPI 感知之间切换。重要的是不要在应用程序清单中进行此设置,这可以通过提供这样的自定义清单来实现(使用控件装饰和 运行 以及当前用户的权限):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
publicKeyToken="6595b64144ccf1df"
language="*"
processorArchitecture="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
这是根据注册表项进行的实际代码切换:
// Set DPI Awareness depending on a registry setting
with TRegIniFile.create('SOFTWARE\' + SRegName) do
begin
setting := readInteger('SETTINGS', 'scale', 0);
Free;
end;
handle := LoadLibrary('shcore.dll');
if handle <> 0 then
begin
setProcessDPIAwareness := GetProcAddress(handle, 'SetProcessDpiAwareness');
if Assigned(setProcessDPIAwareness) then
begin
if setting < 2 then
// setting <2 means no scaling vs Windows
setProcessDPIAwareness(0)
else
// setting 2: 120%, 3: 140% vs. Windows
// The actual used scaling factor multiplies by windows DPI/96
setProcessDPIAwareness(1);
end;
FreeLibrary(handle);
// Get windows scaling as Screen.PixelsPerInch was read before swiching DPI awareness
// Our scaling routines now work with WinDPI instead of Screen.PixelsPerInch
WinDPI:= Screen.MonitorFromWindow(application.handle).PixelsPerInch;
end;
此代码段的最后一行检索当前监视器的当前 DPI,因为 screen.pixelsperinch 似乎之前已初始化,并且对于非 dpi 感知应用程序总是 return 96。我在所有后续的缩放计算中都使用了 winDPI 的值,并且效果很好。