ATL接口映射宏详解[1]

来源:互联网 发布:centos 7 卸载软件 编辑:程序博客网 时间:2024/06/06 21:40
序言:

  这几天看了看ATL的接口映射宏,不知不觉看得比较深入了,突然就萌发了把它写出来的想法。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()
    .....
};
  编写一段最简单的查询接口代码:

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< ATL::CComObject< CMyObject > >::CreateInstance(...)
  2.ATL::CComCreator2< ATL::CComCreator< ATL::CComObject< CMyObject > >,
          ATL::CComCreator< ATL::CComAggObject< CMyObject > > >::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 OBJECT_ENTRY(clsid, class) {&clsid, class::UpdateRegistry, /
        class::_ClassFactoryCreatorClass::CreateInstance, ///关键
        class::_CreatorClass::CreateInstance, /
        NULL, 0, class::GetObjectDescription, /
        class::GetCategoryMap, class::ObjectMain },
  #define END_OBJECT_MAP() {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
  生成一个静态全局_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< ccomobjectcached< cf > > _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函数。

4:

ATLINLINE ATLAPI AtlModuleGetClassObject(_ATL_MODULE* pM, REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    _ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;//从_Module中取出对象映射数组

    while (pEntry->pclsid != NULL)
        {
        if ((pEntry->pfnGetClassObject != NULL) && InlineIsEqualGUID(rclsid, *pEntry->pclsid))
        {
            if (pEntry->pCF == NULL)
            {
                hRes = pEntry->pfnGetClassObject(pEntry->pfnCreateInstance,
                            IID_IUnknown, (LPVOID*)&pEntry->pCF);
            }
            if (pEntry->pCF != NULL)
                hRes = pEntry->pCF->QueryInterface(riid, ppv);
            break;
        }
        pEntry = _NextObjectMapEntry(pM, pEntry);
    }
}
现在好象已经有点看不懂了,看来我们得看看_ATL_OBJMAP_ENTRY的结构了

struct _ATL_OBJMAP_ENTRY
{
    const CLSID* pclsid;
    HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister);
    _ATL_CREATORFUNC* pfnGetClassObject;
    _ATL_CREATORFUNC* pfnCreateInstance;
    IUnknown* pCF;
    DWORD dwRegister;
    _ATL_DESCRIPTIONFUNC* pfnGetObjectDescription;
    _ATL_CATMAPFUNC* pfnGetCategoryMap;
}
  pclsid很清楚就代表着我们组件的CLSID;pfnGetClassObject我们也已经知道了它就是CMyObject::_ClassFactoryCreatorClass::CreateInstance(我们组件所包含的类厂对象的CreateInstance函数);pCF我们也可以猜出它是指向这个类厂的IUnknown指针,代表这个类厂对象是否被创建过,若类厂对象已经存在,就不用再创建新的类厂对象了。现在就剩下pfnCreateInstance我们还不明白怎么回事。其实答案还是在 CComCoClass中!
  在CComCoClass中缺省定义了宏DECLARE_AGGREGATABLE(x),这个宏表示这个组件既可以是聚集的也可以是非聚集的,关于聚集的概念我们暂且不理,先看它的定义:

#define DECLARE_AGGREGATABLE(x) public:/
    typedef CComCreator2< ccomcreator< CComObject< x > >, /
        CComCreator< ccomaggobject< x > > > _CreatorClass;

我们看到了一个熟悉的字符串_CreatorClass, 原来这还有一个组件包含的对象。但还有一个问题我们没有搞清楚,就是为什么_ClassFactoryCreator和_CreatorClass后面都要跟着一个CreateInstance? 看来我们必须先来看看CComCreator是个什么东西了。

template < class T1 >
class CComCreator
{
public:
    static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
    {.....
    }
};
  原来它里面只有一个CreateInstance函数,我们现在终于大体明白_ClassFactoryCreatorClass::CreateInstance 表示什么意思了,它就代表CComClassFactory::CreateInstance(..)吧,差不多就是这样了。那我们再来看看CComCreator2有什么不同:

template < class T1, class T2 >
class CComCreator2
{
public:
    static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
    {
        return (pv == NULL) ?
            T1::CreateInstance(NULL, riid, ppv) :
            T2::CreateInstance(pv, riid, ppv);
    }
};
  这个类与CComCreator很类似,都只有一个CreateInstance成员函数,从_CreatorClass 中我们可以知道它实际上包含两个类CComObject,CComAggObject的CreateInstance函数(通过CComCreator),其中CComObject用于非聚集对象,CComAggObject用于聚集对象根据情况它建立相应的对象。(ATL中实际生成的组件对象不是CMyObject,而是 CComObject,CComAggObject或CComPolyObject对象,这个概念很重要,但现在暂且不谈) 现在我们对AtlModuleGetClassObject(...)基本已经知道是怎么回事了,它就是根据存在对象映射数组中的创建类厂的函数的地址来创建类厂。pfnGetClassObject以及 pfnCreateInstance我们基本上都已经知道是怎么回事了,但还有一个问题为什么要把pEntry->pfnCreateInstance作为pEntry->pfnGetClassObject(...)中的一个参数传递?答案在下面呢,让我们继续路由下去!