{"id":96025,"date":"2017-04-24T07:00:00","date_gmt":"2017-04-24T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=96025"},"modified":"2020-12-22T07:18:09","modified_gmt":"2020-12-22T15:18:09","slug":"20170424-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170424-00\/?p=96025","title":{"rendered":"Filtering the Browse for Folder dialog so it shows only drive letters"},"content":{"rendered":"<p>Today, we&#8217;re going to customize the Browse for Folder dialog so it shows only drive letters.<\/p>\n<p>Start with <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131014-00\/?p=2943\"> our previous Browse for Folder customization program<\/a>, and make these changes:<\/p>\n<pre><span style=\"color: blue;\">\/\/ Lazy global variable\r\nPIDLIST_ABSOLUTE g_pidlMyComputer;<\/span>\r\n\r\nclass 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    <span style=\"color: blue;\">int compare = CompareDepth(pidlFolder);\r\n    if (compare &lt; 0) return S_OK;\r\n    if (compare &gt; 0) return S_FALSE;\r\n\r\n    STRRET str;\r\n    psf-&gt;GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &amp;str);\r\n    wchar_t buf[4];\r\n    if (SUCCEEDED(StrRetToBuf(&amp;str, pidlItem, buf, ARRAYSIZE(buf))) &amp;&amp;\r\n        PathIsRoot(buf)) return S_OK;\r\n    return S_FALSE;<\/span>\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) {        \r\n    <span style=\"color: blue;\">if (CompareDepth(pidlFolder) &gt; 0) *pgrfFlags = 0;<\/span>\r\n    return S_OK;\r\n  }\r\n\r\nprivate:\r\n  <span style=\"color: blue;\">static int CompareDepth(PCIDLIST_ABSOLUTE pidl)\r\n  {\r\n    if (pidl == nullptr) return -1;\r\n    if (ILIsEqual(pidl, g_pidlMyComputer)) return 0;\r\n    if (ILIsParent(pidl, g_pidlMyComputer, FALSE)) return -1;\r\n    return 1;\r\n  }<\/span>\r\n};\r\n\r\nint 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  <span style=\"color: blue;\">SHGetSpecialFolderLocation(nullptr, CSIDL_DRIVES, &amp;g_pidlMyComputer);\r\n  bi.pidlRoot = g_pidlMyComputer;<\/span>\r\n  bi.pszDisplayName = szDisplayName;\r\n  bi.lpfn = BrowseCallbackProc;\r\n  bi.ulFlags = BIF_NEWDIALOGSTYLE <span style=\"color: blue;\">| BIF_RETURNONLYFSDIRS<\/span>;\r\n  PIDLIST_ABSOLUTE pidl = SHBrowseForFolder(&amp;bi);\r\n  CoTaskMemFree(pidl);\r\n  <span style=\"color: blue;\">CoTaskMemFree(g_pidlMyComputer);<\/span>\r\n  return 0;\r\n}\r\n<\/pre>\n<p>Okay, let&#8217;s see what we&#8217;ve got.<\/p>\n<p>First, we declare a global variable to remember the location of what was once called <i>My Computer<\/i> but nowadays goes by the name <i>This PC<\/i>. Whatever it is, it&#8217;s the thing that contains your drive letters.<\/p>\n<p>The real work happens in the filter. Starting at the bottom, we have a method called <code>Check\u00adDepth<\/code> which determines whether the passed-in folder is an ancestor of, equal to, or a descendant of <i>My Computer<\/i>. Actually, we treat anything that isn&#8217;t a parent or equal to <i>My Computer<\/i> as if it were a descendant.<\/p>\n<p>The <code>Check\u00adDepth<\/code> method is method is a bit tricky for a few reasons. First, it treats the null pointer as equivalent to the desktop, so that it is the ancestor of everything. For whatever reason, that&#8217;s what <code>IFolder\u00adFilter<\/code> gives you, so we accommodate it.<\/p>\n<p>Second, if you pass <code>FALSE<\/code> to <code>ILIs\u00adParent<\/code>, it means that the function will return a nonzero value if the first ID list is an ancestor of <i>or is equal to<\/i> the second ID list. Therefore, we have to do the equality test first.<\/p>\n<p>Okay, working upward, the next method is <code>Get\u00adEnum\u00adFlags<\/code>. This is called when the Browse for Folder dialog wants to enumerate the children of a folder, and it&#8217;s our chance to influence what gets enumerated. We don&#8217;t want to expand the drives themselves, so if we have something that is a child of <i>My Computer<\/i>, we set the enumeration flags to zero, which means that nothing gets enumerated.<\/p>\n<p>The first method is <code>Should\u00adShow<\/code>. This is where most of the excitement is. You are given a folder and an item in that folder, and your job is to decide whether that item should be shown in the Browse for Folder dialog.<\/p>\n<p>First, we say that folders which are ancestors of <i>My Computer<\/i> can show all of their children. This ensures that the Browse for Folder dialog can reach <i>My Computer<\/i> in the first place.<\/p>\n<p>Second, we say that descendants of <i>My Computer<\/i> do not show any children. This is technically redundant because our <code>Get\u00adEnum\u00adFlags<\/code> prevented those children from being enumerated, but we&#8217;ll block them here just to be sure they don&#8217;t show up.<\/p>\n<p>Finally, if we are showing children of <i>My Computer<\/i> itself, we ask for the parsing name of the item and see if a drive root comes back. If the parsing name is longer than four characters, then the <code>Str\u00adRet\u00adTo\u00adBuf<\/code> function will fail with an insufficient-buffer error, in which case we know that we don&#8217;t have a drive root.<\/p>\n<p>The handy <code>Str\u00adRet\u00adTo\u00adBuf<\/code> function deals with <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040823-00\/?p=38073\"> the kooky <code>STRRET<\/code> structure<\/a> so we don&#8217;t have to.<\/p>\n<p>So that&#8217;s the filtering. The last changes are to <code>Win\u00adMain<\/code>: We obtain the item ID list for <i>My Computer<\/i> and set it as the root for the Browse for Folder dialog. (Remember that Little Programs do little to no error checking.) We also tell the Browse for Folder dialog that we require the user to select a file system object. That ensures that the <i>OK<\/i> button is disabled when the user is sitting at <i>My Computer<\/i>. And after the excitement is done, we clean up.<\/p>\n<p>There you have it. A Browse for Folder dialog that shows only drive letters.<\/p>\n<p>I&#8217;m not sure how useful this is, but I never claimed that this was useful.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>An exercise in filtering.<\/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-96025","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>An exercise in filtering.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96025","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=96025"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96025\/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=96025"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=96025"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=96025"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}