其他分享
首页 > 其他分享> > CefSharp中文帮助文档

CefSharp中文帮助文档

作者:互联网

CefSharp是围绕Chromium嵌入式框架( Chromium Embedded Framework,CEF)的简单.Net包装器。CEF是一个基于Google Chromium项目的开源项目。与Chromium项目本身(主要专注于Google Chrome应用程序开发)不同,CEF专注于促进第三方应用程序中的嵌入式浏览器用例。 CEF基于多进程Chromium Content API,因此,当前仅存在Chromium的部分功能。例如,对扩展的支持是有限的,仅实现了一部分Extension API

CefSharp提供三种不同的类型WinFormsWPFOffScreen。在WPFOffScreen版本使用的OffScreen Rendering(OSR)渲染模式。在OSR模式中,每个帧被渲染到缓冲器,然后或者在屏幕上绘制作为的情况下WPF或可作为BitmapOffScreen。所有版本都使用CefSharpCefSharp.Core库,因此API在这三种风格中,大部分使用的库都完全相同。这减少了代码重复并降低了添加新功能的维护负担,唯一的缺点是该WPF版本并不像它可能的那样友好(您可以将其ChromiumWebBrowser归类,并在应用程序中实现所需的任何缺少的部分)。您也可以托管WinFormsWPF使用中的版本号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-branchesGoogle最近去除了对较早的操作系统的支持,例如Windows XP,Vista及其服务器版本。如果您要求您的应用程序在这些操作系统上运行,请查看发行版以获取更多详细信息https://github.com/cefsharp/CefSharp/releases

CefSharp要求

笔记:

任何CPU支持

较新的版本现在支持定位AnyCPU,有关如何实现此功能的详细信息,请参见https://github.com/cefsharp/CefSharp/issues/1714。可以使用相同的技术将libcef.dll等等移动到磁盘上的其他文件夹或公共位置。

 

需要知道/限制

例子

CefSharp源代码包含的许多不同的特征的实施例。还有一个MinimalExample项目使用最新的Nuget软件包提供非常简单的Browser实现。这MinimalExample是入门的最佳位置,下载此项目并使其运行以作为基础参考,以确保一切都在您的系统上正常工作。

https://github.com/cefsharp/CefSharp.MinimalExample

记录中

默认情况下CEF,在应用程序的执行文件夹(例如)中维护其自己的日志文件('Debug.log')bin。要禁用日志记录更改settings.LogSeverity,并更改文件名/路径,请使用settings.LogFile

调试问题时,首先要检查的地方是此日志文件,因为它包含低级Chromium消息。如果您看到错误或警告,搜索http://magpcss.org/ceforum/index.phphttps://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++实现的非常简单的包装。

https://bitbucket.org/chromiumembedded/cef/issues/2498/add-support-for-site-per-process#comment-54186905包含有关当前默认流程模型的详细信息。

线程数

CEF使用多个线程进行不同级别的处理。例如browser,该过程包含以下通常引用的线程:

初始化和关闭

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 debugging
    settings.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.ShutdownChromiumWebBrowser将钩住相关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 well
            Cef.EnableHighDPISupport();
	    
            //We're going to manually call Cef.Shutdown below, this maybe required in some complex scenarios
            CefSharpSettings.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();
        }
}

综上所述

CefSettings和BrowserSettings

CefSettings结构允许配置应用程序范围的CEF设置。一些通常配置的成员包括:

有许多设置和命令行参数可能会影响CEF的行为方式。这里有些例子:

