Preface:
Over the past few days, I have looked at the ATL interface ing macro, and then I suddenly came up with the idea of writing it. ATL defines many interface ing macros. Several of them are more important, although it seems that it is not necessary to clarify all its details, however, in the process of deep learning, you can also learn other ATL classes by taking a look at the mechanism of the class. It should be helpful. I wrote it according to my learning process, and I don't know if you can understand it. I want to imitate Mr. Hou's handwriting and try to explain his internal details clearly, but I am not sure to say that I am going to give it a simple explanation. I just hope it will help you.
In the future, we will introduce the interface ing Macros in the form of com_interface_entry_xx in ATL, and explain them in the order of ease to difficulty. Each part will be based on the previous part. Each part is analyzed by analyzing the actual call function stack. the stack is written from bottom to top. The Code involved in this article is omitted and only the relevant sections are listed.
1. com_interface_entry (X)
First, we start with a typical application:
Define the simplest ATL dll:
Class atl_no_vtable cmyobject: Public ccomobjectrootex, Public ccomcoclass, Public idispatchimpl { ..... Begin_com_map (cmyobject) Com_interface_entry (imyobject) // a double interface Com_interface_entry (idispatch) End_com_map () ..... }; |
Compile the simplest 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 the customer code. First, let's see how the component object is created.
Function call stack 1:
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 (...) (client) |
Explanation:
1:
Cocreateinstance (clsid_myobject, null, clsctx_inproc_server, iid_iunknown, (void **) & punk );
It will call the ole api function cogetclassobject () internally, while the cogetclassobject will load the DLL through loadlibrary (...) and call the dllgetclassobject () function in the DLL.
2:
Stdapi dllgetclassobject (refclsid rclsid, refiid riid, lpvoid * bp) {Return _ module. getclassobject (rclsid, riid, GMM ); } |
It is worth noting that the _ module variable defines the global variable in the DLL:
Ccommodule _ module;
ATL uses a set 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 }}; |
Generate a static global _ atl_objmap_entry array: objectmap [];
Then, ATL
Bool winapi dllmain (hinstance, DWORD dwreason, lpvoid/* lpreserved */ { ..... _ Module. INIT (objectmap, hinstance, & libid_test2lib ); ..... } |
Initialize _ module // note that in some cases, _ module is initialized in initinstance ().
So what does _ module perform during initialization? In fact, nothing is actually done. In ccommodule: init, it calls atlmoduleinit (_ atl_module * pm, _ atl_objmap_entry * P, hinstance H), the key is only one sentence: PM-> m_pobjmap = P; visible _ module only saves the Global Object ing array objectmap. So why can we get the class factory through _ module. getclassobject? In fact, the key lies in another base class ccomcoclass inherited by our component cmyobject! In ccomcoclass, a macro declare_classfactory () is defined by default.
# Define declare_classfactory () declare_classfactory_ex (ccomclassfactory) # Define declare_classfactory_ex (CF) Typedef ccomcreator <ccomobjectcached <CF> _ classfactorycreatorclass; |
Ccomcreator, ccomobjectcached. We don't care about it for the time being, but once we see ccomclassfactory, as the name suggests, we will know that the class factory we want has finally arrived! Each component originally has a class factory object. After a large circle, we now know that _ module contains the class factory objects of each component we want. This is enough for the moment. Continue routing now!
3:
Hresult ccommodule: getclassobject (refclsid rclsid, refiid riid, lpvoid * GMM) { Return atlmodulegetclassobject (this, rclsid, riid, PVS ); } |
Ccommodule: The implementation of getclassobject is very simple. It only calls the API functions of ATL.
4:
Atlinline atlapi atlmodulegetclassobject (_ atl_module * pm, refclsid rclsid, refiid riid, lpvoid * bp) { _ Atl_objmap_entry * pentry = PM-> m_pobjmap; // retrieve the object ing array from _ ModuleWhile (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, GMM ); Break; } Pentry = _ nextobjectmapentry (PM, pentry ); } } |
It seems that I can't understand it now. 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 clearly indicates the CLSID of our component. pfngetclassobject is also known as cmyobject: _ classfactorycreatorclass :: createinstance (the createinstance function of the class factory object included in our component); PCF can also guess whether it is the iunknown pointer to the class factory to indicate whether the class factory object has been created, if the class factory object already exists, you do not need to create a new class factory object. We don't know what's going on with the pfncreateinstance. Actually, the answer is still in ccomcoclass!
In ccomcoclass, the macro declare_aggregatable (X) is defined by default. This macro indicates that this component can be both clustered and non-clustered. We will ignore the concept of clustering for the moment, let's first look at its definition:
# Define declare_aggregatable (x) Public :/
Typedef ccomcreator2 <ccomcreator <ccomobject <x> ,/
Ccomcreator <ccomaggobject <x> _ creatorclass;
We can see a familiar string _ creatorclass, which originally contains an object in the component. But there is another question we have not figured out, that is, why must _ classfactorycreator and _ creatorclass be followed by a createinstance? It seems that we must first look at what ccomcreator is.
Template <class T1> Class ccomcreator { Public: Static hresult winapi createinstance (void * PV, refiid riid, lpvoid * PQ) {..... } }; |
It turns out that there is only one createinstance function in it, and now we have a general understanding of what _ classfactorycreatorclass: createinstance means. It represents ccomclassfactory: createinstance (..) that's almost the case. Let's take a look at the differences between ccomcreator2:
Template <class T1, class T2> Class ccomcreator2 { Public: Static hresult winapi createinstance (void * PV, refiid riid, lpvoid * PQ) { Return (Pv = NULL )? T1: createinstance (null, riid, push ): T2: createinstance (PV, riid, push ); } }; |
This class is very similar to ccomcreator. There is only one createinstance member function. From _ creatorclass, we can know that it actually contains two classes: ccomobject and createinstance function of ccomaggobject (through ccomcreator ), ccomobject is used for non-clustered objects, and ccomaggobject is used for clustering objects to create corresponding objects according to the situation. (The component object actually generated in ATL is not a cmyobject, but a ccomobject, ccomaggobject, or ccompolyobject object. This concept is very important, but we will not talk about it now (...) I already know what is going on. It is used to create a class factory based on the address of the function for creating the class factory in the object ing array. We all know what is going on with pfngetclassobject and pfncreateinstance, but there is another question: why should we use pentry-> pfncreateinstance as a parameter in pentry-> pfngetclassobject? The answer is below. Let's continue routing!