{"id":9773,"date":"2011-08-30T07:00:00","date_gmt":"2011-08-30T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/08\/30\/how-can-i-get-information-about-the-items-in-the-recycle-bin\/"},"modified":"2011-08-30T07:00:00","modified_gmt":"2011-08-30T07:00:00","slug":"how-can-i-get-information-about-the-items-in-the-recycle-bin","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110830-00\/?p=9773","title":{"rendered":"How can I get information about the items in the Recycle Bin?"},"content":{"rendered":"<p>\nFor some reason, a lot of people are interested in programmatic\naccess to the contents of the Recycle Bin.\nThey never explain why they care,\nso it&#8217;s possible that they are looking at their problem the wrong way.\n<\/p>\n<p>\nFor example, one reason for asking,\n&#8220;How do I purge an item from the Recycle Bin given a path?&#8221;\nis that\nsome operation in their program results in the files going\ninto the Recycle Bin and they want them to be deleted entirely.\nThe correct solution is to clear the <code>FOF_ALLOW&shy;UNDO<\/code> flag\nwhen deleting the items in the first place.\nMoving to the Recycle Bin and then purging is the wrong solution because\nyour search-and-destroy mission may purge more items than just the\nones your program put there.\n<\/p>\n<p>\nThe Recycle Bin is somewhat strange in that it can have multiple items\nwith the same name.\nCreate a text file called <code>TEST.TXT<\/code> on your desktop,\nthen delete it into the Recycle Bin.\nCreate another text file called <code>TEST.TXT<\/code> on your desktop,\nthen delete it into the Recycle Bin.\nNow open your Recycle Bin.\nHey look, you have two <code>TEST.TXT<\/code> files with the same path!\n<\/p>\n<p>\nNow look at that original problem:\nSuppose the program, as part of some operation, moves\nthe file <code>TEST.TXT<\/code> from the desktop to the Recycle Bin,\nand then the second half of the program goes into the Recycle Bin,\nfinds <code>TEST.TXT<\/code> and purges it.\nWell, there are actually three copies of <code>TEST.TXT<\/code> in the\nRecycle Bin, and only one of them is the one you wanted to purge.\n<\/p>\n<p>\nOkay, I got kind of sidetracked there.\nBack to the issue of getting information about the items in the\nRecycle Bin.\n<\/p>\n<p>\nThe Recycle Bin is a shell folder,\nand the way to enumerate the contents of a shell folder is to\nbind to it and enumerate its contents.\nThe low-level interface to the shell namespace is via\n<code>IShell&shy;Folder<\/code>.\nThere is an easier-to-use medium-level interface based on\n<code>IShell&shy;Item<\/code>,\nand there&#8217;s a high-level interface based on <code>Folder<\/code>\ndesigned for scripting.\n<\/p>\n<p>\nI&#8217;ll start with the low-level interface.\nAs usual, the program starts with a bunch of header files.\n<\/p>\n<pre>\n#include &lt;windows.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;tchar.h&gt;\n#include &lt;shlobj.h&gt;\n#include &lt;shlwapi.h&gt;\n#include &lt;propkey.h&gt;\n<\/pre>\n<p>\nThe <code>Bind&shy;To&shy;Csidl<\/code> function binds to a folder specified\nby a <code>CSIDL<\/code>.\nThe modern way to do this is via <code>KNOWN&shy;FOLDER<\/code>,\nbut just to keep you old fogeys happy, I&#8217;m doing things the\nclassic way since you refuse to upgrade from Windows&nbsp;XP.\n(We&#8217;ll look at the modern way later.)\n<\/p>\n<pre>\nHRESULT BindToCsidl(int csidl, REFIID riid, void **ppv)\n{\n HRESULT hr;\n PIDLIST_ABSOLUTE pidl;\n hr = SHGetSpecialFolderLocation(NULL, csidl, &amp;pidl);\n if (SUCCEEDED(hr)) {\n  IShellFolder *psfDesktop;\n  hr = SHGetDesktopFolder(&amp;psfDesktop);\n  if (SUCCEEDED(hr)) {\n   if (pidl-&gt;mkid.cb) {\n    hr = psfDesktop-&gt;BindToObject(pidl, NULL, riid, ppv);\n   } else {\n    hr = psfDesktop-&gt;QueryInterface(riid, ppv);\n   }\n   psfDesktop-&gt;Release();\n  }\n  CoTaskMemFree(pidl);\n }\n return hr;\n}\n<\/pre>\n<p>\nThe subtlety here is in the test for <code>pidl-&gt;mkid.cb<\/code>.\nThe <code>IShell&shy;Folder::Bind&shy;To&shy;Object<\/code>\nmethod is for binding\nto child objects (or grandchildren or deeper descendants).\nIf the object you want is the desktop itself, then you can&#8217;t use\n<code>IShell&shy;Folder::Bind&shy;To&shy;Object<\/code>\nsince the desktop is not\na child of itself.\nIn fact, if the object you want is the desktop itself,\nthen <i>you already have the desktop<\/i>,\nso we just <code>Query&shy;Interface<\/code> for it.\nIt&#8217;s an annoying special case which usually lurks in your code\nuntil somebody tries something like &#8220;Save file to desktop&#8221;\nor changes the location of a special folder to the desktop,\nand then\nboom you trip over the fact that the desktop is not a child of itself.\n(See further discussion below.)\n<\/p>\n<p>\nAnother helper function prints the display name of a shell namespace item.\nThere isn&#8217;t much interesting here either.\n<\/p>\n<pre>\nvoid PrintDisplayName(IShellFolder *psf,\n    PCUITEMID_CHILD pidl, SHGDNF uFlags, PCTSTR pszLabel)\n{\n <a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/08\/23\/218837.aspx\">STRRET<\/a> sr;\n HRESULT hr = psf-&gt;GetDisplayNameOf(pidl, uFlags, &amp;sr);\n if (SUCCEEDED(hr)) {\n  PTSTR pszName;\n  hr = StrRetToStr(&amp;sr, pidl, &amp;pszName);\n  if (SUCCEEDED(hr)) {\n   _tprintf(TEXT(\"%s = %s\\n\"), pszLabel, pszName);\n   CoTaskMemFree(pszName);\n  }\n }\n}\n<\/pre>\n<p>\nOur last helper function retrieves a property from the shell namespace\nand prints it.\n(Obviously, if we wanted to do something other than print it,\nwe could coerce the type to something other than <code>VT_BSTR<\/code>.)\n<\/p>\n<pre>\nvoid PrintDetail(IShellFolder2 *psf, PCUITEMID_CHILD pidl,\n    const SHCOLUMNID *pscid, PCTSTR pszLabel)\n{\n VARIANT vt;\n HRESULT 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}\n<\/pre>\n<p>\nOkay, now we can get down to business.\nThe properties we will display from each item in the Recycle Bin\nare the item name and path,\nthe original location (before the item was deleted),\nthe date the item was deleted,\nand the size of the item.\n<\/p>\n<p>\nGetting the name and path are done with\nvarious combinations of flags to\n<code>IShell&shy;Folder::Get&shy;Display&shy;Name&shy;Of<\/code>,\nwhereas getting the other properties involve talking to the\nshell property system.\n(My colleague\n<a HREF=\"http:\/\/blogs.msdn.com\/benkaras\/\">\nBen Karas<\/a>\ncovers the\n<a HREF=\"http:\/\/blogs.msdn.com\/benkaras\/archive\/tags\/Property+System\/default.aspx\">\nshell property system<\/a> on his blog.)\nThe\n<a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb759748(VS.85).aspx\">\n<code>SHCOLUMN&shy;ID<\/code> documentation<\/a>\nsays that the displaced property set applies to items which have been\nmoved to the Recycle Bin,\nso we can define those column IDs based on the values provided in\n<code>shlguid.h<\/code>:<\/p>\n<pre>\nconst SHCOLUMNID SCID_OriginalLocation =\n   { <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/08\/29\/10201372.aspx\">PSGUID_DISPLACED<\/a>, PID_DISPLACED_FROM };\nconst SHCOLUMNID SCID_DateDeleted =\n   { PSGUID_DISPLACED, PID_DISPLACED_DATE };\n<\/pre>\n<p>\nThe other property we want is\n<a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb787566(VS.85).aspx\">\n<code>System.Size<\/code><\/a>,\nwhich the documentation says is defined as\n<code>PKEY_Size<\/code> by the\n<code>propkey.h<\/code> header file.\n<\/p>\n<p>\nOkay, let&#8217;s roll!\n<\/p>\n<pre>\nint __cdecl _tmain(int argc, PTSTR *argv)\n{\n HRESULT hr = CoInitialize(NULL);\n if (SUCCEEDED(hr)) {\n  IShellFolder2 *psfRecycleBin;\n  hr = BindToCsidl(CSIDL_BITBUCKET, IID_PPV_ARGS(&amp;psfRecycleBin));\n  if (SUCCEEDED(hr)) {\n   IEnumIDList *peidl;\n   hr = psfRecycleBin-&gt;EnumObjects(NULL,\n     <a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2010\/04\/02\/9989235.aspx\">SHCONTF_FOLDERS | SHCONTF_NONFOLDERS<\/a>, &amp;peidl);\n   if (<font COLOR=\"blue\">hr == S_OK<\/font>) {\n    PITEMID_CHILD pidlItem;\n    while (peidl-&gt;Next(1, &amp;pidlItem, NULL) == S_OK) {\n     _tprintf(TEXT(\"------------------\\n\"));\n     PrintDisplayName(psfRecycleBin, pidlItem,\n                      SHGDN_INFOLDER, TEXT(\"InFolder\"));\n     PrintDisplayName(psfRecycleBin, pidlItem,\n                      SHGDN_NORMAL, TEXT(\"Normal\"));\n     PrintDisplayName(psfRecycleBin, pidlItem,\n                      SHGDN_FORPARSING, TEXT(\"ForParsing\"));\n     PrintDetail(psfRecycleBin, pidlItem,\n                 &amp;SCID_OriginalLocation, TEXT(\"Original Location\"));\n     PrintDetail(psfRecycleBin, pidlItem,\n                 &amp;SCID_DateDeleted, TEXT(\"Date deleted\"));\n     PrintDetail(psfRecycleBin, pidlItem,\n                 &amp;PKEY_Size, TEXT(\"Size\"));\n     CoTaskMemFree(pidlItem);\n    }\n   }\n   psfRecycleBin-&gt;Release();\n  }\n  CoUninitialize();\n }\n return 0;\n}\n<\/pre>\n<p>\nThe only tricky part is the test for whether the call to\n<code>IShell&shy;Folder::Enum&shy;Objects<\/code> succeeded,\nhighlighted above.\nAccording to\n<a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb775066(VS.85).aspx\">\nthe rules for\n<code>IShell&shy;Folder::Enum&shy;Objects<\/code><\/a>,\nthe method is allowed to\nreturn <code>S_FALSE<\/code> to indicate that there are no\nchildren, in which case it sets <code>peidl<\/code> to <code>NULL<\/code>.\n<\/p>\n<p>\nIf you are willing to call functions new to Windows Vista,\nyou can simplify the <code>Bind&shy;To&shy;Csidl<\/code> function\nby using the helper function <code>SHBind&shy;To&shy;Object<\/code>.\nThis does the work of getting the desktop folder and handling the\ndesktop special case.\n<\/p>\n<pre>\nHRESULT BindToCsidl(int csidl, REFIID riid, void **ppv)\n{\n HRESULT hr;\n PIDLIST_ABSOLUTE pidl;\n hr = SHGetSpecialFolderLocation(NULL, csidl, &amp;pidl);\n if (SUCCEEDED(hr)) {\n  <font COLOR=\"blue\">hr = SHBindToObject(NULL, pidl, NULL, riid, ppv);<\/font>\n  CoTaskMemFree(pidl);\n }\n return hr;\n}\n<\/pre>\n<p>\nBut at this point, I&#8217;m starting to steal from the topic I scheduled\nfor next time, namely modernizing this program to take advantage of\nsome new helper functions and interfaces.\nWe&#8217;ll continue next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For some reason, a lot of people are interested in programmatic access to the contents of the Recycle Bin. They never explain why they care, so it&#8217;s possible that they are looking at their problem the wrong way. For example, one reason for asking, &#8220;How do I purge an item from the Recycle Bin given [&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-9773","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>For some reason, a lot of people are interested in programmatic access to the contents of the Recycle Bin. They never explain why they care, so it&#8217;s possible that they are looking at their problem the wrong way. For example, one reason for asking, &#8220;How do I purge an item from the Recycle Bin given [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9773","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=9773"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9773\/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=9773"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=9773"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=9773"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}