{"id":34003,"date":"2005-09-29T10:00:10","date_gmt":"2005-09-29T10:00:10","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/09\/29\/on-objects-with-a-reference-count-of-zero\/"},"modified":"2005-09-29T10:00:10","modified_gmt":"2005-09-29T10:00:10","slug":"on-objects-with-a-reference-count-of-zero","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050929-10\/?p=34003","title":{"rendered":"On objects with a reference count of zero"},"content":{"rendered":"<p><P>\n<A HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2005\/09\/27\/474384.aspx#474405\">\nOne commenter claimed<\/A> that\n<\/P>\n<BLOCKQUOTE CLASS=\"q\">\nWhen the object is first constructed,\nthe reference count should be 0 and\nAddRef should be called at some point\n(probably via QueryInterface) to increment the reference count.\n<\/BLOCKQUOTE>\n<P>\nIf you construct your object with a reference count of zero,\nyou are playing with matches.\nFor starters, when the object is created, there reference\ncount is not zero &#8211; the person who created the object has a reference!\nRemember the COM rule for references:\nIf a function produces a reference (typically an interface pointer),\nthe reference count is incremented\nto account for the produced reference.\nIf you consider the constructor to be a function, then it needs\nto return with an incremented reference\ncount to account for the produced object.\n<\/P>\n<P>\nIf you prefer to play with matches, you can end up burning yourself\nwith code like the following:\n<\/P>\n<PRE>\n\/\/ A static creator method\nHRESULT MyObject::Create(REFIID riid, void **ppvObj)\n{\n *ppvObj = NULL;\n MyObject *pobj = new MyObject();\n HRESULT hr = pobj ? S_OK : E_OUTOFMEMORY;\n if (SUCCEEDED(hr)) {\n  hr = pobj-&gt;Initialize(); \/\/ dangerous!\n  if (SUCCEEDED(hr)) {\n   hr = pobj-&gt;QueryInterface(riid, ppvObj);\n  }\n  if (FAILED(hr)) {\n   delete pobj;\n  }\n }\n return hr;\n}\n<\/PRE>\n<P>\nNotice that you&#8217;re initializing the object while its reference\ncount is zero.\nThis puts you in the same &#8220;limbo zone&#8221; as cleaning up an object\nwhile its reference count is zero,\nand therefore exposes you to the same problems:\n<\/P>\n<PRE>\nHRESULT MyObject::Load()\n{\n CComPtr&lt;IStream&gt; spstm;\n HRESULT hr = GetLoadStream(&amp;spstm);\n if (SUCCEEDED(hr)) {\n  CComQIPtr&lt;IObjectWithSite, &amp;IID_IObjectWithSite&gt; spows(spstm);\n  if (spows) spows-&gt;SetSite(this);\n  hr = LoadFromStream(spstm);\n  if (spows) spows-&gt;SetSite(NULL);\n }\n return hr;\n}<\/p>\n<p>HRESULT MyObject::Initialize()\n{\n return Load();\n}\n<\/PRE>\n<P>\nAn object that saves itself during destruction\nis very likely to load itself during creation.\nAnd you run into exactly the same problem.\nThe call to <CODE>IObjectWithSite::SetSite(this)<\/CODE>\nincrements the reference count of the object from zero to one,\nand the call to\nThe call to <CODE>IObjectWithSite::SetSite(NULL)<\/CODE>\ndecrements it back to zero.\nWhen the reference count decrements to zero, this destroys\nthe object,\nresulting in the object being inadvertently destroyed\nby the\n<CODE>MyObject::Load()<\/CODE> method.\n<\/P>\n<P>\nThe <CODE>MyObject::Create<\/CODE> static method\ndoesn&#8217;t realize that this has happened and proceeds to\ncall the <CODE>QueryInterface<\/CODE> method to return a\npointer back to the caller,\nexpecting it to increment the reference count from zero to one.\nUnfortunately, it&#8217;s doing this to an object that has already\nbeen destroyed.\n<\/P>\n<P>\nThat&#8217;s what happens when you play with an object whose reference\ncount is zero:\nIt can disappear the moment you relinquish control.\nObjects should be created with a reference count of one,\nnot zero.\n<\/P>\n<P>\nATL prefers to play with matches, using the moral equivalent of\nthe above <CODE>MyObject::Create<\/CODE> function in its\nobject construction:\n<\/P>\n<PRE>\nvoid InternalFinalConstructAddRef() {}\nvoid InternalFinalConstructRelease()\n{\n    ATLASSERT(m_dwRef == 0);\n}<\/p>\n<p>static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)\n{\n    ATLASSERT(*ppv == NULL);\n    HRESULT hRes = E_OUTOFMEMORY;\n    T1* p = NULL;\n    ATLTRY(p = new T1(pv))\n    if (p != NULL)\n    {\n\tp-&gt;SetVoid(pv);\n\tp-&gt;InternalFinalConstructAddRef();\n\thRes = p-&gt;FinalConstruct();\n\tp-&gt;InternalFinalConstructRelease();\n\tif (hRes == S_OK)\n\t    hRes = p-&gt;QueryInterface(riid, ppv);\n\tif (hRes != S_OK)\n\t    delete p;\n    }\n    return hRes;\n}\n<\/PRE>\n<P>\nATL hands you a set of matches by calling your\n<CODE>FinalConstruct<\/CODE> method with a reference count of zero.\nIf you know that you&#8217;re going to get burned, you can use the\n<CODE>DECLARE_PROTECT_FINAL_CONSTRUCT<\/CODE> macro to change\nthe <CODE>InternalFinalConstructAddRef<\/CODE> and\n<CODE>InternalFinalConstructRelease<\/CODE> methods to versions\nthat actually increment the reference count temporarily\nduring the call to <CODE>FinalConstruct<\/CODE>,\nthen drop the reference count back to zero (without destructing\nthe object)\nprior to the <CODE>QueryInterface<\/CODE> call.\n<\/P>\n<P>\nIt works, but in my opinion it relies too much on programmer vigilance.\nThe default for ATL is to hand programmers matches\nand relying on programmers &#8220;knowing&#8221; that\nsomething dangerous might happen inside the\n<CODE>FinalConstruct<\/CODE>\nand having the presence of mind to ask for\n<CODE>DECLARE_PROTECT_FINAL_CONSTRUCT<\/CODE>.\nIn other words, it chooses the dangerous default, and\nprogrammers must explicitly ask for the safe version.\nBut programmers have a lot of things on their mind,\nand forcing them to consider the consequences of the transitive\nclosure of every operation performed in the\n<CODE>FinalConstruct<\/CODE> method is an unresonable requirement.\n<\/P>\n<P>\nConsider our example above.\nWhen the code was originally written, the <CODE>Load<\/CODE> method\nmay have been the much simpler\n<\/P>\n<PRE>\nHRESULT MyObject::Load()\n{\n CComPtr&lt;IStream&gt; spstm;\n HRESULT hr = GetLoadStream(&amp;spstm);\n if (SUCCEEDED(hr)) {\n  hr = LoadFromStream(spstm);\n }\n return hr;\n}\n<\/PRE>\n<P>\nIt wasn&#8217;t until a month or two later that somebody added\nsite support to the <CODE>Load<\/CODE> and <CODE>Save<\/CODE> methods.\nThis seemingly simple and isolated change, adhering perfectly to\nthe COM rules for reference counting, had ripple effects\nback through the object creation and destruction code paths.\nIf you put four levels of function calls between the\n<CODE>FinalConstruct<\/CODE> and the <CODE>Load<\/CODE>,\nthis fourth-level-caller effect can very easily be overlooked.\nI suspect that these nonlocal effects are\none of the most significant sources of code defects.\nATL was being clever and optimized out an increment and a decrement\n(something which the compiler most likely could optimize out on its own),\nbut in return, you got handed a book of matches.\n<\/P>\n<P>\n(I don&#8217;t mean to be picking on ATL here,\nso don&#8217;t go linking to this article with the title\n&#8220;Raymond rails into ATL as a poorly-designed pile of dung&#8221;.\nATL is trying to be small and fast,\nbut the cost is added complexity, often subtle.)\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>One commenter claimed that When the object is first constructed, the reference count should be 0 and AddRef should be called at some point (probably via QueryInterface) to increment the reference count. If you construct your object with a reference count of zero, you are playing with matches. For starters, when the object is created, [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-34003","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>One commenter claimed that When the object is first constructed, the reference count should be 0 and AddRef should be called at some point (probably via QueryInterface) to increment the reference count. If you construct your object with a reference count of zero, you are playing with matches. For starters, when the object is created, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/34003","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=34003"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/34003\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=34003"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=34003"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=34003"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}