Monday, June 23, 2008

A Containment Sample

Introduction
Trying to learn about reuse mechanisms that can be applied in COM, I searched a lot on the net to find a very simple example on Containment, for starters. I found a lot of theory on the net, but not any simple example. Keeping in mind the starters, I will try to explain the steps involved in attaining Containment. I am not a good technical writer, but I will try to do my best.

Definition
Containment is a reuse mechanism. In this process of reuse mechanism, the outer component acts as a mediator between the client and the inner component, to delegate the calls. This mechanism makes sure that the inner component's interfaces are not exposed directly to the client.

We will build a ComplexCalculator component that exposes Add and Multiply functionalities through its interface. There is an existing component named “Calculator” that exposes an “Add” method through its interface, ISimpelMath. You can download the Calculator from the link above. I thought: why write code to get an existing functionality, “Add”? So, we will develop a new component, ComplexCalculator, using the existing component, Calculator.

Now, it’s enough for us to worry about “Multiply”.

Steps
Create a new ATL project, name it “ComplexCalculator”, and select the type as DLL.
Insert a new ATL object, name it as “ComplexMath”, and click OK.
Select the Class Wizard tab, right click on IComplexMath, and select “Add method”.
Method Name: Mul
Parameters: [in] long a, [in] long b, [out, retval] long* result
Add the following code:
STDMETHODIMP CComplexMath:: Mul (long a, long b, long *result)
{
*result = a * b;
return S_OK;
}
Select the Class Wizard tab, right click on CComplexMath, and select Implement Interface.
Click the “Add type library” button->Browse, select the Calculator.tlb, and click Open.
Open the ComplexCalculator.idl file and do the following changes:
Changes are indicated in bold letters.

library COMPLEXCALCULATORLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
import "Calculator.idl";
//import the Calculator’s IDL file.
//If not present in local dir, specify the path.
[
uuid(FB30F62F-4DF3-47CD-A67F-50E0CF7C7B67),
helpstring("ComplexMath Class")
]
coclass ComplexMath
{
[default] interface IComplexMath;
interface ISimpleMath;
//Add the interface name to the coclass
//so that it can be exposed to the client.
};
};
Open the ComplexMath.h file and add the following code:
The outerCOM (ComplexMath) creates an object of innerCOM (SimpleMath).

ISimpleMath* SimpleMathptr;

//Override the FinalConstruct to get the
//interface pointer of the Inner Component.
HRESULT FinalConstruct()
{
return CoCreateInstance(__uuidof(SimpleMath),NULL,
CLSCTX_INPROC_SERVER,
__uuidof(ISimpleMath),
(void**)&SimpleMathptr);
}
void FinalRelease()
{
SimpleMathptr->Release();
}
Change the Add method code in the ComplexMath.h as follows:
This is how the outerCOM (ComplexMath) delegates the calls to the innerCOM (SimpleMath).

STDMETHOD(Add)(LONG a, LONG b, LONG * result)
{
SimpleMathptr->Add(a,b,result);
return S_OK;
}
That’s it….

The outer component contains the inner component object. The user feels as if he is using the ComplexCalculator even when he queries for the SimpleMath interface.

Let's quickly build a client and make sure the containment relation works properly.

Client
Create a dialog based MFC application and place a button on the dialog. In the StdAfx.h, add the following:

#import "ComplexCalculator.tlb"
// If this is not present in the current dir, specify the path.
Add the following code to the OnButton event:

using namespace COMPLEXCALCULATORLib;
void CContainmentClientDlg::OnButton1()
{
CoInitialize(NULL);
CString str;
long res1;
IComplexMathPtr objComplex(__uuidof(ComplexMath));
//Use the smart
//pointer to get
//ComplexMath

res1=objComplex->Mul(10,20);
str.Format("Mul is=%d",res1);
AfxMessageBox(str);

ISimpleMathPtr objSimple;
objSimple=objComplex; //Use the assignment operator on the
//smart Pointer which will take care of
//query interface to SimpleMath interface.
long m = objSimple->Add(10,20);
str.Format("Add of 10,20 is=%d",m);
AfxMessageBox(str);
objComplex=NULL;
objSimple=NULL;

CoUninitialize();
}
Register the DLLs before you use the source code.


License

No comments: