{"id":34023,"date":"2005-09-27T10:00:13","date_gmt":"2005-09-27T10:00:13","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/09\/27\/com-object-destructors-are-very-sensitive-functions\/"},"modified":"2023-08-22T17:07:25","modified_gmt":"2023-08-23T00:07:25","slug":"com-object-destructors-are-very-sensitive-functions","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050927-13\/?p=34023","title":{"rendered":"COM object destructors are very sensitive functions"},"content":{"rendered":"<p>If you try to do too much, you can find yourself in trouble.<\/p>\n<p>For example, if your destructor hands a reference to itself to other functions, those functions might decide to call your <code>IUnknown::AddRef<\/code> and <code>IUnknown::Release<\/code> methods as part of their internal operations. Consider:<\/p>\n<pre>ULONG MyObject::Release()\r\n{\r\n LONG cRef = InterlockedDecrement(&amp;m_cRef);\r\n if (cRef == 0) {\r\n  delete this;\r\n }\r\n return cRef;\r\n}\r\n\r\nMyObject::~MyObject()\r\n{\r\n if (m_fNeedSave) Save();\r\n}\r\n<\/pre>\n<p>That doesn&#8217;t look so scary now does it? The object saves itself when destructed.<\/p>\n<p>However, the <code>Save<\/code> method might do something like this:<\/p>\n<pre>HRESULT MyObject::Save()\r\n{\r\n CComPtr&lt;IStream&gt; spstm;\r\n HRESULT hr = GetSaveStream(&amp;spstm);\r\n if (SUCCEEDED(hr)) {\r\n  CComQIPtr&lt;IObjectWithSite, &amp;IID_IObjectWithSite&gt; spows(spstm);\r\n  if (spows) spows-&gt;SetSite(this);\r\n  hr = SaveToStream(spstm);\r\n  if (spows) spows-&gt;SetSite(NULL);\r\n }\r\n return hr;\r\n}\r\n<\/pre>\n<p>On its own, this looks pretty normal. Get a stream and save to it, setting ourselves as its site in case the stream wants to get additional information about the object as part of the saving process.<\/p>\n<p>But in conjunction with the fact that we call it from our destructor, we have a recipe for disaster. Watch what happens when the last reference is released.<\/p>\n<ul>\n<li>The <code>Release()<\/code> method decrements the reference count to zero and performs a <code>delete this<\/code>.<\/li>\n<li>The destructor attempts to save the object.<\/li>\n<li>The <code>Save()<\/code> method obtains the save stream and sets itself as the site. This increments the reference count from zero to one.<\/li>\n<li>The <code>SaveToStream()<\/code> method saves the object.<\/li>\n<li>The <code>Save()<\/code> method clears the site on the stream. This decrements the reference count from one back to zero.<\/li>\n<li>The <code>Release()<\/code> method therefore attempts to destructor the object a second time.<\/li>\n<\/ul>\n<p>Destructing the object a second time tends to result in widespread mayhem. If you&#8217;re lucky, you&#8217;ll crash inside the recursive destruction and identify the source, but if you&#8217;re not lucky, the resulting heap corruption won&#8217;t go detected for quite some time, at which point you&#8217;ll just be left scratching your head.<\/p>\n<p>Therefore, at a minimum, you should assert in your <code>AddRef()<\/code> method that you aren&#8217;t incrementing the reference count from zero.<\/p>\n<pre>ULONG MyObject::AddRef()\r\n{\r\n <span style=\"border: solid 1px currentcolor;\">assert(m_cRef != 0);<\/span>\r\n return InterlockedIncrement(&amp;m_cRef);\r\n}\r\n<\/pre>\n<p>This would catch the &#8220;case of the mysteriously double-destructed object&#8221; much earlier in the game, giving you a fighting chance of identifying the problem. But once you&#8217;ve isolated the problem, what can you do about it? We&#8217;ll look into that <a title=\"Avoiding double-destruction when an object is released\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050928-10\/?p=34013\"> next time<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you try to do too much, you can find yourself in trouble.<\/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-34023","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>If you try to do too much, you can find yourself in trouble.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/34023","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=34023"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/34023\/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=34023"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=34023"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=34023"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}