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

时间:2014-11-10 20:10:03

标签: windows delphi windows-8.1 dpi delphi-xe6

我试图让Windows 8.1识别出我一直在尝试构建的Delphi XE6应用程序(一个演示程序),让它认识到我的应用程序是Per-Monitor DPI识别,纯粹是通过Manifest技术。 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,但我已经知道这种方法存在潜在的弊端,所以我更喜欢工作清单 - 只是方法。

如果我致电GetProcessDPIAwareness(hProc,Awareness),我会回复`Awareness = Process_DPI_Unaware&#39;。

我的另一个担心是,在MSDN源中,他们指定添加ADDITIONAL清单。然而,我正在使用Delphi XE6的IDE将ONE和ONLY ONE清单链接到我的应用程序中的能力。我从来没有注意到添加任何额外的清单而只有一个是一个问题,除了可能Visual Studio 2010中的.manifest管理系统很蹩脚,这就是为什么提示存在,因此与其他IDE无关/语言。

在Visual Studio 2013中,项目选项中有一个复选框,但我没有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>

上面的迷你清单改变了应用程序的行为,但并不完全按我想要的方式改变。使用上面的小清单,可以检测到OLD Windows 8.0 / Windows 7 / Vista DPI感知级别。

更新2:

感谢雷米的想法。有趣的是,以下似乎足以允许应用程序启动。但是,将SMI / 2005语法与上述语法混合会导致并行启动错误。你可以看到微软已经在他们的清单上做了很多努力。请注意,以下内容实际上并没有解决我的问题,但它提供了另一种潜在的基本形式&#34;可能与真正的解决方案关系密切:

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

CODE RED ALERT!请勿在任何Delphi VCL应用程序中使用以下OS兼容性标志: <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>。微软以可怕的方式破坏了鼠标捕捉行为,破碎的窗口绘画,我甚至无法猜测。打开这个标志会在我的应用程序中引起非常微妙的错误,包括绘画问题,无法点击控件(鼠标按下消息不能到达控件,由于鼠标捕获丢失)以及许多其他问题。

3 个答案:

答案 0 :(得分:10)

MSDN主题Writing DPI-Aware Desktop and Win32 Applications中记录了它:

  

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

     
      
  • 您的代码位于通过rundll32.exe运行的dll中。这是一种不支持应用程序清单的启动机制。
  •   
  • 您需要根据操作系统版本或其他注意事项做出复杂的运行时决策。例如,如果您需要应用程序   system-DPI识别Windows 7并在Windows 8.1上动态识别,   使用True / PM清单设置。
  •   
     

如果使用SetProcessDpiAwareness方法设置DPI感知   在任何Win32API调用之前,您必须调用SetProcessDpiAwareness   这迫使系统开始虚拟化。

     

DPI意识清单值,说明

     

错误将应用程序设置为不支持DPI。

     

True 将应用程序设置为系统DPI感知。

     

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

     

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

您只需使用标准DPI感知清单,但指定 True / PM Per-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 替换为您选择的值。

答案 1 :(得分:6)

此清单有效,但有一些警告:

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

  • 正如大卫所指出的可能是True/PM值,而不是True,它会产生重大影响。微软显然是DID记录它。 (笑容)

  • 有些SMI / 2011变种会在没有发现SxS启动错误的情况下启动,但我还没有找到可以使用的SMI / 2011版本。

  • 使用同时启用Per Monitor API并将操作系统兼容性定义为Windows 8.1的应用程序后,我在应用程序中发现了一些可怕的回归。 Microsoft已更改Windows 8.1级别应用程序中的鼠标焦点行为。我不推荐这种方法。选择通过CODE而不是通过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>

你也可以使用代码:

代码示例:

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.

答案 2 :(得分:2)

修改Project |中指向的清单选项|应用程序或在.dpr文件中使用$ R指令包含其他清单。

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