简体   繁体   English

如何让Windows 8.1意识到我的Delphi应用程序想要支持Per Monitor DPI?

[英]How can I make Windows 8.1 aware that my Delphi application wants to support Per Monitor DPI?

I have tried to make Windows 8.1 recognize a Delphi XE6 application (a demo program) that I have been trying to build, and have it recognize my application is Per-Monitor DPI aware, purely by the Manifest technique. 我试图让Windows 8.1识别我一直在尝试构建的Delphi XE6应用程序(一个演示程序),让它认识到我的应用程序是Per-Monitor DPI识别的,纯粹是通过Manifest技术。 Delphi XE6 (and all other similarly up to date versions of Delphi) make adding a manifest easy to do, inside Project Options, and I have done so. Delphi XE6(以及所有其他类似的最新版本的Delphi)在Project Options中添加了一个易于操作的清单,我已经这样做了。

This is the .manifest content I have determined using MSDN resources. 这是我使用MSDN资源确定的.manifest内容。 I suspect it could be slightly incorrect. 我怀疑它可能有些不正确。

If you want to try this manifest, make an empty VCL application, use this content as your manifest, and add the code (code is currently attached to my answer to this question). 如果您想尝试此清单,请创建一个空的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>

Has anyone gotten this to work? 有没有人得到这个工作? What I find is that the above is not recognized. 我发现上述内容未得到承认。 If I call SetProcessDPIAwareness(Process_Per_Monitor_DPI_Aware) first, then call GetProcessDPIAwareness(hProc,Awareness) , I get back the necessary Awareness = Process_Per_Monitor_DPI_Aware , but I have read that there are potential drawbacks to that approach, and so I would prefer a working Manifest-only approach. 如果我先调用SetProcessDPIAwareness(Process_Per_Monitor_DPI_Aware) ,然后调用GetProcessDPIAwareness(hProc,Awareness) ,我会回到必要的Awareness = Process_Per_Monitor_DPI_Aware ,但我已经读过这种方法存在潜在的缺点,所以我更喜欢只有一个有效的Manifest做法。

