代码之家  ›  专栏  ›  技术社区  ›  mfeingold

通过COM接口访问底层托管对象

  •  0
  • mfeingold  · 技术社区  · 14 年前

    我有一个第三方程序集,它具有一个实现特定COM接口的公共抽象类。有某种影响

    [ComVisible(true)]
    public abstract class SomeClass: ISomeInterface
    {
      ....
      public void Method1() {...}
    }
    

    实际对象是扩展SomeClass的内部对象,由第三方代码实例化。

    如果我只有isomeInterface的CCW,那么有没有一种方法可以访问这个类的公共方法?

    澄清我的问题:

    我正在构建一个风味的Visual Studio项目,扩展F项目系统。我的代码和F项目系统都是托管的,但是我们之间有很多非托管代码。在我的项目经理中,我得到了一个指向F项目经理的指针(intptr),我可以将它强制转换为F项目经理实现的许多接口,但我需要对项目经理本身调用(public)方法,到目前为止,我找不到一种实现这一点的方法。

    2 回复  |  直到 8 年前
        1
  •  0
  •   sblom    14 年前

    一般来说,不能。只能在这个COM接口和同一对象上的其他COM接口中获取方法。

        2
  •  0
  •   Glenn Slayden    8 年前

    这取决于你试图调用的未发布的方法是否有一个你无法看到的内部COM(或C++)实现。COM接口要求实现对象提供固定位置 vtables 它通常可以从托管代码中访问和解释,即使它们不打算公开使用。

    对于本机COM库,COM vtable方案通常与运行时内部设置方式的二进制图像相对应。因为这些表是不可移动的,并且与理解良好的COM需求绑定在一起,所以不安全的托管代码可以浏览并找出如何处理任何事情。举例来说,您可以定义一个接口 IFoo 在与COM库的秘密接口并行的代码中——可能包含这样一个方法:

    [ComImport, SuppressUnmanagedCodeSecurity, Guid("00000000-0000-0000-0000-123456789ABC")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IFoo
    {
        int FooFunc(uint x);
    }
    

    请注意,如果要启用 IUnknown 以及其他对COM的clr支持,然后 [ComImport] 应该在这里使用,即使这是您自己独特的接口声明,而不是从任何地方“导入”。现在假设这个接口匹配某个外部COM对象的运行时VTABLE,或者甚至是一个非COM本地C++对象(给定适当的调整),您可以这样做:

    var vtbl = *(IntPtr**)pObj; // ptr to a C++ style object, perhaps from ICustomMarshaller
    // ...or...
    var vtbl = *(IntPtr**)((IntPtr*)pUnk - 1); // or an IUnknown ptr instead
    
    // since IUnknown methods are 0, 1, 2, vtable slot for FooFunc is likely '3'
    // but to fetch it in a more "official way"...
    var mi = typeof(IFoo).GetMethod(nameof(IFoo.FooFunc));
    int slot_ix = Marshal.GetComSlotForMethodInfo(mi);
    
    // fetch unmanaged function pointer to 'FooFunc'
    IntPtr pfn = vtbl[slot_ix];
    

    此时,您需要声明一个托管委托,以便通过这个函数指针进行调用。因为 FooFunc 是一个 instance call ,需要将委托与 this 实例,在本例中是 pObj ,以便每当 福丰公司 按函数的预期和要求调用。注意,您不能利用普通的clr机制将相关实例存储在 my_delegate.Target 属性,因为它只适用于托管对象,这里讨论的目标不是。因此,额外的论点变得比“看不见”要少一些:

    // important... note this crucial attribute and its argument ---v
    [ComVisible(true), UnmanagedFunctionPointer(CallingConvention.ThisCall)]
    public delegate int __this_FooFunc([In] IntPtr _this, [In] uint x);
    
    // create managed delegate for unmanaged function pointer from above
    var __FooFunc = Marshal.GetDelegateForFunctionPointer<__this_FooFunc>(pfn);
    
    // call it...      v--- but don't forget the 'this'
    int i = __FooFunc(pObj, 1234U);   // call vtable-based function from managed code
    

    不管怎样,这一切都是有效的,甚至没有那么粗略或有争议;我们基本上只是在COM规范中建立良好的步行结构,就像CLR的默认封送处理代码一样。

    现在说了这么多 ,如果您感兴趣的COM功能源于托管环境,如您所指出的可能是F,则上述功能很少或都不适用。托管运行时环境有机会在更复杂的基础上提供COM vTables,例如:

    • 只适用于需要它们的对象,
    • 仅针对预期的特定接口,
    • 仅在飞行中或需要时,和/或
    • 仅在要求或授权的时间内有效。

    尽管我不确定这些点中的哪一点适用于您描述的情况,但似乎它们的某些或全部可能会因clr主机的实现细节而有所不同。不幸的是,主机部署这些优化中的任何一个都足以屏蔽上述vtable监听类型。