Realization of IUNKNOWN interface in ATL (1)
ATL and MFC can be used as a tool for developing COM, and ATL is a Template Library, which is a Framework, which is the advantage of Template Library. Its advantage is that it is only provided to the programmer's form, which can be used to build software. Software buildings that are arbitrarily shaped by programmers; and Framework has already established the main body of the building. We only need to add bricks in the inside, do some details to make it in line with our requirements. Both have a thousand autumn. However, since the Template has a difference in OO (template class and template function that meets the required requirements) and the Template class and template function) and the Template are implemented during compilation, the operation is high, so compared with Framework, Template Library It is more flexible, exquisite, and effective.
The core of COM technology is an IUNKNOWN interface. Specific points are addRef (), Release (), queryinterface () three methods for specific implementation. The complexity of the first two interfaces is that the COM has a different thread model: Microsoft defined COM thread models have three types: STA, MTA, and free, three models, the process of reference count is different; the last interface is Interface query - give me a RIID, I can give this RIID pointer for you to call. Its complexity is mainly how to get the correct interface pointer when the COM object is aggregated. Although there are many cases, once it is determined that the physical identity of the thread model and the COM object, we can realize the general action method of these three interfaces. ATL is helping us complete this job, we only need to pass the thread model of the COM object to the ATL, and the ATL will automatically generate the correct code for us - and this is in compilation definite.
For the processing of reference counts, it is nothing more than two actions: add one and minus one of the counts. The operation of the plus and subtraction is completed by the API function interlockedIncrement () and interlockedDecrement (); when single-thread, simply m_cref and -m_ref can be. As a library, it is necessary to make certain packaging for these operations, we need to pass this packaging, give the outside interface. Meanwhile Let's first look at the implementation of INTERNALADDREF ().
The actual packet of different thread models in different thread models is encapsulated in the same function: the addition is increment (), subtraction is DECREMENT (). Three classes representing different thread models: ccommultithreadmodel, ccommultithreadmodel, ccommultithreadmodelnocs, these two functions have been made in CCommultithreadModelnocs. In this way, we only need to pass the thread model as a template parameter to the object, and the object can obtain the correct reference count operation action.
At the same time, since the definition of the thread model has already been defined, the "critical area" should be defined in the class of these thread models, which also provides a unified interface to the "critical area" operation. We only need to use this interface to operate when any operation variables, then you only need to change a thread model parameter, our program can be made by single-threaded support into multi-threaded support. These three classes are as follows:
// from atlbase.h
Class CCommultithreadModelnocs
{
PUBLIC:
Static Ulong WinAPI Increment (LPLONG P) Throw ()
{Return InterlockedIncrement (P);
Static Ulong WinAPI Decrement (LPLONG P) throw ()
{Return InterlockedDecrement (p);}
Typedef ccomfakecriticalsection autocriticalsection;
Typedef ccomfakecriticalsection criticalsection;
Typedef ccommultithreadmodelnocs threadmodelnocs;
}
Class CCommultithreadModel
{
PUBLIC:
Static Ulong WinAPI Increment (LPLONG P) Throw ()
{Return InterlockedIncrement (P);
Static Ulong WinAPI Decrement (LPLONG P) throw ()
{Return InterlockedDecrement (p);}
Typedef ccomautocriticalsection autocriticalsection;
Typedef ccomcriticalsection criticalsection;
Typedef ccommultithreadmodelnocs threadmodelnocs;
}
Class CComsingLethreadModel
{
PUBLIC:
Static Ulong WinApi Increment (LPLONG P) throw () {Return (* P);
Static Ulong WinAPI Decrement (LPLONG P) throw () {return - (* p);
Typedef ccomfakecriticalsection autocriticalsection;
Typedef ccomfakecriticalsection criticalsection;
Typedef ccomsinglethreadmodel threadmodelnocs;
}
There are 3 critical regions in the ATL CcomcriticalSECTION, CCOMAUTOCRITICTION, CCOMFAKECRITICALSECTION. Each critical area provides a unified four operations to operate the critical area: Lock (), unlock (), init (), term (). When we use "critical regions" in the program, we only need to call these four operations to achieve the purpose of protecting data members from being modified by other threads. The calling method is as follows:
ThreadingModel :: autocriticalsection m_cs;
m_cs.lock ();
m_cs.unlock ();
The practical work of the three critical regions is as follows:
// from atlcore.h
Class CComcritics
{
PUBLIC:
Ccomcriticalsection () throw ()
{
MEMSET (& m_sec, 0, sizeof (critical_section);
}
HRESULT LOCK () throw () {
ENTERCRITICALSECTION (& m_sec);
Return S_OK;
}
HRESULT UNLOCK () throw ()
{
LeaveCriticalSection; & m_sec;
Return S_OK;
}
HRESULT INIT () throw ()
{
HRESULT HRES = S_OK;
__TRY
{
InitializecriticalSection (& m_sec);
}
// Structured Exception May Be Raised in Low Memory Situations
__EXCEPT (Exception_execute_Handler)
{
IF (status_no_memory == getExceptioncode ())
HRES = E_OUTOFMEMORY;
Else
HRES = E_FAIL;
}
Return HRES;
}
HRESULT TERM () throw ()
{
DeletecriticalSection; & m_sec);
Return S_OK;
}
Critical_section m_sec;
}
Class CComautocriticalsection: Public CCOMCRITICALSECTION
{
PUBLIC:
Ccomautocriticalsection ()
{
HRESULT HR = ccomcriticalsection :: init ();
IF (Failed (HR))
ATLTHROW (HR);
}
~ Ccomautocriticalsection () throw ()
{
CCOMCRITICALSECTION :: Term ();
}
Private:
HRESULT INIT (); // not implement. Ccomautocritics :: init sales never be called
HRESULT TERM (); // Not Implement. Ccomautocriticalsection :: Term Should Never Be Called
}
Class CCOMFAKECRITICALSECTION
{
PUBLIC:
HRESULT LOCK () throw () {return s_ok;}
HRESULT UNLOCK () throw () {return s_ok;}
HRESULT INIT () throw () {return s_ok;}
HRESULT TERM () throw () {Return S_OK;}
}
ATL provides the CComobjectroTROTEX template class to encapsulate the operation of the above-mentioned operation and the operation of the critical area. As long as it is provided to CComobjectroTRoTex a template parameter representing its thread model, it can make INTERNALADDREF (), INTERRELEASE (), and Lock () and unlock () four unified operation interfaces.
After so many layers, ATL has made all the operations needed by addRef () and release (), but ATL still does not take the final step - not integrates these operations into addRef () and Release. ), This is because ATL has to consider the polymerization factor. In the case of polymerization, the addRef () and release () operations of the COM object are completely different from independent activation. This is a later words, let's take a look at the implementation of QueryInterface () when independent activation. There is a first-character cloud: Table driver, hash table, cache technology is the three most important core of computer technology. Microsoft's play-driven technology is ulterior in its class, MFC is a most typical example, even the C old hand like Mengyan, and does not cover up its macro in MFC. headache. In ATL, Microsoft once again showed us a dazzling table driver practical method: interface map mapping table, object object mapping table, category has a category mapping table, connection point has a connection point mapping table, message has a message Map table. What is it from each table? It is a macro, which is a static function binding. It is a significant increase in efficiency of execution. It is the ability to span different compiler ABIs; of course, it is also a malignant expansion of program code, for C polymorphism Those who have not seen it, the demobilization of the design model of the agent, commission. (From this view, is it similar to the macro and template?)
Although the table driver has the advantages and disadvantages of the surface, it is undeniable that the operation mechanism of queryinterface () is ideal for the use of table drivers to achieve beautification code, improve work efficiency. We only need to provide the interface we want to expose to the ATL, ATL will make an accurate queryinterface ():
Begin_COM_MAP (CMYCLASS)
COM_Interface_entry (ImyInterface)
END_COM_MAP
Want to expose how many interfaces, write how many com_interface_entry, ATL will automatically use these declarations to generate a interface table named _TL_INTMAP_ENTRY, then provide an interNalQueryInterface () function in the CComobjectBase class, use this table as a parameter, and make correct QueryInterface (). Of course, the COM object provides a lot of ways, and com_interface_entry is only the most basic output interface method. Other dual interfaces, tear-off interfaces, aggregated interfaces (also divided into "planned "And" Blind "), through CHAIN to get the base class, etc., these content, etc., will then write one by one.
In addition to this table, another important role of Begin_COM_MAP is to provide a getUnknown () function, which makes us aggregated, inherited, we won't drop our iUnknown pointer!
The previous discussion is when the COM object is a normal object, and when the independent activation is activated, and when the aggregation is related, the practical scene of our class will have another scenery. Looking forward to it!