Preface: After recenting the interface mapped by the ATL, I don't know how deep it, I suddenly wrote the idea of writing it. ATL defines a lot of interface mapping macro, there are still more important, although it is not necessary to make it very clear, but in the process of in-depth learning, you can learn other ATL classes. The mechanism for it can also be more clear, it should still be some benefit. I will write it out according to the process of learning, and I don't know if you can understand. I want to imitate the teacher's hand to explain the internal details, but I don't dare to say the beautiful name "in-depth shallow", huh, I only hope that I can help everyone.
Introduce the interface map macro macro in each form of COM_Interface_entry_xx in ATL, and will be explained in the order from an easy to difficult, and each part will be based on the previous part. Every part will analyze the actual call function stack, and the writing of the stack is from bottom to. The code involved in the article is a slight written, only listing the relevant part.
First, com_interface_entry (x)
First we start from a typical application:
Define a simplest ATL DLL:
class ATL_NO_VTABLE CMyObject: public CComObjectRootEx, public CComCoClass, public IDispatchImpl {..... BEGIN_COM_MAP (CMyObject) COM_INTERFACE_ENTRY (IMyObject) // one pair of interfaces COM_INTERFACE_ENTRY (IDispatch) END_COM_MAP () .....};
Write a shortest query interface code:
IUnknown * pUnk; IMyObject * pMyObject; CoCreateInstance (CLSID_MyObject, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **) & pUnk); pUnk-> QueryInterface (IID_IMyObject, (void **) & pMyObject);
Execute a customer code, first we look at how the component object is created.
Function call stack one:
4 ......... 3.atl :: ccomcreator
2:
Stdapi DllgetClassObject (Refclsid Rclsid, Refiid Riid, LPVOID * PPV) {Return_Module.getClassObject (rclsid, riid, ppv);
Among them, it is worth noting that _Module variables define global variables in DLL:
CCOMMODULE _MODULE;
ATL passes a group of macros:
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 /// key class :: _ CreatorClass :: CreateInstance, / NULL, 0, class :: GetObjectDescription, / class :: GetCategoryMap, class :: ObjectMain}, #define END_OBJECT_MAP () {NULL, NULL, NULL, NULL, NULL NULL, NULL, NULL}}
Generate a static global _tl_objmap_entry type: ObjectMap []; then ATL is
Bool WinApi Dllmain (Hinstance Hinstance, DWORD DWREASON, LPVOID / * LPRESERVED * / {..... _Module.init (Objectmap, hinstance, & libid_test2lib); .....}
Initialization _Module // Note Inly, in InitInstance (), _Module
So _Module initialization, in fact, he didn't do anything, in ccommodule :: INIT, it calls atlmoduleinit (_TL_Module * PM, _TL_OBJMAP_ENTRY * P, HINSTANCE H), in which only one sentence: PM- > m_pobjmap = p; Visible _Module is only given this global object mapping array ObjectMap []. So why can I get the class with _Module.getClassObject? In fact, the key is that our component CMYObject inherits another base class ccomcoclass! Defines a macro declare_classfactory () in ccomcoClass.
#define declare_classfactory () declare_classfactory_ex (ccomclassfactory) #define declare_classfactory_ex (cf) typef ccomcreator
CCOMCREATOR, CCOMOBJECTCACHED We don't care, but see CCOMCLASSFAACTORY, as the name suggests, we know that our products have finally appeared! There is a class factory object inside each component. Wrought around a large circle, we have now known that _Module contains the objects of each component we want, which is already enough for the current, and now continue the route!
3:
HRESULT CCOMMODULE :: GetClassObject (Refclsid Rclsid, Refiid Riid, LPVOID * PPV) {Return AtlModulegetClassObject (this, rclsid, riid, ppv);}
CCommodule :: getClassObject is very simple, just call ATL's API functions.
4:
ATLINLINE ATLAPI AtlModuleGetClassObject (_ATL_MODULE * pM, REFCLSID rclsid, REFIID riid, LPVOID * ppv) {_ATL_OBJMAP_ENTRY * pEntry = pM-> m_pObjMap; // remove objects from an array of mappings while the _Module {if (pEntry-> pclsid = NULL!) ((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);}} Now I can't understand it, it seems that we have to look at the structure of _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 is clearly representative of the CLSID of our components; PfNgetClassObject We also know it is CMYObject :: _ ClassFactoryCreatorClass :: CreateInstance (the CreateInstance function of the class object included in our component); PCF can also guess it is pointing this The IUNKNOWN pointer of the class is created by this class factory object. If the factory object already exists, it will not have to create a new type of factory object. We still don't understand what is going on now. In fact, the answer is still in ccomcoClass!
In CCOMCLASS, the macro declare_AGGREGATABLE (X) is defined. This macro indicates that this component can be both gathered or not gathering, and the concept of gathering We are temporarily ignored, first look at its definition:
#define declare_AGGREGATABLE (X) Public: /
Typedef ccomcreator2
CCMCREATOR
Template
It turns out that there is only one createInstance function in it, and we now finally understand _classfactorycreatorclass :: createInstance, it means that it represents ccomclassfactory :: CreateInstance (..), almost like this. Then let's take a look at the difference between ccomcreator2:
template
This class is very similar to CCMCREATOR, only a CREATEINSTANCE member function, from _CREATORCLASS we can know that it actually contains two class ccomobject, ccomaggobject's CreateInstance function (via ccomcreator), where ccomobject is used for non-aggregated objects, ccomaggobject is used for ccomagGObject The aggregation object is based on the situation it establishes the corresponding object. (The actually generated component objects in ATL is not CMYObject, but ccomobject, ccomaggobject, or ccompolyobile, this concept is very important, but now, don't talk now) Now we basically know what is going on, it It is created based on the address of the creation of the creation of the creation plant in the presence object mapping. PfNgetClassObject and PfncreateInstance We basically have already known what is going on, but there is a question why Pentry-> PFNCREATEINSTANCE is passed as a parameter in Pentry-> PfNgetClassObject (...)? The answer is below, let us continue to route!