
SafeArrays  For the Beginner Author: A. Abdul Azeez Introduction The following article is meant as a primer to Safe Arrays. This can be used by any beginner to Safe Arrays. It consists of a documentation of the most frequently used functions, their explanations followed by examples. For more details about the other functions and a detailed technical explanation about introduction to safe arrays please refer to the MSDN documentation. A Safe Array is defined as an array that contains information about the number of dimensions and the bounds of its dimensions. This is one of the most commonly used COM data types. We will see some of the most commonly used SafeArray methods now. Fundamental Safe Array Methods: SafeArrayMethod  SafeArrayCreate Any array needs to be created and allocated memory before it is used. This function creates a new array descriptor, allocates and initializes the data for the array, and returns a pointer to the new array descriptor. The function signature is as follows,
SAFEARRAY SafeArrayCreate( VARTYPE vt, unsigned int cDims, SAFEARRRAYBOUND FAR* rgsabound );
Parameters vt The base type of the array (the VARTYPE of each element of the array). This indicates the type of data that can be stored in the array. Hence if you specify VT_BSTR it means that the array contains members of type BSTR's. If you are unsure about the data type that can be stored you can make it VT_VARIANT. The VARTYPE is restricted to a subset of the variant types. Neither the VT_ARRAY nor the VT_BYREF flag can be set. VT_EMPTY and VT_NULL are not valid base types for the array. All other types are legal. cDims Number of dimensions in the array. The number cannot be changed after the array is created. For example, a 1D array would have the value of cDims as 1 and a 2d array would have the value of cDims as 2 and so on. rgsabound Pointer to a vector of bounds (one for each dimension) to allocate for the array. This is a SAFEARRAYBOUND structure. The structure is defined as follows.
typedef struct tagSAFEARRAYBOUND {
unsigned long cElements;
long lLbound;
} SAFEARRAYBOUND;
lLbound  Denotes the lower bound of the particular dimension and 1. Example for SAFEARRAYBOUND To declare a 1D safearray which has 5 elements and starts from 0, the SAFEARRAYBOUND structure has to be initialized as follows.
SAFEARRAYBOUND rgsabound[1]; //Denotes number of dimensions
rgsabound[0]. lLbound =0;
rgsabound[0]. CElements=5;
To declare a 2D safearray which has 5 elements in the first dimension and starts from 0 and 10 elements in the second dimension starting from 0, the SAFEARRAYBOUND structure has to be initialized as follows.
SAFEARRAYBOUND rgsabound2D[2]; //Denotes number of dimensions
rgsabound2D[0]. lLbound =0;
rgsabound2D[0]. CElements=5;
rgsabound2D[1]. lLbound =0;
rgsabound2D[1]. CElements=10;
2. Example for SafeArrayCreate using the SAFEARRAYBOUND (Example 1) created above
/*
Create a 1D SafeArray of variants with the SAFEARRAYBOUND from the previous example
*/
SafeArrayCreate(VT_VARIANT, 1, rgsabound)
SafeArrayMethod  SafeArrayCreateVector An easier method to construct a 1D safearray whose lower bound is always zero is this method wherein there is no initialization of the SAFEARRAYBOUND structure etc. The function signature is as follows,
SAFEARRAY SafeArrayCreateVector(
VARTYPE vt,
long lLbound,
unsigned int cElements
);
Creates a onedimensional array whose lower bound is always zero. Parameters vt The base type of the array (the VARTYPE of each element of the array). This indicates the type of data that can be stored in the array. Hence if you specify VT_BSTR it means that the array contains members of type BSTR's. If you are unsure about the data type that can be stored you can make it VT_VARIANT. The VARTYPE is restricted to a subset of the variant types. Neither the VT_ARRAY nor the VT_BYREF flag can be set. VT_EMPTY and VT_NULL are not valid base types for the array. All other types are legal. lLbound The lower bound for the array. Can be negative. cElements The number of elements in the array. Return Value Points to the array descriptor, or Null if the array could not be created. 3. Example for SafeArrayCreateVector
// Creates a safearray of variants containing 3 elements with the lower bound starting at 0
SAFEARRAY *psa;
long NoOfElements=3;
psa=SafeArrayCreateVector(VT_VARIANT, 0, NoOfElements);
SafeArrayMethod  SafeArrayGetDim This method is used to find out the number of dimensions with which the safe array has been created. The function signature is as follows,
UINT SafeArrayGetDim( SAFEARRAY FAR* psa );
Parameter psa Pointer to an array descriptor created by SafeArrayCreate. Return Value Returns the number of dimensions in the array. 4. Example for SafeArrayGetDim
// Try getting the dimension for the safearray created in Example 3
SafeArrayMethod  SafeArrayGetElement This method is used to retrieve a single element from an already populated safe array. The function signature is as follows,
HRESULT SafeArrayGetElement( SAFEARRAY FAR* psa, long FAR* rgIndices, void FAR* pv );
Parameters psa Pointer to an array descriptor created by SafeArrayCreate. rgIndices Pointer to a vector of indexes for each dimension of the array. The rightmost (least significant) dimension is rgIndices[0]. The leftmost dimension is stored at rgIndices[psa>cDims  1]. Look at the examples given below for accessing the elements of a 1D array and a 2D array. pv Pointer to the location to place the element of the array. 5. Example for a 1D SafeArrayGetElement
long lbound;
hr=SafeArrayGetLBound(psa, 1, &lbound); //Get the lower bound of the safearray
long ubound;
hr=SafeArrayGetUBound(psa, 1, &ubound); // Get the upper bound of the safearray
//Navigate through the safe array
for(;lbound<=ubound;lbound++)
{
CComVariant val;
SafeArrayGetElement(psa, &lbound, (void *) & val);
::MessageBox(0,OLE2T(val.bstrVal), "Value",0);
}
6. Example for a 2D SafeArrayGetElement
long aiIndex[2]; //Declare array equal to the number of bounds
psa=SafeArrayCreate(VT_VARIANT, 2, rgsabound); //Create the safe array
/*
aiIndex[0]  Represents the starting index of the row
aiIndex[1]  Represents the starting index of the column
Navigate through the safe array by getting the elements
of each row and then moving forward to the next row
always taking care to check that aiIndex[0] is less
than the maximum row bound and aiIndex[1] is less than
the maximum column bound
*/
for(aiIndex[0] = 0; aiIndex[0] %lt; 2; aiIndex[0]++)
{
for(aiIndex[1] = 0; aiIndex[1] < 2; aiIndex[1]++)
{
CComVariant val;
SafeArrayGetElement(psa, aiIndex, (void *) & val);
::MessageBox(0,OLE2T(val.bstrVal), "Value",0);
}
}
UINT dim;
dim=SafeArrayGetDim(psa); // Will return 1
SafeArrayMethod  SafeArrayPutElement This method is used to assign a single element to an already populated safe array. The function signature is as follows,
HRESULT SafeArrayPutElement( SAFEARRAY FAR* psa, long FAR* rgIndices, void FAR* pv );
Parameters psa Pointer to an array descriptor created by SafeArrayCreate. rgIndices Pointer to a vector of indexes for each dimension of the array. The rightmost (least significant) dimension is rgIndices[0]. The leftmost dimension is stored at rgIndices[psa>cDims  1]. Look at the examples given below for putting elements into a 1D array and a 2D array. pv Pointer to the data to assign to the array. The variant types VT_DISPATCH, VT_UNKNOWN, and VT_BSTR are pointers, and do not require another level of indirection. 7. Example for a 1D SafeArrayPutElement
long count=0;
CComVariant name="Satyam";
hr=SafeArrayPutElement(psa, &count, (void *) &name);
//Always increment the count
count++;
name="Computers";
hr=SafeArrayPutElement(psa, &count, (void *) &name);
8. Example for a 2D SafeArrayPutElement
USES_CONVERSION;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[2];
rgsabound[0].cElements = 2;
rgsabound[0].lLbound = 0;
rgsabound[1].cElements = 2;
rgsabound[1].lLbound = 0;
psa=SafeArrayCreate(VT_VARIANT, 2, rgsabound); //Create a 2D safe array
long aiIndex[2];
/*
aiIndex[0]  Represents the starting index of the row
aiIndex[1]  Represents the starting index of the column
Navigate through the safe array by putting the elements of
each row and then moving forward to the next row always
taking care to check that aiIndex[0] is less than the
maximum row bound and aiIndex[1] is less than the maximum column bound
*/
for(aiIndex[0] = 0; aiIndex[0] < 2; aiIndex[0]++)
{
for(aiIndex[1] = 0; aiIndex[1] < 2; aiIndex[1]++)
{
CComVariant var="Satyam";
hr=SafeArrayPutElement(psa,aiIndex,& var);
}
}
Summary The article covered some of the main methods of Safe Arrays and provided comprehensive examples of how to put them to use. Some of the confusion about safe arrays results from a lack of clear understanding of how to use them in the correct situations. I hope this article will serve as a ready reckoner for people who are new to this topic or for those who are well versed with this topic but may need some kind of an online reference. For further information refer to the source code of published articles Further reading About the author The author is a professional developer with around 2 years of development experience. His areas of interest include AS/400, Lotus Domino, Java, ASP, HTML, JavaScript, VBScript, C++ and COM. Please visit this author in Author Central. Rate and Review this article To review this article and rate it out of 5, please send an email to [email protected], using the article heading as the sublect line of your message. Contribute to IDR: To contribute an article to IDR, a click here.

