#c #com #memory-leaks #atl #safearray
#c #com #утечки памяти #atl #safearray
Вопрос:
Мне нужна помощь. Теперь я не так уж плохо знаком с C , но объединение его с ATL обеспечивает совершенно новый уровень путаницы. В любом случае, моя проблема: мне (наконец) удалось вернуть массив объектов в моем COM-методе вызывающему C #. Но после «тестирования» (многократного выполнения указанной функции несколько раз) Я обнаружил небольшую утечку памяти.
Выдержка из IDL:
...
interface IDISControl : IDispatch{
...
[id(12)] HRESULT GetNets([out,retval] VARIANT* nets);
};
Выдержка из заголовка:
...
STDMETHOD(GetNets)(VARIANT* nets);
...
Код:
STDMETHODIMP CDISControl::GetNets(VARIANT* nets)
{
SNet *netz;
int32_t num;
int result, i;
result = DIS_GetNetNum(securityHandle, amp;num);
netz = new SNet[num];
result = DIS_GetNet(securityHandle, netz, num); //getting some data
CComSafeArray<IDispatch*> netArray;
CComObject<CDISNet> *net;
CComVariant *var;
netArray.Create(num, 0);
for (i = 0;i<num;i ){
CComObject<CDISNet>::CreateInstance(amp;net);
if (net == NULL)
return S_FALSE;
net->AddRef();
net->Convert(netz[i]);
netArray[i] = net;
net->Release();
net = NULL;
}
CComVariant val(netArray.Detach());
val.Detach(nets);
delete [] netz;
netArray.Destroy();
return S_OK;
}
Я создаю экземпляры объектов CDISNet и помещаю в них некоторые данные (Convert()). Я помещаю их в свой safearray и выпускаю. Насколько я понимаю, ответственность за их уничтожение передается safearray. После этого я помещаю массив в ВАРИАНТ, чтобы я мог заполнить свой параметр [out, retval]. Поскольку это параметр out, ответственность за уничтожение должна быть передана вызывающему (в моем случае C #, т. Е. Его сборщику мусора). Я удаляю свой динамический массив «netz» и уничтожаю оболочку safearray.
Итак, чего мне не хватает? Что осталось выделенным? (Этот проект действительно заставляет меня ценить все удобства .net).
Справка. Пожалуйста.
РЕДАКТИРОВАТЬ: дальнейшая отладка показала мне, что проблема, безусловно, в моих объектах CComObject. Они не освобождаются. Если I delete net;
на каждой итерации массив также теряет данные. Я не уверен, как это исправить…
EDIT2: Хорошо, я немного покопался в этом коде, и утечка, похоже, исчезла, когда я прокомментировал вариант бокса. Проблема в том, что я позаимствовал этот фрагмент кода из примера Visual Studio в safearrays. Итак, кто-нибудь имеет представление о том, что происходит:
CComVariant val(netArray.Detach());
val.Detach(nets);
… и что с этим делать?
Комментарии:
1. Возможно, отладочная куча может помочь. Еще одна вещь, на которую я бы обратил особое внимание, — это ваши
detach
вызовы, уверены ли вы, что это делает то, что вы думаете?2. Хорошо… CComXXX — это классы-оболочки, отсоединение должно просто передавать данные. Отсоединение CComSafeArrays дает safearray, отсоединение CComVariants дает вариант. Я проверю кучу отладки.
3. Когда-
ComObject<CDISNet>::CreateInstance
либо происходит сбой, потому что я думаю, что в этом случае у вас может быть утечка памяти (если она завершается сi > 0
ошибкой).4. Нет, все работает так, как должно. Я отладил кучу и обнаружил, что количество блоков на каждой итерации увеличивается на четыре (что является значением ‘num’). Я не знаю. Источники, которые я смог найти, очень отрывочны в отношении этих вещей…
Ответ №1:
Большинство, если не все, оболочек ATL следуют соглашениям COM — они копируют / добавляют входящие данные, поскольку их деструктор уничтожает / освобождает.
Поэтому, когда вы передаете свой отдельный SAFEARRAY
CComVariant
конструктор to, он создает копию SAFEARRAY
, что означает, что никто не освобождает результат от CComSafeArray::Detach
.
В подобных случаях мне всегда было проще полностью отказаться от оболочки для возвращаемого значения;
nets->vt = VT_ARRAY | VT_DISPATCH;
nets->parray = netArray.Detach();
Альтернативой было бы передать ваш CComSafeArray
конструктор непосредственно в CComVariant
конструктор, без вызова Detach
, но это будет стоить вам дополнительной копии. Я бы предпочел необработанный доступ, представленный выше, поскольку он самый простой и дешевый.
Что касается вашего первого редактирования, то то, что вы делаете, AddRef/Release
прекрасно, хотя и несколько излишне. CComObject::CreateInstance
возвращает объект с количеством ссылок 0, поэтому AddRef
он приведет его к 1, а затем присвоит ему CComSafeArray
значение 2, а следующее Release
значение вернется к 1.
Если Convert
метод ничего не делает с подсчетом ссылок на объект (например QueryInterface
, сам или передает себя другому COM-методу), вы можете пропустить AddRef/Release
пару и разрешить Convert
выполнение с refcount == 0 . Затем добавление его в массив увеличит его, и он останется живым до освобождения.