{"id":112021,"date":"2026-01-28T07:00:00","date_gmt":"2026-01-28T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112021"},"modified":"2026-01-28T13:13:28","modified_gmt":"2026-01-28T21:13:28","slug":"why-did-i-lose-the-data-even-though-i-called-safearrayaddref","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260128-00\/?p=112021","title":{"rendered":"Why did I lose the data even though I called <CODE>Safe&shy;Array&shy;Add&shy;Ref<\/CODE>?"},"content":{"rendered":"<p>A customer had a method that received a <code>SAFEARRAY<\/code>, and they called <code>Safe\u00adArray\u00adAdd\u00adRef<\/code> to add a reference to it so that they could continue using the array after the method returned, but they found that if they tried to use the array later, the <code>safearray-&gt;pData<\/code> was <code>NULL<\/code>, and if they called <code>Safe\u00adArray\u00adAdd\u00adRef<\/code> again, it also gave them a null data pointer. They wanted to know if this was expected behavior. &#8220;Does COM invalidate a <code>SAFEARRAY<\/code> when a method returns, ignoring the reference count?&#8221;<\/p>\n<p>Earlier, we looked at <a title=\"What's the difference between Safe\u00adArray\u00adAccess\u00adData&lt;\/CODE&gt; and Safe\u00adArray\u00adAdd\u00adRef\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260126-00\/?p=112016\"> the difference between <code>Safe\u00adArray\u00adAccess\u00adData<\/code> and <code>Safe\u00adArray\u00adAdd\u00adRef<\/code><\/a>, and that will be the key to untangling this riddle.<\/p>\n<p>Scripting <code>SAFEARRAY<\/code>s are not reference-counted objects. When somebody says to destroy them, they are destroyed.<\/p>\n<p>The <code>Safe\u00adArray\u00adAdd\u00adRef<\/code> function lets you extend the <i>memory<\/i> lifetime of the array descriptor and the array data, but the <i>useful<\/i> lifetime ends when the array is destroyed. The purpose of the <code>Safe\u00adArray\u00adAdd\u00adRef<\/code> function is to prevent you from operating on freed memory if somebody destroys the array out from under you.<\/p>\n<p>In this case, what happens is that the caller provides a <code>SAFEARRAY<\/code>, and you call <code>Safe\u00adArray\u00adAdd\u00adRef<\/code> to extend its memory lifetime. When you return, the caller decides that it was a temporary array, so it calls <code>Safe\u00adArray\u00adDestroy<\/code>. This renders the array contents useless, but since you added a reference (to the descriptor and the data), the memory for them is not freed, even though they don&#8217;t contain anything useful any more.<\/p>\n<p>After the array has been destroyed, your calls to <code>Safe\u00adArray\u00adAdd\u00adRef<\/code> continue to extend the lifetime of the array descriptor and the data in it, but the descriptor had already been emptied out when the array was destroyed, so there is no data in the array any more. You are extending the lifetime of no data, which is why <code>Safe\u00adArray\u00adAdd\u00adRef<\/code> produces a null pointer as the data pointer.<\/p>\n<p>To access the original memory, you need to do it through the pointer returned from the original call to <code>Safe\u00adArray\u00adAdd\u00adRef<\/code>.<\/p>\n<p>But even that won&#8217;t help you, because the memory for the array data is zeroed out when the array is destroyed. You have a pointer to a bunch of zeroes.<\/p>\n<p>Next time, we&#8217;ll look at ways of solving the customer&#8217;s problem, now that we understand why their approach didn&#8217;t work.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You have to use the original pointer, but even that won&#8217;t be good enough.<\/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-112021","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You have to use the original pointer, but even that won&#8217;t be good enough.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112021","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=112021"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112021\/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=112021"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112021"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112021"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}