Утечка памяти ATL C с safearray ccomobjects

#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 . Затем добавление его в массив увеличит его, и он останется живым до освобождения.