Detailed description of ATL interface ing macros [2]

Source: Internet
Author: User
5: ccomcreator: createinstance (void * PV, refiid riid, lpvoid * bp)
{
T1 * P = NULL;
Atltry (P = new T1 (PV) // create a class factory object
If (P! = NULL)
{
P-> setvoid (PV );
P-> internalfinalconstructaddref ();
Hres = p-> finalconstruct ();
P-> internalfinalconstructrelease ();
If (hres = s_ OK)
Hres = p-> QueryInterface (riid, GMM );
If (hres! = S_ OK)
Delete P;
}
}
}
Note that here T1 is ccomobjectcached, and this is for ccomcreator
Template parameters. We once again see the familiar operator 'new '! Until now, we have finally created a group.
Component factory. But before the end, let's continue and see what setvoid (PV) has done?
Void ccomclassfactory: setvoid (void * PV)
{
M_pfncreateinstance = (_ atl_creatorfunc *) PV;
}
We still remember that we used cmyobject: _ creatorclass: createinstance as a parameter to pass it
Pentry-> pfngetclassobject (...
Long! It was originally required by the class factory to create component objects! Although we just guessed this from the literal meaning
As we expected, in ccomclassfactory: createinstance (...), we
M_pfncreateinstance (punkouter, riid, ppvobj); now everything is clear,
The layer-by-layer packaging of ATL for creating class factories has been opened, and the rest of the process for creating components is already
We are very familiar with the process!
However, we still need to query an iunknown pointer for the class factory object.
We can see in the previous pentry-> PCF.

6: stdmethod (QueryInterface) (refiid IID, void ** ppvobject)
{Return _ internalqueryinterface (IID, ppvobject );}
Currently, ccomobjectcached: QueryInterface is called. What is special about this class?
I don't seem to need to know yet. I am also tired of saying, haha.

7: hresult _ internalqueryinterface (refiid IID, void ** ppvobject )/
{Return internalqueryinterface (this, _ getentries (), IID, ppvobject );}
The _ internalqueryinterface (...) of all classes are defined in begin_com_map.
Ccomobjectcached does not have a in_com_map macro. Therefore, ccomclassfactory is called.
Note that this pointer and interface ing array _ getentries () are passed to internalqueryinterface (),
This is the basis for internalqueryinterface (...) to implement queries.
Begin_com_map (x) defines an array mapped to the next static interface:
_ Atl_intmap_entry _ entries [];
Every interface ing macro is actually added to this array. An interface ing macro consists of three parts
Points: The IID number, offset value (most of the time) of the interface, and the function to be executed, which is not required for general interfaces.
Execute other functions. _ Getentries () is the returned array. I will discuss some details later.

8: static hresult winapi internalqueryinterface (void * pthis,
Const _ atl_intmap_entry * pentries, refiid IID, void ** ppvobject)
{
...
Hresult hres = atlinternalqueryinterface (pthis, pentries, IID, ppvobject );
...
}
Currently, ccomobjectrootbase: internalqueryinterface (...) is called (...)

9: now we have finally reached the beginning of QueryInterface. Atlinternalqueryinterface (...) is an integer
The end of the query process. it traverses the interface ing table and performs corresponding actions based on each item. Elimination in ATL
There are many kinds of information ing macros, and there are also a lot of corresponding actions, but now we don't care about that, what we need to do now is
It is easy to find an iunknown interface. We do not even need to traverse the interface ing table.
Atlinline atlapi atlinternalqueryinterface (void * pthis,
Const _ atl_intmap_entry * pentries, refiid IID, void ** ppvobject)
{
Atlassert (pentries-> pfunc = _ atl_simplemapentry );
If (ppvobject = NULL)
Return e_pointer;
* Ppvobject = NULL;
If (inlineisequalunknown (IID) // use first Interface
{
Iunknown * punk = (iunknown *) (INT) pthis + pentries-> DW );
Punk-> addref ();
* Ppvobject = punk;
Return s_ OK;
}
... // There are a lot more, but I can't use it now, just save some space.
}
The first interface of the interface ing table must be of the _ atl_simpleentry type. Why
Do you have this requirement, and what does pthis + pentries-> DW mean? Let's talk about it later. It's also a bunch
Problem. In short, we now get the kind factory objects we need and the iunknown
Needle.

4: I almost thought we could win the first step, but in ATL: atlmodulegetclassobject
But it stopped again and looked at its source code. It would have to be queried through the iunknown pointer we just obtained.
Iclassfactory pointer. It is also an identical call. Steps from step 1 to step 2 are exactly the same.
Same call. But note that in step 1, we will not check the iunknown pointer this time, so we need
Let's take a look at the code that I haven't listed just now, but leave it to the worker function stack.
1: finally, we have completed all the operations for creating class factory objects. Now what we need to do is what we are familiar.
Call the createinstance (...) function of the class factory object to create the component. As we have seen
After Ole starts to call ccomclassfactory: createinstance (), we have not forgotten that in the class factory object
The createinstance () function used to create the component is retained, and the process is clear.
2. You don't have to repeat it. Check step 2.
3. You don't have to repeat it. Check step 1.
4. If we continue routing, we can still have a long stack, but this is just a repetitive task. I just
Don't go on, I am also tired to say, alas.

Function call stack 2:
0 :............
5. ATL: atlinternalqueryinterface (...)
4. ATL: ccomobjectrootbase: internalqueryinterface (...)
3. cmyobject: _ internalqueryinterface (...)
2. ATL: ccomobject: QueryInterface (...)
1. Punk-> QueryInterface (iid_imyobject, (void **) & pmyobject); (client)

Explanation:
1. We can query the imyobject pointer through the iunknown interface pointer of the component object we just obtained.
Actually needed pointer.
2. Remember that the components actually created by ATL are not cmyobject, but ccomobject and ccomaggobject.
Or ccompolyobject. Here we create ccomobject.
Ccomobject: QueryInterface (...), and indeed ccomobject implements this function.
Stdmethod (QueryInterface) (refiid IID, void ** ppvobject)
{Return _ internalqueryinterface (IID, ppvobject );}
It just calls _ internalqueryinterface (...). We also said that only
Begin_com_map Macro will have _ internalqueryinterface (...), so now the execution is transferred to its parent
Cmyobject class, so cmyobject: _ interfacequeryinterface (...) will be called (...)
3. We are already familiar with future calls. Can I use it again?
4. We are also familiar with this call. Don't talk about it.
5. Now we are going to query a non-iunknown interface, so let's take a look at the code we didn't list before.

Atlinline atlapi atlinternalqueryinterface (void * pthis,
Const _ atl_intmap_entry * pentries, refiid IID, void ** ppvobject)
{
// Ensure that the first item of interface ing is a simple interface
// If the iunknown interface is queried, perform the corresponding operation.
// The following will traverse the interface ing table and try to find the corresponding interface
While (pentries-> pfunc! = NULL)
{
Bool bblind = (pentries-> piid = NULL );
If (bblind | inlineisequalguid (* (pentries-> piid), IID ))
{
// _ Atl_simplemapentry indicates a simple interface.
If (pentries-> pfunc = _ atl_simplemapentry) // offset
{
Atlassert (! Bblind );
Iunknown * punk = (iunknown *) (INT) pthis + pentries-> DW );
Punk-> addref ();
* Ppvobject = punk;
Return s_ OK;
}
Else // if it is not a simple interface, you need to execute the corresponding function
{
Hresult hres = pentries-> pfunc (pthis, IID, ppvobject, pentries-> DW );
If (hres = s_ OK | (! Bblind & failed (hres )))
Return hres;
}
}
Pentries ++;
}
Return e_nointerface;
}
}
The logic of the function is very clear, and only two points may not be quite understandable. One is
(Iunknown *) (INT) pthis + pentries-> DW)
What to do. The previous question is described in com_interface_entry2.
Different types of interfaces will be explained in the future. It's always a bite to eat, huh, huh.
Now we only need to know how our imyobject is searched. Let's take a look at its macros.
After the com_interface_entry (imyobject) is unbound, the format is:
{& _ Atl_iidof (imyobject), // obtain the IID value of imyobject
Offsetofclass (imyobject, cmyobject), // defines the offset
_ Atl_simplemapentry}, // indicates a simple interface
Similarly, for offsetofclass (imyobject, cmyobject), we will leave it for the next lecture.
Based on this structure, we can easily obtain the imyobject interface pointer.
0: OK, it is over. Stack return in sequence.
In fact, the query process also occurred in the call sequence just now. When querying the iclassfactory interface
There is a similar process, but I still mentioned it separately to see the typical situation.

