Simplifying SAFEARRAY programming with CComSafeArray

EDIT 2017-03-01: Please read my MSDN Magazine article:

Simplify Safe Array Programming in C++ with CComSafeArray

for a detailed discussion.

 

Original blog post follows:

There are some questions on the MSDN forums about passing arrays between C++ and C#. This argument is rich and there are several options. There was a recent thread on this topic on the Visual C++ MFC and ATL MSDN Forum. As requested by the OP, a simple C++ code using raw C arrays with corresponding C# P/Invoke signature was showed.

Another option consists of using SAFEARRAY’s. While programming SAFEARRAY’s using raw C functions (e.g. SafeArrayCreate, SafeArrayLock, etc.) can lead to some boilerplate code, ATL offers a convenient CComSafeArray helper class that makes the life of SAFEARRAY programmers much easier.

CComSafeArray is a C++ template class; suppose that a SAFEARRAY of LONG’s is requested: it can be simply created with code like this:

       CComSafeArray<LONG> saData(count);

 

Thanks to CComSafeArray, SAFEARRAY items can be accessed using classic operator[], like this:

        for (int i = 0; i < count; i++)

            saData[i] = (i+1)*10;

 

And to transfer the created SAFEARRAY to the caller (assuming there is a SAFEARRAY ** ppsaData output parameter), a simple code like this works fine:

 

      *ppsaData = saData.Detach();

 

It is worth noting that CComSafeArray can throw C++ exceptions (instances of CAtlException class) on error. So, if the method in which the CComSafeArray is used returns an HRESULT (as common to COM methods), it is safe to guard the code using CComSafeArray in a try/catch block, to convert CAtlException instances back to HRESULT error codes, that can cross COM method boundaries, e.g. :

    try

    {

        // Create a safe array of LONG’s

        CComSafeArray<LONG> saData(count);

 

        // Fill with some data…

        for (int i = 0; i < count; i++)

            saData[i] = (i+1)*10;

       

        // Copy to output parameter

        *ppsaData = saData.Detach();

    }

    catch (const CAtlException & e)

    {

        // Trap errors signaled as C++ ATL exceptions

        // and return the corresponding HRESULT

 

        return e// implicit cast to HRESULT

    }

 

    // All right

    return S_OK;

 

A sample solution illustrating these concepts is attached to this blog post.