How can I make Windows 8.1 aware that my Delphi application wants to support Per Monitor DPI?
It is documented on the MSDN topic Writing DPI-Aware Desktop and Win32 Applications:
Mark the application as per monitor-DPI aware by modifying the application manifest or by calling the SetProcessDpiAwarenessAPI. We recommend that you use the application manifest because that sets the DPI awareness level when the application is launched. Use the API only in the following cases:
- Your code is in a dll that runs via rundll32.exe. 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.
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.
DPI awareness manifest value, Description
False Sets the application to not DPI-aware.
True Sets the application to system DPI–aware.
Per-monitor On Windows 8.1, sets the application to per monitor-DPI aware. On Windows Vista through Windows 8, sets the application to not DPI–aware.
True/PM On Windows 8.1, sets the application to per monitor-DPI aware. On Windows Vista through Windows 8, sets the application to system-DPI aware.
You just use the standard DPI aware manifest but specify True/PM or Per-monitor instead of True.
The same topic gives the DPI aware manifest as follows:
<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.
This manifest works but with some warnings:
Note the various "metadata" differences about asmv1, and asm.v2 and asmv3. Probably not important.
As David pointed out probably its the
True/PM
value, instead ofTrue
that makes all the difference. Microsoft apparently DID document it. (grins)Some SMI/2011 variants WILL launch WITHOUT dread SxS launch errors, but I haven't found an SMI/2011 variant that WORKS.
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. Microsoft has changed the mouse focus behaviour in Windows 8.1 level applications. 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!
Here is the XML. I am having some trouble with StackOverflow mangling this XML, so if it looks bad, you're seeing StackOverflow bugs.
<?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;interfaceconst Process_DPI_Unaware = 0; Process_System_DPI_Aware = 1; // Old windows 8.0 Process_Per_Monitor_DPI_Aware = 2; // Windows 8.1function 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;implementationuses 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 | Options | Application or include an additional manifest using the $R directive in the .dpr file.
Also your asmv3:application section looks fine except that I think you need to spell "True" with a lower case t as in "true".