0x01 ReoScript 脚本引擎的部分改造
前文讲到 ReoScript 这个脚本引擎并不能支持所有的 事件绑定,因此与CLR 的交互变得很low ,下面看一下引擎对事件绑定的代码,
方法名称是ScriptRunningMachine文件里的AttachEvent。
1 internal void AttachEvent(ScriptContext context, object obj, EventInfo ei, FunctionObject functionValue) 2 { 3 //remove last attached event to sample object 4 DetachEvent(obj, ei); 5 6 EventHandlerInfo ehi = new EventHandlerInfo(this, context, obj, ei, null, functionValue); 7 Action<object> doEvent = (e) => { 8 try 9 { 10 InvokeFunction(context, obj, functionValue, new object[] { e }); 11 } 12 catch(Exception ex) 13 { 14 if(!context.Srm.IgnoreCLRExceptions) 15 { 16 throw ex; 17 } 18 } 19 }; 20 21 Delegate d = null; 22 switch (ei.EventHandlerType.Name) { 23 case "EventHandler": 24 d = new EventHandler((s, e) => doEvent(e)); 25 break; 26 case "MouseEventHandler": 27 d = new MouseEventHandler((s, e) => doEvent(e)); 28 break; 29 case "KeyEventHandler": 30 d = new KeyEventHandler((s, e) => doEvent(e)); 31 break; 32 case "PaintEventHandler": 33 d = new PaintEventHandler((s, e) => doEvent(e)); 34 break; 35 case "KeyPressEventHandler": 36 d = new KeyPressEventHandler((s, e) => doEvent(e)); 37 break; 38 case "ControlEventHandler": 39 d = new ControlEventHandler((s, e) => doEvent(e)); 40 break; 41 case "FormClosedEventHandler": 42 d = new FormClosedEventHandler((s, e) => doEvent(e)); 43 break; 44 case "FormClosingEventHandler": 45 d = new FormClosingEventHandler((s, e) => doEvent(e)); 46 break; 47 case "PopupEventHandler": 48 d = new PopupEventHandler((s, e) => doEvent(e)); 49 break; 50 case "DragEventHandler": 51 d = new DragEventHandler((s, e) => doEvent(e)); 52 break; 53 } 54 55 #region // 56 //if (ei.EventHandlerType == typeof(EventHandler)) { 57 // d = new EventHandler((s, e) => doEvent(e)); 58 //} 59 //else if (ei.EventHandlerType == typeof(MouseEventHandler)) { 60 // d = new MouseEventHandler((s, e) => doEvent(e)); 61 //} 62 //else if (ei.EventHandlerType == typeof(KeyEventHandler)) { 63 // d = new KeyEventHandler((s, e) => doEvent(e)); 64 //} 65 //else if (ei.EventHandlerType == typeof(PaintEventHandler)) { 66 // d = new PaintEventHandler((s, e) => doEvent(e)); 67 //} 68 //else if (ei.EventHandlerType == typeof(KeyPressEventHandler)) { 69 // d = new KeyPressEventHandler((s, e) => doEvent(e)); 70 //} 71 //else if (ei.EventHandlerType == typeof(DragEventHandler)) { 72 // d = new DragEventHandler((s, e) => doEvent(e)); 73 //} 74 #endregion 75 ehi.ActionMethod = d; 76 ei.AddEventHandler(obj, d); 77 RegisteredEventHandlers.Add(ehi); 78 return; 79 }
第一次改造是用这里switch 来增加 事件的类型,但是后文还要做脚本引擎的窗体设计器,里面还要绑定很多事件只支持这么点事件做窗体设计器有个毛用,不行还得搞搞!
二次改造变得很重要——创建动态委托实例
核心是 Delegate 里的 CreateDelegate方法,作者也有意识到但是引擎已经没有在更新了,他的代码在return 后面。(微软官方msdn有demo 的,理解并改造一下就OK了)
这里涉及一点点的IL 指令,下面直接贴代码
1 public object DoEvent(ScriptContext context, object ownerObject, AbstractFunctionObject funObject, object args) { 2 try { 3 return InvokeFunction(context, ownerObject, funObject, new object[2]{ ownerObject, args }, 0, 0); 4 } 5 catch (Exception ex) { 6 Console.WriteLine(ex.Message + "\r\n" + ex.StackTrace); 7 return null; 8 } 9 } 10 11 public ScriptContext GetRunCompiledScriptContext() { 12 return DefaultContext; 13 } 14 15 public static Dictionary<string, FunctionObject> FuncObjects = new Dictionary<string, FunctionObject>(); 16 public FunctionObject GetCurrentFunctionObject(string name) { 17 return FuncObjects[name]; 18 } 19 20 private string CreaateAnonymousFuncName(Dictionary<string,FunctionObject> funcObjects,int index) { 21 if (funcObjects.ContainsKey("AnonymousFunc_"+index)) { 22 return CreaateAnonymousFuncName(funcObjects, index + 1); 23 } 24 else { 25 return "AnonymousFunc_" + index; 26 } 27 } 28 29 public void AttachEvent(ScriptContext context, object obj, EventInfo ei, FunctionObject functionValue) 30 { 31 //remove last attached event to sample object 32 DetachEvent(obj, ei); 33 34 string funcName = null; 35 //<anonymous> 36 if (functionValue.FunName == null) { 37 if (functionValue.FunctionInfo.IsAnonymous) { 38 funcName = CreaateAnonymousFuncName(FuncObjects,0); 39 FuncObjects.Add(funcName, functionValue); 40 } 41 } 42 else { 43 if (!FuncObjects.ContainsKey(functionValue.FunName)) { 44 funcName = functionValue.FunName; 45 FuncObjects.Add(functionValue.FunName, functionValue); 46 } 47 else { 48 funcName = CreaateAnonymousFuncName(FuncObjects, 0); 49 FuncObjects.Add(funcName, functionValue); 50 } 51 } 52 53 EventHandlerInfo ehi = new EventHandlerInfo(this, context, obj, ei, null, functionValue); 54 55 AssemblyName assemblyName = new AssemblyName(); 56 assemblyName.Name = funcName+ "EventMethod"; 57 AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, 58 AssemblyBuilderAccess.Run); 59 60 ModuleBuilder moduleBuilder = 61 assemblyBuilder.DefineDynamicModule(assemblyName.Name); 62 // assemblyBuilder.DefineDynamicModule(assemblyName.Name, "test.dll"); 63 TypeBuilder typeBuilder = moduleBuilder.DefineType("Handler", 64 TypeAttributes.Class | TypeAttributes.Public); 65 66 Type eventType = ei.EventHandlerType; 67 MethodInfo invokeMethod = eventType.GetMethod("Invoke"); 68 ParameterInfo[] parms = invokeMethod.GetParameters(); 69 70 Type[] parmTypes = new Type[parms.Length]; 71 for (int i = 0; i < parms.Length; i++) { 72 parmTypes[i] = parms[i].ParameterType; 73 } 74 75 // IL Event Method Name 76 Control ctrl = obj as Control; 77 string evnetMethodName = ctrl?.Name; 78 evnetMethodName += '_' + funcName; 79 80 MethodBuilder handler = typeBuilder.DefineMethod(evnetMethodName, 81 MethodAttributes.Public | MethodAttributes.Static, 82 invokeMethod.ReturnType, parmTypes); 83 84 Type[] types = new Type[] { 85 typeof(ScriptContext), 86 typeof(object), 87 typeof(AbstractFunctionObject), 88 typeof(object[]), 89 }; 90 // event 要掉用脚本里的方法 91 MethodInfo doEventMethod = typeof(ScriptRunningMachine).GetMethod("DoEvent",types); 92 93 MethodInfo getContextMethod = 94 typeof(ScriptRunningMachine).GetMethod("GetRunCompiledScriptContext"); 95 96 MethodInfo getFuncObj = 97 typeof(ScriptRunningMachine).GetMethod("GetCurrentFunctionObject"); 98 99 ILGenerator il = handler.GetILGenerator(); 100 il.DeclareLocal(typeof(object)); 101 il.DeclareLocal(typeof(object)); 102 il.DeclareLocal(typeof(object)); 103 il.DeclareLocal(typeof(object)); 104 il.DeclareLocal(typeof(object)); 105 106 //ScriptContext context, object ownerObject, AbstractFunctionObject funObject, object[] args 107 108 il.Emit(OpCodes.Ldarg_0); 109 il.Emit(OpCodes.Call, getContextMethod); 110 il.Emit(OpCodes.Stloc_0); // context 111 il.Emit(OpCodes.Ldarg_0); 112 il.Emit(OpCodes.Ldstr, funcName); 113 il.Emit(OpCodes.Call, getFuncObj); 114 il.Emit(OpCodes.Stloc_2); // funObject 115 116 il.Emit(OpCodes.Ldarg_0); 117 il.Emit(OpCodes.Ldarg_0); 118 il.Emit(OpCodes.Stloc_1); //ownerObject 119 120 il.Emit(OpCodes.Ldarg_1); 121 il.Emit(OpCodes.Stloc_3); 122 123 il.Emit(OpCodes.Ldarg_0); 124 il.Emit(OpCodes.Ldloc_0); 125 il.Emit(OpCodes.Ldloc_1); 126 il.Emit(OpCodes.Ldloc_2); 127 il.Emit(OpCodes.Ldloc_3); 128 il.Emit(OpCodes.Call, doEventMethod); 129 il.Emit(OpCodes.Stloc_S, 4); 130 il.Emit(OpCodes.Pop); 131 il.Emit(OpCodes.Nop); 132 il.Emit(OpCodes.Ret); 133 134 // finished 135 Type finished = typeBuilder.CreateType(); 136 // assemblyBuilder.Save("test.dll"); 137 MethodInfo eventHandler = finished.GetMethod(evnetMethodName); 138 139 object[] invokeParams = new object[parms.Length]; 140 for (int i = 0; i < parms.Length; i++) { 141 invokeParams[i] = parms.GetValue(i); 142 } 143 144 Delegate del = Delegate.CreateDelegate(eventType, eventHandler); 145 146 ei.AddEventHandler(obj, del); 147 // support mulit call 148 // ehi.ActionMethod = del; 149 RegisteredEventHandlers.Add(ehi); 150 return; 151 }
这里做个说明,把两个 test.dll 的 代码注释去掉 把AssemblyBuilderAccess 改成 runandsave 就可以看到生成的test.dll 了用dnspy 打开 就可以看到实际生成的代码了,(感觉棒棒哒==)
注意脚本的 ScriptContext DefaultContext 要 改为 static ,否则报内存异常,gc 原因?
下面看一下效果图
s 和 e 的参数也都有(局部 变量还没做出来 否则对脚本设计时参数使用帮助比较大,可以知道实际的可用的变量)
上面放几张设计器的界面,虽然还比较low 但还是可以用的,窗体设计器以后在更,也没有什么,微软官方的demo 改的,msdn 上有的,但要做一些修改,界面用到了
AvalonEdit DockPanel(最新版 修改为 .net 2.0) 这里做个预告