FAQ 34.12 What are COM classes and COM objects?

graphics/new_icon.gif

The concrete implementation of one or more COM interfaces.

A COM class is a body of code that implements all the functions of at least one COM interface. Every COM class has a unique CLSID and callers use the unique CLSID when they want to create objects that are instances of the COM class.

In the following example, the COM class CoStack implements the IUnknown interface and the IStack interface. The following code fragment declares the external interface including the CLSID for CoStack, the IStack interface, and the IID for the IStack interface.

 #include "wtypes.h" extern HRESULT STACK_E_UNDERFLOW; /* {FC3B3F61-BCEC-11D1-91FE-E1CBED988F66} */ DEFINE_GUID(IID_IStack,     0xFC3B3F61, 0xBCEC, 0x11D1, 0x91, 0xFE,     0xE1, 0xCB, 0xED, 0x98, 0x8F, 0x66); DECLARE_INTERFACE_(IStack, IUnknown) {     // *** IStack methods *** //     STDMETHOD(Push)  (THIS_ long  value) PURE;     STDMETHOD(Pop)   (THIS_ long* value) PURE;     STDMETHOD(Empty) (THIS_ long* flag) PURE; }; // {FC3B3F62-BCEC-11D1-91FE-E1CBED988F66} DEFINE_GUID(CLSID_CoStack,     0xFC3B3F62, 0xBCEC, 0x11D1, 0x91, 0xFE,     0xE1, 0xCB, 0xED, 0x98, 0x8F, 0x66); 

Class CoStack declares all the methods of the IUnknown interface (by the way, ULONG is a typedef for unsigned long) and the IStack interface. It also declares two private data members refCnt_ is used to implement reference counting for the object and data_ is the data structure used to hold the elements of the stack.

 #include <stack> using namespace std; class CoStack : public IStack { public:     // *** IUnknown methods *** //     STDMETHOD(QueryInterface) (REFIID riid, void** ppv);     STDMETHOD_(ULONG,AddRef) ();     STDMETHOD_(ULONG,Release) ();     // *** IStack methods *** //     STDMETHOD(Push) (long  value);     STDMETHOD(Pop)  (long* value);     STDMETHOD(Empty)(long* flag);     CoStack(); private:     ULONG refCnt_;     stack<long> data_; }; CoStack::CoStack() : refCnt_(0) , data_() { } 

Note that class CoStack has a constructor even though the COM specification does not define constructors for COM classes. In this case, the implementation is taking advantage of a C++ feature. In particular, the C++ constructor initializes the data structures of the C++ object, which happens to initialize the data structures of the COM object at the same time. The lesson here is that class CoStack is a mix of COM features and C++ features, and sometimes it is hard to tell them apart.

The COM class implements the three methods of the IUnknown interface. QueryInterface tests to see if the caller is requesting one of the two interfaces that this class implements. If the caller is requesting a legitimate interface for this class, QueryInterface returns a pointer to that interface; otherwise, it returns NULL. Notice that QueryInterface copies an interface pointer so that it calls AddRef. AddRef increments the reference count for this object. Release decrements the reference count and destroys the object if the reference count is zero.

 STDMETHODIMP CoStack::QueryInterface(REFIID riid, void** ppv) {     if (riid == IID_IUnknown)         *ppv = this;     else if (riid == IID_IStack)         *ppv = this;     else {         *ppv = NULL;         return E_NOINTERFACE;     }     AddRef();     return S_OK; } STDMETHODIMP_(ULONG) CoStack::AddRef() { return ++refCnt_; } STDMETHODIMP_(ULONG) CoStack::Release() {     ULONG result = --refCnt_;     if (result == 0)         delete this;     return result; } 

The COM class also implements the methods of the IStack interface. These all look pretty normal except for the fact that the return values for the methods are status codes (S_OK if the call succeeds, STACK_E_UNDERFLOW if an underflow condition is detected).

 STDMETHODIMP CoStack::Push(long value) {     data_.push(value);     return S_OK; } STDMETHODIMP CoStack::Pop(long* value) {     if (data_.empty())         return STACK_E_UNDERFLOW;     *value = data_.top();     data_.pop();     return S_OK; } STDMETHODIMP CoStack::Empty(long* flag) {     *flag = data_.empty() ? 1 : 0;     return S_OK; } 

Every COM class has a class object that acts as the meta class for the COM class. The most important function that the class object plays is that it provides the class factory for its COM class by implementing the IClassFactory interface (or the IClassFactory2 interface). Here is the class object for class CoStack (some details have been left out of this example code).

 class CoStackClassObject : public IClassFactory { public:     STDMETHOD(QueryInterface) (REFIID riid, void** ppv);     STDMETHOD_(ULONG,AddRef) ();     STDMETHOD_(ULONG,Release) ();     STDMETHOD(CreateInstance) (IUnknown* outer, REFIID riid, void** ppv);     STDMETHOD(LockServer) (BOOL b); }; STDMETHODIMP CoStackClassObject::QueryInterface(REFIID riid, void** ppv) {     if (riid == IID_IUnknown || riid == IID_IClassFactory)         *ppv = this;     else {         *ppv = NULL;         return E_NOINTERFACE;     }     AddRef();     return S_OK; } STDMETHODIMP_(ULONG) CoStackClassObject::AddRef() { /*...*/ } STDMETHODIMP_(ULONG) CoStackClassObject::Release() { /*...*/ } STDMETHODIMP CoStackClassObject::CreateInstance                  (IUnknown* outer, REFIID riid, void** ppv) {     *ppv = NULL;     if (outer != NULL)         return CLASS_E_NOAGGREGATION;     CoStack* p = new CoStack;     if (p == NULL)         return E_OUTOFMEMORY;     p->AddRef();     HRESULT hr = p->QueryInterface(riid, ppv);     p->Release();     return hr; } STDMETHODIMP CoStackClassObject::LockServer(BOOL b) { /*...*/ } 

The class object is registered in the system registry (see FAQ 34.06) and is used during object creation. Callers create COM objects by calling the API function CoGetClassObject, obtaining a pointer to the class object's IClassFactory interface, and calling CreateInstance (callers may also call CoCreateInstance, which is a helper function that performs this series of actions).

Every COM class and its class object live within a COM server (on Windows this means either a DLL or an EXE), which contains the executable code that implements the class and the class object. When the COM server is a DLL, COM locates and loads the DLL when the caller creates the first object of any class that lives within the server. When the COM server is an EXE, COM locates and runs the EXE when the caller creates the first object of any class that lives within the server.

Most COM classes implement more than one interface. Typically this is done in C++ using nested classes or multiple inheritance. For details, refer to a book dedicated to COM.



C++ FAQs
C Programming FAQs: Frequently Asked Questions
ISBN: 0201845199
EAN: 2147483647
Year: 2005
Pages: 566
Authors: Steve Summit

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net