2. Example of the com_interface_entry2 (x, X2) parameter ATL: commap

ATL implements components in the form of multi-inheritance, but if multiple branches in the inheritance tree implement the same
When querying this interface, you need to know which branch to return to it. This macro is responsible for this job.
This macro is usually used for the idispatch interface. Let's take a look at its typical usage:
Class couter:
Public idispatchimpl & libid_commaplib)>,
Public idispatchimpl & libid_commaplib)>,
Public...
{
Public:
Couter (){}
...
Begin_com_map (couter)
Com_interface_entry2 (idispatch, iouter2), // exposes the routes inherited by iouter2,
Com_interface_entry (iouter1)
Com_interface_entry (iouter2)
...
End_com_map
};
Idispatchimpl <...> This class implements the idispatch interface, so now the component has two idispatches.
. Which implementation is returned when I query the idispatch interface?
Let's take a look at the definition of com_interface_entry2 (x, X2 ).
# Define begin_com_map (x) Public :/
Typedef X _ commapclass ;/
....................
# Define com_interface_entry2 (x, X2 )/
{& _ Atl_iidof (x), // obtain the IID value of the interface
(DWORD) (x *) (X2 *) (_ commapclass *) 8)-8 ,/
_ Atl_simplemapentry}, // indicates a simple interface
The problem is that (DWORD) (x *) (X2 *) (_ commapclass *) 8)-8 what does it mean?

