Merge r68232 to get Windows' rpcrt4.dll to work under ReactOS.
[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 typedef struct
115 {
116 DWORD dwNukeOnDelete;
117 DWORD dwSerial;
118 DWORD dwMaxCapacity;
119 } DRIVE_ITEM_CONTEXT, *PDRIVE_ITEM_CONTEXT;
120
121 BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile)
122 {
123 PSEARCH_CONTEXT pContext = (PSEARCH_CONTEXT)Context;
124
125 PDELETED_FILE_DETAILS_W pFileDetails;
126 DWORD dwSize;
127 BOOL ret;
128
129 if (!GetDeletedFileDetailsW(hDeletedFile,
130 0,
131 NULL,
132 &dwSize) &&
133 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
134 {
135 ERR("GetDeletedFileDetailsW failed\n");
136 return FALSE;
137 }
138
139 pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
140 if (!pFileDetails)
141 {
142 ERR("No memory\n");
143 return FALSE;
144 }
145
146 if (!GetDeletedFileDetailsW(hDeletedFile,
147 dwSize,
148 pFileDetails,
149 NULL))
150 {
151 ERR("GetDeletedFileDetailsW failed\n");
152 SHFree(pFileDetails);
153 return FALSE;
154 }
155
156 ret = memcmp(pFileDetails, pContext->pFileDetails, dwSize);
157 if (!ret)
158 {
159 pContext->hDeletedFile = hDeletedFile;
160 pContext->bFound = TRUE;
161 }
162 else
163 CloseRecycleBinHandle(hDeletedFile);
164
165 SHFree(pFileDetails);
166 return ret;
167 }
168
169 static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl)
170 {
171 LPPIDLDATA pdata = _ILGetDataPointer(pidl);
172
173 if (pdata && pdata->type == 0x00)
174 return (PIDLRecycleStruct*) & (pdata->u.crecycle);
175
176 return NULL;
177 }
178
179 CRecycleBinEnum::CRecycleBinEnum()
180 {
181 }
182
183 CRecycleBinEnum::~CRecycleBinEnum()
184 {
185 }
186
187 HRESULT WINAPI CRecycleBinEnum::Initialize(DWORD dwFlags)
188 {
189 static LPCWSTR szDrive = L"C:\\";
190
191 if (dwFlags & SHCONTF_NONFOLDERS)
192 {
193 TRACE("Starting Enumeration\n");
194
195 if (!EnumerateRecycleBinW(szDrive /* FIXME */ , CBEnumRecycleBin, (PVOID)this))
196 {
197 WARN("Error: EnumerateCRecycleBinW failed\n");
198 return E_FAIL;
199 }
200 }
201 else
202 {
203 // do nothing
204 }
205 return S_OK;
206 }
207
208 static LPITEMIDLIST _ILCreateRecycleItem(PDELETED_FILE_DETAILS_W pFileDetails)
209 {
210 PIDLDATA tmp;
211 LPITEMIDLIST pidl;
212 PIDLRecycleStruct * p;
213 int size0 = (char*)&tmp.u.crecycle.szName - (char*)&tmp.u.crecycle;
214 int size = size0;
215
216 tmp.type = 0x00;
217 size += (wcslen(pFileDetails->FileName) + 1) * sizeof(WCHAR);
218
219 pidl = (LPITEMIDLIST)SHAlloc(size + 4);
220 if (!pidl)
221 return pidl;
222
223 pidl->mkid.cb = size + 2;
224 memcpy(pidl->mkid.abID, &tmp, 2 + size0);
225
226 p = &((PIDLDATA*)pidl->mkid.abID)->u.crecycle;
227 RtlCopyMemory(p, pFileDetails, sizeof(DELETED_FILE_DETAILS_W));
228 wcscpy(p->szName, pFileDetails->FileName);
229 *(WORD*)((char*)pidl + (size + 2)) = 0;
230 return pidl;
231 }
232
233 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile)
234 {
235 return static_cast<CRecycleBinEnum *>(Context)->CBEnumRecycleBin(hDeletedFile);
236 }
237
238 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN HANDLE hDeletedFile)
239 {
240 PDELETED_FILE_DETAILS_W pFileDetails;
241 DWORD dwSize;
242 LPITEMIDLIST pidl = NULL;
243 BOOL ret;
244
245 if (!GetDeletedFileDetailsW(hDeletedFile,
246 0,
247 NULL,
248 &dwSize) &&
249 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
250 {
251 ERR("GetDeletedFileDetailsW failed\n");
252 return FALSE;
253 }
254
255 pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
256 if (!pFileDetails)
257 {
258 ERR("No memory\n");
259 return FALSE;
260 }
261
262 if (!GetDeletedFileDetailsW(hDeletedFile,
263 dwSize,
264 pFileDetails,
265 NULL))
266 {
267 ERR("GetDeletedFileDetailsW failed\n");
268 SHFree(pFileDetails);
269 return FALSE;
270 }
271
272 pidl = _ILCreateRecycleItem(pFileDetails);
273 if (!pidl)
274 {
275 SHFree(pFileDetails);
276 return FALSE;
277 }
278
279 ret = AddToEnumList(pidl);
280
281 if (!ret)
282 SHFree(pidl);
283 SHFree(pFileDetails);
284 TRACE("Returning %d\n", ret);
285 CloseRecycleBinHandle(hDeletedFile);
286 return ret;
287 }
288
289 /**************************************************************************
290 * IContextMenu2 Bitbucket Item Implementation
291 */
292
293 CRecycleBinItemContextMenu::CRecycleBinItemContextMenu()
294 {
295 apidl = NULL;
296 }
297
298 CRecycleBinItemContextMenu::~CRecycleBinItemContextMenu()
299 {
300 ILFree(apidl);
301 }
302
303 HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST pidl)
304 {
305 apidl = ILClone(pidl);
306 if (apidl == NULL)
307 return E_OUTOFMEMORY;
308 return S_OK;
309 }
310
311 HRESULT WINAPI CRecycleBinItemContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
312 {
313 WCHAR szBuffer[30] = {0};
314 ULONG Count = 1;
315
316 TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
317
318 if (LoadStringW(shell32_hInstance, IDS_RESTORE, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
319 {
320 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
321 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_ENABLED);
322 Count++;
323 }
324
325 if (LoadStringW(shell32_hInstance, IDS_CUT, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
326 {
327 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
328 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
329 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED);
330 }
331
332 if (LoadStringW(shell32_hInstance, IDS_DELETE, 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_ENABLED);
337 }
338
339 if (LoadStringW(shell32_hInstance, IDS_PROPERTIES, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
340 {
341 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
342 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
343 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_DEFAULT);
344 }
345
346 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Count);
347 }
348
349 HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
350 {
351 SEARCH_CONTEXT Context;
352 static LPCWSTR szDrive = L"C:\\";
353
354 TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi, lpcmi->lpVerb, lpcmi->hwnd);
355
356 if (lpcmi->lpVerb == MAKEINTRESOURCEA(1) || lpcmi->lpVerb == MAKEINTRESOURCEA(5))
357 {
358 Context.pFileDetails = _ILGetRecycleStruct(apidl);
359 Context.bFound = FALSE;
360
361 EnumerateRecycleBinW(szDrive, CBSearchRecycleBin, (PVOID)&Context);
362 if (!Context.bFound)
363 return E_FAIL;
364
365 if (lpcmi->lpVerb == MAKEINTRESOURCEA(1))
366 {
367 /* restore file */
368 if (RestoreFile(Context.hDeletedFile))
369 return S_OK;
370 else
371 return E_FAIL;
372 }
373 else
374 {
375 DeleteFileHandleToRecycleBin(Context.hDeletedFile);
376 return E_NOTIMPL;
377 }
378 }
379 else if (lpcmi->lpVerb == MAKEINTRESOURCEA(3))
380 {
381 FIXME("implement cut\n");
382 return E_NOTIMPL;
383 }
384 else if (lpcmi->lpVerb == MAKEINTRESOURCEA(7))
385 {
386 FIXME("implement properties\n");
387 return E_NOTIMPL;
388 }
389
390 return S_OK;
391 }
392
393 HRESULT WINAPI CRecycleBinItemContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
394 {
395 TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
396
397 return E_FAIL;
398 }
399
400 HRESULT WINAPI CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
401 {
402 TRACE("CRecycleBin_IContextMenu2Item_HandleMenuMsg (%p)->(msg=%x wp=%lx lp=%lx)\n", this, uMsg, wParam, lParam);
403
404 return E_NOTIMPL;
405 }
406
407 /**************************************************************************
408 * registers clipboardformat once
409 */
410 void CRecycleBin::SF_RegisterClipFmt()
411 {
412 TRACE ("(%p)\n", this);
413
414 if (!cfShellIDList)
415 cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
416 }
417
418 CRecycleBin::CRecycleBin()
419 {
420 pidl = NULL;
421 iIdEmpty = 0;
422 cfShellIDList = 0;
423 SF_RegisterClipFmt();
424 fAcceptFmt = FALSE;
425 }
426
427 CRecycleBin::~CRecycleBin()
428 {
429 /* InterlockedDecrement(&objCount);*/
430 SHFree(pidl);
431 }
432
433 /*************************************************************************
434 * RecycleBin IPersistFolder2 interface
435 */
436
437 HRESULT WINAPI CRecycleBin::GetClassID(CLSID *pClassID)
438 {
439 TRACE("(%p, %p)\n", this, pClassID);
440 if (pClassID == NULL)
441 return E_INVALIDARG;
442 memcpy(pClassID, &CLSID_RecycleBin, sizeof(CLSID));
443 return S_OK;
444 }
445
446 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidl)
447 {
448 TRACE("(%p, %p)\n", this, pidl);
449
450 SHFree((LPVOID)this->pidl);
451 this->pidl = ILClone(pidl);
452 if (this->pidl == NULL)
453 return E_OUTOFMEMORY;
454 return S_OK;
455 }
456
457 HRESULT WINAPI CRecycleBin::GetCurFolder(LPITEMIDLIST *ppidl)
458 {
459 TRACE("\n");
460 *ppidl = ILClone(pidl);
461 return S_OK;
462 }
463
464 /*************************************************************************
465 * RecycleBin IShellFolder2 interface
466 */
467
468 HRESULT WINAPI CRecycleBin::ParseDisplayName(HWND hwnd, LPBC pbc,
469 LPOLESTR pszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl,
470 ULONG *pdwAttributes)
471 {
472 FIXME("stub\n");
473 return E_NOTIMPL;
474 }
475
476
477 PDELETED_FILE_DETAILS_W
478 UnpackDetailsFromPidl(LPCITEMIDLIST pidl)
479 {
480 return (PDELETED_FILE_DETAILS_W)&pidl->mkid.abID;
481 }
482
483 HRESULT WINAPI CRecycleBin::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
484 {
485 return ShellObjectCreatorInit<CRecycleBinEnum>(dwFlags, IID_IEnumIDList, ppEnumIDList);
486 }
487
488 HRESULT WINAPI CRecycleBin::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
489 {
490 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
491 return E_NOTIMPL;
492 }
493
494 HRESULT WINAPI CRecycleBin::BindToStorage(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::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
501 {
502 /* TODO */
503 TRACE("(%p, %p, %p, %p)\n", this, (void *)lParam, pidl1, pidl2);
504 if (pidl1->mkid.cb != pidl2->mkid.cb)
505 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, pidl1->mkid.cb - pidl2->mkid.cb);
506 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (unsigned short)memcmp(pidl1->mkid.abID, pidl2->mkid.abID, pidl1->mkid.cb));
507 }
508
509 HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv)
510 {
511 CComPtr<IShellView> pShellView;
512 HRESULT hr = E_NOINTERFACE;
513
514 TRACE("(%p, %p, %s, %p)\n", this, hwndOwner, debugstr_guid(&riid), ppv);
515
516 if (!ppv)
517 return hr;
518
519 *ppv = NULL;
520
521 if (IsEqualIID (riid, IID_IDropTarget))
522 {
523 hr = this->QueryInterface (IID_IDropTarget, ppv);
524 }
525 else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid, IID_IContextMenu2))
526 {
527 hr = this->QueryInterface(riid, ppv);
528 }
529 else if (IsEqualIID (riid, IID_IShellView))
530 {
531 hr = IShellView_Constructor ((IShellFolder *)this, &pShellView);
532 if (pShellView)
533 {
534 hr = pShellView->QueryInterface(riid, ppv);
535 }
536 }
537 else
538 return hr;
539 TRACE ("-- (%p)->(interface=%p)\n", this, ppv);
540 return hr;
541
542 }
543
544 HRESULT WINAPI CRecycleBin::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
545 SFGAOF *rgfInOut)
546 {
547 TRACE("(%p, %d, {%p, ...}, {%x})\n", this, cidl, apidl ? apidl[0] : NULL, (unsigned int)*rgfInOut);
548 *rgfInOut &= SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANLINK;
549 return S_OK;
550 }
551
552 HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
553 REFIID riid, UINT *prgfInOut, void **ppv)
554 {
555 IUnknown *pObj = NULL;
556 HRESULT hr = E_INVALIDARG;
557
558 TRACE ("(%p)->(%p,%u,apidl=%p, %p %p)\n", this,
559 hwndOwner, cidl, apidl, prgfInOut, ppv);
560
561 if (!ppv)
562 return hr;
563
564 *ppv = NULL;
565
566 if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2)) && (cidl >= 1))
567 {
568 hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(apidl[0], riid, &pObj);
569 }
570 else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
571 {
572 IDropTarget * pDt = NULL;
573 hr = QueryInterface(IID_PPV_ARG(IDropTarget, &pDt));
574 pObj = pDt;
575 }
576 else if(IsEqualIID(riid, IID_IExtractIconA) && (cidl == 1))
577 {
578 // FIXME: This is not correct, it does not show the right icons
579 LPITEMIDLIST pidlItem = ILCombine(pidl, apidl[0]);
580 pObj = IExtractIconA_Constructor(pidlItem);
581 SHFree(pidlItem);
582 hr = S_OK;
583 }
584 else if (IsEqualIID(riid, IID_IExtractIconW) && (cidl == 1))
585 {
586 // FIXME: This is not correct, it does not show the right icons
587 LPITEMIDLIST pidlItem = ILCombine(pidl, apidl[0]);
588 pObj = IExtractIconW_Constructor(pidlItem);
589 SHFree(pidlItem);
590 hr = S_OK;
591 }
592 else
593 hr = E_NOINTERFACE;
594
595 if (SUCCEEDED(hr) && !pObj)
596 hr = E_OUTOFMEMORY;
597
598 *ppv = pObj;
599 TRACE ("(%p)->hr=0x%08x\n", this, hr);
600 return hr;
601 }
602
603 HRESULT WINAPI CRecycleBin::GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF uFlags, STRRET *pName)
604 {
605 PIDLRecycleStruct *pFileDetails;
606 LPWSTR pFileName;
607
608 TRACE("(%p, %p, %x, %p)\n", this, pidl, (unsigned int)uFlags, pName);
609
610
611 if (_ILIsBitBucket (pidl))
612 {
613 WCHAR pszPath[100];
614
615 if (HCR_GetClassNameW(CLSID_RecycleBin, pszPath, MAX_PATH))
616 {
617 pName->uType = STRRET_WSTR;
618 pName->pOleStr = StrDupW(pszPath);
619 return S_OK;
620 }
621 }
622
623 pFileDetails = _ILGetRecycleStruct(pidl);
624 if (!pFileDetails)
625 {
626 pName->cStr[0] = 0;
627 pName->uType = STRRET_CSTR;
628 return E_INVALIDARG;
629 }
630
631 pFileName = wcsrchr(pFileDetails->szName, L'\\');
632 if (!pFileName)
633 {
634 pName->cStr[0] = 0;
635 pName->uType = STRRET_CSTR;
636 return E_UNEXPECTED;
637 }
638
639 pName->pOleStr = StrDupW(pFileName + 1);
640 if (pName->pOleStr == NULL)
641 return E_OUTOFMEMORY;
642
643 pName->uType = STRRET_WSTR;
644 return S_OK;
645 }
646
647 HRESULT WINAPI CRecycleBin::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pszName,
648 SHGDNF uFlags, PITEMID_CHILD *ppidlOut)
649 {
650 TRACE("\n");
651 return E_FAIL; /* not supported */
652 }
653
654 HRESULT WINAPI CRecycleBin::GetDefaultSearchGUID(GUID *pguid)
655 {
656 FIXME("stub\n");
657 return E_NOTIMPL;
658 }
659
660 HRESULT WINAPI CRecycleBin::EnumSearches(IEnumExtraSearch **ppEnum)
661 {
662 FIXME("stub\n");
663 *ppEnum = NULL;
664 return E_NOTIMPL;
665 }
666
667 HRESULT WINAPI CRecycleBin::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
668 {
669 TRACE("(%p, %x, %p, %p)\n", this, (unsigned int)dwReserved, pSort, pDisplay);
670 *pSort = 0;
671 *pDisplay = 0;
672 return S_OK;
673 }
674
675 HRESULT WINAPI CRecycleBin::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
676 {
677 TRACE("(%p, %d, %p)\n", this, iColumn, pcsFlags);
678 if (iColumn >= COLUMNS_COUNT)
679 return E_INVALIDARG;
680 *pcsFlags = RecycleBinColumns[iColumn].pcsFlags;
681 return S_OK;
682 }
683
684 HRESULT WINAPI CRecycleBin::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
685 {
686 FIXME("stub\n");
687 return E_NOTIMPL;
688 }
689
690 static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME * ft)
691 {
692 FILETIME lft;
693 SYSTEMTIME time;
694 int ret;
695
696 FileTimeToLocalFileTime(ft, &lft);
697 FileTimeToSystemTime(&lft, &time);
698
699 ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size);
700 if (ret > 0 && ret < size)
701 {
702 /* Append space + time without seconds */
703 buffer[ret-1] = ' ';
704 GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret);
705 }
706
707 return (ret != 0 ? E_FAIL : S_OK);
708 }
709
710 HRESULT WINAPI CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, LPSHELLDETAILS pDetails)
711 {
712 PIDLRecycleStruct * pFileDetails;
713 WCHAR buffer[MAX_PATH];
714 WCHAR szTypeName[100];
715 LPWSTR pszBackslash;
716 UINT Length;
717
718 TRACE("(%p, %p, %d, %p)\n", this, pidl, iColumn, pDetails);
719 if (iColumn >= COLUMNS_COUNT)
720 return E_FAIL;
721 pDetails->fmt = RecycleBinColumns[iColumn].fmt;
722 pDetails->cxChar = RecycleBinColumns[iColumn].cxChars;
723 if (pidl == NULL)
724 {
725 pDetails->str.uType = STRRET_WSTR;
726 LoadStringW(shell32_hInstance, RecycleBinColumns[iColumn].column_name_id, buffer, MAX_PATH);
727 return SHStrDupW(buffer, &pDetails->str.pOleStr);
728 }
729
730 if (iColumn == COLUMN_NAME)
731 return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
732
733 pFileDetails = _ILGetRecycleStruct(pidl);
734 switch (iColumn)
735 {
736 case COLUMN_DATEDEL:
737 FormatDateTime(buffer, MAX_PATH, &pFileDetails->DeletionTime);
738 break;
739 case COLUMN_DELFROM:
740 pszBackslash = wcsrchr(pFileDetails->szName, L'\\');
741 Length = (pszBackslash - pFileDetails->szName);
742 memcpy((LPVOID)buffer, pFileDetails->szName, Length * sizeof(WCHAR));
743 buffer[Length] = L'\0';
744 break;
745 case COLUMN_SIZE:
746 StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH);
747 break;
748 case COLUMN_MTIME:
749 FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification);
750 break;
751 case COLUMN_TYPE:
752 szTypeName[0] = L'\0';
753 wcscpy(buffer, PathFindExtensionW(pFileDetails->szName));
754 if (!( HCR_MapTypeToValueW(buffer, buffer, sizeof(buffer) / sizeof(WCHAR), TRUE) &&
755 HCR_MapTypeToValueW(buffer, szTypeName, sizeof(szTypeName) / sizeof(WCHAR), FALSE )))
756 {
757 wcscpy (szTypeName, PathFindExtensionW(pFileDetails->szName));
758 wcscat(szTypeName, L"-");
759 Length = wcslen(szTypeName);
760 if (LoadStringW(shell32_hInstance, IDS_SHV_COLUMN1, &szTypeName[Length], (sizeof(szTypeName) / sizeof(WCHAR)) - Length))
761 szTypeName[(sizeof(szTypeName)/sizeof(WCHAR))-1] = L'\0';
762 }
763 pDetails->str.uType = STRRET_WSTR;
764 return SHStrDupW(szTypeName, &pDetails->str.pOleStr);
765 break;
766 default:
767 return E_FAIL;
768 }
769
770 pDetails->str.uType = STRRET_WSTR;
771 return SHStrDupW(buffer, &pDetails->str.pOleStr);
772 }
773
774 HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
775 {
776 TRACE("(%p, %d, %p)\n", this, iColumn, pscid);
777 if (iColumn >= COLUMNS_COUNT)
778 return E_INVALIDARG;
779 pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
780 pscid->pid = RecycleBinColumns[iColumn].pid;
781 return S_OK;
782 }
783
784 /*************************************************************************
785 * RecycleBin IContextMenu interface
786 */
787
788 HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
789 {
790 WCHAR szBuffer[100];
791 MENUITEMINFOW mii;
792 int id = 1;
793
794 TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
795
796 if (!hMenu)
797 return E_INVALIDARG;
798
799 memset(&mii, 0, sizeof(mii));
800 mii.cbSize = sizeof(mii);
801 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
802 mii.fState = MFS_ENABLED;
803 szBuffer[0] = L'\0';
804 LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, sizeof(szBuffer) / sizeof(WCHAR));
805 mii.dwTypeData = szBuffer;
806 mii.cch = wcslen(mii.dwTypeData);
807 mii.wID = idCmdFirst + id++;
808 mii.fType = MFT_STRING;
809 iIdEmpty = 1;
810
811 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
812 return E_FAIL;
813
814 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
815 }
816
817 HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
818 {
819 HRESULT hr;
820 LPSHELLBROWSER lpSB;
821 IShellView * lpSV = NULL;
822
823 TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb);
824
825 if (LOWORD(lpcmi->lpVerb) == iIdEmpty)
826 {
827 // FIXME
828 // path & flags
829 hr = SHEmptyRecycleBinW(lpcmi->hwnd, L"C:\\", 0);
830 TRACE("result %x\n", hr);
831 if (hr != S_OK)
832 return hr;
833
834 lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
835 if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV)))
836 lpSV->Refresh();
837 }
838 return S_OK;
839 }
840
841 HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
842 {
843 FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
844
845 return E_NOTIMPL;
846 }
847
848 /*************************************************************************
849 * RecycleBin IShellPropSheetExt interface
850 */
851
852 HRESULT WINAPI CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
853 {
854 FIXME("%p %p %lu\n", this, pfnAddPage, lParam);
855
856 return E_NOTIMPL;
857 }
858
859 HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam)
860 {
861 FIXME("%p %lu %p %lu\n", this, uPageID, pfnReplaceWith, lParam);
862
863 return E_NOTIMPL;
864 }
865
866 /*************************************************************************
867 * RecycleBin IShellExtInit interface
868 */
869
870 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
871 {
872 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID );
873 return S_OK;
874 }
875
876 void toggleNukeOnDeleteOption(HWND hwndDlg, BOOL bEnable)
877 {
878 if (bEnable)
879 {
880 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_UNCHECKED, 0);
881 EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
882 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_CHECKED, 0);
883 }
884 else
885 {
886 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_CHECKED, 0);
887 EnableWindow(GetDlgItem(hwndDlg, 14002), TRUE);
888 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_UNCHECKED, 0);
889 }
890 }
891
892
893 static VOID
894 InitializeRecycleBinDlg(HWND hwndDlg, WCHAR DefaultDrive)
895 {
896 WCHAR CurDrive = L'A';
897 WCHAR szDrive[] = L"A:\\";
898 DWORD dwDrives;
899 WCHAR szName[100];
900 WCHAR szVolume[100];
901 DWORD MaxComponent, Flags;
902 DWORD dwSerial;
903 LVCOLUMNW lc;
904 HWND hDlgCtrl;
905 LVITEMW li;
906 INT itemCount;
907 ULARGE_INTEGER TotalNumberOfFreeBytes, TotalNumberOfBytes, FreeBytesAvailable;
908 RECT rect;
909 int columnSize;
910 int defIndex = 0;
911 DWORD dwSize;
912 PDRIVE_ITEM_CONTEXT pItem = NULL, pDefault = NULL, pFirst = NULL;
913
914 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
915
916 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_LOCATION, szVolume, sizeof(szVolume) / sizeof(WCHAR)))
917 szVolume[0] = 0;
918
919 GetClientRect(hDlgCtrl, &rect);
920
921 memset(&lc, 0, sizeof(LV_COLUMN) );
922 lc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
923
924 columnSize = 140; //FIXME
925 lc.iSubItem = 0;
926 lc.fmt = LVCFMT_FIXED_WIDTH;
927 lc.cx = columnSize;
928 lc.cchTextMax = wcslen(szVolume);
929 lc.pszText = szVolume;
930 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 0, (LPARAM)&lc);
931
932 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_DISKSPACE, szVolume, sizeof(szVolume) / sizeof(WCHAR)))
933 szVolume[0] = 0;
934
935 lc.iSubItem = 1;
936 lc.cx = rect.right - rect.left - columnSize;
937 lc.cchTextMax = wcslen(szVolume);
938 lc.pszText = szVolume;
939 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 1, (LPARAM)&lc);
940
941 dwDrives = GetLogicalDrives();
942 itemCount = 0;
943 do
944 {
945 if ((dwDrives & 0x1))
946 {
947 UINT Type = GetDriveTypeW(szDrive);
948 if (Type == DRIVE_FIXED) //FIXME
949 {
950 if (!GetVolumeInformationW(szDrive, szName, sizeof(szName) / sizeof(WCHAR), &dwSerial, &MaxComponent, &Flags, NULL, 0))
951 {
952 szName[0] = 0;
953 dwSerial = -1;
954 }
955
956 swprintf(szVolume, L"%s (%c)", szName, szDrive[0]);
957 memset(&li, 0x0, sizeof(LVITEMW));
958 li.mask = LVIF_TEXT | LVIF_PARAM;
959 li.iSubItem = 0;
960 li.pszText = szVolume;
961 li.iItem = itemCount;
962 SendMessageW(hDlgCtrl, LVM_INSERTITEMW, 0, (LPARAM)&li);
963 if (GetDiskFreeSpaceExW(szDrive, &FreeBytesAvailable , &TotalNumberOfBytes, &TotalNumberOfFreeBytes))
964 {
965 if (StrFormatByteSizeW(TotalNumberOfFreeBytes.QuadPart, szVolume, sizeof(szVolume) / sizeof(WCHAR)))
966 {
967
968 pItem = (DRIVE_ITEM_CONTEXT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DRIVE_ITEM_CONTEXT));
969 if (pItem)
970 {
971 swprintf(szName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume\\%04X-%04X", LOWORD(dwSerial), HIWORD(dwSerial));
972 dwSize = sizeof(DWORD);
973 RegGetValueW(HKEY_CURRENT_USER, szName, L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize);
974 dwSize = sizeof(DWORD);
975 RegGetValueW(HKEY_CURRENT_USER, szName, L"NukeOnDelete", RRF_RT_DWORD, NULL, &pItem->dwNukeOnDelete, &dwSize);
976 pItem->dwSerial = dwSerial;
977 li.mask = LVIF_PARAM;
978 li.lParam = (LPARAM)pItem;
979 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
980 if (CurDrive == DefaultDrive)
981 {
982 defIndex = itemCount;
983 pDefault = pItem;
984 }
985 }
986 if (!pFirst)
987 pFirst = pItem;
988
989 li.mask = LVIF_TEXT;
990 li.iSubItem = 1;
991 li.pszText = szVolume;
992 li.iItem = itemCount;
993 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
994 }
995 }
996 itemCount++;
997 }
998 }
999 CurDrive++;
1000 szDrive[0] = CurDrive;
1001 dwDrives = (dwDrives >> 1);
1002 } while(dwDrives);
1003
1004 if (!pDefault)
1005 pDefault = pFirst;
1006 if (pDefault)
1007 {
1008 toggleNukeOnDeleteOption(hwndDlg, pDefault->dwNukeOnDelete);
1009 SetDlgItemInt(hwndDlg, 14002, pDefault->dwMaxCapacity, FALSE);
1010 }
1011 ZeroMemory(&li, sizeof(li));
1012 li.mask = LVIF_STATE;
1013 li.stateMask = (UINT) - 1;
1014 li.state = LVIS_FOCUSED | LVIS_SELECTED;
1015 li.iItem = defIndex;
1016 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
1017
1018 }
1019
1020 static BOOL StoreDriveSettings(HWND hwndDlg)
1021 {
1022 int iCount, iIndex;
1023 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1024 LVITEMW li;
1025 PDRIVE_ITEM_CONTEXT pItem;
1026 HKEY hKey, hSubKey;
1027 WCHAR szSerial[20];
1028 DWORD dwSize;
1029
1030
1031 if (RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
1032 return FALSE;
1033
1034 iCount = ListView_GetItemCount(hDlgCtrl);
1035
1036 ZeroMemory(&li, sizeof(li));
1037 li.mask = LVIF_PARAM;
1038
1039 for(iIndex = 0; iIndex < iCount; iIndex++)
1040 {
1041 li.iItem = iIndex;
1042 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
1043 {
1044 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
1045 swprintf(szSerial, L"%04X-%04X", LOWORD(pItem->dwSerial), HIWORD(pItem->dwSerial));
1046 if (RegCreateKeyExW(hKey, szSerial, 0, NULL, 0, KEY_WRITE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
1047 {
1048 dwSize = sizeof(DWORD);
1049 RegSetValueExW(hSubKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&pItem->dwNukeOnDelete, dwSize);
1050 dwSize = sizeof(DWORD);
1051 RegSetValueExW(hSubKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&pItem->dwMaxCapacity, dwSize);
1052 RegCloseKey(hSubKey);
1053 }
1054 }
1055 }
1056 RegCloseKey(hKey);
1057 return TRUE;
1058
1059 }
1060
1061 static VOID FreeDriveItemContext(HWND hwndDlg)
1062 {
1063 int iCount, iIndex;
1064 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1065 LVITEMW li;
1066
1067 iCount = ListView_GetItemCount(hDlgCtrl);
1068
1069 ZeroMemory(&li, sizeof(li));
1070 li.mask = LVIF_PARAM;
1071
1072 for(iIndex = 0; iIndex < iCount; iIndex++)
1073 {
1074 li.iItem = iIndex;
1075 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
1076 {
1077 HeapFree(GetProcessHeap(), 0, (LPVOID)li.lParam);
1078 }
1079 }
1080 }
1081
1082 static INT
1083 GetDefaultItem(HWND hwndDlg, LVITEMW * li)
1084 {
1085 HWND hDlgCtrl;
1086 UINT iItemCount, iIndex;
1087
1088 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1089 if (!hDlgCtrl)
1090 return -1;
1091
1092 iItemCount = ListView_GetItemCount(hDlgCtrl);
1093 if (!iItemCount)
1094 return -1;
1095
1096 ZeroMemory(li, sizeof(LVITEMW));
1097 li->mask = LVIF_PARAM | LVIF_STATE;
1098 li->stateMask = (UINT) - 1;
1099 for (iIndex = 0; iIndex < iItemCount; iIndex++)
1100 {
1101 li->iItem = iIndex;
1102 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)li))
1103 {
1104 if (li->state & LVIS_SELECTED)
1105 return iIndex;
1106 }
1107 }
1108 return -1;
1109
1110 }
1111
1112 static INT_PTR CALLBACK
1113 RecycleBinDlg(
1114 HWND hwndDlg,
1115 UINT uMsg,
1116 WPARAM wParam,
1117 LPARAM lParam
1118 )
1119 {
1120 LPPSHNOTIFY lppsn;
1121 LPNMLISTVIEW lppl;
1122 LVITEMW li;
1123 PDRIVE_ITEM_CONTEXT pItem;
1124 BOOL bSuccess;
1125 UINT uResult;
1126 PROPSHEETPAGE * page;
1127 DWORD dwStyle;
1128
1129 switch(uMsg)
1130 {
1131 case WM_INITDIALOG:
1132 page = (PROPSHEETPAGE*)lParam;
1133 InitializeRecycleBinDlg(hwndDlg, (WCHAR)page->lParam);
1134 dwStyle = (DWORD) SendDlgItemMessage(hwndDlg, 14000, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
1135 dwStyle = dwStyle | LVS_EX_FULLROWSELECT;
1136 SendDlgItemMessage(hwndDlg, 14000, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
1137 if (GetDlgCtrlID((HWND)wParam) != 14000)
1138 {
1139 SetFocus(GetDlgItem(hwndDlg, 14000));
1140 return FALSE;
1141 }
1142 return TRUE;
1143 case WM_COMMAND:
1144 switch(LOWORD(wParam))
1145 {
1146 case 14001:
1147 toggleNukeOnDeleteOption(hwndDlg, FALSE);
1148 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1149 break;
1150 case 14003:
1151 toggleNukeOnDeleteOption(hwndDlg, TRUE);
1152 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1153 break;
1154 case 14004:
1155 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1156 break;
1157 }
1158 break;
1159 case WM_NOTIFY:
1160 lppsn = (LPPSHNOTIFY) lParam;
1161 lppl = (LPNMLISTVIEW) lParam;
1162 if (lppsn->hdr.code == PSN_APPLY)
1163 {
1164 if (GetDefaultItem(hwndDlg, &li) > -1)
1165 {
1166 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
1167 if (pItem)
1168 {
1169 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
1170 if (bSuccess)
1171 pItem->dwMaxCapacity = uResult;
1172 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
1173 pItem->dwNukeOnDelete = TRUE;
1174 else
1175 pItem->dwNukeOnDelete = FALSE;
1176 }
1177 }
1178 if (StoreDriveSettings(hwndDlg))
1179 {
1180 SetWindowLongPtr( hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR );
1181 return TRUE;
1182 }
1183 }
1184 else if (lppl->hdr.code == LVN_ITEMCHANGING)
1185 {
1186 ZeroMemory(&li, sizeof(li));
1187 li.mask = LVIF_PARAM;
1188 li.iItem = lppl->iItem;
1189 if (!SendMessageW(lppl->hdr.hwndFrom, LVM_GETITEMW, 0, (LPARAM)&li))
1190 return TRUE;
1191
1192 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
1193 if (!pItem)
1194 return TRUE;
1195
1196 if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED))
1197 {
1198 /* new focused item */
1199 toggleNukeOnDeleteOption(lppl->hdr.hwndFrom, pItem->dwNukeOnDelete);
1200 SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
1201 }
1202 else if ((lppl->uOldState & LVIS_FOCUSED) && !(lppl->uNewState & LVIS_FOCUSED))
1203 {
1204 /* kill focus */
1205 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
1206 if (bSuccess)
1207 pItem->dwMaxCapacity = uResult;
1208 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
1209 pItem->dwNukeOnDelete = TRUE;
1210 else
1211 pItem->dwNukeOnDelete = FALSE;
1212 }
1213 return TRUE;
1214
1215 }
1216 break;
1217 case WM_DESTROY:
1218 FreeDriveItemContext(hwndDlg);
1219 break;
1220 }
1221 return FALSE;
1222 }
1223
1224 BOOL SH_ShowRecycleBinProperties(WCHAR sDrive)
1225 {
1226 HPROPSHEETPAGE hpsp[1];
1227 PROPSHEETHEADERW psh;
1228 HPROPSHEETPAGE hprop;
1229
1230 BOOL ret;
1231
1232
1233 ZeroMemory(&psh, sizeof(PROPSHEETHEADERW));
1234 psh.dwSize = sizeof(PROPSHEETHEADERW);
1235 psh.dwFlags = PSP_DEFAULT | PSH_PROPTITLE;
1236 psh.pszCaption = MAKEINTRESOURCEW(IDS_RECYCLEBIN_FOLDER_NAME);
1237 psh.hwndParent = NULL;
1238 psh.phpage = hpsp;
1239 psh.hInstance = shell32_hInstance;
1240
1241 hprop = SH_CreatePropertySheetPage(IDD_RECYCLE_BIN_PROPERTIES, RecycleBinDlg, (LPARAM)sDrive, NULL);
1242 if (!hprop)
1243 {
1244 ERR("Failed to create property sheet\n");
1245 return FALSE;
1246 }
1247 hpsp[psh.nPages] = hprop;
1248 psh.nPages++;
1249
1250
1251 ret = PropertySheetW(&psh);
1252 if (ret < 0)
1253 return FALSE;
1254 else
1255 return TRUE;
1256 }
1257
1258 BOOL
1259 TRASH_CanTrashFile(LPCWSTR wszPath)
1260 {
1261 LONG ret;
1262 DWORD dwNukeOnDelete, dwType, VolSerialNumber, MaxComponentLength;
1263 DWORD FileSystemFlags, dwSize, dwDisposition;
1264 HKEY hKey;
1265 WCHAR szBuffer[10];
1266 WCHAR szKey[150] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume\\";
1267
1268 if (wszPath[1] != L':')
1269 {
1270 /* path is UNC */
1271 return FALSE;
1272 }
1273
1274 if (GetDriveTypeW(wszPath) != DRIVE_FIXED)
1275 {
1276 /* no bitbucket on removable media */
1277 return FALSE;
1278 }
1279
1280 if (!GetVolumeInformationW(wszPath, NULL, 0, &VolSerialNumber, &MaxComponentLength, &FileSystemFlags, NULL, 0))
1281 {
1282 ERR("GetVolumeInformationW failed with %u\n", GetLastError());
1283 return FALSE;
1284 }
1285
1286 swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber), HIWORD(VolSerialNumber));
1287 wcscat(szKey, szBuffer);
1288
1289 if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS)
1290 {
1291 ERR("RegCreateKeyExW failed\n");
1292 return FALSE;
1293 }
1294
1295 if (dwDisposition & REG_CREATED_NEW_KEY)
1296 {
1297 /* per default move to bitbucket */
1298 dwNukeOnDelete = 0;
1299 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
1300 /* per default unlimited size */
1301 dwSize = -1;
1302 RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize, sizeof(DWORD));
1303 RegCloseKey(hKey);
1304 return TRUE;
1305 }
1306 else
1307 {
1308 dwSize = sizeof(dwNukeOnDelete);
1309 ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType, (LPBYTE)&dwNukeOnDelete, &dwSize);
1310 if (ret != ERROR_SUCCESS)
1311 {
1312 if (ret == ERROR_FILE_NOT_FOUND)
1313 {
1314 /* restore key and enable bitbucket */
1315 dwNukeOnDelete = 0;
1316 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
1317 }
1318 RegCloseKey(hKey);
1319 return TRUE;
1320 }
1321 else if (dwNukeOnDelete)
1322 {
1323 /* do not delete to bitbucket */
1324 RegCloseKey(hKey);
1325 return FALSE;
1326 }
1327 /* FIXME
1328 * check if bitbucket is full
1329 */
1330 RegCloseKey(hKey);
1331 return TRUE;
1332 }
1333 }
1334
1335 BOOL
1336 TRASH_TrashFile(LPCWSTR wszPath)
1337 {
1338 TRACE("(%s)\n", debugstr_w(wszPath));
1339 return DeleteFileToRecycleBin(wszPath);
1340 }
1341
1342 /*************************************************************************
1343 * SHUpdateCRecycleBinIcon [SHELL32.@]
1344 *
1345 * Undocumented
1346 */
1347 EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void)
1348 {
1349 FIXME("stub\n");
1350
1351
1352
1353 return S_OK;
1354 }
1355
1356 /****************************************************************************
1357 * IDropTarget implementation
1358 */
1359 BOOL CRecycleBin::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
1360 {
1361 /* TODO on shift we should delete, we should update the cursor manager to show this. */
1362
1363 DWORD dwEffect = DROPEFFECT_COPY;
1364
1365 *pdwEffect = DROPEFFECT_NONE;
1366
1367 if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
1368 *pdwEffect = KeyStateToDropEffect (dwKeyState);
1369
1370 if (*pdwEffect == DROPEFFECT_NONE)
1371 *pdwEffect = dwEffect;
1372
1373 /* ... matches the desired effect ? */
1374 if (dwEffect & *pdwEffect) {
1375 return TRUE;
1376 }
1377 }
1378 return FALSE;
1379 }
1380
1381 HRESULT WINAPI CRecycleBin::DragEnter(IDataObject *pDataObject,
1382 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1383 {
1384 TRACE("Recycle bin drag over (%p)\n", this);
1385 /* The recycle bin accepts pretty much everything, and sets a CSIDL flag. */
1386 fAcceptFmt = TRUE;
1387
1388 QueryDrop(dwKeyState, pdwEffect);
1389 return S_OK;
1390 }
1391
1392 HRESULT WINAPI CRecycleBin::DragOver(DWORD dwKeyState, POINTL pt,
1393 DWORD *pdwEffect)
1394 {
1395 TRACE("(%p)\n", this);
1396
1397 if (!pdwEffect)
1398 return E_INVALIDARG;
1399
1400 QueryDrop(dwKeyState, pdwEffect);
1401
1402 return S_OK;
1403 }
1404
1405 HRESULT WINAPI CRecycleBin::DragLeave()
1406 {
1407 TRACE("(%p)\n", this);
1408
1409 fAcceptFmt = FALSE;
1410
1411 return S_OK;
1412 }
1413
1414 HRESULT WINAPI CRecycleBin::Drop(IDataObject *pDataObject,
1415 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1416 {
1417 TRACE("(%p) object dropped on recycle bin, effect %u\n", this, *pdwEffect);
1418
1419 /* TODO: pdwEffect should be read and make the drop object be permanently deleted in the move case (shift held) */
1420
1421 FORMATETC fmt;
1422 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
1423 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1424
1425 /* Handle cfShellIDList Drop objects here, otherwise send the approriate message to other software */
1426 if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) {
1427 IStream *s;
1428 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &s);
1429 SHCreateThread(DoDeleteThreadProc, s, NULL, NULL);
1430 }
1431 else
1432 {
1433 /*
1434 * TODO call SetData on the data object with format CFSTR_TARGETCLSID
1435 * set to the Recycle Bin's class identifier CLSID_RecycleBin.
1436 */
1437 }
1438 return S_OK;
1439 }
1440
1441 DWORD WINAPI DoDeleteThreadProc(LPVOID lpParameter)
1442 {
1443 CoInitialize(NULL);
1444 CComPtr<IDataObject> pDataObject;
1445 HRESULT hr = CoGetInterfaceAndReleaseStream (static_cast<IStream*>(lpParameter), IID_PPV_ARG(IDataObject, &pDataObject));
1446 if (SUCCEEDED(hr))
1447 {
1448 DoDeleteDataObject(pDataObject);
1449 }
1450 CoUninitialize();
1451 return 0;
1452 }
1453
1454 HRESULT WINAPI DoDeleteDataObject(IDataObject *pda)
1455 {
1456 TRACE("performing delete");
1457 HRESULT hr;
1458
1459 STGMEDIUM medium;
1460 FORMATETC formatetc;
1461 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
1462 hr = pda->GetData(&formatetc, &medium);
1463 if (FAILED(hr))
1464 return hr;
1465
1466 /* lock the handle */
1467 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
1468 if (!lpcida)
1469 {
1470 ReleaseStgMedium(&medium);
1471 return E_FAIL;
1472 }
1473
1474 /* convert the data into pidl */
1475 LPITEMIDLIST pidl;
1476 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
1477 if (!apidl)
1478 {
1479 ReleaseStgMedium(&medium);
1480 return E_FAIL;
1481 }
1482
1483 CComPtr<IShellFolder> psfDesktop;
1484 CComPtr<IShellFolder> psfFrom = NULL;
1485
1486 /* Grab the desktop shell folder */
1487 hr = SHGetDesktopFolder(&psfDesktop);
1488 if (FAILED(hr))
1489 {
1490 ERR("SHGetDesktopFolder failed\n");
1491 SHFree(pidl);
1492 _ILFreeaPidl(apidl, lpcida->cidl);
1493 ReleaseStgMedium(&medium);
1494 return E_FAIL;
1495 }
1496
1497 /* Find source folder, this is where the clipboard data was copied from */
1498 if (_ILIsDesktop(pidl))
1499 {
1500 psfFrom = psfDesktop;
1501 }
1502 else
1503 {
1504 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
1505 if (FAILED(hr))
1506 {
1507 ERR("no IShellFolder\n");
1508 SHFree(pidl);
1509 _ILFreeaPidl(apidl, lpcida->cidl);
1510 ReleaseStgMedium(&medium);
1511 return E_FAIL;
1512 }
1513 }
1514
1515 STRRET strTemp;
1516 hr = psfFrom->GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strTemp);
1517 if (FAILED(hr))
1518 {
1519 ERR("IShellFolder_GetDisplayNameOf failed with %x\n", hr);
1520 SHFree(pidl);
1521 _ILFreeaPidl(apidl, lpcida->cidl);
1522 ReleaseStgMedium(&medium);
1523 return hr;
1524 }
1525
1526 WCHAR wszPath[MAX_PATH];
1527 hr = StrRetToBufW(&strTemp, apidl[0], wszPath, _countof(wszPath));
1528 if (FAILED(hr))
1529 {
1530 ERR("StrRetToBufW failed with %x\n", hr);
1531 SHFree(pidl);
1532 _ILFreeaPidl(apidl, lpcida->cidl);
1533 ReleaseStgMedium(&medium);
1534 return hr;
1535 }
1536
1537 /* Only keep the base path */
1538 LPWSTR pwszFilename = PathFindFileNameW(wszPath);
1539 *pwszFilename = L'\0';
1540
1541 /* Build paths list */
1542 LPWSTR pwszPaths = BuildPathsList(wszPath, lpcida->cidl, (LPCITEMIDLIST*) apidl);
1543 if (!pwszPaths)
1544 {
1545 SHFree(pidl);
1546 _ILFreeaPidl(apidl, lpcida->cidl);
1547 ReleaseStgMedium(&medium);
1548 return E_FAIL;
1549 }
1550
1551 /* Delete them */
1552 SHFILEOPSTRUCTW FileOp;
1553 ZeroMemory(&FileOp, sizeof(FileOp));
1554 FileOp.wFunc = FO_DELETE;
1555 FileOp.pFrom = pwszPaths;
1556 FileOp.fFlags = FOF_ALLOWUNDO;
1557
1558 if (SHFileOperationW(&FileOp) != 0)
1559 {
1560 ERR("SHFileOperation failed with 0x%x for %s\n", GetLastError(), debugstr_w(pwszPaths));
1561 hr = E_FAIL;
1562 }
1563
1564 HeapFree(GetProcessHeap(), 0, pwszPaths);
1565 SHFree(pidl);
1566 _ILFreeaPidl(apidl, lpcida->cidl);
1567 ReleaseStgMedium(&medium);
1568
1569 return hr;
1570 }
1571
1572 /*************************************************************************
1573 * SHEmptyRecycleBinA (SHELL32.@)
1574 */
1575 HRESULT WINAPI SHEmptyRecycleBinA(HWND hwnd, LPCSTR pszRootPath, DWORD dwFlags)
1576 {
1577 LPWSTR szRootPathW = NULL;
1578 int len;
1579 HRESULT hr;
1580
1581 TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_a(pszRootPath), dwFlags);
1582
1583 if (pszRootPath)
1584 {
1585 len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
1586 if (len == 0)
1587 return HRESULT_FROM_WIN32(GetLastError());
1588 szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1589 if (!szRootPathW)
1590 return E_OUTOFMEMORY;
1591 if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
1592 {
1593 HeapFree(GetProcessHeap(), 0, szRootPathW);
1594 return HRESULT_FROM_WIN32(GetLastError());
1595 }
1596 }
1597
1598 hr = SHEmptyRecycleBinW(hwnd, szRootPathW, dwFlags);
1599 HeapFree(GetProcessHeap(), 0, szRootPathW);
1600
1601 return hr;
1602 }
1603
1604 HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags)
1605 {
1606 WCHAR szPath[MAX_PATH] = {0}, szBuffer[MAX_PATH];
1607 DWORD dwSize, dwType, count;
1608 LONG ret;
1609 IShellFolder *pDesktop, *pRecycleBin;
1610 PIDLIST_ABSOLUTE pidlRecycleBin;
1611 PITEMID_CHILD pidl;
1612 HRESULT hr = S_OK;
1613 LPENUMIDLIST penumFiles;
1614 STRRET StrRet;
1615
1616 TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_w(pszRootPath), dwFlags);
1617
1618 if (!(dwFlags & SHERB_NOCONFIRMATION))
1619 {
1620 hr = SHGetDesktopFolder(&pDesktop);
1621 if (FAILED(hr))
1622 return hr;
1623 hr = SHGetFolderLocation(NULL, CSIDL_BITBUCKET, NULL, 0, &pidlRecycleBin);
1624 if (FAILED(hr))
1625 {
1626 pDesktop->Release();
1627 return hr;
1628 }
1629 hr = pDesktop->BindToObject(pidlRecycleBin, NULL, IID_PPV_ARG(IShellFolder, &pRecycleBin));
1630 CoTaskMemFree(pidlRecycleBin);
1631 pDesktop->Release();
1632 if (FAILED(hr))
1633 return hr;
1634 hr = pRecycleBin->EnumObjects(hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penumFiles);
1635 if (FAILED(hr))
1636 {
1637 pRecycleBin->Release();
1638 return hr;
1639 }
1640
1641 count = 0;
1642 if (hr != S_FALSE)
1643 {
1644 while (penumFiles->Next(1, &pidl, NULL) == S_OK)
1645 {
1646 count++;
1647 pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL, &StrRet);
1648 StrRetToBuf(&StrRet, pidl, szBuffer, _countof(szBuffer));
1649 CoTaskMemFree(pidl);
1650 }
1651 penumFiles->Release();
1652 }
1653 pRecycleBin->Release();
1654
1655 switch (count)
1656 {
1657 case 0:
1658 /* no files, don't need confirmation */
1659 break;
1660
1661 case 1:
1662 /* we have only one item inside the bin, so show a message box with its name */
1663 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEITEM_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1664 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1665 {
1666 return S_OK;
1667 }
1668 break;
1669
1670 default:
1671 /* we have more than one item, so show a message box with the count of the items */
1672 StringCbPrintfW(szBuffer, sizeof(szBuffer), L"%u", count);
1673 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEMULTIPLE_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1674 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1675 {
1676 return S_OK;
1677 }
1678 break;
1679 }
1680 }
1681
1682 if (dwFlags & SHERB_NOPROGRESSUI)
1683 {
1684 ret = EmptyRecycleBinW(pszRootPath);
1685 }
1686 else
1687 {
1688 /* FIXME
1689 * show a progress dialog
1690 */
1691 ret = EmptyRecycleBinW(pszRootPath);
1692 }
1693
1694 if (!ret)
1695 return HRESULT_FROM_WIN32(GetLastError());
1696
1697 if (!(dwFlags & SHERB_NOSOUND))
1698 {
1699 dwSize = sizeof(szPath);
1700 ret = RegGetValueW(HKEY_CURRENT_USER,
1701 L"AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current",
1702 NULL,
1703 RRF_RT_REG_SZ,
1704 &dwType,
1705 (PVOID)szPath,
1706 &dwSize);
1707 if (ret != ERROR_SUCCESS)
1708 return S_OK;
1709
1710 if (dwType != REG_EXPAND_SZ) /* type dismatch */
1711 return S_OK;
1712
1713 szPath[(sizeof(szPath)/sizeof(WCHAR))-1] = L'\0';
1714 PlaySoundW(szPath, NULL, SND_FILENAME);
1715 }
1716 return S_OK;
1717 }
1718
1719 HRESULT WINAPI SHQueryRecycleBinA(LPCSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1720 {
1721 LPWSTR szRootPathW = NULL;
1722 int len;
1723 HRESULT hr;
1724
1725 TRACE("%s, %p\n", debugstr_a(pszRootPath), pSHQueryRBInfo);
1726
1727 if (pszRootPath)
1728 {
1729 len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
1730 if (len == 0)
1731 return HRESULT_FROM_WIN32(GetLastError());
1732 szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1733 if (!szRootPathW)
1734 return E_OUTOFMEMORY;
1735 if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
1736 {
1737 HeapFree(GetProcessHeap(), 0, szRootPathW);
1738 return HRESULT_FROM_WIN32(GetLastError());
1739 }
1740 }
1741
1742 hr = SHQueryRecycleBinW(szRootPathW, pSHQueryRBInfo);
1743 HeapFree(GetProcessHeap(), 0, szRootPathW);
1744
1745 return hr;
1746 }
1747
1748 HRESULT WINAPI SHQueryRecycleBinW(LPCWSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1749 {
1750 FIXME("%s, %p - stub\n", debugstr_w(pszRootPath), pSHQueryRBInfo);
1751
1752 if (!(pszRootPath) || (pszRootPath[0] == 0) ||
1753 !(pSHQueryRBInfo) || (pSHQueryRBInfo->cbSize < sizeof(SHQUERYRBINFO)))
1754 {
1755 return E_INVALIDARG;
1756 }
1757
1758 pSHQueryRBInfo->i64Size = 0;
1759 pSHQueryRBInfo->i64NumItems = 0;
1760
1761 return S_OK;
1762 }