将VARIANT从COM转发回C ++客户端

时间:2010-11-17 12:48:46

标签: c++ com marshalling atl

我正在尝试从COM对象编组BSTR的安全阵列,回到C ++客户端应用程序。

我所涉及的功能的IDL定义:

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

这是我对GetStreams()函数的实现:

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;
}

以下是C ++客户端程序调用GetStreams()函数的方法:

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

我使用交互式调试器跟踪程序,所有内容似乎都正常工作(safearray正确填充等),直到GetStreams()函数返回。那时,我得到一个“未处理的异常读取位置”消息。

关于如何调试/解决此问题的建议?

1 个答案:

答案 0 :(得分:3)

有两个问题。第一个是

 VARIANT varStreamNames;

是单元化的,所以

varOutNames.Detach(pvarStreamNames);

在未初始化的变量上运行它调用VariantClear(),这会导致未定义的行为 - 程序崩溃。

在调用COM方法之前,您必须在VariantInit()上调用varStreamNames,或者只使用CComVariant的{​​{1}}类型。

第二个是:

varStreamNames

执行安全数组的浅拷贝 - 现在CComSafeArray<BSTR, VT_BSTR> saStreamNames; CComVariant varOutNames; varOutNames.vt = VT_ARRAY; varOutNames.parray = saStreamNames.m_psa; saStreamNames都拥有安全数组,所以当varOutNames在范围的末尾被销毁时,安全数组被释放。

由于您已将相同的安全数组地址复制到saStreamNames,因此您现在获得了一个带有悬空安全数组指针的变体。尝试访问该安全数组是未定义的行为。您应该使用pvarStreamNames Detach()方法来释放所有权。