После долгих поисков и объединения различных частичных решений я понял, как это сделать.
Сначала вам нужно определить COM-класс для объекта, к которому вы пытаетесь получить доступ:
[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-объект, я мог бы использовать предложение Ганса 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());
}
Дополнительное чтение / Ссылки: