CefSharp icon indicating copy to clipboard operation
CefSharp copied to clipboard

Feature Request - Add BrowserSubProcess DpiAwareness Option

Open amaitland opened this issue 5 years ago • 10 comments

Currently the default DPI Awareness is true/PM, this won't suite everyone.

  • [ ] Add CefSharpSettings option to allow for a new command line arg to be appended
  • [ ] Change default to PerMonitorV2???
  • [ ] Rewrite HighDpi Section (General Usage)

Some additional clarification should be added to High DPI section of General Usage.

  • Both browser process and GPU process (Chrome doesn't appear to worry about render processes) must have a matching awareness.
  • app.manifest setting takes priority over Cef.EnableHighDPISupport();
  • Chrome defaults to PerMonitorV2, we currently use PerMonitor

https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows

More details to follow.

amaitland avatar Oct 07 '19 11:10 amaitland

Should also add note that DPI Awareness for a process can be viewed in Task Manager on Win10

amaitland avatar Oct 07 '19 11:10 amaitland

It might make sense to default the browsersubprocess DPI awareness to that of the main application, see how feasible that is.

amaitland avatar Oct 29 '19 03:10 amaitland

  • https://github.com/microsoft/Windows-classic-samples/blob/master/Samples/DPIAwarenessPerWindow/client/DpiAwarenessContext.cpp
  • https://stackoverflow.com/questions/49033938/c-sharp-pinvoke-for-getwindowdpiawarenesscontext/49115675#49115675
  • https://github.com/dotnet/wpf/blob/8c7ab1592b6c70e732106aeabf58f215d4b71c22/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/DpiUtil/DpiUtil%2BDpiAwarenessContextHelper.cs
  • https://github.com/emoacht/WpfBuiltinDpiTest/blob/master/WpfApiTest/DpiHelper.cs

amaitland avatar Nov 27 '19 07:11 amaitland

Bumping this to the 81 milestone as it would be a stretch to adequately test.

As a short-term workaround use the --disable-gpu-compositing command line arg and the DPI Settings of your main application process will be used instead of the DPI Awareness specified by the GPU Process.

amaitland avatar Jan 20 '20 23:01 amaitland

Chromium only enables DPI Awareness for the main process and GPU process https://cs.chromium.org/chromium/src/chrome/app/chrome_exe_main_win.cc?type=cs&q=base::win::EnableHighDPISupport&sq=package:chromium&g=0&l=225

https://cs.chromium.org/chromium/src/base/win/win_util.cc?type=cs&q=base::win::EnableHighDPISupport&sq=package:chromium&g=0&l=703

amaitland avatar Jan 24 '20 09:01 amaitland

Documentation rewrite add reference to https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-with-the-application-manifest

Doc Rewrite (WIP)

High DPI Displays/Support

Desktop applications using WinForms/WPF need to be made DPI Aware to run correctly on a High DPI Display (A display with a DPI Scale set greater than 100%).

Note If you mouse cursor is incorrectly positioned in the browser or the browser displays black boxes/border with rendering/resizing then your app needs to be made DPI Aware. Other parts of your application may also appear blurry or incorrectly-sized.

There are two main methods to specify the default DPI awareness of a process:

  1. Through an application manifest setting (This is generally the preferred option)
  2. Programmatically through an API call

WinForms High DPI

App.manifest

Set the default awareness using the application manifest. The following example is PerMonitor DPI Aware on Win 10 1703 and above and PerMonitor DPI aware on older version. Make sure you read https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows#dpi-awareness-mode which discusses the different DPI Awareness options

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
        <dpiAware>true/PM</dpiAware>
    </windowsSettings>
</application>
</assembly>

Programatically

Setting High DPI in code you can use the Cef.EnableHighDPISupport(); helper method. This calls the Chromium base::win::EnableHighDPISupport(); function. You then have exactly the same settings as Chromium uses.

Cef.EnableHighDPISupport(); must be called very early on in your application execution, preferably in your application entry point (Program.Main).

The CefSharp.MinimalExample.WinForms project contains a working example.

WPF High DPI

App.manifest

Add the relevant app.manifest entries. It should roughly look like the following (You need to add some xmlns entries to the top level root element, see https://github.com/cefsharp/CefSharp/blob/cefsharp/79/CefSharp.Wpf.Example/app.manifest for a working example)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
        <dpiAware>true/PM</dpiAware>
    </windowsSettings>
</application>
</assembly>

Programatically

WPF applications by default have an automatically generated Program.Main entry point which makes it harder to programatically set the DPI. See https://stackoverflow.com/a/26890426/4583726 for how to create a Program.Main then you can call Cef.EnableHighDPISupport();. This must be called very early on in your application execution, preferably the first call in your custom Program.Main.

OffScreen High DPI

Add the relevant app.manifest entries or call Cef.EnableHighDPISupport() (see above for an example). Read the WinForms section above, choose which option suites your needs.

High DPI Additional Info

Chromium by default performs all rendering in separate sub-process. Specifically the GPU Compositor needs to have a DPI Awareness that matches your main application. Currently the default used by the CefSharp.BrowserSubprocess.exe is Per Monitor DPI Aware. As a workaround use the disable-gpu-compositing command line arg and the DPI Awareness of your main application process will be used instead of the DPI Awareness specified by the GPU Process (which is used for GPU Compositing). Disabling GPU Compositing may have an impact on performance, when https://github.com/cefsharp/CefSharp/issues/2927 is complete it will be possible to programatically set the DPI Awareness used by the CefSharp.BrowserSubprocess.exe

var settings = new CefSettings();
settings.CefCommandLineArgs.Add("disable-gpu-compositing");
Cef.Initialize(settings);

Alternatively you can try the force-device-scale-factor command line flag.

var settings = new CefSettings();
settings.CefCommandLineArgs.Add("force-device-scale-factor", "1");
Cef.Initialize(settings);
  • https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx It's a very long MSDN article, but it's necessary reading if your app needs to be run on high DPI displays.
  • https://blogs.windows.com/windowsdeveloper/2017/05/19/improving-high-dpi-experience-gdi-based-desktop-apps/
  • https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-with-the-application-manifest

amaitland avatar Mar 28 '20 18:03 amaitland

The General Usage Guide has been updated see https://github.com/cefsharp/CefSharp/wiki/General-Usage/_compare/69741e80118da403d7c086695965427869164f1e...592d4fae743f567a30126e6f03c5f92faf9c6f59

Once this feature has been complete additional details will need to be added.

amaitland avatar Mar 30 '20 08:03 amaitland

FYI, I fixed this issue for my projects by modifying the manifest in CefSharp.BrowserSubprocess.EXE to match my main applications DPI awareness. I gave the EXE a different name and set the BrowserSubprocessPath property accordingly.

cwollenhaupt avatar Aug 31 '21 16:08 cwollenhaupt

Be careful copying the Browsersubprocess and renaming it as I've seen previous cases where antivirus software have flagged the exe as suspicious. Much safer to create your own exe or self host the browser sub process using https://github.com/cefsharp/CefSharp/blob/master/CefSharp.Core/BrowserSubprocess/SelfHost.cs#L22

Same code can be used to create own exe.

amaitland avatar Aug 31 '21 21:08 amaitland

Good to know, thanks for the heads up!

cwollenhaupt avatar Sep 01 '21 07:09 cwollenhaupt

Changes in Chromium (#4410) mean that the Browser and GPU processes will now be PerMonitorV2 by default,

You can override the Chromium default via setting the awareness in your app.manifest or programmatically.

The BrowserSubProcess will use the Chromium defaults starting from M111 (commit https://github.com/cefsharp/CefSharp/commit/b17cd2a1b23df786c1051c7c66947f6d52239510)

The interesting side-effect of this change is that it would appear that when set programmatically then the GPU process will mirror the awareness of the browser process (by default this is your application).

So starting in M111 you should be able to PInvoke.SHCore the awareness in your application and Chromium should respect your awareness. (Must be set before Cef.Initialze is called and before your process creates an windows/forms).

SHCore.SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.PROCESS_DPI_UNAWARE);

Only seems to work when set programmatically. If for some reason this doesn't work then Self Hosting the BrowserSubProcess will allow complete control.

There are new more detailed instructions at https://github.com/cefsharp/CefSharp/wiki/SelfHost-BrowserSubProcess

amaitland avatar Mar 07 '23 03:03 amaitland

Setting PerMonitorV2 using PInvoke.User32 should look something like:

User32.SetThreadDpiAwarenessContext(User32.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);

amaitland avatar Mar 15 '23 01:03 amaitland