Click here to Skip to main content
Click here to Skip to main content

A COM Smart Pointer

By , 22 Feb 2006
 

Introduction

CComPtr wraps any interface pointer and will call AddRef() and Release() properly. That is, you don't need to worry about controlling the lifetime of your interface pointer.

Details

  1. ATL does provide a Smart Pointer class named CComQIPtr. But it uses some ATL related funtion so that it can be used only in an ATL environment.
  2. VC does provide _com_ptr_t & _bstr_ptr_t keywords to provide some kind of a wrapper. But it depends on the MS VC compiler, without which you can benefit.
  3. My CComPtr provides compiler unrelated features to wrap any interface pointer except IUnknown. Later I will explain why this limitation comes.

Illustration

Note: INTERFACE and piid are passed into the class CComPtr through template parameters.

CComPtr has four constructors to meet different requirement.

  • CComPtr()

Simply constructor, do nothing.

  • CComPtr(INTERFACE* lPtr)

Construct a new CComPtr object with an existing interface pointer. After that this new CComPtr object can be used as interface pointer itself.

  • CComPtr(IUnknown* pIUnknown, IID iid)

Construct a new CComPtr object with an IUnknown pointer and an IID, which represents the interface you are interested in. Internally constructor will call pIUnknown->QueryInterface to fetch interface pointer according to the IID.

  • CComPtr(const CComPtr<INTERFACE, piid>& RefComPtr)

This contsructor takes another existing CComPtr object as parameter. After that, clone the old CComPtr object.

CComPtr has one destructor, which will release interface pointer.

~CComPtr(){ if (m_Ptr) { m_Ptr->Release(); m_Ptr = NULL; } }

CComPtr has five operators.

  • (a) Operator INTERFACE* : Returns interface pointer wrapped by class CComPtr.

Example:

CComPtr<IWavePciStream> m_wavePciStreamPtr; 
IWavePciStream* pWavePciStream = (IWavePciStream*)m_wavePciStreamPtr;
  • (b) Operator *: Used to fetch interface object wrapped by class CComPtr.

Example:

CComPtr<IWavePciStream> m_wavePciStreamPtr; 
IWavePciStream wavePciStream = *m_wavePciStreamPtr;
  • (c) Operator &: Used to fetch the pointer to interface pointer wrapped by class CComPtr.

Example:

CComPtr<IWavePciStream> m_wavePciStreamPtr; 
IWavePciStream** ppWavePciStream = &m_wavePciStreamPtr;
  • (d) Operator -> : Used to make CComPtr object act as a TRUE interface pointer.

Example

CComPtr<IWavePciStream> m_wavePciStreamPtr; 
m_wavePciStreamPtr->AnyPciStreamMethods();
  • (e) Operator = : Used to transfer an exsiting interface pointer information to a new CComPtr object. This operator has three different parameter list.
    1. In parameter is INTERFACE*
    2. In parameter is another CComPtr object.
    3. In parameter is an interface pointer to IUnknown, which will be used to query other interface pointer defined by piid.

CComPtr has another three methods.

  • Attach : Used to attach an existing interface pointer to a CComPtr object.
  • Detach : Used to detach interface pointer from a CComPtr object.
  • Release : Used to release wrapped interface pointer explicitly.

CComPtr is implemented in pure C++. That is, it does not depend on any specific C/C++ compiler and can be compiled under any ANSI C/C++ compliant compiler. I have tested in VC++ 6.0. I do believe it works in GNU C/C++ and MAC CodeWarrior.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Skeeter
Program Manager Microsoft
China China
Member
Graduated from Shanghai Tongji university in 1997 with bachelor degree in computer science, Wenbin got his first job as a system engineer at Bell Alcatel Mobile Shanghai office responsible for telcom-class mobile system development and deployment. Since then, Wenbin has in turn worked for Intel and Microsoft both inside and outside China, first as senior engineer, later project manager and then senior product manager.
 
