{"id":9763,"date":"2011-08-31T07:00:00","date_gmt":"2011-08-31T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/08\/31\/modernizing-our-simple-program-that-retrieves-information-about-the-items-in-the-recycle-bin\/"},"modified":"2011-08-31T07:00:00","modified_gmt":"2011-08-31T07:00:00","slug":"modernizing-our-simple-program-that-retrieves-information-about-the-items-in-the-recycle-bin","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110831-00\/?p=9763","title":{"rendered":"Modernizing our simple program that retrieves information about the items in the Recycle Bin"},"content":{"rendered":"<p>\nLast time,\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/08\/30\/10202076.aspx\">\nwe wrote a simple program to print various properties of the items\nin the Recycle Bin<\/a>,\nand we did so in the classical style,\nusing item ID lists and <code>IShell&shy;Folder<\/code>s.\nOne thing you may have noticed is that a lot of functions take\nthe combination of an <code>IShell&shy;Folder<\/code> and a\n<code>PCUITEMID_CHILD<\/code>.\nIn the shell namespace, operations on items usually happen\nby means of the pair (folder, child),\nand one of the common mistakes made by beginners is\nfailing to keep track of the pairing and passing child pidls\nto the wrong parent folder.\n<\/p>\n<p>\nEven if you&#8217;re not a beginner and are good at keeping track of\nwhich child pidls correspond to which parent folders,\nit&#8217;s still extra work you have to do,\nand it means that a lot of functions take two parameters in order to\ndescribe one thing.\n<\/p>\n<p>\nEnter <code>IShell&shy;Item<\/code>.\n<\/p>\n<p>\nThe <code>IShell&shy;Item<\/code> encapsulates the pair (folder, child).\nThis solves two problems:\n<\/p>\n<ol>\n<li>You only have to pass one thing around (the <code>IShell&shy;Item<\/code>)\n    instead of two (the <code>IShell&shy;Folder<\/code> and the\n    <code>PCUITEMID_CHILD<\/code>).<\/p>\n<li>By keeping track of the two items as a single unit,\n    it reduces the risk that you&#8217;ll accidentally use a child pidl\n    with the wrong parent folder.\n<\/ol>\n<p>\nAnother complexity of the classic shell interface is that there\nare a bunch of ways of obtaining COM objects from a shell folder:\n<\/p>\n<ul>\n<li><code>IShell&shy;Folder::Bind&shy;To&shy;Object<\/code>\n<li><code>IShell&shy;Folder::Bind&shy;To&shy;Storage<\/code>\n<li><code>IShell&shy;Folder::Create&shy;View&shy;Object<\/code>\n<li><code>IShell&shy;Folder::Get&shy;UI&shy;Object&shy;Of<\/code>\n<li><code>IUnknown::Query&shy;Interface<\/code>\n    (thanks to the desktop special case we saw last time).\n<\/ul>\n<p>\nThe <code>IShell&shy;Item::Bind&shy;To&shy;Handler<\/code>\ninterface hides these\nspecial-cases by dealing with them under the covers so you don&#8217;t have to.\nYou just call <code>IShell&shy;Item::Bind&shy;To&shy;Handler<\/code>\nand it figures\nout where to get the object and what weird special cases apply.\n(It also takes care of the weird <code>S_FALSE<\/code> return value\nfrom <code>IShell&shy;Folder::Enum&shy;Objects<\/code>.)\n<\/p>\n<p>\nAnd then there&#8217;s the annoyance of\n<code>IShell&shy;Folder::Get&shy;Display&shy;Name&shy;Of<\/code>\nusing\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/08\/23\/218837.aspx\">\nthe kooky <code>STRRET<\/code> structure<\/a>.\nThe <code>IShell&shy;Item::Get&shy;Display&shy;Name<\/code>\nfunction encapsulates that\naway for you by doing the work to convert that <code>STRRET<\/code>\ninto a boring string pointer.\n<\/p>\n<p>\nFirst up in modernizing our sample program\nis to change <code>Bind&shy;To&shy;Csidl<\/code> to return a shell item\ninstead of a shell folder.\n<\/p>\n<pre>\nHRESULT BindToCsidlItem(int csidl, IShellItem ** ppsi)\n{\n *ppsi = NULL;\n HRESULT hr;\n PIDLIST_ABSOLUTE pidl;\n hr = SHGetSpecialFolderLocation(NULL, csidl, &amp;pidl);\n if (SUCCEEDED(hr)) {\n  <font COLOR=\"blue\">hr = SHCreateShellItem(NULL, NULL, pidl, ppsi);<\/font>\n  CoTaskMemFree(pidl);\n }\n return hr;\n}\n<\/pre>\n<p>\nBut wait, since we&#8217;re modernizing, we may as well upgrade to\n<code>SHGet&shy;Known&shy;Folder&shy;ID&shy;List<\/code>:\n<\/p>\n<pre>\nHRESULT BindToKnownFolderItem(REFKNOWNFOLDER rfid, IShellItem ** ppsi)\n{\n *ppsi = NULL;\n HRESULT hr;\n PIDLIST_ABSOLUTE pidl;\n hr = SHGetKnownFolderIDList(rfid, 0, NULL, &amp;pidl);\n if (SUCCEEDED(hr)) {\n  <font COLOR=\"blue\">hr = SHCreateShellItem(NULL, NULL, pidl, ppsi);<\/font>\n  CoTaskMemFree(pidl);\n }\n return hr;\n}\n<\/pre>\n<p>\nHey wait, there&#8217;s a function for this already in Windows&nbsp;7!\nIt&#8217;s called <code>SHGet&shy;Known&shy;Folder&shy;Item<\/code>.\nYay, now we can delete the function entirely.\n<\/p>\n<p>\nNext, we convert <code>Print&shy;Display&shy;Name<\/code> to use\n<code>IShell&shy;Item<\/code> and the item-based display name flags\n<code>SIGDN<\/code>.\n<\/p>\n<pre>\nvoid PrintDisplayName(IShellItem *psi, SIGDN sigdn, PCTSTR pszLabel)\n{\n LPWSTR pszName;\n HRESULT hr = psi-&gt;GetDisplayName(sigdn, &amp;pszName);\n if (SUCCEEDED(hr)) {\n  _tprintf(TEXT(\"%s = %ws\\n\"), pszLabel, pszName);\n  CoTaskMemFree(pszName);\n }\n}\n<\/pre>\n<p>\nAnd then we convert <code>Print&shy;Detail<\/code> to use\n<code>IShell&shy;Item<\/code>.\nOh wait, now we&#8217;ve hit a snag:\nThe <code>IShell&shy;Item<\/code> interface doesn&#8217;t have a helper method\nthat wraps <code>IShell&shy;Folder2::Get&shy;Details&shy;Ex<\/code>.\nFortunately, there is a way to ask <code>IShell&shy;Item<\/code> to\nregurgitate the <code>IShell&shy;Folder<\/code>\nand <code>PITEMID_CHILD<\/code>\nthat it is wrapping:\nYou use the\n<code>IParent&shy;And&shy;Item::Get&shy;Parent&shy;And&shy;Item<\/code>\nmethod.\n<\/p>\n<pre>\nvoid PrintDetail(IShellItem *psi,\n    const SHCOLUMNID *pscid, PCTSTR pszLabel)\n{\n <font COLOR=\"blue\">IParentAndItem *ppni;\n HRESULT hr = psi-&gt;QueryInterface(IID_PPV_ARGS(&amp;ppni));\n if (SUCCEEDED(hr)) {\n  IShellFolder *psf;\n  PITEMID_CHILD pidl;\n  hr = ppni-&gt;GetParentAndItem(NULL, &amp;psf, &amp;pidl);\n  if (SUCCEEDED(hr)) {<\/font>\n   VARIANT vt;\n   hr = psf-&gt;GetDetailsEx(pidl, pscid, &amp;vt);\n   if (SUCCEEDED(hr)) {\n    hr = VariantChangeType(&amp;vt, &amp;vt, 0, VT_BSTR);\n    if (SUCCEEDED(hr)) {\n     _tprintf(TEXT(\"%s: %ws\\n\"), pszLabel, V_BSTR(&amp;vt));\n    }\n    VariantClear(&amp;vt);\n   }\n   <font COLOR=\"blue\">psf-&gt;Release();\n   CoTaskMemFree(pidl);\n  }\n }<\/font>\n}\n<\/pre>\n<p>\nWow, it looks like we lost ground there.\nAh, but Windows Vista extends <code>IShell&shy;Item<\/code> with the\n<code>IShell&shy;Item2<\/code> interface, and that has a bunch of new\nmethods for retrieving properties.\n<\/p>\n<pre>\nvoid PrintDetail(<font COLOR=\"blue\">IShellItem2<\/font> *psi,\n    const SHCOLUMNID *pscid, PCTSTR pszLabel)\n{\n  <font COLOR=\"blue\">PROPVARIANT vt;\n  HRESULT hr = psi-&gt;GetProperty(*pscid, &amp;vt);<\/font>\n  if (SUCCEEDED(hr)) {\n   hr = VariantChangeType(&amp;vt, &amp;vt, 0, VT_BSTR);\n   if (SUCCEEDED(hr)) {\n    _tprintf(TEXT(\"%s: %ws\\n\"), pszLabel, V_BSTR(&amp;vt));\n   }\n   <font COLOR=\"blue\">PropVariantClear(&amp;vt);<\/font>\n  }\n }\n}\n<\/pre>\n<p>\nBut wait, there&#8217;s more.\nThere&#8217;s a special accessor just for retrieving properties as strings!\n<\/p>\n<pre>\nvoid PrintDetail(IShellItem2 *psi2,\n    const SHCOLUMNID *pscid, PCTSTR pszLabel)\n{\n <font COLOR=\"blue\">LPWSTR pszValue;\n HRESULT hr = psi2-&gt;GetString(*pscid, &amp;pszValue);<\/font>\n if (SUCCEEDED(hr)) {\n  _tprintf(TEXT(\"%s: %ws\\n\"), pszLabel, pszValue);\n  <font COLOR=\"blue\">CoTaskMemFree(pszValue);<\/font>\n }\n}\n<\/pre>\n<p>\nOkay, that&#8217;s more like it.\nNow let&#8217;s update the main program.\n<\/p>\n<pre>\nint __cdecl _tmain(int argc, PTSTR *argv)\n{\n HRESULT hr = CoInitialize(NULL);\n if (SUCCEEDED(hr)) {\n  IShellItem *psiRecycleBin;\n  hr = SHGetKnownFolderItem(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT,\n                            NULL, IID_PPV_ARGS(&amp;psiRecycleBin));\n  if (SUCCEEDED(hr)) {\n   IEnumShellItems *pesi;\n   hr = psiRecycleBin-&gt;BindToHandler(NULL, BHID_EnumItems,\n                                     IID_PPV_ARGS(&amp;pesi));\n   if (<font COLOR=\"blue\">hr == S_OK<\/font>) {\n    IShellItem *psi;\n    while (pesi-&gt;Next(1, &amp;psi, NULL) == S_OK) {\n     IShellItem2 *psi2;\n     if (SUCCEEDED(psi-&gt;QueryInterface(IID_PPV_ARGS(&amp;psi2)))) {\n      _tprintf(TEXT(\"------------------\\n\"));\n      PrintDisplayName(psi2, SIGDN_PARENTRELATIVE,\n                             TEXT(\"ParentRelative\"));\n      PrintDisplayName(psi2, SIGDN_NORMALDISPLAY, TEXT(\"Normal\"));\n      PrintDisplayName(psi2, SIGDN_FILESYSPATH, TEXT(\"FileSys\"));\n      PrintDetail(psi2, &amp;SCID_OriginalLocation, TEXT(\"Original Location\"));\n      PrintDetail(psi2, &amp;SCID_DateDeleted, TEXT(\"Date deleted\"));\n      PrintDetail(psi2, &amp;PKEY_Size, TEXT(\"Size\"));\n      psi2-&gt;Release();\n     }\n     psi-&gt;Release();\n    }\n   }\n   psiRecycleBin-&gt;Release();\n  }\n  CoUninitialize();\n }\n return 0;\n}\n<\/pre>\n<p>\nOkay, so now we know how to enumerate the contents of the Recycle Bin\nand obtain properties of the items in it.\nHow do we purge or restore items?\nWe&#8217;ll look at that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time, we wrote a simple program to print various properties of the items in the Recycle Bin, and we did so in the classical style, using item ID lists and IShell&shy;Folders. One thing you may have noticed is that a lot of functions take the combination of an IShell&shy;Folder and a PCUITEMID_CHILD. In the [&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-9763","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Last time, we wrote a simple program to print various properties of the items in the Recycle Bin, and we did so in the classical style, using item ID lists and IShell&shy;Folders. One thing you may have noticed is that a lot of functions take the combination of an IShell&shy;Folder and a PCUITEMID_CHILD. In the [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9763","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=9763"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9763\/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=9763"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=9763"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=9763"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}