从 CefSharp 迁移至 DotNetBrowser
从 CefSharp 迁移至 DotNetBrowser
本指南面向已有 CefSharp 代码、正在考虑迁移至 DotNetBrowser 的 .NET 开发者。
文中各节将针对 CefSharp 的具体功能特性,说明如何用 DotNetBrowser 进行替换。
指南提供了对 CefSharp 调用与事件的直接替代方案,指出 DotNetBrowser 与 CefSharp 之间的关键差异,并附上相关文档链接以供参考。
安装依赖项
将 DotNetBrowser 添加到项目的最简方式是通过 NuGet。只需安装适合您 UI framework 的包,其余工作 NuGet 会自动完成,包括下载核心库及所需的 Chromium 文件。
请务必从一开始就选择正确的配置——匹配架构(x86、x64 或 aarch64)与 UI 框架(如 WPF 或 Avalonia)。
所需软件包:
DotNetBrowser.Wpf.x64
— 适用于 Windows x64 的 WPF 集成包。- (传递引用)
DotNetBrowser.Win-x64
— 核心 API。 - (传递引用)
DotNetBrowser.Chromium.Win-x64
— 内置 Chromium。
您可在安装指南中查看所有支持的 UI framework 及平台的完整 NuGet 包列表。
若不使用 NuGet,也可手动添加:引用 DLL 文件 或 VSIX 包即可。
Chromium 引擎
初始化
在 CefSharp 中,您需要先初始化底层的 Chromium Embedded Framework(简称 CEF):
Cef.Initialize(new CefSettings());
在 WPF 和 WinForms 中,CefSharp 会自动调用此方法。但若您需要自定义配置,则需手动预先调用。
注意:此方法必须从主线程调用,且每个进程生命周期内仅允许调用一次。这些限制源于 CefSharp 的进程内架构,其将 Chromium 引擎运行在 .NET 进程中,导致每个进程仅能承载一个引擎(Engine)实例。
在 DotNetBrowser 中,您同样需要初始化 Chromium 引擎,但无上述限制:
IEngine engine = EngineFactory.Create(RenderingMode.HardwareAccelerated);
DotNetBrowser 采用进程外架构,在独立进程中启动 Chromium 引擎。因此,您可以在任何线程的任何时刻创建多个 Engine 实例。
为何需要多个引擎?详见本指南的浏览器隔离部分。
关闭
在 CefSharp 中,当不再需要 Chromium 引擎时,必须将其关闭,否则应用程序将挂起。
同样地,该方法必须在应用程序的主线程中调用;若使用 WPF 或 WinForms,CefSharp 会自动为你执行此操作。
在 DotNetBrowser 中,同样需要显式关闭 Chromium 引擎,但可以在任何线程的任何时刻进行。若未释放 IEngine
,只会造成资源泄漏,不会阻塞 .NET 进程:
CefSharp
Cef.Shutdown();
DotNetBrowser
engine.Dispose();
嵌入浏览器
WPF
CefSharp 和 DotNetBrowser 均提供可直接嵌入 XAML 的 WPF 控件。
实际上,CefSharp 提供了两种不同的组件。如果您需要离屏渲染,可以使用 WPF
实现。如果您需要更快的渲染速度,可以使用 Wpf.HwndHost
实现,它会将 Chromium 窗口重新设置为您的应用程序的父级。
DotNetBrowser 提供了一个组件,它可以以两种模式渲染内容——大致相当于 CefSharp 的模式。这两种模式分别是 HardwareAccelerated
和 OffScreen
,您可以在创建 IEngine
时选择相应的模式。
MainWindow.xaml:
CefSharp
<Window xmlns:wpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"x:Class="CefSharp.Wpf.MainWindow"Title="CefSharp" Width="800" Height="600"><Grid><wpf:ChromiumWebBrowser x:Name="Browser" Address="https://www.teamdev.com" /></Grid>
</Window>
DotNetBrowser
<Window xmlns:wpf="clr-namespace:DotNetBrowser.Wpf;assembly=DotNetBrowser.Wpf"x:Class="DotNetBrowser.Wpf.MainWindow"Title="DotNetBrowser" Width="800" Height="600" Closed="Window_Closed"><Grid><wpf:BrowserView Name="browserView" /></Grid>
</Window>
MainWindow.xaml.cs:
CefSharp
public partial class MainWindow : Window
{static MainWindow(){CefSettings settings = new CefSettings();Cef.Initialize(settings);} public MainWindow(){InitializeComponent();}
}
DotNetBrowser
public partial class MainWindow : Window
{private readonly IEngine engine;public MainWindow(){// 在此配置渲染模式。engine = EngineFactory.Create(RenderingMode.HardwareAccelerated);IBrowser browser = engine.CreateBrowser();InitializeComponent();browserView.InitializeFrom(browser);browser.Navigation.LoadUrl("https://teamdev.com");}private void Window_Closed(object sender, EventArgs e){engine?.Dispose();}
}
请参阅 BrowserView
指南,了解更多关于渲染模式、其性能以及如何选择的信息。
WinForms
在 WinForms 中,CefSharp 始终使用重量级渲染,与 Wpf.Hwnd
中相同。
在 DotNetBrowser 中,WinForms 的 BrowserView
控件将使用您在创建 IEngine
时配置的渲染模式。
其余部分类似:
CefSharp
using CefSharp.WinForms;
...public partial class BrowserForm : Form
{private readonly ChromiumWebBrowser browser;public BrowserForm(){InitializeComponent();browser = new ChromiumWebBrowser("https://teamdev.com");Controls.Add(browser);}
}
DotNetBrowser
using DotNetBrowser.WinForms;
...public partial class BrowserForm : Form
{private readonly IEngine engine;public BrowserForm(){// 您可以在此处配置渲染模式。engine = EngineFactory.Create(RenderingMode.HardwareAccelerated);IBrowser browser = engine.CreateBrowser();InitializeComponent();BrowserView browserView = new BrowserView(); Controls.Add(browserView);FormClosed += Form1_FormClosed;browserView.InitializeFrom(browser);browser.Navigation.LoadUrl("https://teamdev.com");}private void Form1_FormClosed(object sender, FormClosedEventArgs e){engine?.Dispose();}
}
无头模式
CefSharp 为无头使用场景提供了特殊的 OffScreen
实现,即 ChromiumWebBrowser
,此时浏览器不会显示在 UI 中。
在 DotNetBrowser 中,每个 IBrowser
实例都是一个功能齐全的浏览器,无需 UI 即可运行。
CefSharp
using CefSharp.OffScreen;
...public static class Program
{public static int Main(string[] args){var settings = new CefSettings();Cef.Initialize(settings);var browser = new ChromiumWebBrowser("https://www.google.com/");...// 仅等待,以保持进程运行。Console.ReadKey();Cef.Shutdown();return 0;}
}
DotNetBrowser
using DotNetBrowser.Browser;
using DotNetBrowser.Engine;
...public static class Program
{public static int Main(string[] args){using (IEngine engine = EngineFactory.Create(RenderingMode.HardwareAccelerated)){IBrowser browser = engine.CreateBrowser();browser.Navigation.LoadUrl("https://www.google.com/").Wait();...// 仅等待,以保持进程运行。Console.ReadKey();return 0;}}
}
释放浏览器
在 CefSharp 和 DotNetBrowser 中,都必须关闭 Chromium 引擎(Engine)和浏览器(Browser)实例,以防资源泄漏。关闭引擎会自动关闭由其创建的所有浏览器实例:
CefSharp
// 您必须仅在主线程中释放 Browser。
cefBrowser.Dispose();
DotNetBrowser
// 您可以在任何线程中释放 Browser。
browser.Dispose();
CefSharp 不提供通用的方法来检测浏览器何时关闭。
在 DotNetBrowser 中,你可以为浏览器和引擎都注册 Disposed
事件处理程序。无论引擎或浏览器因何种原因、以何种方式关闭,该事件都会被触发。
DotNetBrowser
engine.Disposed += (sender, args) =>
{Console.WriteLine("The engine was closed!");
};browser.Disposed += (sender, args) =>
{Console.WriteLine("The browser was closed!");
};
线程
在 CefSharp 中,调用方法时必须始终留意所处的线程。许多方法对线程有明确要求,文档中常见如下说明:
此方法必须在 CEF XYZ 线程上调用。
DotNetBrowser 无任何线程限制。您可以从任意线程调用该库的 API。
浏览器隔离
数据与网络隔离
在 CefSharp 中,您可以通过将浏览器置于不同的 RequestContext
实现彼此隔离。同一请求上下文内的浏览器会共享所有内容:偏好设置、Cookie、缓存、Service Worker。相应地,不同请求上下文中的浏览器则相互隔离。
在 DotNetBrowser 中,请求上下文的替代方案是配置文件(Profile)。同一配置文件下创建的浏览器会共享偏好设置、网络层、缓存、Cookie、存储等内容。若需实现浏览器间的隔离,请在不同配置文件中创建它们:
CefSharp
var publicContext = RequestContext.Configure().WithCachePath("<path to a public cache location>").Create();
var publicBrowser = new ChromiumWebBrowser("https://teamdev.com");
publicBrowser.RequestContext = publicContext;var secureContext = RequestContext.Configure().WithCachePath("<path to a secure cache location>").Create();
var secureBrowser = new ChromiumWebBrowser("https://internal.system");
secureBrowser.RequestContext = secureContext;
DotNetBrowser
var publicProfile = engine.Profiles.Default;
var publicBrowser = defaultProfile.CreateBrowser();
publicBrowser.Navigation.LoadUrl("https://teamdev.com");var secureProfile = engine.Profiles.Create("secure-profile");
var secureBrowser = secureProfile.CreateBrowser();
secureBrowser.Navigation.LoadUrl("https://internal.system");
运行时隔离
CefSharp 将 Chromium 引擎嵌入 .NET 进程内部。该引擎是单例,每个进程生命周期只能创建一次。若引擎崩溃,不仅浏览器实例会终止,整个应用程序都会随之崩溃。
DotNetBrowser 则在独立进程中运行 Chromium 引擎,因此即使引擎崩溃,.NET 进程也不会受影响。当发生此类崩溃时,.NET 进程会收到通知:
DotNetBrowser
engine.Disposed += (s, e) =>
{if (e.ExitCode != 0){// 引擎已崩溃。}
};
当引擎崩溃时,其所有 IBrowser
实例也会立即关闭。通过在不同的 IEngine
中创建各自的配置文件和浏览器,可实现彼此隔离,避免受到其他引擎崩溃的影响。
请注意,创建并运行额外的
IEngine
实例会占用大量资源,而且通常并不必要。
DotNetBrowser
// 启动公共浏览器的 Chromium 主进程。
var publicEngine = EngineFactory.Create(RenderingMode.HardwareAccelerated);
var publicProfile = publicEngine.Profiles.Default;
var publicBrowser = defaultProfile.CreateBrowser();
publicBrowser.Navigation.LoadUrl("https://teamdev.com");// 启动安全浏览器的 Chromium 主进程。
var secureEngine = EngineFactory.Create(RenderingMode.HardwareAccelerated);
var secureProfile = secureEngine.Profiles.Create("secure-profile");
var secureBrowser = secureProfile.CreateBrowser();
secureBrowser.Navigation.LoadUrl("https://internal.system");
导航方法
在 CefSharp 中,大多数导航方法在 DotNetBrowser 中都能找到一一对应的替代:
CefSharp
ChromiumWebBrowser chromiumBrowser = new ChromiumWebBrowser();
chromiumBrowser.LoadUrl("https://teamdev.com");IBrowser browser = chromiumBrowser.GetBrowser();
browser.GoBack();
browser.GoForward();
browser.Reload(true);
browser.StopLoad();
bool canGoBack = browser.CanGoBack;
bool canGoForward = browser.CanGoForward;
DotNetBrowser
INavigation navigation = browser.Navigation;
navigation.LoadUrl("https://teamdev.com");
navigation.GoBack();
navigation.GoForward();
navigation.Reload();
navigation.ReloadIgnoringCache();
navigation.Stop();
bool canGoBack = navigation.CanGoBack();
bool canGoForward = navigation.CanGoForward();
导航事件
CefSharp 与 DotNetBrowser 都提供了追踪导航状态的机制。在 DotNetBrowser 中,可使用 NavigationStarted
、NavigationStopped
和 NavigationFinished
组合来替代 CefSharp 的 LoadingStateChanged
与 LoadError
事件:
CefSharp
browser.LoadingStateChanged += (sender, args) =>
{if (args.IsLoading){Console.WriteLine("Navigation started.");}else{Console.WriteLine("Navigation completed.");}
};browser.LoadError += (sender, args) =>
{if (args.ErrorCode == CefErrorCode.Aborted){Console.WriteLine("Navigation aborted");}
}
DotNetBrowser
navigation.NavigationStarted += (s, e) =>
{Console.WriteLine("Navigation started.");
};
navigation.NavigationStopped += (s, e) =>
{Console.WriteLine("Navigation stopped.");
};
navigation.NavigationFinished += (sender, args) =>
{if (args.ErrorCode == NetError.Aborted){Console.WriteLine("Navigation aborted");}
};
若需判断某个 Frame 的 JavaScript 上下文与 DOM 树何时就绪,可用 FrameDocumentLoadFinished
替代 FrameLoadEnd
。
CefSharp
browser.FrameLoadEnd += (sender, args) =>
{if (args.Frame.IsMain){Console.WriteLine("DOM and V8 context are ready in the main frame.");}else{Console.WriteLine("DOM and V8 context are in an iframe.");}
};
DotNetBrowser
navigation.FrameDocumentLoadFinished += (s, args) =>
{if (args.frame.IsMain) {Console.WriteLine("DOM and V8 context are ready in the main frame.");}else{Console.WriteLine("DOM and V8 context are in an iframe.");}
};
DotNetBrowser 中有 10 个细粒度的导航事件。可查看完整列表。
使用 POST 数据加载
在 CefSharp 中,无法直接使用 POST 参数加载 URL。要实现这一功能,您需要注册一个全局请求处理器,筛选出要修改的请求,然后手动添加 POST 数据。
在 DotNetBrowser 中,您可以直接使用任意 POST 数据加载 URL:
CefSharp
class MyRequestHandler : RequestHandler
{protected override IResourceRequestHandler GetResourceRequestHandler(...){if (request.Url == "https://my.post.endpoint"){return new MyResourceRequestHandler();}return null;}
}class MyResourceRequestHandler : ResourceRequestHandler
{protected override CefReturnValue OnBeforeResourceLoad(...){var postData = new PostData();postData.AddData("username=test&password=1234");request.Method = "POST";request.PostData = postData;request.SetHeaderByName("Content-Type", "application/x-www-form-urlencoded", true);return CefReturnValue.Continue;}
}browser = new ChromiumWebBrowser("https://my.post.endpoint");
browser.RequestHandler = new CustomRequestHandler();
DotNetBrowser
// 创建包含 POST 数据参数的数组。
KeyValuePair<string, string>[] formData =
{new KeyValuePair<string, string>("username", "test"),new KeyValuePair<string, string>("password", "1234")
};LoadUrlParameters request = new LoadUrlParameters("https://my.post.endpoint")
{// DotNetBrowser 将根据数据类型自动推断内容类型(Content-Type)。UploadData = new FormData(formData)
};// 使用自定义请求进行导航。
browser.Navigation.LoadUrl(request);
DotNetBrowser 会自动填充 POST 请求的 Content-Type
(内容类型)和 Content-Length
(内容长度)标头。对于分段上传,它还会添加表单边界,并设置每个分段的 Content-Disposition
(内容部署)和 Content-Type
标头。
您可以在使用 POST 数据导航指南中了解更多关于如何发送 POST 请求的信息。
加载 HTML 内容
在 CefSharp 和 DotNetBrowser 中,加载 HTML 字符串最简便的方式是将其作为 data:
URL 直接加载:
CefSharp
var html = "<html>Html Encoded in URL!</html>";
var encodedHtml = Convert.ToBase64String(Encoding.UTF8.GetBytes(html));
browser.Load("data:text/html;base64," + encodedHtml);
DotNetBrowser
var html = "<html>Html Encoded in URL!</html>";
var encodedHtml = Convert.ToBase64String(Encoding.UTF8.GetBytes(html));
browser.Navigation.LoadUrl("data:text/html;base64," + encodedHtml);
此方法的 URL 大小上限约为 2 MB。如果您使用 CefSharp 的 ResourceHandler
加载更多内容,请将其替换为自定义方案请求拦截器。
JavaScript
何时开始执行 JavaScript
若要在最早的时机运行 JavaScript,需要先确定 Chromium 已完成 V8 JavaScript 上下文的初始化。
在 CefSharp 中,可通过 OnContextCreated
事件获知某个 frame 的 JavaScript 上下文已创建;此时 frame 内的其他脚本尚未运行。在 DotNetBrowser 中,用语义相同的 InjectJsHandler
替代即可:
CefSharp
class RenderProcessMessageHandler : IRenderProcessMessageHandler
{void OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame){frame.ExecuteJavaScriptAsync("...");}
}
browser.RenderProcessMessageHandler = new RenderProcessMessageHandler();
DotNetBrowser
browser.InjectJsHandler = new Handler<InjectJsParameters>(args =>
{args.Frame.ExecuteJavaScript<IJsObject>("...");
});
从 .NET 调用 JavaScript
两个库在 JavaScript 交互方面提供了类似的能力,区别主要体现在类型转换以及将 .NET 对象注入到 JavaScript 中的方式。
要执行脚本,请将 EvaluateScriptAsync
调用替换为 ExecuteJavaScript
:
CefSharp
JavascriptResponse response = await frame.EvaluateScriptAsync("'Hello'");
DotNetBrowser
string response = await frame.ExecuteJavaScript<string>("'Hello'");
虽然两处调用看起来相似,但返回结果却有所不同。CefSharp 与 DotNetBrowser 都会尝试将 JavaScript 类型转换为相应的 C# 类型,但各自的处理方式不同。
CefSharp 可返回原始类型、简单的非循环 JavaScript 对象以及这些类型的数组。无法返回 DOM 元素或 window
对象。
DotNetBrowser 可返回原始类型和任意复杂度的 JavaScript 对象,包括具有循环引用的对象——该库直接使用 V8 对象,而无需在底层对其进行序列化和复制。
CefSharp
JavascriptResponse numberResponse = await frame.EvaluateScriptAsync("123");
int number = (int) numberResponse.Result;JavascriptResponse booleanResponse = await frame.EvaluateScriptAsync("true");
bool boolean = (bool) booleanResponse.Result;JavascriptResponse strResponse = await frame.EvaluateScriptAsync("'Hello'");
string str = (string) strResponse.Result;JavascriptResponse objResponse = await frame.EvaluateScriptAsync("({'foo': 'bar'})");
dynamic obj = objResponse.Result;
var foo = obj.foo;// 不能这样做:
JavascriptResponse windowResponse = await frame.EvaluateScriptAsync("window");
object window = strResponse.Result; // Null.
DotNetBrowser
// DotNetBrowser 与 CefSharp 一样可直接转换原始类型:
double number = await frame.ExecuteJavaScript<double>("123");
bool boolean = await frame.ExecuteJavaScript<bool>("true");
string str = await frame.ExecuteJavaScript<string>("'Hello'");IJsObject obj = await frame.ExecuteJavaScript<IJsObject>("{'foo': 'bar'}");
var foo = obj.Properties["foo"];// 这在 DotNetBrowser 中可行。
IJsObject window = await frame.ExecuteJavaScript<IJsObject>("window");// 在可能的情况下,DotNetBrowser 会选择更具体的类型:
IElement body = await frame.ExecuteJavaScript<IElement>("document.body");
如需了解更多信息,请参阅指南从 .NET 调用 JavaScript。
从 JavaScript 调用 .NET
在这两种解决方案中,从 JavaScript 调用 .NET 代码都需要将 .NET 对象注入到 JavaScript 环境中。
在 CefSharp 中,此过程分两步:首先在 .NET 端把对象放入 JavascriptObjectRepository
,然后在 JavaScript 端异步“获取”该对象。
在 DotNetBrowser 中,方法更简单:将任意 .NET 对象分配给任意 JavaScript 属性即可。
在 C# 代码中:
CefSharp
// 在注册表中注册 .NET 对象:
browser.JavascriptObjectRepository.ResolveObject += (sender, e) =>
{var repo = e.ObjectRepository;if (e.ObjectName == "myObject"){repo.Register("myObject", new MyObject(), null);}
};
DotNetBrowser
// 将 .NET 对象分配给任意 JavaScript 对象的属性。
IJsObject window = frame.ExecuteJavaScript<IJsObject>("window").Result;
window.Properties["myObject"] = new MyObject();
In JavaScript code:
CefSharp
// 一次性获取 .NET 对象:
await CefSharp.BindObjectAsync("myObject");// 使用注入的 .NET 对象。
myObject.add(40, 2).then((sum) => console.log("Sum is: " + sum));
DotNetBrowser
// 可以直接使用该 JavaScript 对象:
const sum = myObject.add(40, 2);
console.log("Sum is: " + sum);
更多信息请参阅在 DotNetBrowser 中从 JavaScript 调用 .NET 的指南。
拦截流量
CefSharp 与 DotNetBrowser 都支持在标准协议和 custom://
协议中拦截流量并替换服务器响应:
CefSharp
public class MySchemeHandlerFactory : ISchemeHandlerFactory
{IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request){var uri = new Uri(request.Url);var fileName = uri.AbsolutePath;var responseContent = ReadMyCustomResponse(uri);return ResourceHandler.FromString(responseContent, ...);return null;}
}settings.RegisterScheme(new CefCustomScheme
{// 对应 custom-sceme://SchemeName = "custom-scheme",// 您必须为该 scheme 与 host 的组合注册一个处理程序。DomainName = "my-app.com",SchemeHandlerFactory = new MySchemeHandlerFactory()
});Cef.Initialize(settings);
DotNetBrowser
var handler =new Handler<InterceptRequestParameters, InterceptRequestResponse>(p =>{var url = p.UrlRequest.Url;if (GetDomain(url) == "my-app.com") {var opts = new UrlRequestJobOptions{HttpStatusCode = HttpStatusCode.OK,Headers = new List<HttpHeader>{new HttpHeader("Content-Type", "text/html", "charset=utf-8")}};var job = p.Network.CreateUrlRequestJob(p.UrlRequest, opts);Task.Run(() =>{var responseContent = ReadMyCustomResponse(url);job.Write(Encoding.UTF8.GetBytes(responseContent));job.Complete();});// 返回用于填充响应数据的任务。return InterceptRequestResponse.Intercept(job);} else {// 允许请求继续。return InterceptRequestResponse.Proceed();}});var opts = new EngineOptions.Builder
{// 为整个 scheme 注册请求拦截器。 Schemes = { { Scheme.Create("custom-scheme"), handler }}
}.Build();var engine = EngineFactory.Create(opts);
DotNetBrowser 没有提供与 CefSharp 中 ResourceHandler.FromStream
或 ResourceHandler.FromFilePath
等便捷方法对应的替代方法。
请求处理程序
CefSharp 提供了 IRequestHandler
接口,允许您修改传出的请求、处理身份验证以及渲染进程中的某些事件。在 DotNetBrowser 中,相同的功能分布在多个单独的事件处理程序中。
DotNetBrowser 中与 IRequestHandler
等效的方法如下:
IRequestHandler 中的方法 | DotNetBrowser 中的等效方法 |
---|---|
OnSelectClientCertificate | SelectCertificateHandler |
GetAuthCredentials | AuthenticateHandler |
OnCertificateError | VerifyCertificateHandler |
OnRenderProcessTerminated | RenderProcessTerminated |
OnDocumentAvailableInMainFrame | FrameDocumentLoadFinished |
OnOpenUrlFromTab | 无可用替代方案。如需允许或阻止弹出窗口,请使用 CreatePopupHandler 。 |
DotNetBrowser equivalents to IResourceRequestHandler
:
IResourceRequestHandler 中的方法 | DotNetBrowser 中的等效方法 |
---|---|
GetResourceHandler | 使用自定义方案拦截器。 |
OnResourceRedirect | RedirectResponseCodeReceived |
OnBeforeResourceLoad | SendUrlRequestHandler |
OnResourceLoadComplete | RequestCompleted |
OnResourceResponse | StartTransactionHandler |
GetCookieAccessFilter | StartTransactionHandler |
GetResourceResponseFilter 用于读取响应内容 | ResponseBytesReceived |
GetResourceResponseFilter 用于注入自定义 CSS | InjectCssHandler |
GetResourceResponseFilter 用于过滤传入字节 | 无可用替代方案 |
OnProtocolExecution | 无可用替代方案 |
Cookie 管理器
CefSharp 提供了 ICookieManager
接口,用于在存储中创建、读取、更新和删除 Cookie。所有浏览器共享一个全局 Cookie 管理器,且每个 RequestContext
都有其自己的 Cookie 管理器。
DotNetBrowser 提供了一个类似的实体,称为 ICookieStore
。所有 Cookie 存储都绑定到各自的 IProfile
,没有可用的全局存储:
CefSharp
// Cookie 管理器以异步方式创建。
// 使用 IBrowserProcessHandler.OnContextInitialized 了解其何时可使用。
var cookieManager = Cef.GetGlobalCookieManager();// 创建 Cookie。
var success = cookieManager.SetCookie("google.com", new Cookie
{Name = "name",Value = "value",Expires = expirationTime,Path = "/"
});// 删除 Cookie。
cookieManager.DeleteCookies("https://google.com", "cookie-name");// 立即持久化 Cookie 更改。
cookieManager.FlushStore();// 遍历所有现有 Cookie。
cookieManager.VisitAllCookies(new CookieVisitor());// 遍历指定 URL 的所有 Cookie。
bool withHttpOnly = false;
cookieManager.VisitUrlCookies("httos://google.com", withHttpOnly, new CookieVisitor());
DotNetBrowser
// 无需初始化,可直接使用 Cookie 存储。
var cookieStore = profile.CookieStore;// 创建 Cookie。
Cookie cookie = new Cookie.Builder("google.com")
{Name = "name",Value = "value",ExpirationTime = expirationTime,Path = "/"
}.Build();
await engine.Profiles.Default.CookieStore.SetCookie(cookie);// 删除 Cookie。
await cookieStore.CookieStore.Delete(cookie);// 立即持久化 Cookie 更改。
cookieStore.Flush();// 读取所有 Cookie。
IEnumerable<Cookie> allCookies = await cookieStore.CookieStore.GetAllCookies();// 读取指定 URL 的 Cookie。
IEnumerable<Cookie> urlCookies = await cookieStore.CookieStore.GetAllCookies("https://google.com");
代理
CefSharp 允许您通过两种方式配置代理:
- 在初始化引擎时使用特殊的 Chromium 开关;
- 在运行时更改
IRequestContext
中的代理。
在 DotNetBrowser 中,首选方法是使用 IProfile
实例在运行时配置代理,您可以在创建任何 IBrowser
实例之前完成此操作。
CefSharp
// 为所有浏览器指定全局代理设置。
var settings = new CefSettings();// 继承系统代理设置。
settings.CefCommandLineArgs.Add("no-proxy-server"); // 使用 Web 代理自动发现(Web Proxy Auto-Discovery)。
settings.CefCommandLineArgs.Add("proxy-auto-detect"); // 手动配置代理。
settings.CefCommandLineArgs.Add("proxy-server=http=proxy.com:8080;https=proxy.com:8081"); // 使用 PAC 文件。
settings.CefCommandLineArgs.Add("proxy-pac-url=URL");
Cef.Initialize(settings);// 或者,为特定的 RequestContext 配置代理:
var requestContext = RequestContext.Configure().WithProxyServer("proxy.com", 8080).Create();
DotNetBrowser
IProxy proxy = profile.Proxy;// 继承系统代理设置。
proxy.Settings = new SystemProxySettings();// 不使用任何代理服务器。
proxy.Settings = new DirectProxySettings();// 使用 Web 代理自动发现。
proxy.Settings = new AutoDetectProxySettings();// 手动配置代理。
string proxyRules = "http=proxy.com:8080;https=proxy.com:8081";
proxy.Settings = new CustomProxySettings(proxyRules);// 使用 PAC 文件。
proxy.Settings = new PacProxySettings("<pac-file-url>");
要验证代理,请将 CefSharp 的 IRequestHandler.GetAuthCredentials
替换为 AuthenticateHandler
:
CefSharp
class CustomRequestHandler : CefSharp.Handler.RequestHandler
{override bool GetAuthCredentials(...){if (isProxy){using (callback){callback.Continue(username: "user", password: "pass");}return true;}// 取消身份验证请求。return false;}
}browser.RequestHandler = new CustomRequestHandler();
DotNetBrowser
network.AuthenticateHandler = new Handler<AuthenticateParameters, AuthenticateResponse>(p =>{if (p.IsProxy){return AuthenticateResponse.Continue("user", "pass"));}return AuthenticateResponse.Cancel();};
弹出窗口
在 CefSharp 中,弹出浏览器使用 ILifeSpanHandler
进行控制。通过使用此接口的 WPF 或 WinForms 实现,您可以在新窗口中显示新创建的浏览器。例如,在 WinForms 中如下所示:
CefSharp
browser.LifeSpanHandler = CefSharp.WinForms.Handler.LifeSpanHandler.Create().OnBeforePopupCreated((...) => {if (...) {return PopupCreation.Continue;} else{return PopupCreation.Cancel;}}).OnPopupCreated((ctrl, targetUrl) =>{parent.Controls.Add(ctrl);}).OnPopupDestroyed((ctrl, popupBrowser) =>{if (!ctrl.IsDisposed && ctrl.IsHandleCreated){parent.Controls.Remove(ctrl);}}).Build();
在 DotNetBrowser 中,您需要用两个处理器来替代:
CreatePopupHandler
用于启用 / 禁用弹出窗口的创建。OpenPopupHandler
用于在需要时显示弹出窗口。
在 OpenPopupHandler
中,您将收到新创建的 IBrowser
实例,
并且您可以在无头模式下使用它,或者使用 BrowserView
在 UI 中显示它。默认情况下,如果浏览器处于 BrowserView
模式,它会在新窗口中打开弹出窗口;否则,它会抑制弹出窗口:
DotNetBrowser
class MyOpenPopupHandler : IHandler<OpenPopupParameters>
{private readonly Control parent;public MyOpenPopupHandler(Control parent){this.parent = parent;}public void Handle(OpenPopupParameters parameters){Action showPopupAction = () =>{ShowPopup(parameters.PopupBrowser,parameters.Rectangle);};parent.BeginInvoke(showPopupAction);}private void ShowPopup(IBrowser popupBrowser, Rectangle rectangle){BrowserView browserView = new BrowserView{Dock = DockStyle.Fill};browserView.InitializeFrom(popupBrowser);// 将浏览器视图添加到父控件并显示。}
}browser.CreatePopupHandler =new Handler<CreatePopupParameters, CreatePopupResponse>(p =>{if (...){return CreatePopupResponse.Create();}else {return CreatePopupResponse.Suppress();}});// 如果您不想让 DotNetBrowser 的默认行为:
// 在新窗口中显示弹出窗口,请设置此处理程序。
browser.OpenPopupHandler = new MyOpenPopupHandler(parent);
JavaScript 对话框
CefSharp 提供了 IJsDialogHandler
接口,允许您处理 JavaScript 对话框。在 DotNetBrowser 中,您可以将其替换为独立的处理程序:
CefSharp
class MyDialogHandler : IJsDialogHandler
{// 在此方法中处理 alert、prompt 和 confirm 对话框。bool OnJSDialog(IWebBrowser chromiumWebBrowser, ...){...}// 在此方法中处理 onBeforeUnload 对话框。bool OnBeforeUnloadDialog(IWebBrowser chromiumWebBrowser, ...){...}// 以下方法在 DotNetBrowser 中没有直接替代,// 因为 DotNetBrowser 允许在 .NET 代码中完全控制对话框状态。void OnResetDialogState(IWebBrowser chromiumWebBrowser, IBrowser browser) {...} void OnDialogClosed(IWebBrowser chromiumWebBrowser, IBrowser browser) {...}
}browser.JsDialogHandler = new MyDialogHandler();
DotNetBrowser"
var dialogs = browser.JsDialogs;
dialogs.AlertHandler = new Handler<AlertParameters>(p => { });
dialogs.ConfirmHandler =new Handler<ConfirmParameters, ConfirmResponse>(p =>{...return ConfirmResponse.Ok();});
dialogs.PromptHandler =new Handler<PromptParameters, PromptResponse>(p =>{...return PromptResponse.SubmitText("some response");});
dialogs.BeforeUnloadHandler =new Handler<BeforeUnloadParameters, BeforeUnloadResponse>(p =>{...return BeforeUnloadResponse.Leave();});
如果您未设置处理器,DotNetBrowser 会自动管理对话框。在 BrowserView
中,它会使用应用程序的 UI 库显示默认对话框实现。否则,它会跳过这些对话框,就像用户按下了“取消”一样。
下载
CefSharp 提供 IDownloadHandler
用于控制下载过程。在 DotNetBrowser 中,可直接使用 StartDownloadHandler
进行替换:
CefSharp
class MyDownloadHandler : IDownloadHandler
{bool OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback){if (downloadItem.IsValid){string downloadPath = Path.GetFullPath(downloadItem.SuggestedFileName);if (!callback.IsDisposed){using (callback){callback.Continue(downloadPath: downloadPath,// 显示默认的保存对话框。showDialog: true );}}return true;}// 让 CefSharp 处理下载。// 在 Alloy 中,下载默认会被取消。return false;}void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback){if (downloadItem.IsValid){if (downloadItem.IsComplete){Console.WriteLine("Download completed");}}}
}
browser.DownloadHandler = new MyDownloadHandler();
DotNetBrowser
browser.StartDownloadHandler =new Handler<StartDownloadParameters, StartDownloadResponse>(p =>{var fileName = p.Download.Info.SuggestedFileName;var targetPath = Path.GetFullPath(fileName);p.Download.Finished += (sender, args) =>{Console.WriteLine("Download completed");};// 返回 StartDownloadResponse.Cancel() 以取消下载。return StartDownloadResponse.DownloadTo(targetPath);});
如果您未配置此处理程序,DotNetBrowser 将自动处理下载。在 BrowserView
中,它会使用应用的 UI 库显示“Save As(另存为)”对话框。在该上下文之外,它会取消下载。
有关更多详细信息,请参阅下载指南。
屏幕截图
在 CefSharp 中,只能通过 DevTools 协议捕获截图。而 DotNetBrowser 提供了专门的方法来实现这一功能,无需使用 DevTools 或暴露远程调试端口:
CefSharp
using (var devToolsClient = chromiumWebBrowser.GetDevToolsClient())
{var result = await devToolsClient.Page.CaptureScreenshotAsync();byte[] pixels = result.Data;
}
DotNetBrowser
// 获取当前视口的原始像素。
// 无需 DevTools 或窗口。
DotNetBrowser.Ui.Bitmap image = browser.TakeImage();// 将图像保存到文件。
Bitmap bitmap = ToBitmap(image);
bitmap.Save("screenshot.png", ImageFormat.Png);
查看 DotNetBrowser 中的高级截屏教程。
开发者工具
在 CefSharp 和 DotNetBrowser 中,您都可以配置远程调试端口,并为每个浏览器打开 DevTools(开发者工具):
CefSharp
var settings = new CefSettings();
settings.RemoteDebuggingPort = 9222;
settings.CefCommandLineArgs.Add("remote-allow-origins=http://localhost:9222");
Cef.Initialize(settings);...// 打开 DevTools 窗口。
browser.ShowDevTools();
// 关闭它。
browser.HideDevTools();
DotNetBrowser
IEngine engine = EngineFactory.Create(new EngineOptions.Builder
{ChromiumSwitches = { "--remote-allow-origins=http://localhost:9222" },RemoteDebuggingPort = 9222
}.Build());...// 打开 DevTools 窗口。
browser.DevTools.Show();
// 关闭它。
browser.DevTools.Hide();// 或通过 URL 在另一个浏览器中打开 DevTools。
string url = browser.DevTools.RemoteDebuggingUrl;
DotNetBrowser 没有提供 CefSharp 中 browser.GetDevToolsClient()
对应的替代方案。
用户代理
在 CefSharp 中,您可以通过三种方式配置用户代理:
- 在初始化 CEF 之前配置;
- 在运行时,使用 DevTools 协议配置;
- 在运行时,通过重写请求的
User-Agent
头部配置。
在 DotNetBrowser 中,您可以在创建 IEngine
时设置默认用户代理:
DotNetBrowser
IEngine engine = EngineFactory.Create(new EngineOptions.Builder
{UserAgent = "<user-agent>"
}.Build());
运行时,您可以为单个浏览器实例覆盖该值:
DotNetBrowser
browser.UserAgent = "<user-agent>";
或者,通过 StartTransactionHandler
为单个请求设置:
DotNetBrowser
network.StartTransactionHandler =new Handler<StartTransactionParameters, StartTransactionResponse>(p =>{var newHeaders = p.headers.Cast<HttpHeader>().ToList();newHeaders.Add(new HttpHeader("User-Agent", "Mozilla/5.0 ..."));return StartTransactionResponse.OverrideHeaders(newHttpHeaders);});
或者,您也可以指定用户代理客户端提示(User Agent Client Hints)。
结论
如果您曾使用过 CefSharp,您会发现 DotNetBrowser 的工作方式与之相似。大多数功能都可用,但在使用方式上略有不同。
在本指南中,我们介绍了浏览器生命周期管理、导航、弹窗与对话框、JavaScript 等多项内容的迁移流程。
希望本指南能为您提供有力帮助,让从 CefSharp 迁移至 DotNetBrowser 的过程更加顺畅。