Search code examples
delphiwebbrowser-controlc++buildertwebbrowser

Enabling TWebBrowser High DPI support


I am using RAD Studio Rio which has support for High DPI.

But it doesn't seem to work on TWebBrowser, at least not by default like it does on other VCL components.

So when I drag the app from Low-DPI to High-DPI monitor, the user interface and all components resize (including the web browser container), but not the content inside web browser which stays the same.

I am sure it can be enabled, because moving Internet Explorer between monitors does change the content size automatically.

So the question is how to enable it for TWebBrowser too?

I noticed that when dragging IE window the "Zoom" setting changes automatically (from 100% to 200% - 200% is the DPI scaling value of the High DPI monitor) so it may be done through the use of zoom, or somehow else. But there is also a question of scroll-bar sizes too.

I also noted the existance of DOCHOSTUIFLAG_DPI_AWARE flag which might be useful but I don't know how to utilize it yet.

EDIT: I've created a IDocHostUIHandler descendant class which contains GetHostInfo function where I can control the flags, among others add DOCHOSTUIFLAG_DPI_AWARE which automatically zooms TWebBrowser to the system DPI (if system DPI is set to 150%, web browser will automatically also zoom to 150%). There is no need to use FEATURE_BROWSER_EMULATION registry key for this.

More info at https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa753260(v=vs.85)


Solution

  • I have found two ways how to do this:

    Solution 1
    Enable IE's FEATURE_96DPI_PIXEL for your application via Registry key and value:

    uses
      System.SysUtils,
      System.Win.Registry,
      Vcl.Forms,
      Winapi.Windows;
    
    procedure EnableDpiAwareness;
    var
      Reg: TRegistry;
      App: string;
    begin
      Reg := TRegistry.Create;
      try
        App := ExtractFileName(Application.ExeName);
        Reg.RootKey := HKEY_CURRENT_USER;
    
        if Reg.OpenKey('Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_96DPI_PIXEL', True) then
        begin
          Reg.WriteInteger(App, 1);
          Reg.CloseKey;
        end;
      finally;
        Reg.Free;
      end;
    end;
    

    Solution 2
    Implement a descendant class of TWebBrowser that reimplements the IDocHostUIHandler interface (like mentioned in the edit of OP):

    unit DpiAwareWebBrowser;
    
    interface
    
    uses
      Winapi.Mshtmhst,
      SHDocVw;
    
    type
      TDpiAwareWebBrowser = class(TWebBrowser, IDocHostUIHandler)
        strict private
          // IDocHostUIHandler "override"
          function GetHostInfo(var pInfo: TDocHostUIInfo): HRESULT; stdcall;
      end;
    
    implementation
    
    const
      DOCHOSTUIFLAG_DPI_AWARE = $40000000;
    
    function TDpiAwareWebBrowser.GetHostInfo(var pInfo: TDocHostUIInfo): HRESULT;
    begin
      // original code from TWebBrowser.GetHostInfo
      pInfo.cbSize := SizeOf(pInfo);
      pInfo.dwFlags := 0;
      pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_NO3DBORDER;
      pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_THEME;
      pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_DPI_AWARE; // NEW added flag
      Result := S_OK;
    //  ResizeScrollBars; // will be called by subsequent routines anyway.
    end;
    
    end.
    

    Both ways do the same thing.

    DOCHOSTUIFLAG_DPI_AWARE
    Internet Explorer 8. Causes layout engine to calculate document pixels as 96 dpi. Normally, a document pixel is the same size as a screen pixel. This flag is equivalent to setting the FEATURE_96DPI_PIXEL feature control key on a per-host basis.

    Source: Microsoft Docs - DOCHOSTUIFLAG enumeration