我试图让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}"/>
。微软以可怕的方式破坏了鼠标捕捉行为,破碎的窗口绘画,我甚至无法猜测。打开这个标志会在我的应用程序中引起非常微妙的错误,包括绘画问题,无法点击控件(鼠标按下消息不能到达控件,由于鼠标捕获丢失)以及许多其他问题。
答案 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”。