有没有办法在C#中访问COM对象的虚方法表以获取函数的地址?
答案 0 :(得分:8)
经过大量搜索并拼凑出不同的部分解决方案后,我想出了如何做到这一点。
首先,您需要为您尝试访问的对象定义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());
}
额外阅读/参考资料:
答案 1 :(得分:2)
您无法从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-table插槽条目。
答案 2 :(得分:0)
您确定要询问C#吗?
无论方法是否为虚拟,您都不需要在C#中获取函数地址。您可以实例化委托,它包含对该方法的托管引用。如果使用虚方法实例化委托,则将虚拟调用方法,即它将调用派生最多的覆盖。
C#中没有必要读取原始vtable等系统内部。
答案 3 :(得分:0)
我能想到的最接近的是Marshal.GetFunctionPointerForDelegate
,尽管这是从非托管COM方法中删除的至少一个级别(因为COM调用将包装在.NET委托中)。
您需要这些信息?