本指南面向正在从 WebView2 迁移至 DotNetBrowser 的 .NET 开发者,本文是其第二部分。第一部分介绍了安装、初始化、导航、JavaScript、流量拦截、DOM、对话框和弹出窗口。

本部分涵盖第一部分预告的进阶主题:代理与身份验证、Cookie 和持久化存储、Profile(配置文件)、Context menu(上下文菜单)、下载、打印、DevTools 以及 Chrome extension(扩展程序)。与之前一样,每一节都会展示 WebView2 代码、其 DotNetBrowser 替代方案、值得注意的差异,以及相关指南的链接。

与第一部分一样,本部分讲的是如何迁移。如需了解为何要迁移,请查看 DotNetBrowser 与 WebView2 的对比文章。

代码面向 WebView2 SDK 1.0.4258.31 和 DotNetBrowser 4.3.3。我们在两个库上运行了每个代码片段,使用的是 WebView2 Runtime 155 和 DotNetBrowser 的 Chromium 155。第一部分面向 DotNetBrowser 3.3.0,其代码片段无需为 4.3.3 做任何修改。

在 WebView2 代码片段中,core 是控件的 CoreWebView2,view 是 WebView2 控件。在 DotNetBrowser 代码片段中,engine 和 browser 分别是 IEngine 和 IBrowser,与第一部分相同;profile 是 IProfile,view 是 BrowserView。

两个反复出现的差异 

在 DotNetBrowser 中,大多数设置属于配置文件。代理设置、Cookie、扩展程序和 network handlers(网络处理程序)都归配置文件所有。每个 Engine 都有一个默认配置文件 engine.Profiles.Default,您还可以创建更多配置文件。在 WebView2 中,相同的设置分布在三个地方:适用于整个 Browser 进程的环境选项、CoreWebView2Profile,以及 CoreWebView2 本身。

线程模型也不同。WebView2 在 UI 线程上引发事件。DotNetBrowser 在后台线程上调用处理程序并引发事件,因此下面的代码在处理程序需要访问 WPF 控件的地方都使用了 Dispatcher。

WebView2 和 DotNetBrowser 分别在何处保存代理、配置文件和处理程序

设置与处理程序的归属。

API 对照表 

下表将每个 WebView2 API 映射到对应的 DotNetBrowser API,后续各节会逐行说明。

功能WebView2DotNetBrowser
代理AdditionalBrowserArguments 中的 --proxy-serverprofile.Proxy.Settings
身份验证BasicAuthenticationRequestedprofile.Network.AuthenticateHandler
客户端证书ClientCertificateRequestedbrowser.SelectCertificateHandler
CookieCoreWebView2Profile.CookieManagerprofile.CookieStore
用户数据CreationProperties.UserDataFolderEngineOptions.UserDataDirectory
配置文件CreationProperties.ProfileNameengine.Profiles.Create()
上下文菜单ContextMenuRequestedbrowser.ShowContextMenuHandler
下载DownloadStartingbrowser.StartDownloadHandler
打印PrintAsync(), PrintToPdfAsync()RequestPrintHandler, PrintHtmlContentHandler, PrintPdfContentHandler
DevToolsOpenDevToolsWindow()browser.DevTools.Show()
远程调试--remote-debugging-portEngineOptions.RemoteDebuggingPort
扩展程序Profile.AddBrowserExtensionAsync()profile.Extensions.Install()
虚拟主机名SetVirtualHostNameToFolderMapping()InterceptRequestHandler

代理 

WebView2 没有代理 API。您需要在创建环境时,通过环境选项传入 Chromium 的 --proxy-server 命令行开关:

WebView2
DotNetBrowser
CoreWebView2EnvironmentOptions options =
    new CoreWebView2EnvironmentOptions();
options.AdditionalBrowserArguments =
    "--proxy-server=127.0.0.1:5075 " +
    "--proxy-bypass-list=<-loopback>";
return await CoreWebView2Environment.CreateAsync(
    null, userDataFolder, options);
IProfile proxied = engine.Profiles.Default;
proxied.Proxy.Settings =
    new CustomProxySettings("127.0.0.1:5075", "<-loopback>");

IProfile direct = engine.Profiles.Create("direct");
direct.Proxy.Settings = new DirectProxySettings();

该开关作用于整个 Browser 进程,因此所有共享同一用户数据文件夹的 WebView2 控件都会通过同一个代理,无论各自属于哪个配置文件。环境创建之后,您也无法再更改代理。Microsoft 的 Browser 标志页面指出,生产环境中的应用不应使用这些标志。

