Hadden Uff
Hadden Uff

Reputation: 99

Passing a CComSafeArray across a COM boundary

I'm trying to (i) get a safearray of longs across a COM boundary and (ii) use CComSafeArray for convenience.

My problem is that I'm getting unpredictable crashes after setting the COM property (see pPrologue->EligibleProducts = var; below). I've found it very hard to understand how to use CComSafeArray from the Microsoft documentation, can anyone shed any light? Thank you in advance!

In the IDL I have:

[propget, id(1), helpstring("property EligibleProducts")] HRESULT EligibleProducts([out, retval] VARIANT* pVal);
[propput, id(1), helpstring("property EligibleProducts")] HRESULT EligibleProducts([in] VARIANT newVal);

My server code is:

STDMETHODIMP CPrologue::put_EligibleProducts(VARIANT newVal)
{
    HRESULT hr = E_FAIL;

    AFX_MANAGE_STATE(AfxGetStaticModuleState())

    //start by clearing out any existing data
    m_EligibleProducts.clear();

    if(newVal.vt | (VT_ARRAY & VT_I4))
    {
        //construct a wrapper class with the passed in SAFEARRAY
         CComSafeArray<long> wrapper;
         wrapper.Attach(newVal.parray);

         int iProductID = 0;

         //loop through products and add them to our vector
         int iCount = wrapper.GetCount();
         for(int iIndex = 0; iIndex < iCount; iIndex++)
         {
             iProductID = wrapper.GetAt(iIndex);
             if(iProductID > 0)
             {
                 m_EligibleProducts.push_back(iProductID);
             }
         }

         hr = S_OK;

    return hr;
}

And my calling code is:

                VARIANT var;
                ::VariantInit(&var);
                var.vt = VT_ARRAY | VT_I4;

                CComSafeArray<long> wrapper;

                for(std::vector<long>::const_iterator it = products.begin(); it != products.end(); it++)
                {
                    wrapper.Add(*it);
                }

                //get the SAFEARRAY from the wrapper
                var.parray = wrapper.Detach();

                //and store it on the appropriate business object
                IProloguePtr pPrologue = pCustomer->Prologue;    
                **pPrologue->EligibleProducts = var;**

                //clean up the variant (and hence SAFEARRAY)
                ::VariantClear(&var);

Upvotes: 3

Views: 1250

Answers (1)

Igor Tandetnik
Igor Tandetnik

Reputation: 52621

if(newVal.vt | (VT_ARRAY & VT_I4))

This doesn't do what you seem to think it does. This condition is always true. You are looking for if (newVal.vt == VT_ARRAY | VT_I4)

In put_EligibleProducts, you have Attached CComSafeArray to the pointer inside VARIANT, but you haven't detached it. When wrapper goes out of scope, it destroys the safearray. Then the caller attempts to destroy it the second time, by means of VariantClear. This is the immediate cause of your difficulties.

Upvotes: 3

Related Questions