public static void Init()
{
    // Specify Global Settings and Command Line Arguments
    var 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 Acceleration
    settings.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 proxy
    settings.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

IBrowserIFrame对象用于将命令发送到浏览器,并在回调方法中返回状态信息。每个IBrowser对象都有一个代表顶层框架的main IFrame对象,以及零个或多个sub IFrame对象。

例如,加载两个HTML<iframe>的浏览器将具有三个IFrame对象(顶级框架和两个<iframe>)。

要将URL加载到浏览器主机中:

browser.MainFrame.LoadUrl(someurl);

CefSharp提供了许多扩展方法,使执行常见任务更加容易。请参阅参考资料WebBrowserExtensions,以获取这些方法的来源,并更好地了解如何执行常见任务。

IBrowserHost 代表更底层的浏览器方法。

处理程序

CefSharp为了方便起见,提供了一些事件,如下所示(有关所有常见事件以及有关其用法的详细信息,请参见IWebBrowser API文档):

这些是简单的事件,仅提供一小部分提供的基础处理程序CEF。这些事件仅在主浏览器中被调用,对于弹出窗口处理,您可以使用IDisplayHandler和来访问通知ILoadHandler

为了确定页面何时完成加载,我建议在FrameLoadEnd上使用LoadingStateChanged。重要的是要记住,完成的加载不同于完成的渲染。当前尚无确定网页何时完成渲染的方法(Flash,动态内容,动画等功能,甚至像移动鼠标或滚动之类的简单任务也将导致渲染新帧)。

IDialogHandlerIDisplayHandlerIDownloadHandlerIContextMenuHandlerILifeSpanHandlerILoadHandlerIRequestHandler是一些更常见的处理程序(参见其余部分的源极/ API DOC)。这些仅以方便的.NET方式包装基础CEF处理程序。例如CEF的CefDownloadHandlerIDownloadHandlerCefSharp。实施这些处理程序将使您能够访问作为CEF基础的基础事件和回调。可以使用回调以异步方式执行许多处理程序的成员。所有处理程序都遵循一致的模式:返回a的处理程序bool询问您是否要自己处理。如果否,则返回false默认操作。true如果您自己处理,请返回。

它们是您实现并分配给ChromiumWebBrowser实例的基本接口。例如

browser.DownloadHandler = new DownloadHandler();

理想情况下,您应在ChromiumWebBrowser实例化实例后立即设置处理程序。有关更多详细示例,请参见源代码中的示例项目。当前没有可用的默认实现,因此您必须实现每种方法。(如果您希望提供默认实现,请提交请求请求)。

有关处理程序的一些一般说明

可以使用来修改响应ResponseFilter。请参阅以下部分。

请求处理Request Handling

CEF支持两种方法来处理应用程序内部的网络请求。

  1.  Scheme Handler方法允许用于靶向特定原点(方案+结构域)的请求的处理程序的注册。
  2.  Request Interception 方法允许在处理应用程序的自由裁量权的任意请求。

使用HTTP(S)方案而不是自定义方案,可以避免一系列潜在的问题。

如果您选择使用自定义方案(比其他任何事情http://https://等),你必须用CEF注册它,这样它会像预期的那样。如果您希望自定义方案的行为类似于HTTP(支持POST请求并强制实施HTTP访问控制(CORS)限制),则应将其注册为“标准”方案。如果您打算对其他方案执行跨域请求或将POST请求发送XMLHttpRequest到方案处理程序,则应使用HTTP方案而不是自定义方案,以避免潜在的问题。IsSecureIsCorsEnabled参数最近添加。

处理程序可以使用这两个内置的方案(http://https://,等)和自定义方案。使用内置方案时,请为您的应用程序选择一个唯一的域名(如myappinternal)。实现ISchemeHandlerFactoryIResourceHandler类来处理请求并提供响应数据。有关IResourceHandler的默认实现,请参阅ResourceHandler,它具有许多有用的静态帮助器方法。

   Scheme Handler

处理程序可与内置方案(HTTP,HTTPS等)和自定义方案一起使用。使用内置方案时,请为您的应用程序选择一个唯一的域名(如myappinternal)。实现ISchemeHandlerFactoryIResourceHandler类以处理请求并提供响应数据。有关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 null
							defaultPage: "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 implementation

		var 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;
	}
}

提供的ResourceHandlerIResourceHandler的默认实现,并且包含许多用于创建类的静态帮助器方法。有关更多详细信息,请参见下面的“资源处理程序”部分。

使用静态方法的一些示例是:

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类。提供的ResourceHandlerIResourceHandler的默认实现,并且包含许多用于创建类的静态帮助器方法。有关更多详细信息,请参见下面的“资源处理程序”部分。

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's
		if (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.RegisterResourceHandlerIWebBrowser.UnRegisterResourceHandler扩展方法提供提供一种简单的方法IResourceHandler对于给定的Url

例如,您可以请求一个虚构的URL并提供一个响应,就像该网站是真实的一样。

资源处理程序ResourceHandler

ISchemeHandlerFactoryIResourceRequestHandler.GetResourceHandler使用IResourceHandler接口来表示响应(流+头+状态码,等等)。IResourceHandler的默认实现只是ResourceHandler

为了方便起见,ResourceHandler包含许多静态方法

使用静态方法的一些示例是:

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 buffer
				stream.Position = 0;

				//Populate the response values
				ResponseLength = 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 stream
		var 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's
		if (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.Length
		dataInRead = 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 rest
		if (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的更多信息,请参见https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs

自己生成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是最好的资源。

代理解析

有两个用于配置代理服务器的选项。

  1. CEF使用与Google Chrome相同的命令行标志。
  2. 可以使用IRequestContext.SetPreference在运行时设置/更改代理设置。
    • IRequestContext.SetPreference必须在CEF UI线程上调用。使用Cef.UIThreadTaskFactory在上生成任务CEF UI ThreadIRequestContextHandler方法已经在CEF UI线程上调用,因此您可以SetPreference直接调用。
    • 可以逐个指定代理设置,Request Context从而使您可以ChromiumWebBrowser使用不同的代理来拥有不同的实例。
    • 阅读下面的“请求上下文”部分,以获取更多详细信息和基本代码示例。

如果代理要求身份验证,则将使用值为的IRequestHandler.GetAuthCredentials()回调执行以检索用户名和密码。isProxytrue

http://stackoverflow.com/questions/36095566/cefsharp-3-set-proxy-at-runtime上可以找到使用Preferencesin设置代理的一些其他示例。CefSharp

请求上下文(浏览器隔离)

隔离浏览器实例的方法,包括提供自定义缓存路径,不同的代理设置,不同的Cookie管理器以及许多其他功能RequestContext。在较新的版本中,PPAPI插件的加载是在该RequestContext级别上进行的。在CEF条款的底层类是CefRequestContext

以下是一些关键点:

//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 ChromiumWebBrowser
    var 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 behaviour
            return 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 24pt
		string 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问题追踪器上已经有公开的讨论和未解决的问题。

高DPI显示/支持

WinForms/WPF需要使使用DPI的桌面应用程序能够在高DPI显示器DPI Scale设置大于的显示器)上正确运行DPI100%

注意如果鼠标光标在浏览器中的位置不正确,或者浏览器显示带有渲染/调整大小的黑框/边框,则需要制作您的应用程序DPI Aware。应用程序的其他部分也可能会显得模糊或尺寸不正确。

有许多选项可用于配置流程的DPI意识:

  1. 通过应用程序清单设置(通常是首选)
  2. 通过app.config仅限WinForms,目标是.Net 4.7及更高版本)
  3. 通过API调用以编程方式

Windows 10 1703具有其他改进,有关更多详细信息,请参见https://blogs.windows.com/windowsdeveloper/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/

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。有关如何创建的信息,请参见https://stackoverflow.com/a/26890426/4583726Program.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);

多线程消息循环

CefSharp默认使用setting.MultiThreadedMessageLoop = true。这使您的应用程序能够非常快速地启动并运行,需要注意一些重要的事情,但这可能并不适合所有人。

可以将CEF集成到应用程序的现有消息循环中。将CEF集成到现有消息循环中的一种非常简单的实现涉及在UI线程上使用每秒调用30/60次的计时器。

var settings = new CefSettings();
settings.MultiThreadedMessageLoop = false; //This defaults to true

Cef.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 ChromiumWebBrowser
    newBrowser = null;

    return true; //Return true to cancel the popup creation
}

您可以取消弹出窗口的创建,然后以新的形式打开URL ChromiumWebBrowser使用此方法您选择实例中。重要的是要注意,使用此方法将不存在父子关系。因此,一般不建议这样做。

实验选项1:允许您使用中的newBrowser参数托管弹出窗口OnBeforePopup。有一些已知问题(在GitHub项目上搜索)。如果您使用此方法遇到问题,那么您将必须承担责任并通过CEF项目解决该问题。同样重要的是要注意LoadingStateChangedetc不会被弹出窗口调用。如果使用此方法,请实现相关的处理程序。

实验选项2:IWindowInfo.SetAsChild用于指定父句柄。要在WPF中使用此功能,您将需要使用WinForms主机。使用此方法,您将需要处理move和resize事件。大致如下所示:

尽管它们是实验性的,但是在项目源中有一些示例,并且不能保证它们在起作用。备选案文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.OnContextCreatedIRenderProcessMessageHandler.OnContextReleased提供时,可以执行JavaScript的一个边界。OnContextCreated/OnContextReleased将每帧调用一次,用于frame.IsMain检查主帧。

尝试开始访问DOM是很诱人的OnFrameLoadStart,而V8Contextwill已创建,您将能够执行DOM尚未完成加载的脚本。如果您需要DOM尽早访问,请订阅DOMContentLoadedJavaScript下面是一些执行示例。

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 loading
  if (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 loading
  if(args.Frame.IsMain)
  {
    args.Frame.ExecuteJavaScriptAsync("alert('MainFrame finished loading');");
  }
};

有关执行的一些注意事项JavaScript

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

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

概要

如果您不熟悉这里提供的所有Chromium内容,那么async programming可以参考一些非常有用的文章

绑定Async对象JavaScript

CefSharp.BindObjectAsync方法被称为在Javascript结合的对象。CefSharp.BindObjectAsync返回一个Promise,当绑定的对象可用时,该Promise将被解决。在全局上下文(window对象的属性)中创建对象。如果调用时CefSharp.BindObjectAsync没有任何参数,则所有已注册的对象都将被绑定。名称绑定是更具描述性的选项。

简单的工作流程如下所示:

仅支持方法。如果需要设置属性,则创建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 null
		bindingOptions = BindingOptions.DefaultBinder //Use the default binder to serialize values into complex objects
		bindingOptions = new BindingOptions { Binder = new MyCustomBinder() }); //Specify a custom binder
		repo.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 camelCase
		repo.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调用,则如果已注册对象,则将它们全部绑定,如果未注册对象,则将ResolveObjectObjectName设置为All

本节仅介绍基础知识,还有许多高级选项,请查看高级异步Javascript绑定。

如果您想查看一个可行的示例,请查看CefSharp MinimalExample Javascript Binding Demo分支,特别是commit


同步JavaScript绑定(JSB)

这是一个遗产功能-任何正在创建新应用程序的人都在使用Async JavaScript Binding(JSB)实现,因为它正在积极开发中。该Sync版本仅会收到针对回归的错误修复。

绑定对象 JavaScript

绑定是由JavaScript启动的,当绑定的对象可用时,该CefSharp.BindObjectAsync方法将返回Promise解析的结果。在全局上下文(window对象的属性)中创建对象。如果调用时CefSharp.BindObjectAsync没有任何参数,则所有已注册的对象都将被绑定。名称绑定是更具描述性的选项。

简单的工作流程如下所示:

步骤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 are
                var 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 = 8
		assert.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 object
		assert.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调用时,JavascriptObjectRepositoryis查询查询是否已指定给定名称的对象,如果找不到匹配的对象,ResolveObject则引发该事件。对于CefSharp.BindObjectAsync不带任何参数的调用,则如果已经注册了对象,则将它们全部绑定,如果没有注册任何对象,则将ResolveObjectObjectName设置为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 null
		bindingOptions = 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 camelCase
		repo.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