在 DotNetBrowser 中,代理设置属于配置文件,并且可以随时更改;下一个请求就会使用新的设置。要在一个应用程序中使用多个代理,请为每个代理创建一个配置文件。除了自定义代理,您还可以选择 DirectProxySettings、AutoDetectProxySettings、SystemProxySettings(默认值),或者带有代理自动配置(PAC)脚本 URL 的 PacProxySettings。

CustomProxySettings 的第二个参数是绕过列表(不经过代理的地址)。默认情况下,两个库中的 Chromium 都会直接发送本地地址的请求,而不经过代理。<-loopback> 条目会取消这一例外,这就是两个代码片段都包含它的原因。请参阅代理指南。

身份验证 

在 WebView2 中,BasicAuthenticationRequested 事件负责处理身份验证质询,即服务器或代理发出的用户名和密码请求。它涵盖 Basic、Digest 和 NTLM。在 DotNetBrowser 中,配置文件的网络对象上的 AuthenticateHandler 承担同样的工作:

WebView2
DotNetBrowser
core.BasicAuthenticationRequested += (sender, args) =>
{
    args.Response.UserName = "demo";
    args.Response.Password = "secret";
};
profile.Network.AuthenticateHandler =
    new Handler<AuthenticateParameters, AuthenticateResponse>(p =>
    {
        if (!p.IsProxy && p.Realm == "demo")
        {
            return AuthenticateResponse.Continue("demo", "secret");
        }
        return AuthenticateResponse.Cancel();
    });

参数中包含领域(realm)、方案、主机,以及 IsProxy(对于代理质询,其值为 true)。领域是服务器为受保护区域指定的名称;方案是 Basic、Digest 或 NTLM。

如果没有处理程序,DotNetBrowser 会显示服务器的 401(Unauthorized)页面;根据 Microsoft 的文档,WebView2 会显示自己的登录对话框。如果您需要一个对话框,WPF 包中提供了一个:DotNetBrowser.Wpf.Dialogs.DefaultAuthenticationHandler。

对于客户端证书,请将 ClientCertificateRequested 替换为 browser.SelectCertificateHandler。它会收到可用的证书,并通过 SelectCertificateResponse.Select() 返回其中一个。您也可以传入从 PFX 文件加载的证书,即使该证书并未安装。PFX 文件包含证书及其私钥。对于服务器证书错误,ServerCertificateErrorDetected 对应网络对象的 VerifyCertificateHandler。请参阅身份验证指南和网络指南。

两个库都为每个配置文件提供一个 Cookie 存储。在 WebView2 中是 CoreWebView2Profile.CookieManager;在 DotNetBrowser 中是 profile.CookieStore:

WebView2
DotNetBrowser
CoreWebView2CookieManager cookies = core.Profile.CookieManager;
CoreWebView2Cookie extra =
    cookies.CreateCookie("extra", "1", "localhost", "/");
extra.Expires = DateTime.UtcNow.AddDays(30);
cookies.AddOrUpdateCookie(extra);
return await cookies.GetCookiesAsync("http://localhost:5065/");
Cookie extra = new Cookie.Builder("localhost")
{
    Name = "extra",
    Value = "1",
    Path = "/",
    ExpirationTime = DateTime.UtcNow.AddDays(30)
}.Build();
await profile.CookieStore.SetCookie(extra);
profile.CookieStore.Flush();
IEnumerable<Cookie> all = await profile.CookieStore
    .GetAllCookies("http://localhost:5065/");

DotNetBrowser 有两点不同。第一,您使用 Cookie.Builder 构建 Cookie:它接受域名,由您设置名称、值、路径、过期时间,以及 secure 和 HTTP-only 标志。第二,更改 Cookie 之后,请调用 Flush() 将其写入磁盘。

如果您的 C# 文件还导入了 System.Net,请为 Cookie 添加 using 别名,因为 System.Net 中也有一个 Cookie 类。

在两个库中,没有过期时间的 Cookie 都是会话 Cookie,不会在重启后保留。请参阅 Cookie 指南。

在两个库中,持久化数据都位于用户数据文件夹中。在 WebView2 中,您可以像上面的代理代码片段那样将其传给 CoreWebView2Environment.CreateAsync(),或者在控件的 CreationProperties 中设置。在 DotNetBrowser 中,您在 Engine 选项中设置 UserDataDirectory:

