{"id":38393,"date":"2004-07-20T07:00:00","date_gmt":"2004-07-20T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2004\/07\/20\/querying-information-from-an-explorer-window\/"},"modified":"2004-07-20T07:00:00","modified_gmt":"2004-07-20T07:00:00","slug":"querying-information-from-an-explorer-window","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040720-00\/?p=38393","title":{"rendered":"Querying information from an Explorer window"},"content":{"rendered":"<p>\nSometimes software development is inventing new stuff.\nBut often, it&#8217;s just putting together the stuff you already have.\nToday&#8217;s puzzle is one of the latter type of problem.\n<\/p>\n<p>\nGiven a window handle, you can you determine (1)&nbsp;whether it is\nan Explorer window, and if so (2)&nbsp;what folder it is viewing, and\n(3)&nbsp;what item is currently focused.\n<\/p>\n<p>\nThis is not an inherently difficult task.\nYou just have to put together lots of small pieces.\n<\/p>\n<p>\nStart with\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/objects\/shellwindows\/shellwindows.asp\">\nthe ShellWindows object<\/a>\nwhich represents all the open shell windows.\nYou can enumerate through them all with\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/objects\/shellwindows\/item.asp\">the Item property<\/a>.\nThis is rather clumsy from C++ because the ShellWindows object\nwas designed for use by a scripting language like JScript or Visual Basic.\n<\/p>\n<pre>\n IShellWindows *psw;\n if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,\n                                IID_IShellWindows, (void**)&amp;psw))) {\n  VARIANT v;\n  V_VT(&amp;v) = VT_I4;\n  IDispatch  *pdisp;\n  BOOL fFound = FALSE;\n  for (V_I4(&amp;v) = 0; !fFound &amp;&amp; psw-&gt;Item(v, &amp;pdisp) == S_OK;\n       V_I4(&amp;v)++) {\n    ...\n    pdisp-&gt;Release();\n  }\n  psw-&gt;Release();\n }\n<\/pre>\n<p>\nFrom each item, we can ask it for its window handle and see if it&#8217;s the one\nwe want.<\/p>\n<pre>\n   IWebBrowserApp *pwba;\n   if (SUCCEEDED(pdisp-&gt;QueryInterface(IID_IWebBrowserApp, (void**)&amp;pwba))) {\n     HWND hwndWBA;\n     if (SUCCEEDED(pwba-&gt;get_HWND((LONG_PTR*)&amp;hwndWBA)) &amp;&amp;\n       hwndWBA == hwndFind) {\n       fFound = TRUE;\n       ...\n     }\n     pwba-&gt;Release();\n   }\n<\/pre>\n<p>\nOkay, now that we have found the folder via its IWebBrowserApp,\nwe need to get to the top shell browser.  This is done by\nquerying for the SID_STopLevelBrowser service and asking for\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/IShellBrowser\/IShellBrowser.asp\">\nthe IShellBrowser interface<\/a>.\n<\/p>\n<pre>\n       IServiceProvider *psp;\n       if (SUCCEEDED(pwba-&gt;QueryInterface(IID_IServiceProvider, (void**)&amp;psp))) {\n         IShellBrowser *psb;\n         if (SUCCEEDED(psp-&gt;QueryService(SID_STopLevelBrowser,\n                              IID_IShellBrowser, (void**)&amp;psb))) {\n           ...\n           psb-&gt;Release();\n         }\n         psp-&gt;Release();\n       }\n<\/pre>\n<p>\nFrom the IShellBrowser, we can ask for the current shell view\nvia\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/ishellbrowser\/queryactiveshellview.asp\">\nthe QueryActiveShellView method<\/a>.\n<\/p>\n<pre>\n           IShellView *psv;\n           if (SUCCEEDED(psb-&gt;QueryActiveShellView(&amp;psv))) {\n             ...\n             psv-&gt;Release();\n           }\n<\/pre>\n<p>\nOf course, what we really want is\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/ifolderview\/ifolderview.asp\">\nthe IFolderView interface<\/a>,\nwhich is the automation object that contains all the real goodies.\n<\/p>\n<pre>\n             IFolderView *pfv;\n             if (SUCCEEDED(psv-&gt;QueryInterface(IID_IFolderView,\n                                               (void**)&amp;pfv))) {\n               ...\n               pfv-&gt;Release();\n             }\n<\/pre>\n<p>\nOkay, now we&#8217;re golden.  What do you want to get from the view?\nHow about the location of the IShellFolder being viewed.\nTo do that, we need to use\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/ipersistfolder2\/ipersistfolder2.asp\">\nIPersistFolder2<\/a>::<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/ipersistfolder2\/getcurfolder.asp\">GetCurFolder<\/a>.\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/ifolderview\/getfolder.asp\">\nThe GetFolder method<\/a> will give us access to the shell folder,\nfrom which we ask for IPersistFolder2.\n(Most of the time you want the IShellFolder interface,\nsince that&#8217;s where most of the cool stuff hangs out.)\n<\/p>\n<pre>\n               IPersistFolder2 *ppf2;\n               if (SUCCEEDED(pfv-&gt;GetFolder(IID_IPersistFolder2,\n                                            (void**)&amp;ppf2))) {\n                 LPITEMIDLIST pidlFolder;\n                 if (SUCCEEDED(ppf2-&gt;GetCurFolder(&amp;pidlFolder))) {\n                   ...\n                   CoTaskMemFree(pidlFolder);\n                 }\n                 ppf2-&gt;Release();\n               }\n<\/pre>\n<p>\nLet&#8217;s convert that <code>pidl<\/code> into a path, for display purposes.\n<\/p>\n<pre>\n                   if (!SHGetPathFromIDList(pidlFolder, g_szPath)) {\n                     lstrcpyn(g_szPath, TEXT(\"&lt;not a directory&gt;\"), MAX_PATH);\n                   }\n                   ...\n<\/pre>\n<p>\nWhat else can we do with what we&#8217;ve got?  Oh right, let&#8217;s see what the\ncurrently-focused object is.\n<\/p>\n<pre>\n                   int iFocus;\n                   if (SUCCEEDED(pfv-&gt;GetFocusedItem(&amp;iFocus))) {\n                     ...\n                   }\n<\/pre>\n<p>\nLet&#8217;s display the name of the focused item.\nTo do that we need the item&#8217;s pidl and the IShellFolder.\n(See, I told you the IShellFolder is where the cool stuff is.)\nThe item comes from\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/ifolderview\/item.asp\">\nthe Item method<\/a> (surprisingly enough).\n<\/p>\n<pre>\n                     LPITEMIDLIST pidlItem;\n                     if (SUCCEEDED(pfv-&gt;Item(iFocus, &amp;pidlItem))) {\n                       ...\n                       CoTaskMemFree(pidlItem);\n                     }\n<\/pre>\n<p>\n(If we had wanted a list of selected items we could have used\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/ifolderview\/items.asp\">\nthe Items method<\/a>, passing SVGIO_SELECTION.)\n<\/p>\n<p>\nAfter we get the item&#8217;s pidl, we also need the IShellFolder:\n<\/p>\n<pre>\n                       IShellFolder *psf;\n                       if (SUCCEEDED(ppf2-&gt;QueryInterface(IID_IShellFolder,\n                                                          (void**)&amp;psf))) {\n                         ...\n                         psf-&gt;Release();\n                       }\n<\/pre>\n<p>\nThen we put the two together to get the item&#8217;s display name,\nwith the help of\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/ishellfolder\/getdisplaynameof.asp\">\nthe GetDisplayNameOf method<\/a>.\n<\/p>\n<pre>\n                         STRRET str;\n                         if (SUCCEEDED(psf-&gt;GetDisplayNameOf(pidlItem,\n                                                   SHGDN_INFOLDER,\n                                                   &amp;str))) {\n                           ...\n                         }\n<\/pre>\n<p>\nWe can use the helper function\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/shlwapi\/string\/strrettobuf.asp\">\nStrRetToBuf<\/a> to convert the kooky\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/structures\/strret.asp\">\nSTRRET structure<\/a> into\na boring string buffer.\n(The history of the kooky STRRET structure will have to wait for\nanother day.)\n<\/p>\n<pre>\n                           StrRetToBuf(&amp;str, pidlItem, g_szItem, MAX_PATH);\n<\/pre>\n<p>\nOkay, let&#8217;s put this all together.\nIt looks rather ugly because I put everything into one huge\nfunction instead of breaking them out into subfunctions.\nIn &#8220;real life&#8221; I would have broken things up into little helper\nfunctions to make things more manageable.\n<\/p>\n<p>\nStart with\n<a href=\"http:\/\/weblogs.asp.net\/oldnewthing\/archive\/2003\/07\/23\/54576.aspx\">the\nscratch program<\/a> and add this new function:\n<\/p>\n<pre>\n#include &lt;shlobj.h&gt;\n#include &lt;exdisp.h&gt;\nTCHAR g_szPath[MAX_PATH];\nTCHAR g_szItem[MAX_PATH];\nvoid CALLBACK RecalcText(HWND hwnd, UINT, UINT_PTR, DWORD)\n{\n HWND hwndFind = GetForegroundWindow();\n g_szPath[0] = TEXT('\\0');\n g_szItem[0] = TEXT('\\0');\n IShellWindows *psw;\n if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,\n                                IID_IShellWindows, (void**)&amp;psw))) {\n  VARIANT v;\n  V_VT(&amp;v) = VT_I4;\n  IDispatch  *pdisp;\n  BOOL fFound = FALSE;\n  for (V_I4(&amp;v) = 0; !fFound &amp;&amp; psw-&gt;Item(v, &amp;pdisp) == S_OK;\n       V_I4(&amp;v)++) {\n   IWebBrowserApp *pwba;\n   if (SUCCEEDED(pdisp-&gt;QueryInterface(IID_IWebBrowserApp, (void**)&amp;pwba))) {\n     HWND hwndWBA;\n     if (SUCCEEDED(pwba-&gt;get_HWND((LONG_PTR*)&amp;hwndWBA)) &amp;&amp;\n       hwndWBA == hwndFind) {\n       fFound = TRUE;\n       IServiceProvider *psp;\n       if (SUCCEEDED(pwba-&gt;QueryInterface(IID_IServiceProvider, (void**)&amp;psp))) {\n         IShellBrowser *psb;\n         if (SUCCEEDED(psp-&gt;QueryService(SID_STopLevelBrowser,\n                              IID_IShellBrowser, (void**)&amp;psb))) {\n           IShellView *psv;\n           if (SUCCEEDED(psb-&gt;QueryActiveShellView(&amp;psv))) {\n             IFolderView *pfv;\n             if (SUCCEEDED(psv-&gt;QueryInterface(IID_IFolderView,\n                                               (void**)&amp;pfv))) {\n               IPersistFolder2 *ppf2;\n               if (SUCCEEDED(pfv-&gt;GetFolder(IID_IPersistFolder2,\n                                            (void**)&amp;ppf2))) {\n                 LPITEMIDLIST pidlFolder;\n                 if (SUCCEEDED(ppf2-&gt;GetCurFolder(&amp;pidlFolder))) {\n                   if (!SHGetPathFromIDList(pidlFolder, g_szPath)) {\n                     lstrcpyn(g_szPath, TEXT(\"&lt;not a directory&gt;\"), MAX_PATH);\n                   }\n                   int iFocus;\n                   if (SUCCEEDED(pfv-&gt;GetFocusedItem(&amp;iFocus))) {\n                     LPITEMIDLIST pidlItem;\n                     if (SUCCEEDED(pfv-&gt;Item(iFocus, &amp;pidlItem))) {\n                       IShellFolder *psf;\n                       if (SUCCEEDED(ppf2-&gt;QueryInterface(IID_IShellFolder,\n                                                          (void**)&amp;psf))) {\n                         STRRET str;\n                         if (SUCCEEDED(psf-&gt;GetDisplayNameOf(pidlItem,\n                                                   SHGDN_INFOLDER,\n                                                   &amp;str))) {\n                           StrRetToBuf(&amp;str, pidlItem, g_szItem, MAX_PATH);\n                         }\n                         psf-&gt;Release();\n                       }\n                       CoTaskMemFree(pidlItem);\n                     }\n                   }\n                   CoTaskMemFree(pidlFolder);\n                 }\n                 ppf2-&gt;Release();\n               }\n               pfv-&gt;Release();\n             }\n             psv-&gt;Release();\n           }\n           psb-&gt;Release();\n         }\n         psp-&gt;Release();\n       }\n     }\n     pwba-&gt;Release();\n   }\n    pdisp-&gt;Release();\n  }\n  psw-&gt;Release();\n }\n InvalidateRect(hwnd, NULL, TRUE);\n}\n<\/pre>\n<p>\nNow all we have to do is call this function periodically\nand print the results.\n<\/p>\n<pre>\nBOOL\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\n{\n    <font COLOR=\"blue\">SetTimer(hwnd, 1, 1000, RecalcText);<\/font>\n    return TRUE;\n}\nvoid\nPaintContent(HWND hwnd, PAINTSTRUCT *pps)\n{\n<font COLOR=\"blue\">  TextOut(pps-&gt;hdc, 0, 0, g_szPath, lstrlen(g_szPath));\n  TextOut(pps-&gt;hdc, 0, 20, g_szItem, lstrlen(g_szItem));<\/font>\n}\n<\/pre>\n<p>\nWe&#8217;re ready to roll.  Run this program and set it to the side.\nThen launch an Explorer window and watch the program track the folder\nyou&#8217;re in and what item you have focused.\n<\/p>\n<p>\nOkay, so I hope I made my point:\nOften, the pieces you need are already there; you just have to\nfigure out how to put them together.  Notice that each of the\npieces is in itself not very big.  You just had to recognize\nthat they could be put together in an interesting way.\n<\/p>\n<p>\nExercise: Change this program so it takes the folder and\nswitches it to details view.\n<\/p>\n<p>\n[Raymond is currently on vacation; this message was pre-recorded.]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes software development is inventing new stuff. But often, it&#8217;s just putting together the stuff you already have. Today&#8217;s puzzle is one of the latter type of problem. Given a window handle, you can you determine (1)&nbsp;whether it is an Explorer window, and if so (2)&nbsp;what folder it is viewing, and (3)&nbsp;what item is currently [&hellip;]<\/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-38393","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Sometimes software development is inventing new stuff. But often, it&#8217;s just putting together the stuff you already have. Today&#8217;s puzzle is one of the latter type of problem. Given a window handle, you can you determine (1)&nbsp;whether it is an Explorer window, and if so (2)&nbsp;what folder it is viewing, and (3)&nbsp;what item is currently [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/38393","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=38393"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/38393\/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=38393"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=38393"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=38393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}