请注意:

RegisterAsyncJsObject

此方法已删除。请参阅异步JavaScript绑定(JSB)

RegisterJsObject

这已被删除。请参阅同步JavaScript绑定(JSB)

Adobe Flash Player(Pepper Flash)

注意:现在不建议使用Flash,并且Chromium将删除支持,有关更多详细信息,请参阅https://www.chromium.org/flash-roadmap#TOC-Upcoming-Changes。从版本开始,81默认情况下现在已禁用它,请参见https://github.com/cefsharp/CefSharp/issues/3048#issuecomment-592263009

CefSharp可以从Adobe下载可以自动发现并加载的Pepper Flash的系统范围安装。从下拉列表中选择FP for Opera and Chromium-PPAPI版本。要测试Flash是否正常运行,只需加载http://www.adobe.com/software/flash/about/

注意首次打开Flash时,将短暂显示控制台窗口,显示NOT SANDBOXED。有一个问题Chromium问题跟踪,但不幸的是Google已经将其标记为WontFix。一些聪明的人一起破解了一些解决方法。它们很复杂,我从未尝试过。请访问https://github.com/cefsharp/CefSharp/issues/1259中的链接以获取详细信息。

屏幕外渲染(OSR)

WPF和OffScreen版本使用OffScreen Rendering(OSR)渲染模式。在OSR模式每帧被渲染到缓冲器中,然后在屏幕上或者绘制为在WPF的情况下,或提供作为BitmapOffScreen

