{"id":2943,"date":"2013-10-14T07:00:00","date_gmt":"2013-10-14T14:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/10\/14\/filtering-the-folders-that-appear-in-the-browse-for-folder-dialog\/"},"modified":"2020-12-22T07:45:57","modified_gmt":"2020-12-22T15:45:57","slug":"20131014-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131014-00\/?p=2943\/","title":{"rendered":"Filtering the folders that appear in the Browse for Folder dialog"},"content":{"rendered":"<p>Today&#8217;s Little Program applies an arbitrary filter to the <i>Browse for Folder<\/i> dialog: We will filter out drives smaller than 512<a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20090611-00\/?p=17933\">GB<\/a>. Hey, remember, I said that Little Programs <a href=\"http:\/\/www.guardian.co.uk\/film\/2009\/jan\/12\/woody-allen-vicky-cristina-barcelona\"> do not need motivation<\/a>.<\/p>\n<p>Today&#8217;s smart pointer class library is&#8230; (rolls dice)&#8230; WRL!<\/p>\n<pre>#define STRICT\r\n#define STRICT_TYPED_ITEMIDS\r\n#include &lt;windows.h&gt;\r\n#include &lt;shlobj.h&gt;\r\n#include &lt;propkey.h&gt;\r\n#include &lt;propvarutil.h&gt;\r\n#include &lt;wrl\/implements.h&gt;\r\nusing namespace Microsoft::WRL;\r\n\r\nstruct ComVariant : public VARIANT\r\n{\r\n    ComVariant() { VariantInit(this); }\r\n    ~ComVariant() { VariantClear(this); }\r\n};\r\n<\/pre>\n<p>WRL does not come with a &#8220;smart <code>VARIANT<\/code>&#8221; class analogous to ATL&#8217;s <code>CComVariant<\/code>, so we provide our own very simple one.<\/p>\n<pre>class CFunnyFilter :\r\n  public RuntimeClass&lt;\r\n                RuntimeClassFlags&lt;RuntimeClassType::ClassicCom&gt;,\r\n                IFolderFilter&gt;\r\n{\r\npublic:\r\n  \/\/ *** IFolderFilter ***\r\n  IFACEMETHODIMP ShouldShow(\r\n        IShellFolder* psf,\r\n        PCIDLIST_ABSOLUTE pidlFolder,\r\n        PCUITEMID_CHILD pidlItem)\r\n  {\r\n    if (!<a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130920-00\/?p=3173\">IsMyComputerFolder<\/a>(psf)) return S_OK;\r\n\r\n    ComPtr&lt;IShellFolder2&gt; spsf2;\r\n    if (FAILED(ComPtr&lt;IUnknown&gt;(psf).As(&amp;spsf2))) {\r\n      return S_OK;\r\n    }\r\n\r\n    ComVariant svt;\r\n    if (FAILED(spsf2-&gt;GetDetailsEx(pidlItem,\r\n                                   &amp;PKEY_Capacity, &amp;svt))) {\r\n      return S_OK;\r\n    }\r\n\r\n    if (VariantToUInt64WithDefault(svt, 0) &gt;=\r\n                             512ULL*1024ULL*1024ULL*1024ULL) {\r\n      return S_OK;\r\n    }\r\n\r\n    return S_FALSE;\r\n  }\r\n\r\n  IFACEMETHODIMP GetEnumFlags(\r\n        IShellFolder* psf,\r\n        PCIDLIST_ABSOLUTE pidlFolder,\r\n        HWND *phwnd,\r\n        DWORD *pgrfFlags) { return S_OK; }\r\n};\r\n<\/pre>\n<p>Our custom <code>Should\u00adShow<\/code> method first checks if we are showing children of <i>My Computer<\/i>. If not, then we allow the item to pass through the filter.<\/p>\n<p>Next, we convert the folder to <code>IShell\u00adFolder2<\/code>. If we can&#8217;t, then we allow the item to pass through the filter. (Arbitrary choice.)<\/p>\n<p>Next, we ask for the capacity of the item. If we can&#8217;t (no media in drive, or it&#8217;s not a drive in the first place), then we allow the item to pass through the filter. (Arbitrary choice.)<\/p>\n<p>Next, we look at the capacity, and if it&#8217;s at least 512GB, then we allow the item to pass through the filter.<\/p>\n<p>Otherwise, we have a drive smaller than 512GB, so we filter it out.<\/p>\n<p>That&#8217;s it! Let&#8217;s install this filter in the callback function:<\/p>\n<pre>int CALLBACK BrowseCallbackProc(HWND hwnd,\r\n    UINT uMsg, LPARAM lParam, LPARAM lpData)\r\n{\r\n  switch (uMsg) {\r\n  case BFFM_IUNKNOWN:\r\n    if (lParam) {\r\n      IUnknown *punk = reinterpret_cast&lt;IUnknown*&gt;(lParam);\r\n      ComPtr&lt;IFolderFilterSite&gt; spFilterSite;\r\n      if (SUCCEEDED(ComPtr&lt;IUnknown&gt;(punk).As(&amp;spFilterSite))) {\r\n        spFilterSite-&gt;SetFilter(Make&lt;CFunnyFilter&gt;().Get());\r\n      }\r\n    }\r\n    break;\r\n  }\r\n  return 0;\r\n}\r\n<\/pre>\n<p>When we get the <code>BFFM_<wbr \/>IUNKNOWN<\/code> message, we convert the <code>IUnknown<\/code> (cast to <code>LPARAM<\/code>) to a <code>IFolder\u00adFilter\u00adSite<\/code> and tell it to apply our custom filter.<\/p>\n<p>Okay, let&#8217;s plug it in and see if smoke comes out.<\/p>\n<pre>int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,\r\n                   LPSTR lpCmdLine, int nShowCmd)\r\n{\r\n  <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040520-00\/?p=39243\">CCoInitialize<\/a> init;\r\n  BROWSEINFO bi = { };\r\n  TCHAR szDisplayName[MAX_PATH];\r\n  bi.pszDisplayName = szDisplayName;\r\n  bi.lpfn = BrowseCallbackProc;\r\n  bi.ulFlags = BIF_NEWDIALOGSTYLE;\r\n  PIDLIST_ABSOLUTE pidl = SHBrowseForFolder(&amp;bi);\r\n  CoTaskMemFree(pidl);\r\n  return 0;\r\n}\r\n<\/pre>\n<p>The tricky part here is that we have to pass the <code>BIF_<wbr \/>NEW\u00adDIALOG\u00adSTYLE<\/code> flag because it is the new <i>Browse for Folder<\/i> dialog that sends the <code>BFFM_<wbr \/>IUNKNOWN<\/code> message.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>IFolderFilter.<\/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-2943","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>IFolderFilter.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2943","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=2943"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2943\/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=2943"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=2943"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=2943"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}