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
	//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));

		//Put the Element at (1,0), (1,1)  , (1,2) ,.............(1,n)
		lDimension[1] = iFlavorIndex;
		lDimension[0] = 1;
		CComVariant variantPrices(*iterPrices);

	return S_OK;


Download demo project (Server) - 32 KB

Download demo project (Client) - 18 KB