2012-02-27 59 views
2

我写它有很多接口和方法的COM服务器。而且大多数方法都以BSTR作为参数,并将其用作返回的本地参数。一个片段看起来像使用:: SysFreeString()释放BSTR。更多的平台依赖?

更新5:

真正的代码。这从基于特定条件的数据库中获取数据库来填充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:

我已经使用的CComBSTR按照建议在原BSTR的地方尝试过,用直接的CString的初始化和排除SysFreeString( )。但我的麻烦,在获取超出范围的系统调用SysFreeString()这又导致崩溃:(

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

更新3: 我厌倦了所有的选项,并在和平,我铭记唯一的问题

是否有必要通过自由的SysFreeString BSTR(),它是使用SysAllocString()/ string.AllocSysString()分配?

更新4: 我错过了提供有关崩溃的信息。当我试图调试COM服务器崩溃了错误说

“可能堆损坏”

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

+0

IMO,分配器应该是更自由。也就是说,调用FooMethod的客户端应该执行bstrName的分配。它也应该释放它。另外,你的代码很混乱,你有bstrname和bstrName。也许obj.Name = bstrName;应该真的是obj.Name = bstrname; ? – 2012-02-27 14:54:37

+2

不应该代码类似于'obj.Name = :: SysAllocString(someString);'。另外,考虑使用'CComBSTR'或'_bstr_t'代替原始的BSTR,会为您节省很多麻烦。 – 2012-02-27 15:14:55

+1

是的,有必要释放分配的内存块。请为我们提供使用'obj'变量的完整代码。否则,这是不可能的,以帮助您找到问题 – 2012-02-29 19:51:24

回答

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的一个时间和 旧的系统? :(

感谢所有谁把自己的时间来回答和解决我的问题:)

+4

您的分析已经过去了大约一英里。 BSTR可以通过系统跨越公寓边界进行编组,而不会有任何问题。你的问题是'SysReAllocString'更新**临时**('&bstrString')。当'TrimBSTR'返回时'bstrCityName'指向一个可能不再有效的字符串。为了修正bug,可以改变'TrimBSTR'的签名来取'BSTR *'或从'TrimBSTR'返回'bstrString'的更新值(甚至可以编译?)。 – IInspectable 2014-04-22 09:19:18

2
// Now All Things are packed in to the Object 
obj.Name = bstrName; 
obj.Name2 = bstrname2; 

我不太明白你说,事情都挤满因为你只是复制指针指向字符串的意思,而在那一刻,当你调用SysFreeString obj.Name和obj.Name2将指向内存无效块。虽然这个代码是不是安全,它看起来像,如果你的问题的根源是阶级Cfoo的。你应该告诉我们你的代码的更多细节

我建议你使用一个CComBSTR类,它将负责释放内存。

UPDATE

#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 首先是一个应该释放bstrCity和bstrStreetName用SysFreeString或内代替该块使用的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 with iCount < lRecCount。

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

而且,在这里:

m_sCityName = ::SysAllocString(bstrCityName); 

你分配内存,但永远不会释放它,因为内部的CComBSTR &运营商=(OLESTR ..)分配一个新的存储本身。每个人都应该改写如下:

m_sCityName = bstrCityName; 

一切,看起来很不错,我

UPDATE3 好,堆腐败往往是分配的内存块以外写一些值的结果。假设你分配一个长度为5的数组,并把一些值放到第6个位置

+0

感谢Kids-Fox的答案。正如你所提到的,包装正在填充Class的成员变量,因为这将会被返回。对于我的谜题,即使在XP机器上,也不会崩溃,只能在现代操作系统上崩溃。 – 2012-02-28 04:07:30

+0

编辑并为您提供了一个明确的代码.. – 2012-02-28 04:55:16

+0

它可能不会因为巧合而崩溃。在COM中有一个通用的经验法则:一个函数应该为其返回值和所有输出值分配内存。因此FooMethod不应该释放内存。看看我添加到我的回答中的代码片段 – 2012-02-28 07:01:16