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}}"); });
以上根据我的理解总结,若有错误的地方,欢迎前辈指出,很是感谢!