我正在尝试从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()函数返回。那时,我得到一个“未处理的异常读取位置”消息。
关于如何调试/解决此问题的建议?
答案 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()
方法来释放所有权。