5
votes

Some of my customers want to be able to scale my application manually (when Windows dpi is set to 96), so I had to implement scaling. Unfortunately these customers cannot go with setting Windows DPI to an other value and let WIndows scale my app because some very important applications they use do not behave well at all on resolutions <> 96 DPI.

I managed to make my Delphi 10.1 application scale quite well even on 200% but the higher the factor becomes the more some proportions become "not so nice looking". Many 3rd party components need special treatment for scaling and even then do not scale 100% accurate. Although applications scaled by windows look a little blurry on high resolutions, all proportions are 100% accurate and imho the app looks much more professional.

So I asked myself whether it is possible to create a setting that allows to tell Windows to do the scaling as a default and only scale on my own if the customer wants a scaling that is different from the current Windows scaling. This setting is hosted in the Windows manifest of the executable that is read on start of the application. Is there a way to change it at runtime (early startup of the application)? Creating two executables with different manifests is surely no good solution.

Thanks for any help

1
Is the setting you mention <dpiAware>? If so, see remarks in SetProcessDpiAwareness.Sertac Akyuz
Thanks, i will try that!MichaSchumann
Why was my question voted down?MichaSchumann
Probably because of the same reason with the existing close vote: "unclear what you're asking". I was not very sure either about suggesting the link I gave, but I took my chances.Sertac Akyuz
@MichaSchumann Don't bother with the down votes. People many times feel they need to do something but don't know what and they down vote, vote to close on non-reasonable reasons. Anyway, thanks for sharing the answer. Very helpfulJohn Kouraklis

1 Answers

9
votes

Thanks to Sertac Akyuz I found a solution to my problem. In the Initialization part of the unit containing the scaling code I can switch between DPI-Awareness and Non-DPI-Awareness. It is important not to have this setting in the application manifest, which can be achieved by supplying a custom manifest like this (use control decoration and run with rights of current user):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
 <dependency>
   <dependentAssembly>
     <assemblyIdentity
       type="win32"
       name="Microsoft.Windows.Common-Controls"
       version="6.0.0.0"
       publicKeyToken="6595b64144ccf1df"
       language="*"
       processorArchitecture="*"/>
   </dependentAssembly>
 </dependency>
 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
   <security>
     <requestedPrivileges>
       <requestedExecutionLevel
         level="asInvoker"
         uiAccess="false"/>
       </requestedPrivileges>
   </security>
 </trustInfo>
</assembly>

This is the actual code switching depending on a registry key:

// Set DPI Awareness depending on a registry setting
with TRegIniFile.create('SOFTWARE\' + SRegName) do
begin
  setting := readInteger('SETTINGS', 'scale', 0);
  Free;
end;
handle := LoadLibrary('shcore.dll');
if handle <> 0 then
begin
  setProcessDPIAwareness := GetProcAddress(handle, 'SetProcessDpiAwareness');
  if Assigned(setProcessDPIAwareness) then
  begin
    if setting < 2 then
      // setting <2 means no scaling vs Windows
      setProcessDPIAwareness(0)
    else
      // setting 2: 120%, 3: 140% vs. Windows
      // The actual used scaling factor multiplies by windows DPI/96
      setProcessDPIAwareness(1);
  end;
  FreeLibrary(handle);
  // Get windows scaling as Screen.PixelsPerInch was read before swiching DPI awareness
  // Our scaling routines now work with WinDPI instead of Screen.PixelsPerInch
  WinDPI:= Screen.MonitorFromWindow(application.handle).PixelsPerInch;
end;

The last line of this snippet retrieves the current DPI for the current monitor as screen.pixelsperinch seems to be initialized before and does always return 96 as for a non dpi aware application. I use the value of winDPI in all subsequent scaling calculations and it works perfect.