• .NET 4.0 Interop新特性ICustomQueryInterface (转载)


    .NET 4.0 Interop新特性ICustomQueryInterface

    在.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,画图超级累啊!!!!)

    ICustomQueryInterface

    从这个图上我们可以看到,除了不能处理对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

     

  • 相关阅读:
    Java NIO3:缓冲区Buffer
    Java NIO2:NIO概述
    Mybatis学习总结(六)——高级映射(一对一,一对多,多对多)
    Java NIO1:浅谈I/O模型
    Java多线程(三)—— synchronized关键字详解
    Java IO(五)——字符流进阶及BufferedWriter、BufferedReader
    Java IO(四)——字符流
    mysql 实现树形的遍历
    Java IO(三)——字节流
    使用 SVN Hook 实现服务器端代码自动更新
  • 原文地址:https://www.cnblogs.com/wuhenke/p/1654657.html
Copyright © 2020-2023  润新知