CefSharp
是围绕Chromium嵌入式框架( Chromium Embedded Framework,CEF)的.Net包装器。CEF是一个基于Google Chromium项目的开源项目。与Chromium项目本身(主要专注于Google Chrome应用程序开发)不同,CEF专注于促进第三方应用程序中的嵌入式浏览器用例。 CEF
基于多进程Chromium Content API,因此,当前仅存在Chromium的部分功能。例如,对扩展的支持是有限的,仅实现了一部分Extension API
。
CefSharp
提供三种不同的类型:WinForms
,WPF
和OffScreen
。在WPF
与OffScreen
版本使用的OffScreen Rendering(OSR)
渲染模式。在OSR
模式中,每个帧被渲染到缓冲器,然后或者在屏幕上绘制作为的情况下WPF
或可作为Bitmap
在OffScreen
。所有版本都使用CefSharp
和CefSharp.Core
库,因此API
在这三种风格中,大部分使用的库都完全相同。这减少了代码重复并降低了添加新功能的维护负担,唯一的缺点是该WPF
版本并不像它可能的那样友好(您可以将其ChromiumWebBrowser
归类,并在应用程序中实现所需的任何缺少的部分)。您也可以托管WinForms
WPF
使用中的版本号WindowsFormsHost
,可能需要绕过该WPF
版本的某些限制(CEF
尚未在OSR
模式中实现完全的触摸屏支持,在上存在一个开放问题CEF Issue Tracker
,如果您需要这样做,请参与其中)。
发行说明
有关每个版本的发行说明,请访问https://github.com/cefsharp/CefSharp/releases,如果您有问题或对更改有所好奇,请抽出时间阅读它们。如果遇到问题,请查看“已知问题”部分,通常会有一些说明包含有关发行版的有用信息。
软件需求
CefSharp
使用Visual C ++(VC++
)与本机C ++ API交互,因此它只能在Windows上运行。(没有Windows APP Store版本)。CefSharp
在每个第二Chromium
版本上发布版本,例如47、49、51。每个CefSharp
版本都有其自己的分支,有关每个分支的详细信息和要求,请参见https://github.com/cefsharp/CefSharp#release-branches。Google
最近去除了对较早的操作系统的支持,例如Windows XP,Vista及其服务器版本。如果您要求您的应用程序在这些操作系统上运行,请查看发行版以获取更多详细信息https://github.com/cefsharp/CefSharp/releases
CefSharp要求:
- 微软
.Net 4.5.2
或更高 - Microsoft Visual C ++可再发行组件包(
x86
或者x64
取决于您的应用程序)。要确定Visual C++
您需要哪个版本,请参见https://github.com/cefsharp/CefSharp#release-branches
笔记:
- 您可以将其
VC++ Redist Dll's
与您的应用程序打包,有关详细信息,请参见https://github.com/cefsharp/CefSharp/wiki/Frequently-asked-questions#Inclusion_vcredist - 可在此处获得用于Visual Studio 2012和Visual Studio 2013的Microsoft .NET Framework 4.5.2开发人员包:https : //www.microsoft.com/en-gb/download/details.aspx?id=42637
任何CPU支持
较新的版本现在支持定位AnyCPU
,有关如何实现此功能的详细信息,请参见https://github.com/cefsharp/CefSharp/issues/1714。可以使用相同的技术将libcef.dll
等等移动到磁盘上的其他文件夹或公共位置。
需要知道/限制
- 要指定
CachePath用于
Cookie的持久性,如保存密码等,需要默认指定In-Memory Cache
(类似于Incogneto
)。有关使用以下内容初始化CEF的示例,请参见下面的“初始化和关闭”部分CachePath
。 - 您可以使用Network.clearBrowserCache清除磁盘缓存,请参阅#3158以获得有关执行
DevTools
命令的详细信息。 - 在中
app.manifest
为您的应用添加,以获取HiDPI
支持,应用兼容性(在上运行Windows 10
)和中的工具提示WinForms
。这些示例包含示例app.manifest
文件。这非常重要(CEF Forum • [SOLVED] Check failed: fallback_available) - 类似于日志中的错误
Check failed: fallback_available == base::win::GetVersion() > base::win::VERSION_WIN8 (1 vs. 0)
是您的应用程序需要app.manifest
使用相关compatibility
条目的标志。 CEF
Initialized/Shutdown
每个进程只能有一次,请参阅以下部分以获取完整详细信息,这是对基础Chromium
框架的限制。Minimal
同时为版本WPF
和WinForms
版本添加了设计师支持57.0.0
,有关详细信息,请参见#1989(WPF)和#1946(WinForms)。设计人员的支持需要您定位x86
(理论上AnyCPU
也应该有效,但尚未经过测试)。Visual Studio
是,x86
因此您无法使用该x64
版本。对于较旧的版本,不提供设计人员支持(设计人员将引发异常)。- 仅在默认的AppDomain中运行,有一些变通办法,例如https://github.com/flole/CefSharp.AppDomain和 https://github.com/stever/AppHostCefSharp
- 由于资源有限,一次仅支持一个版本,请参阅https://github.com/cefsharp/CefSharp#release-branches以查看哪个版本为最新版本。如果您使用的是旧版本,则遇到问题,则必须升级到当前支持的版本。
- 仅在
Windows
没有App Store
版本上运行。 - 支持.Net Core,但需要其他步骤,请参见https://github.com/cefsharp/CefSharp.MinimalExample#net-core-support
Sandboxing
尚未实现,因为直接在上添加支持在技术上是不可行的CefSharp
,有关详细信息,请参见#697。WinForms
屏幕键盘上的屏幕可能会受益于disable-usb-keyboard-detect
命令行参数 https://github.com/cefsharp/CefSharp/issues/1691#issuecomment-323603277WPF
High DPI
建议具有监视器的用户.Net 4.6
在其目标计算机上安装,因为其中存在一个错误,该错误.Net Framework
可能会导致MILERR_WIN32ERROR Exception
参见#2035的详细信息CEF
当前不支持PNaCl
加载所需的内容,Google Earth
请参见CEF Forum • Google Earth Web
例子
CefSharp
源代码包含的许多不同的特征的实施例。还有一个MinimalExample
项目使用最新的Nuget
软件包提供非常简单的Browser
实现。这MinimalExample
是入门的最佳位置,下载此项目并使其运行以作为基础参考,以确保一切都在您的系统上正常工作。
https://github.com/cefsharp/CefSharp.MinimalExample
记录中
默认情况下CEF
,在应用程序的执行文件夹(例如)中维护其自己的日志文件('Debug.log')bin
。要禁用日志记录更改settings.LogSeverity
,并更改文件名/路径,请使用settings.LogFile
。
调试问题时,首先要检查的地方是此日志文件,因为它包含低级Chromium
消息。如果您看到错误或警告,请搜索CEF Forum • Index page和https://bitbucket.org/chromiumembedded/cef/issues?status=new&status=open
工艺流程
CEF
使用多进程运行。处理窗口创建,绘画和网络访问的主进程称为browser
进程。通常,此过程与主机应用程序相同,并且大多数应用程序逻辑将在浏览器进程中运行。闪烁呈现和JavaScript执行在单独的render
过程中进行。一些应用程序逻辑(例如JavaScript绑定)也将在渲染过程中运行。默认进程模型将为每个唯一的来源(方案+域)生成一个新的渲染过程。将根据需要生成其他进程,例如处理插件(如Flash)的“插件”进程和处理加速合成的“ gpu”进程。
默认情况CefSharp
下,该render
进程的默认实现称为CefSharp.BrowserSubProcess.exe
。如上所述,将多次产生此过程以表示单独的过程。从版本开始,51.0.0
可以提供自己的自定义BrowserSubProcess
,因为可执行文件现在是基础VC++
实现的非常简单的包装。
chromiumembedded / cef / issues / #2498 - Add support for site-per-process — Bitbucket包含有关当前默认流程模型的详细信息。
线程数
CEF使用多个线程进行不同级别的处理。例如browser
,该过程包含以下通常引用的线程:
- UI线程:是浏览器过程中的主线程。默认情况下
CefSharp
使用,setting.MultiThreadedMessageLoop = true
因此该CEF UI
线程不同于您的主应用程序线程 - IO线程:在浏览器进程中用于处理IPC和网络消息
- FILE线程:在浏览器进程中用于与文件系统进行交互
- RENDERER线程:是渲染器过程中的主线程
初始化和关闭
Initialize
每个进程(应用程序)只能调用一次。可以运行您的应用程序的多个实例,您需要CachePath
为每个实例提供唯一的实例,请参阅CefSettings
下文。
有关如何在运行时更改设置,隔离浏览器实例,为不同实例设置不同的缓存路径的详细信息,请参见请求上下文(浏览器隔离)。
重要的是要注意,有必要初始化基础CEF
库。这可以通过显式和隐式两种方式之一来实现。创建新实例时ChromiumWebBrowser
,它将检查CEF是否已初始化,如果尚未初始化,请使用默认值为您初始化。对于那些希望指定一些自定义设置的用户,您可以CEF
如下所示显式初始化自己:
public static void Init()
{var settings = new CefSettings();// Increase the log severity so CEF outputs detailed information, useful for debuggingsettings.LogSeverity = LogSeverity.Verbose;// By default CEF uses an in memory cache, to save cached data e.g. to persist cookies you need to specify a cache path// NOTE: The executing user must have sufficient privileges to write to this folder.settings.CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache");Cef.Initialize(settings);
}
对于Cef.Shutdown,ChromiumWebBrowser(
ChromiumWebBrowser is the WinForms web browser control,ChrominuWebBrowser是Winform的浏览器控件)
将钩住相关Application Exit事件的WinForms和WPF实例,并且默认情况下调用Cef.Shutdown()。设置CefSharpSettings.ShutdownOnExit
=false; 禁用此行为。在ChromiumWebBrowser
创建事件的第一个实例之前,需要设置此值,因为事件处理程序已挂接到ChromiumWebBrowser
该类的静态构造函数中。
重要的是要注意CEF Initialize
/Shutdown
必须在主应用程序线程(通常是UI线程)上调用。如果您在不同的线程上调用它们,则您的应用程序将挂起。
一个使用Initialize
/Shutdown
手动调用/的示例,WinForms
可以将该示例应用于WPF
使用该CefSharp.OffScreen
包的控制台应用程序(该OffScreen
示例位于https://github.com/cefsharp/CefSharp.MinimalExample是一个很好的起点,其中也有一个示例)主项目存储库,它要高级一些)。
public class Program
{[STAThread]public static void Main(){//For Windows 7 and above, best to include relevant app.manifest entries as wellCef.EnableHighDPISupport();//We're going to manually call Cef.Shutdown below, this maybe required in some complex scenariosCefSharpSettings.ShutdownOnExit = false;//Perform dependency check to make sure all relevant resources are in our output directory.Cef.Initialize(new CefSettings(), performDependencyCheck: true, browserProcessHandler: null);var browser = new BrowserForm();Application.Run(browser);//Shutdown before your application exists or it will hang.Cef.Shutdown();}
}
综上所述
Cef.Initialize
并且Cef.Shutdown
每个进程(应用程序)只能调用一次Initialize,因此仅使用完后CefSharp后才调用Shutdown
。Cef.Initialize
并且Cef.Shutdown
必须在同一线程上调用。Cef.Initialize
如果您创建新ChromiumWebBrowser
实例并且尚未调用,则会为您隐式调用Cef.Initialize
。- 对于WinForms和WPF实例,默认情况下
ChromiumWebBrowser
相关的Application Exit事件被钩住,然后默认调用Cef.Shutdown()
方法处理。设置CefSharpSettings.ShutdownOnExit = false;用于
禁用此行为。在ChromiumWebBrowser
创建事件的第一个实例之前,需要设置此值,因为事件处理程序已挂接到ChromiumWebBrowser
该类的静态构造函数中。 - 在其中
CefSharp.OffScreen
,必须Cef.Shutdown()
在应用程序存在之前显式调用它,否则它将挂起。
CefSettings和BrowserSettings
该CefSettings
结构允许配置应用程序范围的CEF设置。一些通常配置的成员包括:
BrowserSubprocessPath
此路径是
子流程启动的独立可执行文件的路径。通常,无需更改此设置。MultiThreadedMessageLoop 在CefSharp中
默认值为True,尽管可以将其集成CEF
到您的应用程序现有的消息循环中,请参阅下面的MultiThreadedMessageLoop部分。CommandLineArgsDisabled
设置为true可禁用使用标准CEF和Chromium命令行参数配置浏览器进程功能的功能。有关更多信息,请参见“命令行参数”部分。RootCachePath
所有CefSettings.CachePath
和RequestContextSettings.CachePath
值必须具有相同的根目录。如果此值为空且CefSettings.CachePath
非空,则默认为该CefSettings.CachePath
值。如果此值为非空值,那么它必须是绝对路径。非空RootCachePath可以与空CefSettings.CachePath结合使用,在您希望浏览器连接到以“隐身模式”创建的Global RequestContext(默认)的实例以及使用基于磁盘的缓存使用自定义RequestContext创建的实例的情况下, 。有关更多详细信息,请参见下面的RequestContext部分。CachePath
全局浏览器缓存的数据将存储在磁盘上的位置。此值是非空的,那么它必须是绝对路径,该路径必须等于CefSettings.RootCachePath或子目录(如果RootCachePath为空,则默认为该值)。如果该值为空,则将在“隐身模式”下创建浏览器,在该模式下,将使用内存中的缓存进行存储,并且不会将任何数据持久化到磁盘上。如果指定了缓存路径,则诸如localStorage之类的HTML5数据库将仅在会话之间持久存在。可以RequestContext
通过该RequestContextSettings.CachePath
值覆盖单个实例。有关更多详细信息,请参见下面的RequestContext部分。Locale
将传递给Blink的语言环境字符串。如果为空,则将使用默认语言环境“ en-US”。也可以使用“ lang”命令行开关进行配置。更改此项以同时设置上下文菜单语言。LogFile
用于调试日志的目录和文件名。如果为空,将使用默认名称“ debug.log”,并将文件写入应用程序目录。也可以使用“ log-file”命令行开关进行配置。LogSeverity
日志严重性。仅记录此严重级别或更高级别的消息。也可以使用“ log-severity”命令行开关进行配置,其值为“ verbose”,“ info”,“ warning”,“ error”,“ error-report”或“ disable”。ResourcesDirPath
资源目录的标准路径。如果此值为空,则cef.pak和/或devtools_resources.pak文件必须位于模块目录中。也可以使用“ resources-dir-path”命令行开关进行配置。LocalesDirPath
语言环境目录的标准路径。如果此值为空,则语言环境目录必须位于模块目录中。在Mac OS X上,始终从应用程序包Resources目录中加载打包文件的情况下,将忽略此值。也可以使用“ locales-dir-path”命令行开关进行配置。RemoteDebuggingPort
设置为1024到65535之间的值以在指定的端口上启用远程调试。例如,如果指定8080,则远程调试URL将为http:// localhost:8080。可以从任何CEF或Chrome浏览器窗口中远程调试CEF。也可以使用“ remote-debugging-port”命令行开关进行配置。
有许多设置和命令行参数可能会影响CEF的行为方式。这里有些例子:
public static void Init()
{// Specify Global Settings and Command Line Argumentsvar settings = new CefSettings();// By default CEF uses an in memory cache, to save cached data e.g. to persist cookies you need to specify a cache path// NOTE: The executing user must have sufficient privileges to write to this folder.settings.CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache");;// There are many command line arguments that can either be turned on or off// Enable WebRTC settings.CefCommandLineArgs.Add("enable-media-stream");//Disable GPU Accelerationsettings.CefCommandLineArgs.Add("disable-gpu");// Don't use a proxy server, always make direct connections. Overrides any other proxy server flags that are passed.// Slightly improves Cef initialize time as it won't attempt to resolve a proxysettings.CefCommandLineArgs.Add("no-proxy-server"); Cef.Initialize(settings);
}
些设置可以应用于特定ChromiumWebBrowser
实例。如果您正在使用WPF
,则可以BrowserSettings
在中指定XAML
。
var browser = new ChromiumWebBrowser(url)
{BrowserSettings ={DefaultEncoding = "UTF-8",WebGl = CefState.Disabled}
};
<!--xmlns:cefSharpCore="clr-namespace:CefSharp;assembly=CefSharp.Core"-->
<!--xmlns:cefSharpWpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"--><cefSharpWpf:ChromiumWebBrowser><cefSharpWpf:ChromiumWebBrowser.BrowserSettings><cefSharpCore:BrowserSettings DefaultEncoding="UTF-8"/></cefSharpWpf:ChromiumWebBrowser.BrowserSettings>
</cefSharpWpf:ChromiumWebBrowser>
IBrowser,IFrame和IBrowserHost
IBrowser
和IFrame
对象用于将命令发送到浏览器,并在回调方法中返回状态信息。这两个对象都是包装类,是对浏览器的包装。每个IBrowser
对象都有一个代表顶层框架的main IFrame
对象,以及零个或多个sub IFrame
对象。
例如,加载两个HTML<iframe>
的浏览器将具有三个IFrame
对象(顶级框架和两个<iframe>
)。
要将URL加载到浏览器主机中:
browser.MainFrame.LoadUrl(someurl);
CefSharp
提供了许多扩展方法,使执行常见任务更加容易。请参阅参考资料WebBrowserExtensions
,以获取这些方法的来源,并更好地了解如何执行常见任务。
IBrowserHost
代表更底层的浏览器方法。
处理程序
CefSharp
为了方便起见,提供了一些事件,如下所示(有关所有常见事件以及有关其用法的详细信息,请参见IWebBrowser API文档):
- ConsoleMessage
- StatusMessage
- FrameLoadStart
- FrameLoadEnd
- LoadError
- LoadingStateChanged
这些是简单的事件,仅提供一小部分提供的基础处理程序CEF
。这些事件仅在主浏览器中被调用,对于弹出窗口处理,您可以使用IDisplayHandler
和来访问通知ILoadHandler
。
为了确定页面何时完成加载,我建议在FrameLoadEnd上使用LoadingStateChanged。重要的是要记住,完成的加载不同于完成的渲染。当前尚无确定网页何时完成渲染的方法(Flash,动态内容,动画等功能,甚至像移动鼠标或滚动之类的简单任务也将导致渲染新帧)。
IDialogHandler
,IDisplayHandler
,IDownloadHandler
,IContextMenuHandler
,ILifeSpanHandler
,ILoadHandler
和IRequestHandler
是一些更常见的处理程序(参见其余部分的源极/ API DOC)。这些仅以方便的.NET方式包装基础CEF处理程序。例如CEF的CefDownloadHandler
是IDownloadHandler
在CefSharp
。实施这些处理程序将使您能够访问作为CEF框架的基础事件和回调。可以使用回调以异步方式执行许多处理程序的成员。所有处理程序都遵循一致的模式:返回a的处理程序bool
询问您是否要自己处理。如果否,则返回false
默认操作。true
如果您自己处理,请返回。
它们是您实现并分配给ChromiumWebBrowser
实例的基本接口。例如
browser.DownloadHandler = new DownloadHandler();
理想情况下,您应在ChromiumWebBrowser
实例化实例后立即设置处理程序。有关更多详细示例,请参见源代码中的示例项目。当前没有可用的默认实现,因此您必须实现每种方法。(如果您希望提供默认实现,请提交请求请求)。
有关处理程序的一些一般说明
- IDownloadHandler 需要实现,以允许下载文件,进度通知,暂停,取消等
- IRequestHandler用于处理导航,重定向,资源加载通知等
- IDialogHandler用于文件对话框通知
- IDisplayHandler用于地址更改,状态消息,控制台消息,全屏模式更改通知(以及更多)
- ILoadHandler用于加载状态,其中一些映射到事件,将其用于弹出窗口
- ILifeSpanHandler用于处理弹出窗口和关闭事件
- IKeyboardHandler用于键盘事件
- IJsDialogHandler用于javascript消息框/弹出窗口
- IDragHandler用于拖动开始
- IContextMenuHandler用于自定义上下文菜单
- IFocusHandler用于与焦点有关的通知
- IResourceRequestHandlerFactory是CefSharp独有的接口,无需执行IRequestHandler和IResourceRequestHandler就可以拦截资源请求。
- IRenderProcessMessageHandler用于
CefSharp
呈现过程中发送的自定义消息 - IFindHandler用于查找通知
可以使用来修改响应ResponseFilter
。请参阅以下部分。
请求处理Request Handling
CEF支持两种方法来处理应用程序内部的网络请求。
- Scheme Handler方法允许用于靶向特定原点(方案+结构域)的请求的处理程序的注册。
- Request Interception 方法允许在处理应用程序的自由裁量权的任意请求。
使用HTTP(S)方案而不是自定义方案,可以避免一系列潜在的问题。
如果您选择使用自定义方案(比其他任何事情http://
,https://
等),你必须用CEF注册它,这样它会像预期的那样。如果您希望自定义方案的行为类似于HTTP(支持POST请求并强制实施HTTP访问控制(CORS)限制),则应将其注册为“标准”方案。如果您打算对其他方案执行跨域请求或将POST请求发送XMLHttpRequest
到方案处理程序,则应使用HTTP方案而不是自定义方案,以避免潜在的问题。IsSecure
和IsCorsEnabled
参数最近添加。
处理程序可以使用这两个内置的方案(http://
,https://
,等)和自定义方案。使用内置方案时,请为您的应用程序选择一个唯一的域名(如myapp
或internal
)。实现ISchemeHandlerFactory
和IResourceHandler类来处理请求并提供响应数据。有关IResourceHandler的默认实现,请参阅ResourceHandler,它具有许多有用的静态帮助器方法。
Scheme Handler
处理程序可与内置方案(HTTP,HTTPS等)和自定义方案一起使用。使用内置方案时,请为您的应用程序选择一个唯一的域名(如myapp
或internal
)。实现ISchemeHandlerFactory和IResourceHandler类以处理请求并提供响应数据。有关IResourceHandler的默认实现,请参阅ResourceHandler,它具有许多有用的静态帮助器方法。
计划处理程序通过CefSettings.RegisterScheme函数进行注册。例如,您可以为“ localfolder:// cefsharp /”请求注册一个处理程序(下面还有另一个示例,并且在项目源代码中有一些有效的示例):
settings.RegisterScheme(new CefCustomScheme
{SchemeName = "localfolder",DomainName = "cefsharp",SchemeHandlerFactory = new FolderSchemeHandlerFactory(rootFolder: @"..\..\..\..\CefSharp.Example\Resources",hostName: "cefsharp", //Optional param no hostname/domain checking if nulldefaultPage: "home.html") //Optional param will default to index.html
});
该FolderSchemeHandlerFactory是使用一个方案处理从磁盘读取文件的简单默认实现。您可以使用自定义方案(换句话说,您可以以形式提供URL customscheme://folder/yourfile
)或标准方案(https://
,https://
)。
实现自己的工厂的示例可能如下所示:
public class CefSharpSchemeHandlerFactory : ISchemeHandlerFactory
{public const string SchemeName = "custom";private static readonly IDictionary<string, string> ResourceDictionary;static CefSharpSchemeHandlerFactory(){ResourceDictionary = new Dictionary<string, string>{{ "/home.html", Resources.home_html },{ "/bootstrap/bootstrap.min.css", Resources.bootstrap_min_css },{ "/bootstrap/bootstrap.min.js", Resources.bootstrap_min_js },{ "/BindingTest.html", Resources.BindingTest },{ "/ExceptionTest.html", Resources.ExceptionTest },{ "/PopupTest.html", Resources.PopupTest },{ "/SchemeTest.html", Resources.SchemeTest }};}public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request){//Notes:// - The 'host' portion is entirely ignored by this scheme handler.// - If you register a ISchemeHandlerFactory for http/https schemes you should also specify a domain name// - Avoid doing lots of processing in this method as it will affect performance.// - Uses the Default ResourceHandler implementationvar uri = new Uri(request.Url);var fileName = uri.AbsolutePath;string resource;if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource)){var fileExtension = Path.GetExtension(fileName);return ResourceHandler.FromString(resource, , mimeType: Cef.GetMimeType(fileExtension));}return null;}
}
提供的ResourceHandler是IResourceHandler的默认实现,并且包含许多用于创建类的静态帮助器方法。有关更多详细信息,请参见下面的“资源处理程序”部分。
使用静态方法的一些示例是:
ResourceHandler.FromStream(stream, mimeType);
ResourceHandler.FromString(htmlString, includePreamble:true, mimeType:Cef.GetMimeType(fileExtension));
ResourceHandler.FromFilePath("CefSharp.Core.xml", mimeType);
最后,您必须使用以下代码注册此方案处理程序:
public static void Init()
{// Pseudo code; you probably need more in your CefSettings also.var settings = new CefSettings();settings.RegisterScheme(new CefCustomScheme{SchemeName = "custom",SchemeHandlerFactory = new CefSharpSchemeHandlerFactory()});Cef.Initialize(settings);
}
计划注册必须在调用之前进行,这一点很重要Cef.Initialize()
。
请求拦截Request Interception
IResourceRequestHandler.GetResourceRequestHandler支持拦截任意请求。它使用与方案处理程序方法相同的IResourceHandler类。提供的ResourceHandler是IResourceHandler的默认实现,并且包含许多用于创建类的静态帮助器方法。有关更多详细信息,请参见下面的“资源处理程序”部分。
public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
{protected override IResourceHandler GetResourceHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request){//ResourceHandler has many static methods for dealing with Streams, // byte[], files on disk, strings// Alternatively ou can inheir from IResourceHandler and implement// a custom behaviour that suites your requirements.return ResourceHandler.FromString("Welcome to CefSharp!", mimeType: Cef.GetMimeType("html"));}
}public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling){//Only intercept specific Url'sif (request.Url == "http://cefsharp.test/" || request.Url == "https://cefsharp.test/"){return new CustomResourceRequestHandler();}//Default behaviour, url will be loaded normally.return null;}
}browser.RequestHandler = new CustomRequestHandler();
IWebBrowser.RegisterResourceHandler和IWebBrowser.UnRegisterResourceHandler扩展方法提供提供一种简单的方法IResourceHandler对于给定的Url
。
例如,您可以请求一个虚构的URL并提供一个响应,就像该网站是真实的一样。
资源处理程序ResourceHandler
ISchemeHandlerFactory和IResourceRequestHandler.GetResourceHandler使用IResourceHandler接口来表示响应(流+头+状态码,等等)。IResourceHandler的默认实现只是ResourceHandler。
为了方便起见,ResourceHandler包含许多静态方法
- ResourceHandler.FromString
- ResourceHandler.FromByteArray
- ResourceHandler.ForErrorMessage
- ResourceHandler.FromStream
- ResourceHandler.FromFilePath
使用静态方法的一些示例是:
ResourceHandler.FromStream(stream, mimeType);
ResourceHandler.FromString(htmlString, includePreamble:true, mimeType:Cef.GetMimeType(fileExtension));
ResourceHandler.FromFilePath("CefSharp.Core.xml", mimeType);
该源代码包含ISchemeHandlerFactory的详细示例。
实施实例ResourceHandler.ProcessRequestAsync
。如果需要完全控制,则可以实现IResourceHandler,但是在大多数情况下,这不是必需的。
//A simple example of a ResourceHandler that downloads a file from the internet.
public class ExampleResourceHandler : ResourceHandler
{public override CefReturnValue ProcessRequestAsync(IRequest request, ICallback callback){Task.Run(() =>{using (callback){var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://samples.mplayerhq.hu/SWF/zeldaADPCM5bit.swf");var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();// Get the stream associated with the response.var receiveStream = httpWebResponse.GetResponseStream();var mime = httpWebResponse.ContentType;var stream = new MemoryStream();receiveStream.CopyTo(stream);httpWebResponse.Close();//Reset the stream position to 0 so the stream can be copied into the underlying unmanaged bufferstream.Position = 0;//Populate the response valuesResponseLength = stream.Length;MimeType = mime;StatusCode = (int)HttpStatusCode.OK;Stream = stream;callback.Continue();}});return CefReturnValue.ContinueAsync;}
}
响应过滤Response Filtering
IResourceRequestHandler.GetResourceResponseFilter()支持过滤响应请求而接收的数据。您可以检索原始响应数据,也可以将数据追加到响应中,例如在文件末尾注入一些自定义CSS。您可以根据需要重写响应。可用于接收任何请求的响应,即AJAX(XHRHttpRequest
)/ POST / GET。
将响应作为UTF8
字符串获取的基本示例是:
public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
{private readonly System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();protected override IResponseFilter GetResourceResponseFilter(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response){return new CefSharp.ResponseFilter.StreamResponseFilter(memoryStream);}protected override void OnResourceLoadComplete(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength){//You can now get the data from the streamvar bytes = memoryStream.ToArray();if (response.Charset == "utf-8"){var str = System.Text.Encoding.UTF8.GetString(bytes);}else{//Deal with different encoding here}}
}public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling){//Only intercept specific Url'sif (request.Url == "http://cefsharp.github.io/" || request.Url == "https://cefsharp.github.io/"){return new CustomResourceRequestHandler();}//Default behaviour, url will be loaded normally.return null;}
}browser.RequestHandler = new CustomRequestHandler();
当前,StreamResponseFilter是框架中提供的唯一过滤器。
将dataIn复制到dataOut的简单响应过滤器。数据以块的形式进行流传输,通常大小为64kb。
/// <summary>
/// PassThruResponseFilter - copies all data from DataIn to DataOut.
/// Upstream documentation link
/// https://magpcss.org/ceforum/apidocs3/projects/(default)/CefResponseFilter.html#Filter(void*,size_t,size_t&,void*,size_t,size_t&)
/// </summary>
public class PassThruResponseFilter : IResponseFilter
{bool IResponseFilter.InitFilter(){return true;}FilterStatus IResponseFilter.Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten){if (dataIn == null){dataInRead = 0;dataOutWritten = 0;return FilterStatus.Done;}//Calculate how much data we can read, in some instances dataIn.Length is//greater than dataOut.LengthdataInRead = Math.Min(dataIn.Length, dataOut.Length);dataOutWritten = dataInRead;var readBytes = new byte[dataInRead];dataIn.Read(readBytes, 0, readBytes.Length);dataOut.Write(readBytes, 0, readBytes.Length);//If we read less than the total amount avaliable then we need//return FilterStatus.NeedMoreData so we can then write the restif (dataInRead < dataIn.Length){return FilterStatus.NeedMoreData;}return FilterStatus.Done;}public void Dispose(){}
}
有关IResourceFilter的CefSharp.Example
其他示例实现,请参见源代码中的项目。该功能实现起来非常复杂。提出任何问题之前,请确保您已阅读并调试了现有示例。
从磁盘/数据库/嵌入式资源/流中加载HTML / CSS / JavaScript / etc
CefSharp.WebBrowserExtensions类中提供了一些扩展方法,以方便使用。
//Load a data encoded Uri
//NOTE There are limits to the size of a Data Uri, use the overload that takes a Url if you need to load large files
LoadHtml(this IWebBrowser browser, string html, bool base64Encode = false);//Register a ResourceHandler with the `ResourceRequestHandlerFactory` and calls browser.Load
LoadHtml(this IWebBrowser browser, string html, string url)`;//Register a resource handler with the `ResourceRequestHandlerFactory`
RegisterResourceHandler(this IWebBrowser browser, string url, Stream stream, string mimeType =
ResourceHandler.DefaultMimeType);//Unregister a resource handler with the `ResourceRequestHandlerFactory`
UnRegisterResourceHandler(this IWebBrowser browser, string url);//In `WinForms` you can pass a `HtmlString` directly into the constructor and have it load as a Data Uri
new ChromiumWebBrowser((CefSharp.Web.HtmlString)"<html><body style='background:red;'>Data Uri Test</body></html>");
有关data:
包含URI本身中的请求正文的已编码URI的更多信息,请参见Data URLs - HTTP | MDN
自己生成Data URI
将类似于:
const string html = "<html><head><title>Test</title></head><body><h1>Html Encoded in URL!</h1></body></html>";
var base64EncodedHtml = Convert.ToBase64String(Encoding.UTF8.GetBytes(html));
browser.Load("data:text/html;base64," + base64EncodedHtml);
文件URI(file:///)
我强烈建议不要file:///
从本地磁盘加载时使用。应用了不同的安全限制,并且存在许多限制。我建议使用Scheme
处理程序或实现自己的处理程序IResourceRequestHandlerFactory
。(data:
特别是对于OffScreen
项目而言,加载编码的URI也非常方便)。
如果您选择忽略此建议,则必须解决file:///
自己遇到的任何问题。ceforum
是最好的资源。
代理解析
有两个用于配置代理服务器的选项。
- CEF使用与Google Chrome相同的命令行标志。
- 参见chromiumembedded / cef / wiki / GeneralUsage — Bitbucket
- 如果您使用命令行args指定代理,则您将无法在运行时使用对其进行更改
SetPreference
。 - 所有
ChromiumWebBrowser
实例将共享同一个代理
- 可以使用IRequestContext.SetPreference在运行时设置/更改代理设置。
- IRequestContext.SetPreference必须在
CEF UI
线程上调用。使用Cef.UIThreadTaskFactory在上生成任务CEF UI Thread
。IRequestContextHandler
方法已经在CEF UI
线程上调用,因此您可以SetPreference
直接调用。 - 可以逐个指定代理设置,
Request Context
从而使您可以ChromiumWebBrowser
使用不同的代理来拥有不同的实例。 - 阅读下面的“请求上下文”部分,以获取更多详细信息和基本代码示例。
- IRequestContext.SetPreference必须在
如果代理要求身份验证,则将使用值为的IRequestHandler.GetAuthCredentials()回调执行以检索用户名和密码。isProxy
true
在c# - CefSharp 3 set proxy at Runtime - Stack Overflow上可以找到使用Preferences
in设置代理的一些其他示例。CefSharp
请求上下文(浏览器隔离)
隔离浏览器实例的方法,包括提供自定义缓存路径,不同的代理设置,不同的Cookie管理器以及许多其他功能RequestContext
。在较新的版本中,PPAPI插件的加载是在该RequestContext
级别上进行的。在CEF
条款的底层类是CefRequestContext
。
以下是一些关键点:
- 默认情况下,将使用全局请求上下文(所有浏览器共享的设置)
- 您可以在运行时使用以下命令更改某些(不是全部)设置
Preferences
- 如果要使用以下命令更改值,请不要使用命令行参数
SetPreference
SetPreference
必须在上调用CEF UI Thread
。使用Cef.UIThreadTaskFactory在CEF UI Thread
WinForms
:RequestContext
在创建浏览器实例后立即设置OffScreen
:传递RequestContext
给构造函数WPF
:调用后在您的Control
/Window
构造函数中设置InitializeComponent()
- 插件加载通知通过
IRequestContextHandler
接口处理 - 将RequestContextSettings.CachePath设置为持久化cookie,数据,localStorage等
- RequestContextSettings.CachePath 必须等于CefSettings.RootCachePath的子代,请参见https://github.com/cefsharp/CefSharp/issues/3111#issuecomment-629713608
//WinForms Examples - WPF and OffScreen are similar, see notes above.//Default implementation of RequestContext
//Default settings will be used, this means an in-memory cache (no data persisted)
browser = new ChromiumWebBrowser();
browser.RequestContext = new RequestContext();//CustomRequestContextHanler needs to implement `IRequestContextHandler`
//Default settings will be used, this means an in-memory cache (no data persisted)
browser = new ChromiumWebBrowser();
browser.RequestContext = new RequestContext(new CustomRequestContextHandler());//Custom settings and CustomRequestContextHandler
//Use the specified cache path (if empty, in memory cache will be used). To share the global
//browser cache and related configuration set this value to match the CefSettings.CachePath
//value.
var requestContextSettings = new RequestContextSettings { CachePath = cachePath };
browser = new ChromiumWebBrowser();
browser.RequestContext = new RequestContext(requestContextSettings, new CustomRequestContextHandler());
有关更多详细示例,请参见项目源。
//When you are already on the CEF UI Thread you can use the following
string errorMessage;
//You can set most preferences using a `.` notation rather than having to create a complex set of dictionaries.
//The default is true, you can change to false to disable
context.SetPreference("webkit.webprefs.plugins_enabled", true, out errorMessage);
//Change the minimum font size to 24pt
context.SetPreference("webkit.webprefs.minimum_font_size", 24, out errorMessage);//To execute on the CEF UI Thread you can use
Cef.UIThreadTaskFactory.StartNew(delegate
{string errorMessage;//Use this to check that settings preferences are working in your code//the browser variable is an instance of ChromiumWebBrowservar success = browser.RequestContext.SetPreference("webkit.webprefs.minimum_font_size", 24, out errorMessage);
});
在OnRequestContextInitialized中设置首选项(建议使用此方法来设置代理,因为它将在浏览器尝试加载任何网页之前被调用)
public class RequestContextHandler : IRequestContextHandler
{IResourceRequestHandler IRequestContextHandler.GetResourceRequestHandler(IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling){//Return null for the default behaviourreturn null;}bool IRequestContextHandler.OnBeforePluginLoad(string mimeType, string url, bool isMainFrame, string topOriginUrl, WebPluginInfo pluginInfo, ref PluginPolicy pluginPolicy){//pluginPolicy = PluginPolicy.Disable;//return true;return false;}void IRequestContextHandler.OnRequestContextInitialized(IRequestContext requestContext){//You can set preferences here on your newly initialized request context.//Note, there is called on the CEF UI Thread, so you can directly call SetPreference//Use this to check that settings preferences are working in your code//You should see the minimum font size is now 24ptstring errorMessage;var success = requestContext.SetPreference("webkit.webprefs.minimum_font_size", 24, out errorMessage);//You can set the proxy with code similar to the code below//https://stackoverflow.com/questions/36095566/cefsharp-3-set-proxy-at-runtime has some additional examples//var v = new Dictionary<string, object>//{// ["mode"] = "fixed_servers",// ["server"] = "scheme://host:port"//};//string errorMessage;//bool success = requestContext.SetPreference("proxy", v, out errorMessage);}
}
打印
CEF API仅公开了有限的打印支持。当前不支持在Kiosk模式下打印(打印到没有对话框的默认设置)。建议的解决方法是先打印,PDF
然后使用3rd party
应用程序来打印PDF
。
如果您需要更好的打印支持,则应在上进行讨论ceforum
。在CEF问题追踪器上已经有公开的讨论和未解决的问题。
- CEF Forum • PrintHandler for PDF reader and context menu
- chromiumembedded / cef / issues / #1283 - Adding Print Options to Cef3 — Bitbucket
- CEF Forum • Enable Kiosk-Printing Mode in CEF
高DPI显示/支持
WinForms/WPF
需要使使用DPI的桌面应用程序能够在高DPI显示器(DPI Scale
设置大于的显示器)上正确运行DPI100%
。
注意如果鼠标光标在浏览器中的位置不正确,或者浏览器显示带有渲染/调整大小的黑框/边框,则需要制作您的应用程序DPI Aware
。应用程序的其他部分也可能会显得模糊或尺寸不正确。
有许多选项可用于配置流程的DPI意识:
- 通过应用程序清单设置(通常是首选)
- 通过app.config(仅限WinForms,目标是.Net 4.7及更高版本)
- 通过API调用以编程方式
Windows 10 1703具有其他改进,有关更多详细信息,请参见High-DPI Scaling Improvements for Desktop Applications in the Windows 10 Creators Update (1703) - Windows Developer Blog。
WinForms高DPI
从.NET Framework 4.7开始,Windows Forms包括针对常见的高DPI和动态DPI方案的增强功能。在.NET Framework的早期版本中,您使用清单添加了高级DPI支持。不再建议使用此方法,因为它会覆盖app.config文件中定义的设置。请确保阅读Windows窗体中的High DPI支持以获取Microsoft的更多详细信息。
应用清单
重要事项 如果您要定位.Net 4.7
或以上定位,Microsoft
建议DPI Awareness
通过app.config
而不是进行配置app.manifest
。请确保阅读Windows窗体中的High DPI支持以获取Microsoft的更多详细信息。
使用应用程序清单设置默认感知。以下示例是Win 10 1703及更高版本上的PerMonitor DPI Aware和旧版本上的PerMonitor DPI感知。确保阅读了https://docs.microsoft.com/zh-cn/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows#dpi-awareness-mode,其中讨论了不同的DPI Awareness
选项。如果您的项目还没有app.manifest
使用Visual Studio New Item
模板,则可以使用模板来添加模板,而不是手动添加模板以确保添加文件中的相关<ApplicationManifest/>
条目csproj/vbproj
(这是一种特殊类型)。
<?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>
以编程方式
在代码中设置高DPI,可以使用Cef.EnableHighDPISupport();。辅助方法。这将调用Chromium
base :: win :: EnableHighDPISupport(); 功能。然后,您将拥有与Chromium
用途完全相同的设置。
Cef.EnableHighDPISupport(); 必须在应用程序执行的最早期就调用,最好在应用程序入口点(Program.Main)中调用。
该CefSharp.MinimalExample.WinForms项目包含一个工作的例子。
WPF高DPI
应用清单
添加相关条目,请参阅app.manifest中针对Microsoft的建议打开Windows级每个监视器的DPI感知。
有关工作示例,请参见https://github.com/cefsharp/CefSharp/blob/cefsharp/84/CefSharp.Wpf.Example/app.manifest了解工作示例。如果您的项目还没有app.manifest
使用Visual Studio New Item
模板,则可以使用模板来添加模板,而不是手动添加模板以确保添加文件中的相关<ApplicationManifest/>
条目csproj/vbproj
(这是一种特殊类型)。
<?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>
以编程方式
WPF
默认情况下,应用程序具有自动生成的Program.Main
入口点,这使得以编程方式设置更加困难DPI
。有关如何创建的信息,请参见c# - No Main() in WPF? - Stack Overflow,Program.Main
然后可以调用Cef.EnableHighDPISupport();。。这 必须在你的应用程序执行很早就被调用,最好在您的自定义Program.Main第一个电话。
屏幕外高DPI
添加相关app.manifest
条目或调用Cef.EnableHighDPISupport()
(请参阅上面的示例)。阅读WinForms
以上部分,选择适合您需求的选项。
高DPI附加信息
Chromium
默认情况下,将在单独的子流程中执行所有渲染。特别是GPU Compositor
需要有一个DPI Awareness
与您的主应用程序匹配的需求。目前,所使用的默认CefSharp.BrowserSubprocess.exe
值为Per Monitor DPI Aware
。作为一种解决方法,请使用disable-gpu-compositing
命令行arg,并将DPI Awareness
使用您的主应用程序进程的,而不是由所DPI Awareness
指定的GPU Process
(用于GPU Compositing
)。禁用GPU Compositing
可能会对性能产生影响,当https://github.com/cefsharp/CefSharp/issues/2927完成后,将有可能以编程方式设置DPI Awareness
使用的CefSharp.BrowserSubprocess.exe
var settings = new CefSettings();
settings.CefCommandLineArgs.Add("disable-gpu-compositing");
Cef.Initialize(settings);
另外,您可以尝试使用force-device-scale-factor 命令行标志。
var settings = new CefSettings();
settings.CefCommandLineArgs.Add("force-device-scale-factor", "1");
Cef.Initialize(settings);
- https://msdn.microsoft.com/zh-cn/library/windows/desktop/dn469266(v=vs.85).aspx 这是一篇很长的MSDN文章,但是如果您的应用程序需要在较高的环境下运行,则有必要阅读DPI显示。
- Improving the high-DPI experience in GDI based Desktop Apps - Windows Developer Blog
- https://docs.microsoft.com/zh-cn/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-with-the-application-manifest
多线程消息循环
CefSharp
默认使用setting.MultiThreadedMessageLoop = true
。这使您的应用程序能够非常快速地启动并运行,需要注意一些重要的事情,但这可能并不适合所有人。
- 对消息泵使用其他线程。
- CEF UI线程与应用程序的UI线程不同,这可能导致消息处理中的某些断开连接。
- 一个示例是打开菜单,然后在浏览器控件中单击并使菜单保持打开状态。
- 低级Win32消息不会在
CEF
和之间传播WinForms
可以将CEF集成到应用程序的现有消息循环中。将CEF集成到现有消息循环中的一种非常简单的实现涉及在UI线程上使用每秒调用30/60次的计时器。
var settings = new CefSettings();
settings.MultiThreadedMessageLoop = false; //This defaults to trueCef.Initialize(settings);- For WPF use [DispatcherTimer](https://docs.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netframework-4.8)
- For WinForms use [Timer](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.timer?view=netframework-4.8)//Set the timer Interval to 30 times per second, can be increased to 60 if required
//For WPF
timer.Interval = TimeSpan.FromMilliseconds(1000 / 30);
//For WinForms
timer.Interval = 1000 / 30;
timer.Tick += UiThreadTimerTick;
timer.Start();//Before closing your app
//Calling Cef.DoMessageLoopWork() after Cef.Shutdown has been called will result in
//an access violation, make sure you stop you timer first.
timer.Tick -= UiThreadTimerTick;
timer.Stop();private void UiThreadTimerTick(object sender, EventArgs e)
{//Must be called on the UI Thread.Cef.DoMessageLoopWork();
}
更高级的选项包括设置CefSettings.ExternalMessagePump = true;。和实现 IBrowserProcessHandler.OnScheduleMessagePumpWork。这样可以CEF
在需要执行工作时发出通知,在某些情况下,这可能会使您的应用程序响应速度更快。有关其他详细信息,请参见https://github.com/cefsharp/CefSharp/issues/1748。项目源代码中包含更多高级示例。
您可以在使用时挂接消息循环MultiThreadedMessageLoop
,尽管这很复杂。项目源代码包含一个示例,网址为https://github.com/cefsharp/CefSharp/blob/v53.0.0/CefSharp.WinForms.Example/BrowserTabUserControl.cs#L224 您可以使用此方法获取Win32鼠标消息。
弹出窗口
一个常见的请求是控制弹出窗口的创建。实施ILifeSpanHandler.OnBeforePopup
以控制如何创建弹出窗口。要完全取消弹出窗口的创建return true;
。
bool ILifeSpanHandler.OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{//Set newBrowser to null unless you're attempting to host the popup in a new instance of ChromiumWebBrowsernewBrowser = null;return true; //Return true to cancel the popup creation
}
您可以取消弹出窗口的创建,然后以新的形式打开URL ChromiumWebBrowser
使用此方法您选择实例中。重要的是要注意,使用此方法将不存在父子关系。因此,一般不建议这样做。
实验选项1:允许您使用中的newBrowser
参数托管弹出窗口OnBeforePopup
。有一些已知问题(在GitHub
项目上搜索)。如果您使用此方法遇到问题,那么您将必须承担责任并通过CEF项目解决该问题。同样重要的是要注意LoadingStateChanged
etc不会被弹出窗口调用。如果使用此方法,请实现相关的处理程序。
实验选项2:IWindowInfo.SetAsChild
用于指定父句柄。要在WPF中使用此功能,您将需要使用WinForms主机。使用此方法,您将需要处理move和resize事件。大致如下所示:
- 抓住
IBrowserHost
从新创建的IBrowser
实例表示弹出然后订阅窗口移动的通知和呼叫NotifyMoveOrResizeStarted
SetWindowPos
大小更改时在浏览器上调用HWND(隐藏时设置为0,0以停止渲染)
尽管它们是实验性的,但是在项目源中有一些示例,并且不能保证它们在起作用。备选案文2的例子不完整,尽管有报告表明它运作良好,尽管此人从未提供过有效的例子。
JavaScript整合
1.如何从.NET调用JavaScript方法?
//There are a number of extension methods that simplify execution, they all work on the main frame
//They all exists in the CefSharp.WebBrowserExtensions class, make sure you add "using CefSharp;"
browser.ExecuteScriptAsync("document.body.style.background = 'red';");// When executing multiple statements, group them together in an IIFE
// https://developer.mozilla.org/en-US/docs/Glossary/IIFE
// For Google.com pre-populate the search text box and click the search button
browser.ExecuteJavaScriptAsync("(function(){ document.getElementsByName('q')[0].value = 'CefSharp Was Here!'; document.getElementsByName('btnK')[0].click(); })();");
如果您的网页包含多个框架,则可以在子框架上执行脚本
browser.GetBrowser().GetFrame("SubFrame").ExecuteJavaScriptAsync("document.body.style.background = 'red';");
我什么时候可以开始执行JavaScript?
JavaScript
只能在V8Context中执行。在IRenderProcessMessageHandler.OnContextCreated
和IRenderProcessMessageHandler.OnContextReleased
提供时,可以执行JavaScript的一个边界。OnContextCreated/OnContextReleased
将每帧调用一次,用于frame.IsMain
检查主帧。
尝试开始访问DOM
是很诱人的OnFrameLoadStart
,而V8Context
will已创建,您将能够执行DOM
尚未完成加载的脚本。如果您需要DOM
尽早访问,请订阅DOMContentLoaded
,JavaScript
下面是一些执行示例。
browser.RenderProcessMessageHandler = new RenderProcessMessageHandler();public class RenderProcessMessageHandler : IRenderProcessMessageHandler
{// Wait for the underlying JavaScript Context to be created. This is only called for the main frame.// If the page has no JavaScript, no context will be created.void IRenderProcessMessageHandler.OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame){const string script = "document.addEventListener('DOMContentLoaded', function(){ alert('DomLoaded'); });";frame.ExecuteJavaScriptAsync(script);}
}//Wait for the page to finish loading (all resources will have been loaded, rendering is likely still happening)
browser.LoadingStateChanged += (sender, args) =>
{//Wait for the Page to finish loadingif (args.IsLoading == false){browser.ExecuteJavaScriptAsync("alert('All Resources Have Loaded');");}
}//Wait for the MainFrame to finish loading
browser.FrameLoadEnd += (sender, args) =>
{//Wait for the MainFrame to finish loadingif(args.Frame.IsMain){args.Frame.ExecuteJavaScriptAsync("alert('MainFrame finished loading');");}
};
有关执行的一些注意事项JavaScript
:
- 脚本在框架级别执行,并且每个页面至少有一个框架(
MainFrame
)。 - 该
IWebBrowser.ExecuteScriptAsync
扩展方法是为了向下兼容性,可以使用它作为快捷方式来执行js
在主框架上。 - 如果框架不包含JavaScript,则不会
V8Context
创建任何框架。 - 对于没有上下文的框架,一旦框架加载完成,就可以使用创建V8Context
IFrame.ExecuteJavaScriptAsync
。 - 该
DOM
不会已完成加载时OnFrameLoadStart
被触发 IRenderProcessMessageHandler.OnContextCreated/OnContextReleased
仅针对主机。
2.如何调用返回结果的JavaScript方法?
如果您需要调用(评估)返回值的JavaScript,请使用以下方法之一:
//An extension method that evaluates JavaScript against the main frame.
Task<JavascriptResponse> response = await browser.EvaluateScriptAsync(script);
//Evaluate javascript directly against a frame
Task<JavascriptResponse> response = await frame.EvaluateScriptAsync(script);//An extension method that evaluates Javascript Promise against the main frame.
//Uses Promise.resolve to return the script execution into a promise regardless of the return type
//This method differs from EvaluateScriptAsync in that your script **must return** a value
//Examples below
Task<JavascriptResponse> response = await browser.EvaluateScriptAsPromiseAsync(script);
JavaScript代码是异步执行的,因此返回Task,其中包含错误消息,结果和success(bool
)标志。这是评估时需要了解的基本知识JavaScript
- 请确保您阅读了 何时可以开始执行JavaScript?。
- 脚本在框架级别执行,并且每个页面至少有一个框架(
MainFrame
)。 - 脚本在渲染过程中执行,并通过进行传输
IPC
,仅返回出于性能原因所需的数据。 - 支持原始数据类型:int,double,date,bool和string。
- 在某种程度上支持对象,并且将以形式返回对象。支持
IDictionary<string, object>
使用dynamic
关键字来简化访问属性值的过程。 - 您不能直接返回
DOM
Element(或任何具有循环引用的元素),需要创建一个仅包含您需要返回的信息的新对象。 - 支持包含上面列出的原语和对象的数组,它们将以形式返回
IList<object>
。 Array Like
像HTMLCollection这样的对象不能直接使用Array.from返回并返回数组- 可以返回的对象图的复杂度受到限制(当前不支持带有循环引用的图),在这种情况下,您可能需要使用JavaScript
JSON.stringify()
方法将JavaScript对象转换为JSON字符串,然后将该字符串返回您的.NET代码。然后,您可以使用类似JSON.NET的方式将该字符串解码为.NET对象。有关更多详细信息,请参见MDN JSON.stringify。有关与结合使用的一些指导,请参见javascript - How to JSON.stringify a dom element? - Stack Overflow。JSON.stringify
HTMLElement
//Start with something simple, the following will return the value 2 as type int
//Don't use the `return` keyword
Task<JavascriptResponse> response = await frame.EvaluateScriptAsync("1 + 1");//A javascript IFFE will be evaluated and it's result returned.
//https://developer.mozilla.org/en-US/docs/Glossary/IIFE
//If you want to execute multiple lines of javascript then an IIFE is recommended to
//avoid any variable scoping issues
var script = @"(function() { let val = 1 + 1; return val; })();";
Task<JavascriptResponse> response = await frame.EvaluateScriptAsync(script);//If your script uses a Promise then you must use the EvaluateScriptAsPromiseAsync method, it differs slightly
//in that you must return the value.
//The following will return a Promise that after one second resolves with a simple objec
var script = "return new Promise(function(resolve, reject) { setTimeout(resolve.bind(null, { a: 'CefSharp', b: 42, }), 1000); });"
Task<JavascriptResponse> javascriptResponse = await browser.EvaluateScriptAsPromiseAsync(script);
//You can access the object using the dynamic keyword for convenience.
dynamic result = javascriptResponse.Result;
var a = result.a;
var b = result.b;//EvaluateScriptAsPromiseAsync calls Promise.resolve internally so even if your code doesn't
//return a Promise it will still execute successfully.
var script = @"return (function() { return 1 + 1; })();";
Task<JavascriptResponse> response = await frame.EvaluateScriptAsPromiseAsync(script);// An example that gets the Document Height
var task = frame.EvaluateScriptAsync("(function() { var body = document.body, html = document.documentElement; return Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight ); })();");//Continue execution on the UI Thread
task.ContinueWith(t =>
{if (!t.IsFaulted){var response = t.Result;EvaluateJavaScriptResult = response.Success ? (response.Result ?? "null") : response.Message;}
}, TaskScheduler.FromCurrentSynchronizationContext());//HTMLElement/HTMLCollection Examples
//As stated above, you cannot return a HTMLElement/HTMLCollection directly.
//It's best to return only the data you require, here are some examples of using Array.from to convert a HTMLCollection into an array of objects
//which can be returned to your .Net application.//Get all the span elements and create an array that contains their innerText
var script = @"Array.from(document.getElementsByTagName('span')).map(x => ( x.innerText));";
Task<JavascriptResponse> response = await frame.EvaluateScriptAsync(script);//Get all the a tags and create an array that contains a list of objects
//Second param is the mapping function
var script = @"Array.from(document.getElementsByTagName('a'), x => ({ innerText : x.innerText, href : x.href }));";
Task<JavascriptResponse> response = await frame.EvaluateScriptAsync(script);//List of Links, click represents a function pointer which can be used to execute the link click)
//In .Net the https://cefsharp.github.io/api/86.0.x/html/T_CefSharp_IJavascriptCallback.htm is used
//to represent the function.
var script = @"Array.from(document.getElementsByTagName('a')).map(x => ({ innerText: x.innerText, click: x.click}));";
Task<JavascriptResponse> response = await frame.EvaluateScriptAsync(script);//Execute the following against google.com to get the `I'm Feeling Lucky` button then click the button in .Net
//NOTE: This is a simple example, you could return an aggregate object consisting of data from multiple html elements.
const string script = @"(function()
{let element = document.getElementsByName('btnI')[0];let obj = {};obj.id = element.id;obj.nodeValue = element.nodeValue;obj.localName = element.localName;obj.tagName = element.tagName;obj.innerText = element.innerText;obj.click = element.click;obj.attributes = Array.from(element.attributes).map(x => ({name: x.name, value: x.value}));return obj;
})();";var javascriptResponse = await browser.EvaluateScriptAsync(script);
dynamic result = javascriptResponse.Result;
var clickJavascriptCallback = (IJavascriptCallback)result.click;
await clickJavascriptCallback.ExecuteAsync();
//Dispose of the click callback when done
clickJavascriptCallback.Dispose();
3.如何将.NET类公开给JavaScript?
JavaScript的绑定(JSB
)允许之间的通信JavaScript
和.Net
。当前有两个不同的实现,Async
版本和较旧的Sync
版本。该Sync
版本不再被积极开发,它依赖于WCF
该版本不可用.Net Core
或即将发布.Net 5.0
。
异步JavaScript绑定(JSB)
概要
-
用于
Native Chromium IPC
在浏览器进程和渲染进程之间来回传递消息,因此非常快。 -
仅基于消息
methods
是受支持的,Native Chromium IPC
并且只能以某种async
方式使用(Property
不能以异步方式完成获取/设置) -
Methods
可以返回简单对象,structs
并且classes
受支持,仅将的副本Properties
传输到JavaScript
。就像webservice/ajax
打电话一样,您会得到一个响应对象。 -
JavaScript callbacks
通过IJavascriptCallback支持 -
所有方法调用都是非阻塞的,并返回可以是的标准JavaScript Promise
awaited
。 -
默认情况下
CamelCase
,方法名称会转换为(第一个字母转换为小写,MyFunction
变为myFunction
)。这可以通过在注册对象之前设置browser.JavascriptObjectRepository.NameConverter属性来配置,将其设置为null以禁用名称转换,详细示例如下。 -
JavaScript Binding API详细介绍了可用的不同方法。
-
Exceptions
在.Net
被抓住,Promise
意志将会被抓住rejected
。 -
请参阅高级异步JavaScript绑定(JSB)Wiki,请确保您先阅读此内容。
如果您不熟悉这里提供的所有Chromium
内容,那么async programming
可以参考一些非常有用的文章
- Promise - JavaScript | MDN
- https://developers.google.com/web/fundamentals/primers/async-functions
- https://developers.google.com/web/fundamentals/primers/promises
绑定Async
对象JavaScript
该CefSharp.BindObjectAsync方法被称为在Javascript
结合的对象。CefSharp.BindObjectAsync返回一个Promise,当绑定的对象可用时,该Promise将被解决。在全局上下文(window
对象的属性)中创建对象。如果调用时CefSharp.BindObjectAsync
没有任何参数,则所有已注册的对象都将被绑定。名称绑定是更具描述性的选项。
简单的工作流程如下所示:
- 第1步:创建一个您希望公开使用javascript的类(不要使用您的
Form/Window
或Control
) - 步骤2向您的课程注册一个实例
JavaScriptObjectRepository
- 步骤3使用您要注册的对象的名称来调用CefSharp.BindObjectAsync,例如
CefSharp.BindObjectAsync("boundAsync");
(对象只有在Promise
解析后才可用。
仅支持方法。如果需要设置属性,则创建Get
/Set
方法。
步骤1建立课程
一个简单的类如下所示:
public class BoundObject
{public int Add(int a, int b){return a + b;}
}
步骤2向您的课程注册一个实例JavaScriptObjectRepository
该过程的第二部分是向中注册对象JavascriptObjectRepository
(可通过browser.JavascriptObjectRepository属性访问)。您有两个选项用于在中注册对象.Net
,第一个选项是预先注册的,通常在创建ChromiumWebBrowser
实例后立即完成。第二个选项更加灵活,并允许Resolved
在需要时放置对象。
第一种选择:
//For async object registration (equivalent to the old RegisterAsyncJsObject)
browser.JavascriptObjectRepository.Register("boundAsync", new BoundObject(), true, BindingOptions.DefaultBinder);
第二种选择(首选)
browser.JavascriptObjectRepository.ResolveObject += (sender, e) =>
{var repo = e.ObjectRepository;if (e.ObjectName == "boundAsync"){BindingOptions bindingOptions = null; //Binding options is an optional param, defaults to nullbindingOptions = BindingOptions.DefaultBinder //Use the default binder to serialize values into complex objectsbindingOptions = new BindingOptions { Binder = new MyCustomBinder() }); //Specify a custom binderrepo.NameConverter = null; //No CamelCase of Javascript Names//For backwards compatability reasons the default NameConverter doesn't change the case of the objects returned from methods calls.//https://github.com/cefsharp/CefSharp/issues/2442//Use the new name converter to bound object method names and property names of returned objects converted to camelCaserepo.NameConverter = new CamelCaseJavascriptNameConverter();repo.Register("boundAsync", new BoundObject(), isAsync: true, options: bindingOptions);}
};
要在.Net
绑定对象时得到通知,JavaScript
您可以订阅ObjectBoundInJavascript事件或ObjectsBoundInJavascript事件(这两个事件显然非常相似)。
browser.JavascriptObjectRepository.ObjectBoundInJavascript += (sender, e) =>
{var name = e.ObjectName;Debug.WriteLine($"Object {e.ObjectName} was bound successfully.");
};
步骤3呼叫CefSharp.BindObjectAsync
<script type="text/javascript">
(async function()
{await CefSharp.BindObjectAsync("boundAsync");//The default is to camel case method names (the first letter of the method name is changed to lowercase)boundAsync.add(16, 2).then(function (actualResult){const expectedResult = 18;assert.equal(expectedResult, actualResult, "Add 16 + 2 resulted in " + expectedResult);});
})();
</script>
进行CefSharp.BindObjectAsync调用时,将JavascriptObjectRepository
查询以查看是否已注册具有给定名称的对象,如果未找到匹配的对象,ResolveObject
则引发该事件。对于不带任何参数的CefSharp.BindObjectAsync调用,则如果已注册对象,则将它们全部绑定,如果未注册对象,则将ResolveObject
其ObjectName
设置为All
。
本节仅介绍基础知识,还有许多高级选项,请查看高级异步Javascript绑定。
如果您想查看一个可行的示例,请查看CefSharp MinimalExample Javascript Binding Demo分支,特别是commit
同步JavaScript绑定(JSB)
这是一个遗产功能-任何正在创建新应用程序的人都在使用Async JavaScript Binding(JSB)实现,因为它正在积极开发中。该Sync
版本仅会收到针对回归的错误修复。
- 使用
WCF
通信服务(微软还没有为支持WCF
中.Net Core/.Net 5.0
,有没有长远的未来WCF
)。 - 同时支持方法和属性
- 呼叫以某种
sync
方式执行且正在阻塞,长时间运行的呼叫会阻塞Render Process
并导致您的应用显示缓慢或无响应。 - 支持半复杂的对象结构
- 有时,该
WCF
服务无法完全关闭,并减慢了应用程序的关闭速度
绑定对象 JavaScript
绑定是由JavaScript启动的,当绑定的对象可用时,该CefSharp.BindObjectAsync
方法将返回Promise
解析的结果。在全局上下文(window
对象的属性)中创建对象。如果调用时CefSharp.BindObjectAsync
没有任何参数,则所有已注册的对象都将被绑定。名称绑定是更具描述性的选项。
简单的工作流程如下所示:
- 第1步创建一个您希望向JavaScript公开的类(不要使用您的
Form/Window
或Control
) - 步骤2
CefSharp.BindObjectAsync
使用您要注册的对象的名称进行调用,例如CefSharp.BindObjectAsync("myObject");
(对象只有在Promise
解析后才能使用。 - 步骤3向
JavaScriptObjectRepository
步骤1
public class BoundObject
{public string MyProperty { get; set; }public void MyMethod(){// Do something really cool here.}public void TestCallback(IJavascriptCallback javascriptCallback){const int taskDelay = 1500;Task.Run(async () =>{await Task.Delay(taskDelay);using (javascriptCallback){//NOTE: Classes are not supported, simple structs arevar response = new CallbackResponseStruct("This callback from C# was delayed " + taskDelay + "ms");await javascriptCallback.ExecuteAsync(response);}});}
}
步骤2调用CefSharp.BindObjectAsync
,下面Binding
的对象示例如下所示:
注意这是一个两部分的过程,有关详细信息,请参见下面的示例
<script type="text/javascript">
(async function()
{await CefSharp.BindObjectAsync("boundAsync");boundAsync.div(16, 2).then(function (actualResult){const expectedResult = 8assert.equal(expectedResult, actualResult, "Divide 16 / 2 resulted in " + expectedResult);});boundAsync.error().catch(function (e){var msg = "Error: " + e + "(" + Date() + ")";});
})();(async () =>
{await CefSharp.BindObjectAsync("boundAsync");boundAsync.hello('CefSharp').then(function (res){assert.equal(res, "Hello CefSharp")});
})();CefSharp.BindObjectAsync("boundAsync2").then(function(result)
{boundAsync2.hello('CefSharp').then(function (res){assert.equal(res, "Hello CefSharp")// NOTE the ability to delete a bound objectassert.equal(true, CefSharp.DeleteBoundObject("boundAsync2"), "Object was unbound");assert.ok(window.boundAsync2 === undefined, "boundAsync2 is now undefined");});
});
</script>
第三步
该过程的第二部分是将对象注册为JavascriptObjectRepository
(可通过browser.JavascriptObjectRepository
属性访问)。您有两个选项用于在中注册对象.Net
,第一个选项是预先注册的,通常在创建ChromiumWebBrowser
实例后立即完成。第二个选项更加灵活,并允许Resolved
在需要时放置对象。
进行CefSharp.BindObjectAsync
调用时,JavascriptObjectRepository
is查询查询是否已指定给定名称的对象,如果找不到匹配的对象,ResolveObject
则引发该事件。对于CefSharp.BindObjectAsync
不带任何参数的调用,则如果已经注册了对象,则将它们全部绑定,如果没有注册任何对象,则将ResolveObject
其ObjectName
设置为All
。
//When a
browser.JavascriptObjectRepository.ResolveObject += (sender, e) =>
{var repo = e.ObjectRepository;if (e.ObjectName == "boundAsync2"){BindingOptions bindingOptions = null; //Binding options is an optional param, defaults to nullbindingOptions = BindingOptions.DefaultBinder //Use the default binder to serialize values into complex objects,bindingOptions = new BindingOptions { Binder = new MyCustomBinder() }); //No camelcase of names and specify a custom binder//For backwards compatability reasons the default NameConverter doesn't change the case of the objects returned from methods calls.//https://github.com/cefsharp/CefSharp/issues/2442//Use the new name converter to bound object method names and property names of returned objects converted to camelCaserepo.NameConverter = new CamelCaseJavascriptNameConverter();repo.Register("bound", new BoundObject(), isAsync: false, options: bindingOptions);}
};
在实际的JS代码中,您将使用这样的对象(默认为CamelCase Javascript Names,可通过JavascriptObjectRepository.NameConverter进行控制,请参见上面的示例)。
bound.myProperty; // use this syntax to access the property
bound.myMethod(); // use this to call the method.
bound.testCallback(callback); //Pass a function in to use as a callback
请注意:
- 不要注册表格/窗口/控件。如果需要,创建一个类并代理调用。
- 默认情况下,方法和属性都更改为camelCase(即首字母小写)以使其在JavaScript代码中自然使用。禁用将browser.JavascriptObjectRepository.NameConverter设置为null
- 属性支持复杂对象(如果适用),因此您现在可以执行
bound.subObject.myFunction()
和bound.subObject.myProperty = 1
。 - 现在可以通过
IBinder
接口实现对函数的复杂对象支持,您可以实现自己的或使用DefaultBinder
例如repo.Register("bound", new BoundObject(), BindingOptions.DefaultBinder);
RegisterAsyncJsObject
此方法已删除。请参阅异步JavaScript绑定(JSB)。
RegisterJsObject
这已被删除。请参阅同步JavaScript绑定(JSB)。
Adobe Flash Player(Pepper Flash)
注意:现在不建议使用Flash,并且Chromium
将删除支持,有关更多详细信息,请参阅Flash Roadmap。从版本开始,81
默认情况下现在已禁用它,请参见https://github.com/cefsharp/CefSharp/issues/3048#issuecomment-592263009
CefSharp
可以从Adobe下载可以自动发现并加载的Pepper Flash的系统范围安装。从下拉列表中选择FP for Opera and Chromium-PPAPI版本。要测试Flash是否正常运行,只需加载Adobe - Flash Player。
注意首次打开Flash时,将短暂显示控制台窗口,显示NOT SANDBOXED
。有一个问题在Chromium
问题跟踪,但不幸的是Google
已经将其标记为WontFix
。一些聪明的人一起破解了一些解决方法。它们很复杂,我从未尝试过。请访问https://github.com/cefsharp/CefSharp/issues/1259中的链接以获取详细信息。
屏幕外渲染(OSR)
WPF和OffScreen版本使用OffScreen Rendering(OSR)渲染模式。在OSR模式每帧被渲染到缓冲器中,然后在屏幕上或者绘制为在WPF的情况下,或提供作为Bitmap
在OffScreen
。
WPF
对于WPF控件,用户输入(鼠标单击/移动和按键)将通过IBrowserHost
界面上的方法转发到基础浏览器。可以访问每个Bitmap
渲染的对象。
应特别注意ChromiumWebBrowser
在内托管ViewBox
。这远非理想,因为渲染了每一帧,然后进行后处理来调整图像的大小/缩放。这会严重影响性能,并且通常会降低质量(通常很模糊)。您可以使用调整调整大小的质量RenderOptions.SetBitmapScalingMode
。最好避免使用ViewBox
。您可以通过调整来缩放浏览器中包含的内容ZoomLevel
,这是迄今为止性能最高的选项。
屏幕外
对于CefSharp.OffScreen
包装,将每个帧渲染到Bitmap
并暴露以供使用。如果希望通过键盘或鼠标与浏览器进行交互,则可以使用IBrowser
主机界面上的方法。模拟按键和鼠标单击/移动可能非常复杂。您可以使用WPF控件作为开始示例,因为它使用相同的方法(添加调试以查看所需的事件顺序)。按键和鼠标的点击/移动通常由多个部件,up
/down
与许多其它可能的组合。
用户代理
您可以通过设置CefSettingsBase.UserAgent Property来指定自定义UserAgent
的UserAgent
可以在运行时使用DevTools协议来改变看到c# - CefSharp Change UserAgent in real time - Stack Overflow的一个例子。
您可以在中修改User-Agent
HTTP标头IResourceRequestHandler.OnBeforeResourceLoad
,这对于每个请求都需要完成。它不做的是UserAgent
将浏览器报告更改为JavaScript。
public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
{protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){//Set the header by name, override the existing valuerequest.SetHeaderByName("user-agent", "MyBrowser CefSharp Browser", true);return CefReturnValue.Continue;}
}public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling){//Where possible only intercept specific Url's//Load https://www.whatismybrowser.com/detect/what-is-my-user-agent in the browser and you'll//see our custom user agentif (request.Url == "https://www.whatismybrowser.com/detect/what-is-my-user-agent"){return new CustomResourceRequestHandler();}//Default behaviour, url will be loaded normally.return null;}
}browser.RequestHandler = new CustomRequestHandler();
开发工具
您可以从CefSharp中打开DevTools。并非所有功能都起作用。任何缺少的东西都需要在CEF中实施。
browser.ShowDevTools();
您可以将Chrome连接到正在运行的实例。这可能会为您提供更多选项(不幸的是,并非Chrome中存在所有选项)。
var settings = new CefSettings();
settings.RemoteDebuggingPort = 8088;
Cef.Initialize(settings);
http://localhost:8088
在Chrome中打开。
屏幕截图
底层的CEF Web浏览器不是特别适合于截屏。以下是一些注意事项和警告:
屏幕外/ WPF
无论Offscreen
和WPF
使用的屏幕外着色(OSR),其中每一帧被渲染为位图。它仍然是一个网络浏览器,并不是特别适合这种情况。这里有一些注意事项:
- 降低帧频以使其更容易捕获帧可能值得考虑
- 页面加载完成后,您需要等待一段时间,以允许浏览器呈现
- 当前尚无确定网页何时完成渲染的方法(Flash,动态内容,动画等功能,甚至像移动鼠标或滚动之类的简单任务也将导致渲染新帧)。
- 一种确定何时大致完成渲染的破解方法是让计时器在每次渲染帧时重置,如果没有其他帧渲染,则计时器将归档(不理想)
WinForms
这是在Windows下拍摄屏幕快照的一些示例
- 使用BitBlt方法捕获当前视图
- 使用滚动捕获整页
Win32内存不足
使用32bit
版本时,请确保您的应用程序支持大地址(处理大于2gb的地址)
根据http://magpcss.org/ceforum/viewtopic.php?f=6&t=15120#p34802中的建议,现在看来有必要在32位应用程序运行时在应用程序可执行文件上设置“大地址感知”链接器设置。遇到高内存负载。
https://msdn.microsoft.com/zh-CN/library/wz223b1z.aspx
CefSharp附带的默认x86 SubProcess可以识别大型地址,您也应该使应用程序识别。
将大地址感知链接器设置应用于可执行文件后,如果仍然遇到完全相同的问题,请在http://magpcss.org/ceforum/viewtopic.php?f=6&t=15120上讨论您的问题。
使用PostData加载URL
有两种加载URL的方法Post Data
,第一种是修改现有的Request
。在下面的示例中,Request
如果我们访问http://httpbin.org/post,我们会将发布数据添加到
public class CustomRequestHandler : CefSharp.Handler.RequestHandler
{protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling){//Where possible only intercept specific Url's//Load http://httpbin.org/post in the browser and you'll//see the post dataif (request.Url == "http://httpbin.org/post"){return new CustomResourceRequestHandler();}//Default behaviour, url will be loaded normally.return null;}
}public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler
{protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){//Modify the request to add post data//Make sure to read https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POSTvar postData = new PostData();postData.AddData("test=123&data=456");request.Method = "POST";request.PostData = postData;//Set the Content-Type header to whatever suites your requirementrequest.SetHeaderByName("Content-Type", "application/x-www-form-urlencoded", true);//Set additional Request headers as required.return CefReturnValue.Continue;}
}//Load http://httpbin.org/post in the browser to see the post data
browser = new ChromiumWebBrowser("http://httpbin.org/post");
browser.RequestHandler = new CustomRequestHandler();
第二种方法是使用IFrame.LoadRequest,仅当您首次成功执行导航后才能使用此方法。例如,您必须先导航到google.com,然后才能调用IFrame.LoadRequest。加载about:blank
是不够的,因为它是特例,并且不会产生渲染过程。
public void LoadCustomRequestExample()
{var frame = browser.GetMainFrame();//Create a new request knowing we'd like to use PostDatavar request = frame.CreateRequest(initializePostData:true);request.Method = "POST";request.Url = "http://httpbin.org/post";//Set AllowStoredCredentials so cookies are sent with Requestrequest.Flags = UrlRequestFlags.AllowStoredCredentials;request.PostData.AddData("test=123&data=456");frame.LoadRequest(request);
}
该browser.LoadUrlWithPostData扩展方法可用于简单的情况下,它会调用LoadRequest
并针对具有相同的限制进行了成功的导航应用。
拼写检查
默认情况下CefSettings.Locale
将指示使用哪个字典,默认为en-US
。可以在运行中配置拼写检查的许多方面,enable/disable
在运行中进行更改dictionary
,甚至启用多个词典。使用RequestContext.SetPreference
(有关RequestContext
如何设置首选项的详细信息,请参阅本文档的部分)。
只能使用spellcheck.dictionaries
首选项(重要的是使用复数版本) 动态地更改拼写检查https://bitbucket.org/chromiumembedded/cef/issues/2222/spell-checking-language-cannot-be-changed#comment-38338016
这是一些有用的链接
http://magpcss.org/ceforum/viewtopic.php?f=6&t=14911&p=33882&hilit=spellcheck#p33882 https://cs.chromium.org/chromium/src/components/spellcheck/browser/pref_names.cc?type = cs&q =%22spellcheck.dictionary%22&l = 11 https://cs.chromium.org/chromium/src/components/spellcheck/browser/pref_names.cc?type=cs&q=%22spellcheck.dictionary%22&l=15
并非所有语言都支持拼写检查,请参阅CEF Forum • CEF spellcheck.dictionaries for Chinese Language
Web组装
在较新的版本中默认为启用,请参见WebAssembly - Chrome Platform Status
对于较旧的版本,您需要手动启用,WebAssembly
请参见chromiumembedded / cef / issues / #2101 - Add WebAssembly support — Bitbucket
settings.javascript_flags
转换为 settings.JavascriptFlags = "--expose-wasm";
异常处理
捕获非托管异常非常困难,并且CEF
可能处于损坏状态,需要您的应用程序终止并重新启动。由于这是一个一般的编程主题,因此不在本文的CefSharp
特别讨论范围之内,因此有一些资源可帮助您开始自己进行研究。
http://stackoverflow.com/questions/233255/how-does-setunhandledexceptionfilter-work-in-net-winforms-applications https://msdn.microsoft.com/zh-CN/library/windows/desktop/ms680634(v = vs.85).aspx https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Application.cs,8243b844777a16c3 https://referencesource.microsoft.com/#System。 Windows.Forms / winforms / Managed / System / WinForms / Application.cs,3192
在混合的本地/ CLR环境中捕获未处理的异常 Capturing unhandled exceptions in a mixed native/CLR environment – Ivan Krivyakov
依赖检查
CefSharp
有一个非常简单的类,用于检查是否存在所有相关的非托管资源。
//Perform dependency check to make sure all relevant resources are in our output directory.
//https://cefsharp.github.io/api/86.0.x/html/M_CefSharp_Cef_Initialize_1.htm
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);//Manually check
//https://cefsharp.github.io/api/86.0.x/html/T_CefSharp_DependencyChecker.htm
DependencyChecker.AssertAllDependenciesPresent(cefSettings.Locale, cefSettings.LocalesDirPath, cefSettings.ResourcesDirPath, cefSettings.PackLoadingDisabled, cefSettings.BrowserSubprocessPath);
这不是100%
万无一失的,如果您遇到问题并且所有资源都存在,请禁用依赖性检查。在某些情况下,它不起作用。
https://github.com/cefsharp/CefSharp/wiki/Output-files-description-table-%28Redistribution%29
多媒体(音频/视频)
CEF
并且随后CefSharp
仅支持免费提供的音频和视频编解码器。要查看CefSharp
您所使用的版本支持,请在ChromiumWebBrowser
实例中打开HTML5test - How well does your browser support HTML5?。
MP3
专利已过期,因此受65.0.0
病房支持。H264/AAC
被归类为Proprietary Codecs
并且不受支持,它们要求您获得许可。喜欢Netflix/Twitter/Instagram
使用的网站H264
,其视频因此无法播放。请参阅Information about the H.264 patent license — Free Software Foundation — Working together for free software,以获取Free Software Foundation
有关该主题的一些评论。
CEF
对的支持编译H264/AAC
超出了该项目的范围。以下内容仅供参考,请不要寻求支持CEF
。
- CEF Forum • [SOLVED] Build CEF with proprietary codecs support
- https://github.com/cefsharp/CefSharp/issues/1934#issuecomment-279305821
- https://github.com/mitchcapper/CefSharpDockerfiles
屏幕(虚拟)键盘
该WinForms
版本已经内置在屏幕键盘的支持,它已经报道,有时它并不总是正确弹出,使用disable-usb-keyboard-detect
命令行参数 https://github.com/cefsharp/CefSharp/issues/1691#issuecomment-323603277报道解决这个问题。
从WPF
屏幕版本(虚拟)开始,它没有内置的支持,从版本开始,73
新VirtualKeyboardRequested
事件现在会在您的应用程序应显示虚拟键盘时提供通知。不幸的是,Windows 7, 8.1 and 10
由于没有.Net API
显示虚拟键盘的功能,因此很难提供支持的默认实现。Windows 10 Only
在https://github.com/cefsharp/CefSharp/commit/0b57e526158e57e522d46671404c557256529416中添加了一个示例如果您需要支持,Windows 8 and 10
则https://github.com/maximcus/WPFTabTip可能会有用。对于Windows 7
Incorporating the Windows 7 onscreen keyboard into a WPF app - Stack Overflow有一些建议。
CefSharp中RequestHandler的处理流程
OnBeforeResourceLoad()->GetResourceResponseFilter()->OnResourceResponse()->OnResourceLoadComplete()
OnBeforeResourceLoad()
Called before a resource request is loaded. For async processing return CefSharp.CefReturnValue.ContinueAsync and execute CefSharp.IRequestCallback.Continue(System.Boolean) or CefSharp.IRequestCallback.Cancel
在加载资源请求之前调用。异步处理返回CefSharp.CefReturnValue.ContinueAsync并且执行CefSharp.IRequestCallback.Continue(System.Boolean)或者CefSharp.IRequestCallback.Cancel。所以,这一步可以修改请求。
GetResourceResponseFilter()
Called on the CEF IO thread to optionally filter resource response content.
在CEF IO线程上调用以选择性地筛选资源响应内容。
OnResourceResponse()
在收到资源响应时在CEF IO线程上调用。若要允许资源正常加载,则返回false。
重定向或重试资源修改请求(url、headers或post body)并返回true。
无法在此回调中修改响应对象。
OnResourceLoadComplete()
资源加载完成后调用,可以获取或修改响应数据
当资源加载完成时在CEF IO线程上调用