Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request - Add BrowserSubProcess DpiAwareness Option #2927

Closed
1 of 2 tasks
amaitland opened this issue Oct 7, 2019 · 15 comments
Closed
1 of 2 tasks

Feature Request - Add BrowserSubProcess DpiAwareness Option #2927

amaitland opened this issue Oct 7, 2019 · 15 comments

Comments

@amaitland
Copy link
Member

amaitland commented Oct 7, 2019

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???

  • 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

UPDATE
Chromium now sets the DPI Awareness to PermonitorV2 by default. You can override this via app.manifest or via programmatic call. #4410

@amaitland amaitland added this to the 77.0.0 milestone Oct 7, 2019
@amaitland
Copy link
Member Author

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

@amaitland
Copy link
Member Author

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

@amaitland amaitland modified the milestones: 79.0.0, 81.0.0 Jan 20, 2020
@amaitland
Copy link
Member Author

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
Copy link
Member Author

amaitland commented Mar 28, 2020

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 #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);

@amaitland
Copy link
Member Author

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.

@cwollenhaupt
Copy link

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.

@amaitland
Copy link
Member Author

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.

@cwollenhaupt
Copy link

Good to know, thanks for the heads up!

@amaitland amaitland added this to the 111.1.x milestone Mar 7, 2023
@amaitland
Copy link
Member Author

amaitland commented Mar 7, 2023

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 b17cd2a)

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
Copy link
Member Author

Setting PerMonitorV2 using PInvoke.User32 should look something like:

User32.SetThreadDpiAwarenessContext(User32.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);

@davidei1955
Copy link

davidei1955 commented Jun 25, 2024

FWIW, I am seeing this issue in .Net 8.0 Core Winforms on Windows 10 when the display is set to 125% (but not when set to 100%). I don't see it at all on Windows 11. The fix for me was to delete app.manifest, then recreate it in Visual Studio, and uncomment the

<application xmlns="urn:schemas-microsoft-com:asm.v3">
     <windowsSettings>
       <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
      <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
    </windowsSettings>
 </application>

section in the new app.manifest. The manifest change suggested above in Section Winforms HI DPI does not appear to be supported in Core Winforms.

@amaitland
Copy link
Member Author

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

To revise this statement in the context of WinForms. Setting DPI Awareness via app.manifest is no longer recommended when using .Net 4.8 or greater (this includes newer versions including .Net 8.0).

See https://learn.microsoft.com/en-us/dotnet/desktop/winforms/wfdev-diagnostics/wfac010?view=netdesktop-8.0 for Microsoft guidance regarding the compiler warning that will be raised.

Starting in .Net Core 3.0 you can use the WinForms specific API. Some examples (refer to the Microsoft documentation for more details)

Application.SetHighDpiMode(HighDpiMode.DpiUnaware);
Application.SetHighDpiMode(HighDpiMode.DpiUnawareGdiScaled);
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.SetHighDpiMode(HighDpiMode.PerMonitor);
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);

https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.application.sethighdpimode?view=windowsdesktop-8.0

@davidei1955
Copy link

davidei1955 commented Jun 26, 2024

I was just hunting down down the compiler warning when I saw your post. Adding
Application.SetHighDpiMode(HighDpiMode.SystemAware);
To my main() method also resolved the issue. Thanks for the heads-up!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants