user206705
user206705

Reputation:

Marshalling a VARIANT back from COM to C++ client

I'm attempting to marshal a safearray of BSTRs from a COM object, back to a C++ client application.

My IDL definition of the function involved:

[id(5), helpstring("method GetStreams")] 
    HRESULT GetStreams( [out,retval] VARIANT* pvarStreamNames );

Here's my implementation of the GetStreams() function:

STDMETHODIMP CArchiveManager::GetStreams(VARIANT* pvarStreamNames)
{   
CComSafeArray<BSTR, VT_BSTR>    saStreamNames;
CComVariant                     varOutNames;

Stream* pNext       = NULL;
int     nNumStreams = m_Streams.NumStreams();

if( nNumStreams == 0 )
    return S_OK;

for(int x = 0; x < nNumStreams; x++)
{
    pNext = m_Streams.GetAt(x); 
    if( pNext )             
        saStreamNames.Add( pNext->StreamName() );   
}

if( saStreamNames.GetCount() > 0 )
{
    varOutNames.vt      = VT_ARRAY;
    varOutNames.parray  = saStreamNames.m_psa;

    varOutNames.Detach(pvarStreamNames);
}

return S_OK;
}

Here's how the C++ client program calls the GetStreams() function:

VARIANT varStreamNames;
hr = spArchiveMgr->GetStreams( &varStreamNames );

I trace through the program using the interactive debugger and everything appears to work correctly (safearray populates correctly, etc) until the GetStreams() function returns. At that point, I get an "unhandled exception reading location" message.

advice on how to debug/solve this?

Upvotes: 1

Views: 1321

Answers (1)

sharptooth
sharptooth

Reputation: 170489

There're two problems. The first one is

 VARIANT varStreamNames;

is unitialized, so when

varOutNames.Detach(pvarStreamNames);

runs it calls VariantClear() on an uninitialized variable and this leads to undefined behavior - your program crashes.

You have to call VariantInit() on varStreamNames before invoking the COM method or just use CComVariant type for varStreamNames.

The second one is:

CComSafeArray<BSTR, VT_BSTR>    saStreamNames;
CComVariant                     varOutNames;

varOutNames.vt      = VT_ARRAY;
varOutNames.parray  = saStreamNames.m_psa;

perfoms shallow copy of the safe array - now both saStreamNames and varOutNames own the safe array and so when saStreamNames gets destroyed at the end of the scope the safe array is released.

Since you've copied the same safe array address into pvarStreamNames you've now got a variant with a dangling safe array pointer. Trying to access that safe array is undefined behavior. You should use Detach() method of CComSafeArray to release ownership.

Upvotes: 3

Related Questions