从C#访问COM vtable

lfa*_*lin 7 c# com interop

在C#中是否有任何方法可以访问COM对象的虚方法表以获取函数的地址?

lfa*_*lin 8

在经过大量搜索并拼凑出不同的部分解决方案后,我想出了如何做到这一点.

首先,您需要为要尝试访问的对象定义COM coclass:

[ComImport, Guid("..."), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ISomeCOMInterface
{
   // Define interface methods here, using PInvoke conversion between types
}
Run Code Online (Sandbox Code Playgroud)

接下来,您需要实例化COM对象.有几种方法可以做到这一点.由于我对DirectSound感兴趣,我用过:

[DllImport("dsound.dll", EntryPoint = "DirectSoundCreate", ...]
static extern void DirectSoundCreate(IntPtr GUID, [Out, MarshalAs(UnmanagedType.Interface)] out IDirectSound directSound, IntPtr pUnkOuter);

IDirectSound directSound;
DirectSoundCreate(IntPtr.Zero, out directSound, IntPtr.Zero);
Run Code Online (Sandbox Code Playgroud)

因为我现在有了COM对象,所以我可以使用Hans的建议Marshal.GetComInterfaceForObject():

IntPtr comPtr = Marshal.GetComInterfaceForObject(directSound, typeof(IDirectSound));
IntPtr vTable = Marshal.ReadIntPtr(comPtr);
Run Code Online (Sandbox Code Playgroud)

作为额外的奖励,您可以像这样迭代vtable函数:

int start = Marshal.GetStartComSlot(typeof(IDirectSound));
int end = Marshal.GetEndComSlot(typeof(IDirectSound));

ComMemberType mType = 0;
for (int i = start; i <= end; i++)
{
    System.Reflection.MemberInfo mi = Marshal.GetMethodInfoForComSlot(typeof(IDirectSound), i, ref mType);
    Console.WriteLine("Method {0} at address 0x{1:X}", mi.Name, Marshal.ReadIntPtr(vTable, i * Marshal.SizeOf(typeof(IntPtr))).ToInt64());
}
Run Code Online (Sandbox Code Playgroud)

额外阅读/参考: