CefSharp
CefSharp copied to clipboard
Feature Request - Add BrowserSubProcess DpiAwareness Option
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 aboutrender
processes) must have a matching awareness. -
app.manifest
setting takes priority overCef.EnableHighDPISupport();
-
Chrome
defaults toPerMonitorV2
, we currently usePerMonitor
https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
More details to follow.
Should also add note that DPI Awareness
for a process can be viewed in Task Manager
on Win10
It might make sense to default the browsersubprocess DPI awareness to that of the main application, see how feasible that is.
- 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
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
.
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
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:
- Through an application manifest setting (This is generally the preferred option)
- 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
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.
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.
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.
Good to know, thanks for the heads up!
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
Setting PerMonitorV2
using PInvoke.User32 should look something like:
User32.SetThreadDpiAwarenessContext(User32.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);