Win32 COM调用惯例指定了对象的虚拟方法表(vtable)的布局。如果一种语言/编译器想支持COM,它必须以指定的方式布局其对象,以便其他组件可以使用它。
Win32 COM对象布局与C++对象布局密切吻合,这并非巧合。尽管COM最初开发的时候,C语言是最主要的编程语言,但设计者认为应该与新兴的新语言C++"友好相处"。
COM对象的布局在各种接口的头文件中都有明确的规定。例如,这里是objidl.h中的IPersist,在清理了一些宏之后。
typedef struct IPersistVtbl
{
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IPersist * This,
/* [in] */ REFIID riid,
/* [iid_is][out] */ void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
IPersist * This);
ULONG ( STDMETHODCALLTYPE *Release )(
IPersist * This);
HRESULT ( STDMETHODCALLTYPE *GetClassID )(
IPersist * This,
/* [out] */ CLSID *pClassID);
} IPersistVtbl;
struct IPersist
{
const struct IPersistVtbl *lpVtbl;
};
这对应于下面的内存布局。
p -> | lpVtbl -> | QueryInterface | / | AddRef | / | Release | / | GetClassID |
---|
这是什么意思呢?
COM接口指针是指向一个结构的指针,这个结构只由一个vtable组成。vtable是一个包含一堆函数指针的结构。列表中的每个函数都以那个接口指针(p)作为它的第一个参数("this")。
这一切的神奇之处在于,由于你的函数得到了p作为它的第一个参数,你可以把额外的东西 "挂 "到vtable上。
p -> | lpVtbl -> | QueryInterface | / | ... | AddRef | / | other stuff | Release | / | ... | GetClassID |
---|
vtable中的函数可以使用相对于接口指针的偏移来访问它的其他东西。
如果一个对象实现了多个接口,但它们都是彼此的后代,那么一个vtable就可以用于所有的接口。例如,上面的对象已经被设置为可以作为IUnknown或者IPersist使用,因为IUnknown是IPersist的一个子集。
另一方面,如果一个对象实现了多个接口,而这些接口又不是彼此的子集,那么就会得到多重继承,在这种情况下,对象在内存中的布局通常是这样的。
p -> | lpVtbl | -> | QueryInterface (1) | q -> | lpVtbl -> | QueryInterface (2) | AddRef (1) | / | … | AddRef (2) | Release (1) | / | other stuff | Release (2) | … | / | … | … |
---|
如果你使用的是来自第一个v表的接口,那么接口指针是p,但如果你使用的是来自第二个v表的接口,那么接口指针是q。
挂好这张图,因为明天我们将学习那些神秘的 "调整器thunks"。
www.deepl.com 翻译
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!