If I call GetProcessDPIAwareness(hProc,Awareness) , I get back `Awareness = Process_DPI_Unaware'. 如果我调用GetProcessDPIAwareness(hProc,Awareness) ,我会回到'Awareness = Process_DPI_Unaware'。

My other worry is that in the MSDN sources they specify adding an ADDITIONAL manifest. 我担心的另一个问题是,在MSDN源代码中,他们指定添加一个ADDITIONAL清单。 Whereas, I am using Delphi XE6's IDE's ability to link ONE and ONLY ONE manifest into my application. 然而,我正在使用Delphi XE6的IDE将ONE和ONLY ONE清单链接到我的应用程序中。 I have never noticed that adding any additional manifest versus having only one was ever a problem, other than that perhaps the .manifest management system in Visual Studio 2010 was lame, and that is why the tip existed, and so has no relevance to other IDEs/Languages. 我从来没有注意到添加任何额外的清单而只有一个是一个问题,除了可能Visual Studio 2010中的.manifest管理系统很蹩脚,这就是为什么提示存在,因此与其他IDE无关/语言。

In Visual Studio 2013, there is a checkbox right inside the project options, but I don't have Visual Studio 2013 so I can't examine a working .manifest. 在Visual Studio 2013中,项目选项中有一个复选框,但我没有Visual Studio 2013,所以我无法检查工作.manifest。

update: 更新:

Here's another shot at a 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>

The above mini manifest changes the behavior of the application, but not exactly the way I wanted it to. 上面的迷你清单改变了应用程序的行为,但不完全是我想要的方式。 With the above tiny manifest, the OLD Windows 8.0/Windows 7/Vista DPI awareness level is detected. 使用上面的小清单,可以检测到OLD Windows 8.0 / Windows 7 / Vista DPI感知级别。

update 2: 更新2:

Thanks Remy for the ideas. 谢谢雷米的想法。 Interestingly the following appears to be valid enough to allow an application launch. 有趣的是,以下似乎足以允许应用程序启动。 However mixing the SMI/2005 syntax with the above caused a side-by-side launch error. 但是,将SMI / 2005语法与上述语法混合会导致并行启动错误。 You can see that Microsoft has been churning on their manifests rather a lot. 你可以看到微软已经在他们的清单上做了很多努力。 Note that the following does not actually solve my problem, but it provides yet another "potential base form" that might be CLOSE to the real solution: 请注意,以下内容实际上并没有解决我的问题,但它提供了另一个可能与实际解决方案关联的“潜在基础形式”:

 <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>

update 3: 更新3:

CODE RED ALERT! 代码红色警告! DO NOT USE the following OS COMPATIBILITY flag in any Delphi VCL application: <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> . 请勿在任何Delphi VCL应用程序中使用以下OS兼容性标志: <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> Microsoft has BROKEN MOUSE CAPTURE BEHAVIOUR, BROKEN WINDOW PAINTING in horrible ways that I have not even been able to guess at. 微软以可怕的方式破坏了鼠标捕捉行为,破碎的窗口绘画,我甚至无法猜测。 Turning this flag on caused very subtle bugs in my application, including painting problems, inability to click on controls (mouse down messages not reaching controls, due to mouse capture being lost), and many other problems. 打开这个标志会在我的应用程序中引起非常微妙的错误,包括绘画问题,无法点击控件(鼠标按下消息没有到达控件,由于鼠标捕获丢失)以及许多其他问题。

It is documented on the MSDN topic Writing DPI-Aware Desktop and Win32 Applications : 它在MSDN主题编写DPI-Aware Desktop和Win32应用程序中有记录

Mark the application as per monitor-DPI aware by modifying the application manifest or by calling the SetProcessDpiAwarenessAPI. 通过修改应用程序清单或调用SetProcessDpiAwarenessAPI,根据monitor-DPI标记应用程序。 We recommend that you use the application manifest because that sets the DPI awareness level when the application is launched. 我们建议您使用应用程序清单,因为它在启动应用程序时设置DPI感知级别。 Use the API only in the following cases: 仅在以下情况下使用API​​:

  • Your code is in a dll that runs via rundll32.exe. 您的代码位于通过rundll32.exe运行的DLL中。 This is a launch mechanism that does not support the application manifest. 这是一种不支持应用程序清单的启动机制。
  • You need to make complex run-time decisions based on OS version or other considerations. 您需要根据操作系统版本或其他注意事项做出复杂的运行时决策。 For example, if you need the application to be system-DPI aware on Windows 7 and dynamically aware on Windows 8.1, use the True/PM manifest setting. 例如,如果您需要在Windows 7上识别系统DPI并在Windows 8.1上动态识别,请使用True / PM清单设置。

If you use the SetProcessDpiAwareness method to set the DPI awareness level, you must call SetProcessDpiAwareness prior to any Win32API call that forces the system to begin virtualization. 如果使用SetProcessDpiAwareness方法设置DPI感知级别,则必须在强制系统开始虚拟化的任何Win32API调用之前调用SetProcessDpiAwareness。

DPI awareness manifest value, Description DPI意识显示值,描述

False Sets the application to not DPI-aware. False将应用程序设置为不支持DPI。

True Sets the application to system DPI–aware. True将应用程序设置为系统DPI感知。

Per-monitor On Windows 8.1, sets the application to per monitor-DPI aware. 每个监视器在Windows 8.1上,将应用程序设置为每个监视器-DPI识别。 On Windows Vista through Windows 8, sets the application to not DPI–aware. 在Windows Vista到Windows 8中,将应用程序设置为不支持DPI。

True/PM On Windows 8.1, sets the application to per monitor-DPI aware. True / PM在Windows 8.1上,将应用程序设置为每个监视器-DPI识别。 On Windows Vista through Windows 8, sets the application to system-DPI aware. 在Windows Vista到Windows 8上,将应用程序设置为可识别系统DPI。

You just use the standard DPI aware manifest but specify True/PM or Per-monitor instead of True . 您只需使用标准的DPI识别清单,但指定True / PMPer-monitor而不是True

The same topic gives the DPI aware manifest as follows: 相同的主题给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>

So, replace True with your chosen value. 因此,将True替换为您选择的值。

This manifest works but with some warnings: 这个清单有效,但有一些警告:

  • Note the various "metadata" differences about asmv1, and asm.v2 and asmv3. 请注意有关asmv1,asm.v2和asmv3的各种“元数据”差异。 Probably not important. 可能不重要。

  • As David pointed out probably its the True/PM value, instead of True that makes all the difference. 正如大卫所指出的那样,可能是它的True/PM值,而不是True ,这会产生重大影响。 Microsoft apparently DID document it. 微软显然是DID记录它。 (grins) (笑容)

  • Some SMI/2011 variants WILL launch WITHOUT dread SxS launch errors, but I haven't found an SMI/2011 variant that WORKS. 一些SMI / 2011变种将在没有可怕的SxS启动错误的情况下启动,但我还没有找到可行的SMI / 2011变体。

  • After using an application that both enabled Per Monitor API, and defined OS compatibility as Windows 8.1, I found some HORRIBLE regressions in my application. 使用同时启用Per Monitor API并将操作系统兼容性定义为Windows 8.1的应用程序后,我在应用程序中发现了一些可怕的回归。 Microsoft has changed the mouse focus behaviour in Windows 8.1 level applications. Microsoft已更改Windows 8.1级别应用程序中的鼠标焦点行为。 I do NOT RECOMMEND THIS APPROACH. 我不推荐这种方法。 Opt in via CODE instead of via MANIFEST, and DO NOT USE the <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> example below! 选择通过CODE代替通过MANIFEST,并且不要使用下面的<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>示例!

Here is the XML. 这是XML。 I am having some trouble with StackOverflow mangling this XML, so if it looks bad, you're seeing StackOverflow bugs. 我在使用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>

And you can has teh codez too: 你也可以有代码:

Code sample: 代码示例:

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.

Either modify the manifest pointed to in Project | 修改Project |中指向的清单 Options | 选项| Application or include an additional manifest using the $R directive in the .dpr file. 应用程序或在.dpr文件中使用$ R指令包含其他清单。

Also your asmv3:application section looks fine except that I think you need to spell "True" with a lower case t as in "true". 另外你的asmv3:应用程序部分看起来很好,除了我认为你需要用小写t拼写“True”,如“true”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM