ATL中定义了很多接口映射宏,有几个还是比较重要的,虽然好象没有必要把它所有的细节都弄得很清楚,但深入学习的过程中也可以顺带学一学其他的ATL类,对它的机制也可以更清楚一些,应该还是会有些好处的吧。
以后将分别介绍ATL中各个形式为COM_INTERFACE_ENTRY_XX的接口映射宏。并将按照从易到难的顺序讲解,每一部分都将建立在前一部分的基础上。每一部分都将通过分析实际的调用函数堆栈来进行分析,堆栈的写法是从下向上。文中所涉及的代码都为略写,只列出相关部分。 一、COM_INTERFACE_ENTRY(x)
首先我们从一个最典型的应用开始:
定义一个最简单的ATL DLL:
class ATL_NO_VTABLE CMyObject : public CComObjectRootEx, public CComCoClass, public IDispatchImpl { ..... BEGIN_COM_MAP(CMyObject) COM_INTERFACE_ENTRY(IMyObject) //一个双接口 COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() END_COM_MAP() ..... }; |
编写一段最简单的查询接口代码:
IUnknown *pUnk; IMyObject *pMyObject; CoCreateInstance(CLSID_MyObject, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&pUnk); pUnk->QueryInterface(IID_IMyObject, (void **)&pMyObject); |
执行客户代码,首先我们看看组件对象是如何被创建的。 函数调用堆栈一:
4........... 3.ATL::CComCreator >::CreateInstance(...) 2.ATL::CComCreator2 >, ATL::CComCreator > >::CreateInstance(...) 1.ATL::CComClassFactory::CreateInstance(...) 4.ATL::AtlModuleGetClassObject(...) 9.ATL::AtlInternalQueryInterface(...) 8.ATL::CComObjectRootBase::InternalQueryInterface(...) 7.ATL::CComClassFactory::_InternalQueryInterface(...) 6.ATL::CComObjectCached::QueryInterface(...) 5.ATL::CComCreator >:: CreateInstance(...) 4.ATL::AtlModuleGetClassObject(...) 3.ATL::CComModule::GetClassObject(...) 2.DllGetClassObject(...) 1.CoCreateInstance(...)(客户端) |
解释如下:
1:CoCreateInstance(CLSID_MyObject, NULL, CLSCTX_INPROC_SERVER,IID_IUnknown, (void **)&pUnk);
其内部将调用OLE API函数CoGetClassObject(), 而CoGetClassObject则会通过LoadLibrary(...)装入DLL,并调用DLL中的DllGetClassObject()函数。
2:STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}
其中值得注意的是_Module变量,在DLL中定义了全局变量:
CComModule _Module;
ATL通过一组宏:
BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_MyObject, CMyObject) END_OBJECT_MAP() #define BEGIN_OBJECT_MAP(x) static _ATL_OBJMAP_ENTRY x[] = { #define END_OBJECT_MAP() {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}}; #define OBJECT_ENTRY(clsid, class) {&clsid, class::UpdateRegistry, class::_ClassFactoryCreatorClass::CreateInstance, //关键 class::_CreatorClass::CreateInstance, NULL, 0, class::GetObjectDescription, class::GetCategoryMap, class::ObjectMain }, |
生成一个静态全局_ATL_OBJMAP_ENTRY型数组:ObjectMap[]; 然后ATL又在
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/ { ..... _Module.Init(ObjectMap, hInstance, &LIBID_TEST2Lib); ..... } |
初始化_Module
注意在有的情况下是在InitInstance()中初始化_Module,那么_Module初始化都做了些什么呢,其实他什么也没做,在CComModule::Init中,它调用AtlModuleInit(_ATL_MODULE* pM, _ATL_OBJMAP_ENTRY* p, HINSTANCE h),在其中关键的只有一句:pM->m_pObjMap = p;可见_Module仅仅是把这个全局对象映射数组
ObjectMap[]给存了起来。那么为什么可以通过_Module.GetClassObject得到类厂呢?
其实关键在于我们的组件CMyObject继承的又一个基类CComCoClass! 在CComCoClass中缺省定义了一个宏DECLARE_CLASSFACTORY()而 #define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(CComClassFactory)
#define DECLARE_CLASSFACTORY_EX(cf)
typedef CComCreator > _ClassFactoryCreatorClass;
CComCreator,CComObjectCached我们暂且不管,但一看到CComClassFactory,顾名思义,我们就知道我们要的类厂终于出现了!每个组件内部原来都有一个类厂对象。绕了一大圈,我们现在已经知道了_Module中包含了我们所要的每个组件的类厂对象,这对目前来说已经足够了,现在继续路由下去!
3:HRESULT CComModule::GetClassObject(REFCLSID rclsid,REFIID riid,LPVOID* ppv)
{
return AtlModuleGetClassObject(this, rclsid, riid, ppv);
}
CComModule::GetClassObject的实现非常简单,仅仅是调用ATL的API函数。