Есть ли в C # способ получить доступ к таблице виртуальных методов COM-объекта для получения адреса функции?
После долгого поиска и объединения различных частичных решений я понял, как это сделать.
Сначала вам нужно определить компонентный класс 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());
}
Дополнительное чтение / ссылки:
Вы не можете извлечь указатель на собственный интерфейс из RCW. Но вы можете вызвать Marshal.GetComInterfaceForObject (), это должно быть хорошо, если вы запрашиваете правильный интерфейс. Оттуда вы можете Marshal.ReadIntPtr (), чтобы получить записи v-таблицы. Смещение 0 - это QueryInterface, 4 - это AddRef, 8 - это Release, 12 - это первый метод интерфейса и т. Д. Удвойте это значение для кода x64. Marshal.GetComSlotForMethodInfo () - это вариант.
На самом деле для вызова метода требуется Marshal.GetDelegateForFunctionPointer (), вам необходимо объявить делегат с точной подписью метода интерфейса COM. Это будет не подпись, которую он имеет при обычном вызове этого метода, это подпись [PreserveSig]. Другими словами, функция, которая возвращает HRESULT и аргумент ref, если она возвращает значение.
Возможностей взорвать вашу программу предостаточно.
После обновления: вам потребуются Marshal.GetFunctionPointerForDelegate и Marshal.WriteIntPtr для исправления записи слота v-таблицы.
Вы уверены, что спрашиваете о C #?
Вам не нужно чтобы получить адреса функций в C #, независимо от того, является ли метод виртуальным или нет. Вы можете создать экземпляр делегата , который содержит управляемую ссылку на метод. Если вы создаете экземпляр делегата с помощью виртуального метода, тогда метод будет вызываться виртуально, т.е. он вызовет наиболее производное переопределение.
В C # нет необходимости читать внутренние компоненты системы, такие как необработанная vtable.
Самое близкое, что я могу придумать, это Marshal.GetFunctionPointerForDelegate
, хотя это по крайней мере один уровень от неуправляемого метода COM (поскольку вызов COM будет обернут в делегат .NET).
Для чего вам нужна эта информация?