Search code examples
delphidelphi-xedpihighdpi

Make Delphi XE application DPI-unaware


I just created a new application in Delphi and even though other applications written the same way, this new application does not get automatically scaled by Windows. All applications have exactly the same settings (e.g., scaled = false). And Windows usually scales them properly (otherwise they would be too small if scaling is set to 150% or higher).

I found out that using properties menu of the .exe file, tab "Compatibility", "Change HIGH-DPI settings" and set "Scaling by system" on bottom solves the issue and the application gets scaled properly.

But how can I either activate this option by default (Registry?) or how can I do it programmatically?


Solution

  • You can provide your app with an app manifest that specifies <dpiAwareness>unaware</dpiAwareness> (Windows 10 v1607 and later), <gdiScaling>false</gdiScaling> (Windows 10 v1703 and later), and <dpiAware>false</dpiAware> (earlier versions), eg:

    <?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">
      <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
          <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
          <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">unaware</dpiAwareness>
          <gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">false</gdiScaling>
        </windowsSettings>
      </application>
    </assembly>
    

    Alternatively, before creating any GUI windows at runtime, you can call either SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE) (Windows 10 v1607 and later) or SetProcessDpiAwareness(PROCESS_DPI_UNAWARE) (earlier versions), eg:

    program MyProject;
    
    uses
      Vcl.Forms, Winapi.Windows,
      MyMainForm in 'MainForm.pas' {MyMainForm};
    
    {$R *.res}
    
    {$IF NOT DECLARED(DPI_AWARENESS)}
    type
      DPI_AWARENESS = (
        DPI_AWARENESS_INVALID = -1,
        DPI_AWARENESS_UNAWARE = 0,
        DPI_AWARENESS_SYSTEM_AWARE = 1,
        DPI_AWARENESS_PER_MONITOR_AWARE = 2
      );
    {$IFEND}
    
    {$IF NOT DECLARED(DPI_AWARENESS_CONTEXT)}
    type
      DPI_AWARENESS_CONTEXT__ = record
      var
        unused: Integer;
      end;
      DPI_AWARENESS_CONTEXT = ^DPI_AWARENESS_CONTEXT__;
    
    const
      DPI_AWARENESS_CONTEXT_UNAWARE: DPI_AWARENESS_CONTEXT = DPI_AWARENESS_CONTEXT(-1);
    {$IFEND}
    
    type
      LPFN_SetProcessDpiAwarenessContext = function(value: DPI_AWARENESS_CONTEXT): BOOL; stdcall;
      LPFN_SetProcessDpiAwareness = function(value: PROCESS_DPI_AWARENESS): HRESULT; stdcall;
    
    var
      lpSetProcessDpiAwarenessContext: LPFN_SetProcessDpiAwarenessContext;
      lpSetProcessDpiAwareness: LPFN_SetProcessDpiAwareness;
    begin
      @lpSetProcessDpiAwarenessContext := GetProcAddress(LoadLibrary('User32.dll'), 'SetProcessDpiAwarenessContext');
      if Assigned(lpSetProcessDpiAwarenessContext) then begin
        lpSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
      end else begin
        @lpSetProcessDpiAwareness := GetProcAddress(LoadLibrary('Shcore.dll'), 'SetProcessDpiAwareness');
        if Assigned(lpSetProcessDpiAwareness) then
          lpSetProcessDpiAwareness(PROCESS_DPI_UNAWARE);
      end;
    
      Application.Initialize;
      Application.MainFormOnTaskbar := True;
      Application.CreateForm(TMyMainForm, MyMainForm);
      Application.Run;
    end.
    

    See Setting the default DPI awareness for a process on MSDN for more details.