在.NET Framework v4.0发布的新功能中,在名字空间System.Runtime.InteropServices新增加了一个叫做ICustomQueryInterface的Interface, 顾名思义,这个Interface的功能就是使得用户可以自己控制QueryInterface这个COM最常用的函数的行为。在v4.0以前,所有作用于托管组件上的QI行为,都是由CLR内部的IUnkown:QueryInterface控制的,比如,如果你QI著名的IDispatch接口时,你得到的永远都是CLR提供的那个IDispatch,诸如此类的还有IMarshal/IProvideClassInfo等一些常用的Interface。如果你非常希望用自己的IDispatch实现来替换clr提供的实现,那么恭喜你,ICustomQueryInterface就是为你而生的!当然,ICustomQueryInterface所带来的,不仅仅是简单的Interface替换,它甚至可以使得Aggregate托管组件也成为现实,wow,如果你了解Aggregation的话,一定会因此而雀跃不已的。我会在另一篇文章中通过例程给大家做一个详细的介绍。
让我们来看看这个Interface的定义吧
1: public interface ICustomQueryInterface 2: { 3: CustomQueryInterfaceResult GetInterface([In]ref Guid iid, out IntPtr ppv); 4: } 5:是的,就是这么简单,就一个GetInterface方法,再仔细看看它的方法参数,是不是和c++里面的QueryInterface有点神似啊。哈哈,其实你可以把它理解成QueryInterface的托管实现也无妨啊!不过它还有个小小的功能,就是如果自己不想处理这个QI,就返回NotHandled, clr看到这个返回值,就会调用自己的QI实现来帮你处理这个请求,爽吧。
让我们来看看有了这个Interface之后clr内部关于QI的处理流程图吧(画的是英文版,烦请大家将就一下啦,没装viso,画图超级累啊!!!!)
从这个图上我们可以看到,除了不能处理对IUnknown的QI请求(要求别太高嘛),其他统统OK!
理论一大堆了,来实战一下。
看看我们的托管组件的实现
1: using System; 2: using System.Runtime.InteropServices; 3: 4: namespace States 5: { 6: [Guid("00020400-0000-0000-C000-000000001147")] 7: [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 8: public interface ICQ 9: { 10: int func(); 11: void slot2(); 12: void slot3(); 13: void slot4(); 14: } 15: 16: [Guid("11120400-0000-0000-C000-000000001148")] 17: [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 18: public interface IA 19: { 20: int FuncA(); 21: } 22: 23: [Guid("22220400-0000-0000-C000-000000001149")] 24: [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 25: public interface IB 26: { 27: int FuncB(); 28: } 29: 30: 31: 32: [Guid("00020400-0000-0000-C000-000000001150")] 33: [ClassInterface(ClassInterfaceType.None)] 34: public class StatesComServer : ICustomQueryInterface, ICQ, IA, IB 35: { 36: public readonly Guid IID_IA = new Guid("11120400-0000-0000-C000-000000001148"); 37: 38: public CustomQueryInterfaceResult GetInterface([In]ref Guid iid, out IntPtr intf) 39: { 40: if (iid == WellKnownGuids.IID_IDispatch) 41: { 42: intf = Marshal.GetComInterfaceForObject(this, typeof(ICQ), CustomQueryInterfaceMode.Ignore); 43: return CustomQueryInterfaceResult.Handled; 44: } 45: 46: if (iid == IID_IA) 47: { 48: intf = IntPtr.Zero; 49: return CustomQueryInterfaceResult.Failed; 50: } 51: 52: intf = IntPtr.Zero; 53: return CustomQueryInterfaceResult.NotHandled; 54: } 55: 56: public int func() 57: { 58: Console.WriteLine("This is Interface ICQ, not the IDispatch!!!"); 59: return 2008; 60: } 61: 62: public int FuncA() 63: { 64: Console.WriteLine("This is Interface IA!!!"); 65: return 3008; 66: } 67: 68: public int FuncB() 69: { 70: Console.WriteLine("This is Interface IB!!!"); 71: return 4008; 72: } 73: 74: 75: #region Empty Functions 76: public void slot2() { } 77: public void slot3() { } 78: public void slot4() { } 79: #endregion 80: } 81: 82: } 83:这里有两个地方需要解释一下
1)对于ICQ这个接口,他实际上是作为自定义的IDispatch出现的,所以理论上应该他所有的函数声明应该和IDispatch保持一致,但是个人比较偷懒,毕竟这只是一个例程,主要是将如何使用ICustomQueryInterface这个接口,所以后三个方法的用途仅仅是为了保持ICQ的虚拟函数表(一共4个函数)和IDispatch保持一致,如果真的有人在客户端调IDispatch.Invoke(函数表中的第四个函数)的话,那调用必然会被重定向到ICQ.slot4,因为两个函数的参数声明完全不一样,AccessViolationException就无法避免了。
2)再讲一下GetInterface的返回值,如果是CustomQueryInterfaceResult.Failed,意思是QI失败。CustomQueryInterfaceResult.NotHandled意思是让clr去处理这个请求,CustomQueryInterfaceResult.Handled是告诉clr,已经处理好了,指针值保存在intf里面,直接返回给用户就可以了。
再来看看我们的客户端
IDispatch * pDisp = NULL; printf("Scenario 1: QI IDispatch interface, Expected the Custom IDispatch interface\n"); hresult = pUnknown->QueryInterface(IID_IDispatch, (void**)&pDisp); UINT count = 0; hresult = pDisp->GetTypeInfoCount(&count); printf("Return value of GetTypeInfoCount is %d\n", count); IA * pA = NULL; printf("Scenario 2: QI IA interface, Expected failed\n"); hresult = pUnknown->QueryInterface(IID_IA, (void**)&pA); if (FAILED(hresult)) { printf("Failed to QI IA with error code %x\n", count); } IB * pB = NULL; printf("Scenario 3: QI IB interface interface, Expected the IB interface\n"); hresult = pUnknown->QueryInterface(IID_IB, (void**)&pB); long i = 0; hresult = pB->FuncB(&i);再来看看我们的输出结果。
Scenario 1: QI IDispatch interface, Expected the Custom IDispatch interface This is Interface ICQ, not the IDispatch!!! Return value of GetTypeInfoCount is 2008 Scenario 2: QI iA interface, Expected failed Failed to QI IA with error code 7d8 Scenario 3: QI IB interface interface, Expected the IB interface This is Interface IB!!!Published Monday, August 10, 2009 11:34 AM by SilverlightShanghai
转自:http://blogs.msdn.com/silverlightshanghai/archive/2009/08/10/net-4-0-interop-icustomqueryinterface.aspx
转载于:https://www.cnblogs.com/wuhenke/archive/2010/01/23/1654657.html
相关资源:数据结构—成绩单生成器