{"id":9753,"date":"2011-09-01T07:00:00","date_gmt":"2011-09-01T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/09\/01\/invoking-commands-on-items-in-the-recycle-bin\/"},"modified":"2024-06-10T15:03:50","modified_gmt":"2024-06-10T22:03:50","slug":"20110901-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110901-00\/?p=9753","title":{"rendered":"Invoking commands on items in the Recycle Bin"},"content":{"rendered":"<p>Once you&#8217;ve found the items you want in the Recycle Bin, you may want to perform some operation on them. This brings us back to our old friend, <code>IContextMenu<\/code>. At this point, you&#8217;re just <a href=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2009\/08\/04\/9856634.aspx\"> snapping two blocks together<\/a>. You have one block called <i>Retrieving properties from items in the Recycle Bin<\/i> and you have another block called <i>Invoking verbs on items<\/i>.<\/p>\n<p>For the first block, let&#8217;s assume you&#8217;ve written a function called <code>WantToRestoreThisItem<\/code> which studies the properties of a Recycle Bin item and determines whether you want to restore it. I leave this for you to implement, since I don&#8217;t know what your criteria are. Maybe you want to restore files only if they were deleted from a particular directory. Maybe you want to restore files that were deleted while you were drunk. (This assumes you have some other computer program that tracks when you&#8217;re drunk.)\u00b9 Whatever. It&#8217;s your function.<\/p>\n<p>For the second block, we have a helper function which <a title=\"How to host an IContextMenu, part 1 - Initial foray\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040920-00\/?p=37823\"> should look awfully familiar<\/a>.<\/p>\n<pre>void InvokeVerb(IContextMenu *pcm, PCSTR pszVerb)\r\n{\r\n HMENU hmenu = CreatePopupMenu();\r\n if (hmenu) {\r\n  HRESULT hr = pcm-&gt;QueryContextMenu(hmenu, 0, 1, 0x7FFF, CMF_NORMAL);\r\n  if(SUCCEEDED(hr)) {\r\n   CMINVOKECOMMANDINFO info = { 0 };\r\n   info.cbSize = sizeof(info);\r\n   info.lpVerb = pszVerb;\r\n   pcm-&gt;InvokeCommand(&amp;info);\r\n  }\r\n  DestroyMenu(hmenu);\r\n }\r\n}\r\n<\/pre>\n<p>And now we snap the two blocks together.<\/p>\n<pre>int __cdecl _tmain(int argc, PTSTR *argv)\r\n{\r\n HRESULT hr = CoInitialize(NULL);\r\n if (SUCCEEDED(hr)) {\r\n  IShellItem *psiRecycleBin;\r\n  hr = SHGetKnownFolderItem(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT,\r\n                            NULL, IID_PPV_ARGS(&amp;psiRecycleBin));\r\n  if (SUCCEEDED(hr)) {\r\n   IEnumShellItems *pesi;\r\n   hr = psiRecycleBin-&gt;BindToHandler(NULL, BHID_EnumItems,\r\n                                     IID_PPV_ARGS(&amp;pesi));\r\n   if (<span style=\"border: solid 1px black;\">hr == S_OK<\/span>) {\r\n    IShellItem *psi;\r\n    while (pesi-&gt;Next(1, &amp;psi, NULL) == S_OK) {\r\n     <span style=\"border: solid 1px currentcolor; border-bottom: none;\">if (<span style=\"text-decoration: underline wavy;\">WantToRestoreThisItem<\/span>(psi)) {              <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\"> IContextMenu *pcm;                            <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\"> hr = psi-&gt;BindToHandler(NULL, BHID_SFUIObject,<\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\">                         IID_PPV_ARGS(&amp;pcm);   <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\"> if (SUCCEEDED(hr)) {                          <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\">  <span style=\"text-decoration: underline wavy;\">InvokeVerb<\/span>(pcm, \"undelete\");                 <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\">  pcm-&gt;Release();                              <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\"> }                                             <\/span>\r\n     <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                              <\/span>\r\n     psi-&gt;Release();\r\n    }\r\n   }\r\n   psiRecycleBin-&gt;Release();\r\n  }\r\n  CoUninitialize();\r\n }\r\n return 0;\r\n}\r\n<\/pre>\n<p>One annoyance of the Recycle Bin is that, at least up until Windows\u00a07, it ignores the <code>CMIC_MASK_FLAG_NO_UI<\/code> flag. It always displays a confirmation dialog if something dangerous is about to happen (like overwriting an existing file). To mitigate this problem, we can at least reduce the number of confirmations from one-per-file to just one by batching up all the objects we want to operate on into a single context menu. For this, it&#8217;s easier to go back to the classical version of the program.<\/p>\n<pre>int __cdecl _tmain(int argc, PTSTR *argv)\r\n{\r\n HRESULT hr = CoInitialize(NULL);\r\n if (SUCCEEDED(hr)) {\r\n  IShellFolder2 *psfRecycleBin;\r\n  hr = BindToCsidl(CSIDL_BITBUCKET, IID_PPV_ARGS(&amp;psfRecycleBin));\r\n  if (SUCCEEDED(hr)) {\r\n   IEnumIDList *peidl;\r\n   hr = psfRecycleBin-&gt;EnumObjects(NULL,\r\n     SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &amp;peidl);\r\n   if (hr == S_OK) {\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ in a real program you wouldn't hard-code a fixed limit<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">PITEMID_CHILD rgpidlItems[100];                          <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">UINT cpidlItems = 0;                                     <\/span>\r\n    PITEMID_CHILD pidlItem;\r\n    while (peidl-&gt;Next(1, &amp;pidlItem, NULL) == S_OK) {\r\n     <span style=\"border: solid 1px currentcolor; border-bottom: none;\">if (WantToRestoreThisItem(psfRecycleBin, pidlItem) &amp;&amp; <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\">    cpidlItems &lt; ARRAYSIZE(rgpidlItems)) {            <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\"> rgpidlItems[cpidlItems++] = pidlItem;                <\/span>\r\n     <span style=\"border: solid 1px currentcolor; border-top: none;\">} else {                                              <\/span>\r\n      CoTaskMemFree(pidlItem);\r\n     <span style=\"border: solid 1px currentcolor;\">}                                                     <\/span>\r\n    }\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ restore the items we collected                      <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">if (cpidlItems) {                                      <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\"> IContextMenu *pcm;                                    <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\"> hr = psfRecycleBin-&gt;GetUIObjectOf(NULL, cpidlItems,   <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">                 (PCUITEMID_CHILD_ARRAY)rgpidlItems,   <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">                 IID_IContextMenu, NULL, (void**)&amp;pcm);<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\"> if (SUCCEEDED(hr)) {                                  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">  InvokeVerb(pcm, \"undelete\");                         <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">  pcm-&gt;Release();                                      <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\"> }                                                     <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\"> for (UINT i = 0; i &lt; cpidlItems; i++) {               <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">  CoTaskMemFree(rgpidlItems[i]);                       <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\"> }                                                     <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                                      <\/span>\r\n   }\r\n   psfRecycleBin-&gt;Release();\r\n  }\r\n  CoUninitialize();\r\n }\r\n return 0;\r\n}\r\n<\/pre>\n<p>In the course of the enumeration, we save the <code>ITEMIDLIST<\/code>s of all the items we want to restore, then create one giant context menu for all of them. This is the programmatic equivalent of multi-selecting the items from the Recycle Bin and then right-clicking. We then invoke the undelete verb on the entire group.<\/p>\n<p>Okay, so now suppose you want to restore the files, but instead of restoring them to their original locations, you want to restore them to a special folder. Like, say, <i>C:\\Files I deleted while I was drunk<\/i>.\u00b9 No problem. We just need a different block to snap into: <a href=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2008\/07\/24\/8768095.aspx\"> The drag\/drop block<\/a>.<\/p>\n<pre>void DropOnRestoreFolder(IDataObject *pdto)\r\n{\r\n IDropTarget *pdt;\r\n if (SUCCEEDED(GetUIObjectOfFile(NULL,\r\n        L\"C:\\\\Files I deleted while I was drunk\",\r\n        IID_PPV_ARGS(&amp;pdt)))) {\r\n  POINTL pt = { 0, 0 };\r\n  DWORD dwEffect = DROPEFFECT_MOVE;\r\n  if (SUCCEEDED(pdt-&gt;DragEnter(pdto, MK_LBUTTON,\r\n                               pt, &amp;dwEffect))) {\r\n   dwEffect &amp;= DROPEFFECT_MOVE;\r\n   if (dwEffect) {\r\n    pdt-&gt;Drop(pdto, MK_LBUTTON, pt, &amp;dwEffect);\r\n   } else {\r\n    pdt-&gt;DragLeave();\r\n   }\r\n  }\r\n  pdt-&gt;Release();\r\n }\r\n}\r\n<\/pre>\n<p>And now it&#8217;s just a matter of snapping out the undelete block and snapping in the drag\/drop block.<\/p>\n<pre>int __cdecl _tmain(int argc, PTSTR *argv)\r\n{\r\n HRESULT hr = CoInitialize(NULL);\r\n if (SUCCEEDED(hr)) {\r\n  IShellFolder2 *psfRecycleBin;\r\n  hr = BindToCsidl(CSIDL_BITBUCKET, IID_PPV_ARGS(&amp;psfRecycleBin));\r\n  if (SUCCEEDED(hr)) {\r\n   IEnumIDList *peidl;\r\n   hr = psfRecycleBin-&gt;EnumObjects(NULL,\r\n     SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &amp;peidl);\r\n   if (hr == S_OK) {\r\n    \/\/ in a real program you wouldn't hard-code a fixed limit\r\n    PITEMID_CHILD rgpidlItems[100];\r\n    UINT cpidlItems = 0;\r\n    PITEMID_CHILD pidlItem;\r\n    while (peidl-&gt;Next(1, &amp;pidlItem, NULL) == S_OK) {\r\n     if (WantToRestoreThisItem(psfRecycleBin, pidlItem) &amp;&amp;\r\n         cpidlItems &lt; ARRAYSIZE(rgpidlItems)) {\r\n      rgpidlItems[cpidlItems++] = pidlItem;\r\n     } else {\r\n      CoTaskMemFree(pidlItem);\r\n     }\r\n    }\r\n    \/\/ restore the items we collected\r\n    if (cpidlItems) {\r\n     <span style=\"border: solid 1px currentcolor; border-bottom: none;\">IDataObject *pdto;                                    <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\">hr = psfRecycleBin-&gt;GetUIObjectOf(NULL, cpidlItems,   <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\">                (PCUITEMID_CHILD_ARRAY)rgpidlItems,   <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\">                IID_IDataObject, NULL, (void**)&amp;pdto);<\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\">if (SUCCEEDED(hr)) {                                  <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\"> DropOnRestoreFolder(pdto);                           <\/span>\r\n     <span style=\"border: 1px currentcolor; border-style: none solid;\"> pdto-&gt;Release();                                     <\/span>\r\n     <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                                     <\/span>\r\n     for (UINT i = 0; i &lt; cpidlItems; i++) {\r\n      CoTaskMemFree(rgpidlItems[i]);\r\n     }\r\n    }\r\n   }\r\n   psfRecycleBin-&gt;Release();\r\n  }\r\n  CoUninitialize();\r\n }\r\n return 0;\r\n}\r\n<\/pre>\n<p><b>Footnotes<\/b><\/p>\n<p>\u00b9 If being drunk isn&#8217;t your thing, then substitute some other form of impaired judgment.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Our old friend, the IContextMenu.<\/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-9753","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Our old friend, the IContextMenu.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9753","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=9753"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9753\/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=9753"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=9753"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=9753"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}