//
you're reading...
COM, Programming Issues/Tips

Generic Helper Function for Creating SAFEARRAYs.

1. Introduction.

1.1 SAFEARRAYs are very useful objects in COM. They are containers for data.

1.2 SAFEARRAYs are also used for transporting arrays between managed and unmanaged code.

1.3 Although standard APIs are used to create and fill SAFEARRAYs, these APIs are not the easiest nor the most intuitive to use. Additional structures like SAFEARRAYBOUND nned to be understood and used.

1.4 This article presents a generic helper function that can be conveniently used to create and fill values into a one-dimensional SAFEARRAY of various VARTYPEs.

2. The Generic Helper Function for Creating SAFEARRAYs.

2.1 Listed below is CreateSafeArray(), a generic templated function that can be used to create SAFEARRAYs of any VARTYPE :

template <class T, VARTYPE v>
void CreateSafeArray
(
  T* lpT,
  ULONG ulSize,
  SAFEARRAY** ppSafeArrayReceiver
)
{
  if ((lpT == NULL) || (ppSafeArrayReceiver == NULL))
  {
	// lpT and ppSafeArrayReceiver each cannot be NULL.
	return;
  }

  HRESULT hrRetTemp = S_OK;
  SAFEARRAYBOUND rgsabound[1];
  ULONG	ulIndex = 0;
  long lRet = 0;

  // Initialise receiver.
  *ppSafeArrayReceiver = NULL;

  rgsabound[0].lLbound = 0;
  rgsabound[0].cElements = ulSize;

  *ppSafeArrayReceiver = (SAFEARRAY*)SafeArrayCreate
  (
    (VARTYPE)v,
    (unsigned int)1,
    (SAFEARRAYBOUND*)rgsabound
  );

  for (ulIndex = 0; ulIndex < ulSize; ulIndex++)
  {
	long lIndexVector[1];

	lIndexVector[0] = ulIndex;

    	SafeArrayPutElement
	(
	  (SAFEARRAY*)(*ppSafeArrayReceiver),
	  (long*)lIndexVector,
	  (void*)(&(lpT[ulIndex]))
    	);
  }

  return;
}

The following is a general synopsis of this function :

  • The CreateSafeArray() function will produce a one-dimensional SAFEARRAY the VARTYPE of which can be set via a template parameter.
  • It takes 2 template parameters “T” and “v”.
  • “T” is meant to indicate the type of the input array which is to be used to fill the output SAFEARRAY.
  • “v” indicates the VARTYPE of the SAFEARRAY data.
  • The function itself takes 3 function parameters : lpT, ulSize and ppSafeArrayReceiver.
  • lpT is a pointer to an array of “T” types.
  • ulSize is the size of the input lpT array.
  • ppSafeArrayReceiver is a double pointer to a SAFEARRAY. It is an “out” parameter which is returned from the function.
  • Both lpT and ppSafeArrayReceiver cannot be NULL. NULL values for either one of them renders the function quite pointless.
  • The standard SafeArrayCreate() and SafeArrayPutElement() APIs are used to create and fill the SAFEARRAY to be returned.

3. Sample Client Code.

3.1 Listed below is a sample client function which tests the use of the CreateSafeArray() function to produce a one-dimensional SAFEARRAY :

void DoTest()
{
	// Create an STL vector.
	std::vector<byte>	byte_vector;

	// Fill the vector with 10 byte values
	// from 0 through 9.
	for (int i = 0; i < 10; i++)
	{
		byte_vector.push_back((byte)i);
	}

	SAFEARRAY* pSafeArrayOfBytes = NULL;

	// Create a SAFEARRAY out of the vector of bytes.
	CreateSafeArray<byte, VT_UI1>(&(byte_vector[0]), byte_vector.size(), &pSafeArrayOfBytes);

	VARTYPE vt;

	// Get the VARTYPE of the data stored inside the SAFEARRAY.
	SafeArrayGetVartype(pSafeArrayOfBytes, &vt);

	// This should be VT_UI1 (byte).
	_ASSERT(vt == VT_UI1);

	// Get the number of dimensions in the SAFEARRAY.
	UINT uiDim = SafeArrayGetDim(pSafeArrayOfBytes);

	// This should be just 1.
	_ASSERT(uiDim == 1);

	long lLBound = 0, lUBound = 0;

	// Obtain the lower and upper bounds of dimension 1
	// of the SAFEARRAY.
	SafeArrayGetLBound(pSafeArrayOfBytes, 1, &lLBound);
	SafeArrayGetUBound(pSafeArrayOfBytes, 1, &lUBound);

	// Display the value of each and every array element.
	for (long l = lLBound; l <= lUBound; l++)
	{
		byte byValue = 0;

		SafeArrayGetElement(pSafeArrayOfBytes, &l, (void*)&byValue);
		printf ("%d\r\n", byValue);
	}

	// Finally destroy the SAFEARRAY.
	SafeArrayDestroy(pSafeArrayOfBytes);
	pSafeArrayOfBytes = NULL;
}

The following is a general synopsis of the DoTest() function :

  • It creates an STL vector of bytes and fills it with 10 byte values from 0 through 9.
  • It then calls the CreateSafeArray() function.
  • The first template parameter “byte” indicates that the first parameter to the function will be an array of bytes.
  • The second template parameter “VT_UI1” indicates that the SAFEARRAY to be returned is to contain elements of VARTYPE VT_UI1 (unsigned integer of 1 byte).
  • A pointer to the first element of the byte vector is passed as the first parameter.
  • The std::vector::size() function is passed as the second parameter. This indicates the number of elements in the vector.
  • The address of a pointer to a SAFEARRAY (pSafeArrayOfBytes) is passed as the third parameter.
  • When CreateSafeArray() returned, we use various SAFEARRAY APIs to test the returned SAFEARRAY.
  • SafeArrayGetVartype() is used to get the VARTYPE of the data stored inside the SAFEARRAY. This must be VT_UI1.
  • SafeArrayGetDim() is used to get the number of dimensions in the SAFEARRAY. This must be 1.
  • SafeArrayGetLBound() and SafeArrayGetUBound() are used to obtain the lower and upper bounds of the SAFEARRAY.
  • The lower and upper bound values and the SafeArrayGetElement() API are then used to loop through the values contained in the SAFEARRAY. These values are displayed on the console output.

4. In Conclusion.

4.1 I hope this small helper function will be of great help to C++ developers who frequently create SAFEARRAYs.

4.2 The success of a function like CreateSafeArray() demonstrates the tremendous utility of C++ templates and its far-reaching potential in the simplification and generalization of algorithms.

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Discussion

One thought on “Generic Helper Function for Creating SAFEARRAYs.

  1. Instead SafeArrayPutElement and SafeArrayGetElement, you can use SafeArrayAccessData.

    http://msdn.microsoft.com/en-us/library/windows/desktop/ms221620(v=vs.85).aspx

    Posted by Cobaia | May 28, 2014, 2:56 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: