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
很容易就能找到御三家。
示例:
#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。