Метод выпуска COM + не сбрасывается до 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?
Почему последний Release метод IUnknown объект не возвращается 0 как исключено?

Что мне может не хватать?

0

Решение

Согласно документации MDSN AddRef а также Release, возвращаемое значение предназначено для использования только в целях тестирования. Это может не быть точным отражением фактического количества ссылок; и, в частности, тестирование его против 0 не является гарантией того, что объект закончен.

При каких условиях метод IUnknown :: AddRef вернет 0?.

1

Другие решения

Зачем QueryInterface вернул мне IUnknown какой внутренний счет начинается с 3?

pDoc а также pUnk по сути два способа доступа к одному объекту. Поскольку это один объект, это отражается в подсчете ссылок и объясняет, почему он не начинается с 1.

Но из этого объяснения можно ожидать, что счетчик ссылок будет начинаться с 2, а не с 3. Тот факт, что он начинается с 3, вероятно, вызван внутренним вспомогательным объектом, используемым DOMDocument обращаться с IUnknown интерфейс, где этот внутренний вспомогательный объект поддерживает дополнительную ссылку.

Почему последний Release метод IUnknown объект не возвращает 0 как исключено?

По той же причине: pDoc а также pUnk по сути один и тот же объект. Поскольку у вас все еще есть невыпущенная ссылка (доступная через pDoc) в этот момент объект все еще жив.

4

Когда объект запрашивается для его IUnknown В частности, COM ожидает, что один и тот же объект будет возвращаться каждый раз, чтобы обеспечить работу тестов идентичности (вы можете проверить, указывают ли два интерфейса на один и тот же объект в памяти, запросив оба интерфейса для IUnknown а затем сравнить запрашиваемые указатели). Это указано в QueryInterface() документация:

Для любого одного объекта, конкретный запрос для IUnknown Интерфейс на любом из интерфейсов объекта должен всегда возвращать одно и то же значение указателя. Это позволяет клиенту определить, указывают ли два указателя на один и тот же компонент, вызывая QueryInterface с IID_IUnknown и сравнивая результаты. Это конкретно не тот случай, когда запросы для интерфейсов, кроме IUnknown (даже тот же интерфейс через тот же указатель) должен возвращать одно и то же значение указателя.

Таким образом, при запросе объекта DOMDocument IUnknown интерфейс через QueryInterface()можно ожидать, что счетчик ссылок будет увеличен на 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;
}

Объект DOMDocument, вероятно, возвращает указатель на внутренний вспомогательный объект при запросе для IUnknownи этот вспомогательный объект возвращает счетчик ссылок DOMDocument из AddRef() а также Release() вместо того, чтобы возвращать свой собственный счетчик ссылок.

1

то, что вы видите — это Com агрегирование и здесь pUnk является внутренний объект и pDoc является aggregable объект. также интересно, что при запросе IXMLDOMDocument интерфейс на внутренний объект — он каждый раз выделяет новый aggregable объект, который реализует этот интерфейс


Позвольте вначале создать служебную функцию для печати подсчета ссылок на объекте, а также сравнить 2 указателя объекта с точки зрения com (двоичные значения этих указателей могут быть разными, но IUnknown для обоих объектов одинаковое)

ULONG GetRefCount(IUnknown *pUnk, BOOLEAN bPrint = TRUE)
{
pUnk->AddRef();
ULONG d = pUnk->Release();
if (bPrint) DbgPrint("%p>%u\n", 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=%u\n", 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=%u\n", 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=%u\n", 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
1
По вопросам рекламы [email protected]