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

时间:2010-04-02 16:56:38

标签: c# com-interop

我有一个第三方程序集,其中包含一个实现某个COM接口的公共抽象类。

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

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

实例化

如果我拥有的是ISomeInterface的CCW,是否有办法访问此类的公共方法?

澄清我的问题:

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

2 个答案:

答案 0 :(得分:0)

一般来说,没有。您只能获取此COM接口中的方法以及同一对象上的其他COM接口。

答案 1 :(得分:0)

这取决于您尝试调用的未发布方法是否具有您无法看到的内部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);
}

请注意,如果要为COM启用IUnknown和其他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];

此时,您需要声明一个托管委托,以便通过此函数指针进行调用。由于FooFuncinstance call,因此委托需要与相关的this实例绑定,在这种情况下为pObj,以便将该值插入每当调用FooFunc时,'invisible'第一个参数,就像函数所期望的那样。请注意,您无法利用将相关实例存储在my_delegate.Target属性中的常规CLR机制,因为这仅适用于托管对象,此处所讨论的目标不是。因此,额外的论证变得比“不可见”小:

// 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 vtable,例如:

  • 仅适用于需要它们的对象,
  • 仅针对特定的接口,
  • 仅在动态或需要时,和/或
  • 仅在需要或授权的时间内使用。

虽然我不确定这些要点中哪些适用于您所描述的情况,但似乎其中的部分或全部可能会因CLR主机的实施细节而异。不幸的是,主机部署这些优化中的任何一个都足以掩盖上述vtable snooping的类型。