War*_* P 22 windows delphi dpi windows-8.1 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>
Run Code Online (Sandbox Code Playgroud)
有没有人得到这个工作?我发现上述内容未得到承认.如果我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中的.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>
Run Code Online (Sandbox Code Playgroud)
上面的迷你清单改变了应用程序的行为,但不完全是我想要的方式.使用上面的小清单,可以检测到OLD Windows 8.0/Windows 7/Vista DPI感知级别.
更新2:
谢谢雷米的想法.有趣的是,以下似乎足以允许应用程序启动.但是,将SMI/2005语法与上述语法混合会导致并行启动错误.你可以看到微软已经在他们的清单上做了很多努力.请注意,以下内容实际上并没有解决我的问题,但它提供了另一个可能与实际解决方案关联的"潜在基础形式":
<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>
Run Code Online (Sandbox Code Playgroud)
更新3:
代码红色警告! 请勿在任何Delphi VCL应用程序中使用以下OS兼容性标志:
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>.微软以可怕的方式破坏了鼠标捕捉行为,破碎的窗口绘画,我甚至无法猜测.打开此标志会在我的应用程序中引起非常微妙的错误,包括绘制问题,无法单击控件(鼠标按下消息不能到达控件,由于鼠标捕获丢失)以及许多其他问题.
Dav*_*nan 10
它在MSDN主题编写DPI-Aware Desktop和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上,将应用程序设置为每个监视器-DPI识别.在Windows Vista到Windows 8上,将应用程序设置为可识别系统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>
Run Code Online (Sandbox Code Playgroud)
因此,将True替换为您选择的值.
War*_* P 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>
Run Code Online (Sandbox Code Playgroud)
你也可以有代码:
代码示例:
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.
Run Code Online (Sandbox Code Playgroud)