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