With 15-year experience working with the world top IT companies, Wenbin has developed solid skill in C/C++, C#, Java, software engineering, agile development, etc, and multiple talents in product management, public presentation and speech, etc. He has always been an active member at PMI (Project Management Institution) and a regular lecture at Intel Developer Forum, Microsoft TechED conference as well as many other world-class industrial conferences. His wide-ranged industrial practice and high-level personal maturity have made him one of the best in public speech and professional training.
 
Over the years, Wenbin has cultivated his very own style in public speech, which is considered informative, engaging and refined. Since last year, Wenbin has also taken new adventure in project and product management consulting business and has proven high capacity through his work with many local emergent IT firms. Wenbin’s specialty in management consulting is on project management methodologies and processes, project management tools (e.g. MS Project), and team recruitment, build, and motivation.
 
In addition, Wenbin has received many professional qualifications including MCSE (Microsoft Certified System Engineer), MCSD (Microsoft Certified System Developer), MCDBA (Microsoft Certified Database Administrator), SCJP 2 (Sun Certified Java Programmer 2), and PMP (PMI Certified Project Management Professional). On top of that, Wenbin has got several on-duty inventions and one of them was successfully patented by United States Patent and Trademark Office.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberjohnsonpm26 Apr '11 - 21:02 
I was seeing such an article about this CComPtr for the first time and it helped me a lot.
This portion explains many of the minute details of this pointer. I am grateful to the article and author.
NewsReconsider your stuff [modified]membercnstrnd18 Oct '06 - 15:19 
Pointing goldsam was wrong doesn't prove you right. In fact, your wrapper is flawed in many ways. To state what a lot of your readers have already noticed :
 
- In all you accessors, you return your inner interface pointer. It ruins the whole encapsulation purpose intended by any smart-pointer/reference/handle. You should return *this; as a reference to self.
 
- Mixing run-time and compile-time use of the IID doesn't make sense. The IID should be statically available within your class. There is no need for a CComPtr(IUnknown* pIUnknown, IID iid) copy constructor. By the way, your implementation is not even consistent since you don't supply an operator= (IUnknown* pIUnknown, IID iid).
 
- operator* is irrevelant. COM interfaces are abstract thus not supposed to be dereferenced to their underlying struct. operator-> is sufficient.
 
- Implicit cast to the inner interface pointer is questionnable. The code you write, based on that wrapper, doesn't need such convertion. When giving away the actual interface to other APIs, a simple GetRawInterface() accessor is generally preferred.
 
- IsEqualObject should be redesigned to a symetrical operator== not only for cosmetics but because you MUST query for the address of BOTH the IUnknown. Another thing : you seem to prefer to call QueryInterface and Release on different interface pointers. This only obfuscate your code. Regarding IUnknown that ALL interfaces must implement, a COM object MUST behave the exact same way whatever interface the client use to access that particular object.
 
- Over ASSERTing every statement and setting m_Ptr to NULL in every way possible doesn't make your code more robust, it only show quite clearly you're not confident with how you're doing things.
 
- Talking about robustness, overloading operator& is awful : no well designed wrapper exposes its implementation details.
 
- You might also consider to make your class follow the RAII idiom so it can't be in a inconsistent state.
 
- And it goes on ...
 
To summer it up, while the use of smart-pointers is meant to be simple and natural, these little beasts are difficult to implement correctly and need very special care. See ?
 
It would have been a simple matter of respect to FIX your code when kind people took time to review it and pointed mistakes. Jumping on the first attempt of flamming just adds up to the pollution, your code included ... sorry. Writing articles here isn't about teaching, it is about sharing experiences so everybody can learn, you're included, not the other way around.
GeneralCometmemberJim Crafton22 Feb '06 - 8:47 
Check out the Comet[^] COM library - very cool stuff and they don't rely on ATL or VC's COM compiler extensions.
 
¡El diablo está en mis pantalones! ¡Mire, mire!
 
Real Mentats use only 100% pure, unfooled around with Sapho Juice(tm)!
 
SELECT * FROM User WHERE Clue > 0
0 rows returned

Save an Orange - Use the VCF!
GeneralRe: Comet - looks good!memberSkeeter22 Feb '06 - 11:10 
Hi Jim,
Thank you for the recommendation. Comet looks cool. I will spend time in studying it in next few days. However I notice that the lastest version of Coment is release on 2004-9-26, one and half years ago. What happened to the owner? Are they too busy to continue the work? Or they gave up?
 
Skeeter
MCP, MCP+I, MCSE, MCSD & MCDBA
GeneralRe: Comet - looks good!memberJim Crafton22 Feb '06 - 13:42 
Not really sure. I've started to use it for some stuff in my projects, and on the forums, there's a link to a newer version, but I don't why the development seems to have stopped. But even in it's current state it's quite useful.

 
¡El diablo está en mis pantalones! ¡Mire, mire!
 
Real Mentats use only 100% pure, unfooled around with Sapho Juice(tm)!
 
SELECT * FROM User WHERE Clue > 0
0 rows returned

Save an Orange - Use the VCF!
GeneralThis is buggy and in efficient as hellmembergoldsam28 Oct '05 - 7:46 
This class is horribly flawed in logic as well as inefficient.
 
Consider the IsEqualObject(IUnknown* pOther) method:
First, there is no need to check if both are NULL.. they either equal each other or they don't. It doesn't make sense to compare the interfaces after a call to QueryInterface with IID = IID_IUnknown. m_Ptr is the interface already being held, so IT MUST BE AN IUnknown. The same goes with pOther... you already know all COM interfaces have IUnknown as a base... thats not what QueryInterface was designed to discover. YOU COULDN'T CALL QueryInterface IF THEY DIDN'T SUPPORT IT!! In addition to that total brain dead logic, you are leaking memory!!! you are only calling Release() on the interfaces if both point to an object, what happens if only one of the QueryInterface calls is succesful?
 
You also seem to have some obsession of setting m_Ptr to 0 in the most pointless places. Here is one of many tasty examples of your zeroing fetish:
 
CComPtr(const CComPtr& RefComPtr)
{
m_Ptr = NULL; <----------- WHY?!
m_Ptr = (INTERFACE*)RefComPtr; <----- this gonna be zero... or have a value
if (m_Ptr)
{
m_Ptr->AddRef();
}
}
 
In the future, you should really take the time to review you're code before you post it to a site like this. The inefficiencies are minor, buy the memory leak issue is hard to miss. Just slow down a bit.
 
-- modified at 13:46 Friday 28th October, 2005
GeneralRe: This is buggy and in efficient as hellmemberSkeeter21 Feb '06 - 11:08 
Though I very appreciate your inputs, I think your attitude somehow has some problems. First fo all, your suggestion about IsEqualObject(IUnknown* pOther) method tells me you don't understand COM at all. A COM pointer doesn't necessarily points to IUnknown interface. It may points to other interfaces in the same COM object. That is why QueryInterface() must be called even if m_Ptr is there. Secondly my class is clearly a COM Smart Wrapper class, the pointer passed in MUST be a COM pointer. This is the requirement to the developer using my code. Because of this, it is safe for my code to call QueryInterface() on lPtr to get the IUnknown interface. In this situation, I think you are the one who is in total brain dead logic. Last but not least, why I set m_Ptr to 0 in most of the place is because in Visual C++ Compiler, the default value given to a pointer is 0xcccccccc instead of 0. After querying interface, I must check whether it is successful. I don't see any problem to set pointer to 0 and then validate it after QueryInterface() returns. This is not about efficiency. It is about code robutness. If you don't do that in your code, you definitely a sucks developer.
Though you are so unfriendly, I still value your inputs and look forward to more suggestions from you.
 
Skeeter
 
Skeeter
MCP, MCP+I, MCSE, MCSD & MCDBA
GeneralDCOMsussjavn4 Aug '03 - 14:01 
How can I activate a COM object in a remote machine? I allways get the message "Access is denied".
 
Júlio
GeneralSome bugs herememberTim Smith17 Mar '02 - 4:52 
About a month ago I reported bugs to the author so he could fix them. They have gone unfixed so I wanted to warn people.
 
1. All the operator = methods will leak a reference. When fixing this bug, be very careful that you don't make the mistake of releasing the previous interface then assigning the new. The can cause a bug if the new interface is the same as the current interface.
 
