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