{"id":44843,"date":"2015-01-23T07:00:00","date_gmt":"2015-01-23T22:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2015\/01\/23\/helper-functions-to-make-shell-bind-contexts-slightly-more-manageable\/"},"modified":"2019-03-13T12:12:19","modified_gmt":"2019-03-13T19:12:19","slug":"20150123-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20150123-00\/?p=44843","title":{"rendered":"Helper functions to make shell bind contexts slightly more manageable"},"content":{"rendered":"<p>Last time, we learned about <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2015\/01\/22\/10587918.aspx\">the wonderful world of shell bind context strings<\/a>, and I promised some helper functions to make this slightly more manageable. <\/p>\n<p>Here are some helper functions which supplement the <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/05\/03\/10415778.aspx\"><code>Create&shy;Bind&shy;Ctx&shy;With&shy;Opts<\/code> function<\/a> we created some time ago. <\/p>\n<pre>\n#include &lt;propsys.h&gt;\n\nHRESULT EnsureBindCtxPropertyBag(\n    IBindCtx *pbc, REFIID riid, void **ppv)\n{\n *ppv = nullptr;\n CComPtr&lt;IUnknown&gt; spunk;\n HRESULT hr = pbc-&gt;GetObjectParam(STR_PROPERTYBAG_PARAM, &amp;spunk);\n if (FAILED(hr)) {\n  hr = PSCreateMemoryPropertyStore(IID_PPV_ARGS(&amp;spunk));\n  if (SUCCEEDED(hr)) {\n   hr = pbc-&gt;RegisterObjectParam(STR_PROPERTYBAG_PARAM, spunk);\n  }\n }\n if (SUCCEEDED(hr)) {\n  hr = spunk-&gt;QueryInterface(riid, ppv);\n }\n  return hr;\n}\n\nHRESULT AddBindCtxDWORD(\n    IBindCtx *pbc, LPCWSTR pszName, DWORD dwValue)\n{\n CComPtr&lt;IPropertyBag&gt; sppb;\n HRESULT hr = EnsureBindCtxPropertyBag(pbc, IID_PPV_ARGS(&amp;sppb));\n if (SUCCEEDED(hr)) {\n  hr = PSPropertyBag_WriteDWORD(sppb, pszName, dwValue);\n }\n return hr;\n}\n\nHRESULT AddBindCtxString(\n    IBindCtx *pbc, LPCWSTR pszName, LPCWSTR pszValue)\n{\n CComPtr&lt;IPropertyBag&gt; sppb;\n HRESULT hr = EnsureBindCtxPropertyBag(pbc, IID_PPV_ARGS(&amp;sppb));\n if (SUCCEEDED(hr)) {\n  hr = PSPropertyBag_WriteStr(sppb, pszName, pszValue);\n }\n return hr;\n}\n\nHRESULT CreateDwordBindCtx(\n    LPCWSTR pszName, DWORD dwValue, IBindCtx **ppbc)\n{\n CComPtr&lt;IBindCtx&gt; spbc;\n HRESULT hr = CreateBindCtx(0, &amp;spbc);\n if (SUCCEEDED(hr)) {\n  hr = AddBindCtxDWORD(spbc, pszName, dwValue);\n }\n *ppbc = SUCCEEDED(hr) ? spbc.Detach() : nullptr;\n return hr;\n}\n\nHRESULT CreateStringBindCtx(\n    LPCWSTR pszName, LPCWSTR pszValue, IBindCtx **ppbc)\n{\n CComPtr&lt;IBindCtx&gt; spbc;\n HRESULT hr = CreateBindCtx(0, &amp;spbc);\n if (SUCCEEDED(hr)) {\n  hr = AddBindCtxString(spbc, pszName, pszValue);\n }\n *ppbc = SUCCEEDED(hr) ? spbc.Detach() : nullptr;\n return hr;\n}\n<\/pre>\n<p>The <code>Ensure&shy;Bind&shy;Ctx&shy;Property&shy;Bag<\/code> function puts a property bag in the bind context if there isn&#8217;t one already. <\/p>\n<p>The <code>Add&shy;Bind&shy;Ctx&shy;DWORD<\/code> function adds a <code>DWORD<\/code> to that associated property bag. If you wanted to add multiple <code>DWORD<\/code>s to a bind context, you would call this function multiple times. You can also use the <code>Add&shy;Bind&shy;Ctx&shy;String<\/code> if the thing you want to add is a string. <\/p>\n<p>The <code>Create&shy;Dword&shy;Bind&shy;Ctx<\/code> function handles the simple case where you want to create a bind context that contains a single <code>DWORD<\/code>. Similarly, <code>Create&shy;String&shy;Bind&shy;Ctx<\/code>. <\/p>\n<p>But now things are starting to get kind of unwieldy. What if you want a bind context with a string and a <code>DWORD<\/code>? Let&#8217;s go for something a bit more fluent. <\/p>\n<p>But first, some scaffolding. <\/p>\n<pre>\nclass CStaticUnknown : public IUnknown\n{\npublic:\n \/\/ *** IUnknown ***\n IFACEMETHODIMP QueryInterface(\n  _In_ REFIID riid, _Outptr_ void **ppv)\n {\n  *ppv = nullptr;\n  HRESULT hr = E_NOINTERFACE;\n  if (riid == IID_IUnknown) {\n   *ppv = static_cast&lt;IUnknown *&gt;(this);\n   AddRef();\n   hr = S_OK;\n  }\n  return hr;\n }\n\n IFACEMETHODIMP_(ULONG) AddRef()\n {\n  return 2;\n }\n\n IFACEMETHODIMP_(ULONG) Release()\n {\n  return 1;\n }\n\n};\n\nCStaticUnknown s_unkStatic;\n<\/pre>\n<p>This static implementation of <code>IUnknown<\/code> is one we&#8217;ll use for the bind context strings whose mere presence indicates that a flag is set. <\/p>\n<pre>\nclass CBindCtxBuilder\n{\npublic:\n CBindCtxBuilder()\n {\n  m_hrCumulative = CreateBindCtx(0, &amp;m_spbc);\n }\n\n CBindCtxBuilder&amp; SetMode(DWORD grfMode);\n CBindCtxBuilder&amp; SetFindData(const WIN32_FIND_DATA *pfd);\n CBindCtxBuilder&amp; SetFlag(PCWSTR pszName);\n CBindCtxBuilder&amp; SetVariantDword(PCWSTR pszName, DWORD dwValue);\n CBindCtxBuilder&amp; SetVariantString(PCWSTR pszName, PCWSTR pszValue);\n\n HRESULT Result() const { return m_hrCumulative; }\n\n IBindCtx *GetBindCtx() const\n { return SUCCEEDED(m_hrCumulative) ? m_spbc : nullptr; }\nprivate:\n HRESULT EnsurePropertyBag();\n\nprivate:\n CComPtr&lt;IBindCtx&gt; m_spbc;\n CComPtr&lt;IPropertyBag&gt; m_sppb;\n HRESULT m_hrCumulative;\n};\n<\/pre>\n<p>The bind context builder class is a helper class that creates a bind context, and then fills it with stuff. For now, we let you set the following: <\/p>\n<ul>\n<li>The mode to use for opening the target of the bind.     <a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms686636(v=vs.85).aspx\">    The default is <code>STGM_READ&shy;WRITE<\/code><\/a>. \n<li>The find data to use, if     <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/05\/03\/10415778.aspx\">    creating a simple pidl<\/a>. \n<li>An arbitrary flag, associated with a dummy <code>IUnknown<\/code>. \n<li>A <code>DWORD<\/code> in the property bag. \n<li>A string in the property bag. <\/ul>\n<p>After you build up the bind context, you can check the <code>Result()<\/code> to see if it was built successfully, and use <code>Get&shy;Bind&shy;Ctx<\/code> to extract the result. <\/p>\n<p>Here&#8217;s the implementation. It&#8217;s really not that exciting. We accumulate any error in <code>m_hrCumulative<\/code>, and once an error occurs, all future methods do nothing aside from preserving the error. To make the object fluent, the methods return a reference to themselves. <\/p>\n<p>There is a special bind context method for setting the mode: <\/p>\n<pre>\nCBindCtxBuilder&amp;\nCBindCtxBuilder::SetMode(DWORD grfMode)\n{\n if (SUCCEEDED(m_hrCumulative)) {\n  BIND_OPTS bo = { sizeof(bo), 0, grfMode, 0 };\n  m_hrCumulative = m_spbc-&gt;SetBindOptions(&amp;bo);\n }\n return *this;\n}\n<\/pre>\n<p>Find data is set as a direct object on the bind context, <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/05\/03\/10415778.aspx\">as we saw some time ago<\/a>:<\/p>\n<pre>\nCBindCtxBuilder&amp;\nCBindCtxBuilder::SetFindData(const WIN32_FIND_DATA *pfd)\n{\n if (SUCCEEDED(m_hrCumulative)) {\n  m_hrCumulative = <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/05\/03\/10415778.aspx\">AddFileSysBindCtx<\/a>(m_spbc, pfd);\n }\n return *this;\n}\n<\/pre>\n<p>Flags are set by there mere presence, so we associate them with a dummy <code>IUnknown<\/code> that does nothing: <\/p>\n<pre>\nCBindCtxBuilder&amp;\nCBindCtxBuilder::SetFlag(PCWSTR pszName)\n{\n if (SUCCEEDED(m_hrCumulative)) {\n  m_hrCumulative = m_spbc-&gt;RegisterObjectParam(\n    const_cast&lt;PWSTR&gt;(pszName), &amp;s_unkStatic);\n }\n return *this;\n}\n<\/pre>\n<p>If a property is set in the property bag, we need to proceed in two steps. First, we create the property bag if we don&#8217;t have one already. Second, we put the value into the property bag: <\/p>\n<pre>\nCBindCtxBuilder&amp;\nCBindCtxBuilder::SetVariantDword(\n    PCWSTR pszName, DWORD dwValue)\n{\n if (SUCCEEDED(m_hrCumulative)) {\n  m_hrCumulative = EnsurePropertyBag();\n }\n if (SUCCEEDED(m_hrCumulative)) {\n  m_hrCumulative =  PSPropertyBag_WriteDWORD(\n    m_sppb, pszName, dwValue);\n }\n return *this;\n}\n\nCBindCtxBuilder&amp;\nCBindCtxBuilder::SetVariantString(\n    PCWSTR pszName, PCWSTR pszValue)\n{\n if (SUCCEEDED(m_hrCumulative)) {\n  m_hrCumulative = EnsurePropertyBag();\n }\n if (SUCCEEDED(m_hrCumulative)) {\n  m_hrCumulative =  PSPropertyBag_WriteStr(\n    m_sppb, pszName, pszValue);\n }\n return *this;\n}\n<\/pre>\n<p>And finally, the helper function that creates a property bag if we don&#8217;t have one already. <\/p>\n<pre>\nHRESULT CBindCtxBuilder::EnsurePropertyBag()\n{\n HRESULT hr = S_OK;\n if (!m_sppb) {\n  hr = PSCreateMemoryPropertyStore(\n    IID_PPV_ARGS(&amp;m_sppb));\n  if (SUCCEEDED(hr)) {\n   hr = m_spbc-&gt;RegisterObjectParam(\n    STR_PROPERTYBAG_PARAM, m_sppb);\n  }\n }\n return hr;\n}\n<\/pre>\n<p>The idea here is that the class is used like this: <\/p>\n<pre>\nCBindCtxBuilder builder;\nbuilder.SetMode(STGM_CREATE)\n       .SetFindData(&amp;wfd)\n       .SetFlag(STR_FILE_SYS_BIND_DATA_WIN7_FORMAT)\n       .SetFlag(STR_BIND_FOLDERS_READ_ONLY);\nhr = builder.Result();\nif (SUCCEEDED(hr)) {\n hr = psf-&gt;ParseDisplayName(hwnd, builder.GetBindCtx(),\n   pszName, &amp;cchEaten, &amp;pidl, &amp;dwAttributes);\n}\n<\/pre>\n<p>You create the bind context builder, then use the various <code>Set&shy;Xxx<\/code> methods to fill the bind context with goodies, and then you check if it all worked okay. If so, then you use <code>Get&shy;Bind&shy;Ctx<\/code> to get the resulting bind context and proceed on your way. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Setting the options.<\/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-44843","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Setting the options.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/44843","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=44843"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/44843\/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=44843"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=44843"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=44843"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}