2. Aside from a minor optimization, the IsEqualObject method can leak a reference if one of the objects fails to return an interface for IUnknown. It is very true that this should never happen, but since the author is already testing for this case, he should properly handle it.
 
Tim Smith
 
I know what you're thinking punk, you're thinking did he spell check this document? Well, to tell you the truth I kinda forgot myself in all this excitement. But being this here's CodeProject, the most powerful forums in the world and would blow your head clean off, you've got to ask yourself one question, Do I feel lucky? Well do ya punk?
GeneralRe: Some bugs herememberNish [BusterBoy]17 Mar '02 - 13:31 
Tim Smith - BugSlayer Smile | :)
 
Go Tim Go....
 
Nish
 

My miniputt high is now 29
I do not think I can improve on that
My temperament won't hold
 
www.busterboy.org


Questionis this new?memberAnonymous1 Mar '02 - 4:55 
what about class _com_ptr_t?
already exists and surely more reliable than yours!
AnswerRe: is this new?memberTim Smith1 Mar '02 - 5:05 
Read the article
 
Tim Smith
 
I know what you're thinking punk, you're thinking did he spell check this document? Well, to tell you the truth I kinda forgot myself in all this excitement. But being this here's CodeProject, the most powerful forums in the world and would blow your head clean off, you've got to ask yourself one question, Do I feel lucky? Well do ya punk?
GeneralATL versionmemberAnonymous21 Oct '01 - 23:13 
You wrote:
>But it uses some ATL related funtion so that it can be
>used only in ATL environment
 
Which functions ?
There are only 2 small and simple functions.
 
And you use COM on Linux & Mac ??
GeneralRe: ATL versionmemberAnonymous5 Nov '01 - 10:16 
You can use COM on non Windows OS. I did it by implementing myself the INPROC support of the COM library, and since then we run our server both on NT and Solaris.
The source is free - from
http://www.geocities.com/ResearchTriangle/Facility/1124/software/software.html
 
enjoy.
 
Noam Cohen,
Vsoft.
GeneralYou're just coping the info in MSDNmemberAnonymous16 Oct '01 - 3:59 
What's going on ? Have you just discovered CComPtr ? It's been around for years and has been extremely well documented. Your article doesn't really explain a thing.
Very disappointingMad | :mad:
GeneralRe: You're just coping the info in MSDNmemberAndrew Peace16 Oct '01 - 11:25 
The implementation described here is not the one described in MSDN - the author has created a new implementation for the reasons described in the article.
 
--
Andrew.
GeneralRe: You're just coping the info in MSDNmemberSkeeter16 Oct '01 - 15:01 
Every innovation is based on previous one. Nobody can create a new idea from empty. This com smart pointer is used in our driver programming architecture.
When talking about driver programming, maybe you will think it is too simple to use COM. Actually our driver is very complicated. It has its own hardware abstract layer, its own OS service layer, its own plug in layer. Every module is based on C++/COM. That is, we implemented our own IUnknown, INondelegationQuryInterface, etc. Let me say, we do all the Windows COM can do. Our driver can run under not only Windows platform but also MAC and LINUX. We have core code and OS specific code, which can be replaced smoothly. As a core developer of such driver project, I'd like to share my experience with other programmers. If you think this is useful to you, I feel very happy. If not, I have to say sorry and I hope you can do it better
and share your experience with me. Thank you.
 
Skeeter
------------------------------
MCP, MCP+I, MCSE, MCSD & MCDBA
GeneralRe: You're just coping the info in MSDNmemberARAM @ WAYZ22 Oct '01 - 0:21 
Personally, I find this article very useful. The implementation is definitely different from the one found in MSDN. It is much easier to comprehend CComPtr by reading this article rather than MSDN, especially for "new-COMers". My vote is excellent.
I'd also be very happy if the author could spare some time, and share his experience about driver programming with COM, particularly WDM.
Thank you.
 
Aram Sargsyan
 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 22 Feb 2006
Article Copyright 2001 by Skeeter
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid