preface
Now commonly used schemes
- Duilib+CEF It only supports the choice of Windows. The advantage is that the packaged files are small (using C + +) QQ, wechat and Youdao excellent courses.
- Qt+CEF supports cross platform, but the disadvantage is that the packaged file is large (using C + +).
- WPF/(WPF+CEFSharp) has small packaged files, but its performance is weaker than the first two, but stronger than Electron. It occupies a high memory and only supports Windows.
- Electron packaging file is large, but its performance is weak and memory consumption is high. It supports cross platform.
Several schemes have their own advantages and disadvantages. They can be selected according to the situation of the team. They are relatively good. Other schemes such as fluent and Java are not recommended.
At present, due to the technical stack of C + +, our team mainly uses WPF or Electron for desktop development.
Some interfaces are better developed by web, so here we will integrate CEFSharp to load
be careful
Adding CEF will greatly increase the installation package size.
Why use CEF
- The WebBrowser provided with. NET is the IE most hated by WEB developers, with low performance and poor compatibility
- Webkit: Project no longer supports
- Cef is the key to the Chrome kernel, performance and compatibility. The disadvantage is that there are too many and too large DLL s. A release version should be about 150M, and the X86+X64 will be 300M faster. In addition, EXE loading speed will be slightly slower.
Installation dependency
Through Nuget installation, right-click the project - > manage Nuget package - > search CefSharp in the open interface, install CefSharp.Common and CefSharp.Wpf successively, and cef.redist.x64 and cef.redist.x86 will be installed automatically.

