CefSharp 提供了多種執行CDP(Chrome DevTools Protocol)方式,有高度封裝的DevToolsClient.Page、DevToolsClient.DOM等等,也有徹底手動執行的IBrowserHost下的SendDevToolsMessage,這裏咱們只討論手動執行方式。web
只傳入CDP方法名稱、參數,返回結果(Cefsharp維護 發送消息ID、接收消息ID; 有些方法也提供了消息ID入參),使用方便,可是因爲消息id是Cefsharp維護,頻繁發送時有時會拋出消息ID不匹配異常;json
手動控制發送json,監聽返回結果(徹底控制,就剩下websocket連接等基本信息cefsharp維護),可是操做比較麻煩瀏覽器
Cefsharp提供的CDP封裝類,封裝了CDP各類方法模塊直接調用方法,可是使用姿式不對可能會執行後程序卡死,具體各類卡死狀況請跳轉stackoverflow。服務器
能夠經過chromiumWebBrowser.GetBrowser().GetDevToolsClient() 得到DevToolsClient實例。websocket
最好不要頻繁調用GetDevToolsClient() 獲取DevToolsClient,由於聽說每次獲取會重置消息ID,頻繁獲取可能會致使 發送/接收消息ID衝突,因此最好聲明全局變量在ChromiumWebBrowser實例初始化完成時獲取一次:異步
DevToolsClient devTool = null; private void Form1_Load(object sender, EventArgs e){ //.... ChromiumWebBrowser chromiumWebBrowser1 = new ChromiumWebBrowser(); chromiumWebBrowser1.IsBrowserInitializedChanged+= new EventHandler(delegate { devTool = chromiumWebBrowser1.GetBrowser().GetDevToolsClient(); }); }
我感受相對比較簡單的手動調用CDP方式,CefSharp維護髮送消息ID,Cefsharp已經簡單封裝了消息結果類型socket
方法原型:函數
public class DevToolsClient : IDevToolsMessageObserver, IDisposable, IDevToolsClient { //.... public Task<DevToolsMethodResponse> ExecuteDevToolsMethodAsync(string method, IDictionary<string, object> parameters = null); }
method: CDP 方法名稱
parameters: 方法參數
返回結果DevToolsMethodResponse:
public class DevToolsMethodResponse { public DevToolsMethodResponse(); public int MessageId { get; set; } public string ResponseAsJsonString { get; set; } public bool Success { get; set; } }
MessageId: 消息IDui
ResponseAsJsonString: 返回消息內容(消息的result內容)this
Success: 是否執行成功
好比執行刷新頁面:
private void button8_Click(object sender, EventArgs e) { devTool.ExecuteDevToolsMethodAsync("Page.reload").ContinueWith(delegate(Task<DevToolsMethodResponse> result) { Console.WriteLine(result.Result.ResponseAsJsonString); }); }
獲取頁面結構:
private void button8_Click(object sender, EventArgs e) { devTool.ExecuteDevToolsMethodAsync("DOM.enable").ContinueWith(delegate(Task<DevToolsMethodResponse> result) { Dictionary<string, object> param = new Dictionary<string, object>() { { "depth", 10 }, { "pierce", true } }; devTool.ExecuteDevToolsMethodAsync("DOM.getDocument", param).ContinueWith(delegate(Task<DevToolsMethodResponse> resultA) { Console.WriteLine(resultA.Result.ResponseAsJsonString); }); }); }
DOM.enable: 開啓DOM代理
DOM.getDocument: 獲取頁面結構(包含嵌套的iframe內容),有兩個可選參數(depth: 獲取結構深度,pierce: 是否遞歸向下查詢iframes)
可是別使用Wait()奧- -,像這樣:
private void button8_Click(object sender, EventArgs e) { devTool.ExecuteDevToolsMethodAsync("DOM.enable").Wait(); }
會發現程序卡死了...當初這個問題困擾很久,上邊的overflow上的問題就是我提出的,截止到如今,尚未大佬關注...o(╥﹏╥)o
另外一個執行CDP方法的靜態類,主要用來擴展實現IBrowserHost、IWebBrowser、IBrowser接口的實例能夠直接執行CDP方法,由於ChromiumWebBrowser實現了IWebBrowser和IBrowser,因此能夠在ChromiumWebBrowser實例中直接調用ExecuteDevToolsMethodAsync方法。
上邊chromiumWebBrowser1.GetBrowser().GetDevToolsClient()中GetDevToolsClient方法就是使用的此類中的擴展函數。
執行CDP方法,執行成功返回消息id,失敗則返回0。
和上邊不一樣的是,上邊執行CDP方法後,會異步返回方法執行結果,此方法沒有異步執行,而是返回了傳入的消息id,而且此方法必須在cefsharp線程中調用
public static class DevToolsExtensions { public static int ExecuteDevToolsMethod(this IBrowserHost browserHost, int messageId, string method, JsonString parameters); }
browserHost: 此處傳入 chromiumWebBrowser1.GetBrowserHost();
messageId: 消息ID
method: 方法名稱
parameters: 方法參數,傳入方法參數json字符串
好比獲取頁面結構:
int i = 1;
Cef.UIThreadTaskFactory.StartNew(delegate { chromiumWebBrowser1.GetBrowserHost().ExecuteDevToolsMethod(i, "DOM.enable"); i++; Console.WriteLine(chromiumWebBrowser1.GetBrowserHost().ExecuteDevToolsMethod(i, "DOM.getDocument", new JsonString("{\"pierce\": true, \"depth\": 1}"))); });
上邊方法只是發送指令,若是想要拿到指令對應的結果,就須要實現IDevToolsMessageObserver接口,至關於添加了一個監聽,監聽websocket發送過來的消息:
class DevToolsMessageObserverHandler : IDevToolsMessageObserver { public void Dispose() { } public void OnDevToolsAgentAttached(IBrowser browser) { } public void OnDevToolsAgentDetached(IBrowser browser) { } public void OnDevToolsEvent(IBrowser browser, string method, Stream parameters) { } public bool OnDevToolsMessage(IBrowser browser, Stream message) { return false; } public void OnDevToolsMethodResult(IBrowser browser, int messageId, bool success, Stream result) { byte[] bytes = new byte[result.Length]; result.Read(bytes, 0, bytes.Length); StringBuilder sb = new StringBuilder(); foreach (byte item in bytes) { sb.Append((char)item); } Console.WriteLine(sb.ToString()); } }
這裏主要關注OnDevToolsMessage和OnDevToolsMethodResult方法,服務器發送一條消息時,先到OnDevToolsMessage方法,在到OnDevToolsMethodResult方法。
若是OnDevToolsMessage方法返回true,表示消息已處理,不會在執行後續的OnDevToolsMethodResult.
OnDevToolsMethodResult方法的messageId就是發送指令時傳入的消息ID.
把監聽類註冊到BrowserHost中:
chromiumWebBrowser1.IsBrowserInitializedChanged += new EventHandler(delegate { chromiumWebBrowser1.GetBrowserHost().AddDevToolsMessageObserver(new DevToolsMessageObserverHandler()); });
這樣每一條瀏覽器端的發送過來的消息,都會監聽到,可是這時就須要咱們本身來實現根據消息ID匹配CDP方法的返回結果了。
徹底手動控制發送json,雖然自由度高但使用起來跟上邊比起來確實有些繁瑣
方法原型很簡單,只傳入一個json,CefSharp會直接給瀏覽器端發送這個json字符串,返回true表示執行成功,false表示執行失敗
bool SendDevToolsMessage(string messageAsJson);
發前須要咱們記一下消息id,發送後須要使用上邊監聽瀏覽器端消息內容方式匹配每一條消息ID,直到找到對應此條命令消息id對應返回結果。
和DevToolsExtensions.ExecuteDevToolsMethod函數同樣,須要在cefsharp線程中使用:
Cef.UIThreadTaskFactory.StartNew(delegate { IBrowserHost browserHose = chromiumWebBrowser1.GetBrowserHost(); browserHose.SendDevToolsMessage("{\"id\": 1, \"method\": \"DOM.enable\"}"); browserHose.SendDevToolsMessage("{\"id\": 2, \"method\": \"DOM.getDocument\", \"params\": {\"pierce\": true, \"depth\": 40}}"); });
以上根據我的理解總結,若有錯誤的地方,歡迎前輩指出,很是感謝!