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 HANDLE hDeletedFile);
66 
67 static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl);
68 
69 typedef struct _SEARCH_CONTEXT
70 {
71     PIDLRecycleStruct *pFileDetails;
72     HANDLE 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 HANDLE hDeletedFile);
126         BOOL WINAPI CBEnumRecycleBin(IN HANDLE 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         virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
146         virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
147         virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
148 
149         // IContextMenu2
150         virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
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 HANDLE 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 HANDLE hDeletedFile)
277 {
278     return static_cast<CRecycleBinEnum *>(Context)->CBEnumRecycleBin(hDeletedFile);
279 }
280 
281 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN HANDLE 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] = L'\0';
772             break;
773         case COLUMN_SIZE:
774             StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH);
775             break;
776         case COLUMN_MTIME:
777             FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification);
778             break;
779         case COLUMN_TYPE:
780             {
781                 SEARCH_CONTEXT Context;
782                 Context.pFileDetails = pFileDetails;
783                 Context.bFound = FALSE;
784                 EnumerateRecycleBinW(NULL, CBSearchRecycleBin, (PVOID)&Context);
785 
786                 if (Context.bFound)
787                 {
788                     GetDeletedFileTypeNameW(Context.hDeletedFile, buffer, _countof(buffer), NULL);
789 
790                     CloseRecycleBinHandle(Context.hDeletedFile);
791                 }
792                 /* load localized file string */
793                 else if (LoadStringW(shell32_hInstance, IDS_ANY_FILE, szTypeName, _countof(szTypeName)))
794                 {
795                     StringCchPrintfW(buffer, _countof(buffer), szTypeName, PathFindExtensionW(pFileDetails->szName));
796                 }
797 
798                 return SHSetStrRet(&pDetails->str, buffer);
799             }
800         default:
801             return E_FAIL;
802     }
803 
804     return SHSetStrRet(&pDetails->str, buffer);
805 }
806 
807 HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
808 {
809     TRACE("(%p, %d, %p)\n", this, iColumn, pscid);
810     if (iColumn >= COLUMNS_COUNT)
811         return E_INVALIDARG;
812     pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
813     pscid->pid = RecycleBinColumns[iColumn].pid;
814     return S_OK;
815 }
816 
817 BOOL CRecycleBin::RecycleBinIsEmpty()
818 {
819     CComPtr<IEnumIDList> spEnumFiles;
820     HRESULT hr = EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &spEnumFiles);
821     if (FAILED(hr))
822         return TRUE;
823     CComHeapPtr<ITEMIDLIST> spPidl;
824     ULONG itemcount;
825     return spEnumFiles->Next(1, &spPidl, &itemcount) != S_OK;
826     }
827 
828 /*************************************************************************
829  * RecycleBin IContextMenu interface
830  */
831 
832 HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
833 {
834     WCHAR szBuffer[100];
835     MENUITEMINFOW mii;
836     int id = 1;
837 
838     TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
839 
840     if (!hMenu)
841         return E_INVALIDARG;
842 
843     ZeroMemory(&mii, sizeof(mii));
844     mii.cbSize = sizeof(mii);
845     mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
846     mii.fState = RecycleBinIsEmpty() ? MFS_DISABLED : MFS_ENABLED;
847     szBuffer[0] = L'\0';
848     LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, _countof(szBuffer));
849     mii.dwTypeData = szBuffer;
850     mii.cch = wcslen(mii.dwTypeData);
851     mii.wID = idCmdFirst + id++;
852     mii.fType = MFT_STRING;
853     iIdEmpty = 1;
854 
855     if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
856         return E_FAIL;
857 
858     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
859 }
860 
861 HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
862 {
863     HRESULT hr;
864     LPSHELLBROWSER lpSB;
865     IShellView * lpSV = NULL;
866     WCHAR szDrive[8];
867 
868     TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb);
869 
870     if (LOWORD(lpcmi->lpVerb) == iIdEmpty)
871     {
872         if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive) - 1))
873         {
874             ERR("GetEnvironmentVariableW failed\n");
875             return E_FAIL;
876         }
877         PathAddBackslashW(szDrive);
878 
879         hr = SHEmptyRecycleBinW(lpcmi->hwnd, szDrive, 0);
880         TRACE("result %x\n", hr);
881         if (hr != S_OK)
882             return hr;
883 
884         lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
885         if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV)))
886             lpSV->Refresh();
887     }
888     return S_OK;
889 }
890 
891 HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
892 {
893     FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
894 
895     return E_NOTIMPL;
896 }
897 
898 /*************************************************************************
899  * RecycleBin IShellPropSheetExt interface
900  */
901 
902 HRESULT WINAPI CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
903 {
904     FIXME("%p %p %lu\n", this, pfnAddPage, lParam);
905 
906     return E_NOTIMPL;
907 }
908 
909 HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam)
910 {
911     FIXME("%p %lu %p %lu\n", this, uPageID, pfnReplaceWith, lParam);
912 
913     return E_NOTIMPL;
914 }
915 
916 /*************************************************************************
917  * RecycleBin IShellExtInit interface
918  */
919 
920 HRESULT WINAPI CRecycleBin::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
921 {
922     TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID );
923     return S_OK;
924 }
925 
926 /**
927  * Tests whether a file can be trashed
928  * @param wszPath Path to the file to be trash
929  * @returns TRUE if the file can be trashed, FALSE otherwise
930  */
931 BOOL
932 TRASH_CanTrashFile(LPCWSTR wszPath)
933 {
934     LONG ret;
935     DWORD dwNukeOnDelete, dwType, VolSerialNumber, MaxComponentLength;
936     DWORD FileSystemFlags, dwSize, dwDisposition;
937     HKEY hKey;
938     WCHAR szBuffer[10];
939     WCHAR szKey[150] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume\\";
940 
941     if (wszPath[1] != L':')
942     {
943         /* path is UNC */
944         return FALSE;
945     }
946 
947     // Copy and retrieve the root path from get given string
948     WCHAR wszRootPathName[MAX_PATH];
949     StringCbCopyW(wszRootPathName, sizeof(wszRootPathName), wszPath);
950     PathStripToRootW(wszRootPathName);
951 
952     // Test to see if the drive is fixed (non removable)
953     if (GetDriveTypeW(wszRootPathName) != DRIVE_FIXED)
954     {
955         /* no bitbucket on removable media */
956         return FALSE;
957     }
958 
959     if (!GetVolumeInformationW(wszRootPathName, NULL, 0, &VolSerialNumber, &MaxComponentLength, &FileSystemFlags, NULL, 0))
960     {
961         ERR("GetVolumeInformationW failed with %u wszRootPathName=%s\n", GetLastError(), debugstr_w(wszRootPathName));
962         return FALSE;
963     }
964 
965     swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber), HIWORD(VolSerialNumber));
966     wcscat(szKey, szBuffer);
967 
968     if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS)
969     {
970         ERR("RegCreateKeyExW failed\n");
971         return FALSE;
972     }
973 
974     if (dwDisposition  & REG_CREATED_NEW_KEY)
975     {
976         /* per default move to bitbucket */
977         dwNukeOnDelete = 0;
978         RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
979         /* per default unlimited size */
980         dwSize = -1;
981         RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize, sizeof(DWORD));
982         RegCloseKey(hKey);
983         return TRUE;
984     }
985     else
986     {
987         dwSize = sizeof(dwNukeOnDelete);
988         ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType, (LPBYTE)&dwNukeOnDelete, &dwSize);
989         if (ret != ERROR_SUCCESS)
990         {
991             if (ret ==  ERROR_FILE_NOT_FOUND)
992             {
993                 /* restore key and enable bitbucket */
994                 dwNukeOnDelete = 0;
995                 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
996             }
997             RegCloseKey(hKey);
998             return TRUE;
999         }
1000         else if (dwNukeOnDelete)
1001         {
1002             /* do not delete to bitbucket */
1003             RegCloseKey(hKey);
1004             return FALSE;
1005         }
1006         /* FIXME
1007          * check if bitbucket is full
1008          */
1009         RegCloseKey(hKey);
1010         return TRUE;
1011     }
1012 }
1013 
1014 BOOL
1015 TRASH_TrashFile(LPCWSTR wszPath)
1016 {
1017     TRACE("(%s)\n", debugstr_w(wszPath));
1018     return DeleteFileToRecycleBin(wszPath);
1019 }
1020 
1021 static void TRASH_PlayEmptyRecycleBinSound()
1022 {
1023     CRegKey regKey;
1024     CHeapPtr<WCHAR> pszValue;
1025     CHeapPtr<WCHAR> pszSndPath;
1026     DWORD dwType, dwSize;
1027     LONG lError;
1028 
1029     lError = regKey.Open(HKEY_CURRENT_USER,
1030                          L"AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current",
1031                          KEY_READ);
1032     if (lError != ERROR_SUCCESS)
1033         return;
1034 
1035     lError = regKey.QueryValue(NULL, &dwType, NULL, &dwSize);
1036     if (lError != ERROR_SUCCESS)
1037         return;
1038 
1039     if (!pszValue.AllocateBytes(dwSize))
1040         return;
1041 
1042     lError = regKey.QueryValue(NULL, &dwType, pszValue, &dwSize);
1043     if (lError != ERROR_SUCCESS)
1044         return;
1045 
1046     if (dwType == REG_EXPAND_SZ)
1047     {
1048         dwSize = ExpandEnvironmentStringsW(pszValue, NULL, 0);
1049         if (dwSize == 0)
1050             return;
1051 
1052         if (!pszSndPath.Allocate(dwSize))
1053             return;
1054 
1055         if (ExpandEnvironmentStringsW(pszValue, pszSndPath, dwSize) == 0)
1056             return;
1057     }
1058     else if (dwType == REG_SZ)
1059     {
1060         /* The type is REG_SZ, no need to expand */
1061         pszSndPath.Attach(pszValue.Detach());
1062     }
1063     else
1064     {
1065         /* Invalid type */
1066         return;
1067     }
1068 
1069     PlaySoundW(pszSndPath, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
1070 }
1071 
1072 /*************************************************************************
1073  * SHUpdateCRecycleBinIcon                                [SHELL32.@]
1074  *
1075  * Undocumented
1076  */
1077 EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void)
1078 {
1079     FIXME("stub\n");
1080 
1081     return S_OK;
1082 }
1083 
1084 /*************************************************************************
1085  *              SHEmptyRecycleBinA (SHELL32.@)
1086  */
1087 HRESULT WINAPI SHEmptyRecycleBinA(HWND hwnd, LPCSTR pszRootPath, DWORD dwFlags)
1088 {
1089     LPWSTR szRootPathW = NULL;
1090     int len;
1091     HRESULT hr;
1092 
1093     TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_a(pszRootPath), dwFlags);
1094 
1095     if (pszRootPath)
1096     {
1097         len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
1098         if (len == 0)
1099             return HRESULT_FROM_WIN32(GetLastError());
1100         szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1101         if (!szRootPathW)
1102             return E_OUTOFMEMORY;
1103         if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
1104         {
1105             HeapFree(GetProcessHeap(), 0, szRootPathW);
1106             return HRESULT_FROM_WIN32(GetLastError());
1107         }
1108     }
1109 
1110     hr = SHEmptyRecycleBinW(hwnd, szRootPathW, dwFlags);
1111     HeapFree(GetProcessHeap(), 0, szRootPathW);
1112 
1113     return hr;
1114 }
1115 
1116 HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags)
1117 {
1118     WCHAR szBuffer[MAX_PATH];
1119     DWORD count;
1120     LONG ret;
1121     IShellFolder *pDesktop, *pRecycleBin;
1122     PIDLIST_ABSOLUTE pidlRecycleBin;
1123     PITEMID_CHILD pidl;
1124     HRESULT hr = S_OK;
1125     LPENUMIDLIST penumFiles;
1126     STRRET StrRet;
1127 
1128     TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_w(pszRootPath), dwFlags);
1129 
1130     if (!(dwFlags & SHERB_NOCONFIRMATION))
1131     {
1132         hr = SHGetDesktopFolder(&pDesktop);
1133         if (FAILED(hr))
1134             return hr;
1135         hr = SHGetFolderLocation(NULL, CSIDL_BITBUCKET, NULL, 0, &pidlRecycleBin);
1136         if (FAILED(hr))
1137         {
1138             pDesktop->Release();
1139             return hr;
1140         }
1141         hr = pDesktop->BindToObject(pidlRecycleBin, NULL, IID_PPV_ARG(IShellFolder, &pRecycleBin));
1142         CoTaskMemFree(pidlRecycleBin);
1143         pDesktop->Release();
1144         if (FAILED(hr))
1145             return hr;
1146         hr = pRecycleBin->EnumObjects(hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penumFiles);
1147         if (FAILED(hr))
1148         {
1149             pRecycleBin->Release();
1150             return hr;
1151         }
1152 
1153         count = 0;
1154         if (hr != S_FALSE)
1155         {
1156             while (penumFiles->Next(1, &pidl, NULL) == S_OK)
1157             {
1158                 count++;
1159                 pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL, &StrRet);
1160                 StrRetToBuf(&StrRet, pidl, szBuffer, _countof(szBuffer));
1161                 CoTaskMemFree(pidl);
1162             }
1163             penumFiles->Release();
1164         }
1165         pRecycleBin->Release();
1166 
1167         switch (count)
1168         {
1169             case 0:
1170                 /* no files, don't need confirmation */
1171                 break;
1172 
1173             case 1:
1174                 /* we have only one item inside the bin, so show a message box with its name */
1175                 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEITEM_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1176                                    MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1177                 {
1178                     return S_OK;
1179                 }
1180                 break;
1181 
1182             default:
1183                 /* we have more than one item, so show a message box with the count of the items */
1184                 StringCbPrintfW(szBuffer, sizeof(szBuffer), L"%u", count);
1185                 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEMULTIPLE_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1186                                    MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1187                 {
1188                     return S_OK;
1189                 }
1190                 break;
1191         }
1192     }
1193 
1194     if (dwFlags & SHERB_NOPROGRESSUI)
1195     {
1196         ret = EmptyRecycleBinW(pszRootPath);
1197     }
1198     else
1199     {
1200        /* FIXME
1201         * show a progress dialog
1202         */
1203         ret = EmptyRecycleBinW(pszRootPath);
1204     }
1205 
1206     if (!ret)
1207         return HRESULT_FROM_WIN32(GetLastError());
1208 
1209     if (!(dwFlags & SHERB_NOSOUND))
1210     {
1211         TRASH_PlayEmptyRecycleBinSound();
1212     }
1213     return S_OK;
1214 }
1215 
1216 HRESULT WINAPI SHQueryRecycleBinA(LPCSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1217 {
1218     LPWSTR szRootPathW = NULL;
1219     int len;
1220     HRESULT hr;
1221 
1222     TRACE("%s, %p\n", debugstr_a(pszRootPath), pSHQueryRBInfo);
1223 
1224     if (pszRootPath)
1225     {
1226         len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
1227         if (len == 0)
1228             return HRESULT_FROM_WIN32(GetLastError());
1229         szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1230         if (!szRootPathW)
1231             return E_OUTOFMEMORY;
1232         if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
1233         {
1234             HeapFree(GetProcessHeap(), 0, szRootPathW);
1235             return HRESULT_FROM_WIN32(GetLastError());
1236         }
1237     }
1238 
1239     hr = SHQueryRecycleBinW(szRootPathW, pSHQueryRBInfo);
1240     HeapFree(GetProcessHeap(), 0, szRootPathW);
1241 
1242     return hr;
1243 }
1244 
1245 HRESULT WINAPI SHQueryRecycleBinW(LPCWSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1246 {
1247     TRACE("%s, %p\n", debugstr_w(pszRootPath), pSHQueryRBInfo);
1248 
1249     if (!pszRootPath || (pszRootPath[0] == 0) ||
1250         !pSHQueryRBInfo || (pSHQueryRBInfo->cbSize < sizeof(SHQUERYRBINFO)))
1251     {
1252         return E_INVALIDARG;
1253     }
1254 
1255     pSHQueryRBInfo->i64Size = 0;
1256     pSHQueryRBInfo->i64NumItems = 0;
1257 
1258     CComPtr<IRecycleBin> spRecycleBin;
1259     HRESULT hr;
1260     if (FAILED_UNEXPECTEDLY((hr = GetDefaultRecycleBin(pszRootPath, &spRecycleBin))))
1261         return hr;
1262 
1263     CComPtr<IRecycleBinEnumList> spEnumList;
1264     hr = spRecycleBin->EnumObjects(&spEnumList);
1265     if (!SUCCEEDED(hr))
1266         return hr;
1267 
1268     while (TRUE)
1269     {
1270         CComPtr<IRecycleBinFile> spFile;
1271         hr = spEnumList->Next(1, &spFile, NULL);
1272         if (hr == S_FALSE)
1273             return S_OK;
1274 
1275         if (FAILED_UNEXPECTEDLY(hr))
1276             return hr;
1277 
1278         ULARGE_INTEGER Size = {};
1279         if (FAILED_UNEXPECTEDLY((hr = spFile->GetFileSize(&Size))))
1280             return hr;
1281 
1282         pSHQueryRBInfo->i64Size += Size.QuadPart;
1283         pSHQueryRBInfo->i64NumItems++;
1284     }
1285 
1286     return S_OK;
1287 }
1288