COM isn't the kind of thing you can study for 15 minutes, get yourself confused, then fire off a quick question to some internet forum and then have everything become completely clear. I've studied it intensively for 15 years, have a whole bookshelf of books on it, and still have not completely mastered it to my complete satisfaction. However, to directly answer your question, one of the most common 'design patterns' found with everything related to COM is the concept of returning pointers to objects as the last parameter of a function.
To elaborate, COM objects aren't instantiated the way C++ objects are, that is, with the 'new' operator. Rather, they are instantiated through CoCreateInstance() or CoGetClassObject(), or some such function. Those functions return success/failure codes (a whole topic in itself) - not pointers to the object as with C++'s 'new' operator. That being the case, where does one obtain a pointer to the newly instantiated COM object if not through the function's return value? The answer is through that last parameter, i.e., ppv, of CoCreateInstance. It is an [out] parameter. That's a another whole topic one must master with COM. There are [in] parameters, [out] parameters, [out, retval] parameters, and so forth.
kbw said ...
If you intend to use COM, don't used the API directly, it's a world of pain. Use ATL instead, as difficult as it may intially seem, it's the easiest was to use COM from C++.
That's probably good advice for most people. Maybe unfortunately for me, I'm not like most people. I invested a great deal of time in using ATL, have most books written on it, but eventually turned away from it. I use COM a lot and write my own raw code, including ActiveX controls. My advice to anyone learning COM is to get several books on ATL and attempt to learn and use it, if for no other reason than that all ATL books I've ever seen devote the 1st half of the book to explaining the fundamental concepts of COM and component development.