{"id":105922,"date":"2021-11-15T07:00:00","date_gmt":"2021-11-15T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105922"},"modified":"2021-11-15T05:16:34","modified_gmt":"2021-11-15T13:16:34","slug":"20211115-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211115-00\/?p=105922","title":{"rendered":"A practical use for <CODE>GetHGlobal&shy;FromStream<\/CODE> when sharing was never your intention"},"content":{"rendered":"<p>A little while ago, I noted that <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210928-00\/?p=105737\"> managing shared access to the <code>HGLOBAL<\/code> inside a stream can get tricky<\/a>, and opined that <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211001-00\/?p=105748\"> the <code>GetHGlobal\u00adFromStream<\/code> should have been something like <code>IStream\u00adOn\u00adHGlobal::<wbr \/>Detach\u00adHGlobal<\/code><\/a>. But in fact there&#8217;s a straightforward use case for <code>GetHGlobal\u00adFromStream<\/code> even when there is no sharing going on.<\/p>\n<p>The <code>STGMEDIUM<\/code> structure is a currency for passing data into and out of an <code>IData\u00adObject<\/code>. it is basically a discriminated union:<\/p>\n<pre>struct STGMEDIUM\r\n{\r\n    DWORD tymed;\r\n    union\r\n    {\r\n        HBITMAP hBitmap;\r\n        HMETAFILEPICT hMetaFilePict;\r\n        HENHMETAFILE hEnhMetaFile;\r\n        HGLOBAL hGlobal;\r\n        LPOLESTR lpszFileName;\r\n        IStream* pstm;\r\n        IStorage* pstg;\r\n    };\r\n    IUnknown* pUnkForRelease;\r\n};\r\n<\/pre>\n<p>The <code>tymed<\/code> member specifies which of the members of the union is active. Let&#8217;s focus on the case where the value is <code>TYMED_MWBR&gt;HGLOBAL<\/code>, in which case the structure simplifies to<\/p>\n<pre>struct STGMEDIUM\r\n{\r\n    DWORD tymed = TYMED_HGLOBAL;\r\n    HGLOBAL hGlobal;\r\n    IUnknown* pUnkForRelease;\r\n};\r\n<\/pre>\n<p>When you are finished with the <code>STGMEDIUM<\/code>, you call <code>Release\u00adStg\u00adMedium<\/code>, and in the case of a <code>TYMED_<wbr \/>HGLOBAL<\/code> the rule for <code>Release\u00adStg\u00adMedium<\/code> is<\/p>\n<ul>\n<li>If <code>pUnkForRelease<\/code> is not null, then call <code>pUnkForRelease-&gt;Release();<\/code>.<\/li>\n<li>If <code>pUnkForRelease<\/code> is null, then call <code>GlobalFree(hGlobal);<\/code>.<\/li>\n<\/ul>\n<p>In words, if there is a <code>pUnkForRelease<\/code>, then it is the &#8220;owner&#8221; which controls the lifetime of the <code>HGLOBAL<\/code>, and you tell it that you are done by releasing the reference count on the owner. When the owner&#8217;s reference count goes to zero, it destroys itself (and the <code>HGLOBAL<\/code>).<\/p>\n<p>On the other hand, if there is no <code>pUnkForRelease<\/code>, then the <code>HGLOBAL<\/code> is &#8220;ownerless&#8221;, and you just free it when you&#8217;re done.<\/p>\n<p>The idea here is that if the <code>HGLOBAL<\/code> was expensive to produce, your data object may decide to cache the result rather than having to produce it from scratch every time. In that case, when you give out the data, you can do this:<\/p>\n<pre>if (m_cachedHglobal == nullptr)\r\n{\r\n    RETURN_IF_FAILED(Calculate(&amp;m_cachedHglobal));\r\n}\r\n\r\npstgm-&gt;tymed = TYMED_HGLOBAL;\r\npstgm-&gt;hGlobal = m_cachedHglobal;\r\npstgm-&gt;pUnkForRelease = this;\r\nthis-&gt;AddRef(); \/\/ because we put it in pUnkForRelease\r\n<\/pre>\n<p>When the recipient of the data is finished, they will call <code>Release\u00adStg\u00adMedium<\/code>, and <code>Release\u00adStg\u00adMedium<\/code> will see that there is a non-null <code>pUnkForRelease<\/code> and instead of freeing the <code>HGLOBAL<\/code>, it&#8217;ll release the <code>pUnkForRelease<\/code>.<\/p>\n<p>This means that the <code>m_cachedHglobal<\/code> is not destroyed when the recipient of the data is finished. It lives on, so it can be returned to another client.<\/p>\n<p>Finally, when the data object is destroyed, it also destroys the <code>m_cachedHglobal<\/code>.<\/p>\n<p>This pattern means that as long as anybody still has a <code>STGMEDIUM<\/code> referring to your cached <code>HGLOBAL<\/code>, your entire data object will remain alive. But maybe your data object has a lot of stuff in it, like an entire HTML DOM, and the <code>HGLOBAL<\/code> is a cache of the <code>textContent<\/code>. Somebody who asks for the <code>textContent<\/code> and hangs onto it for a long time will keep your data object alive, even if they aren&#8217;t using the data object any more:<\/p>\n<pre>HRESULT GetTheText(STGMEDIUM* pstgm)\r\n{\r\n    wil::com_ptr&lt;IDataObject&gt; pdto;\r\n    RETURN_IF_FAILED(GetTheDataObject(&amp;pdto));\r\n\r\n    FORMATETC fe;\r\n    fe.cfFormat = CF_UNICODETEXT;\r\n    fe.ptd = nullptr;\r\n    fe.dwAspect = DVASPECT_CONTENT;\r\n    fe.lindex = -1;\r\n    fe.tymed = TYMED_HGLOBAL;\r\n    \r\n    RETURN_IF_FAILED(pdto-&gt;GetData(&amp;&amp;fe, pstgm));\r\n\r\n    return S_OK;\r\n}\r\n<\/pre>\n<p>This function gets the data object and extracts the Unicode text. The data object is thrown away when the <code>com_ptr<\/code> destructs, and all that remains is the text in the <code>STGMEDIUM<\/code>&#8216;s <code>HGLOBAL<\/code>.<\/p>\n<p>The catch here is that the caller of this function might decide to keep the text for a long time, and that&#8217;s going to keep your big data object around for a long time. Even though all that really needs to be kept alive is the text.<\/p>\n<p>This is where you can use one of the lesser powers of <code>GetHGlobal\u00adFromStream<\/code>.<\/p>\n<p>Instead of making the data object be the cache for the <code>HGLOBAL<\/code>, you can make an <code>HGLOBAL<\/code>-backed stream with <code>Create\u00adStream\u00adOn\u00adHGlobal<\/code>, and let the stream be the one in charge of the <code>HGLOBAL<\/code>&#8216;s lifetime.<\/p>\n<pre>if (m_cachedStream.get() == nullptr)\r\n{\r\n    wil::unique_hglobal text;\r\n    RETURN_IF_FAILED(Calculate(&amp;text));\r\n    RETURN_IF_FAILED(CreateStreamOnHGlobal(\r\n        text.get(), TRUE \/* fDeleteOnRelease *\/,\r\n        &amp;m_cachedStream));\r\n    text.release(); \/\/ m_cachedStream owns it now\r\n}\r\n\r\npstgm-&gt;tymed = TYMED_HGLOBAL;\r\n<span style=\"color: blue;\">RETURN_IF_FAILED(GetHGlobalFromStream(\r\n        m_cachedStream.get(), &amp;pstgm-&gt;hGlobal));<\/span>\r\n\/\/ The stream is the owner of the HGLOBAL\r\npstgm-&gt;pUnkForRelease = <span style=\"color: blue;\">m_cachedStream.copy().detach();<\/span>\r\n<\/pre>\n<p>This time, the owner of the <code>HGLOBAL<\/code> is the <code>m_cached\u00adStream<\/code>, and therefore if the storage medium is retained beyond the life of the data object, the data object can destruct, and the <code>m_cachedStream<\/code> will deal with freeing the <code>HGLOBAL<\/code> on final release.<\/p>\n<p>I&#8217;m guessing this might even have been the scenario for which <code>GetHGlobal\u00adFromStream<\/code> was originally invented.<\/p>\n<p>Mind you, we&#8217;re using an entire stream just to babysit an <code>HGLOBAL<\/code>. We could have written a custom babysitter:<\/p>\n<pre>struct IUnknownOnHGLOBAL : winrt::implements&lt;IUnknownOnHGLOBAL, ::IUnknown&gt;\r\n{\r\n    IUnknownOnHGLOBAL(HGLOBAL glob) : m_glob(glob) {}\r\n    wil::unique_hglobal glob;\r\n};\r\n<\/pre>\n<p>On the other hand, using <code>Create\u00adStream\u00adOn\u00adHGlobal<\/code> may end up being the easier route if the <code>HGLOBAL<\/code> was originally generated from a stream in the first place:<\/p>\n<pre>if (m_cachedStream.get() == nullptr)\r\n{\r\n    <span style=\"color: blue;\">wil::com_ptr&lt;IStream&gt; stm;\r\n    RETURN_IF_FAILED(CreateStreamOnHGlobal(\r\n        nullptr, TRUE \/* fDeleteOnRelease *\/,\r\n        &amp;stm));\r\n    RETURN_IF_FAILED(SaveToStream(stm.get()));\r\n    m_cachedStream = std::move(stm);<\/span>\r\n}\r\n\r\npstgm-&gt;tymed = TYMED_HGLOBAL;\r\nRETURN_IF_FAILED(GetHGlobalFromStream(\r\n        m_cachedStream.get(), &amp;pstgm-&gt;hGlobal));\r\npstgm-&gt;pUnkForRelease = m_cachedStream.copy().detach();\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>This may even have been its original purpose.<\/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-105922","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>This may even have been its original purpose.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105922","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=105922"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105922\/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=105922"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105922"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105922"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}