FAQ 34.13 How hard is it for callers to create and use a COM object?

FAQ 34.13 How hard is it for callers to create and use a COM object?

graphics/new_icon.gif

Not that hard. To create and use a COM object, a caller needs to

  • Include the external interface of the COM class. In this case the caller declares the IID for the IStack interface, the CLSID for CoStack, and the IStack interface. Usually all these declarations are provided by the writer of the COM class in an include file.

  • Create the object (line 1). The program calls CoCreateInstance passing it the class identifier for the COM class that is being created (CLSID_CoStack in this case), the interface identifier for IUnknown (IID_IUnknown), and a pointer to an interface pointer (&unknownPtr in this case), which CoCreateInstance uses to return a pointer to the IUnknown interface for the newly created object.

  • Query the object for one or more of its interfaces (line 2). The program calls QueryInterface passing it the interface identifier for the interface it wants access to (IID_IStack in this case) and a pointer to an interface pointer (&stackPtr in this case), which QueryInterface uses to return a pointer to the requested interface for the object (the interface pointer is NULL if the object does not support the requested interface).

  • Use the services provided by the interface (lines 3 and 4). The program calls the Push and Pop methods of the IStack interface using the interface pointer returned by QueryInterface.

  • Release the object (lines 5 and 6). The program holds two interface pointers for the Stack object (stackPtr and unknownPtr in this case), and therefore it calls Release once for each interface pointer it holds.

#include "wtypes.h"
#include <initguid.h>

// {FC3B3F61-BCEC-11D1-91FE-E1CBED988F66}
DEFINE_GUID(IID_IStack,
    0xFC3B3F61, 0xBCEC, 0x11D1, 0x91, 0xFE,
    0xE1, 0xCB, 0xED, 0x98, 0x8F, 0x66);
// {FC3B3F62-BCEC-11D1-91FE-E1CBED988F66}
DEFINE_GUID(CLSID_CoStack,
    0xFC3B3F62, 0xBCEC, 0x11D1, 0x91, 0xFE,
    0xE1, 0xCB, 0xED, 0x98, 0x8F, 0x66);

class IStack : public IUnknown {
public:
    STDMETHOD(Push) (long  value);
    STDMETHOD(Pop)  (long* value);
    STDMETHOD(Empty)(long* flag);
};

void useStack()
{
    IUnknown* unknownPtr = NULL;
    HRESULT hr = CoCreateInstance(CLSID_CoStack,     //Line 1
        NULL, CLSCTX_ALL, IID_IUnknown, (void**)&unknownPtr);
    if (SUCCEEDED(hr)) {
        IStack* stackPtr = NULL;
        hr = unknownPtr->QueryInterface(IID_IStack,  //Line 2
                                   (void**)&stackPtr);

        if (SUCCEEDED(hr)) {
            hr = stackPtr->Push(10);      //Line 3
            long val;
            hr = stackPtr->Pop(&val);     //Line 4
            stackPtr->Release();          //Line 5
        }
        unknownPtr->Release();            //Line 6
    }
}
 

The caller must take a couple of steps to initialize COM and signal when it is finished using COM:

  1. Initialize COM (line 7). The program loads the COM libraries by calling CoInitialize.

  2. Uninitialize COM (line 8). The program unloads the COM libraries by calling CoUninitialize.

int main()
{
    HRESULT hr = CoInitialize(NULL);   //Line 7
    if (SUCCEEDED(hr)) {
        useStack();
    }
    CoUninitialize();                  //Line 8
    return 0;
}
 

Of course, there are a zillion details that we don't have space to go into here.