Simplifying SAFEARRAY programming with CComSafeArray

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.

4 thoughts on “Simplifying SAFEARRAY programming with CComSafeArray”

  1. Thank you for the article. I find it confusing to return array of BSTR to a caller. Can I bundle ragged array of strings in CComSafeArray and is the caller responsible for freeing the members?

  2. It is also easy to extract data from an already created SAFEARRAY (please correct where necessary):


    STDMETHOD(put_MyArray)(SAFEARRAY* value)
    {
    long long sum = 0;
    try
    {
    // Attach a safe array of int's
    CComSafeArray saData(value);

    // Pull out some data...
    for(int i = 0; i < saData.GetCount(); ++i)
    {
    sum += saData[i];
    }

    // Release
    saData.Detach();
    }
    catch (const CAtlException &e) { return e; }
    return S_OK;
    }

  3. Edit to the above (the constructor makes a copy):

    change:
    // Attach a safe array of int’s
    CComSafeArray saData(value);

    to:
    // Attach a safe array of int’s
    CComSafeArray saData;
    saData.Attach(value);

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>