DotNetBrowser
EngineOptions options = new EngineOptions.Builder
{
    RenderingMode = RenderingMode.HardwareAccelerated,
    UserDataDirectory = userDataDir,
    IncognitoEnabled = incognito
}.Build();
return EngineFactory.CreateAsync(options);

如果不设置该目录,DotNetBrowser 会使用一个临时目录。一个目录同一时间只能供一个 Engine 使用,即使第二个 Engine 运行在另一个进程中也是如此。

要清除数据,WebView2 提供了接受一组数据类型的 Profile.ClearBrowsingDataAsync()。在 DotNetBrowser 中,可以通过 CookieStore、HttpCache 和 HttpAuthCache 逐个清除各存储,或者调用 profile.ClearAllData()。请参阅缓存指南。

要让 Cookie、历史记录和站点数据只保存在内存中,请像上面的代码片段那样在 Engine 选项中设置 IncognitoEnabled。该选项会使默认配置文件成为 incognito(隐身)配置文件;下一节将介绍如何创建其他隐身配置文件。

配置文件 

在 WebView2 中,您在创建控件时选择配置文件:在创建属性中设置 ProfileName,并可选择设置 IsInPrivateModeEnabled。在 DotNetBrowser 中,配置文件是从 Engine 创建的对象,每个配置文件创建自己的 Browser:

WebView2
DotNetBrowser
WebView2 view = new WebView2();
view.CreationProperties = new CoreWebView2CreationProperties
{
    UserDataFolder = userDataFolder,
    ProfileName = profileName,
    IsInPrivateModeEnabled = inPrivate
};
IProfile alpha = engine.Profiles.Create("alpha");
IProfile beta = engine.Profiles.Create("beta");
IProfile secret =
    engine.Profiles.Create("secret", ProfileType.Incognito);
IBrowser browser = alpha.CreateBrowser();
bool removed = engine.Profiles.Remove(beta);

在 DotNetBrowser 中,每个配置文件将其数据保存在用户数据目录内各自的文件夹中。除了本文开头列出的设置之外,它还有自己的权限、缩放级别和拼写检查器。

删除配置文件的方式在两个库中有所不同。CoreWebView2Profile.Delete() 会关闭使用该配置文件的所有 WebView2 控件,并移除该配置文件的文件夹。Delete() 对默认配置文件同样有效。

Microsoft 的文档称该文件夹会在浏览器进程退出时移除;在我们的测试中,它在几秒钟内就消失了。即便如此,创建同名配置文件仍然失败:错误信息称该文件夹仍在删除中。因此,不要指望在应用程序的同一次运行中重用已删除配置文件的名称。

在 DotNetBrowser 中,engine.Profiles.Remove() 会立即处置该配置文件的 Browser,并在片刻之后删除其文件夹。默认配置文件无法移除:Remove() 返回 false。您可以立即创建同名配置文件,它会以空数据开始。请参阅配置文件指南。

上下文菜单 

WebView2 会引发带有默认菜单项的 ContextMenuRequested。您可以向列表中添加自己的菜单项,或者设置 Handled 并显示自己基于 MenuItems 构建的菜单。如果您异步构建该菜单,请调用 GetDeferral(),让 WebView2 等待您的结果。DotNetBrowser 只在 BrowserView 内部显示其默认菜单。要用自己的菜单替换它,请设置 ShowContextMenuHandler 并自行构建菜单:

WebView2
DotNetBrowser
core.ContextMenuRequested += (sender, args) =>
{
    CoreWebView2ContextMenuItem item =
        core.Environment.CreateContextMenuItem(
            "Say hi", null,
            CoreWebView2ContextMenuItemKind.Command);
    item.CustomItemSelected += (s, e) => onSelected();
    args.MenuItems.Insert(0, item);
};
browser.ShowContextMenuHandler =
    new AsyncHandler<ShowContextMenuParameters,
                     ShowContextMenuResponse>(p =>
    {
        TaskCompletionSource<ShowContextMenuResponse> tcs =
            new TaskCompletionSource<ShowContextMenuResponse>(
                TaskCreationOptions.RunContinuationsAsynchronously);
        view.Dispatcher.BeginInvoke(new Action(() =>
        {
            ContextMenu menu = new ContextMenu();
            menu.Items.Add(Item("Say hi", sayHi));
            menu.Closed += (s, e) =>
                tcs.TrySetResult(ShowContextMenuResponse.Close());
            menu.PlacementTarget = view;
            menu.Placement = PlacementMode.Relative;
            menu.HorizontalOffset = p.Location.X;
            menu.VerticalOffset = p.Location.Y;
            menu.IsOpen = true;
        }));
        return tcs.Task;
    });

