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