Configure solution platform
Because CefSharp does not support Any CPU, to configure x86 and x64, click menu generation - > configuration manager.
Select the solution platform, click Edit, delete x64 and x86, and then create a new one. It is easier to reconfigure.
Any CPU support
If we want to support Any CPU, we have to implement it ourselves.
using System.Windows; using System; using System.Runtime.CompilerServices; using CefSharp; using System.IO; using System.Reflection; using System.Windows.Threading; using CefSharpWpfDemo.Log; namespace CEFSharpTest { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { public App() { // Add Custom assembly resolver AppDomain.CurrentDomain.AssemblyResolve += Resolver; //Any CefSharp references have to be in another method with NonInlining // attribute so the assembly rolver has time to do it's thing. InitializeCefSharp(); } [MethodImpl(MethodImplOptions.NoInlining)] private static void InitializeCefSharp() { var settings = new CefSettings(); // Set BrowserSubProcessPath based on app bitness at runtime settings.BrowserSubprocessPath = Path.Combine( AppDomain.CurrentDomain.SetupInformation.ApplicationBase, Environment.Is64BitProcess ? "x64" : "x86", "CefSharp.BrowserSubprocess.exe" ); // Make sure you set performDependencyCheck false Cef.Initialize(settings, performDependencyCheck: false, browserProcessHandler: null); } // Will attempt to load missing assembly from either x86 or x64 subdir // Required by CefSharp to load the unmanaged dependencies when running using AnyCPU private static Assembly Resolver(object sender, ResolveEventArgs args) { if (args.Name.StartsWith("CefSharp")) { string assemblyName = args.Name.Split(new[] { ',' }, 2)[0] + ".dll"; string archSpecificPath = Path.Combine( AppDomain.CurrentDomain.SetupInformation.ApplicationBase, Environment.Is64BitProcess ? "x64" : "x86", assemblyName ); return File.Exists(archSpecificPath) ? Assembly.LoadFile(archSpecificPath) : null; } return null; } } }
use
When using, you can directly add the chromium WebBrowser control to the xaml file, but the chromium WebBrowser control consumes special memory, so it is also a good choice to add it dynamically in the code.
Add browser in xaml
Insert reference in xmal file header
xmlns:wpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
Add controls as follows:
<Grid x:Name="ctrlBrowerGrid"> <wpf:ChromiumWebBrowser x:Name="Browser"/> </Grid>
To access the web address of the operation control in the cs file:
Browser.Load("https://www.psvmc.cn");
Code add browser
Add browser class:
using CefSharp.Wpf; using System.ComponentModel; using System.Windows; namespace CEFSharpTest.view { internal sealed class CollapsableChromiumWebBrowser : ChromiumWebBrowser { public CollapsableChromiumWebBrowser() { Loaded += BrowserLoaded; } private void BrowserLoaded(object sender, RoutedEventArgs e) { // Avoid loading CEF in designer if (DesignerProperties.GetIsInDesignMode(this)) { return; } // Avoid NRE in AbstractRenderHandler.OnPaint ApplyTemplate(); } } }
Dynamically add and manipulate controls:
private CollapsableChromiumWebBrowser MyBrowser = null; private void InitWebBrower() { MyBrowser = new CollapsableChromiumWebBrowser(); //Page insert control ctrlBrowerGrid.Children.Add(MyBrowser); //The Load() method cannot be used here, and an error will be reported. MyBrowser.Address = "https://www.psvmc.cn"; }
Get cookies and Html
Add Cookie access class
using CefSharp; using System; namespace CEFSharpTest.view { public class CookieVisitor : ICookieVisitor { private string Cookies = null; public event Action<object> Action; public bool Visit(Cookie cookie, int count, int total, ref bool deleteCookie) { if (count == 0) Cookies = null; Cookies += cookie.Name + "=" + cookie.Value + ";"; deleteCookie = false; return true; } public void Dispose() { if (Action != null) Action(Cookies); return; } } }
The browser control accesses the web address and sets the callback
private CollapsableChromiumWebBrowser MyBrowser = null; private void InitWebBrower() { MyBrowser = new CollapsableChromiumWebBrowser(); //Page insert control ctrlBrowerGrid.Children.Add(MyBrowser); MyBrowser.FrameLoadEnd += Browser_FrameLoadEnd; //The Load() method cannot be used here, and an error will be reported. MyBrowser.Address = "https://www.psvmc.cn"; } private async void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e) { CookieVisitor visitor = new CookieVisitor(); string html = await MyBrowser.GetSourceAsync(); Console.WriteLine("html:" + html); visitor.Action += RecieveCookie; Cef.GetGlobalCookieManager().VisitAllCookies(visitor); return; } public async void RecieveCookie(object data) { string cookies = (string)data; Console.WriteLine("cookies:" + cookies); return; }
Load local pages and JS callbacks
Add HTML
Add HTML path under item html\index.html
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <script type="text/javascript"> function callback() { callbackObj.showMessage('message from js'); } function alert_msg(msg) { alert(msg); } </script> </head> <body> <button onclick="callback()">Click</button> <style> * { margin: 0; padding: 0; } body { background-color: #f3f3f3; width: 100vw; height: 100vh; display:flex; align-items:center; justify-content:center; } </style> </body> </html>
Copy page to destination directory
Mode 1
Project - > properties - > generate event - > pre build event command line
Add as follows
xcopy /Y /i /e $(ProjectDir)\html $(TargetDir)\html
Mode 2
Right click the properties of the file to set the copy to output directory and generate operation.
If there are many documents, mode 1 is recommended.

code
Register a JS object
private ChromiumWebBrowser MyBrowser = null; private void InitWebBrower() { CefSettings cSettings = new CefSettings() { Locale = "zh-CN", CachePath = Directory.GetCurrentDirectory() + @"\Cache" }; cSettings.MultiThreadedMessageLoop = true; cSettings.CefCommandLineArgs.Add("proxy-auto-detect", "0"); cSettings.CefCommandLineArgs.Add("--disable-web-security", ""); //Disable GPU acceleration cSettings.CefCommandLineArgs.Add("disable-gpu"); //Disable GPU vsync cSettings.CefCommandLineArgs.Add("disable-gpu-vsync"); //This configuration allows the camera to turn on the camera cSettings.CefCommandLineArgs.Add("enable-media-stream", "1"); Cef.Initialize(cSettings); string pagepath = string.Format(@"{0}html\index.html", AppDomain.CurrentDomain.BaseDirectory); if (!File.Exists(pagepath)) { MessageBox.Show("HTML non-existent: " + pagepath); return; } // Create a browser component MyBrowser = new ChromiumWebBrowser(); //Disable right-click menu MyBrowser.MenuHandler = new MenuHandler(); //Disable pop ups MyBrowser.LifeSpanHandler = new LifeSpanHandler(); MyBrowser.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#f3f3f3")); //Page insert control ctrlBrowerGrid.Children.Add(MyBrowser); MyBrowser.Address = pagepath; MyBrowser.JavascriptObjectRepository.Settings.LegacyBindingEnabled = true; MyBrowser.JavascriptObjectRepository.Register( "callbackObj", new CallbackObjectForJs(), isAsync: true, options: BindingOptions.DefaultBinder ); }
Call JS method
private void Button_Click(object sender, RoutedEventArgs e) { MyBrowser.ExecuteScriptAsync("alert_msg('123')"); }
Event callback class
public class CallbackObjectForJs { public void showMessage(string msg) { MessageBox.Show(msg); } }
Disable classes for right-click menus
public class MenuHandler : IContextMenuHandler { public void OnBeforeContextMenu( IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model ) { model.Clear(); } public bool OnContextMenuCommand( IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags ) { return false; } public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame) { } public bool RunContextMenu( IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback ) { return false; } }
Open linked class from original window
public class LifeSpanHandler : ILifeSpanHandler { //Event triggered before pop-up public bool OnBeforePopup( IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) { //Use the source window to open the link and cancel the creation of a new window newBrowser = null; var chromiumWebBrowser = (ChromiumWebBrowser)webBrowser; chromiumWebBrowser.Load(targetUrl); return true; } public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser) { } public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser) { return true; } public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser) { } }
Attention items
API change
//Old Method MyBrowser.RegisterAsyncJsObject("callbackObj", new CallbackObjectForJs(), options: BindingOptions.DefaultBinder); //Replaced with MyBrowser.JavascriptObjectRepository.Settings.LegacyBindingEnabled = true; MyBrowser.JavascriptObjectRepository.Register("callbackObj", new CallbackObjectForJs(), isAsync: true, options: BindingOptions.DefaultBinder);
Local file path
The file path cannot contain special characters, otherwise it cannot be loaded. Before, my project was in the C# directory and the page could not be loaded.