Introduction
This article about techniques of reusing COM objects in client application or in another COM object. To understand it you have to know some basics of COM and ATL, mostly you have to know what is IUnknown
interface and it's methods (QueryInerface(), AddRef(),Release()
), and how a simple ATL object works and is implemented. So first you can take a look at articles in CodeProject about basics of ATL.
Object Reuse in C++
As you know there are two way of reusing an existing class in C++, Containment and Inheritance.Containment is declaration of an object within a class scope.Let's take a look at an example:
class CMyCalss
{
protected:
CBaseclass m_base;
public:
CMyClass(){m_base.basefunction();}
DoSomething(){m_base.DoSomething();}
}
You can see that CMyClass
contains an instance of the pre-existing "CBaseclass
" and it can control access to the CBaseclass
object.
Another technique is Inheritance:
class CMyClass public CBaseClass
{
public:
CMyClass(){basefunction();}
}
Here you have access to public and protected member functions of CBaseclass
as the member of CMyClass
.
Ok, I know that you know all these things so let's talk about COM
COM
COM supports containment but it does not support inheritance in the way that C++ does.In the example above CMyClass
inherits functionality implemented by the CBaseclass
.The public or protected member functions of the CBaseclass
are available to the CMyClass
.This kind of inheritance is known as implementation inheritance.
Now take a look at an example of inheritance in COM:
IMyInreface:public IUnknown
{
}
What's the difference?
By deriving one interface from another,you specify the structure of the vtable that will hold pointer to the instantiated methods. In the example above it means IMyInterface
, besides having its own methods, has three function of IUnknown
interface too: QueryInterface,AddRef,Release
)
There is nothing the same as virtual function you see in C++ and MFC. Here, IMyInterface
has QueryInterface
method of IUnknown
interface, if you want another QueryInterface
method you have to implement IUnknown
interface yourself and change QueryInterface
of it,and then inherit IMyInterface
from your own IUnknown
interface. This kind of inheritance is called Interface inheritance.Clear? Hmm!
Containment
Containment is having one component as a member variable of another.(Do you remember the containment in C++?) The outer (controlling,containing) component makes use of the inner(contained) component,but does not directly expose the contained component to its client. The outer component manage the lifetime of the inner component. It creates the inner component with CoCtreateInstance()
when it initializes itself(in FinalConstruct()
), and releases the inner component's interface pointer when it uninitialize itself (in FinalRelease()
). I can say outer component isn't doing anything very different from what a standard client would do.
Aggregation
In aggregation the interface of the inner object directly expose to the client without collection of wrapper.A client creates the outer object and when it asks QueryInterface()
for an interface supported by the inner object, it gets passed a pointer to the inner object's interface.
One advantage of aggregation over containment is that it requiers much less knowledge of the inner component on the part of the person implementing the outer component.Another is that bypassing the wrapper layer makes the component that bit more efficient.
The disadvantage of aggregation is that the inner object can only be implemented in a DLL that is directly loaded into the same apartment as the outer object.There is no cross-apartment,cross-process or cross-machine aggregation.
For aggregation to work ,the inner object must be aggregatable ,it must be written to support aggregation. To be aggregatable,the inner component must have two distinc implementations of the three IUnknown
methods,QueryInterface(),AddRef()
and Release()
.One version is called non-delegating and is used by outer object,another one is called delegating which used by client.(Because the client of the outer object can now obtian interface pointers exposed by the inner object)
Aggregation can be used to perform the 'black box' reuse of code.You can view 'black box' reuse from two perspectives: First,the outer object is adding functionality to the inner object,or else it's purposely creating the inner object to implement one or more interfaces.In both cases ,the identity of the composite object is the outer object,the outer object can aggregate many different objects,each with their own identities,but they will be combined into one single object with one identity.
It can be useful to write objects aggregatable.Doing so has no effect at all on the object's instrinsic usefulness, but it'll increase size of code a little(the COM runtime,for example).
Aggregation is a little bit complicated topic,although the ATL wizard handles the hard parts of aggregation for you.
References
- Beginning ATL3 COM Programming by Dr.Richard Grimes
- Desktop application with Microsoft Visual C++6.0 MCSD Training Kit
- MSDN