如何使Windows 8.1知道我的Delphi应用程序想要支持每个监视器DPI?

我试图让Windows 8.1识别出我一直在尝试构build的Delphi XE6应用程序(一个演示程序),并且只有通过Manifest技术才能识别我的应用程序是Per-Monitor DPI。 Delphi XE6(以及所有其他类似Delphi的最新版本)使得在Project Options中添加一个易于执行的清单,并且我已经这样做了。

这是我使用MSDN资源确定的.manifest内容。 我怀疑它可能会稍微不正确。

如果你想尝试这个清单,做一个空的VCL应用程序,使用这个内容作为你的清单,并添加代码(代码是目前附加到我对这个问题的答案)。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" > <!-- Per Monitor DPI Awareness in Windows 8.1 uses asmv3:application + asmv3:windowsSettings --> <asmv3:application> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> <dpiAware>True</dpiAware> </asmv3:windowsSettings> </asmv3:application> <!-- Dear Microsoft, Don't Lie to Me About What Version of Windows I am On --> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <!-- Windows Vista and Windows Server 2008 --> <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!-- Windows 7 and Windows Server 2008 R2 --> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <!-- Windows 8 and Windows Server 2012 --> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <!-- Windows 8.1 and Windows Server 2012 R2 --> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> </application> </compatibility> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> </assembly> 

有没有人得到这个工作? 我发现上述是​​不被认可的。 如果我先调用SetProcessDPIAwareness(Process_Per_Monitor_DPI_Aware) ,然后调用GetProcessDPIAwareness(hProc,Awareness) ,我得到必要的Awareness = Process_Per_Monitor_DPI_Aware ,但是我已经看到这种方法存在潜在的缺点,所以我宁愿只使用Manifest做法。

如果我打电话给GetProcessDPIAwareness(hProc,Awareness) ,我回到`Awareness = Process_DPI_Unaware'。

我的另一个担心是,他们在MSDN中指定添加一个ADDITIONAL清单。 而我正在使用Delphi XE6的IDE将ONE和ONLY ONE清单链接到我的应用程序中。 我从来没有注意到,添加任何额外的清单,而不是只有一个是有问题的,除此之外也许在Visual Studio 2010中的.manifestpipe理系统是跛脚的,这就是为什么提示存在,所以与其他IDE没有关系/语言。

在Visual Studio 2013中,项目选项内部有一个checkbox,但是我没有Visual Studio 2013,所以我无法检查工作的.manifest。

更新:

这是另一个清单的镜头:

 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" > <asmv3:application> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> <dpiAware>true</dpiAware> </asmv3:windowsSettings> </asmv3:application> </assembly> 

上面的迷你清单改变了应用程序的行为,但不完全是我想要的方式。 通过上面的小清单,检测到旧的Windows 8.0 / Windows 7 / Vista DPI感知水平。

更新2:

感谢雷米的想法。 有趣的是,以下内容似乎足以使应用程序启动。 但是,将SMI / 2005语法与上面的语句混合在一起导致了并行启动错误。 你可以看到微软一直在炫耀他们的performance。 请注意,以下内容实际上并不能解决我的问题,但它提供了另一个“潜在的基础forms”,可能与真正的解决scheme相关:

  <assembly xmlns="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0" > <application> <windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings"> <dpiAware>true</dpiAware> </windowsSettings> </application> </assembly> 

更新3:

代码红色警报! 请勿在任何Delphi VCL应用程序中使用以下OS兼容性标志: <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> 。 微软已经以可怕的方式破坏了鼠标的捕捉行为,破坏了窗口的画面,我甚至都没有想到。 打开这个标志在我的应用程序中造成了非常微妙的错误,包括绘画问题,无法点击控件(由于鼠标捕捉丢失而鼠标向下的消息未到达控件)以及许多其他问题。

它是记录在MSDN主题编写DPI-Aware桌面和Win32应用程序 :

通过修改应用程序清单或调用SetProcessDpiAwarenessAPI,将应用程序标记为monitor-DPI。 我们建议您使用应用程序清单,因为它会在启动应用程序时设置DPI感知级别。 仅在以下情况下使用API​​:

  • 您的代码是在通过rundll32.exe运行的DLL。 这是一个不支持应用程序清单的启动机制。
  • 您需要根据操作系统版本或其他考虑因素做出复杂的运行时决策。 例如,如果您需要应用程序在Windows 7上识别系统DPI并在Windows 8.1上动态识别,请使用True / PM清单设置。

如果使用SetProcessDpiAwareness方法设置DPI感知级别,则必须在强制系统开始虚拟化的任何Win32API调用之前调用SetProcessDpiAwareness。

DPI意识清单值,说明

False将应用程序设置为不识别DPI。

True将应用程序设置为支持系统DPI。

每个监视器在Windows 8.1上,将应用程序设置为每个监视器-DPI感知。 在Windows Vista到Windows 8上,将应用程序设置为不识别DPI。

True / PM在Windows 8.1上,将应用程序设置为每个monitor-DPI可识别。 在Windows Vista到Windows 8上,将应用程序设置为system-DPI。

您只需使用标准的DPI识别清单,但指定True / PMPer-monitor而不是True

相同的主题给DPI的意识清单如下:

 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" > <asmv3:application> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> <dpiAware>true</dpiAware> </asmv3:windowsSettings> </asmv3:application> </assembly> 

所以,用您选择的值替换True

这个表单有效,但有一些警告:

  • 注意关于asmv1,asm.v2和asmv3的各种“元数据”差异。 可能不重要。

  • 正如大卫指出的那样, True/PM价值,而不是True ,使所有的差异。 微软显然是DID文件吧。 (笑容)

  • 某些SMI / 2011变体将启动,但不会出现SxS启动错误,但是我还没有找到SMI / 2011变体。

  • 在使用启用了Per Monitor API的应用程序并且在Windows 8.1中定义了操作系统兼容性之后,我在应用程序中发现了一些HORRIBLE回归。 微软已经改变了Windows 8.1级应用程序中的鼠标焦点行为。 我不推荐这个方法。 选择通过代码而不是通过MANIFEST,不要使用下面的<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>例子!

这是XML。 我用StackOverflow来处理这个XML,所以如果看起来不好,你会看到StackOverflow的错误。

 <?xml version="1.0" encoding="utf-8" ?> <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="asInvoker" uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> </application> </compatibility> <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> <dpiAware>True/PM</dpiAware> </asmv3:windowsSettings> </asmv3:application> </asmv1:assembly> 

你也可以使用codez:

代码示例:

 unit PerMonitorApi; interface const Process_DPI_Unaware = 0; Process_System_DPI_Aware = 1; // Old windows 8.0 Process_Per_Monitor_DPI_Aware = 2; // Windows 8.1 function SystemCanSupportPerMonitorDpi(AutoEnable: Boolean): Boolean; // New Windows 8.1 dpi awareness available? function SystemCanSupportOldDpiAwareness(AutoEnable: Boolean): Boolean; // Windows Vista/ Windows 7 Global System DPI functional level. var _RequestedLevelOfAwareness:LongInt; _ProcessDpiAwarenessValue:LongInt; implementation uses System.SysUtils, WinApi.Windows; type TGetProcessDPIAwarenessProc = function(const hprocess: THandle; var ProcessDpiAwareness: LongInt): HRESULT; stdcall; TSetProcessDPIAwarenessProc = function(const ProcessDpiAwareness: LongInt): HRESULT; stdcall; const E_ACCESSDENIED = $80070005; function _GetProcessDpiAwareness(AutoEnable: Boolean): LongInt; var hprocess: THandle; HRESULT: DWORD; BAwareness: Integer; GetProcessDPIAwareness: TGetProcessDPIAwarenessProc; LibHandle: THandle; PID: DWORD; function ManifestOverride: Boolean; var HRESULT: DWORD; SetProcessDPIAwareness: TSetProcessDPIAwarenessProc; begin Result := False; SetProcessDPIAwareness := TSetProcessDPIAwarenessProc(GetProcAddress(LibHandle, 'SetProcessDpiAwareness')); if Assigned(SetProcessDPIAwareness) and (_RequestedLevelOfAwareness>=0) then begin HRESULT := SetProcessDPIAwareness(_RequestedLevelOfAwareness ); // If we do this we don't need the manifest change. Result := (HRESULT = 0) or (HRESULT = E_ACCESSDENIED) // if Result = 80070005 then ACESS IS DENIED, means already set. end end; begin Result := _ProcessDpiAwarenessValue; if (Result = -1) then begin BAwareness := 3; LibHandle := LoadLibrary('shcore.dll'); if LibHandle <> 0 then begin if (not AutoEnable) or ManifestOverride then begin // This supercedes the Vista era IsProcessDPIAware api, and is available in Windows 8.0 and 8.1,although only // windows 8.1 and later will return a per-monitor-dpi-aware result. GetProcessDPIAwareness := TGetProcessDPIAwarenessProc(GetProcAddress(LibHandle, 'GetProcessDpiAwareness')); if Assigned(GetProcessDPIAwareness) then begin PID := WinApi.Windows.GetCurrentProcessId; hprocess := OpenProcess(PROCESS_ALL_ACCESS, False, PID); if hprocess > 0 then begin HRESULT := GetProcessDPIAwareness(hprocess, BAwareness); if HRESULT = 0 then Result := BAwareness; end; end; end; end; end; end; // If this returns true, this is a windows 8.1 system that has Per Monitor DPI Awareness enabled // at a system level. function SystemCanSupportPerMonitorDpi(AutoEnable: Boolean): Boolean; begin if AutoEnable then begin _RequestedLevelOfAwareness := Process_Per_Monitor_DPI_Aware; _ProcessDpiAwarenessValue := -1; end; Result := _GetProcessDpiAwareness(AutoEnable) = Process_Per_Monitor_DPI_Aware; end; // If this returns true, This is either a Windows 7 machine, or a Windows 8 machine, or a // Windows 8.1 machine where the Per-DPI Monitor Awareness feature has been disabled. function SystemCanSupportOldDpiAwareness(AutoEnable: Boolean): Boolean; begin if AutoEnable then begin _RequestedLevelOfAwareness := Process_Per_Monitor_DPI_Aware; _ProcessDpiAwarenessValue := -1; end; Result := _GetProcessDpiAwareness(AutoEnable) = Process_System_DPI_Aware; end; initialization _ProcessDpiAwarenessValue := -1;// not yet determined. _RequestedLevelOfAwareness := -1; end. 

修改指向Project |中的清单 选项| 使用.dpr文件中的$ R指令应用或包含额外的清单。

另外你的asmv3:应用程序部分看起来很好,除了我认为你需要拼写“真”与小写字母“t”在“真”。