Effective use and design of COM smart pointer-Clause 5: Understand behind the design of _ com_ptr_t

Source: Internet
Author: User

Article 5: Learn about the historical reasons behind the _ com_ptr_t Design
_ Com_ptr_t is a proprietary template class of Microsoft in VC. It encapsulates the QueryInterface (), AddRef (), and Release () operations of IUnknown, and provides some member functions to operate COM interface pointers. At the same time, _ com_ptr_t also simplifies the operations of the COM interface on reference count and query operations between different interfaces.

To use the smart pointer _ com_ptr_t, you must first use the macro _ COM_SMARTPTR_TYPEDEF to declare the _ com_ptr_t category of the Specialization version. Then, you can use a name such as "interface name + Ptr" to define the smart pointer of this interface type. For example:

View plaincopy to clipboardprint? _ COM_SMARTPTR_TYPEDEF (ICalculator, _ uuidof (ICalculator ));
_ COM_SMARTPTR_TYPEDEF (ICOMDebugger ,__ uuidof (ICOMDebugger ));
HRESULT Calculaltor ()
{
ICOMDebuggerPtr spDebugger = NULL;
ICalculatorPtr spCalculator (CLSID_CALCULATOR); // The constructor can create COM components.
Int nSum = 0;
SpCalculator-> Add (1, 2, & nSum );

SpDebugger = spCalculator; // automatically calls the interface required for QueryInterface query.
SpDebugger-> GetRefCount ();

Return S_ OK;
} // You do not need to manually call Release (). The API automatically calls Release () during smart pointer analysis ().
_ COM_SMARTPTR_TYPEDEF (ICalculator, _ uuidof (ICalculator ));
_ COM_SMARTPTR_TYPEDEF (ICOMDebugger ,__ uuidof (ICOMDebugger ));
HRESULT Calculaltor ()
{
ICOMDebuggerPtr spDebugger = NULL;
ICalculatorPtr spCalculator (CLSID_CALCULATOR); // The constructor can create COM components.
Int nSum = 0;
SpCalculator-> Add (1, 2, & nSum );

SpDebugger = spCalculator; // automatically calls the interface required for QueryInterface query.
SpDebugger-> GetRefCount ();

Return S_ OK;
} // You do not need to manually call Release (). The API automatically calls Release () during smart pointer analysis ().

 

The _ COM_SMARTPTR_TYPEDEF macro is generally placed in a separate header file. In this way, all files related to this header file can use smart pointers of the type "interface name + Ptr.

This makes the _ com_ptr_t smart pointer relatively simple to use, there is no lot of Type parameterization process for the template when writing the code. The user does not feel the existence of the template. This smart pointer can be used in a method similar to the interface pointer.

If you want to explore how the specificity process of the _ com_ptr_t smart pointer is completed, we can expand the macro _ COM_SMARTPTR_TYPEDEF used in the specificity process:

View plaincopy to clipboardprint? Typedef _ com_ptr_t <_ com_IIID <IMyInterface, _ uuidof (IMyInterface)> IMyInterfacePtr;
Typedef _ com_ptr_t <_ com_IIID <IMyInterface, _ uuidof (IMyInterface)> IMyInterfacePtr;
The prototype of _ com_IIID is:

View plaincopy to clipboardprint? Template <typename _ Interface, const IID * _ IID/* = & __ uuidof (_ Interface) */>
Class _ com_IIID
Template <typename _ Interface, const IID * _ IID/* = & __ uuidof (_ Interface) */>
Class _ com_IIID
We can see that the function of the _ com_IID class template is to encapsulate the IID and the specific type, and bind them together. _ Com_ptr_t then uses the _ com_IID parameterized type as the real parameter of the type parameter to construct a specific version of the smart pointer type.

It is also worth mentioning that if you want to use the _ uuidof vc-specific keyword, you need to add the following form when declaring the interface:

View plaincopy to clipboardprint? _ Declspec (uuid ("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX "))
_ Declspec (uuid ("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX "))
This syntax. The following is the declaration of the ICalculator interface:

View plaincopy to clipboardprint? Interface _ declspec (uuid ("994d80ac-a5b1-440a-a3e9-2533100b87ce") ICalculator: IUnknown
{
Virtual hresult stdmethodcalltype Add (
Const int nNum1,
Const int nNum2,
Int * pnSum
) Const = 0;

Virtual hresult stdmethodcalltype Sub (
Const int nMinuend,
Const int nSubtrahend,
Int * pnQuotient
) Const = 0;
};
Interface _ declspec (uuid ("994d80ac-a5b1-440a-a3e9-2533100b87ce") ICalculator: IUnknown
{
Virtual hresult stdmethodcalltype Add (
Const int nNum1,
Const int nNum2,
Int * pnSum
) Const = 0;

Virtual hresult stdmethodcalltype Sub (
Const int nMinuend,
Const int nSubtrahend,
Int * pnQuotient
) Const = 0;
};
More functional functions are encapsulated in _ com_ptr_t (for example, you can create a COM component when constructing a smart pointer), and you can use the value assignment operator to query interfaces. You may ask why CComPtr does not provide similar operations. This topic involves the design principles of smart pointers. We will conduct an in-depth discussion on "considering the trade-off in Design Principles.

After reading some basic usage of _ com_ptr_t, let's imagine another situation: if we have a COM component but cannot get its header file, so how should we operate them in VC? Maybe you think it is unlikely that you can call a function without getting the header file, because your Code cannot be compiled. However, the fact is that the lack of C/C ++ header files exists in a large number of COM components.

These COM designers do not take care of C/C ++ programmers (to a large extent, they also use C ++ to develop COM ), instead, they use a better method to declare the component's interface-Type Library.

A Type Library is a language-independent equivalent to C ++ header files used in explanatory and macro languages [1 ]. In other words, in C ++ and C, our type declarations are replaced by header files, while VB and delphi can be completed through the Type Library.

The # import preprocessing Command provided by Microsoft for VC can convert a type library into an equivalent C/C ++ header file. In this way, developers only need to publish a set of Type libraries to define corresponding interfaces in multiple languages.

First, we can use the # import preprocessing command to import a Type Library and see what the compiler has done for us. Taking ADO as an example, we use the # import preprocessing command to import the source code of the ADO Type Library as follows:

View plaincopy to clipboardprint? # Import "C: \ Program Files \ Common Files \ System \ ado \ msado15.dll" rename ("EOF", "rsEOF ")
# Import "C: \ Program Files \ Common Files \ System \ ado \ msado15.dll" rename ("EOF", "rsEOF ")
It looks a little complicated and slightly different from the normal compilation preprocessing command form. However, it is very convenient. If you compile this program a little, the msado15.tlh and msado15.tli files will be output under the corresponding directory.

Msado15.tlh contains the interface declaration, which looks like the following:

View plaincopy to clipboardprint? // Created by Microsoft (R) C/C ++ Compiler Version 12.00.8168.0 (a2f27f36 ).
//
// D: \... \ debug \ msado15.tlh
//
// C ++ source equivalent of Win32 type library C: \... \ ado \ msado15.dll
// Compiler-generated file created 08/22/11 at 14:19:31-do not edit!
Struct _ declspec (uuid ("00000512-0000-0010-8000-00aa006d2ea4 "))
/* Dual interface */_ Collection;
Struct _ declspec (uuid ("00000513-0000-0010-8000-00aa006d2ea4 "))
/* Dual interface */_ DynaCollection;
Struct _ declspec (uuid ("00000534-0000-0010-8000-00aa006d2ea4 "))
/* Dual interface */_ ADO;
Struct _ declspec (uuid ("00000504-0000-0010-8000-00aa006d2ea4 "))
/* Dual interface */Properties;
...
//
// Smart pointer typedef declarations
//
_ COM_SMARTPTR_TYPEDEF (_ Collection, _ uuidof (_ Collection); // OH ~ Too familiar!
_ COM_SMARTPTR_TYPEDEF (_ DynaCollection, _ uuidof (_ DynaCollection ));
_ COM_SMARTPTR_TYPEDEF (_ ADO, _ uuidof (_ ADO ));
_ COM_SMARTPTR_TYPEDEF (Properties, _ uuidof (Properties ));
_ COM_SMARTPTR_TYPEDEF (Property, _ uuidof (Property ));
_ COM_SMARTPTR_TYPEDEF (Error, _ uuidof (Error ));
_ COM_SMARTPTR_TYPEDEF (Errors, _ uuidof (Errors ));
_ COM_SMARTPTR_TYPEDEF (Command15, _ uuidof (Command15 ));
...
// Created by Microsoft (R) C/C ++ Compiler Version 12.00.8168.0 (a2f27f36 ).
//
// D: \... \ debug \ msado15.tlh
//
// C ++ source equivalent of Win32 type library C: \... \ ado \ msado15.dll
// Compiler-generated file created 08/22/11 at 14:19:31-do not edit!
Struct _ declspec (uuid ("00000512-0000-0010-8000-00aa006d2ea4 "))
/* Dual interface */_ Collection;
Struct _ declspec (uuid ("00000513-0000-0010-8000-00aa006d2ea4 "))
/* Dual interface */_ DynaCollection;
Struct _ declspec (uuid ("00000534-0000-0010-8000-00aa006d2ea4 "))
/* Dual interface */_ ADO;
Struct _ declspec (uuid ("00000504-0000-0010-8000-00aa006d2ea4 "))
/* Dual interface */Properties;
...
//
// Smart pointer typedef declarations
//
_ COM_SMARTPTR_TYPEDEF (_ Collection, _ uuidof (_ Collection); // OH ~ Too familiar!
_ COM_SMARTPTR_TYPEDEF (_ DynaCollection, _ uuidof (_ DynaCollection ));
_ COM_SMARTPTR_TYPEDEF (_ ADO, _ uuidof (_ ADO ));
_ COM_SMARTPTR_TYPEDEF (Properties, _ uuidof (Properties ));
_ COM_SMARTPTR_TYPEDEF (Property, _ uuidof (Property ));
_ COM_SMARTPTR_TYPEDEF (Error, _ uuidof (Error ));
_ COM_SMARTPTR_TYPEDEF (Errors, _ uuidof (Errors ));
_ COM_SMARTPTR_TYPEDEF (Command15, _ uuidof (Command15 ));
...
 

 

Msado15.tli includes the interface implementation:

View plaincopy to clipboardprint? // Created by Microsoft (R) C/C ++ Compiler Version 12.00.8168.0 (a2f27f36 ).
//
// D: \... \ debug \ msado15.tli
//
// Wrapper implementations for Win32 type library C: \... \ ado \ msado15.dll
// Compiler-generated file created 08/22/11 at 14:19:31-do not edit!
// Interface _ Collection wrapper method implementations
# Pragma implementation_key (1)
Inline long _ Collection: GetCount (){
Long _ result;
HRESULT _ hr = get_Count (& _ result );
If (FAILED (_ hr) _ com_issue_errorex (_ hr, this, _ uuidof (this ));
Return _ result;
}
# Pragma implementation_key (2)
Inline IUnknownPtr _ Collection: _ NewEnum (){
IUnknown * _ result;
HRESULT _ hr = raw _ NewEnum (& _ result );
If (FAILED (_ hr) _ com_issue_errorex (_ hr, this, _ uuidof (this ));
Return IUnknownPtr (_ result, false );
}
...
// Created by Microsoft (R) C/C ++ Compiler Version 12.00.8168.0 (a2f27f36 ).
//
// D: \... \ debug \ msado15.tli
//
// Wrapper implementations for Win32 type library C: \... \ ado \ msado15.dll
// Compiler-generated file created 08/22/11 at 14:19:31-do not edit!
// Interface _ Collection wrapper method implementations
# Pragma implementation_key (1)
Inline long _ Collection: GetCount (){
Long _ result;
HRESULT _ hr = get_Count (& _ result );
If (FAILED (_ hr) _ com_issue_errorex (_ hr, this, _ uuidof (this ));
Return _ result;
}
# Pragma implementation_key (2)
Inline IUnknownPtr _ Collection: _ NewEnum (){
IUnknown * _ result;
HRESULT _ hr = raw _ NewEnum (& _ result );
If (FAILED (_ hr) _ com_issue_errorex (_ hr, this, _ uuidof (this ));
Return IUnknownPtr (_ result, false );
}
...
Microsoft does not want you to read these two sets of files, nor does it expect you to modify them. Do not edit !" It will definitely let you get rid of this idea. But from msado15.tlh, you must have found such a friendly and familiar statement:

View plaincopy to clipboardprint? //
// Smart pointer typedef declarations
//
_ COM_SMARTPTR_TYPEDEF (_ Collection, _ uuidof (_ Collection); // OH ~ Too familiar!
_ COM_SMARTPTR_TYPEDEF (_ DynaCollection, _ uuidof (_ DynaCollection ));
_ COM_SMARTPTR_TYPEDEF (_ ADO, _ uuidof (_ ADO ));
//
// Smart pointer typedef declarations
//
_ COM_SMARTPTR_TYPEDEF (_ Collection, _ uuidof (_ Collection); // OH ~ Too familiar!
_ COM_SMARTPTR_TYPEDEF (_ DynaCollection, _ uuidof (_ DynaCollection ));
_ COM_SMARTPTR_TYPEDEF (_ ADO, _ uuidof (_ ADO ));
Oh ~ This preprocessing command generates the intelligent pointer code of _ com_ptr_t with the Type Library! If you forget how _ COM_SMARTPTR_TYPEDEF specizes a set of smart pointers, review article 2. It is rare to bind a compilation preprocessing command with the code of a specific function. Therefore, do not expect that # import is portable. In fact, COM components cannot be transplanted to other platforms.

However, you may feel that COM, _ com_ptr_t, And the compiler (which should be the Preprocessor of the compiler) have some associations. Indeed, after Microsoft proposed COM, it added support for COM to the VC compiler. While VB, delphi, and javascript support COM at the syntax level (in fact, they all have a runtime that supports COM to support these features of COM [8 ]), there is no smart pointer. The variable pointing to the COM interface is a smart pointer. Let's take a look at a piece of VB code. He may give us a better understanding of the smart pointer _ com_ptr_t:

View plaincopy to clipboardprint? Dim objVar as MyClass
Set objVar = new MyOtherClass
ObjVar. DoSomething
Dim objVar as MyClass
Set objVar = new MyOtherClass
ObjVar. DoSomething
My VB skills are not very good, but the above lines of code are enough for a COM component to work. Let's further analyze its running process:

1. First, it defines a variable named objVar and its type is myClass.

2. instantiate a COM component of MyOtherClass and assign it to objVar.

3. objVar executes the corresponding DoSomething function.

You may ask, is there a parent-child relationship between the two sides of the set objVar = new mytherclass equal sign in step 2? If not, then the VB compiler will allow it to compile?

In VB, MyClass and mytherclass do not need to have any relationship. In fact, as long as the hidden components behind mytherclass implement interfaces of this type, the program will work correctly. What if not? Then he will throw a runtime exception and wait for the programmer to handle it.

If this weak type of language affects your reading, you may consider objVar as an instance of _ com_ptr_t. Then we use the C ++ syntax to re-implement the above process and see what happened.

View plaincopy to clipboardprint? _ COM_SMARTPTR_TYPEDEF (MyClass, _ uuidof (MyClass ));
_ COM_SMARTPTR_TYPEDEF (mytherclass, _ uuidof (mytherclass ));
MyClassPtr spMyClass = NULL; // dim objVar as MyClass
Mytherclassptr spMyOtherClass (clsid_mytherclass );
SpMyClass = spmytherclass; // set objVar = new mytherclass
SpMyClass. DoSomething (); // objVar. DoSomething
_ COM_SMARTPTR_TYPEDEF (MyClass, _ uuidof (MyClass ));
_ COM_SMARTPTR_TYPEDEF (mytherclass, _ uuidof (mytherclass ));
MyClassPtr spMyClass = NULL; // dim objVar as MyClass
Mytherclassptr spMyOtherClass (clsid_mytherclass );
SpMyClass = spmytherclass; // set objVar = new mytherclass
SpMyClass. DoSomething (); // objVar. DoSomething


You will find that the methods for operating the COM interface through _ com_ptr_t are surprisingly similar to those using the variable operation interface in VB. Such as "spMyClass = spmytherclass;". In this way, query operations for different types of interfaces are implemented in VC through _ com_ptr_t's overload of the value assignment operator. If the query interface fails, a running exception is thrown.

Because VC lacks the necessary runtime [8] for COM, the designers of _ com_ptr_t may consider the following when applying COM technology to VC:

1. If VB is compatible, VC should also be able to use it. So # the emergence of import makes it easy for VC to import the Type Library through _ com_ptr_t.

2. The interface query and use mode VC used by VB should also be available. Therefore, _ com_ptr_t overload the value assignment operator to query interfaces. Multiple constructors are overloaded to create objects like VB.

3. The Features Shown by vb vc should also be presented in the same way. Therefore, an error occurs during interface query. _ com_ptr_t throws an exception like VB.

It seems that it exists to operate the COM interface with similar syntax or mechanism with VB or Delphi. Therefore, it violates the C/C ++ conventions in many cases (for example, it may throw an exception in the value assignment operator ). However, this feature makes the code easier to reuse and shortens the time needed to learn smart pointers.

The existence of _ com_ptr_t unifies the methods for operating COM interfaces in different languages. His design is complex and powerful. So that VC can use the Type Library as conveniently as other languages. Of course, the pursuit of this uniformity also exposes a considerable number of problems (such as the risks arising from automatic interface queries in clause 7 ).

But no matter what it is, you know its design intent. This helps you understand other details about this smart pointer.

Author's "liuchang5 column"

Related Article

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.