一般来说COM类要想要具有事件通知,那么它必须实现事件源,但事件源具有两类形式一类是传统的“IConnectPoint”接口方式“点对点”挂接,比较麻烦需要知道被挂接事件接口的IID,另一类是“IReflect”接口挂接,它不需要知道挂接事件接口的IID。
一般来说大多数COM事件的挂接,都只采取传统的方式用“IConnectPoint”挂接,而类似如“MSXML2.XMLHTTP”这类COM对象的事件就必须要用“IReflect”接口挂接。
COM技术在现在的时代已经可以算过时了,还会用、提到的人越来越稀少(莫说别人,我自己也快忘得差不多了),不过上面的两个挂接方法的确是足够的经典,今天就写个demo,天秀一波,炒炒冷饭!
经典的“事件挂接”,挂接成功后返回“事件挂接 cookie”
public static int Advise(object obj, Guid riid, object sink) { int cookie = -1; IConnectionPointContainer icpc = obj as IConnectionPointContainer; if (icpc != null) { IConnectionPoint icp = null; try { icpc.FindConnectionPoint(ref riid, out icp); } catch (Exception e) { Debug.WriteLine(e.Message); } if (icp != null) { icp.Advise(sink, out cookie); } } return cookie; }
苍天啊,让我们秀一把啊,请你闭上眼睛,以免误伤!
public static int AtlAdvise(object obj, Guid riid, object sink) { if (advise == null) { Type[] Types = { typeof(object), typeof(Guid), typeof(object) }; advise = new DynamicMethod("Advise", typeof(int), Types, true); var mdil = advise.GetILGenerator(); var lbTrue = mdil.DefineLabel(); Types = new Type[] { typeof(int), typeof(IConnectionPoint), typeof(IConnectionPointContainer), typeof(Guid), typeof(Exception), typeof(Debug), typeof(string) }; mdil.DeclareLocal(Types[0]); mdil.DeclareLocal(Types[1]); mdil.DeclareLocal(Types[2]); mdil.Emit(OpCodes.Ldc_I4_M1); mdil.Emit(OpCodes.Stloc_0); mdil.Emit(OpCodes.Ldarg_S, 0); mdil.Emit(OpCodes.Isinst, Types[2]); mdil.Emit(OpCodes.Stloc_2); mdil.Emit(OpCodes.Ldloc_2); mdil.Emit(OpCodes.Ldnull); mdil.Emit(OpCodes.Ceq); mdil.Emit(OpCodes.Brtrue, lbTrue); mdil.BeginExceptionBlock(); mdil.Emit(OpCodes.Ldloc_2); mdil.Emit(OpCodes.Ldarga, 1); mdil.Emit(OpCodes.Ldloca, 1); mdil.Emit(OpCodes.Callvirt, Types[2].GetMethod("FindConnectionPoint")); mdil.Emit(OpCodes.Ldloc_1); mdil.Emit(OpCodes.Ldarg_2); mdil.Emit(OpCodes.Ldloca, 0); mdil.Emit(OpCodes.Callvirt, Types[1].GetMethod("Advise")); mdil.BeginCatchBlock(Types[4]); mdil.Emit(OpCodes.Callvirt, Types[4].GetMethod("get_Message")); mdil.Emit(OpCodes.Call, Types[5].GetMethod("WriteLine", new Type[] { Types[6] })); mdil.EndExceptionBlock(); mdil.MarkLabel(lbTrue); mdil.Emit(OpCodes.Ldloc_0); mdil.Emit(OpCodes.Ret); } return (int)advise.Invoke(advise, new object[] { obj, riid, sink }); }
经典的“取消事件挂接”,返回取消是否成功。
public static bool Unadvise(object obj, Guid riid, object sink) { int cookie = -1; IConnectionPointContainer icpc = obj as IConnectionPointContainer; if (icpc != null) { IConnectionPoint icp = null; try { icpc.FindConnectionPoint(ref riid, out icp); } catch (Exception e) { Debug.WriteLine(e.Message); } try { icp.Unadvise(cookie); return true; } catch (Exception e) { Debug.WriteLine(e.Message); } } return false; }
喜迎陈独秀同学!
public static bool AtlUnadvise(object obj, Guid riid, int cookie) { if (unadvise == null) { Type[] Types = { typeof(object), typeof(Guid), typeof(int) }; unadvise = new DynamicMethod("Unadvise", typeof(bool), Types, true); Types = new Type[] { typeof(bool), typeof(IConnectionPoint), typeof(IConnectionPointContainer), typeof(Exception), typeof(Debug), typeof(string), typeof(DWebBrowserEvents) }; var il = unadvise.GetILGenerator(); for (var i = 0; i < 3; i++) il.DeclareLocal(Types[i]); var lbTrue = il.DefineLabel(); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Isinst, Types[2]); il.Emit(OpCodes.Stloc_2); il.Emit(OpCodes.Ldloc_2); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brtrue, lbTrue); il.BeginExceptionBlock(); il.Emit(OpCodes.Ldloc_2); il.Emit(OpCodes.Ldarga, 1); il.Emit(OpCodes.Ldloca, 1); il.Emit(OpCodes.Callvirt, Types[2].GetMethod("FindConnectionPoint")); il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Callvirt, Types[1].GetMethod("Unadvise")); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Stloc_0); il.BeginCatchBlock(Types[3]); il.Emit(OpCodes.Callvirt, Types[3].GetMethod("get_Message")); il.Emit(OpCodes.Call, Types[4].GetMethod("WriteLine", new Type[] { Types[5] })); il.EndExceptionBlock(); il.MarkLabel(lbTrue); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ret); } return (bool)AtlUnadvise(obj, riid, cookie); }
上面秀了这么多,“IReflect”接口方式挂接呢?本文将以“MSXML2.XMLHTTP”为例子来一个demo来秀天地秀地板,展示我作为一个合格”菜鸟“的正确姿态。
namespace TECHMARS.VM { using System; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; class Program { [MTAThread] static void Main(string[] args) { dynamic xhr = Activator.CreateInstance(Type.GetTypeFromProgID("MSXML2.XMLHTTP")); xhr.open( "GET", "http://zhangmenshiting.qianqian.com/data2/music/d94b2fcee28daff91d4ef0097ac868f9/556455954/556455954.mp3?xcode=8369c60cd042ff6e26d7084657a59bf4", true); xhr.onreadystatechange = new XhrToClrEventProxy((object)xhr, "onreadystatechange", (sender, e) => { Console.WriteLine(xhr.readyState); }); xhr.send(null); Console.ReadLine(); } [Guid("F55597A2-D12E-4C2B-AF23-D877634796C7")] private class XhrToClrEventProxy : IReflect { private string evtName; private object sender; private IReflect reflect; private EventHandler evtHandler; public string EventName { get { return this.evtName; } } Type IReflect.UnderlyingSystemType { get { return this.reflect.UnderlyingSystemType; } } FieldInfo IReflect.GetField(string name, BindingFlags attr) { return this.reflect.GetField(name, attr); } FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) { return this.reflect.GetFields(bindingAttr); } MemberInfo[] IReflect.GetMember(string name, BindingFlags attr) { return this.reflect.GetMember(name, attr); } MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) { return this.reflect.GetMembers(bindingAttr); } MethodInfo IReflect.GetMethod(string name, BindingFlags attr) { return this.reflect.GetMethod(name, attr); } MethodInfo IReflect.GetMethod(string name, BindingFlags attr, Binder binder, Type[] types, ParameterModifier[] modifiers) { return this.reflect.GetMethod(name, attr, binder, types, modifiers); } MethodInfo[] IReflect.GetMethods(BindingFlags attr) { return this.reflect.GetMethods(attr); } PropertyInfo[] IReflect.GetProperties(BindingFlags attr) { return this.reflect.GetProperties(attr); } PropertyInfo IReflect.GetProperty(string name, BindingFlags attr) { return this.reflect.GetProperty(name, attr); } PropertyInfo IReflect.GetProperty(string name, BindingFlags attr, Binder binder, Type ret, Type[] types, ParameterModifier[] modifiers) { return this.reflect.GetProperty(name, attr, binder, ret, types, modifiers); } object IReflect.InvokeMember(string name, BindingFlags attr, Binder binder, object obj, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] nameds) { if (name == "[DISPID=0]") { EventHandler handler = this.evtHandler; if (handler != null) { handler(this.sender, EventArgs.Empty); } return null; } return this.reflect.InvokeMember(name, attr, binder, obj, args, modifiers, culture, nameds); } public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) { throw new NotImplementedException(); } public XhrToClrEventProxy(object sender, string evtName, EventHandler evtHandler) { if (evtName == null) { throw new ArgumentNullException("evtName"); } if (sender == null) { throw new ArgumentNullException("sender"); } if (evtHandler == null) { throw new ArgumentNullException("eventHandler"); } if (evtName.Length <= 0) { throw new ArgumentException("evtName"); } this.reflect = typeof(XhrToClrEventProxy); this.sender = sender; this.evtName = evtName; this.evtHandler = evtHandler; } } } }