Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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

This is the .manifest content I have determined using MSDN resources. 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).

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

If I call GetProcessDPIAwareness(hProc,Awareness), I get back `Awareness = Process_DPI_Unaware'.

My other worry is that in the MSDN sources they specify adding an ADDITIONAL manifest. Whereas, I am using Delphi XE6's IDE's ability to link ONE and ONLY ONE manifest into my application. 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.

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.

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.

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

CODE RED ALERT! DO NOT USE the following OS COMPATIBILITY flag in any Delphi VCL application: <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.

like image 916
Warren P Avatar asked Nov 10 '14 20:11

Warren P


1 Answers

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.

like image 138
David Heffernan Avatar answered Dec 13 '22 23:12

David Heffernan