Как перебрать vtable из COM Coclass? - PullRequest
4 голосов
/ 10 марта 2010

Как я могу выполнить итерацию / получить доступ к виртуальной таблице COM-класса COM, которая будет реализовывать методы его открытых интерфейсов?

Мне нужно получить доступ к той части виртуальной таблицы, где хранятся все адреса открытых методов ее интерфейсов..

Например, Math - это COM-объект, его открытый интерфейс - «Операции», а «Sum» - метод этого интерфейса, как мне получить адрес «Sum»?

Ответы [ 2 ]

6 голосов
/ 07 декабря 2010

Я не собираюсь спрашивать, почему вы делаете это таким образом, но, возможно, это могло бы помочь ... Каждый COM-объект должен реализовывать как минимум интерфейс IUnknown. Следовательно, первые четыре байта экземпляра объекта COM являются указателем на объект IUnknown. Первые четыре байта объекта IUnknown (и любого другого объекта с виртуальными функциями) - это указатель на vtbl.

(В этом примере проверка ошибок отсутствует, поэтому, пожалуйста, не разбивайте волосы по этому вопросу.)

Я использовал экземпляр IReferenceClock для демонстрации.

int main()
{

    CoInitialize( NULL );

    IReferenceClock* pRefClock;
    HRESULT hr = CoCreateInstance( CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER, IID_IReferenceClock, (void**)&pRefClock );

    DWORD* pIUnknownAddress = (DWORD*)pRefClock;
    DWORD* pVTBLaddress = (DWORD*)*pIUnknownAddress;

    // for example, the next interface could be accessed like this
    DWORD* pNextInterfaceAddress = ( (DWORD*)pRefClock ) + 1;
    DWORD* pNextVTBLaddress = (DWORD*)*pNextInterfaceAddress;
    // and you would access virtual functions in the same way as QueryInterface, AddRef and Release below in this example

    HRESULT (__stdcall *pQueryInterfaceFunction)(void*, REFIID, void**);
    ULONG (__stdcall *pAddRef)( void* );
    ULONG (__stdcall *pRelease)( void* );

    // IUnknown looks like this:
    // 
    // virtual HRESULT QueryInterface( REFIID riid, void** ppvObject);
    // virtual ULONG AddRef( void );
    // virtual ULONG Release( void );
    //
    // So, the first function in vtbl is QueryInterface, the second is AddRef...

    pQueryInterfaceFunction = (HRESULT (__stdcall*)(void*, REFIID, void**))*pVTBLaddress;
    pAddRef = (ULONG (__stdcall *)( void* ))*( pVTBLaddress + 1 );
    pRelease = (ULONG (__stdcall *)( void* ))*( pVTBLaddress + 2 );

    // Note: extra void* is actually this pointer.. see below that we pass pRefClock to every call

    IUnknown* pUnknown;
    UINT nRefCount;

    hr = pQueryInterfaceFunction( pRefClock, IID_IUnknown, (void**)&pUnknown );

    if( SUCCEEDED( hr ) )
    {
        nRefCount = pUnknown->Release();
        ATLTRACE( TEXT( "nRefCount = %d\n" ), nRefCount );
    }

    nRefCount = pAddRef( pRefClock );
    ATLTRACE( TEXT( "nRefCount after AddRef() call = %d\n" ), nRefCount );

    nRefCount = pRelease( pRefClock );
    ATLTRACE( TEXT( "nRefCount after Release() call = %d\n" ), nRefCount );

    nRefCount = pRefClock->Release();

    CoUninitialize();

    return 0;
}
0 голосов
/ 22 июля 2010

Извините, что отвечаю на вопрос, но я должен спросить "откуда?"

Если вы имеете в виду, как вы можете перебирать vtable из COM-клиента, я не думаю, что вы можете. На стороне клиента все, что у вас есть, это прокси-сервер, который знает, как взаимодействовать (возможно, между квартирами или кросс-процессами) с COM-сервером. Возможно, вы можете проверить vtable этого прокси-сервера, но он никогда не скажет вам адреса функций внутри COM-сервера.

Конечно, если сервер на самом деле работает в другом процессе, адрес функций может быть вам мало полезен. Даже если сервер находится в том же процессе, но в другой квартире, получение адресов функций может быть опасным: вы можете вызывать функции напрямую, обойти перехват COM и нарушить предположения класса сервера относительно вызывающего потока и т. Д.

Полагаю, что итерация vtable - это средство для достижения цели? Возможно, опубликуйте, что вы на самом деле пытаетесь сделать, и я думаю, что у COM, вероятно, есть способ сделать это.

...