1 /*
2  * Trash virtual folder support. The trashing engine is implemented in trash.c
3  *
4  * Copyright (C) 2006 Mikolaj Zalewski
5  * Copyright (C) 2009 Andrew Hill
6  * Copyright (C) 2018 Russell Johnson
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include <precomp.h>
24 
25 #include <mmsystem.h>
26 #include <ntquery.h>
27 
28 WINE_DEFAULT_DEBUG_CHANNEL(CRecycleBin);
29 
30 typedef struct
31 {
32     int column_name_id;
33     const GUID *fmtId;
34     DWORD pid;
35     int pcsFlags;
36     int fmt;
37     int cxChars;
38 } columninfo;
39 
40 static const columninfo RecycleBinColumns[] =
41 {
42     {IDS_SHV_COLUMN_NAME,     &FMTID_Storage,   PID_STG_NAME,        SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,  LVCFMT_LEFT,  25},
43     {IDS_SHV_COLUMN_DELFROM,  &FMTID_Displaced, PID_DISPLACED_FROM,  SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,  LVCFMT_LEFT,  35},
44     {IDS_SHV_COLUMN_DELDATE,  &FMTID_Displaced, PID_DISPLACED_DATE,  SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT,  15},
45     {IDS_SHV_COLUMN_SIZE,     &FMTID_Storage,   PID_STG_SIZE,        SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT,  LVCFMT_RIGHT, 10},
46     {IDS_SHV_COLUMN_TYPE,     &FMTID_Storage,   PID_STG_STORAGETYPE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT,  LVCFMT_LEFT,  15},
47     {IDS_SHV_COLUMN_MODIFIED, &FMTID_Storage,   PID_STG_WRITETIME,   SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT,  15},
48     /* {"creation time",  &FMTID_Storage,   PID_STG_CREATETIME, SHCOLSTATE_TYPE_DATE, LVCFMT_LEFT,  20}, */
49     /* {"attribs",        &FMTID_Storage,   PID_STG_ATTRIBUTES, SHCOLSTATE_TYPE_STR,  LVCFMT_LEFT,  20}, */
50 };
51 
52 #define COLUMN_NAME    0
53 #define COLUMN_DELFROM 1
54 #define COLUMN_DATEDEL 2
55 #define COLUMN_SIZE    3
56 #define COLUMN_TYPE    4
57 #define COLUMN_MTIME   5
58 
59 #define COLUMNS_COUNT  6
60 
61 /*
62  * Recycle Bin folder
63  */
64 
65 BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile);
66 
67 static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl);
68 
69 typedef struct _SEARCH_CONTEXT
70 {
71     PIDLRecycleStruct *pFileDetails;
72     HDELFILE hDeletedFile;
73     BOOL bFound;
74 } SEARCH_CONTEXT, *PSEARCH_CONTEXT;
75 
76 HRESULT CRecyclerExtractIcon_CreateInstance(
77     LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
78 {
79     PIDLRecycleStruct *pFileDetails = _ILGetRecycleStruct(pidl);
80     if (pFileDetails == NULL)
81         goto fallback;
82 
83     // Try to obtain the file
84     SEARCH_CONTEXT Context;
85     Context.pFileDetails = pFileDetails;
86     Context.bFound = FALSE;
87 
88     EnumerateRecycleBinW(NULL, CBSearchRecycleBin, &Context);
89     if (Context.bFound)
90     {
91         // This should be executed any time, if not, there are some errors in the implementation
92         IRecycleBinFile* pRecycleFile = (IRecycleBinFile*)Context.hDeletedFile;
93 
94         // Query the interface from the private interface
95         HRESULT hr = pRecycleFile->QueryInterface(riid, ppvOut);
96 
97         // Close the file handle as we don't need it anymore
98         CloseRecycleBinHandle(Context.hDeletedFile);
99 
100         return hr;
101     }
102 
103 fallback:
104     // In case the search fails we use a default icon
105     ERR("Recycler could not retrieve the icon, this shouldn't happen\n");
106 
107     CComPtr<IDefaultExtractIconInit> initIcon;
108     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
109     if (FAILED_UNEXPECTEDLY(hr))
110         return hr;
111 
112     initIcon->SetNormalIcon(swShell32Name, 0);
113 
114     return initIcon->QueryInterface(riid, ppvOut);
115 }
116 
117 class CRecycleBinEnum :
118     public CEnumIDListBase
119 {
120     private:
121     public:
122         CRecycleBinEnum();
123         ~CRecycleBinEnum();
124         HRESULT WINAPI Initialize(DWORD dwFlags);
125         static BOOL WINAPI CBEnumRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile);
126         BOOL WINAPI CBEnumRecycleBin(IN HDELFILE hDeletedFile);
127 
128         BEGIN_COM_MAP(CRecycleBinEnum)
129         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
130         END_COM_MAP()
131 };
132 
133 class CRecycleBinItemContextMenu :
134     public CComObjectRootEx<CComMultiThreadModelNoCS>,
135     public IContextMenu2
136 {
137     private:
138         LPITEMIDLIST                        apidl;
139     public:
140         CRecycleBinItemContextMenu();
141         ~CRecycleBinItemContextMenu();
142         HRESULT WINAPI Initialize(LPCITEMIDLIST pidl);
143 
144         // IContextMenu
145         STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override;
146         STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpcmi) override;
147         STDMETHOD(GetCommandString)(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) override;
148 
149         // IContextMenu2
150         STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
151 
152         BEGIN_COM_MAP(CRecycleBinItemContextMenu)
153         COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
154         COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
155         END_COM_MAP()
156 };
157 
158 BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile)
159 {
160     PSEARCH_CONTEXT pContext = (PSEARCH_CONTEXT)Context;
161 
162     PDELETED_FILE_DETAILS_W pFileDetails;
163     DWORD dwSize;
164     BOOL ret;
165 
166     if (!GetDeletedFileDetailsW(hDeletedFile,
167                                 0,
168                                 NULL,
169                                 &dwSize) &&
170             GetLastError() != ERROR_INSUFFICIENT_BUFFER)
171     {
172         ERR("GetDeletedFileDetailsW failed\n");
173         return FALSE;
174     }
175 
176     pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
177     if (!pFileDetails)
178     {
179         ERR("No memory\n");
180         return FALSE;
181     }
182 
183     if (!GetDeletedFileDetailsW(hDeletedFile,
184                                 dwSize,
185                                 pFileDetails,
186                                 NULL))
187     {
188         ERR("GetDeletedFileDetailsW failed\n");
189         SHFree(pFileDetails);
190         return FALSE;
191     }
192 
193     ret = memcmp(pFileDetails, pContext->pFileDetails, dwSize);
194     if (!ret)
195     {
196         pContext->hDeletedFile = hDeletedFile;
197         pContext->bFound = TRUE;
198     }
199     else
200         CloseRecycleBinHandle(hDeletedFile);
201 
202     SHFree(pFileDetails);
203     return ret;
204 }
205 
206 static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl)
207 {
208     LPPIDLDATA pdata = _ILGetDataPointer(pidl);
209 
210     if (pdata && pdata->type == 0x00)
211         return (PIDLRecycleStruct*) & (pdata->u.crecycle);
212 
213     return NULL;
214 }
215 
216 CRecycleBinEnum::CRecycleBinEnum()
217 {
218 }
219 
220 CRecycleBinEnum::~CRecycleBinEnum()
221 {
222 }
223 
224 HRESULT WINAPI CRecycleBinEnum::Initialize(DWORD dwFlags)
225 {
226     WCHAR szDrive[8];
227     if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive) - 1))
228     {
229         ERR("GetEnvironmentVariableW failed\n");
230         return E_FAIL;
231     }
232     PathAddBackslashW(szDrive);
233 
234     if (dwFlags & SHCONTF_NONFOLDERS)
235     {
236         TRACE("Starting Enumeration\n");
237 
238         if (!EnumerateRecycleBinW(szDrive, CBEnumRecycleBin, this))
239         {
240             WARN("Error: EnumerateCRecycleBinW failed\n");
241             return E_FAIL;
242         }
243     }
244     else
245     {
246         // do nothing
247     }
248     return S_OK;
249 }
250 
251 static LPITEMIDLIST _ILCreateRecycleItem(PDELETED_FILE_DETAILS_W pFileDetails)
252 {
253     PIDLDATA tmp;
254     LPITEMIDLIST pidl;
255     PIDLRecycleStruct * p;
256     int size0 = (char*)&tmp.u.crecycle.szName - (char*)&tmp.u.crecycle;
257     int size = size0;
258 
259     tmp.type = 0x00;
260     size += (wcslen(pFileDetails->FileName) + 1) * sizeof(WCHAR);
261 
262     pidl = (LPITEMIDLIST)SHAlloc(size + 4);
263     if (!pidl)
264         return pidl;
265 
266     pidl->mkid.cb = size + 2;
267     memcpy(pidl->mkid.abID, &tmp, 2 + size0);
268 
269     p = &((PIDLDATA*)pidl->mkid.abID)->u.crecycle;
270     RtlCopyMemory(p, pFileDetails, sizeof(DELETED_FILE_DETAILS_W));
271     wcscpy(p->szName, pFileDetails->FileName);
272     *(WORD*)((char*)pidl + (size + 2)) = 0;
273     return pidl;
274 }
275 
276 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile)
277 {
278     return static_cast<CRecycleBinEnum *>(Context)->CBEnumRecycleBin(hDeletedFile);
279 }
280 
281 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN HDELFILE hDeletedFile)
282 {
283     PDELETED_FILE_DETAILS_W pFileDetails;
284     DWORD dwSize;
285     LPITEMIDLIST pidl = NULL;
286     BOOL ret;
287 
288     if (!GetDeletedFileDetailsW(hDeletedFile,
289                                 0,
290                                 NULL,
291                                 &dwSize) &&
292             GetLastError() != ERROR_INSUFFICIENT_BUFFER)
293     {
294         ERR("GetDeletedFileDetailsW failed\n");
295         return FALSE;
296     }
297 
298     pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
299     if (!pFileDetails)
300     {
301         ERR("No memory\n");
302         return FALSE;
303     }
304 
305     if (!GetDeletedFileDetailsW(hDeletedFile,
306                                 dwSize,
307                                 pFileDetails,
308                                 NULL))
309     {
310         ERR("GetDeletedFileDetailsW failed\n");
311         SHFree(pFileDetails);
312         return FALSE;
313     }
314 
315     pidl = _ILCreateRecycleItem(pFileDetails);
316     if (!pidl)
317     {
318         SHFree(pFileDetails);
319         return FALSE;
320     }
321 
322     ret = AddToEnumList(pidl);
323 
324     if (!ret)
325         SHFree(pidl);
326     SHFree(pFileDetails);
327     TRACE("Returning %d\n", ret);
328     CloseRecycleBinHandle(hDeletedFile);
329     return ret;
330 }
331 
332 /**************************************************************************
333 * IContextMenu2 Bitbucket Item Implementation
334 */
335 
336 CRecycleBinItemContextMenu::CRecycleBinItemContextMenu()
337 {
338     apidl = NULL;
339 }
340 
341 CRecycleBinItemContextMenu::~CRecycleBinItemContextMenu()
342 {
343     ILFree(apidl);
344 }
345 
346 HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST pidl)
347 {
348     apidl = ILClone(pidl);
349     if (apidl == NULL)
350         return E_OUTOFMEMORY;
351     return S_OK;
352 }
353 
354 HRESULT WINAPI CRecycleBinItemContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
355 {
356     WCHAR szBuffer[30] = {0};
357     ULONG Count = 1;
358 
359     TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
360 
361     if (LoadStringW(shell32_hInstance, IDS_RESTORE, szBuffer, _countof(szBuffer)))
362     {
363         szBuffer[_countof(szBuffer)-1] = L'\0';
364         _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_ENABLED);
365         Count++;
366     }
367 
368     if (LoadStringW(shell32_hInstance, IDS_CUT, szBuffer, _countof(szBuffer)))
369     {
370         _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
371         szBuffer[_countof(szBuffer)-1] = L'\0';
372         _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED);
373     }
374 
375     if (LoadStringW(shell32_hInstance, IDS_DELETE, szBuffer, _countof(szBuffer)))
376     {
377         szBuffer[_countof(szBuffer)-1] = L'\0';
378         _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
379         _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED);
380     }
381 
382     if (LoadStringW(shell32_hInstance, IDS_PROPERTIES, szBuffer, _countof(szBuffer)))
383     {
384         szBuffer[_countof(szBuffer)-1] = L'\0';
385         _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
386         _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_DEFAULT);
387     }
388 
389     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Count);
390 }
391 
392 HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
393 {
394     SEARCH_CONTEXT Context;
395     WCHAR szDrive[8];
396 
397     TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi, lpcmi->lpVerb, lpcmi->hwnd);
398 
399     if (lpcmi->lpVerb == MAKEINTRESOURCEA(1) || lpcmi->lpVerb == MAKEINTRESOURCEA(5))
400     {
401         Context.pFileDetails = _ILGetRecycleStruct(apidl);
402         Context.bFound = FALSE;
403 
404         if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive) - 1))
405         {
406             ERR("GetEnvironmentVariableW failed\n");
407             return E_FAIL;
408         }
409         PathAddBackslashW(szDrive);
410 
411         EnumerateRecycleBinW(szDrive, CBSearchRecycleBin, (PVOID)&Context);
412         if (!Context.bFound)
413             return E_FAIL;
414 
415         BOOL ret = TRUE;
416 
417         /* restore file */
418         if (lpcmi->lpVerb == MAKEINTRESOURCEA(1))
419             ret = RestoreFile(Context.hDeletedFile);
420         /* delete file */
421         else
422             DeleteFileHandleToRecycleBin(Context.hDeletedFile);
423 
424         CloseRecycleBinHandle(Context.hDeletedFile);
425 
426         return (ret ? S_OK : E_FAIL);
427     }
428     else if (lpcmi->lpVerb == MAKEINTRESOURCEA(3))
429     {
430         FIXME("implement cut\n");
431         return E_NOTIMPL;
432     }
433     else if (lpcmi->lpVerb == MAKEINTRESOURCEA(7))
434     {
435         FIXME("implement properties\n");
436         return E_NOTIMPL;
437     }
438 
439     return S_OK;
440 }
441 
442 HRESULT WINAPI CRecycleBinItemContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
443 {
444     TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
445 
446     return E_FAIL;
447 }
448 
449 HRESULT WINAPI CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
450 {
451     TRACE("CRecycleBin_IContextMenu2Item_HandleMenuMsg (%p)->(msg=%x wp=%lx lp=%lx)\n", this, uMsg, wParam, lParam);
452 
453     return E_NOTIMPL;
454 }
455 
456 CRecycleBin::CRecycleBin()
457 {
458     pidl = NULL;
459 }
460 
461 CRecycleBin::~CRecycleBin()
462 {
463     SHFree(pidl);
464 }
465 
466 /*************************************************************************
467  * RecycleBin IPersistFolder2 interface
468  */
469 
470 HRESULT WINAPI CRecycleBin::GetClassID(CLSID *pClassID)
471 {
472     TRACE("(%p, %p)\n", this, pClassID);
473     if (pClassID == NULL)
474         return E_INVALIDARG;
475     memcpy(pClassID, &CLSID_RecycleBin, sizeof(CLSID));
476     return S_OK;
477 }
478 
479 HRESULT WINAPI CRecycleBin::Initialize(PCIDLIST_ABSOLUTE pidl)
480 {
481     TRACE("(%p, %p)\n", this, pidl);
482 
483     SHFree((LPVOID)this->pidl);
484     this->pidl = ILClone(pidl);
485     if (this->pidl == NULL)
486         return E_OUTOFMEMORY;
487     return S_OK;
488 }
489 
490 HRESULT WINAPI CRecycleBin::GetCurFolder(PIDLIST_ABSOLUTE *ppidl)
491 {
492     TRACE("\n");
493     *ppidl = ILClone(pidl);
494     return S_OK;
495 }
496 
497 /*************************************************************************
498  * RecycleBin IShellFolder2 interface
499  */
500 
501 HRESULT WINAPI CRecycleBin::ParseDisplayName(HWND hwnd, LPBC pbc,
502         LPOLESTR pszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl,
503         ULONG *pdwAttributes)
504 {
505     FIXME("stub\n");
506     return E_NOTIMPL;
507 }
508 
509 
510 PDELETED_FILE_DETAILS_W
511 UnpackDetailsFromPidl(LPCITEMIDLIST pidl)
512 {
513     return (PDELETED_FILE_DETAILS_W)&pidl->mkid.abID;
514 }
515 
516 HRESULT WINAPI CRecycleBin::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
517 {
518     return ShellObjectCreatorInit<CRecycleBinEnum>(dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
519 }
520 
521 HRESULT WINAPI CRecycleBin::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
522 {
523     FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
524     return E_NOTIMPL;
525 }
526 
527 HRESULT WINAPI CRecycleBin::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
528 {
529     FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
530     return E_NOTIMPL;
531 }
532 
533 HRESULT WINAPI CRecycleBin::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
534 {
535     PIDLRecycleStruct* pData1 = _ILGetRecycleStruct(pidl1);
536     PIDLRecycleStruct* pData2 = _ILGetRecycleStruct(pidl2);
537     LPWSTR pName1, pName2;
538 
539     if(!pData1 || !pData2 || LOWORD(lParam) >= COLUMNS_COUNT)
540         return E_INVALIDARG;
541 
542     SHORT result;
543     LONGLONG diff;
544     switch (LOWORD(lParam))
545     {
546         case 0: /* Name */
547             pName1 = PathFindFileNameW(pData1->szName);
548             pName2 = PathFindFileNameW(pData2->szName);
549             result = wcsicmp(pName1, pName2);
550             break;
551         case 1: /* Orig. Location */
552             result = wcsicmp(pData1->szName, pData2->szName);
553             break;
554         case 2: /* Date Deleted */
555             result = CompareFileTime(&pData1->DeletionTime, &pData2->DeletionTime);
556             break;
557         case 3: /* Size */
558             diff = pData1->FileSize.QuadPart - pData2->FileSize.QuadPart;
559             return MAKE_COMPARE_HRESULT(diff);
560         case 4: /* Type */
561             pName1 = PathFindExtensionW(pData1->szName);
562             pName2 = PathFindExtensionW(pData2->szName);
563             result = wcsicmp(pName1, pName2);
564             break;
565         case 5: /* Modified */
566             result = CompareFileTime(&pData1->LastModification, &pData2->LastModification);
567             break;
568     }
569     return MAKE_COMPARE_HRESULT(result);
570 }
571 
572 HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv)
573 {
574     CComPtr<IShellView> pShellView;
575     HRESULT hr = E_NOINTERFACE;
576 
577     TRACE("(%p, %p, %s, %p)\n", this, hwndOwner, debugstr_guid(&riid), ppv);
578 
579     if (!ppv)
580         return hr;
581 
582     *ppv = NULL;
583 
584     if (IsEqualIID (riid, IID_IDropTarget))
585     {
586         hr = CRecyclerDropTarget_CreateInstance(riid, ppv);
587     }
588     else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid, IID_IContextMenu2))
589     {
590         hr = this->QueryInterface(riid, ppv);
591     }
592     else if (IsEqualIID (riid, IID_IShellView))
593     {
594         SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
595         hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppv);
596     }
597     else
598         return hr;
599 
600     TRACE ("-- (%p)->(interface=%p)\n", this, ppv);
601     return hr;
602 
603 }
604 
605 HRESULT WINAPI CRecycleBin::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
606         SFGAOF *rgfInOut)
607 {
608     TRACE("(%p, %d, {%p, ...}, {%x})\n", this, cidl, apidl ? apidl[0] : NULL, (unsigned int)*rgfInOut);
609     *rgfInOut &= SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANLINK;
610     return S_OK;
611 }
612 
613 HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
614         REFIID riid, UINT *prgfInOut, void **ppv)
615 {
616     LPVOID pObj = NULL;
617     HRESULT hr = E_INVALIDARG;
618 
619     TRACE ("(%p)->(%p,%u,apidl=%p, %p %p)\n", this,
620            hwndOwner, cidl, apidl, prgfInOut, ppv);
621 
622     if (!ppv)
623         return hr;
624 
625     *ppv = NULL;
626 
627     if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2)) && (cidl >= 1))
628     {
629         hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(apidl[0], riid, &pObj);
630     }
631     else if((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1))
632     {
633         hr = CRecyclerExtractIcon_CreateInstance(apidl[0], riid, &pObj);
634     }
635     else
636         hr = E_NOINTERFACE;
637 
638     if (SUCCEEDED(hr) && !pObj)
639         hr = E_OUTOFMEMORY;
640 
641     *ppv = pObj;
642     TRACE ("(%p)->hr=0x%08x\n", this, hr);
643     return hr;
644 }
645 
646 HRESULT WINAPI CRecycleBin::GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF uFlags, STRRET *pName)
647 {
648     PIDLRecycleStruct *pFileDetails;
649     LPWSTR pFileName;
650 
651     TRACE("(%p, %p, %x, %p)\n", this, pidl, (unsigned int)uFlags, pName);
652 
653     pFileDetails = _ILGetRecycleStruct(pidl);
654     if (!pFileDetails)
655     {
656         pName->cStr[0] = 0;
657         pName->uType = STRRET_CSTR;
658         return E_INVALIDARG;
659     }
660 
661     pFileName = wcsrchr(pFileDetails->szName, L'\\');
662     if (!pFileName)
663     {
664         pName->cStr[0] = 0;
665         pName->uType = STRRET_CSTR;
666         return E_UNEXPECTED;
667     }
668 
669     pName->pOleStr = StrDupW(pFileName + 1);
670     if (pName->pOleStr == NULL)
671         return E_OUTOFMEMORY;
672 
673     pName->uType = STRRET_WSTR;
674     return S_OK;
675 }
676 
677 HRESULT WINAPI CRecycleBin::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pszName,
678                                       SHGDNF uFlags, PITEMID_CHILD *ppidlOut)
679 {
680     TRACE("\n");
681     return E_FAIL; /* not supported */
682 }
683 
684 HRESULT WINAPI CRecycleBin::GetDefaultSearchGUID(GUID *pguid)
685 {
686     FIXME("stub\n");
687     return E_NOTIMPL;
688 }
689 
690 HRESULT WINAPI CRecycleBin::EnumSearches(IEnumExtraSearch **ppEnum)
691 {
692     FIXME("stub\n");
693     *ppEnum = NULL;
694     return E_NOTIMPL;
695 }
696 
697 HRESULT WINAPI CRecycleBin::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
698 {
699     TRACE("(%p, %x, %p, %p)\n", this, (unsigned int)dwReserved, pSort, pDisplay);
700     if (pSort)
701         *pSort = 0;
702     if (pDisplay)
703         *pDisplay = 0;
704     return S_OK;
705 }
706 
707 HRESULT WINAPI CRecycleBin::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
708 {
709     TRACE("(%p, %d, %p)\n", this, iColumn, pcsFlags);
710     if (iColumn >= COLUMNS_COUNT)
711         return E_INVALIDARG;
712     *pcsFlags = RecycleBinColumns[iColumn].pcsFlags;
713     return S_OK;
714 }
715 
716 HRESULT WINAPI CRecycleBin::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
717 {
718     FIXME("stub\n");
719     return E_NOTIMPL;
720 }
721 
722 static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME * ft)
723 {
724     FILETIME lft;
725     SYSTEMTIME time;
726     int ret;
727 
728     FileTimeToLocalFileTime(ft, &lft);
729     FileTimeToSystemTime(&lft, &time);
730 
731     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size);
732     if (ret > 0 && ret < size)
733     {
734         /* Append space + time without seconds */
735         buffer[ret-1] = ' ';
736         GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret);
737     }
738 
739     return (ret != 0 ? E_FAIL : S_OK);
740 }
741 
742 HRESULT WINAPI CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, LPSHELLDETAILS pDetails)
743 {
744     PIDLRecycleStruct * pFileDetails;
745     WCHAR buffer[MAX_PATH];
746     WCHAR szTypeName[100];
747     LPWSTR pszBackslash;
748     UINT Length;
749 
750     TRACE("(%p, %p, %d, %p)\n", this, pidl, iColumn, pDetails);
751     if (iColumn >= COLUMNS_COUNT)
752         return E_FAIL;
753     pDetails->fmt = RecycleBinColumns[iColumn].fmt;
754     pDetails->cxChar = RecycleBinColumns[iColumn].cxChars;
755     if (pidl == NULL)
756         return SHSetStrRet(&pDetails->str, RecycleBinColumns[iColumn].column_name_id);
757 
758     if (iColumn == COLUMN_NAME)
759         return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
760 
761     pFileDetails = _ILGetRecycleStruct(pidl);
762     switch (iColumn)
763     {
764         case COLUMN_DATEDEL:
765             FormatDateTime(buffer, MAX_PATH, &pFileDetails->DeletionTime);
766             break;
767         case COLUMN_DELFROM:
768             pszBackslash = wcsrchr(pFileDetails->szName, L'\\');
769             Length = (pszBackslash - pFileDetails->szName);
770             memcpy((LPVOID)buffer, pFileDetails->szName, Length * sizeof(WCHAR));
771             buffer[Length] = UNICODE_NULL;
772             if (buffer[0] && buffer[1] == L':' && !buffer[2])
773             {
774                 buffer[2] = L'\\';
775                 buffer[3] = UNICODE_NULL;
776             }
777             break;
778         case COLUMN_SIZE:
779             StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH);
780             break;
781         case COLUMN_MTIME:
782             FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification);
783             break;
784         case COLUMN_TYPE:
785             {
786                 SEARCH_CONTEXT Context;
787                 Context.pFileDetails = pFileDetails;
788                 Context.bFound = FALSE;
789                 EnumerateRecycleBinW(NULL, CBSearchRecycleBin, (PVOID)&Context);
790 
791                 if (Context.bFound)
792                 {
793                     GetDeletedFileTypeNameW(Context.hDeletedFile, buffer, _countof(buffer), NULL);
794 
795                     CloseRecycleBinHandle(Context.hDeletedFile);
796                 }
797                 /* load localized file string */
798                 else if (LoadStringW(shell32_hInstance, IDS_ANY_FILE, szTypeName, _countof(szTypeName)))
799                 {
800                     StringCchPrintfW(buffer, _countof(buffer), szTypeName, PathFindExtensionW(pFileDetails->szName));
801                 }
802 
803                 return SHSetStrRet(&pDetails->str, buffer);
804             }
805         default:
806             return E_FAIL;
807     }
808 
809     return SHSetStrRet(&pDetails->str, buffer);
810 }
811 
812 HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
813 {
814     TRACE("(%p, %d, %p)\n", this, iColumn, pscid);
815     if (iColumn >= COLUMNS_COUNT)
816         return E_INVALIDARG;
817     pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
818     pscid->pid = RecycleBinColumns[iColumn].pid;
819     return S_OK;
820 }
821 
822 BOOL CRecycleBin::RecycleBinIsEmpty()
823 {
824     CComPtr<IEnumIDList> spEnumFiles;
825     HRESULT hr = EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &spEnumFiles);
826     if (FAILED(hr))
827         return TRUE;
828     CComHeapPtr<ITEMIDLIST> spPidl;
829     ULONG itemcount;
830     return spEnumFiles->Next(1, &spPidl, &itemcount) != S_OK;
831     }
832 
833 /*************************************************************************
834  * RecycleBin IContextMenu interface
835  */
836 
837 HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
838 {
839     WCHAR szBuffer[100];
840     MENUITEMINFOW mii;
841     int id = 1;
842 
843     TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
844 
845     if (!hMenu)
846         return E_INVALIDARG;
847 
848     ZeroMemory(&mii, sizeof(mii));
849     mii.cbSize = sizeof(mii);
850     mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
851     mii.fState = RecycleBinIsEmpty() ? MFS_DISABLED : MFS_ENABLED;
852     szBuffer[0] = L'\0';
853     LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, _countof(szBuffer));
854     mii.dwTypeData = szBuffer;
855     mii.cch = wcslen(mii.dwTypeData);
856     mii.wID = idCmdFirst + id++;
857     mii.fType = MFT_STRING;
858     iIdEmpty = 1;
859 
860     if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
861         return E_FAIL;
862 
863     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
864 }
865 
866 HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
867 {
868     HRESULT hr;
869     LPSHELLBROWSER lpSB;
870     IShellView * lpSV = NULL;
871     WCHAR szDrive[8];
872 
873     TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb);
874 
875     if (LOWORD(lpcmi->lpVerb) == iIdEmpty)
876     {
877         if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive) - 1))
878         {
879             ERR("GetEnvironmentVariableW failed\n");
880             return E_FAIL;
881         }
882         PathAddBackslashW(szDrive);
883 
884         hr = SHEmptyRecycleBinW(lpcmi->hwnd, szDrive, 0);
885         TRACE("result %x\n", hr);
886         if (hr != S_OK)
887             return hr;
888 
889         lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
890         if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV)))
891             lpSV->Refresh();
892     }
893     return S_OK;
894 }
895 
896 HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
897 {
898     FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
899 
900     return E_NOTIMPL;
901 }
902 
903 /*************************************************************************
904  * RecycleBin IShellPropSheetExt interface
905  */
906 
907 HRESULT WINAPI CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
908 {
909     FIXME("%p %p %lu\n", this, pfnAddPage, lParam);
910 
911     return E_NOTIMPL;
912 }
913 
914 HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam)
915 {
916     FIXME("%p %lu %p %lu\n", this, uPageID, pfnReplaceWith, lParam);
917 
918     return E_NOTIMPL;
919 }
920 
921 /*************************************************************************
922  * RecycleBin IShellExtInit interface
923  */
924 
925 HRESULT WINAPI CRecycleBin::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
926 {
927     TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID );
928     return S_OK;
929 }
930 
931 /**
932  * Tests whether a file can be trashed
933  * @param wszPath Path to the file to be trash
934  * @returns TRUE if the file can be trashed, FALSE otherwise
935  */
936 BOOL
937 TRASH_CanTrashFile(LPCWSTR wszPath)
938 {
939     LONG ret;
940     DWORD dwNukeOnDelete, dwType, VolSerialNumber, MaxComponentLength;
941     DWORD FileSystemFlags, dwSize, dwDisposition;
942     HKEY hKey;
943     WCHAR szBuffer[10];
944     WCHAR szKey[150] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume\\";
945 
946     if (wszPath[1] != L':')
947     {
948         /* path is UNC */
949         return FALSE;
950     }
951 
952     // Copy and retrieve the root path from get given string
953     WCHAR wszRootPathName[MAX_PATH];
954     StringCbCopyW(wszRootPathName, sizeof(wszRootPathName), wszPath);
955     PathStripToRootW(wszRootPathName);
956 
957     // Test to see if the drive is fixed (non removable)
958     if (GetDriveTypeW(wszRootPathName) != DRIVE_FIXED)
959     {
960         /* no bitbucket on removable media */
961         return FALSE;
962     }
963 
964     if (!GetVolumeInformationW(wszRootPathName, NULL, 0, &VolSerialNumber, &MaxComponentLength, &FileSystemFlags, NULL, 0))
965     {
966         ERR("GetVolumeInformationW failed with %u wszRootPathName=%s\n", GetLastError(), debugstr_w(wszRootPathName));
967         return FALSE;
968     }
969 
970     swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber), HIWORD(VolSerialNumber));
971     wcscat(szKey, szBuffer);
972 
973     if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS)
974     {
975         ERR("RegCreateKeyExW failed\n");
976         return FALSE;
977     }
978 
979     if (dwDisposition  & REG_CREATED_NEW_KEY)
980     {
981         /* per default move to bitbucket */
982         dwNukeOnDelete = 0;
983         RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
984         /* per default unlimited size */
985         dwSize = -1;
986         RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize, sizeof(DWORD));
987         RegCloseKey(hKey);
988         return TRUE;
989     }
990     else
991     {
992         dwSize = sizeof(dwNukeOnDelete);
993         ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType, (LPBYTE)&dwNukeOnDelete, &dwSize);
994         if (ret != ERROR_SUCCESS)
995         {
996             if (ret ==  ERROR_FILE_NOT_FOUND)
997             {
998                 /* restore key and enable bitbucket */
999                 dwNukeOnDelete = 0;
1000                 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
1001             }
1002             RegCloseKey(hKey);
1003             return TRUE;
1004         }
1005         else if (dwNukeOnDelete)
1006         {
1007             /* do not delete to bitbucket */
1008             RegCloseKey(hKey);
1009             return FALSE;
1010         }
1011         /* FIXME
1012          * check if bitbucket is full
1013          */
1014         RegCloseKey(hKey);
1015         return TRUE;
1016     }
1017 }
1018 
1019 BOOL
1020 TRASH_TrashFile(LPCWSTR wszPath)
1021 {
1022     TRACE("(%s)\n", debugstr_w(wszPath));
1023     return DeleteFileToRecycleBin(wszPath);
1024 }
1025 
1026 static void TRASH_PlayEmptyRecycleBinSound()
1027 {
1028     CRegKey regKey;
1029     CHeapPtr<WCHAR> pszValue;
1030     CHeapPtr<WCHAR> pszSndPath;
1031     DWORD dwType, dwSize;
1032     LONG lError;
1033 
1034     lError = regKey.Open(HKEY_CURRENT_USER,
1035                          L"AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current",
1036                          KEY_READ);
1037     if (lError != ERROR_SUCCESS)
1038         return;
1039 
1040     lError = regKey.QueryValue(NULL, &dwType, NULL, &dwSize);
1041     if (lError != ERROR_SUCCESS)
1042         return;
1043 
1044     if (!pszValue.AllocateBytes(dwSize))
1045         return;
1046 
1047     lError = regKey.QueryValue(NULL, &dwType, pszValue, &dwSize);
1048     if (lError != ERROR_SUCCESS)
1049         return;
1050 
1051     if (dwType == REG_EXPAND_SZ)
1052     {
1053         dwSize = ExpandEnvironmentStringsW(pszValue, NULL, 0);
1054         if (dwSize == 0)
1055             return;
1056 
1057         if (!pszSndPath.Allocate(dwSize))
1058             return;
1059 
1060         if (ExpandEnvironmentStringsW(pszValue, pszSndPath, dwSize) == 0)
1061             return;
1062     }
1063     else if (dwType == REG_SZ)
1064     {
1065         /* The type is REG_SZ, no need to expand */
1066         pszSndPath.Attach(pszValue.Detach());
1067     }
1068     else
1069     {
1070         /* Invalid type */
1071         return;
1072     }
1073 
1074     PlaySoundW(pszSndPath, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
1075 }
1076 
1077 /*************************************************************************
1078  * SHUpdateCRecycleBinIcon                                [SHELL32.@]
1079  *
1080  * Undocumented
1081  */
1082 EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void)
1083 {
1084     FIXME("stub\n");
1085 
1086     // HACK! This dwItem2 should be the icon index in the system image list that has changed.
1087     // FIXME: Call SHMapPIDLToSystemImageListIndex
1088     DWORD dwItem2 = -1;
1089 
1090     SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD, NULL, &dwItem2);
1091     return S_OK;
1092 }
1093 
1094 /*************************************************************************
1095  *              SHEmptyRecycleBinA (SHELL32.@)
1096  */
1097 HRESULT WINAPI SHEmptyRecycleBinA(HWND hwnd, LPCSTR pszRootPath, DWORD dwFlags)
1098 {
1099     LPWSTR szRootPathW = NULL;
1100     int len;
1101     HRESULT hr;
1102 
1103     TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_a(pszRootPath), dwFlags);
1104 
1105     if (pszRootPath)
1106     {
1107         len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
1108         if (len == 0)
1109             return HRESULT_FROM_WIN32(GetLastError());
1110         szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1111         if (!szRootPathW)
1112             return E_OUTOFMEMORY;
1113         if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
1114         {
1115             HeapFree(GetProcessHeap(), 0, szRootPathW);
1116             return HRESULT_FROM_WIN32(GetLastError());
1117         }
1118     }
1119 
1120     hr = SHEmptyRecycleBinW(hwnd, szRootPathW, dwFlags);
1121     HeapFree(GetProcessHeap(), 0, szRootPathW);
1122 
1123     return hr;
1124 }
1125 
1126 HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags)
1127 {
1128     WCHAR szBuffer[MAX_PATH];
1129     DWORD count;
1130     LONG ret;
1131     IShellFolder *pDesktop, *pRecycleBin;
1132     PIDLIST_ABSOLUTE pidlRecycleBin;
1133     PITEMID_CHILD pidl;
1134     HRESULT hr = S_OK;
1135     LPENUMIDLIST penumFiles;
1136     STRRET StrRet;
1137 
1138     TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_w(pszRootPath), dwFlags);
1139 
1140     if (!(dwFlags & SHERB_NOCONFIRMATION))
1141     {
1142         hr = SHGetDesktopFolder(&pDesktop);
1143         if (FAILED(hr))
1144             return hr;
1145         hr = SHGetFolderLocation(NULL, CSIDL_BITBUCKET, NULL, 0, &pidlRecycleBin);
1146         if (FAILED(hr))
1147         {
1148             pDesktop->Release();
1149             return hr;
1150         }
1151         hr = pDesktop->BindToObject(pidlRecycleBin, NULL, IID_PPV_ARG(IShellFolder, &pRecycleBin));
1152         CoTaskMemFree(pidlRecycleBin);
1153         pDesktop->Release();
1154         if (FAILED(hr))
1155             return hr;
1156         hr = pRecycleBin->EnumObjects(hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penumFiles);
1157         if (FAILED(hr))
1158         {
1159             pRecycleBin->Release();
1160             return hr;
1161         }
1162 
1163         count = 0;
1164         if (hr != S_FALSE)
1165         {
1166             while (penumFiles->Next(1, &pidl, NULL) == S_OK)
1167             {
1168                 count++;
1169                 pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL, &StrRet);
1170                 StrRetToBuf(&StrRet, pidl, szBuffer, _countof(szBuffer));
1171                 CoTaskMemFree(pidl);
1172             }
1173             penumFiles->Release();
1174         }
1175         pRecycleBin->Release();
1176 
1177         switch (count)
1178         {
1179             case 0:
1180                 /* no files, don't need confirmation */
1181                 break;
1182 
1183             case 1:
1184                 /* we have only one item inside the bin, so show a message box with its name */
1185                 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEITEM_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1186                                    MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1187                 {
1188                     return S_OK;
1189                 }
1190                 break;
1191 
1192             default:
1193                 /* we have more than one item, so show a message box with the count of the items */
1194                 StringCbPrintfW(szBuffer, sizeof(szBuffer), L"%u", count);
1195                 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEMULTIPLE_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1196                                    MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1197                 {
1198                     return S_OK;
1199                 }
1200                 break;
1201         }
1202     }
1203 
1204     if (dwFlags & SHERB_NOPROGRESSUI)
1205     {
1206         ret = EmptyRecycleBinW(pszRootPath);
1207     }
1208     else
1209     {
1210        /* FIXME
1211         * show a progress dialog
1212         */
1213         ret = EmptyRecycleBinW(pszRootPath);
1214     }
1215 
1216     if (!ret)
1217         return HRESULT_FROM_WIN32(GetLastError());
1218 
1219     if (!(dwFlags & SHERB_NOSOUND))
1220     {
1221         TRASH_PlayEmptyRecycleBinSound();
1222     }
1223     return S_OK;
1224 }
1225 
1226 HRESULT WINAPI SHQueryRecycleBinA(LPCSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1227 {
1228     LPWSTR szRootPathW = NULL;
1229     int len;
1230     HRESULT hr;
1231 
1232     TRACE("%s, %p\n", debugstr_a(pszRootPath), pSHQueryRBInfo);
1233 
1234     if (pszRootPath)
1235     {
1236         len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
1237         if (len == 0)
1238             return HRESULT_FROM_WIN32(GetLastError());
1239         szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1240         if (!szRootPathW)
1241             return E_OUTOFMEMORY;
1242         if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
1243         {
1244             HeapFree(GetProcessHeap(), 0, szRootPathW);
1245             return HRESULT_FROM_WIN32(GetLastError());
1246         }
1247     }
1248 
1249     hr = SHQueryRecycleBinW(szRootPathW, pSHQueryRBInfo);
1250     HeapFree(GetProcessHeap(), 0, szRootPathW);
1251 
1252     return hr;
1253 }
1254 
1255 HRESULT WINAPI SHQueryRecycleBinW(LPCWSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1256 {
1257     TRACE("%s, %p\n", debugstr_w(pszRootPath), pSHQueryRBInfo);
1258 
1259     if (!pszRootPath || (pszRootPath[0] == 0) ||
1260         !pSHQueryRBInfo || (pSHQueryRBInfo->cbSize < sizeof(SHQUERYRBINFO)))
1261     {
1262         return E_INVALIDARG;
1263     }
1264 
1265     pSHQueryRBInfo->i64Size = 0;
1266     pSHQueryRBInfo->i64NumItems = 0;
1267 
1268     CComPtr<IRecycleBin> spRecycleBin;
1269     HRESULT hr;
1270     if (FAILED_UNEXPECTEDLY((hr = GetDefaultRecycleBin(pszRootPath, &spRecycleBin))))
1271         return hr;
1272 
1273     CComPtr<IRecycleBinEnumList> spEnumList;
1274     hr = spRecycleBin->EnumObjects(&spEnumList);
1275     if (!SUCCEEDED(hr))
1276         return hr;
1277 
1278     while (TRUE)
1279     {
1280         CComPtr<IRecycleBinFile> spFile;
1281         hr = spEnumList->Next(1, &spFile, NULL);
1282         if (hr == S_FALSE)
1283             return S_OK;
1284 
1285         if (FAILED_UNEXPECTEDLY(hr))
1286             return hr;
1287 
1288         ULARGE_INTEGER Size = {};
1289         if (FAILED_UNEXPECTEDLY((hr = spFile->GetFileSize(&Size))))
1290             return hr;
1291 
1292         pSHQueryRBInfo->i64Size += Size.QuadPart;
1293         pSHQueryRBInfo->i64NumItems++;
1294     }
1295 
1296     return S_OK;
1297 }
1298