问题
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?
回答1:
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.
来源:https://stackoverflow.com/questions/4204488/marshalling-a-variant-back-from-com-to-c-client