3

我发现自己需要帮助。现在,我对 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, &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:好的,我在这段代码周围看了一会儿,当我注释掉变体装箱时,泄漏似乎消失了。问题是我从 safearrays 上的 Visual Studio 示例中借用了这段代码。那么,有没有人知道发生了什么:

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

……该怎么办?

4

1 回答 1

2

大多数(如果不是全部)ATL 的包装器都遵循 COM 约定——它们复制/添加引用传入的数据,因为它们的析构函数将销毁/释放。

因此,当您将 detached 传递SAFEARRAYCComVariant的构造函数时,它将制作 的副本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对,并让Convertrefcount == 0 执行。然后将它添加到数组中会增加它,它会活着直到被释放。

于 2011-12-11T16:52:54.340 回答