Let's first take a look at the following code of pipeline:
Class A1
{
Public:
Virtual void test (){}
};

Class A2: Public A1
{
Public:
Virtual void test (){}
};

Class A3: Public A1
{
Public:
Virtual void test (){}
};

Class A: Public A2, public A3
{
};

{
DWORD dw;
DW = (DWORD) (A *) 8); // DW = 0x08
DW = (DWORD) (A3 *) (A *) 8); // DW = 0x0c
DW = (DWORD) (A1 *) (A3 *) (A *) 8); // DW = 0x0c
DW = (DWORD) (A1 *) (A3 *) (A *) 8)-8; // DW = 4
}
This inheritance graph is a typical diamond structure. In Class A, there are two virtual function table pointers, representing the two
Branches. When an object is declared and instantiated for Class A, the system allocates memory for it. At the top of the memory
Keep its two virtual function table pointers. The analysis results show that the final result 4 represents
The offset between the virtual function table pointer of interface A3 and the memory block top of Class A object.

Next we will look at a more complex inheritance relationship:
Class B1
{
Public:
Virtual void test (){}
};

Class B2
{
Public:
Virtual void test (){}
};

Class B3
{
Public:
Public:
Virtual void test (){}
};

Class B4: Public B1, public B2
{
Public:
Virtual void test (){}
};

Class B5: Public B2, public B3
{
Public:
Virtual void test (){}
};

Class B: Public B4, public B5
{
};

{
DWORD dw;
DW = (DWORD) (B *) 8); // DW = 0x08
DW = (DWORD) (B5 *) (B *) 8); // DW = 0x10
DW = (DWORD) (B2 *) (B5 *) (B *) 8); // DW = 0x10
DW = (DWORD) (B2 *) (B5 *) (B *) 8)-8; // DW = 8
}
Class B retains four virtual function table pointers because it has four branches. Our goal is to obtain B: B5: B2
The B2 interface in each branch. The final result 8 is exactly what we need. It indicates the offset of the Class B memory block.
From the above two examples, we have understood the functions of (DWORD) (x *) (X2 *) (_ commapclass *) 8)-8.
With this value, we can obtain the interface we need.
Next we will analyze the actual situation com_interface_entry2 (idispatch, iouter2 ).
The idispatchimpl template class is derived from class T, so the couter must be from two of its template classes.
Inheritance: iouter1 and iouter2 are both double interfaces, that is, they are all classes derived from idispatch, so you can get the couter
There are two branches, which are also a diamond structure. Therefore, according to our example, the offset value should also be 4. To prove
Let's use the function stack to verify our results.

Function Stack:
5. ATL: atlinternalqueryinterface (...)
4. ATL: ccomobjectrootbase: internalqueryinterface (...)
3. cmyobject: _ internalqueryinterface (...)
2. ATL: ccomobject: QueryInterface (...)
1. Punk-> QueryInterface (iid_idispatch, (void **) & pdispatch)

Explanation:
Explanation:
1: This is our verification code. Punk is the iunknown pointer of the component.
2-5: we are all familiar with these codes. We only need to look at atlinternalqueryinterface.
.
Atlinline atlapi atlinternalqueryinterface (void * pthis,
Const _ atl_intmap_entry * pentries, refiid IID, void ** ppvobject)
{
...........
While (pentries-> pfunc! = NULL)
{
Bool bblind = (pentries-> piid = NULL );
If (bblind | inlineisequalguid (* (pentries-> piid), IID ))
{
If (pentries-> pfunc = _ atl_simplemapentry) // offset
{
Atlassert (! Bblind );
Iunknown * punk = (iunknown *) (INT) pthis + pentries-> DW );
Punk-> addref ();
* Ppvobject = punk;
Return s_ OK;
}
... // If it is not a simple interface...
}
}
Pentries ++;
}
Return e_nointerface;
}
The key phrase is iunknown * punk = (iunknown *) (INT) pthis + pentries-> DW );
By observing the variables, as we expected, pentries-> DW = 4. (INT) pthis + pentries-> DW) ensures that we
We can obtain the virtual function table of iouter2 branch, because idispatch is also inherited from iunknown, In the virtual function table
Is the iunknown virtual function pointer at the top, so (iunknown *) forced conversion, you can get this
The top address of the virtual function table, which is exactly what we need. I may ask why I got the location of the virtual function table.
Instead of the address of a class instance? Don't forget that the interface does not have data. It only has pure virtual functions. For
For the customer, it can only access it through virtual functions defined by the interface, but cannot access the members of the class that implements the interface.
Variable, component data is invisible to customers, so you only need to get the address of the virtual function table.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.