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