com 释放方法不降至0

COM+ Release Method Not Dropping to 0

本文关键字:释放 方法 com      更新时间:2023-10-16

我希望与您分享我面临的问题。长话短说,我有这个小的代码(仅测试目的(:

    int main ()
    {
        IXMLDOMDocument *pDoc(nullptr);
        CoCreateInstance(CLSID_DOMDocument, nullptr, CLSCTX_ALL, IID_IXMLDOMDocument, reinterpret_cast<LPVOID*>(&pDoc));
        DWORD d = pDoc->AddRef();
        std::cout << "pDoc: add ptr=" << pDoc << " d=" << d << std::endl;
        d = pDoc->Release();
        std::cout << "pDoc: rel ptr=" << pDoc << " d=" << d << std::endl;
        IUnknown *pUnk(nullptr);
        pDoc->QueryInterface(IID_IUnknown, reinterpret_cast<LPVOID*>(&pUnk));
        d = pUnk->AddRef();
        std::cout << "pUnk: add ptr=" << pUnk << " d=" << d << std::endl;
        d = pUnk->Release();
        std::cout << "pUnk: rel ptr=" << pUnk << " d=" << d << std::endl;
        /*Release objects*/
        d = pUnk->Release();
        std::cout << "pUnk: rel ptr=" << pUnk << " d=" << d << std::endl;
        d = pDoc->Release();
        std::cout << "pDoc: rel ptr=" << pDoc << " d=" << d << std::endl;
        return 0;
    }

我期望最后2个 cout打印0作为返回的计数,在我看到的地方:

pDoc: add ptr=004A4628 d=2
pDoc: rel ptr=004A4628 d=1
pUnk: add ptr=004A3A10 d=4
pUnk: rel ptr=004A3A10 d=3
pUnk: rel ptr=004A3A10 d=2
pDoc: rel ptr=004A4628 d=0

为什么QueryInterface将我送给我一个IUnknown哪个内部计数以3开始?为什么IUnknown对象的最后一个Release未返回0的方法?

我可能会缺少什么?

为什么 QueryInterface返回了我一个 IUnknown,哪个内部计数在3?

中开始

pDocpUnk本质上是访问单个对象的两种方法。由于它是一个对象,所以这反映在参考计数中,并解释了为什么它不在1中开始。

但是,从该解释中,您可能会期望参考计数从2而不是3开始。事实始于3,可能是由DOMDocument使用的内部辅助对象引起的,用于处理IUnknown接口,其中该内部辅助对象在其中维护附加参考。

为什么IUnknown对象的最后一个Release未返回0的方法?

出于相同的原因:pDocpUnk本质上是同一对象。由于您仍然有一个未发布的参考(可以通过pDoc访问(,因此该对象仍然存在。

根据AddRefRelease的MDSN文档,返回值仅用于测试目的。这可能不是对实际参考数量的准确反映;特别是对0进行测试并不能保证对象完成。

在什么条件下,iunknown :: addref方法返回0?。

当对象查询其IUnknown接口时,com期望每次返回同一对象,以确保身份测试工作(您可以测试两个接口是否指向同一对象在内存中,通过查询IUnknown的两个接口,然后比较查询指针(。这在QueryInterface()文档中指出:

对于任何一个对象,该对象接口上的IUnknown接口的特定查询必须始终返回相同的指针值。这使客户能够通过使用IID_IUnknown调用QueryInterface并比较结果来确定两个指针是否指向同一组件。具体来说,不是针对IUnknown以外的接口(即使是相同指针的相同接口(的查询,必须返回相同的指针值。

因此,当通过QueryInterface()请求DomDocument对象的IUnknown接口时,人们会期望参考计数会增加1,而不是2。在这种情况下,您应该在您的输出:

int main ()
{
    IXMLDOMDocument *pDoc(nullptr);
    CoCreateInstance(CLSID_DOMDocument, nullptr, CLSCTX_ALL, IID_IXMLDOMDocument, reinterpret_cast<LPVOID*>(&pDoc));
    // DOMDoc refcnt=1
    DWORD d = pDoc->AddRef();
    // DOMDoc refcnt=2
    std::cout << "pDoc: add ptr=" << pDoc << " d=" << d << std::endl;
    d = pDoc->Release();
    // DOMDoc refcnt=1
    std::cout << "pDoc: rel ptr=" << pDoc << " d=" << d << std::endl;
    IUnknown *pUnk(nullptr);
    pDoc->QueryInterface(IID_IUnknown, reinterpret_cast<LPVOID*>(&pUnk));
    // DOMDoc refcnt=2
    d = pUnk->AddRef();
    // DOMDoc refcnt=3
    std::cout << "pUnk: add ptr=" << pUnk << " d=" << d << std::endl;
    d = pUnk->Release();
    // DOMDoc refcnt=2
    std::cout << "pUnk: rel ptr=" << pUnk << " d=" << d << std::endl;
    /*Release objects*/
    d = pUnk->Release();
    // DOMDoc refcnt=1
    std::cout << "pUnk: rel ptr=" << pUnk << " d=" << d << std::endl;
    d = pDoc->Release();
    // DOMDoc refcnt=0
    std::cout << "pDoc: rel ptr=" << pDoc << " d=" << d << std::endl;
    return 0;
}

但是,实际上,当您查询domdocument对象的 IUnknown接口时,显然会对对象进行额外的内部引用,并且直到发布了对查询的IUnknown接口的所有引用之前,该额外的引用才会发布。这将说明您看到的数字:

int main ()
{
    IXMLDOMDocument *pDoc(nullptr);
    CoCreateInstance(CLSID_DOMDocument, nullptr, CLSCTX_ALL, IID_IXMLDOMDocument, reinterpret_cast<LPVOID*>(&pDoc));
    // DOMDoc refcnt=1
    DWORD d = pDoc->AddRef();
    // DOMDoc refcnt=2
    std::cout << "pDoc: add ptr=" << pDoc << " d=" << d << std::endl;
    d = pDoc->Release();
    // DOMDoc refcnt=1
    std::cout << "pDoc: rel ptr=" << pDoc << " d=" << d << std::endl;
    IUnknown *pUnk(nullptr);
    pDoc->QueryInterface(IID_IUnknown, reinterpret_cast<LPVOID*>(&pUnk));
    // DOMDoc refcnt=3, not 2!
    d = pUnk->AddRef();
    // DOMDoc refcnt=4
    std::cout << "pUnk: add ptr=" << pUnk << " d=" << d << std::endl;
    d = pUnk->Release();
    // DOMDoc refcnt=3
    std::cout << "pUnk: rel ptr=" << pUnk << " d=" << d << std::endl;
    /*Release objects*/
    d = pUnk->Release();
    // DOMDoc refcnt=1, not 2!
    std::cout << "pUnk: rel ptr=" << pUnk << " d=" << d << std::endl;
    d = pDoc->Release();
    // DOMDoc refcnt=0
    std::cout << "pDoc: rel ptr=" << pDoc << " d=" << d << std::endl;
    return 0;
}

在查询IUnknown时,DomDocument对象可能会返回指针转移到内部辅助对象,并且该辅助对象正在返回AddRef()Release()中拥有的DomDocument的参考计数,而不是返回其自己的参考计数。

您要查看的内容 - 是com contregation ,此处 pUnk inne> inne 对象, pDoc is contregable 对象。同样有趣的是,当您查询 innion 对象上的 IXMLDOMDocument接口时 - 他每次分配 new cotregable object,它实现此界面


在开始创建实用程序函数以进行对象的打印参考计数,并比较com视图中的2个对象指针(该指针的二进制值可能有所不同,但对于两个对象,IUnknown相同(

(
ULONG GetRefCount(IUnknown *pUnk, BOOLEAN bPrint = TRUE)
{
    pUnk->AddRef();
    ULONG d = pUnk->Release();
    if (bPrint) DbgPrint("%p>%un", pUnk, d);
    return d;
}
BOOLEAN IsSameObjects(IUnknown *p, IUnknown *q)
{
    BOOLEAN f = FALSE;
    IUnknown *Unk1, *Unk2;
    if (0 <= p->QueryInterface(IID_PPV_ARGS(&Unk1)))
    {
        if (0 <= q->QueryInterface(IID_PPV_ARGS(&Unk2)))
        {
            f = Unk1 == Unk2;
            Unk2->Release();
        }
        Unk1->Release();
    }
    DbgPrint("%p[%u] %s %p[%u]n", p, GetRefCount(p, FALSE), f ? "==" : "!=", q, GetRefCount(q, FALSE));
    return f;
}

现在让我们先进行测试:

void test1 ()
{
    IXMLDOMDocument *pDoc, *pDoc2;
    if (0 <= CoCreateInstance(__uuidof(DOMDocument), 0, CLSCTX_ALL, IID_PPV_ARGS(&pDoc)))
    {
        GetRefCount(pDoc);
        IUnknown *pUnk;
        if (0 <= pDoc->QueryInterface(IID_PPV_ARGS(&pUnk)))
        {
            IsSameObjects(pDoc, pUnk);
            if (0 <= pUnk->QueryInterface(IID_PPV_ARGS(&pDoc2)))
            {
                IsSameObjects(pDoc, pDoc2);
                GetRefCount(pUnk);
                pDoc2->Release();
                GetRefCount(pUnk);
            }
            pUnk->Release();
        }
        GetRefCount(pDoc);
        DbgPrint("Final Release=%un", pDoc->Release());
    }
}

它输出:

000001DD8DCE71A0>1
000001DD8DCE71A0[1] == 000001DD8DCE5950[3]
000001DD8DCE71A0[1] == 000001DD8DCE7270[1]
000001DD8DCE5950>4
000001DD8DCE5950>3
000001DD8DCE71A0>1
Final Release=0

在这里可见pUnkpDoc(pDoc2(指向不同的内存位置,但这是相同的com对象


基于此,让我们进行更多的对称测试:

void test2 ()
{
    IUnknown *pUnk;
    if (0 <= CoCreateInstance(__uuidof(DOMDocument), 0, CLSCTX_ALL, IID_PPV_ARGS(&pUnk)))
    {
        GetRefCount(pUnk);
        IXMLDOMDocument *pDoc, *pDoc2;
        if (0 <= pUnk->QueryInterface(IID_PPV_ARGS(&pDoc)))
        {
            IsSameObjects(pUnk, pDoc);
            if (0 <= pUnk->QueryInterface(IID_PPV_ARGS(&pDoc2)))
            {
                IsSameObjects(pDoc2, pDoc);
                GetRefCount(pUnk);
                pDoc2->Release();
            }
            if (0 <= pDoc->QueryInterface(IID_PPV_ARGS(&pDoc2)))
            {
                IsSameObjects(pDoc2, pDoc);
                GetRefCount(pUnk);
                pDoc2->Release();
            }
            pDoc->Release();
        }
        GetRefCount(pUnk);
        DbgPrint("Final Release=%un", pUnk->Release());
    }
}

和输出:

000001DD8DCE5950>1
000001DD8DCE5950[3] == 000001DD8DCE7270[1]
000001DD8DCE71A0[1] == 000001DD8DCE7270[1]
000001DD8DCE5950>4
000001DD8DCE7270[2] == 000001DD8DCE7270[2]
000001DD8DCE5950>3
000001DD8DCE5950>1
Final Release=0

在这里最好的可见,最初创建了内在对象。每当我们在此对象上查询IXMLDOMDocument时 - 创建了新的汇总对象,并返回指针。


如何在代码中实现?只需演示

struct __declspec(novtable) __declspec(uuid("78979DF1-A166-4797-AF2B-21BBE60D0B2E")) IDemo : public IUnknown
{
    virtual void Demo() = 0;
};
class CDemo : public IDemo
{
    IUnknown* _pUnkOuter;
    LONG _dwRef;
    ~CDemo()
    {
        DbgPrint("%s<%p>n", __FUNCTION__, this);
        _pUnkOuter->Release();
    }
public:
    CDemo(IUnknown* pUnkOuter) : _pUnkOuter(pUnkOuter)
    {
        DbgPrint("%s<%p>n", __FUNCTION__, this);
        _dwRef = 1;
        pUnkOuter->AddRef();
    }
    virtual void Demo()
    {
        DbgPrint("%s<%p>n", __FUNCTION__, this);
    }
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void**ppvObject)
    {
        if (riid == __uuidof(IDemo))
        {
            AddRef();
            *ppvObject = static_cast<IUnknown*>(this);
            return S_OK;
        }
        return _pUnkOuter->QueryInterface(riid, ppvObject);
    }
    virtual ULONG STDMETHODCALLTYPE AddRef()
    {
        return InterlockedIncrement(&_dwRef);
    }
    virtual ULONG STDMETHODCALLTYPE Release()
    {
        ULONG dwRef = InterlockedDecrement(&_dwRef);
        if (!dwRef) delete this;
        return dwRef;
    }
};
class CObject : public IUnknown
{
    LONG _dwRef;
    ~CObject()
    {
        DbgPrint("%s<%p>n", __FUNCTION__, this);
    }
public:
    CObject()
    {
        DbgPrint("%s<%p>n", __FUNCTION__, this);
        _dwRef = 1;
    }
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void**ppvObject)
    {
        *ppvObject = 0;
        if (riid == __uuidof(IUnknown))
        {
            AddRef();
            *ppvObject = static_cast<IUnknown*>(this);
            return S_OK;
        }
        else if (riid == __uuidof(IDemo))
        {
            if (CDemo* pDoc = new CDemo(this))
            {
                *ppvObject = static_cast<IUnknown*>(pDoc);
                return S_OK;
            }
            return E_OUTOFMEMORY;
        }
        return E_NOINTERFACE;
    }
    virtual ULONG STDMETHODCALLTYPE AddRef()
    {
        return InterlockedIncrement(&_dwRef);
    }
    virtual ULONG STDMETHODCALLTYPE Release()
    {
        ULONG dwRef = InterlockedDecrement(&_dwRef);
        if (!dwRef) delete this;
        return dwRef;
    }
};
void test3()
{
    if (CObject* pUnk = new CObject)
    {
        GetRefCount(pUnk);
        IDemo *pDoc, *pDoc2;
        if (0 <= pUnk->QueryInterface(IID_PPV_ARGS(&pDoc)))
        {
            IsSameObjects(pUnk, pDoc);
            if (0 <= pUnk->QueryInterface(IID_PPV_ARGS(&pDoc2)))
            {
                IsSameObjects(pDoc2, pDoc);
                GetRefCount(pUnk);
                pDoc2->Release();
            }
            if (0 <= pDoc->QueryInterface(IID_PPV_ARGS(&pDoc2)))
            {
                IsSameObjects(pDoc2, pDoc);
                GetRefCount(pUnk);
                pDoc2->Release();
            }
            pDoc->Release();
        }
        GetRefCount(pUnk);
        DbgPrint("Final Release=%un", pUnk->Release());
    }
}

和输出:

CObject::CObject<000001DD8C340970>
000001DD8C340970>1
CDemo::CDemo<000001DD8C33B950>
000001DD8C340970[2] == 000001DD8C33B950[1]
CDemo::CDemo<000001DD8C338930>
000001DD8C338930[1] == 000001DD8C33B950[1]
000001DD8C340970>3
CDemo::~CDemo<000001DD8C338930>
000001DD8C33B950[2] == 000001DD8C33B950[2]
000001DD8C340970>2
CDemo::~CDemo<000001DD8C33B950>
000001DD8C340970>1
CObject::~CObject<000001DD8C340970>
Final Release=0