WPF

对于WPF控件,用户输入(鼠标单击/移动和按键)将通过IBrowserHost界面上的方法转发到基础浏览器。可以访问每个Bitmap渲染的对象。

应特别注意ChromiumWebBrowser在内托管ViewBox。这远非理想,因为渲染了每一帧,然后进行后处理来调整图像的大小/缩放。这会严重影响性能,并且通常会降低质量(通常很模糊)。您可以使用调整调整大小的质量RenderOptions.SetBitmapScalingMode。最好避免使用ViewBox。您可以通过调整来缩放浏览器中包含的内容ZoomLevel,这是迄今为止性能最高的选项。

屏幕外

对于CefSharp.OffScreen包装,将每个帧渲染到Bitmap并暴露以供使用。如果希望通过键盘或鼠标与浏览器进行交互,则可以使用IBrowser主机界面上的方法。模拟按键和鼠标单击/移动可能非常复杂。您可以使用WPF控件作为开始示例,因为它使用相同的方法(添加调试以查看所需的事件顺序)。按键和鼠标的点击/移动通常由多个部件,up/down与许多其它可能的组合。

用户代理

您可以通过设置https://cefsharp.github.io/api/86.0.x/html/P_CefSharp_CefSettingsBase_UserAgent.htm来指定自定义UserAgent

UserAgent可以在运行时使用DevTools协议来改变看到https://stackoverflow.com/a/64543667/4583726的一个例子。

您可以在中修改User-AgentHTTP标头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 value
		request.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 agent
		if (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

无论OffscreenWPF使用的屏幕外着色(OSR),其中每一帧被渲染为位图。它仍然是一个网络浏览器,并不是特别适合这种情况。这里有一些注意事项:

WinForms

这是在Windows下拍摄屏幕快照的一些示例

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 data
		if (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/POST
		var postData = new PostData();

		postData.AddData("test=123&data=456");

		request.Method = "POST";
		request.PostData = postData;
		//Set the Content-Type header to whatever suites your requirement
		request.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 PostData
	var request = frame.CreateRequest(initializePostData:true);
	request.Method = "POST";
	request.Url = "http://httpbin.org/post";
        //Set AllowStoredCredentials so cookies are sent with Request
        request.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

并非所有语言都支持拼写检查,请参阅https://magpcss.org/ceforum/viewtopic.php?f=6&t=16508#p40684

Web组装

在较新的版本中默认为启用,请参见https://www.chromestatus.com/feature/5453022515691520

对于较旧的版本,您需要手动启用,WebAssembly请参见https://bitbucket.org/chromiumembedded/cef/issues/2101/add-webassembly-support

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环境中捕获未处理的异常 http://www.ikriv.com/blog/?p=1440

依赖检查

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实例中打开http://html5test.com/

CEF对的支持编译H264/AAC超出了该项目的范围。以下内容仅供参考,请不要寻求支持CEF

屏幕(虚拟)键盘

WinForms版本已经内置在屏幕键盘的支持,它已经报道,有时它并不总是正确弹出,使用disable-usb-keyboard-detect命令行参数 https://github.com/cefsharp/CefSharp/issues/1691#issuecomment-323603277报道解决这个问题。

WPF屏幕版本(虚拟)开始,它没有内置的支持,从版本开始,73VirtualKeyboardRequested事件现在会在您的应用程序应显示虚拟键盘时提供通知。不幸的是,Windows 7, 8.1 and 10由于没有.Net API显示虚拟键盘的功能,因此很难提供支持的默认实现。Windows 10 Onlyhttps://github.com/cefsharp/CefSharp/commit/0b57e526158e57e522d46671404c557256529416中添加了一个示例如果您需要支持,Windows 8 and 10https://github.com/maximcus/WPFTabTip可能会有用。对于Windows 7 https://stackoverflow.com/questions/1168203/incorporating-the-windows-7-onscreen-keyboard-into-a-wpf-app有一些建议。

 

标签:CEF,CefSharp,中文,文档,https,new,com,browser
来源: https://blog.csdn.net/fuhanghang/article/details/110468063