Delphi High DPI switch between own scaling and Windows scaling

前端 未结 1 1116
孤城傲影
孤城傲影 2021-02-06 13:09

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 wit

相关标签:
1条回答
  • 2021-02-06 13:35

    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.

    0 讨论(0)
提交回复
热议问题