我正在编写一个具有大量接口和方法的COM服务器。并且大多数方法具有BSTR作为参数以及用于返回的本地参数。一个片段看起来像
更新5:
真实的代码。这是根据DB填充Object数组的特定条件从一堆数据中获取的。
STDMETHODIMP CApplication::GetAllAddressByName(BSTR bstrParamName, VARIANT *vAdddresses)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
//check the Database server connection
COleSafeArray saAddress;
HRESULT hr;
// Prepare the SQL Strings dan Query the DB
long lRecCount = table.GetRecordCount();
if (lRecCount > 0)
{
//create one dimension safe array for putting details
saAddress.CreateOneDim(VT_DISPATCH,lRecCount);
IAddress *pIAddress = NULL;
//retrieve details
for(long iRet = table.MoveFirst(),iCount=0; !iRet; iRet = table.MoveNext(),iCount++)
{
CComObject<CAddress> *pAddress;
hr = CComObject<CAddress>::CreateInstance(&pAddress);
if (SUCCEEDED(hr))
{
BSTR bstrStreet = ::SysAllocString(table.m_pRecordData->Street);
pAddress->put_StreetName(bstrStreet);
BSTR bstrCity = ::SysAllocString(table.m_pRecordData->City);
pAddress->put_CityName(bstrCity);
}
hr = pAddress->QueryInterface(IID_IAddress, (void**)&pIAddress);
if(SUCCEEDED(hr))
{
saAddress.PutElement(&iCount,pIAddress);
}
}
*vAdddresses=saAddress.Detach();
}
table.Close();
return S_OK;
}
STDMETHODIMP CAddress::put_CityName(BSTR bstrCityName)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
// m_sCityName is of CComBSTR Type
m_sCityName.Empty();//free the old string
m_sCityName = ::SysAllocString(bstrCityName);//create the memory for the new string
return S_OK;
}
问题出在Memory Freeing部分。代码在任何Win XP机器上都能正常工作,但是当涉及到WIN2K8 R2和WIN7时,代码崩溃并指向:: SysFreeString()作为罪魁祸首。 MSDN不适合解决方案。
有人可以帮助找到合适的解决方案吗?
提前多多感谢:)
更新1:
我已尝试按照原始BSTR位置的建议使用CComBSTR,使用直接CString初始化并排除SysFreeString()。但是对于我的麻烦,在超出范围时系统调用SysFreeString()再次导致崩溃:(
更新2: 使用相同的CComBSTR,我尝试使用SysAllocString()进行分配,问题仍然存在:(
更新3: 我厌倦了所有的选择,在和平中,我只有一个问题
是否有必要通过SysFreeString()释放BSTR 使用SysAllocString()/ string.AllocSysString()?
分配
更新4: 我错过了提供有关崩溃的信息。当我尝试调试COM服务器崩溃时出现错误
“可能的堆腐败”
。请帮助我离开这里.. :(
答案 0 :(得分:2)
// Now All Things are packed in to the Object
obj.Name = bstrName;
obj.Name2 = bstrname2;
我不太明白你说的是什么意思,因为你只是复制指向字符串的指针,并且当你调用SysFreeString obj.Name和obj.Name2指向无效时记忆块。虽然这段代码不安全,但看起来你的问题来源是CFoo类。您应该向我们展示您的代码的更多细节
我建议您使用CComBSTR类,它将负责释放内存。
<强>更新强>
#include <atlbase.h>
using namespace ATL;
...
{
CComBSTR bstrname(_T("Some Name"));
CComBSTR bstrname2(_T("Another Name"));
// Here one may work with these variables if needed
...
// Copy the local values to the Obj's member Variable
bstrname.Copy(&obj.Name);
bstrname2.Copy(&obj.Name2);
}
<强> UPDATE2 强> 首先,应该使用SysFreeString释放bstrCity和bstrStreetName,或者在此块中使用CComBSTR:
if (SUCCEEDED(hr))
{
BSTR bstrStreet = ::SysAllocString(table.m_pRecordData->Street);
pAddress->put_StreetName(bstrStreet);
BSTR bstrCity = ::SysAllocString(table.m_pRecordData->City);
pAddress->put_CityName(bstrCity);
// SysFreeString(bstrStreet)
// SysFreeString(bstrCity)
}
考虑放大循环的条件!iRet与iCount&lt; lRecCount。
for(...; !iRet /* && (iCount < lRecCount) */; ...)
另外,这里:
m_sCityName = ::SysAllocString(bstrCityName);
你分配内存但从未在内部发布CComBSTR&amp; operator =(OLESTR ..)自己分配一个新存储。应该重写如下:
m_sCityName = bstrCityName;
其他一切,对我来说都很好
<强> UPDATE3 强> 好吧,堆损坏通常是在分配的内存块之外写入一些值的结果。假设你分配一个长度为5的数组并将一些值放到第6个位置
答案 1 :(得分:1)
最后,我找到了代码中发生的堆腐败的真正原因。
IAddress / CAddress的put_StreetName / put_CityName按以下方式设计。
STDMETHODIMP CAddress::put_CityName(BSTR bstrCityName)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
m_sCityName.Empty();
TrimBSTR(bstrCityName);
m_sCityName = ::SysAllocString(bstrCityName);
return S_OK;
}
BSTR CAddress::TrimBSTR(BSTR bstrString)
{
CString sTmpStr(bstrString);
sTmpStr.TrimLeft();
sTmpStr.TrimRight();
SysReAllocString(&bstrString,sTmpStr); // The Devilish Line
}
恶魔的代码行是造成记忆地狱的真正罪魁祸首。
是什么造成了麻烦?
在这行代码中,作为参数传递的BSTR字符串来自另一个应用程序,而实际内存位于另一个域中。所以系统正在尝试重新分配字符串。无论是否成功,都会尝试从原始应用程序/域中的内存中清除,从而导致崩溃。
还有什么尚未解决?
为什么同一段代码在Win XP和Windows XP中没有崩溃一次 旧系统? :(
感谢所有花时间回答并解决问题的人:)