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