5

我正在编写一个具有大量接口和方法的 COM 服务器。并且大多数方法都将 BSTR 作为参数并作为用于返回的本地参数。一个片段看起来像

更新 5:

真正的代码。这会根据数据库的特定条件从一堆数据中获取,以填充对象数组。

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

问题在于内存释放部分。该代码在任何 Win XP 机器上都可以正常工作,但是当涉及到 WIN2K8 R2 和 WIN7 时,代码会崩溃并指向 ::SysFreeString() 作为罪魁祸首。MSDN 不足以解决此问题。

任何人都可以帮助找到正确的解决方案吗?

提前非常感谢:)

更新1:

我已经尝试按照建议使用 CComBSTR 代替原始 BSTR,使用直接 CString 初始化并排除 SysFreeString()。但是对于我的麻烦,在超出范围时,系统正在调用 SysFreeString() 再次导致崩溃:(

更新 2: 使用相同的 CComBSTR 我尝试使用 SysAllocString() 进行分配,问题仍然相同:(

更新 3: 我厌倦了所有的选择,我心里只想着一个问题

是否有必要通过使用 SysAllocString()/string.AllocSysString() 分配的 SysFreeString() 来释放 BSTR?

更新 4: 我错过了提供有关崩溃的信息。当我尝试调试 COM 服务器时出现错误提示

“可能的堆损坏”

. 请帮我离开这里.. :(

4

2 回答 2

4
// 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)
} 

考虑用 iCount < lRecCount 放大循环的条件 !iRet。

for(...; !iRet /* && (iCount < lRecCount) */; ...)

也在这里:

m_sCityName = ::SysAllocString(bstrCityName);

您分配内存但从不释放它,因为内部 CComBSTR& operator = (OLESTR ..) 自己分配了一个新的存储空间。应该重写如下:

m_sCityName = bstrCityName;

其他的,对我来说看起来不错

UPDATE3 好吧,堆损坏通常是在分配的内存块之外写入一些值的结果。假设您分配了一个长度为 5 的数组并将一些值放在第 6 个位置

于 2012-02-27T14:56:20.547 回答
2

最后我找到了代码中发生堆损坏的真正原因。

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 和旧系统中一次不会崩溃?:(

感谢所有花时间回答和解决我的问题的人:)

于 2013-07-26T12:22:09.847 回答