这里的 Item() 是一个小的辅助方法,用于创建带有点击处理程序的 MenuItem。ShowContextMenuHandler 是一个 AsyncHandler:它通过 Dispatcher 在 UI 线程上构建菜单,并返回一个在菜单关闭时完成的任务。若不想显示任何上下文菜单,请直接返回 ShowContextMenuResponse.Close()。

与 WebView2 的事件参数不同,ShowContextMenuParameters 不包含默认菜单项。如果您的菜单需要“后退”或“重新加载”,请自行添加。参数中包含的是上下文:链接、媒体、选中的文本和拼写检查建议。详情请参阅上下文菜单指南。

下载 

在 WebView2 中,DownloadStarting 让您选择文件路径、隐藏默认的下载 UI 或取消下载,CoreWebView2DownloadOperation 负责报告进度。在 DotNetBrowser 中,StartDownloadHandler 做同样的决定,IDownload 对象引发进度事件:

WebView2
DotNetBrowser
core.DownloadStarting += (sender, args) =>
{
    args.ResultFilePath = path;
    args.Handled = true;
    CoreWebView2DownloadOperation download =
        args.DownloadOperation;
    download.StateChanged += (s, e) =>
    {
        if (download.State !=
            CoreWebView2DownloadState.InProgress)
        {
            onFinished(download);
        }
    };
};
browser.StartDownloadHandler =
    new Handler<StartDownloadParameters, StartDownloadResponse>(p =>
    {
        IDownload download = p.Download;
        download.Finished += (s, e) => finished(download);
        string name = download.Info.SuggestedFileName;
        return StartDownloadResponse.DownloadTo(
            Path.Combine(folder, Path.GetFileName(name)));
    });

DownloadTo() 接受文件的绝对路径。IDownload 还通过 Updated 报告进度,您可以暂停、恢复或取消下载。

默认行为有所不同。WebView2 会下载到配置文件的下载文件夹并显示自己的 UI。如果没有处理程序,DotNetBrowser 会取消所有下载。BrowserView 会安装自己的处理程序,显示“另存为”对话框。更多内容请参阅下载指南。

打印 

在 WebView2 中,您在需要打印时调用一个方法。PrintAsync() 接受打印设置,PrintToPdfAsync() 或 PrintToPdfStreamAsync() 生成 PDF,ShowPrintUI() 打开 Browser 或系统的打印对话框。

在 DotNetBrowser 中,您通过 browser.MainFrame.Print() 开始打印。每个打印请求(包括这一调用)都会经过 RequestPrintHandler,您可以在其中取消打印、显示打印预览,或者通过 RequestPrintResponse.Print() 继续。如果继续,接下来会运行 PrintHtmlContentHandler,您在其中选择打印机及其设置:

WebView2
DotNetBrowser
CoreWebView2PrintSettings settings =
    core.Environment.CreatePrintSettings();
settings.PageRanges = pages;
return await core.PrintToPdfAsync(path, settings);
browser.RequestPrintHandler =
    new Handler<RequestPrintParameters, RequestPrintResponse>(p =>
        RequestPrintResponse.Print());
browser.PrintHtmlContentHandler =
    new Handler<PrintHtmlContentParameters,
                PrintHtmlContentResponse>(p =>
    {
        PdfPrinter<PdfPrinter.IHtmlSettings> pdf = p.Printers.Pdf;
        pdf.PrintJob.Settings.Apply(s =>
        {
            s.PdfFilePath = pdfPath;
            s.PageRanges = new[] { pages };
        });
        pdf.PrintJob.PrintCompleted += (s, e) =>
            done(e.IsCompletedSuccessfully);
        return PrintHtmlContentResponse.Print(pdf);
    });

打印机集合包含系统默认打印机、内置的 PDF 打印机,以及按设备名称列出的每一台已安装的打印机。每台打印机都会报告其功能,例如纸张尺寸、颜色模式和双面打印模式,因此您可以在应用设置之前先行检查。PrintCompleted 会在文件写入完成时通知您。

如果不设置 RequestPrintHandler,DotNetBrowser 会取消所有打印请求。在 BrowserView 内部,默认处理程序会改为显示打印预览。

