Доступ к COM vtable из C # - PullRequest
       3

Доступ к COM vtable из C #

7 голосов
/ 17 августа 2010

Есть ли какой-либо способ в C # получить доступ к таблице виртуальных методов COM-объекта, чтобы получить адрес функции?

Ответы [ 4 ]

8 голосов
/ 18 августа 2010

После долгих поисков и объединения различных частичных решений я понял, как это сделать.

Сначала вам нужно определить 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());
}

Дополнительное чтение / Ссылки:

2 голосов
/ 17 августа 2010

Вы не можете выкопать указатель на собственный интерфейс из 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-таблицы.

0 голосов
/ 17 августа 2010

Самое близкое, что я могу вспомнить, это Marshal.GetFunctionPointerForDelegate, хотя это не менее одного уровня, удаленного из неуправляемого метода COM (поскольку вызов COM будет заключен в делегат .NET).*

Зачем вам нужна эта информация?

0 голосов
/ 17 августа 2010

Вы уверены, что спрашиваете о C #?

Вам не нужно получать адреса функций в C #, независимо от того, является ли метод виртуальным или нет. Вы можете создать экземпляр делегата , который содержит управляемую ссылку на метод. Если вы создаете экземпляр делегата с помощью виртуального метода, то метод будет вызываться виртуально , то есть будет вызываться наиболее производное переопределение.

В C # нет необходимости читать внутренние компоненты системы, такие как необработанный vtable.

...