WinForm WPF基于一个脚本引擎(ReoScript)的窗体设计器开发总结(二)(创建动态委托实例)

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 }
View Code

第一次改造是用这里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 }
View Code

这里做个说明,把两个 test.dll 的 代码注释去掉 把AssemblyBuilderAccess 改成 runandsave 就可以看到生成的test.dll 了用dnspy 打开 就可以看到实际生成的代码了,(感觉棒棒哒==)

注意脚本的 ScriptContext  DefaultContext  要 改为 static  ,否则报内存异常,gc 原因?

下面看一下效果图

 

 

s 和 e 的参数也都有(局部 变量还没做出来 否则对脚本设计时参数使用帮助比较大,可以知道实际的可用的变量)

上面放几张设计器的界面,虽然还比较low 但还是可以用的,窗体设计器以后在更,也没有什么,微软官方的demo 改的,msdn 上有的,但要做一些修改,界面用到了

AvalonEdit  DockPanel(最新版 修改为 .net 2.0) 这里做个预告

猜你喜欢

转载自www.cnblogs.com/Lite/p/9023623.html
今日推荐