在 WebView2 中,pages 是一个字符串,例如 "2-3";在 DotNetBrowser 中,它是一个 PageRange,例如 new PageRange(2, 3)。WPF 有自己的 System.Windows.Controls.PageRange,因此请添加 using 别名。Apply() 是一个扩展方法,因此请添加 using DotNetBrowser.Print.Settings;。

DotNetBrowser 没有输出到流的对应方式:PDF 打印机将写入 PdfFilePath。当 Browser 显示的是 PDF 文档而非网页时,运行的是 PrintPdfContentHandler 而非 PrintHtmlContentHandler。

DotNetBrowser 没有与 ShowPrintUI(System)(系统打印对话框)等效的功能;RequestPrintResponse.ShowPrintPreview() 改为显示 Chromium 的打印预览。反过来,WebView2 没有针对 window.print() 的事件:页面会弹出 Browser 的打印对话框,而您的代码不会收到任何通知。在 DotNetBrowser 中,window.print() 与其他打印请求一样经过 RequestPrintHandler,因此页面无法擅自弹出打印对话框。请参阅打印指南。

DevTools 

在两个库中,打开 DevTools 都只需一行代码:WebView2 中是 OpenDevToolsWindow(),DotNetBrowser 中是 browser.DevTools.Show()。远程调试允许另一个 Browser 或自动化工具通过端口检查您的 Browser。它在 WebView2 中需要 --remote-debugging-port 开关,在 DotNetBrowser 中需要 RemoteDebuggingPort Engine 选项:

WebView2
DotNetBrowser
CoreWebView2EnvironmentOptions options =
    new CoreWebView2EnvironmentOptions();
options.AdditionalBrowserArguments =
    "--remote-debugging-port=9334";
return await CoreWebView2Environment.CreateAsync(
    null, userDataFolder, options);
// ...
core.OpenDevToolsWindow();
EngineOptions options = new EngineOptions.Builder
{
    RenderingMode = RenderingMode.HardwareAccelerated,
    RemoteDebuggingPort = 9334,
    ChromiumSwitches =
    {
        "--remote-allow-origins=http://localhost:9334"
    }
}.Build();
IEngine engine = await EngineFactory.CreateAsync(options);
IBrowser browser = engine.CreateBrowser();
browser.DevTools.Show();

Show() 在单独的窗口中打开 DevTools,Hide() 将其关闭。

browser.DevTools.RemoteDebuggingUrl 提供该 Browser 的 DevTools 页面地址。请在另一个 IBrowser 中加载该页面,而不是在 Firefox 或其他任何 Browser 中:Engine 指南警告说,在其他地方加载它可能会导致 DevTools 服务器崩溃。代码片段中的 --remote-allow-origins 开关允许该页面建立连接;没有它,页面可以打开,但会报告连接已关闭。

WebView2 的 CallDevToolsProtocolMethodAsync() 没有直接的等效项。远程调试端口公开了同样的 DevTools 协议。DevTools 协议示例展示了如何通过它使用 Puppeteer、Playwright 和 Selenium 控制 Browser。请参阅 Browser 指南的 DevTools 一节。

Chrome 扩展程序 

两个库都按配置文件安装扩展程序,但来源不同。WebView2 加载未打包的扩展程序文件夹,并且只有在环境选项中启用了扩展程序时才能加载。DotNetBrowser 安装 CRX 文件,即 Chrome 应用商店使用的打包格式。如果您允许,它还可以让用户从应用商店安装扩展程序:

WebView2
DotNetBrowser
CoreWebView2EnvironmentOptions options =
    new CoreWebView2EnvironmentOptions();
options.AreBrowserExtensionsEnabled = true;
CoreWebView2Environment env =
    await CoreWebView2Environment.CreateAsync(
        null, userDataFolder, options);
await view.EnsureCoreWebView2Async(env);
CoreWebView2Profile profile = view.CoreWebView2.Profile;
return await profile.AddBrowserExtensionAsync(extensionFolder);
IExtension extension =
    await profile.Extensions.Install(Path.GetFullPath(crxPath));
IExtensionAction action = extension.GetAction(browser);
action?.Click();
await extension.Uninstall();

Install() 只接受 CRX 文件;传入未打包的文件夹会抛出异常。来自应用商店的安装会经过 InstallExtensionHandler,您可以在其中允许或拒绝。

