fb8c1d8dc18eb9119abe94fe4876c4e7c41a7a65
[reactos.git] / 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 WINE_DEFAULT_DEBUG_CHANNEL(CRecycleBin);
28
29 typedef struct
30 {
31 int column_name_id;
32 const GUID *fmtId;
33 DWORD pid;
34 int pcsFlags;
35 int fmt;
36 int cxChars;
37 } columninfo;
38
39 static const columninfo RecycleBinColumns[] =
40 {
41 {IDS_SHV_COLUMN_NAME, &FMTID_Storage, PID_STG_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
42 {IDS_SHV_COLUMN_DELFROM, &FMTID_Displaced, PID_DISPLACED_FROM, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
43 {IDS_SHV_COLUMN_DELDATE, &FMTID_Displaced, PID_DISPLACED_DATE, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
44 {IDS_SHV_COLUMN_SIZE, &FMTID_Storage, PID_STG_SIZE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 20},
45 {IDS_SHV_COLUMN_TYPE, &FMTID_Storage, PID_STG_STORAGETYPE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
46 {IDS_SHV_COLUMN_MODIFIED, &FMTID_Storage, PID_STG_WRITETIME, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
47 /* {"creation time", &FMTID_Storage, PID_STG_CREATETIME, SHCOLSTATE_TYPE_DATE, LVCFMT_LEFT, 20}, */
48 /* {"attribs", &FMTID_Storage, PID_STG_ATTRIBUTES, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 20}, */
49 };
50
51 #define COLUMN_NAME 0
52 #define COLUMN_DELFROM 1
53 #define COLUMN_DATEDEL 2
54 #define COLUMN_SIZE 3
55 #define COLUMN_TYPE 4
56 #define COLUMN_MTIME 5
57
58 #define COLUMNS_COUNT 6
59
60 /*
61 * Recycle Bin folder
62 */
63
64 HRESULT CRecyclerExtractIcon_CreateInstance(LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
65 {
66 CComPtr<IDefaultExtractIconInit> initIcon;
67 HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
68 if (FAILED_UNEXPECTEDLY(hr))
69 return hr;
70
71 /* FIXME: This is completely unimplemented */
72 initIcon->SetNormalIcon(swShell32Name, 0);
73
74 return initIcon->QueryInterface(riid, ppvOut);
75 }
76
77 class CRecycleBinEnum :
78 public CEnumIDListBase
79 {
80 private:
81 public:
82 CRecycleBinEnum();
83 ~CRecycleBinEnum();
84 HRESULT WINAPI Initialize(DWORD dwFlags);
85 static BOOL WINAPI CBEnumRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile);
86 BOOL WINAPI CBEnumRecycleBin(IN HANDLE hDeletedFile);
87
88 BEGIN_COM_MAP(CRecycleBinEnum)
89 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
90 END_COM_MAP()
91 };
92
93 class CRecycleBinItemContextMenu :
94 public CComObjectRootEx<CComMultiThreadModelNoCS>,
95 public IContextMenu2
96 {
97 private:
98 LPITEMIDLIST apidl;
99 public:
100 CRecycleBinItemContextMenu();
101 ~CRecycleBinItemContextMenu();
102 HRESULT WINAPI Initialize(LPCITEMIDLIST pidl);
103
104 // IContextMenu
105 virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
106 virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
107 virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
108
109 // IContextMenu2
110 virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
111
112 BEGIN_COM_MAP(CRecycleBinItemContextMenu)
113 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
114 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
115 END_COM_MAP()
116 };
117
118 typedef struct
119 {
120 PIDLRecycleStruct *pFileDetails;
121 HANDLE hDeletedFile;
122 BOOL bFound;
123 } SEARCH_CONTEXT, *PSEARCH_CONTEXT;
124
125 BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile)
126 {
127 PSEARCH_CONTEXT pContext = (PSEARCH_CONTEXT)Context;
128
129 PDELETED_FILE_DETAILS_W pFileDetails;
130 DWORD dwSize;
131 BOOL ret;
132
133 if (!GetDeletedFileDetailsW(hDeletedFile,
134 0,
135 NULL,
136 &dwSize) &&
137 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
138 {
139 ERR("GetDeletedFileDetailsW failed\n");
140 return FALSE;
141 }
142
143 pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
144 if (!pFileDetails)
145 {
146 ERR("No memory\n");
147 return FALSE;
148 }
149
150 if (!GetDeletedFileDetailsW(hDeletedFile,
151 dwSize,
152 pFileDetails,
153 NULL))
154 {
155 ERR("GetDeletedFileDetailsW failed\n");
156 SHFree(pFileDetails);
157 return FALSE;
158 }
159
160 ret = memcmp(pFileDetails, pContext->pFileDetails, dwSize);
161 if (!ret)
162 {
163 pContext->hDeletedFile = hDeletedFile;
164 pContext->bFound = TRUE;
165 }
166 else
167 CloseRecycleBinHandle(hDeletedFile);
168
169 SHFree(pFileDetails);
170 return ret;
171 }
172
173 static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl)
174 {
175 LPPIDLDATA pdata = _ILGetDataPointer(pidl);
176
177 if (pdata && pdata->type == 0x00)
178 return (PIDLRecycleStruct*) & (pdata->u.crecycle);
179
180 return NULL;
181 }
182
183 CRecycleBinEnum::CRecycleBinEnum()
184 {
185 }
186
187 CRecycleBinEnum::~CRecycleBinEnum()
188 {
189 }
190
191 HRESULT WINAPI CRecycleBinEnum::Initialize(DWORD dwFlags)
192 {
193 static LPCWSTR szDrive = L"C:\\";
194
195 if (dwFlags & SHCONTF_NONFOLDERS)
196 {
197 TRACE("Starting Enumeration\n");
198
199 if (!EnumerateRecycleBinW(szDrive /* FIXME */ , CBEnumRecycleBin, (PVOID)this))
200 {
201 WARN("Error: EnumerateCRecycleBinW failed\n");
202 return E_FAIL;
203 }
204 }
205 else
206 {
207 // do nothing
208 }
209 return S_OK;
210 }
211
212 static LPITEMIDLIST _ILCreateRecycleItem(PDELETED_FILE_DETAILS_W pFileDetails)
213 {
214 PIDLDATA tmp;
215 LPITEMIDLIST pidl;
216 PIDLRecycleStruct * p;
217 int size0 = (char*)&tmp.u.crecycle.szName - (char*)&tmp.u.crecycle;
218 int size = size0;
219
220 tmp.type = 0x00;
221 size += (wcslen(pFileDetails->FileName) + 1) * sizeof(WCHAR);
222
223 pidl = (LPITEMIDLIST)SHAlloc(size + 4);
224 if (!pidl)
225 return pidl;
226
227 pidl->mkid.cb = size + 2;
228 memcpy(pidl->mkid.abID, &tmp, 2 + size0);
229
230 p = &((PIDLDATA*)pidl->mkid.abID)->u.crecycle;
231 RtlCopyMemory(p, pFileDetails, sizeof(DELETED_FILE_DETAILS_W));
232 wcscpy(p->szName, pFileDetails->FileName);
233 *(WORD*)((char*)pidl + (size + 2)) = 0;
234 return pidl;
235 }
236
237 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile)
238 {
239 return static_cast<CRecycleBinEnum *>(Context)->CBEnumRecycleBin(hDeletedFile);
240 }
241
242 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN HANDLE hDeletedFile)
243 {
244 PDELETED_FILE_DETAILS_W pFileDetails;
245 DWORD dwSize;
246 LPITEMIDLIST pidl = NULL;
247 BOOL ret;
248
249 if (!GetDeletedFileDetailsW(hDeletedFile,
250 0,
251 NULL,
252 &dwSize) &&
253 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
254 {
255 ERR("GetDeletedFileDetailsW failed\n");
256 return FALSE;
257 }
258
259 pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
260 if (!pFileDetails)
261 {
262 ERR("No memory\n");
263 return FALSE;
264 }
265
266 if (!GetDeletedFileDetailsW(hDeletedFile,
267 dwSize,
268 pFileDetails,
269 NULL))
270 {
271 ERR("GetDeletedFileDetailsW failed\n");
272 SHFree(pFileDetails);
273 return FALSE;
274 }
275
276 pidl = _ILCreateRecycleItem(pFileDetails);
277 if (!pidl)
278 {
279 SHFree(pFileDetails);
280 return FALSE;
281 }
282
283 ret = AddToEnumList(pidl);
284
285 if (!ret)
286 SHFree(pidl);
287 SHFree(pFileDetails);
288 TRACE("Returning %d\n", ret);
289 CloseRecycleBinHandle(hDeletedFile);
290 return ret;
291 }
292
293 /**************************************************************************
294 * IContextMenu2 Bitbucket Item Implementation
295 */
296
297 CRecycleBinItemContextMenu::CRecycleBinItemContextMenu()
298 {
299 apidl = NULL;
300 }
301
302 CRecycleBinItemContextMenu::~CRecycleBinItemContextMenu()
303 {
304 ILFree(apidl);
305 }
306
307 HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST pidl)
308 {
309 apidl = ILClone(pidl);
310 if (apidl == NULL)
311 return E_OUTOFMEMORY;
312 return S_OK;
313 }
314
315 HRESULT WINAPI CRecycleBinItemContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
316 {
317 WCHAR szBuffer[30] = {0};
318 ULONG Count = 1;
319
320 TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
321
322 if (LoadStringW(shell32_hInstance, IDS_RESTORE, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
323 {
324 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
325 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_ENABLED);
326 Count++;
327 }
328
329 if (LoadStringW(shell32_hInstance, IDS_CUT, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
330 {
331 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
332 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
333 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED);
334 }
335
336 if (LoadStringW(shell32_hInstance, IDS_DELETE, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
337 {
338 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
339 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
340 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED);
341 }
342
343 if (LoadStringW(shell32_hInstance, IDS_PROPERTIES, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
344 {
345 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
346 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
347 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_DEFAULT);
348 }
349
350 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Count);
351 }
352
353 HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
354 {
355 SEARCH_CONTEXT Context;
356 static LPCWSTR szDrive = L"C:\\";
357
358 TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi, lpcmi->lpVerb, lpcmi->hwnd);
359
360 if (lpcmi->lpVerb == MAKEINTRESOURCEA(1) || lpcmi->lpVerb == MAKEINTRESOURCEA(5))
361 {
362 Context.pFileDetails = _ILGetRecycleStruct(apidl);
363 Context.bFound = FALSE;
364
365 EnumerateRecycleBinW(szDrive, CBSearchRecycleBin, (PVOID)&Context);
366 if (!Context.bFound)
367 return E_FAIL;
368
369 if (lpcmi->lpVerb == MAKEINTRESOURCEA(1))
370 {
371 /* restore file */
372 if (RestoreFile(Context.hDeletedFile))
373 return S_OK;
374 else
375 return E_FAIL;
376 }
377 else
378 {
379 DeleteFileHandleToRecycleBin(Context.hDeletedFile);
380 return E_NOTIMPL;
381 }
382 }
383 else if (lpcmi->lpVerb == MAKEINTRESOURCEA(3))
384 {
385 FIXME("implement cut\n");
386 return E_NOTIMPL;
387 }
388 else if (lpcmi->lpVerb == MAKEINTRESOURCEA(7))
389 {
390 FIXME("implement properties\n");
391 return E_NOTIMPL;
392 }
393
394 return S_OK;
395 }
396
397 HRESULT WINAPI CRecycleBinItemContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
398 {
399 TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
400
401 return E_FAIL;
402 }
403
404 HRESULT WINAPI CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
405 {
406 TRACE("CRecycleBin_IContextMenu2Item_HandleMenuMsg (%p)->(msg=%x wp=%lx lp=%lx)\n", this, uMsg, wParam, lParam);
407
408 return E_NOTIMPL;
409 }
410
411 CRecycleBin::CRecycleBin()
412 {
413 pidl = NULL;
414 }
415
416 CRecycleBin::~CRecycleBin()
417 {
418 SHFree(pidl);
419 }
420
421 /*************************************************************************
422 * RecycleBin IPersistFolder2 interface
423 */
424
425 HRESULT WINAPI CRecycleBin::GetClassID(CLSID *pClassID)
426 {
427 TRACE("(%p, %p)\n", this, pClassID);
428 if (pClassID == NULL)
429 return E_INVALIDARG;
430 memcpy(pClassID, &CLSID_RecycleBin, sizeof(CLSID));
431 return S_OK;
432 }
433
434 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidl)
435 {
436 TRACE("(%p, %p)\n", this, pidl);
437
438 SHFree((LPVOID)this->pidl);
439 this->pidl = ILClone(pidl);
440 if (this->pidl == NULL)
441 return E_OUTOFMEMORY;
442 return S_OK;
443 }
444
445 HRESULT WINAPI CRecycleBin::GetCurFolder(LPITEMIDLIST *ppidl)
446 {
447 TRACE("\n");
448 *ppidl = ILClone(pidl);
449 return S_OK;
450 }
451
452 /*************************************************************************
453 * RecycleBin IShellFolder2 interface
454 */
455
456 HRESULT WINAPI CRecycleBin::ParseDisplayName(HWND hwnd, LPBC pbc,
457 LPOLESTR pszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl,
458 ULONG *pdwAttributes)
459 {
460 FIXME("stub\n");
461 return E_NOTIMPL;
462 }
463
464
465 PDELETED_FILE_DETAILS_W
466 UnpackDetailsFromPidl(LPCITEMIDLIST pidl)
467 {
468 return (PDELETED_FILE_DETAILS_W)&pidl->mkid.abID;
469 }
470
471 HRESULT WINAPI CRecycleBin::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
472 {
473 return ShellObjectCreatorInit<CRecycleBinEnum>(dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
474 }
475
476 HRESULT WINAPI CRecycleBin::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
477 {
478 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
479 return E_NOTIMPL;
480 }
481
482 HRESULT WINAPI CRecycleBin::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
483 {
484 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
485 return E_NOTIMPL;
486 }
487
488 HRESULT WINAPI CRecycleBin::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
489 {
490 PIDLRecycleStruct* pData1 = _ILGetRecycleStruct(pidl1);
491 PIDLRecycleStruct* pData2 = _ILGetRecycleStruct(pidl2);
492 LPWSTR pName1, pName2;
493
494 if(!pData1 || !pData2 || LOWORD(lParam) >= COLUMNS_COUNT)
495 return E_INVALIDARG;
496
497 SHORT result;
498 LONGLONG diff;
499 switch (LOWORD(lParam))
500 {
501 case 0: /* Name */
502 pName1 = PathFindFileNameW(pData1->szName);
503 pName2 = PathFindFileNameW(pData2->szName);
504 result = wcsicmp(pName1, pName2);
505 break;
506 case 1: /* Orig. Location */
507 result = wcsicmp(pData1->szName, pData2->szName);
508 break;
509 case 2: /* Date Deleted */
510 result = CompareFileTime(&pData1->DeletionTime, &pData2->DeletionTime);
511 break;
512 case 3: /* Size */
513 diff = pData1->FileSize.QuadPart - pData2->FileSize.QuadPart;
514 return MAKE_COMPARE_HRESULT(diff);
515 case 4: /* Type */
516 pName1 = PathFindExtensionW(pData1->szName);
517 pName2 = PathFindExtensionW(pData2->szName);
518 result = wcsicmp(pName1, pName2);
519 break;
520 case 5: /* Modified */
521 result = CompareFileTime(&pData1->LastModification, &pData2->LastModification);
522 break;
523 }
524 return MAKE_COMPARE_HRESULT(result);
525 }
526
527 HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv)
528 {
529 CComPtr<IShellView> pShellView;
530 HRESULT hr = E_NOINTERFACE;
531
532 TRACE("(%p, %p, %s, %p)\n", this, hwndOwner, debugstr_guid(&riid), ppv);
533
534 if (!ppv)
535 return hr;
536
537 *ppv = NULL;
538
539 if (IsEqualIID (riid, IID_IDropTarget))
540 {
541 hr = CRecyclerDropTarget_CreateInstance(riid, ppv);
542 }
543 else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid, IID_IContextMenu2))
544 {
545 hr = this->QueryInterface(riid, ppv);
546 }
547 else if (IsEqualIID (riid, IID_IShellView))
548 {
549 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
550 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppv);
551 }
552 else
553 return hr;
554
555 TRACE ("-- (%p)->(interface=%p)\n", this, ppv);
556 return hr;
557
558 }
559
560 HRESULT WINAPI CRecycleBin::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
561 SFGAOF *rgfInOut)
562 {
563 TRACE("(%p, %d, {%p, ...}, {%x})\n", this, cidl, apidl ? apidl[0] : NULL, (unsigned int)*rgfInOut);
564 *rgfInOut &= SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANLINK;
565 return S_OK;
566 }
567
568 HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
569 REFIID riid, UINT *prgfInOut, void **ppv)
570 {
571 LPVOID pObj = NULL;
572 HRESULT hr = E_INVALIDARG;
573
574 TRACE ("(%p)->(%p,%u,apidl=%p, %p %p)\n", this,
575 hwndOwner, cidl, apidl, prgfInOut, ppv);
576
577 if (!ppv)
578 return hr;
579
580 *ppv = NULL;
581
582 if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2)) && (cidl >= 1))
583 {
584 hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(apidl[0], riid, &pObj);
585 }
586 else if((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1))
587 {
588 hr = CRecyclerExtractIcon_CreateInstance(apidl[0], riid, &pObj);
589 }
590 else
591 hr = E_NOINTERFACE;
592
593 if (SUCCEEDED(hr) && !pObj)
594 hr = E_OUTOFMEMORY;
595
596 *ppv = pObj;
597 TRACE ("(%p)->hr=0x%08x\n", this, hr);
598 return hr;
599 }
600
601 HRESULT WINAPI CRecycleBin::GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF uFlags, STRRET *pName)
602 {
603 PIDLRecycleStruct *pFileDetails;
604 LPWSTR pFileName;
605
606 TRACE("(%p, %p, %x, %p)\n", this, pidl, (unsigned int)uFlags, pName);
607
608 pFileDetails = _ILGetRecycleStruct(pidl);
609 if (!pFileDetails)
610 {
611 pName->cStr[0] = 0;
612 pName->uType = STRRET_CSTR;
613 return E_INVALIDARG;
614 }
615
616 pFileName = wcsrchr(pFileDetails->szName, L'\\');
617 if (!pFileName)
618 {
619 pName->cStr[0] = 0;
620 pName->uType = STRRET_CSTR;
621 return E_UNEXPECTED;
622 }
623
624 pName->pOleStr = StrDupW(pFileName + 1);
625 if (pName->pOleStr == NULL)
626 return E_OUTOFMEMORY;
627
628 pName->uType = STRRET_WSTR;
629 return S_OK;
630 }
631
632 HRESULT WINAPI CRecycleBin::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pszName,
633 SHGDNF uFlags, PITEMID_CHILD *ppidlOut)
634 {
635 TRACE("\n");
636 return E_FAIL; /* not supported */
637 }
638
639 HRESULT WINAPI CRecycleBin::GetDefaultSearchGUID(GUID *pguid)
640 {
641 FIXME("stub\n");
642 return E_NOTIMPL;
643 }
644
645 HRESULT WINAPI CRecycleBin::EnumSearches(IEnumExtraSearch **ppEnum)
646 {
647 FIXME("stub\n");
648 *ppEnum = NULL;
649 return E_NOTIMPL;
650 }
651
652 HRESULT WINAPI CRecycleBin::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
653 {
654 TRACE("(%p, %x, %p, %p)\n", this, (unsigned int)dwReserved, pSort, pDisplay);
655 if (pSort)
656 *pSort = 0;
657 if (pDisplay)
658 *pDisplay = 0;
659 return S_OK;
660 }
661
662 HRESULT WINAPI CRecycleBin::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
663 {
664 TRACE("(%p, %d, %p)\n", this, iColumn, pcsFlags);
665 if (iColumn >= COLUMNS_COUNT)
666 return E_INVALIDARG;
667 *pcsFlags = RecycleBinColumns[iColumn].pcsFlags;
668 return S_OK;
669 }
670
671 HRESULT WINAPI CRecycleBin::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
672 {
673 FIXME("stub\n");
674 return E_NOTIMPL;
675 }
676
677 static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME * ft)
678 {
679 FILETIME lft;
680 SYSTEMTIME time;
681 int ret;
682
683 FileTimeToLocalFileTime(ft, &lft);
684 FileTimeToSystemTime(&lft, &time);
685
686 ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size);
687 if (ret > 0 && ret < size)
688 {
689 /* Append space + time without seconds */
690 buffer[ret-1] = ' ';
691 GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret);
692 }
693
694 return (ret != 0 ? E_FAIL : S_OK);
695 }
696
697 HRESULT WINAPI CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, LPSHELLDETAILS pDetails)
698 {
699 PIDLRecycleStruct * pFileDetails;
700 WCHAR buffer[MAX_PATH];
701 WCHAR szTypeName[100];
702 LPWSTR pszBackslash;
703 UINT Length;
704
705 TRACE("(%p, %p, %d, %p)\n", this, pidl, iColumn, pDetails);
706 if (iColumn >= COLUMNS_COUNT)
707 return E_FAIL;
708 pDetails->fmt = RecycleBinColumns[iColumn].fmt;
709 pDetails->cxChar = RecycleBinColumns[iColumn].cxChars;
710 if (pidl == NULL)
711 return SHSetStrRet(&pDetails->str, RecycleBinColumns[iColumn].column_name_id);
712
713 if (iColumn == COLUMN_NAME)
714 return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
715
716 pFileDetails = _ILGetRecycleStruct(pidl);
717 switch (iColumn)
718 {
719 case COLUMN_DATEDEL:
720 FormatDateTime(buffer, MAX_PATH, &pFileDetails->DeletionTime);
721 break;
722 case COLUMN_DELFROM:
723 pszBackslash = wcsrchr(pFileDetails->szName, L'\\');
724 Length = (pszBackslash - pFileDetails->szName);
725 memcpy((LPVOID)buffer, pFileDetails->szName, Length * sizeof(WCHAR));
726 buffer[Length] = L'\0';
727 break;
728 case COLUMN_SIZE:
729 StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH);
730 break;
731 case COLUMN_MTIME:
732 FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification);
733 break;
734 case COLUMN_TYPE:
735 // FIXME: We should in fact use a UNICODE version of _ILGetFileType
736 szTypeName[0] = L'\0';
737 wcscpy(buffer, PathFindExtensionW(pFileDetails->szName));
738 if (!( HCR_MapTypeToValueW(buffer, buffer, _countof(buffer), TRUE) &&
739 HCR_MapTypeToValueW(buffer, szTypeName, _countof(szTypeName), FALSE )))
740 {
741 /* load localized file string */
742 szTypeName[0] = '\0';
743 if(LoadStringW(shell32_hInstance, IDS_ANY_FILE, szTypeName, _countof(szTypeName)))
744 {
745 szTypeName[63] = '\0';
746 StringCchPrintfW(buffer, _countof(buffer), szTypeName, PathFindExtensionW(pFileDetails->szName));
747 }
748 }
749 return SHSetStrRet(&pDetails->str, szTypeName);
750 default:
751 return E_FAIL;
752 }
753
754 return SHSetStrRet(&pDetails->str, buffer);
755 }
756
757 HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
758 {
759 TRACE("(%p, %d, %p)\n", this, iColumn, pscid);
760 if (iColumn >= COLUMNS_COUNT)
761 return E_INVALIDARG;
762 pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
763 pscid->pid = RecycleBinColumns[iColumn].pid;
764 return S_OK;
765 }
766
767 BOOL CRecycleBin::RecycleBinIsEmpty()
768 {
769 CComPtr<IEnumIDList> spEnumFiles;
770 HRESULT hr = EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &spEnumFiles);
771 if (FAILED(hr))
772 return TRUE;
773 CComHeapPtr<ITEMIDLIST> spPidl;
774 ULONG itemcount;
775 return spEnumFiles->Next(1, &spPidl, &itemcount) != S_OK;
776 }
777
778 /*************************************************************************
779 * RecycleBin IContextMenu interface
780 */
781
782 HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
783 {
784 WCHAR szBuffer[100];
785 MENUITEMINFOW mii;
786 int id = 1;
787
788 TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
789
790 if (!hMenu)
791 return E_INVALIDARG;
792
793 memset(&mii, 0, sizeof(mii));
794 mii.cbSize = sizeof(mii);
795 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
796 mii.fState = RecycleBinIsEmpty() ? MFS_DISABLED : MFS_ENABLED;
797 szBuffer[0] = L'\0';
798 LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, sizeof(szBuffer) / sizeof(WCHAR));
799 mii.dwTypeData = szBuffer;
800 mii.cch = wcslen(mii.dwTypeData);
801 mii.wID = idCmdFirst + id++;
802 mii.fType = MFT_STRING;
803 iIdEmpty = 1;
804
805 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
806 return E_FAIL;
807
808 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
809 }
810
811 HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
812 {
813 HRESULT hr;
814 LPSHELLBROWSER lpSB;
815 IShellView * lpSV = NULL;
816
817 TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb);
818
819 if (LOWORD(lpcmi->lpVerb) == iIdEmpty)
820 {
821 // FIXME
822 // path & flags
823 hr = SHEmptyRecycleBinW(lpcmi->hwnd, L"C:\\", 0);
824 TRACE("result %x\n", hr);
825 if (hr != S_OK)
826 return hr;
827
828 lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
829 if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV)))
830 lpSV->Refresh();
831 }
832 return S_OK;
833 }
834
835 HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
836 {
837 FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
838
839 return E_NOTIMPL;
840 }
841
842 /*************************************************************************
843 * RecycleBin IShellPropSheetExt interface
844 */
845
846 HRESULT WINAPI CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
847 {
848 FIXME("%p %p %lu\n", this, pfnAddPage, lParam);
849
850 return E_NOTIMPL;
851 }
852
853 HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam)
854 {
855 FIXME("%p %lu %p %lu\n", this, uPageID, pfnReplaceWith, lParam);
856
857 return E_NOTIMPL;
858 }
859
860 /*************************************************************************
861 * RecycleBin IShellExtInit interface
862 */
863
864 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
865 {
866 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID );
867 return S_OK;
868 }
869
870 BOOL
871 TRASH_CanTrashFile(LPCWSTR wszPath)
872 {
873 LONG ret;
874 DWORD dwNukeOnDelete, dwType, VolSerialNumber, MaxComponentLength;
875 DWORD FileSystemFlags, dwSize, dwDisposition;
876 HKEY hKey;
877 WCHAR szBuffer[10];
878 WCHAR szKey[150] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume\\";
879
880 if (wszPath[1] != L':')
881 {
882 /* path is UNC */
883 return FALSE;
884 }
885
886 // Only keep the base path.
887 WCHAR wszRootPathName[MAX_PATH];
888 strcpyW(wszRootPathName, wszPath);
889 PathRemoveFileSpecW(wszRootPathName);
890 PathAddBackslashW(wszRootPathName);
891
892 if (GetDriveTypeW(wszRootPathName) != DRIVE_FIXED)
893 {
894 /* no bitbucket on removable media */
895 return FALSE;
896 }
897
898 if (!GetVolumeInformationW(wszRootPathName, NULL, 0, &VolSerialNumber, &MaxComponentLength, &FileSystemFlags, NULL, 0))
899 {
900 ERR("GetVolumeInformationW failed with %u\n", GetLastError());
901 return FALSE;
902 }
903
904 swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber), HIWORD(VolSerialNumber));
905 wcscat(szKey, szBuffer);
906
907 if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS)
908 {
909 ERR("RegCreateKeyExW failed\n");
910 return FALSE;
911 }
912
913 if (dwDisposition & REG_CREATED_NEW_KEY)
914 {
915 /* per default move to bitbucket */
916 dwNukeOnDelete = 0;
917 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
918 /* per default unlimited size */
919 dwSize = -1;
920 RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize, sizeof(DWORD));
921 RegCloseKey(hKey);
922 return TRUE;
923 }
924 else
925 {
926 dwSize = sizeof(dwNukeOnDelete);
927 ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType, (LPBYTE)&dwNukeOnDelete, &dwSize);
928 if (ret != ERROR_SUCCESS)
929 {
930 if (ret == ERROR_FILE_NOT_FOUND)
931 {
932 /* restore key and enable bitbucket */
933 dwNukeOnDelete = 0;
934 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
935 }
936 RegCloseKey(hKey);
937 return TRUE;
938 }
939 else if (dwNukeOnDelete)
940 {
941 /* do not delete to bitbucket */
942 RegCloseKey(hKey);
943 return FALSE;
944 }
945 /* FIXME
946 * check if bitbucket is full
947 */
948 RegCloseKey(hKey);
949 return TRUE;
950 }
951 }
952
953 BOOL
954 TRASH_TrashFile(LPCWSTR wszPath)
955 {
956 TRACE("(%s)\n", debugstr_w(wszPath));
957 return DeleteFileToRecycleBin(wszPath);
958 }
959
960 /*************************************************************************
961 * SHUpdateCRecycleBinIcon [SHELL32.@]
962 *
963 * Undocumented
964 */
965 EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void)
966 {
967 FIXME("stub\n");
968
969 return S_OK;
970 }
971
972 /*************************************************************************
973 * SHEmptyRecycleBinA (SHELL32.@)
974 */
975 HRESULT WINAPI SHEmptyRecycleBinA(HWND hwnd, LPCSTR pszRootPath, DWORD dwFlags)
976 {
977 LPWSTR szRootPathW = NULL;
978 int len;
979 HRESULT hr;
980
981 TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_a(pszRootPath), dwFlags);
982
983 if (pszRootPath)
984 {
985 len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
986 if (len == 0)
987 return HRESULT_FROM_WIN32(GetLastError());
988 szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
989 if (!szRootPathW)
990 return E_OUTOFMEMORY;
991 if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
992 {
993 HeapFree(GetProcessHeap(), 0, szRootPathW);
994 return HRESULT_FROM_WIN32(GetLastError());
995 }
996 }
997
998 hr = SHEmptyRecycleBinW(hwnd, szRootPathW, dwFlags);
999 HeapFree(GetProcessHeap(), 0, szRootPathW);
1000
1001 return hr;
1002 }
1003
1004 HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags)
1005 {
1006 WCHAR szPath[MAX_PATH] = {0}, szBuffer[MAX_PATH];
1007 DWORD dwSize, dwType, count;
1008 LONG ret;
1009 IShellFolder *pDesktop, *pRecycleBin;
1010 PIDLIST_ABSOLUTE pidlRecycleBin;
1011 PITEMID_CHILD pidl;
1012 HRESULT hr = S_OK;
1013 LPENUMIDLIST penumFiles;
1014 STRRET StrRet;
1015
1016 TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_w(pszRootPath), dwFlags);
1017
1018 if (!(dwFlags & SHERB_NOCONFIRMATION))
1019 {
1020 hr = SHGetDesktopFolder(&pDesktop);
1021 if (FAILED(hr))
1022 return hr;
1023 hr = SHGetFolderLocation(NULL, CSIDL_BITBUCKET, NULL, 0, &pidlRecycleBin);
1024 if (FAILED(hr))
1025 {
1026 pDesktop->Release();
1027 return hr;
1028 }
1029 hr = pDesktop->BindToObject(pidlRecycleBin, NULL, IID_PPV_ARG(IShellFolder, &pRecycleBin));
1030 CoTaskMemFree(pidlRecycleBin);
1031 pDesktop->Release();
1032 if (FAILED(hr))
1033 return hr;
1034 hr = pRecycleBin->EnumObjects(hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penumFiles);
1035 if (FAILED(hr))
1036 {
1037 pRecycleBin->Release();
1038 return hr;
1039 }
1040
1041 count = 0;
1042 if (hr != S_FALSE)
1043 {
1044 while (penumFiles->Next(1, &pidl, NULL) == S_OK)
1045 {
1046 count++;
1047 pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL, &StrRet);
1048 StrRetToBuf(&StrRet, pidl, szBuffer, _countof(szBuffer));
1049 CoTaskMemFree(pidl);
1050 }
1051 penumFiles->Release();
1052 }
1053 pRecycleBin->Release();
1054
1055 switch (count)
1056 {
1057 case 0:
1058 /* no files, don't need confirmation */
1059 break;
1060
1061 case 1:
1062 /* we have only one item inside the bin, so show a message box with its name */
1063 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEITEM_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1064 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1065 {
1066 return S_OK;
1067 }
1068 break;
1069
1070 default:
1071 /* we have more than one item, so show a message box with the count of the items */
1072 StringCbPrintfW(szBuffer, sizeof(szBuffer), L"%u", count);
1073 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEMULTIPLE_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1074 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1075 {
1076 return S_OK;
1077 }
1078 break;
1079 }
1080 }
1081
1082 if (dwFlags & SHERB_NOPROGRESSUI)
1083 {
1084 ret = EmptyRecycleBinW(pszRootPath);
1085 }
1086 else
1087 {
1088 /* FIXME
1089 * show a progress dialog
1090 */
1091 ret = EmptyRecycleBinW(pszRootPath);
1092 }
1093
1094 if (!ret)
1095 return HRESULT_FROM_WIN32(GetLastError());
1096
1097 if (!(dwFlags & SHERB_NOSOUND))
1098 {
1099 dwSize = sizeof(szPath);
1100 ret = RegGetValueW(HKEY_CURRENT_USER,
1101 L"AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current",
1102 NULL,
1103 RRF_RT_REG_SZ,
1104 &dwType,
1105 (PVOID)szPath,
1106 &dwSize);
1107 if (ret != ERROR_SUCCESS)
1108 return S_OK;
1109
1110 if (dwType != REG_EXPAND_SZ) /* type dismatch */
1111 return S_OK;
1112
1113 szPath[(sizeof(szPath)/sizeof(WCHAR))-1] = L'\0';
1114 PlaySoundW(szPath, NULL, SND_FILENAME);
1115 }
1116 return S_OK;
1117 }
1118
1119 HRESULT WINAPI SHQueryRecycleBinA(LPCSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1120 {
1121 LPWSTR szRootPathW = NULL;
1122 int len;
1123 HRESULT hr;
1124
1125 TRACE("%s, %p\n", debugstr_a(pszRootPath), pSHQueryRBInfo);
1126
1127 if (pszRootPath)
1128 {
1129 len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
1130 if (len == 0)
1131 return HRESULT_FROM_WIN32(GetLastError());
1132 szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1133 if (!szRootPathW)
1134 return E_OUTOFMEMORY;
1135 if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
1136 {
1137 HeapFree(GetProcessHeap(), 0, szRootPathW);
1138 return HRESULT_FROM_WIN32(GetLastError());
1139 }
1140 }
1141
1142 hr = SHQueryRecycleBinW(szRootPathW, pSHQueryRBInfo);
1143 HeapFree(GetProcessHeap(), 0, szRootPathW);
1144
1145 return hr;
1146 }
1147
1148 HRESULT WINAPI SHQueryRecycleBinW(LPCWSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1149 {
1150 FIXME("%s, %p - stub\n", debugstr_w(pszRootPath), pSHQueryRBInfo);
1151
1152 if (!(pszRootPath) || (pszRootPath[0] == 0) ||
1153 !(pSHQueryRBInfo) || (pSHQueryRBInfo->cbSize < sizeof(SHQUERYRBINFO)))
1154 {
1155 return E_INVALIDARG;
1156 }
1157
1158 pSHQueryRBInfo->i64Size = 0;
1159 pSHQueryRBInfo->i64NumItems = 0;
1160
1161 return S_OK;
1162 }