Освобождение BSTR с помощью::SysFreeString(). Больше зависит от платформы?
Я пишу 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: я устал от всех вариантов, и в мире я имею в виду только вопрос
Необходимо ли освобождать BSTR через SysFreeString(), который был выделен с помощью SysAllocString()/string.AllocSysString()?
Обновление 4: я пропустил, чтобы предоставить информацию о сбое. Когда я попытался отладить, COM-сервер упал с ошибкой, говоря
"Возможное повреждение кучи"
, Пожалуйста, помогите мне отсюда..:(
2 ответа
Наконец, я нашел реальную причину повреждения кучи, которое произошло в коде.
Метод put_StreetName/put_CityName объекта IAddress/CAddress разработан следующим образом.
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 и более старых системах?:(
Спасибо всем, кто нашел время, чтобы ответить и решить мою проблему:)
// 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);
}
ОБНОВЛЕНИЕ2 Прежде всего нужно освободить 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 с iCount Также здесь: Вы выделяете память, но никогда не освобождаете ее, поскольку внутренне CComBSTR& operator = (OLESTR ..) выделяет само новое хранилище. Надо переписать так: Все остальное, выглядит хорошо для меня ОБНОВЛЕНИЕ3 Ну, повреждение кучи часто является следствием записи некоторых значений вне выделенного блока памяти. Допустим, вы выделили массив длиной 5 и поместили какое-то значение в 6-ю позициюfor(...; !iRet /* && (iCount < lRecCount) */; ...)
m_sCityName = ::SysAllocString(bstrCityName);
m_sCityName = bstrCityName;