WebView2 没有针对扩展程序工具栏按钮的 API。在 DotNetBrowser 中,extension.GetAction(browser) 提供图标、徽章和工具提示,Click() 打开扩展程序的弹出窗口。当 Browser 位于 BrowserView 中时,弹出窗口会显示为单独的窗口。

反过来,WebView2 可以通过 EnableAsync(false) 禁用扩展程序,而 DotNetBrowser 只能卸载它。DotNetBrowser 并不支持所有扩展程序 API;扩展程序指南列出了例外情况。

没有一一对应等效项的功能 

有三项 WebView2 功能对应的不是形式相同的方法,而是另一套机制。

同步宿主对象 

第一部分展示了如何将 .NET 对象注入 JavaScript。在 WebView2 中,同步调用通过 chrome.webview.hostObjects.sync 进行。从页面的角度看,对注入的 DotNetBrowser 对象的调用本身就是同步的:bridge.Add(2, 3) 返回 5,而不是一个 promise。因此 hostObjects.sync 没有对应项,也不需要。有一点差异仍然存在:WebView2 在 UI 线程上运行宿主对象的方法;DotNetBrowser 在后台线程上运行它们,因此访问 UI 控件时请使用 Dispatcher。

发送到页面的消息 

WebView2 通过 PostWebMessageAsJson() 向页面发送数据。DotNetBrowser 没有等效项:请改为通过 ExecuteJavaScript() 或 IJsObject 调用 JavaScript 函数。第一部分展示了相反的方向,即如何模拟 WebMessageReceived。

虚拟主机名 

WebView2 通过 SetVirtualHostNameToFolderMapping() 将主机名映射到文件夹。DotNetBrowser 通过第一部分介绍的请求拦截来提供本地文件:为 https URL 方案注册 InterceptRequestHandler,并从文件夹中响应发往您的主机名的请求:

WebView2
DotNetBrowser
core.SetVirtualHostNameToFolderMapping(
    "app.example", folder,
    CoreWebView2HostResourceAccessKind.Allow);
IHandler<InterceptRequestParameters, InterceptRequestResponse>
    handler = new Handler<InterceptRequestParameters,
                          InterceptRequestResponse>(p =>
    {
        Uri uri = new Uri(p.UrlRequest.Url);
        if (uri.Host != "app.example")
        {
            return InterceptRequestResponse.Proceed();
        }
        string root = Path.GetFullPath(folder);
        string file = Path.GetFullPath(Path.Combine(root,
            Uri.UnescapeDataString(uri.AbsolutePath)
                .TrimStart('/')));
        bool found = file.StartsWith(
                         root + Path.DirectorySeparatorChar,
                         StringComparison.OrdinalIgnoreCase)
                     && File.Exists(file);
        UrlRequestJobOptions options = new UrlRequestJobOptions
        {
            HttpStatusCode = found
                ? HttpStatusCode.OK
                : HttpStatusCode.NotFound,
            Headers = new List<HttpHeader>
            {
                new HttpHeader("Content-Type", MimeOf(file))
            }
        };
        UrlRequestJob job =
            p.Network.CreateUrlRequestJob(p.UrlRequest, options);
        if (found)
        {
            job.Write(File.ReadAllBytes(file));
        }
        job.Complete();
        return InterceptRequestResponse.Intercept(job);
    });
EngineOptions engineOptions = new EngineOptions.Builder
{
    RenderingMode = RenderingMode.HardwareAccelerated,
    Schemes = { { Scheme.Https, handler } }
}.Build();

MimeOf() 将文件扩展名映射到内容类型。StartsWith() 检查会拒绝文件夹之外的所有路径,包括 https://app.example/C:/Windows/win.ini 之类的越界路径。被拦截的 URL 方案集合在创建 Engine 时就已固定。每个配置文件的每个 HTTPS 请求都会经过该处理程序,因此对于您不提供服务的主机,请尽早返回 Proceed()。

总结 

本指南与第一部分一起,将大多数应用程序使用的 WebView2 API 映射到了对应的 DotNetBrowser 替代方案。如果您的应用程序使用了两部分都未涉及的功能,请发送邮件至 ,我们将为您提供帮助。

检验您自己的应用程序最快的方式是申请免费评估许可证:

Spinner

发送中。。。

抱歉,发送中断

请再次尝试,如果问题仍然存在,请联系我们 info@teamdev.com.

阅读并同意条款以继续。

您的个人 DotNetBrowser 试用密钥和快速入门指南将在几分钟内发送至您的电子邮箱。