COM接口实现的随意的一点小记录

1、所有的COM接口的vtbl前三项都是一样的。
COM接口和C++编译器生成的抽象基类的内存接口是相同的。
vtbl的初始位置保存着IUnknown的御三家的地址,take an example:

在VS里面的例子,实例化IWebBrowser2之后,查看其地址的数据:

>dd 0x00821ae8
0x00821AE8  63362618(vtbl) 005d25a8 005d87fc abababab  
0x00821AF8  abababab       feeefeee 00000000 00000000  
0x00821B08  45b71a23       00001234 008237f8 008200c4  
0x00821B18  feeefeee       feeefeee feeefeee feeefeee  

f2.png

很容易就能找到御三家。

示例:

#include <ObjBase.h>
#include <iostream>

static IID IID_IA = {0x12345678, 0x1234, 0x1234, {0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12}};

interface IA : IUnknown
{
    virtual void __stdcall foo() = 0;
};

class CA : public IA
{
public:
    virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
    virtual ULONG __stdcall AddRef(){std::cout<<"addref\n"<<std::endl; return 0;}
    virtual ULONG __stdcall Release(){std::cout<<"release\n"<<std::endl; return 0;}
    virtual void __stdcall foo()
    {
        std::cout << "a" << std::endl;
    }
};

HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
{
    if (iid == IID_IUnknown || iid == IID_IA)
    {
        *ppv = static_cast<IA*>(this);
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
    return S_OK;
}

IUnknown* CreateInstance()
{
    IUnknown* pI = static_cast<IA*>(new CA);
    pI->AddRef();
    return pI;
}

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;
    IUnknown* pIUnknown = CreateInstance();

    IA* pIA = NULL;
    hr = pIUnknown->QueryInterface(IID_IA, (void**)&pIA);
    if (SUCCEEDED(hr))
    {
        pIA->foo();

    printf("%08x %08x %08x",*(DWORD*)pIA, 
        *((DWORD*)pIA+1), 
        *((DWORD*)pIA+2));
    }   

    return 0;
}

输出:
00a07834 fdfdfdfd abababab

其实事实上pIA此时内存也就这一个东西了,拿出VS的dd证明一下代码没错:

>dd 0x007c8c38
0x007C8C38  00a07834(vtbl) fdfdfdfd abababab abababab  
0x007C8C48  00000000 00000000 18fb7b76 1c00dcbf  
0x007C8C58  007c8c18 007c4810 0f9a22b0 0000041d  
0x007C8C68  00000010 00000002 000000ba fdfdfdfd 

00a07834解引用后,前三项为QueryInterface、AddRef、Release的地址。

跟这关系不大的一个,在添加了私有成员之后:

class CA : public IA
{
public:
………………
private:
    ULONG m_SomeLong;
};

vtbl后面才会多出数据来,以前只是看书知道这事儿,这回算是实践了吧:

{,,testInterface.exe}(*(CA*){*}pIA).m_SomeLong  3452816845  unsigned long

>dd 0x00888c38
0x00888C38  00377834 *cdcdcdcd* fdfdfdfd abababab  
0x00888C48  abababab feeefeee 00000000 00000000  
0x00888C58  0d743d6b 1c0082e0 00888c18 00884810  
0x00888C68  0fbf22b0 0000041d 00000010 00000002  

(备注: 3452816845 (dec) == 0xcdcdcdcd (hex))

2、IDispatch接口中GetIDsOfNames根据提供的函数名称返回DISPID,Invoke则内部实现了一组按索引访问的函数,有点像是vtbl。

标签:none

添加新评论

captcha
请输入验证码