Disclosure Statement: This site contains affiliate links, which means that I may receive a commission if you make a purchase using these links. As an eBay Partner, I earn from qualifying purchases.

Combining Windows manifest gdiScaling and CEF4Delphi

Post Reply
hvassbotn
Posts: 29
Joined: Tue Apr 25, 2017 3:02 pm
Location: Oslo, Norway
Contact:

Combining Windows manifest gdiScaling and CEF4Delphi

Post by hvassbotn »

Hi,

We have an application that still isn't High-DPI compliant - we're still on XE6 for production.

Planning to port the code to Delphi Tokyo 10.2 and have a project to try an be High DPI-compliant.

Right now the production version declares dpiAware=false in the application's manifest file. This ensures that Windows does a kind of bitmap-rescaling of windows and contents when rendering on monitors with > 100% font scaling enabled.

This works - including CEF windows, but the result is fuzzy.

Now with recent versions of Windows 10 they support something they call GDI scaling. The application can keep old non-DPI aware GDI code, but the Windows OS automatically scales GDI calls for you, resulting in much more accurate scaling of windows and contents, giving a much crisper result.

Now, this works fine for all windows in the application - except for the CEF windows.

When Windows does the scaling, it increases the effective size of the windows, but it leaves non-GDI windows (like DirectDraw/DirectWrite that I think CEF/Chromium uses) alone. The end result is that the Delphi parent window becomes correct size (big), while the CEF child window becomes too small, and is positioned wrong. Example:

https://www.dropbox.com/s/jt097aw5x27nc ... n.png?dl=0

We have tried different workarounds, including:

1. Calling new API SetThreadDpiAwarenessContext to override DPI-awareness for the both the Delphi parent window and the TCEFWindowParent instance - no effect. Sample code:

Code: Select all

  TCEFWindowParent = class(TWinControl)
    protected
      procedure CreateHandle; override;
...

procedure TCEFWindowParent.CreateHandle;
var
  PrevDpiContext: DPI_AWARENESS_CONTEXT;
begin
  PrevDpiContext := 0;
  if (WindowHandle = 0) and Assigned(SetThreadDpiAwarenessContext) then
    PrevDpiContext := SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
  inherited;
  if PrevDpiContext <> 0 then
    SetThreadDpiAwarenessContext(PrevDpiContext);
end;
2. Experimenting with changing the size of the ChildWindowHandle window, by modifying the existing code in:

Code: Select all

procedure TCEFWindowParent.UpdateSize;
var
  TempRect : TRect;
  hdwp: THandle;
  TempHandle : THandle;
begin
  TempHandle := ChildWindowHandle;
  if (TempHandle = 0) then Exit;

  TempRect := GetClientRect;
//TempRect.Width := TempRect.Width * 2; // - no effect
//TempRect.Height := TempRect.Height * 2; // - no effect
  hdwp     := BeginDeferWindowPos(1);

  try
    hdwp := DeferWindowPos(hdwp, TempHandle, 0,
                           TempRect.left, TempRect.top, TempRect.right - TempRect.left, TempRect.bottom - TempRect.top,
                           SWP_NOZORDER);
  finally
    EndDeferWindowPos(hdwp);
  end;
end;
I know there is a EnableHighDPISupport property in TCefApplication, but the only thing this does it to programatically set the equivalent dpiAware=True in the application manifest file.

Example manifest file snippet:

Code: Select all

  <asmv3:application>
    <asmv3:windowsSettings >
      <dpiAware     xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">false</dpiAwareness>
      <gdiScaling   xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>
    </asmv3:windowsSettings>
  </asmv3:application>
Dynamic API import-code for SetThreadDpiAwarenessContext:

Code: Select all

type
  DPI_AWARENESS_CONTEXT = type THandle;
const
  DPI_AWARENESS_CONTEXT_UNAWARE              = DPI_AWARENESS_CONTEXT(-1);
  DPI_AWARENESS_CONTEXT_SYSTEM_AWARE         = DPI_AWARENESS_CONTEXT(-2);
  DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE    = DPI_AWARENESS_CONTEXT(-3);
  DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = DPI_AWARENESS_CONTEXT(-4);

var
  SetThreadDpiAwarenessContext: function (dpiContext: DPI_AWARENESS_CONTEXT): DPI_AWARENESS_CONTEXT; stdcall;

procedure LoadDpiAPI;
var
  User32Handle: HMODULE;
begin
  // my bolted-on dpi fix for Windows 10 anniversary+
  User32Handle := GetModuleHandle('User32.dll');
  if User32Handle > HINSTANCE_ERROR then
    SetThreadDpiAwarenessContext := GetProcAddress(User32Handle, 'SetThreadDpiAwarenessContext');
end;
Background info:
https://docs.microsoft.com/en-us/window ... plications
https://blogs.windows.com/buildingapps/ ... lDZvwy0.97

Does anyone have any experience with this or suggestions on how it is possible to get GDI Scaling to work, while getting the right position and size of the CEF windows?

/Hallvard
User avatar
salvadordf
Posts: 4057
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Combining Windows manifest gdiScaling and CEF4Delphi

Post by salvadordf »

Hi,

I've only used the "regular" high dpi support features in Delphi and I don't know anything about the new GDI Scaling feature in Windows 10. :oops:

I can only suggest that you try the OSR mode because you have absolute control about the scaling of all the browser features.

If you prefer to use TChromium in normal mode, then consider using the regular high dpi features included in Delphi. The latest versions include some bug fixes that will make your portability slightly easier.
hvassbotn
Posts: 29
Joined: Tue Apr 25, 2017 3:02 pm
Location: Oslo, Norway
Contact:

Re: Combining Windows manifest gdiScaling and CEF4Delphi

Post by hvassbotn »

Thanks for your reply!

Yes, the full High DPI support in the latest Delphi version is the long term solution.

We're just exploring if the GDI scaling would be a good short term solution.

Might look into the OSR mode - is there a significant overhead in doing that? I guess there is a demo.

/H
User avatar
salvadordf
Posts: 4057
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Combining Windows manifest gdiScaling and CEF4Delphi

Post by salvadordf »

hvassbotn wrote: Mon Jul 02, 2018 4:18 pm Thanks for your reply!
Yes, the full High DPI support in the latest Delphi version is the long term solution.
We're just exploring if the GDI scaling would be a good short term solution.
Might look into the OSR mode - is there a significant overhead in doing that? I guess there is a demo.
/H
These are the OSR mode demos for Delphi :
  • SimpleOSRBrowser : The simplest OSR demo.
  • OSRExternalPumpBrowser : Same as SimpleOSRBrowser but using an external message pump in single thread mode.
In your case, don't set GlobalCEFApp.EnableHighDPISupport to true like the demos do.

These demos use a custom component to show the contents with a background bitmap but other CEF bindings projects use OpenGL to improve performance. Take a look at the OSR demo in fpCEF3 if you want to use OpenGL :
https://github.com/dliw/fpCEF3/tree/mas ... es/OSRDemo
hvassbotn
Posts: 29
Joined: Tue Apr 25, 2017 3:02 pm
Location: Oslo, Norway
Contact:

Re: Combining Windows manifest gdiScaling and CEF4Delphi

Post by hvassbotn »

Thanks!

We have started porting to Delphi Tokyo 10 now, and initial High DPI results look promising. We get crips fonts both in our application windows and the CEF browser window. Lots of minor adjustments to do, new bitmap sizes to be fixed etc, but it looks manageable.
Post Reply