com 释放方法不降至0
COM+ Release Method Not Dropping to 0
我希望与您分享我面临的问题。长话短说,我有这个小的代码(仅测试目的(:
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?
pDoc
和pUnk
本质上是访问单个对象的两种方法。由于它是一个对象,所以这反映在参考计数中,并解释了为什么它不在1中开始。
但是,从该解释中,您可能会期望参考计数从2而不是3开始。事实始于3,可能是由DOMDocument
使用的内部辅助对象引起的,用于处理IUnknown
接口,其中该内部辅助对象在其中维护附加参考。
为什么
IUnknown
对象的最后一个Release
未返回0的方法?
出于相同的原因:pDoc
和pUnk
本质上是同一对象。由于您仍然有一个未发布的参考(可以通过pDoc
访问(,因此该对象仍然存在。
根据AddRef
和Release
的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
在这里可见pUnk
和pDoc
(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
- 释放 std::vector 中指针内存的最有效方法是什么?
- 释放分配给大量矢量的内存的最有效方法是什么?
- C++:释放动态数组(结构成员)和指向此结构的指针的方法
- 在崩溃时释放分配的指针的正确方法
- com 释放方法不降至0
- 使用删除与智能指针释放内存以及释放内存的正确方法
- 释放数组的不同方法 - c++
- 有什么方法可以通过删除表达式安全地处理两次释放内存?
- C++ 释放自定义类向量的正确方法
- c++创建新的LinkedList类:Clear()方法指针被释放未分配
- 释放组合结构内存的正确方法
- 调用 std::函数对象,指向释放对象的方法
- 释放方法 C/C++ 中动态分配的 char*
- 使用 std::分配器和 std::move 防止释放的正确方法
- 调用析构函数以释放C++运算符中的内存的正确方法是什么
- 为包含C++对象数组的对象数组释放内存的正确方法
- 为我将在std::map中使用的对象数组分配和释放内存的正确方法
- 将 std::vector 截断<char>为长度 N - 以释放内存的有效方法
- 为什么std::vector没有释放方法
- 从c++方法接收char*,转换为托管字符串,然后释放内存