{"id":93705,"date":"2016-06-20T07:00:00","date_gmt":"2016-06-20T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=93705"},"modified":"2019-03-13T11:51:12","modified_gmt":"2019-03-13T18:51:12","slug":"20160620-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160620-00\/?p=93705","title":{"rendered":"Peeking inside an IShellItem to see what it&#8217;s made of"},"content":{"rendered":"<p>Windows XP introduced <a HREF=\"https:\/\/msdn.microsoft.com\/library\/windows\/desktop\/bb761144(v=vs.85).aspx\">the <code>IShell&shy;Item<\/code> interface<\/a> which represents an item in the shell namespace. This <a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/\">encapsulates what traditionally is represented by a pair of things<\/a>, the <a HREF=\"https:\/\/msdn.microsoft.com\/library\/windows\/desktop\/bb775075(v=vs.85).aspx\">the <code>IShell&shy;Folder<\/code> interface<\/a> and an <a HREF=\"https:\/\/msdn.microsoft.com\/library\/windows\/desktop\/bb773321(v=vs.85).aspx\"><code>ITEMID_CHILD<\/code><\/a>. The shell item lets you carry just one object around instead of two. <\/p>\n<p>Another way of representing an item in the shell namespace is in the form of a single <code>ITEMID_ABSOLUTE<\/code>, and you can also create a shell item from that. <\/p>\n<p>Creating a single unit of currency to represent a shell item tries to solve the problem of <a HREF=\"https:\/\/en.wikipedia.org\/wiki\/Category:Currencies_replaced_by_the_euro\">having to exchange money every time you cross a boundary<\/a>. The <code>IShell&shy;Item<\/code> also gives you some methods which simplifies various operations by wrapping low-level methods on <code>IShell&shy;Folder<\/code>. For example, the <code>IShell&shy;Item::Bind&shy;To&shy;Handler<\/code> method figures out the right way to get the item you ask for rather than making you puzzle out the arcane rules behind <code>IShell&shy;Folder::Bind&shy;To&shy;Object<\/code>, <code>IShell&shy;Folder::Bind&shy;To&shy;Storage<\/code>, <code>IShell&shy;Folder::Create&shy;View&shy;Object<\/code>, <code>IShell&shy;Folder::Get&shy;UI&shy;Object&shy;Of<\/code>, and more. <\/p>\n<p>But what if you need something that <code>IShell&shy;Item<\/code> doesn&#8217;t provide a convenience wrapper for? Then you need to peek inside. <\/p>\n<p>If you want to peek inside and get the <code>IShell&shy;Folder<\/code> and <code>ITEMID_CHILD<\/code>, you can use <a HREF=\"https:\/\/msdn.microsoft.com\/library\/windows\/desktop\/bb775360(v=vs.85).aspx\">the <code>IParent&shy;And&shy;Item<\/code> interface<\/a>, specifically, <a HREF=\"https:\/\/msdn.microsoft.com\/library\/windows\/desktop\/bb775358(v=vs.85).aspx\">the <code>IParent&shy;And&shy;Item::Get&shy;Parent&shy;And&shy;Item<\/code> method<\/a>. Once nice thing about the <code>IParent&shy;And&shy;Item::Get&shy;Parent&shy;And&shy;Item<\/code> method is that you can pass <code>nullptr<\/code> for the things you aren&#8217;t interested in. <\/p>\n<p>Alternatively, if you want to peek inside and get the <code>ITEMIDLIST_ABSOLUTE<\/code>, then you can use <a HREF=\"https:\/\/msdn.microsoft.com\/library\/windows\/desktop\/bb775326(v=vs.85).aspx\">the <code>IPersist&shy;ID&shy;List::Get&shy;ID&shy;List<\/code> method<\/a> to suck it out. We saw this <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20130128-00\/?p=5433\">a while back<\/a>, but I&#8217;ll repeat it here just so the information is all in one place. <\/p>\n<p>If you are willing to abandon Windows XP support, you can use <a HREF=\"https:\/\/msdn.microsoft.com\/library\/windows\/desktop\/bb762184(v=vs.85).aspx\">the <code>SH&shy;Get&shy;ID&shy;List&shy;From&shy;Object<\/code> function<\/a> which knows how to do this. (It tries other things, too.) <\/p>\n<p>Okay, let&#8217;s take things out for a spin. We&#8217;ll get the normal display name for a shell item in four ways: <\/p>\n<ul>\n<li>By asking the item directly. \n<li>By using the <code>IShell&shy;Folder::Get&shy;Display&shy;Name<\/code>     method. \n<li>By using the     <code>IPersist&shy;ID&shy;List::Get&shy;ID&shy;List<\/code>     method,     and then the     <code>SH&shy;Get&shy;Name&shy;From&shy;ID&shy;List<\/code> function. \n<li>By using the     <code>SH&shy;Get&shy;ID&shy;List&shy;From&shy;Object<\/code>     function,     and then the     <code>SH&shy;Get&shy;Name&shy;From&shy;ID&shy;List<\/code> function. <\/ul>\n<p>If all goes well, we should get the same string printed each time. <\/p>\n<p>Remember that Little Programs do little to no error checking. <\/p>\n<pre>\n#include &lt;windows.h&gt;\n#include &lt;shlobj.h&gt;\n#include &lt;atlbase.h&gt;\n#include &lt;atlalloc.h&gt;\n#include &lt;stdio.h&gt;     \/\/ horrors! mixing C and C++!\n\nvoid PrintNameDirectlyFromItem(IShellItem* item)\n{\n    CComHeapPtr&lt;wchar_t&gt; name;\n    item-&gt;GetDisplayName(SIGDN_NORMALDISPLAY, &amp;name);\n    _putws(name);\n}\n\nvoid PrintNameViaIShellFolder(IShellItem* item)\n{\n    CComPtr&lt;IShellFolder&gt; folder;\n    CComHeapPtr&lt;ITEMID_CHILD&gt; child;\n    CComQIPtr&lt;IParentAndItem&gt;(item)-&gt;GetParentAndItem(nullptr, &amp;folder, &amp;child);\n    <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20040823-00\/?p=38073\">STRRET<\/a> ret;\n    folder-&gt;GetDisplayNameOf(child, SHGDN_NORMAL, &amp;ret);\n    CComHeapPtr&lt;wchar_t&gt; name;\n    StrRetToStrW(&amp;ret, child, &amp;name);\n    _putws(name);\n}\n\nvoid PrintNameViaAbsoluteIDList(IShellItem* item)\n{\n    CComHeapPtr&lt;ITEMIDLIST_ABSOLUTE&gt; absolute;\n    CComQIPtr&lt;IPersistIDList&gt;(item)-&gt;GetIDList(&amp;absolute);\n    CComHeapPtr&lt;wchar_t&gt; name;\n    SHGetNameFromIDList(absolute, SIGDN_NORMALDISPLAY, &amp;name);\n    _putws(name);\n}\n\nvoid PrintNameViaAbsoluteIDList2(IShellItem* item)\n{\n    CComHeapPtr&lt;ITEMIDLIST_ABSOLUTE&gt; absolute;\n    SHGetIDListFromObject(item, &amp;absolute);\n    CComHeapPtr&lt;wchar_t&gt; name;\n    SHGetNameFromIDList(absolute, SIGDN_NORMALDISPLAY, &amp;name);\n    _putws(name);\n}\n\nint main(int, char**)\n{\n    <a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/\">CCoInitialize<\/a> init;\n\n    CComPtr&lt;IShellItem&gt; item;\n    SHGetKnownFolderItem(FOLDERID_Downloads, KF_FLAG_DEFAULT, nullptr, IID_PPV_ARGS(&amp;item));\n\n    PrintNameDirectlyFromItem(item);\n    PrintNameViaIShellFolder(item);\n    PrintNameViaAbsoluteIDList(item);\n    PrintNameViaAbsoluteIDList2(item);\n\n    return 0;\n}\n<\/pre>\n<p><b>Bonus chatter<\/b>: When you create a shell item, it takes the things you created it from, and it produces the other equivalent things on demand. For example, if you create a shell item from an absolute item ID list, and then you ask for the folder and child item ID, it will convert the absolute item ID list into a folder and child item ID list. (It also caches the result so that the next time you ask, it&#8217;ll be able to answer the question more quickly.) <\/p>\n","protected":false},"excerpt":{"rendered":"<p>There are a folder and a pidl in there, but only if you look.<\/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-93705","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>There are a folder and a pidl in there, but only if you look.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93705","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=93705"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93705\/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=93705"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=93705"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=93705"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}