从C#访问COM vtable

有没有办法在C#中访问COM对象的虚方法表以获取函数的地址?

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

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

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

接下来,您需要实例化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); 

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

 IntPtr comPtr = Marshal.GetComInterfaceForObject(directSound, typeof(IDirectSound)); IntPtr vTable = Marshal.ReadIntPtr(comPtr); 

作为额外的奖励,您可以像这样迭代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()); } 

额外阅读/参考:

您无法从RCW中挖掘本机接口指针。 但是你可以调用Marshal.GetComInterfaceForObject(),只要你要求正确的界面就应该是好的。 从那里,您可以使用Marshal.ReadIntPtr()来获取v表条目。 Offset 0是QueryInterface,4是AddRef,8是Release,12是第一个接口方法,等等。对于x64代码是双倍的。 Marshal.GetComSlotForMethodInfo()是一个选项。

实际调用该方法需要Marshal.GetDelegateForFunctionPointer(),您需要使用COM接口方法的确切签名声明该委托。 当你正常调用这个方法时,它不会是它的签名,它是[PreserveSig]签名。 换句话说,返回HRESULT的函数和返回值的ref参数。

轰炸你的计划的机会很多。


更新后:您需要Marshal.GetFunctionPointerForDelegate和Marshal.WriteIntPtr来修补v表插槽条目。

我能想到的最接近的是Marshal.GetFunctionPointerForDelegate ,尽管这至少是从非托管COM方法中删除的一个级别(因为COM调用将包装在.NET委托中)。

你需要这些信息是什么?

你确定你在询问C#吗?

无论方法是否为虚拟,您都不需要在C#中获取函数地址。 您可以实例化一个委托 ,该委托包含对该方法的托管引用。 如果使用虚方法实例化委托,则将虚拟调用该方法,即它将调用最派生的覆盖。

C#中没有必要读取像原始vtable那样的系统内部。