Using a Multidimensional SAFEARRAY to pass data across from COM objects

The SAFEARRAY is a standard way to pass arrays or collections between COM objects.
Using COM’s standard marshaller , we can pass a collection of OLE Automation compatible
data types using SAFEARRAYs.
Multidimensional SAFARRAYs help us pass various automation compatible data types through the same array

Assume , we own an icecream parlor and would like to give our customers a list of
all the icecream flavors and their prices.
Now wouldn’t it be nice to package an array with both the flavor (represented by a
BSTR) and the price (represented as a float data type).
Notice that we have two diferent data types one a float and another a BSTR and yet
we package them neatly in a SAFEARRAY and send them across using COM’s standard marshaller.

Our data structure should look something like this:

flavor 1 flavor 2 flavor n
Flavors (0,0) (0,1) ………. (0,n)

price 1 price 2 price n
Prices (1,0) (1,0) ………. (1,n)

You can extend this whole analogy to actually pack every record in a database
table into an N-dimensional SAFEARRAY where N represents the number of fields
in the table.
While wading through some of the SAFEARRAY documentation , you may happen to come across
the term array descriptor.
An array descriptor is actually a pointer to an allocated SAFEARRAY structure.

Time now to have a look at our Icecream parlor example.

//////////////////////////////////////////////////////////////////////////////
//Function : GetFlavorsWithPrices (Example for Multidimensional SAFEARRAY)
//Parameters: VARIANT (out parameter that contains a SAFEARRAY of VARIANTs
// helping us to pass BSTR and float in the same array)
//Return Type : HRESULT
//////////////////////////////////////////////////////////////////////////////

STDMETHODIMP CIceCreamOrder::GetFlavorsWithPrices(VARIANT *pVariant)
{
// TODO: Add your implementation code here

//Initialize the bounds for the array
//Ours is a 2 dimensional array
SAFEARRAYBOUND safeBound[2];

//Set up the bounds for the first index
//That’s the number of flavors that we have
safeBound[0].cElements = m_vecIcecreamFlavors.size();
safeBound[0].lLbound = 0;

//Set up the bounds for the second index
safeBound[1].cElements = m_vecIcecreamPrices.size();
safeBound[1].lLbound = 0 ;

///Initialize the VARIANT
VariantInit(pVariant);
//The array type is VARIANT
//Storage will accomodate a BSTR and a float
pVariant->vt = VT_VARIANT | VT_ARRAY;
pVariant->parray = SafeArrayCreate(VT_VARIANT,2,safeBound);

//Initialize the vector iterators
std::vector::iterator iterFlavor;
std::vector<float>::iterator iterPrices;

//Used for indicating indexes in the Multidimensional array
long lDimension[2];
int iFlavorIndex = 0;

//Start iteration
iterPrices = m_vecIcecreamPrices.begin();
iterFlavor = m_vecIcecreamFlavors.begin();

//Iterate thru the list of flavors and prices
while(iterFlavor != m_vecIcecreamFlavors.end())
{

//Put the Element at (0,0), (0,1) , (0,2) ,………….(0,n)
lDimension[1] = iFlavorIndex;
lDimension[0] = 0;
CComVariant variantFlavor(SysAllocString((*iterFlavor).m_str));
SafeArrayPutElement(pVariant->parray,lDimension,&variantFlavor);

//Put the Element at (1,0), (1,1) , (1,2) ,………….(1,n)
lDimension[1] = iFlavorIndex;
lDimension[0] = 1;
CComVariant variantPrices(*iterPrices);
SafeArrayPutElement(pVariant->parray,lDimension,&variantPrices);

iFlavorIndex++;
iterPrices++;
iterFlavor++;

}

return S_OK;

}

Download demo project (Server) – 32 KB

Download demo project (Client) – 18 KB

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read