一般来说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;
}
}
}
}