Admittedly, pretty much all of the COM development I did pre-.NET was in Visual Basic. But on occasion, I broke out Visual C++ to make some things happen out of necessity. Mostly because of APIs that were not COM-based that we needed to interact with our software written in VB. For example, early versions of FrontPage did not support COM for doing things like inserting Webbots. Instead you had to use the FrontPage SDK with Visual C++ and include the Webbot.h header file. Being extremely naive about the world of COM in C++, I hacked together whatever I could from MSDN examples to get things working. I remember one nightmare in particular: dealing the raw SAFEARRAY and VARIANT types doing something like this...
//Create the safe array.
psa_Attributes = SafeArrayCreate(VT_BSTR, 2, rgsabound);
if(psa_Attributes == NULL)
{
//error handling
}
//Put the values from the lpcszAttribs array into the SAFEARRAY
for(lOutLoop = 0; lOutLoop < lNumKeys+1; lOutLoop++)
for(lInLoop = 0; lInLoop < 2; lInLoop++)
{
//Place the value into the array, if no more items then
//place a blank value into the SAFEARRAY so it contains 50 items.
if(lOutLoop <= lNumKeys)
{
lpOLEstring = A2OLE(lpcszAttribs[lOutLoop][lInLoop]);
}
else
{
lpOLEstring = A2OLE(NULL);
}
//Get the binary string value of the OLE String value.
bstr = SysAllocString(lpOLEstring);
//place the loop value into the idxvariable for use in the
//SafeArrayPutElement() function. This is an index for the safe array.
idx[0] = lOutLoop;
idx[1] = lInLoop;
hr = SafeArrayPutElement(psa_Attributes, idx, bstr);
//free resources
::SysFreeString(bstr);
}
//initialize variant variable
VariantInit(&var);
// Specify the variant type and value
var.vt = VT_ARRAY | VT_BSTR;
var.parray = psa_Attributes;
//Create the VB ActiveX DLL (COM object)
if(EDComponent.CreateDispatch(lpszCLS) == FALSE)
{
//error handling
}
//Invoke the Method
VBComponent.SomeMethod(&var);
VBComponent.DetachDispatch();
VBComponent.ReleaseDispatch();
Pretty ugly. Not only that I didn't even include the code to create and destroy the COM component in the above code, which can be even more ugly without using ATL. After reading my Programming WPF book which Chris Sells co-authored, I decided to read ATL Internals. I'm still actually in the middle of reading it, but I had one of those "Ah ha!" moments and decided to play around with this kind of scenario again using the ATL templates. Wow, what an amazing difference that I missed out on! :-)
Let's say you have a VB6 ActiveX DLL project with a CUtility class that looks like this...
Public Function Add(ByVal a As Integer, ByVal b As Integer) As Integer
Add = a + b
End Function
Public Function SayHello(ByVal name As String) As String
SayHello = "Hello " & name
End Function
Public Function TestVariant(ByVal v As Variant) As String
TestVariant = v(0, 0) & v(0, 1)
End Function
...you can use ATL templates to make your life a lot easier: CComPtr, CComSafeArray, and CComVariant.
#include "stdafx.h"
#include "atlsafe.h"
#import "progid:SeanCOM.CUtility" named_guids no_namespace
int _tmain(int argc, _TCHAR* argv[])
{
//COM init
HRESULT hr = CoInitialize(NULL);
CComPtr<_CUtility> utility;
hr = utility.CoCreateInstance(CLSID_CUtility);
//Call the Add method
short sum = utility->Add(2, 2);
printf("%d\r\n", sum);
//Call the SayHello method
BSTR helloMessage = utility->SayHello(OLESTR("Sean"));
printf("%S\r\n", helloMessage);
::SysFreeString(helloMessage);
//Create an array that can be placed in a variant
CComSafeArray<VARIANT> safeArray;
SAFEARRAYBOUND bounds[2];
bounds[0].cElements = 2;
bounds[0].lLbound = 0;
bounds[1].cElements = 2;
bounds[1].lLbound = 0;
safeArray.Create(bounds ,2);
//put two items in the safe array: "Hello ", "World"
long index[2];
index[0] = 0;
index[1] = 0;
safeArray.MultiDimSetAt(index, CComVariant(L"Hello "));
index[1] = 1;
safeArray.MultiDimSetAt(index, CComVariant(L"World"));
//put the safe array in a variant and call the TestVariant method
CComVariant testArg(safeArray.m_psa);
BSTR retStr = utility->TestVariant(&testArg);
printf("%S\r\n", retStr);
::SysFreeString(retStr);
//COM cleanup
utility.Release();
CoUninitialize();
return 0;
}
Much nicer. I'm doing some bug fixes a project that uses ATL right now, and this book will be helpful. I'm looking forward to reading the rest of ATL Internals and posting a review when I'm finished. Also, I'd like to mention that I read Essential COM by Don Box last week just out of curiosity before delving into the ATL book, and it just rocked! The first couple of chapters alone is worth the price of the book if you haven't already read it. OK, maybe it's not as exciting as reading about WPF or DLINQ, but still quite interesting if you are a geek who likes to read. :-)