CefSharp 是圍繞Chromium嵌入式框架(?Chromium Embedded Framework,CEF)的簡單.Net包裝器。CEF是一個基于Google Chromium項目的開源項目。與Chromium項目本身(主要專注于Google Chrome應用程序開發(fā))不同,CEF專注于促進第三方應用程序中的嵌入式瀏覽器用例。?CEF 基于多進程Chromium Content API,因此,當前僅存在Chromium的部分功能。例如,對擴展的支持是有限的,僅實現(xiàn)了一部分Extension API 。
CefSharp 提供三種不同的類型WinForms ,WPF 和OffScreen 。在WPF 與OffScreen 版本使用的OffScreen Rendering(OSR) 渲染模式。在OSR 模式中,每個幀被渲染到緩沖器,然后或者在屏幕上繪制作為的情況下WPF 或可作為Bitmap 在OffScreen 。所有版本都使用CefSharp 和CefSharp.Core 庫,因此API 在這三種風格中,大部分使用的庫都完全相同。這減少了代碼重復并降低了添加新功能的維護負擔,唯一的缺點是該WPF 版本并不像它可能的那樣友好(您可以將其ChromiumWebBrowser 歸類,并在應用程序中實現(xiàn)所需的任何缺少的部分)。您也可以托管WinForms WPF 使用中的版本號WindowsFormsHost ,可能需要繞過該WPF 版本的某些限制(CEF 尚未在OSR 模式中實現(xiàn)完全的觸摸屏支持,在上存在一個開放問題CEF Issue Tracker ,如果您需要這樣做,請參與其中)。
發(fā)行說明
有關(guān)每個版本的發(fā)行說明,請訪問https://github.com/cefsharp/CefSharp/releases,如果您有問題或?qū)Ω挠兴闷?,請抽出時間閱讀它們。如果遇到問題,請查看“已知問題”部分,通常會有一些說明包含有關(guān)發(fā)行版的有用信息。
軟件需求
CefSharp 使用Visual C (VC )與本機C API交互,因此它只能在Windows上運行。(沒有Windows APP Store版本)。CefSharp 在每個第二Chromium 版本上發(fā)布版本,例如47、49、51。每個CefSharp 版本都有其自己的分支,有關(guān)每個分支的詳細信息和要求,請參見https://github.com/cefsharp/CefSharp#release-branches。Google 最近去除了對較早的操作系統(tǒng)的支持,例如Windows XP,Vista及其服務(wù)器版本。如果您要求您的應用程序在這些操作系統(tǒng)上運行,請查看發(fā)行版以獲取更多詳細信息https://github.com/cefsharp/CefSharp/releases
CefSharp要求:
筆記:
任何CPU支持
較新的版本現(xiàn)在支持定位AnyCPU ,有關(guān)如何實現(xiàn)此功能的詳細信息,請參見https://github.com/cefsharp/CefSharp/issues/1714??梢允褂孟嗤募夹g(shù)將libcef.dll 等等移動到磁盤上的其他文件夾或公共位置。
?
需要知道/限制
例子
CefSharp 源代碼包含的許多不同的特征的實施例。還有一個MinimalExample 項目使用最新的Nuget 軟件包提供非常簡單的Browser 實現(xiàn)。這MinimalExample 是入門的最佳位置,下載此項目并使其運行以作為基礎(chǔ)參考,以確保一切都在您的系統(tǒng)上正常工作。
https://github.com/cefsharp/CefSharp.MinimalExample
記錄中
默認情況下CEF ,在應用程序的執(zhí)行文件夾(例如)中維護其自己的日志文件('Debug.log')bin 。要禁用日志記錄更改settings.LogSeverity ,并更改文件名/路徑,請使用settings.LogFile 。
調(diào)試問題時,首先要檢查的地方是此日志文件,因為它包含低級Chromium 消息。如果您看到錯誤或警告,請搜索http:///ceforum/index.php和https:///chromiumembedded/cef/issues?status=new&status=open
工藝流程
CEF 使用多進程運行。處理窗口創(chuàng)建,繪畫和網(wǎng)絡(luò)訪問的主進程稱為browser 進程。通常,此過程與主機應用程序相同,并且大多數(shù)應用程序邏輯將在瀏覽器進程中運行。閃爍呈現(xiàn)和JavaScript執(zhí)行在單獨的render 過程中進行。一些應用程序邏輯(例如JavaScript綁定)也將在渲染過程中運行。默認進程模型將為每個唯一的來源(方案 域)生成一個新的渲染過程。將根據(jù)需要生成其他進程,例如處理插件(如Flash)的“插件”進程和處理加速合成的“ gpu”進程。
默認情況CefSharp 下,該render 進程的默認實現(xiàn)稱為CefSharp.BrowserSubProcess.exe 。如上所述,將多次產(chǎn)生此過程以表示單獨的過程。從版本開始,51.0.0 可以提供自己的自定義BrowserSubProcess ,因為可執(zhí)行文件現(xiàn)在是基礎(chǔ)VC 實現(xiàn)的非常簡單的包裝。
https:///chromiumembedded/cef/issues/2498/add-support-for-site-per-process#comment-54186905包含有關(guān)當前默認流程模型的詳細信息。
線程數(shù)
CEF使用多個線程進行不同級別的處理。例如browser ,該過程包含以下通常引用的線程:
- UI線程是瀏覽器過程中的主線程。默認情況下
CefSharp 使用,setting.MultiThreadedMessageLoop = true 因此該CEF UI 線程不同于您的主應用程序線程 - IO線程在瀏覽器進程中用于處理IPC和網(wǎng)絡(luò)消息
- FILE線程在瀏覽器進程中用于與文件系統(tǒng)進行交互
- RENDERER線程是渲染器過程中的主線程
初始化和關(guān)閉
Initialize 每個進程(應用程序)只能調(diào)用一次??梢赃\行您的應用程序的多個實例,您需要CachePath 為每個實例提供唯一的實例,請參閱CefSettings 下文。
有關(guān)如何在運行時更改設(shè)置,隔離瀏覽器實例,為不同實例設(shè)置不同的緩存路徑的詳細信息,請參見請求上下文(瀏覽器隔離)。
重要的是要注意,有必要初始化基礎(chǔ)CEF 庫。這可以通過顯式和隱式兩種方式之一來實現(xiàn)。創(chuàng)建新實例時ChromiumWebBrowser ,它將檢查CEF是否已初始化,如果尚未初始化,請使用默認值為您初始化。對于那些希望指定一些自定義設(shè)置的用戶,您可以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.Shutdown,ChromiumWebBrowser 將鉤住相關(guān)Application Exit事件的WinForms和WPF實例,并且默認情況下調(diào)用Cef.Shutdown()。設(shè)置CefSharpSettings.ShutdownOnExit =false;?禁用此行為。在ChromiumWebBrowser 創(chuàng)建事件的第一個實例之前,需要設(shè)置此值,因為事件處理程序已掛接到ChromiumWebBrowser 該類的靜態(tài)構(gòu)造函數(shù)中。
重要的是要注意CEF?Initialize /Shutdown ?必須在主應用程序線程(通常是UI線程)上調(diào)用。如果您在不同的線程上調(diào)用它們,則您的應用程序?qū)炱稹?/p>
一個使用Initialize /Shutdown 手動調(diào)用/的示例,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();
}
}
綜上所述
Cef.Initialize 并且Cef.Shutdown 每個進程(應用程序)只能調(diào)用一次Initialize,因此僅使用完后CefSharp后才調(diào)用Shutdown 。Cef.Initialize 并且Cef.Shutdown 必須在同一線程上調(diào)用。Cef.Initialize 如果您創(chuàng)建新ChromiumWebBrowser 實例并且尚未調(diào)用,則會為您隱式調(diào)用Cef.Initialize 。- 對于WinForms和WPF實例,默認情況下
ChromiumWebBrowser 相關(guān)的Application Exit事件被鉤住,然后默認調(diào)用Cef.Shutdown() 方法處理。設(shè)置CefSharpSettings.ShutdownOnExit = false;用于 禁用此行為。在ChromiumWebBrowser 創(chuàng)建事件的第一個實例之前,需要設(shè)置此值,因為事件處理程序已掛接到ChromiumWebBrowser 該類的靜態(tài)構(gòu)造函數(shù)中。 - 在其中
CefSharp.OffScreen ,必須Cef.Shutdown() 在應用程序存在之前顯式調(diào)用它,否則它將掛起。
CefSettings和BrowserSettings
該CefSettings 結(jié)構(gòu)允許配置應用程序范圍的CEF設(shè)置。一些通常配置的成員包括:
BrowserSubprocessPath 此路徑是 子流程啟動的獨立可執(zhí)行文件的路徑。通常,無需更改此設(shè)置。MultiThreadedMessageLoop 在CefSharp中 默認值為True,盡管可以將其集成CEF 到您的應用程序現(xiàn)有的消息循環(huán)中,請參閱下面的MultiThreadedMessageLoop部分。CommandLineArgsDisabled? 設(shè)置為true可禁用使用標準CEF和Chromium命令行參數(shù)配置瀏覽器進程功能的功能。有關(guān)更多信息,請參見“命令行參數(shù)”部分。RootCachePath? 所有CefSettings.CachePath 和RequestContextSettings.CachePath 值必須具有相同的根目錄。如果此值為空且CefSettings.CachePath 非空,則默認為該CefSettings.CachePath 值。如果此值為非空值,那么它必須是絕對路徑。非空RootCachePath可以與空CefSettings.CachePath結(jié)合使用,在您希望瀏覽器連接到以“隱身模式”創(chuàng)建的Global RequestContext(默認)的實例以及使用基于磁盤的緩存使用自定義RequestContext創(chuàng)建的實例的情況下, 。有關(guān)更多詳細信息,請參見下面的RequestContext部分。CachePath? 全局瀏覽器緩存的數(shù)據(jù)將存儲在磁盤上的位置。此值是非空的,那么它必須是絕對路徑,該路徑必須等于CefSettings.RootCachePath或子目錄(如果RootCachePath為空,則默認為該值)。如果該值為空,則將在“隱身模式”下創(chuàng)建瀏覽器,在該模式下,將使用內(nèi)存中的緩存進行存儲,并且不會將任何數(shù)據(jù)持久化到磁盤上。如果指定了緩存路徑,則諸如localStorage之類的HTML5數(shù)據(jù)庫將僅在會話之間持久存在??梢?code>RequestContext通過該RequestContextSettings.CachePath 值覆蓋單個實例。有關(guān)更多詳細信息,請參見下面的RequestContext部分。Locale? 將傳遞給Blink的語言環(huán)境字符串。如果為空,則將使用默認語言環(huán)境“ en-US”。也可以使用“ lang”命令行開關(guān)進行配置。更改此項以同時設(shè)置上下文菜單語言。LogFile? 用于調(diào)試日志的目錄和文件名。如果為空,將使用默認名稱“ debug.log”,并將文件寫入應用程序目錄。也可以使用“ log-file”命令行開關(guān)進行配置。LogSeverity? 日志嚴重性。僅記錄此嚴重級別或更高級別的消息。也可以使用“ log-severity”命令行開關(guān)進行配置,其值為“ verbose”,“ info”,“ warning”,“ error”,“ error-report”或“ disable”。ResourcesDirPath? 資源目錄的標準路徑。如果此值為空,則cef.pak和/或devtools_resources.pak文件必須位于模塊目錄中。也可以使用“ resources-dir-path”命令行開關(guān)進行配置。LocalesDirPath? 語言環(huán)境目錄的標準路徑。如果此值為空,則語言環(huán)境目錄必須位于模塊目錄中。在Mac OS X上,始終從應用程序包Resources目錄中加載打包文件的情況下,將忽略此值。也可以使用“ locales-dir-path”命令行開關(guān)進行配置。RemoteDebuggingPort? 設(shè)置為1024到65535之間的值以在指定的端口上啟用遠程調(diào)試。例如,如果指定8080,則遠程調(diào)試URL將為http:// localhost:8080??梢詮娜魏蜟EF或Chrome瀏覽器窗口中遠程調(diào)試CEF。也可以使用“ remote-debugging-port”命令行開關(guān)進行配置。
有許多設(shè)置和命令行參數(shù)可能會影響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);
}
些設(shè)置可以應用于特定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 對象用于將命令發(fā)送到瀏覽器,并在回調(diào)方法中返回狀態(tài)信息。每個IBrowser 對象都有一個代表頂層框架的main?IFrame 對象,以及零個或多個sub?IFrame 對象。
例如,加載兩個HTML<iframe> 的瀏覽器將具有三個IFrame 對象(頂級框架和兩個<iframe> )。
要將URL加載到瀏覽器主機中:
browser.MainFrame.LoadUrl(someurl);
CefSharp 提供了許多擴展方法,使執(zhí)行常見任務(wù)更加容易。請參閱參考資料WebBrowserExtensions ,以獲取這些方法的來源,并更好地了解如何執(zhí)行常見任務(wù)。
IBrowserHost ?代表更底層的瀏覽器方法。
處理程序
CefSharp 為了方便起見,提供了一些事件,如下所示(有關(guān)所有常見事件以及有關(guān)其用法的詳細信息,請參見IWebBrowser?API文檔):
這些是簡單的事件,僅提供一小部分提供的基礎(chǔ)處理程序CEF 。這些事件僅在主瀏覽器中被調(diào)用,對于彈出窗口處理,您可以使用IDisplayHandler 和來訪問通知ILoadHandler 。
為了確定頁面何時完成加載,我建議在FrameLoadEnd上使用LoadingStateChanged。重要的是要記住,完成的加載不同于完成的渲染。當前尚無確定網(wǎng)頁何時完成渲染的方法(Flash,動態(tài)內(nèi)容,動畫等功能,甚至像移動鼠標或滾動之類的簡單任務(wù)也將導致渲染新幀)。
IDialogHandler ,IDisplayHandler ,IDownloadHandler ,IContextMenuHandler ,ILifeSpanHandler ,ILoadHandler 和IRequestHandler 是一些更常見的處理程序(參見其余部分的源極/ API DOC)。這些僅以方便的.NET方式包裝基礎(chǔ)CEF處理程序。例如CEF的CefDownloadHandler 是IDownloadHandler 在CefSharp 。實施這些處理程序?qū)⑹鼓軌蛟L問作為CEF基礎(chǔ)的基礎(chǔ)事件和回調(diào)??梢允褂没卣{(diào)以異步方式執(zhí)行許多處理程序的成員。所有處理程序都遵循一致的模式:返回a的處理程序bool 詢問您是否要自己處理。如果否,則返回false 默認操作。true 如果您自己處理,請返回。
它們是您實現(xiàn)并分配給ChromiumWebBrowser 實例的基本接口。例如
browser.DownloadHandler = new DownloadHandler();
理想情況下,您應在ChromiumWebBrowser 實例化實例后立即設(shè)置處理程序。有關(guān)更多詳細示例,請參見源代碼中的示例項目。當前沒有可用的默認實現(xiàn),因此您必須實現(xiàn)每種方法。(如果您希望提供默認實現(xiàn),請?zhí)峤徽埱笳埱螅?/p>
有關(guān)處理程序的一些一般說明
可以使用來修改響應ResponseFilter 。請參閱以下部分。
請求處理Request Handling
CEF支持兩種方法來處理應用程序內(nèi)部的網(wǎng)絡(luò)請求。
- ?Scheme Handler方法允許用于靶向特定原點(方案 結(jié)構(gòu)域)的請求的處理程序的注冊。
- ?Request Interception?方法允許在處理應用程序的自由裁量權(quán)的任意請求。
使用HTTP(S)方案而不是自定義方案,可以避免一系列潛在的問題。
如果您選擇使用自定義方案(比其他任何事情http:// ,https:// 等),你必須用CEF注冊它,這樣它會像預期的那樣。如果您希望自定義方案的行為類似于HTTP(支持POST請求并強制實施HTTP訪問控制(CORS)限制),則應將其注冊為“標準”方案。如果您打算對其他方案執(zhí)行跨域請求或?qū)OST請求發(fā)送XMLHttpRequest 到方案處理程序,則應使用HTTP方案而不是自定義方案,以避免潛在的問題。IsSecure 和IsCorsEnabled 參數(shù)最近添加。
處理程序可以使用這兩個內(nèi)置的方案(http:// ,https:// ,等)和自定義方案。使用內(nèi)置方案時,請為您的應用程序選擇一個唯一的域名(如myapp 或internal )。實現(xiàn)ISchemeHandlerFactory 和IResourceHandler類來處理請求并提供響應數(shù)據(jù)。有關(guān)IResourceHandler的默認實現(xiàn),請參閱ResourceHandler,它具有許多有用的靜態(tài)幫助器方法。
? ?Scheme Handler
處理程序可與內(nèi)置方案(HTTP,HTTPS等)和自定義方案一起使用。使用內(nèi)置方案時,請為您的應用程序選擇一個唯一的域名(如myapp 或internal )。實現(xiàn)ISchemeHandlerFactory和IResourceHandler類以處理請求并提供響應數(shù)據(jù)。有關(guān)IResourceHandler的默認實現(xiàn),請參閱ResourceHandler,它具有許多有用的靜態(tài)幫助器方法。
計劃處理程序通過CefSettings.RegisterScheme函數(shù)進行注冊。例如,您可以為“ 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是使用一個方案處理從磁盤讀取文件的簡單默認實現(xiàn)。您可以使用自定義方案(換句話說,您可以以形式提供URL?customscheme://folder/yourfile )或標準方案(https:// ,https:// )。
實現(xiàn)自己的工廠的示例可能如下所示:
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;
}
}
提供的ResourceHandler是IResourceHandler的默認實現(xiàn),并且包含許多用于創(chuàng)建類的靜態(tài)幫助器方法。有關(guān)更多詳細信息,請參見下面的“資源處理程序”部分。
使用靜態(tài)方法的一些示例是:
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);
}
計劃注冊必須在調(diào)用之前進行,這一點很重要Cef.Initialize() 。
請求攔截Request Interception
IResourceRequestHandler.GetResourceRequestHandler支持攔截任意請求。它使用與方案處理程序方法相同的IResourceHandler類。提供的ResourceHandler是IResourceHandler的默認實現(xiàn),并且包含許多用于創(chuàng)建類的靜態(tài)幫助器方法。有關(guān)更多詳細信息,請參見下面的“資源處理程序”部分。
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.RegisterResourceHandler和IWebBrowser.UnRegisterResourceHandler擴展方法提供提供一種簡單的方法IResourceHandler對于給定的Url 。
例如,您可以請求一個虛構(gòu)的URL并提供一個響應,就像該網(wǎng)站是真實的一樣。
資源處理程序ResourceHandler
ISchemeHandlerFactory和IResourceRequestHandler.GetResourceHandler使用IResourceHandler接口來表示響應(流 頭 狀態(tài)碼,等等)。IResourceHandler的默認實現(xiàn)只是ResourceHandler。
為了方便起見,ResourceHandler包含許多靜態(tài)方法
使用靜態(tài)方法的一些示例是:
ResourceHandler.FromStream(stream, mimeType);
ResourceHandler.FromString(htmlString, includePreamble:true, mimeType:Cef.GetMimeType(fileExtension));
ResourceHandler.FromFilePath("CefSharp.Core.xml", mimeType);
該源代碼包含ISchemeHandlerFactory的詳細示例。
實施實例ResourceHandler.ProcessRequestAsync 。如果需要完全控制,則可以實現(xiàn)IResourceHandler,但是在大多數(shù)情況下,這不是必需的。
//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./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()支持過濾響應請求而接收的數(shù)據(jù)。您可以檢索原始響應數(shù)據(jù),也可以將數(shù)據(jù)追加到響應中,例如在文件末尾注入一些自定義CSS。您可以根據(jù)需要重寫響應??捎糜诮邮杖魏握埱蟮捻憫?,即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./" || request.Url == "https://cefsharp./")
{
return new CustomResourceRequestHandler();
}
//Default behaviour, url will be loaded normally.
return null;
}
}
browser.RequestHandler = new CustomRequestHandler();
當前,StreamResponseFilter是框架中提供的唯一過濾器。
將dataIn復制到dataOut的簡單響應過濾器。數(shù)據(jù)以塊的形式進行流傳輸,通常大小為64kb。
/// <summary>
/// PassThruResponseFilter - copies all data from DataIn to DataOut.
/// Upstream documentation link
/// https:///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()
{
}
}
有關(guān)IResourceFilter的CefSharp.Example 其他示例實現(xiàn),請參見源代碼中的項目。該功能實現(xiàn)起來非常復雜。提出任何問題之前,請確保您已閱讀并調(diào)試了現(xiàn)有示例。
從磁盤/數(shù)據(jù)庫/嵌入式資源/流中加載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>");
有關(guān)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 處理程序或?qū)崿F(xiàn)自己的處理程序IResourceRequestHandlerFactory 。(data: 特別是對于OffScreen 項目而言,加載編碼的URI也非常方便)。
如果您選擇忽略此建議,則必須解決file:/// 自己遇到的任何問題。ceforum 是最好的資源。
代理解析
有兩個用于配置代理服務(wù)器的選項。
- CEF使用與Google Chrome相同的命令行標志。
- 可以使用IRequestContext.SetPreference在運行時設(shè)置/更改代理設(shè)置。
如果代理要求身份驗證,則將使用值為的IRequestHandler.GetAuthCredentials()回調(diào)執(zhí)行以檢索用戶名和密碼。isProxy true
在http:///questions/36095566/cefsharp-3-set-proxy-at-runtime上可以找到使用Preferences in設(shè)置代理的一些其他示例。CefSharp
請求上下文(瀏覽器隔離)
隔離瀏覽器實例的方法,包括提供自定義緩存路徑,不同的代理設(shè)置,不同的Cookie管理器以及許多其他功能RequestContext 。在較新的版本中,PPAPI插件的加載是在該RequestContext 級別上進行的。在CEF 條款的底層類是CefRequestContext 。
以下是一些關(guān)鍵點:
//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());
有關(guān)更多詳細示例,請參見項目源。
//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中設(shè)置首選項(建議使用此方法來設(shè)置代理,因為它將在瀏覽器嘗試加載任何網(wǎng)頁之前被調(diào)用)
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:///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模式下打?。ù蛴〉?jīng)]有對話框的默認設(shè)置)。建議的解決方法是先打印,PDF 然后使用3rd party 應用程序來打印PDF 。
如果您需要更好的打印支持,則應在上進行討論ceforum 。在CEF問題追蹤器上已經(jīng)有公開的討論和未解決的問題。
高DPI顯示/支持
WinForms/WPF 需要使使用DPI的桌面應用程序能夠在高DPI顯示器(DPI Scale 設(shè)置大于的顯示器)上正確運行DPI100% 。
注意如果鼠標光標在瀏覽器中的位置不正確,或者瀏覽器顯示帶有渲染/調(diào)整大小的黑框/邊框,則需要制作您的應用程序DPI Aware 。應用程序的其他部分也可能會顯得模糊或尺寸不正確。
有許多選項可用于配置流程的DPI意識:
- 通過應用程序清單設(shè)置(通常是首選)
- 通過app.config(僅限WinForms,目標是.Net 4.7及更高版本)
- 通過API調(diào)用以編程方式
Windows 10 1703具有其他改進,有關(guān)更多詳細信息,請參見https://blogs./windowsdeveloper/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/。
WinForms高DPI
從.NET Framework 4.7開始,Windows Forms包括針對常見的高DPI和動態(tài)DPI方案的增強功能。在.NET Framework的早期版本中,您使用清單添加了高級DPI支持。不再建議使用此方法,因為它會覆蓋app.config文件中定義的設(shè)置。請確保閱讀Windows窗體中的High DPI支持以獲取Microsoft的更多詳細信息。
應用清單
重要事項?如果您要定位.Net 4.7 或以上定位,Microsoft 建議DPI Awareness 通過app.config 而不是進行配置app.manifest 。請確保閱讀Windows窗體中的High DPI支持以獲取Microsoft的更多詳細信息。
使用應用程序清單設(shè)置默認感知。以下示例是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 模板,則可以使用模板來添加模板,而不是手動添加模板以確保添加文件中的相關(guān)<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>
以編程方式
在代碼中設(shè)置高DPI,可以使用Cef.EnableHighDPISupport();。輔助方法。這將調(diào)用Chromium ?base :: win :: EnableHighDPISupport();?功能。然后,您將擁有與Chromium 用途完全相同的設(shè)置。
Cef.EnableHighDPISupport();?必須在應用程序執(zhí)行的最早期就調(diào)用,最好在應用程序入口點(Program.Main)中調(diào)用。
該CefSharp.MinimalExample.WinForms項目包含一個工作的例子。
WPF高DPI
應用清單
添加相關(guān)條目,請參閱app.manifest中針對Microsoft的建議打開Windows級每個監(jiān)視器的DPI感知。
有關(guān)工作示例,請參見https://github.com/cefsharp/CefSharp/blob/cefsharp/84/CefSharp.Wpf.Example/app.manifest了解工作示例。如果您的項目還沒有app.manifest 使用Visual Studio New Item 模板,則可以使用模板來添加模板,而不是手動添加模板以確保添加文件中的相關(guān)<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 入口點,這使得以編程方式設(shè)置更加困難DPI 。有關(guān)如何創(chuàng)建的信息,請參見https:///a/26890426/4583726,Program.Main 然后可以調(diào)用Cef.EnableHighDPISupport();。。這?必須在你的應用程序執(zhí)行很早就被調(diào)用,最好在您的自定義Program.Main第一個電話。
屏幕外高DPI
添加相關(guān)app.manifest 條目或調(diào)用Cef.EnableHighDPISupport() (請參閱上面的示例)。閱讀WinForms 以上部分,選擇適合您需求的選項。
高DPI附加信息
Chromium 默認情況下,將在單獨的子流程中執(zhí)行所有渲染。特別是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 可能會對性能產(chǎn)生影響,當https://github.com/cefsharp/CefSharp/issues/2927完成后,將有可能以編程方式設(shè)置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);
多線程消息循環(huán)
CefSharp 默認使用setting.MultiThreadedMessageLoop = true 。這使您的應用程序能夠非常快速地啟動并運行,需要注意一些重要的事情,但這可能并不適合所有人。
- 對消息泵使用其他線程。
- CEF UI線程與應用程序的UI線程不同,這可能導致消息處理中的某些斷開連接。
- 一個示例是打開菜單,然后在瀏覽器控件中單擊并使菜單保持打開狀態(tài)。
- 低級Win32消息不會在
CEF 和之間傳播WinForms
可以將CEF集成到應用程序的現(xiàn)有消息循環(huán)中。將CEF集成到現(xiàn)有消息循環(huán)中的一種非常簡單的實現(xiàn)涉及在UI線程上使用每秒調(diào)用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();
}
更高級的選項包括設(shè)置CefSettings.ExternalMessagePump = true;。和實現(xiàn)?IBrowserProcessHandler.OnScheduleMessagePumpWork。這樣可以CEF 在需要執(zhí)行工作時發(fā)出通知,在某些情況下,這可能會使您的應用程序響應速度更快。有關(guān)其他詳細信息,請參見https://github.com/cefsharp/CefSharp/issues/1748。項目源代碼中包含更多高級示例。
您可以在使用時掛接消息循環(huán)MultiThreadedMessageLoop ,盡管這很復雜。項目源代碼包含一個示例,網(wǎng)址為https://github.com/cefsharp/CefSharp/blob/v53.0.0/CefSharp.WinForms.Example/BrowserTabUserControl.cs#L224?您可以使用此方法獲取Win32鼠標消息。
彈出窗口
一個常見的請求是控制彈出窗口的創(chuàng)建。實施ILifeSpanHandler.OnBeforePopup 以控制如何創(chuàng)建彈出窗口。要完全取消彈出窗口的創(chuàng)建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
}
您可以取消彈出窗口的創(chuàng)建,然后以新的形式打開URL?ChromiumWebBrowser 使用此方法您選擇實例中。重要的是要注意,使用此方法將不存在父子關(guān)系。因此,一般不建議這樣做。
實驗選項1:允許您使用中的newBrowser 參數(shù)托管彈出窗口OnBeforePopup 。有一些已知問題(在GitHub 項目上搜索)。如果您使用此方法遇到問題,那么您將必須承擔責任并通過CEF項目解決該問題。同樣重要的是要注意LoadingStateChanged etc不會被彈出窗口調(diào)用。如果使用此方法,請實現(xiàn)相關(guān)的處理程序。
實驗選項2:IWindowInfo.SetAsChild 用于指定父句柄。要在WPF中使用此功能,您將需要使用WinForms主機。使用此方法,您將需要處理move和resize事件。大致如下所示:
- 抓住
IBrowserHost 從新創(chuàng)建的IBrowser 實例表示彈出然后訂閱窗口移動的通知和呼叫NotifyMoveOrResizeStarted SetWindowPos 大小更改時在瀏覽器上調(diào)用HWND(隱藏時設(shè)置為0,0以停止渲染)
盡管它們是實驗性的,但是在項目源中有一些示例,并且不能保證它們在起作用。備選案文2的例子不完整,盡管有報告表明它運作良好,盡管此人從未提供過有效的例子。
JavaScript整合
1.如何從.NET調(diào)用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(); })();");
如果您的網(wǎng)頁包含多個框架,則可以在子框架上執(zhí)行腳本
browser.GetBrowser().GetFrame("SubFrame").ExecuteJavaScriptAsync("document.body.style.background = 'red';");
我什么時候可以開始執(zhí)行JavaScript?
JavaScript 只能在V8Context中執(zhí)行。在IRenderProcessMessageHandler.OnContextCreated 和IRenderProcessMessageHandler.OnContextReleased 提供時,可以執(zhí)行JavaScript的一個邊界。OnContextCreated/OnContextReleased 將每幀調(diào)用一次,用于frame.IsMain 檢查主幀。
嘗試開始訪問DOM 是很誘人的OnFrameLoadStart ,而V8Context will已創(chuàng)建,您將能夠執(zhí)行DOM 尚未完成加載的腳本。如果您需要DOM 盡早訪問,請訂閱DOMContentLoaded ,JavaScript 下面是一些執(zhí)行示例。
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');");
}
};
有關(guān)執(zhí)行的一些注意事項JavaScript :
- 腳本在框架級別執(zhí)行,并且每個頁面至少有一個框架(
MainFrame )。 - 該
IWebBrowser.ExecuteScriptAsync 擴展方法是為了向下兼容性,可以使用它作為快捷方式來執(zhí)行js 在主框架上。 - 如果框架不包含JavaScript,則不會
V8Context 創(chuàng)建任何框架。 - 對于沒有上下文的框架,一旦框架加載完成,就可以使用創(chuàng)建V8Context?
IFrame.ExecuteJavaScriptAsync 。 - 該
DOM 不會已完成加載時OnFrameLoadStart 被觸發(fā) IRenderProcessMessageHandler.OnContextCreated/OnContextReleased ?僅針對主機。
2.如何調(diào)用返回結(jié)果的JavaScript方法?
如果您需要調(diào)用(評估)返回值的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代碼是異步執(zhí)行的,因此返回Task,其中包含錯誤消息,結(jié)果和success(bool )標志。這是評估時需要了解的基本知識JavaScript
- 請確保您閱讀了?何時可以開始執(zhí)行
JavaScript ?。 - 腳本在框架級別執(zhí)行,并且每個頁面至少有一個框架(
MainFrame )。 - 腳本在渲染過程中執(zhí)行,并通過進行傳輸
IPC ,僅返回出于性能原因所需的數(shù)據(jù)。 - 支持原始數(shù)據(jù)類型:int,double,date,bool和string。
- 在某種程度上支持對象,并且將以形式返回對象。支持
IDictionary<string, object> 使用dynamic 關(guān)鍵字來簡化訪問屬性值的過程。 - 您不能直接返回
DOM ?Element(或任何具有循環(huán)引用的元素),需要創(chuàng)建一個僅包含您需要返回的信息的新對象。 - 支持包含上面列出的原語和對象的數(shù)組,它們將以形式返回
IList<object> 。 Array Like 像HTMLCollection這樣的對象不能直接使用Array.from返回并返回數(shù)組- 可以返回的對象圖的復雜度受到限制(當前不支持帶有循環(huán)引用的圖),在這種情況下,您可能需要使用JavaScript
JSON.stringify() 方法將JavaScript對象轉(zhuǎn)換為JSON字符串,然后將該字符串返回您的.NET代碼。然后,您可以使用類似JSON.NET的方式將該字符串解碼為.NET對象。有關(guān)更多詳細信息,請參見MDN JSON.stringify。有關(guān)與結(jié)合使用的一些指導,請參見https:///a/46881092/4583726。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./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 。當前有兩個不同的實現(xiàn),Async 版本和較舊的Sync 版本。該Sync 版本不再被積極開發(fā),它依賴于WCF 該版本不可用.Net Core 或即將發(fā)布.Net 5.0 。
異步JavaScript綁定(JSB)
概要
如果您不熟悉這里提供的所有Chromium 內(nèi)容,那么async programming 可以參考一些非常有用的文章
綁定Async 對象JavaScript
該CefSharp.BindObjectAsync方法被稱為在Javascript 結(jié)合的對象。CefSharp.BindObjectAsync返回一個Promise,當綁定的對象可用時,該Promise將被解決。在全局上下文(window 對象的屬性)中創(chuàng)建對象。如果調(diào)用時CefSharp.BindObjectAsync 沒有任何參數(shù),則所有已注冊的對象都將被綁定。名稱綁定是更具描述性的選項。
簡單的工作流程如下所示:
- 第1步:創(chuàng)建一個您希望公開使用javascript的類(不要使用您的
Form/Window 或Control ) - 步驟2向您的課程注冊一個實例
JavaScriptObjectRepository - 步驟3使用您要注冊的對象的名稱來調(diào)用(對象只有在
Promise 解析后才可用。
僅支持方法。如果需要設(shè)置屬性,則創(chuàng)建Get /Set 方法。
步驟1建立課程
一個簡單的類如下所示:
public class BoundObject
{
public int Add(int a, int b)
{
return a b;
}
}
步驟2向您的課程注冊一個實例JavaScriptObjectRepository
該過程的第二部分是向中注冊對象JavascriptObjectRepository (可通過browser.JavascriptObjectRepository屬性訪問)。您有兩個選項用于在中注冊對象.Net ,第一個選項是預先注冊的,通常在創(chuàng)建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調(diào)用時,將JavascriptObjectRepository 查詢以查看是否已注冊具有給定名稱的對象,如果未找到匹配的對象,ResolveObject 則引發(fā)該事件。對于不帶任何參數(shù)的CefSharp.BindObjectAsync調(diào)用,則如果已注冊對象,則將它們?nèi)拷壎ǎ绻醋詫ο?,則將ResolveObject 其ObjectName 設(shè)置為All 。
本節(jié)僅介紹基礎(chǔ)知識,還有許多高級選項,請查看高級異步Javascript綁定。
如果您想查看一個可行的示例,請查看CefSharp MinimalExample Javascript Binding Demo分支,特別是commit
同步JavaScript綁定(JSB)
這是一個遺產(chǎn)功能-任何正在創(chuàng)建新應用程序的人都在使用Async JavaScript Binding(JSB)實現(xiàn),因為它正在積極開發(fā)中。該Sync 版本僅會收到針對回歸的錯誤修復。
- 使用
WCF 通信服務(wù)(微軟還沒有為支持WCF 中.Net Core/.Net 5.0 ,有沒有長遠的未來WCF )。 - 同時支持方法和屬性
- 呼叫以某種
sync 方式執(zhí)行且正在阻塞,長時間運行的呼叫會阻塞Render Process 并導致您的應用顯示緩慢或無響應。 - 支持半復雜的對象結(jié)構(gòu)
- 有時,該
WCF 服務(wù)無法完全關(guān)閉,并減慢了應用程序的關(guān)閉速度
綁定對象?JavaScript
綁定是由JavaScript啟動的,當綁定的對象可用時,該CefSharp.BindObjectAsync 方法將返回Promise 解析的結(jié)果。在全局上下文(window 對象的屬性)中創(chuàng)建對象。如果調(diào)用時CefSharp.BindObjectAsync 沒有任何參數(shù),則所有已注冊的對象都將被綁定。名稱綁定是更具描述性的選項。
簡單的工作流程如下所示:
- 第1步創(chuàng)建一個您希望向JavaScript公開的類(不要使用您的
Form/Window 或Control ) - 步驟2
CefSharp.BindObjectAsync 使用您要注冊的對象的名稱進行調(diào)用,例如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 are
var response = new CallbackResponseStruct("This callback from C# was delayed " taskDelay "ms");
await javascriptCallback.ExecuteAsync(response);
}
});
}
}
步驟2調(diào)用CefSharp.BindObjectAsync ,下面Binding 的對象示例如下所示:
注意這是一個兩部分的過程,有關(guān)詳細信息,請參見下面的示例
<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 ,第一個選項是預先注冊的,通常在創(chuàng)建ChromiumWebBrowser 實例后立即完成。第二個選項更加靈活,并允許Resolved 在需要時放置對象。
進行CefSharp.BindObjectAsync 調(diào)用時,JavascriptObjectRepository is查詢查詢是否已指定給定名稱的對象,如果找不到匹配的對象,ResolveObject 則引發(fā)該事件。對于CefSharp.BindObjectAsync 不帶任何參數(shù)的調(diào)用,則如果已經(jīng)注冊了對象,則將它們?nèi)拷壎?,如果沒有注冊任何對象,則將ResolveObject 其ObjectName 設(shè)置為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
請注意:
- 不要注冊表格/窗口/控件。如果需要,創(chuàng)建一個類并代理調(diào)用。
- 默認情況下,方法和屬性都更改為camelCase(即首字母小寫)以使其在JavaScript代碼中自然使用。禁用將browser.JavascriptObjectRepository.NameConverter設(shè)置為null
- 屬性支持復雜對象(如果適用),因此您現(xiàn)在可以執(zhí)行
bound.subObject.myFunction() 和bound.subObject.myProperty = 1 。 - 現(xiàn)在可以通過
IBinder 接口實現(xiàn)對函數(shù)的復雜對象支持,您可以實現(xiàn)自己的或使用DefaultBinder 例如repo.Register("bound", new BoundObject(), BindingOptions.DefaultBinder);
RegisterAsyncJsObject
此方法已刪除。請參閱異步JavaScript綁定(JSB)。
RegisterJsObject
這已被刪除。請參閱同步JavaScript綁定(JSB)。
Adobe Flash Player(Pepper Flash)
注意:現(xiàn)在不建議使用Flash,并且Chromium 將刪除支持,有關(guān)更多詳細信息,請參閱https://www./flash-roadmap#TOC-Upcoming-Changes。從版本開始,81 默認情況下現(xiàn)在已禁用它,請參見https://github.com/cefsharp/CefSharp/issues/3048#issuecomment-592263009
CefSharp 可以從Adobe下載可以自動發(fā)現(xiàn)并加載的Pepper Flash的系統(tǒng)范圍安裝。從下拉列表中選擇FP for Opera and Chromium-PPAPI版本。要測試Flash是否正常運行,只需加載http://www.adobe.com/software/flash/about/。
注意首次打開Flash時,將短暫顯示控制臺窗口,顯示NOT SANDBOXED 。有一個問題在Chromium 問題跟蹤,但不幸的是Google 已經(jīng)將其標記為WontFix 。一些聰明的人一起破解了一些解決方法。它們很復雜,我從未嘗試過。請訪問https://github.com/cefsharp/CefSharp/issues/1259中的鏈接以獲取詳細信息。
屏幕外渲染(OSR)
WPF和OffScreen版本使用OffScreen Rendering(OSR)渲染模式。在OSR模式每幀被渲染到緩沖器中,然后在屏幕上或者繪制為在WPF的情況下,或提供作為Bitmap 在OffScreen 。
WPF
對于WPF控件,用戶輸入(鼠標單擊/移動和按鍵)將通過IBrowserHost 界面上的方法轉(zhuǎn)發(fā)到基礎(chǔ)瀏覽器。可以訪問每個Bitmap 渲染的對象。
應特別注意ChromiumWebBrowser 在內(nèi)托管ViewBox 。這遠非理想,因為渲染了每一幀,然后進行后處理來調(diào)整圖像的大小/縮放。這會嚴重影響性能,并且通常會降低質(zhì)量(通常很模糊)。您可以使用調(diào)整調(diào)整大小的質(zhì)量RenderOptions.SetBitmapScalingMode 。最好避免使用ViewBox 。您可以通過調(diào)整來縮放瀏覽器中包含的內(nèi)容ZoomLevel ,這是迄今為止性能最高的選項。
屏幕外
對于CefSharp.OffScreen 包裝,將每個幀渲染到Bitmap 并暴露以供使用。如果希望通過鍵盤或鼠標與瀏覽器進行交互,則可以使用IBrowser 主機界面上的方法。模擬按鍵和鼠標單擊/移動可能非常復雜。您可以使用WPF控件作為開始示例,因為它使用相同的方法(添加調(diào)試以查看所需的事件順序)。按鍵和鼠標的點擊/移動通常由多個部件,up /down 與許多其它可能的組合。
用戶代理
您可以通過設(shè)置https://cefsharp./api/86.0.x/html/P_CefSharp_CefSettingsBase_UserAgent.htm來指定自定義UserAgent
的UserAgent 可以在運行時使用DevTools協(xié)議來改變看到https:///a/64543667/4583726的一個例子。
您可以在中修改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 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./detect/what-is-my-user-agent in the browser and you'll
//see our custom user agent
if (request.Url == "https://www./detect/what-is-my-user-agent")
{
return new CustomResourceRequestHandler();
}
//Default behaviour, url will be loaded normally.
return null;
}
}
browser.RequestHandler = new CustomRequestHandler();
開發(fā)工具
您可以從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),其中每一幀被渲染為位圖。它仍然是一個網(wǎng)絡(luò)瀏覽器,并不是特別適合這種情況。這里有一些注意事項:
- 降低幀頻以使其更容易捕獲幀可能值得考慮
- 頁面加載完成后,您需要等待一段時間,以允許瀏覽器呈現(xiàn)
- 當前尚無確定網(wǎng)頁何時完成渲染的方法(Flash,動態(tài)內(nèi)容,動畫等功能,甚至像移動鼠標或滾動之類的簡單任務(wù)也將導致渲染新幀)。
- 一種確定何時大致完成渲染的破解方法是讓計時器在每次渲染幀時重置,如果沒有其他幀渲染,則計時器將歸檔(不理想)
WinForms
這是在Windows下拍攝屏幕快照的一些示例
Win32內(nèi)存不足
使用32bit 版本時,請確保您的應用程序支持大地址(處理大于2gb的地址)
根據(jù)http:///ceforum/viewtopic.php?f=6&t=15120#p34802中的建議,現(xiàn)在看來有必要在32位應用程序運行時在應用程序可執(zhí)行文件上設(shè)置“大地址感知”鏈接器設(shè)置。遇到高內(nèi)存負載。
https://msdn.microsoft.com/zh-CN/library/wz223b1z.aspx
CefSharp附帶的默認x86 SubProcess可以識別大型地址,您也應該使應用程序識別。
將大地址感知鏈接器設(shè)置應用于可執(zhí)行文件后,如果仍然遇到完全相同的問題,請在http:///ceforum/viewtopic.php?f=6&t=15120上討論您的問題。
使用PostData加載URL
有兩種加載URL的方法Post Data ,第一種是修改現(xiàn)有的Request 。在下面的示例中,Request 如果我們訪問http:///post,我們會將發(fā)布數(shù)據(jù)添加到
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:///post in the browser and you'll
//see the post data
if (request.Url == "http:///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:///post in the browser to see the post data
browser = new ChromiumWebBrowser("http:///post");
browser.RequestHandler = new CustomRequestHandler();
第二種方法是使用IFrame.LoadRequest,僅當您首次成功執(zhí)行導航后才能使用此方法。例如,您必須先導航到google.com,然后才能調(diào)用IFrame.LoadRequest。加載about:blank 是不夠的,因為它是特例,并且不會產(chǎn)生渲染過程。
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:///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擴展方法可用于簡單的情況下,它會調(diào)用LoadRequest 并針對具有相同的限制進行了成功的導航應用。
拼寫檢查
默認情況下CefSettings.Locale 將指示使用哪個字典,默認為en-US ??梢栽谶\行中配置拼寫檢查的許多方面,enable/disable 在運行中進行更改dictionary ,甚至啟用多個詞典。使用RequestContext.SetPreference (有關(guān)RequestContext 如何設(shè)置首選項的詳細信息,請參閱本文檔的部分)。
只能使用spellcheck.dictionaries 首選項(重要的是使用復數(shù)版本)?動態(tài)地更改拼寫檢查https:///chromiumembedded/cef/issues/2222/spell-checking-language-cannot-be-changed#comment-38338016
這是一些有用的鏈接
http:///ceforum/viewtopic.php?f=6&t=14911&p=33882&hilit=spellcheck#p33882?https://cs./chromium/src/components/spellcheck/browser/pref_names.cc?type = cs&q =%22spellcheck.dictionary%22&l = 11?https://cs./chromium/src/components/spellcheck/browser/pref_names.cc?type=cs&q="spellcheck.dictionary"&l=15
并非所有語言都支持拼寫檢查,請參閱https:///ceforum/viewtopic.php?f=6&t=16508#p40684
Web組裝
在較新的版本中默認為啟用,請參見https://www./feature/5453022515691520
對于較舊的版本,您需要手動啟用,WebAssembly 請參見https:///chromiumembedded/cef/issues/2101/add-webassembly-support
settings.javascript_flags ?轉(zhuǎn)換為?settings.JavascriptFlags = "--expose-wasm";
異常處理
捕獲非托管異常非常困難,并且CEF 可能處于損壞狀態(tài),需要您的應用程序終止并重新啟動。由于這是一個一般的編程主題,因此不在本文的CefSharp 特別討論范圍之內(nèi),因此有一些資源可幫助您開始自己進行研究。
http:///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環(huán)境中捕獲未處理的異常?http://www./blog/?p=1440
依賴檢查
CefSharp ?有一個非常簡單的類,用于檢查是否存在所有相關(guān)的非托管資源。
//Perform dependency check to make sure all relevant resources are in our output directory.
//https://cefsharp./api/86.0.x/html/M_CefSharp_Cef_Initialize_1.htm
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
//Manually check
//https://cefsharp./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-(Redistribution)
多媒體(音頻/視頻)
CEF 并且隨后CefSharp 僅支持免費提供的音頻和視頻編解碼器。要查看CefSharp 您所使用的版本支持,請在ChromiumWebBrowser 實例中打開http:///。
MP3 專利已過期,因此受65.0.0 病房支持。H264/AAC 被歸類為Proprietary Codecs 并且不受支持,它們要求您獲得許可。喜歡Netflix/Twitter/Instagram 使用的網(wǎng)站H264 ,其視頻因此無法播放。請參閱https://www./licensing/h264-patent-license,以獲取Free Software Foundation 有關(guān)該主題的一些評論。
CEF 對的支持編譯H264/AAC 超出了該項目的范圍。以下內(nèi)容僅供參考,請不要尋求支持CEF 。
屏幕(虛擬)鍵盤
該WinForms 版本已經(jīng)內(nèi)置在屏幕鍵盤的支持,它已經(jīng)報道,有時它并不總是正確彈出,使用disable-usb-keyboard-detect 命令行參數(shù)?https://github.com/cefsharp/CefSharp/issues/1691#issuecomment-323603277報道解決這個問題。
從WPF 屏幕版本(虛擬)開始,它沒有內(nèi)置的支持,從版本開始,73 新VirtualKeyboardRequested 事件現(xiàn)在會在您的應用程序應顯示虛擬鍵盤時提供通知。不幸的是,Windows 7, 8.1 and 10 由于沒有.Net API 顯示虛擬鍵盤的功能,因此很難提供支持的默認實現(xiàn)。Windows 10 Only 在https://github.com/cefsharp/CefSharp/commit/0b57e526158e57e522d46671404c557256529416中添加了一個示例如果您需要支持,Windows 8 and 10 則https://github.com/maximcus/WPFTabTip可能會有用。對于Windows 7 ?https:///questions/1168203/incorporating-the-windows-7-onscreen-keyboard-into-a-wpf-app有一些建議。
?
來源:https://www./content-4-771401.html
|