1. hg clone程式碼回來後,還要下載對應的cef dll . 從http://xilium.bitbucket.org/cefglue/ 選擇對應的.
然後把dll和resource目錄裡的內容都考到debug目錄裡才能執行.
2.不能使用 Enable the Visual Studio hosting process 來執行. 因為還會啟動2個程式. 這兩個程式是相同的exe只是命令列不同, 如果使用了 hosting process ,那麼就不能啟動另外兩個程式.
除錯的時候把所有的專案型別從framework 2.0 3.5 統一到4.0 除錯斷點才管用.
由於chrome是多程式執行, 有時候分不清楚程式碼是在哪個程式中執行, 所以斷點很難下.
建議用Windbg 開啟exe 並且 Debug child processes also ,來除錯. 程式碼裡用Console.WriteLine message的形式來看是否執行到.
3. Js 於Clr 互動:
可以為window註冊一個external:
1 private ClrObjectAv8Handler m_extantHandler =new JavaScriptExtMethod(); 2 protected override void OnContextCreated(CefBrowser browser, CefFrame frame, CefV8Context context) 3 { 4 var global = context.GetGlobal(); 5 6 var extent = CefV8Value.CreateObject(null); 7 8 global.SetValue("external", extent, CefV8PropertyAttribute.None); 9 AddMethodToJSObject(this.m_extantHandler, extent); 10 } 11 //用反射的把所有公共方法註冊到js object 12 public static void AddMethodToJSObject(CefV8Handler hendlerObj, CefV8Value extent) 13 { 14 Type t = hendlerObj.GetType(); 15 var functions = t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); 16 17 foreach (var m in functions) 18 { 19 string funName =m.Name; 20 21 extent.SetValue(funName, CefV8Value.CreateFunction(m.Name, hendlerObj), CefV8PropertyAttribute.None); 22 23 } 24 25 }
JavaScriptExtMethod 繼承CefV8Handler 過載方法Execute.
1 protected override bool Execute(string name, CefV8Value obj, CefV8Value[] arguments, out CefV8Value returnValue, out string exception) 2 { 3 4 exception = null; 5 try 6 { 7 List<object> paras = V8ValueClrMap.ConvertToClrParameters(arguments); 8 Type t = this.GetType(); 9 10 var function = t.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod | 11 BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly); 12 if (function != null) 13 { 14 var objRst = function.Invoke(this, paras.Count == 0 ? null : paras.ToArray()); 15 if (objRst is CefV8Handler) 16 { 17 returnValue = CefV8Value.CreateObject(null); 18 AddMethodToJSObject(objRst as CefV8Handler, returnValue); 19 } 20 else 21 { 22 23 returnValue = V8ValueClrMap.ConvertToV8Value(objRst); 24 } 25 } 26 else 27 { 28 exception = "Method no find:"+name; 29 returnValue = CefV8Value.CreateString(""); 30 var message3 = CefProcessMessage.Create("Exception"); 31 message3.Arguments.SetString(0, exception); 32 var success2 = Browser.SendProcessMessage(CefProcessId.Browser, message3); 33 34 return true; 35 } 36 return true; 37 } 38 catch (Exception ee) 39 { 40 returnValue = CefV8Value.CreateString(""); 41 exception = ee.Message; 42 43 returnValue = CefV8Value.CreateString(""); 44 var message3 = CefProcessMessage.Create("Exception"); 45 message3.Arguments.SetString(0, ee.ToString()); 46 var success2 = Browser.SendProcessMessage(CefProcessId.Browser, message3); 47 48 return true; 49 } 50 51 }
再實現一個V8Value與Clr的對應.
1 public class V8ValueClrMap 2 { 3 public static List<object> ConvertToClrParameters(CefV8Value[] arguments) 4 { 5 List<object> rtn = new List<object>(); 6 if (arguments == null || arguments.Length == 0) 7 { 8 return rtn; 9 } 10 foreach (var v in arguments) 11 { 12 rtn.Add(GetClrValue(v)); 13 } 14 return rtn; 15 } 16 public static object GetClrValue(CefV8Value v) 17 { 18 if (v.IsArray) 19 { 20 int length = v.GetArrayLength(); 21 object[] objs = new object[length]; 22 for (int i = 0; i < length; i++) 23 { 24 var value = v.GetValue(i); 25 objs[i] = GetClrValue(value); 26 } 27 return objs; 28 } 29 30 if (v.IsBool ) return v.GetBoolValue(); 31 32 if (v.IsDate) return v.GetDateValue(); 33 if (v.IsInt) return v.GetIntValue(); 34 35 if (v.IsDouble) return v.GetDoubleValue(); 36 37 if (v.IsFunction){ 38 throw new NotSupportedException("IsFunction"); 39 } 40 41 42 if (v.IsNull) return null; 43 if (v.IsObject) 44 { 45 //throw new NotSupportedException("IsObject"); 46 var map = v.GetUserData() as Clr2V8ValueWrapper; 47 if (map != null) 48 return map.ClrObject; 49 return null; 50 } 51 if (v.IsString) return v.GetStringValue(); 52 if (v.IsUInt) return v.GetUIntValue(); 53 if (v.IsUndefined) return null; 54 if (v.IsUserCreated) 55 { 56 throw new NotSupportedException("IsUserCreated"); 57 } 58 throw new NotSupportedException("??"); 59 60 } 61 62 public static CefV8Value ConvertToV8Value(object o) 63 { 64 if (o == null) return CefV8Value.CreateUndefined(); 65 if (o is bool) return CefV8Value.CreateBool((bool)o); 66 if (o is DateTime) return CefV8Value.CreateDate((DateTime)o); 67 if (o is double) return CefV8Value.CreateDouble((double)o); 68 if (o is int) return CefV8Value.CreateInt((int)o); 69 if (o is string) return CefV8Value.CreateString((string)o); 70 if (o is uint) return CefV8Value.CreateUInt((uint)o); 71 if (o is Array) 72 { 73 var a=(Array)o; 74 var rtn = CefV8Value.CreateArray(a.Length); 75 for(int i=0;i<a.Length;i++) 76 { 77 rtn.SetValue(i,ConvertToV8Value(a.GetValue(i))); 78 } 79 return rtn; 80 } 81 if (o is System.Collections.IList) 82 { 83 var a = (System.Collections.IList)o; 84 var rtn = CefV8Value.CreateArray(a.Count); 85 for (int i = 0; i < a.Count; i++) 86 { 87 rtn.SetValue(i, ConvertToV8Value(a[i])); 88 } 89 return rtn; 90 } 91 throw new NotSupportedException("??"); 92 } 93 }
然後就可以在js裡呼叫JavaScriptExtMethod的方法: window.external.SomeMethod();
C# 裡呼叫Js方法, 推薦使用發Message 到Render程式, 然後render程式執行完後再發訊息給browser程式的方法.這樣才能方便的捕獲到js裡的異常和返回值.
render程式:
1 protected override bool OnProcessMessageReceived(CefBrowser browser, CefProcessId sourceProcess, CefProcessMessage message) 2 { 3 if (message.Name == "ExecuteJavaScript") 4 { 5 string code = message.Arguments.GetString(0); 6 var context = browser.GetMainFrame().V8Context; 7 context.Enter(); 8 try 9 { 10 var global = context.GetGlobal(); 11 var evalFunc = global.GetValue("eval"); 12 CefV8Value arg0 = CefV8Value.CreateString(code); 13 14 var rtn = evalFunc.ExecuteFunctionWithContext(context, evalFunc, 15 new CefV8Value[] { arg0 }); 16 if (evalFunc.HasException) 17 { 18 var exception = evalFunc.GetException(); 19 var message3 = CefProcessMessage.Create("Exception"); 20 var arguments = message3.Arguments; 21 arguments.SetString(0, exception.Message + " At Line" + exception.LineNumber); 22 var success2 = browser.SendProcessMessage(CefProcessId.Browser, message3); 23 24 } 25 else 26 { 27 var message3 = CefProcessMessage.Create("JavascriptExecutedResult"); 28 var arguments = message3.Arguments; 29 arguments.SetString(0,rtn.GetStringValue()); 30 var success2 = browser.SendProcessMessage(CefProcessId.Browser, message3); 31 } 32 } 33 34 finally 35 { 36 context.Exit(); 37 } 38 } 39 40 41 42 return false; 43 }
4. javascript 的執行都在renderer 的Main Thread 上面.