MFC对COM应用的支持(1)
来源:互联网 发布:navicat导出sql语句 编辑:程序博客网 时间:2024/06/04 18:42
实现COM接口的关键是引用计数和QueryInterface函数的实现,MFC的引用计数实现方法很简单,在CCmdTarget类中使用m_dwRef数据成员作为计数器,然后按照COM规范维护计数器的增1减1操作,所有的接口都共享同一个引用计数器,QueryInterface函数的实现取决于COM接口的实现机制——多重继承或是嵌套类。接口映射表记录了CCmdTarget类中每一个嵌套类的接口ID以及几口vtable与父类this指针之间的的偏移量,使嵌套类可以计算出父类的this指针,从而访问父类成员。此偏移量实际上是个常数,用offset宏可以给出成员与父类之间的偏移值,编译器在编译时刻计算此值。
为了透彻了解接口映射表技术,我们还是从C++嵌套类开始探索。
首先我们按嵌套类的方式写出字典对象类CDictionary的定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class CDictionary
{
public:
CDictionary();
~CDictionary();
public:
HRESULT _stdcall QueryInterface(REFIID iid, void** ppvobj);
ULONG _stdcall AddRef();
ULONG _stdcall Release();
class XDictionaryObj : public Idictionary
{
public:
CDictionary* m_pParent;
virtual HRESULT _stdcall QueryInterface(REFIID iid, void** ppvObj);
virtual ULONG _stdcall AddRef();
virtual ULONG _stdcall Release();
virtual BOOL _stdcall Initialize();
virtual BOOL _stdcall LoadLibrary(String);
virtual BOOL _stdcall InsertWord(String, String);
virtual BOOL _stdcall DeleteWord(String, String);
virtual BOOL _stdcall LookupWord(String, *String);
virtual BOOL _stdcall RestoreLibrary(String);
virtual void _stdcall FreeLibrary();
} m_dictionaryObj;
class XSpellCheckObj : public IspellCheck
{
public:
CDictionary* m_pParent;
virtual HRESULT _stdcall QueryInterface(REFIID iid, void** ppvobj);
virtual ULONG _stdcall AddRef();
virtual ULONG _stdcall Release();
virtual ULONG _stdcall CheckWord(String word, String*);
}m_spellCheckObj;
private:
struct Dictword* m_pData;
char* m_DictFilename[128];
int m_Ref;
int m_nWordNumber, m_nStructNumber;
};
从CDictionary的定义可以看出,它包含两个嵌套类XDictionaryObj和XspellCheckObj,并且它定义了两个嵌套类数据成员m_dictionaryObj和m_spellCheckObj,两个嵌套类内部都包含一个指向父类的数据成员,下面的代码片段反映了父类和嵌套类中IUknown成员函数的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
CDictionary::CDictionary()
{
// ...... Initializtion
m_dictionaryObj.m_pParent = this;
m_dictionaryObj.m_pParent = this;
}
CDictionary::~CDictionary()
{
// ......
}
HRESULT CDictionary::QueryInterface(const IID& iid, void** ppvobj)
{
if(iid == IID_IUnknown || iid == IID_Dictionary) {
*ppvobj = &m_dictionaryObj;
AddRef();
return S_OK;
}
else if(iid == IID_SpellCheck) {
*ppvobj = &m_spellCheckObj;
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
ULONG CDictionary::AddRef()
{
++m_Ref;
return (ULONG) m_Ref;
}
ULONG CDictionary::Release()
{
if(--m_Ref == 0){
delete this;
return 0;
}
return (ULONG) m_Ref;
}
/***********************************************************************************************/
ULONG CDictionary::XDictionaryObj::QueryInterface(const IID& iid, void** ppvObj)
{
return m_pParent->QueryInterface(iid, ppvObj);
}
ULONG CDictionary::XDictionaryObj::AddRef()
{
return m_pParent->AddRef();
}
ULONG CDictionary::XDictionaryObj::Release()
{
return m_pParent->Release();
}
// the implemention of member function in CDictionary::XSpellCheckObj
// behaves the same way as CDictionary::XDictionaryObj
注意,在CDictionary类的实现过程中,IUnknown成员函数的实现被放在了父类的CDictionary中,两个嵌套类中的对象成员函数只是简单调用父类的成员函数,这样既可以减少代码的数量,还可以减少出错的可能,保持对象处理引用计数的一致性。这样处理的两一个好处是QueryInterface成员函数处理的一致性,不过从哪个接口,它总是能得到对象所支持的任一个COM接口,而且是唯一的,这完全符合COM规范的要求。
现在我们回头看看CCmdTarget类中给出的接口映射表的宏定义。在CCmdTarget类的定义中,使用了DECLARE_INTERFACE_MAP()宏,其定义如下:
1
2
3
4
5
6
7
#define DECLARE_INTERFACE_MAP() /
private: /
static const AFX_INTERFACEMAP_ENTRY _interfaceEntries[]; /
protected: /
static AFX_DATA const AFX_INTERFACEMAP interfaceMap; /
static const AFX_INTERFACEMAP* PASCAL _GetBaseInterfaceMap(); /
virtual const AFX_INTERFACEMAP* GetInterfaceMap() const; /
代码很容易看懂。CCmdTarget定义了静态成员_interfaceEntries和静态的interfaceMap成员,以及静态成员函数_GetBaseInterfaceMap()和虚函数GetInterfaceMap。其中AFX_INTERFACEMAP_ENTRY和AFX_INTERFACEMAP的数据结构的定义为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct AFX_INTERFACEMAP_ENTRY
{
const void* piid; // the interface id(IID) (NULL for aggregate)
size_t nOffest; // offset of the interface vtable from m_Unknown
};
struct AFX_INTERFACEMAP
{
#ifdef _AFXDLL
const AFX_INTERFACE* (PASCAL *pfnGetBaseMap) ();
#else
const AFX_INTERFACE* pBaseMap;
#endif
const AFX_INTERFACE_ENTRY* pEntry; // map for this class
};
通过以上这些定义,我们可以看出,CCmdTarget利用DECLARE_INTERFACE_MAP()宏定义了一张接口表:
u 表中第一项指向其基类的映射表(CCmdTarget类中,此成员为NULL);
u 第二项指向接口表的入口。
接口表中每一项都包括了IID和接口vtable与父类this指针之间的偏移,这样的结构既允许一个对象实现多个接口,接口数目没有限制,而且用户编写自己的COM对象类时,可以从CCmdTarget派生,甚至形成多层派生结构,被派生的类可以继承其父类实现的接口,通过AFX_INTERFACEMAP结构的第一项获取其基类的接口表,从而形成接口链。
CCmdTarget本身只提供了COM接口支持的结构,并没有实现特定的接口。继续前面的CDictionary例子,我们看看利用接口映射表如何实现:
1
2
3
4
BEGIN_INTERFACE_MAP(CDictionary, CCmdTarget)
INTERFACE_PART(CDictionary, IID_IDictionary, Dictionary)
INTERFACE_PART(CDictionary, IID_ISpellCheck, SpellCheck)
END_INTERFACE_MAP()
其中,各个宏的具体定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define BEGIN_INTERFACE_MAP(theClass, theBase) /
/* 以下给出了静态函数_GetBaseInterfaceMap()和GetInterfaceMap()的实现过程*/
const AFX_INTERFACEMAP* PASCAL theClass::_GetBaseInterfaceMap() { /
return &theBase::interfaceMap; } /
const AFX_INTERFACEMAP* theClass::GetInterfaceMap() const { /
return &theClass::interfaceMap; } /
/*定义静态成员interfaceMap和_interfaceEntries*/
AFX_COMDAT const AFX_DATADEF AFX_INTERFACEMAP theClass::_interfaceMap = { /
&theClass::_GetBaseInterfaceMap, &theClass::_interfaceEntries[0] }; /
AFX_COMDAT const AFX_DATADEF AFX_INTERFACEMAP_ENTRY theClass::interfaceEntries[] = /
{ /
#define INTERFACE_PART(theClass, iid, localClass) { /
&iid, offset(theClass, m_x##localClass) }, /
#define END_INTERFACE_MAP() { /
NULL, (size_t) -1} /
}; /
不得不说的一句话是,MFC通过这样一系列设计巧妙的宏使得编码工作得以简化,但无疑也提高了上手的难度。
定义了接口映射表之后,我们再看一下嵌套类的定义和实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class CDictionary : public CCmdTarget
{
DECLARE_DYNCREAT(CDictionary)
CDictionary();
DECLARE_INTERFACE_MAP()
......
//Interface Maps
public:
//IDictionary
BEGIN_INTERFACE_PART(Dictionary,IDictionary)
INIT_INTERFACE_PART(CDictionary, Dictionary)
STDMETHOD_(BOOL, Initialize) ();
STDMETHOD_(BOOL, LoadLibrary)(LPOLESTR);
STDMETHOD_(BOOL, InsertWord)(LPOLESTR);
STDMETHOD_(void, DeleteWord)(LPOLESTR);
STDMETHOD_(BOOL, LookupWord)(LPOLESTR, LPOLESTR*);
STDMETHOD_(BOOL, RestoreLibrary)LPOLESTR);
STDMETHOD_(void, FreeLibrary)();
END_INTERFACE_PART_STATIC(Dictionary)
//ISpellCheck
BEGIN_INTERFACE_PART(SpellCheck, ISpellCheck)
INIT_INTERFACE_PART(CDictionary, SpellCheck)
STDMETHOD_(BOOL, CheckWord)(LPOLESTR, LPOLESTR*);
END_INTERFACE_PART_STATIC(SpellCheck)
};
宏BEGIN_INTERFACE_PART、INIT_INTERFACE_PART和END_INTERFACE_PART封装了嵌套类的定义,嵌套类的类名为Xdictionary,嵌套类的实例成员名为m_xDictionary。
² 宏BEGIN_INTERFACE_PART定义了接口的前三个成员函数:QueryInterface、AddRef、Release,所以我们在上面的代码中看不到这三个函数的定义;
² 宏INIT_INTERFACE_PART定义了记录偏移量的数据成员m_nOffset,并在嵌套类的构造函数中对m_nOffset进行了初始赋值。
下面是这三个宏的定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define BEGIN_INTERFACE_MAP(theClass, theBase) /
const AFX_INTERFACEMAP* PASCAL theClass::GetThisInterfaceMap() /
{ return &theClass::interfaceMap; } /
const AFX_INTERFACEMAP* theClass::GetInterfaceMap() const /
{ return &theClass::interfaceMap; } /
AFX_COMDAT const AFX_INTERFACEMAP theClass::interfaceMap = /
{ &theBase::GetThisInterfaceMap, &theClass::_interfaceEntries[0], }; /
AFX_COMDAT const AFX_INTERFACEMAP_ENTRY theClass::_interfaceEntries[] = /
{ /
#define INIT_INTERFACE_PART(theClass, localClass) /
size_t m_nOffset; /
INIT_INTERFACE_PART_DERIVE(theClass, localClass) /
#define INIT_INTERFACE_PART_DERIVE(theClass, localClass) /
X##localClass() /
{ m_nOffset = offsetof(theClass, m_x##localClass); } /
#define END_INTERFACE_PART(localClass) /
} m_x##localClass; /
friend class X##localClass; /
至此,我们看到了接口映射表有关宏和实现的支持,最后我们总结一下接口映射表实现的步骤:
1、在CCmdTarget类及其派生类定义中使用宏DECLARE_INTERFACE_MAP()声明接口映射表使用的一些静态成员以及两个成员函数。
2、在类的实现部分使用BEGIN_INTERFACE_MAP,BEGIN_INTERFACE_PART、INIT_INTERFACE_PART、END_INTERFACE_PART,和END_INTERFACE_MAP宏定义接口映射表。
3、为每一个接口定义嵌套类成员。
4、实现嵌套类。
- MFC对COM应用的支持(1)
- MFC中对COM的支持
- duilib对MFC的支持
- MFC对多线程的支持
- duilib对MFC的支持
- MFC:启用对ActiveX控件的支持
- ce6.0对mfc的支持
- MFC对多线程编程的支持
- MFC对多线程编程的支持
- MFC对多线程编程的支持
- MFC对多线程编程的支持
- WinCE6添加对MFC的支持
- MFC对多线程编程的支持
- atl server com 支持mfc
- 如何利用MFC的嵌套类编写进程外com!(对潘爱民先生的com原理及应用的补充)
- MS C、STL、MFC对Windows Mobile开发的支持
- Rose对MFC的往返工程支持介绍
- MS C、STL、MFC对Windows Mobile开发的支持
- Flash 与性能实验笔记:几种常用数据类型创建时间
- Flex 验证 Validator
- flex的数据验证!
- 自定义flex的数据验证组件!
- Matlab绘图附加参数意义
- MFC对COM应用的支持(1)
- 七个受用一生的心理寓言
- Android数据存储之SharedPreferences
- 数据存储之 SharedPreferences
- 关于读书。
- 开通了博客
- 关于 ixwebhosting.com主机 503错误 解决方法
- 收集一些解决 Warning: Cannot modifi header inform - header alreadi sent by 问题的方法
- WordPress 首页空白