1. 事实上,组件类获得接口映射表是通过GetInterfaceMap()静态成员函数先
获得interfaceMap结构变量,此变量的第二个值便是接口映射表的入口.
InterfaceMap的第一个值保存了基类的_GetBaseInterfaceMap函数指针.
为在组件类中找不到接口的定义时,通过_GetBaseInterfaceMap
获得基类的interfaceMap,从而获得基类的接口映射表入口….
就这样不断上溯..
通过这些宏,我们实现了接口映射表,并为在类层次中辗转提供了
方法.这样,组件类就可以通过这张表实现接口的查询和地址定位了.
然而这里还有一个大问题
我们说,任何一个接口必须实现AddRef、Release、QueryInterface.
(注2:事实上,接口的定义在Idispatch出现后,有了一定的转变,你可将任何
实现了方法和数据的结合看作接口.)…可这里没见什么关于这方面的定义啊..
不错,这里还有一个”大阴谋”呢…. 关于这个还得从
#define INTERFACE_PART(theClass, iid, localClass)
{ &iid, offsetof(theClass, m_x##localClass) },
//填充接口影射表
说起….
在实际的工程中,默认的上面的localClass将会被Dispatch取代.
Dispatch又为何物 ? 事实上,在CDK1.0中,COM开发不用上面这些宏,
在那里你可以清楚的看到问题的实质,没办法,时代变了,就面前的情况来
探讨吧…
上面Dispatch的位置,规定置入实现接口的嵌套类的…
那么毫无疑问Dispatch就是嵌套类啦.可是你说,这是哪里来的 ?
事实上,在CCmdTarget中,有这么一片段:
struct XDispatch
{
DWORD m_vtbl; // place-holder for IDispatch vtable
#ifndef _AFX_NO_NESTED_DERIVATION
size_t m_nOffset;
#endif
} m_xDispatch;
它将成为实现接口的嵌套类.
这怎么可能成为嵌套类呢 . .它什么也没有啊…简简单单的结构而已啊….
事实在内部,存在一样的虚表vtable指派行为,正是这种行为使得m_xDispatch彻底的
变了,这种行为的引爆器就是组件类构造函数中的EnableAutomation();
代码如下:
void CCmdTarget::EnableAutomation()
{
ASSERT(GetDispatchMap() != NULL); // must have DECLARE_DISPATCH_MAP
// construct an COleDispatchImpl instance just to get to the vtable
COleDispatchImpl dispatch;
// vtable pointer should be already set to same or NULL
ASSERT(m_xDispatch.m_vtbl == NULL||
*(DWORD*)&dispatch == m_xDispatch.m_vtbl);
// sizeof(COleDispatchImpl) should be just a DWORD (vtable pointer)
ASSERT(sizeof(m_xDispatch) == sizeof(COleDispatchImpl));
// copy the vtable (and other data) to make sure it is initialized
m_xDispatch.m_vtbl = *(DWORD*)&dispatch;
*(COleDispatchImpl*)&m_xDispatch = dispatch;
}
PART2 深入CCmdTarget看一看COM三大元素的实现
我们之所以深入到CCmdTarget
不是想只是为了那简简单单的实现
我们想知道MFC对COM的一大美景:聚合
是怎么实现的.
二话不说,摆出架势先 :
public:
// data used when CCmdTarget is made OLE aware
long m_dwRef;
LPUNKNOWN m_pOuterUnknown; // external controlling unknown if != NULL
DWORD m_xInnerUnknown; // place-holder for inner controlling unknown
public:
// advanced operations
void EnableAggregation(); // call to enable aggregation
void ExternalDisconnect(); // forcibly disconnect
LPUNKNOWN GetControllingUnknown();
// get controlling IUnknown for aggregate creation
// these versions do not delegate to m_pOuterUnknown
DWORD InternalQueryInterface(const void*, LPVOID* ppvObj);
DWORD InternalAddRef();
DWORD InternalRelease();
// these versions delegate to m_pOuterUnknown
DWORD ExternalQueryInterface(const void*, LPVOID* ppvObj);
DWORD ExternalAddRef();
DWORD ExternalRelease();
// implementation helpers
LPUNKNOWN GetInterface(const void*);
LPUNKNOWN QueryAggregates(const void*);
// advanced overrideables for implementation
virtual BOOL OnCreateAggregates();
virtual LPUNKNOWN GetInterfaceHook(const void*);
从上面的声明中,你可发现:
这里声明了两套标准接口方法,
Externalxx对应于COM模型中的委托IUnknown
而Internalxx对应COM模型中的非委托Iunknown
有了这两套接口方法,聚合的实现就OK了…..
关于聚合的实现细节,欲知详情,请参阅相关专门
的书籍,这里不再赘述.
通过PART1和PART2关于接口的基础构造已经完成.
PART3------类厂的由来
不用说,COM对象的创建是需要类厂的.
---------------------------------------------------------------------------------------------------------
DECLARE_OLECREATE(CSAM)宏剖析
-----------------------------------------------------------------------------
#define DECLARE_OLECREATE(class_name)
public:
static AFX_DATA COleObjectFactory factory;
//定义类厂对象…
static AFX_DATA const GUID guid;
//组件类的GUID
------------------------------------------------------------------------------------------------------------
IMPLEMENT_OLECREATE 宏剖析
------------------------------------------------------------------
#define IMPLEMENT_OLECREATE(class_name, external_name, l, w1, w2, b1,
b2, b3, b4, b5, b6, b7, b8)
AFX_DATADEF COleObjectFactory class_name::factory(class_name::guid,
RUNTIME_CLASS(class_name), FALSE, _T(external_name));
//这里要注意的是external_name:ProgID
AFX_COMDAT const AFX_DATADEF GUID class_name::guid =
{ l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } };
//将组件类的CLSID赋予组件类的成员变量guid.
-----------------------------------------------------------------------------------------------------------
IMPLEMENT_OLECREATE(CSAM, "MFCCOM.SAM",
0x43d242f9, 0x4f7e, 0x4cbb, 0xae, 0xda, 0x77, 0x8d, 0xa1, 0x16, 0xd0, 0xd9)
说明:我们知道,在创建组件类对象时,首先由App核心获得当前状态,
从中取出类厂表,依据CLSID获得相应的类厂对象指针.正是在这里
将类厂和CLSID、ProID等信息关联.
PART4-------自动化
事实上,自动化也是一个极大的主题.自动化技术增强了组件的环境适应性.
对于MFC中的自动化组件,由于默认的接口为dispinterface,所以它对方法的
访问一律采用分发的手段.不同于你意识中,一直牢记的通过vtable[_index]
来访问方法.在使用组件的过程中,首先获得Idispatch接口,然后调用Idispatch
的方法GetIDsOfNames获得programmers希望的方法的令牌(ID),最后通过
Idispatch的方法Invoke来执行.这种技术使得脚本和宏环境可以使用COM
对象,不过对于有预先编译能力的环境来说,会使得组件系统的性能大打
折扣.因为它多了一个反复调用的中间层.
关于Dispatch的支持,基本上构建思路原理同于
上述的Interface..
-----------------------------------------------------------------------------------------------------------------------
DECLARE_DISPATCH_MAP()
BEGIN_DISPATCH_MAP(CSAM, CCmdTarget)[.cpp]
DISP_PROPERTY_NOTIFY(CSAM, "Fook", m_Fook, OnFookChanged, VT_R4)
DISP_FUNCTION(CSAM, "Post", Post, VT_R4, VTS_NONE)
END_DISPATCH_MAP()
宏剖析
--------------------------------------------------------------------
#ifdef _AFXDLL
#define DECLARE_DISPATCH_MAP()
private:
static const AFX_DISPMAP_ENTRY _dispatchEntries[];
static UINT _dispatchEntryCount;
static DWORD _dwStockPropMask;
protected:
static AFX_DATA const AFX_DISPMAP dispatchMap;