Утечка памяти в ATL C ++ с безопасным набором объектов ccomobjects - PullRequest
3 голосов
/ 14 октября 2011

Я нуждаюсь в помощи.Теперь я не так уж и незнаком с C ++, но объединение его с ATL дает совершенно новый уровень путаницы.В любом случае, моя проблема: мне (наконец-то) удалось вернуть массив объектов в моем COM-методе в C # caller.Но после «тестирования» (многократного запуска указанной функции) я обнаружил небольшую утечку памяти.

Выдержка 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, &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(&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 ()).Я положил их в свой сейф и выпустил.Насколько я понимаю, ответственность за их уничтожение передается на страховой ящик.После этого я помещаю массив в VARIANT, чтобы заполнить свой параметр [out, retval].Поскольку это выходной параметр, ответственность за уничтожение должна быть передана вызывающей стороне (в моем случае C #, то есть его GarbageCollector).Я избавляюсь от своего динамического массива 'netz' и уничтожаю оболочку Safearray.

Так чего мне не хватает?Что осталось выделить?(Этот проект действительно заставляет меня ценить все удобства .net).

Помощь.Пожалуйста.

РЕДАКТИРОВАТЬ: Дальнейшая отладка показала мне, что проблема определенно в моих объектах CComObject.Они не освобождаются.Если я delete net; в каждой итерации, массив также теряет данные.Я не уверен, как это исправить ...

EDIT2: Хорошо, я немного поковырялся в этом коде, и утечка, похоже, прошла, когда я закомментировал вариантный бокс.Проблема в том, что я позаимствовал этот фрагмент кода из примера Visual Studio на безопасных носителях.Итак, кто-нибудь знает, что случилось:

CComVariant val(netArray.Detach());
val.Detach(nets);

... и что с этим делать?

1 Ответ

2 голосов
/ 11 декабря 2011

Большинство, если не все, оболочки ATL следуют соглашениям COM - они копируют / добавляют входящие данные, поскольку их деструктор уничтожит / освободит.

Поэтому, когда вы передадите свой отсоединенный SAFEARRAY конструктору CComVariant, он создаст копию 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. Затем добавление его в массив увеличит его, и оно останется в живых до тех пор, пока не будет освобождено.

...