51be9662cd44074d10ccff6d0d8c6cc296c4eef7
[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 #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_COLUMN_NAME, &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_COLUMN_SIZE, &FMTID_Storage, PID_STG_SIZE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 20},
47 {IDS_SHV_COLUMN_TYPE, &FMTID_Storage, PID_STG_STORAGETYPE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
48 {IDS_SHV_COLUMN_MODIFIED, &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_UNEXPECTEDLY(hr))
71 return hr;
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 CRecycleBin::CRecycleBin()
414 {
415 pidl = NULL;
416 }
417
418 CRecycleBin::~CRecycleBin()
419 {
420 SHFree(pidl);
421 }
422
423 /*************************************************************************
424 * RecycleBin IPersistFolder2 interface
425 */
426
427 HRESULT WINAPI CRecycleBin::GetClassID(CLSID *pClassID)
428 {
429 TRACE("(%p, %p)\n", this, pClassID);
430 if (pClassID == NULL)
431 return E_INVALIDARG;
432 memcpy(pClassID, &CLSID_RecycleBin, sizeof(CLSID));
433 return S_OK;
434 }
435
436 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidl)
437 {
438 TRACE("(%p, %p)\n", this, pidl);
439
440 SHFree((LPVOID)this->pidl);
441 this->pidl = ILClone(pidl);
442 if (this->pidl == NULL)
443 return E_OUTOFMEMORY;
444 return S_OK;
445 }
446
447 HRESULT WINAPI CRecycleBin::GetCurFolder(LPITEMIDLIST *ppidl)
448 {
449 TRACE("\n");
450 *ppidl = ILClone(pidl);
451 return S_OK;
452 }
453
454 /*************************************************************************
455 * RecycleBin IShellFolder2 interface
456 */
457
458 HRESULT WINAPI CRecycleBin::ParseDisplayName(HWND hwnd, LPBC pbc,
459 LPOLESTR pszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl,
460 ULONG *pdwAttributes)
461 {
462 FIXME("stub\n");
463 return E_NOTIMPL;
464 }
465
466
467 PDELETED_FILE_DETAILS_W
468 UnpackDetailsFromPidl(LPCITEMIDLIST pidl)
469 {
470 return (PDELETED_FILE_DETAILS_W)&pidl->mkid.abID;
471 }
472
473 HRESULT WINAPI CRecycleBin::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
474 {
475 return ShellObjectCreatorInit<CRecycleBinEnum>(dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
476 }
477
478 HRESULT WINAPI CRecycleBin::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
479 {
480 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
481 return E_NOTIMPL;
482 }
483
484 HRESULT WINAPI CRecycleBin::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
485 {
486 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
487 return E_NOTIMPL;
488 }
489
490 HRESULT WINAPI CRecycleBin::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
491 {
492 PIDLRecycleStruct* pData1 = _ILGetRecycleStruct(pidl1);
493 PIDLRecycleStruct* pData2 = _ILGetRecycleStruct(pidl2);
494 LPWSTR pName1, pName2;
495
496 if(!pData1 || !pData2 || LOWORD(lParam) >= COLUMNS_COUNT)
497 return E_INVALIDARG;
498
499 SHORT result;
500 LONGLONG diff;
501 switch (LOWORD(lParam))
502 {
503 case 0: /* Name */
504 pName1 = PathFindFileNameW(pData1->szName);
505 pName2 = PathFindFileNameW(pData2->szName);
506 result = wcsicmp(pName1, pName2);
507 break;
508 case 1: /* Orig. Location */
509 result = wcsicmp(pData1->szName, pData2->szName);
510 break;
511 case 2: /* Date Deleted */
512 result = CompareFileTime(&pData1->DeletionTime, &pData2->DeletionTime);
513 break;
514 case 3: /* Size */
515 diff = pData1->FileSize.QuadPart - pData2->FileSize.QuadPart;
516 return MAKE_COMPARE_HRESULT(diff);
517 case 4: /* Type */
518 pName1 = PathFindExtensionW(pData1->szName);
519 pName2 = PathFindExtensionW(pData2->szName);
520 result = wcsicmp(pName1, pName2);
521 break;
522 case 5: /* Modified */
523 result = CompareFileTime(&pData1->LastModification, &pData2->LastModification);
524 break;
525 }
526 return MAKE_COMPARE_HRESULT(result);
527 }
528
529 HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv)
530 {
531 CComPtr<IShellView> pShellView;
532 HRESULT hr = E_NOINTERFACE;
533
534 TRACE("(%p, %p, %s, %p)\n", this, hwndOwner, debugstr_guid(&riid), ppv);
535
536 if (!ppv)
537 return hr;
538
539 *ppv = NULL;
540
541 if (IsEqualIID (riid, IID_IDropTarget))
542 {
543 hr = CRecyclerDropTarget_CreateInstance(riid, ppv);
544 }
545 else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid, IID_IContextMenu2))
546 {
547 hr = this->QueryInterface(riid, ppv);
548 }
549 else if (IsEqualIID (riid, IID_IShellView))
550 {
551 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
552 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppv);
553 }
554 else
555 return hr;
556
557 TRACE ("-- (%p)->(interface=%p)\n", this, ppv);
558 return hr;
559
560 }
561
562 HRESULT WINAPI CRecycleBin::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
563 SFGAOF *rgfInOut)
564 {
565 TRACE("(%p, %d, {%p, ...}, {%x})\n", this, cidl, apidl ? apidl[0] : NULL, (unsigned int)*rgfInOut);
566 *rgfInOut &= SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANLINK;
567 return S_OK;
568 }
569
570 HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
571 REFIID riid, UINT *prgfInOut, void **ppv)
572 {
573 LPVOID pObj = NULL;
574 HRESULT hr = E_INVALIDARG;
575
576 TRACE ("(%p)->(%p,%u,apidl=%p, %p %p)\n", this,
577 hwndOwner, cidl, apidl, prgfInOut, ppv);
578
579 if (!ppv)
580 return hr;
581
582 *ppv = NULL;
583
584 if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2)) && (cidl >= 1))
585 {
586 hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(apidl[0], riid, &pObj);
587 }
588 else if((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1))
589 {
590 hr = CRecyclerExtractIcon_CreateInstance(apidl[0], riid, &pObj);
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 pFileDetails = _ILGetRecycleStruct(pidl);
611 if (!pFileDetails)
612 {
613 pName->cStr[0] = 0;
614 pName->uType = STRRET_CSTR;
615 return E_INVALIDARG;
616 }
617
618 pFileName = wcsrchr(pFileDetails->szName, L'\\');
619 if (!pFileName)
620 {
621 pName->cStr[0] = 0;
622 pName->uType = STRRET_CSTR;
623 return E_UNEXPECTED;
624 }
625
626 pName->pOleStr = StrDupW(pFileName + 1);
627 if (pName->pOleStr == NULL)
628 return E_OUTOFMEMORY;
629
630 pName->uType = STRRET_WSTR;
631 return S_OK;
632 }
633
634 HRESULT WINAPI CRecycleBin::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pszName,
635 SHGDNF uFlags, PITEMID_CHILD *ppidlOut)
636 {
637 TRACE("\n");
638 return E_FAIL; /* not supported */
639 }
640
641 HRESULT WINAPI CRecycleBin::GetDefaultSearchGUID(GUID *pguid)
642 {
643 FIXME("stub\n");
644 return E_NOTIMPL;
645 }
646
647 HRESULT WINAPI CRecycleBin::EnumSearches(IEnumExtraSearch **ppEnum)
648 {
649 FIXME("stub\n");
650 *ppEnum = NULL;
651 return E_NOTIMPL;
652 }
653
654 HRESULT WINAPI CRecycleBin::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
655 {
656 TRACE("(%p, %x, %p, %p)\n", this, (unsigned int)dwReserved, pSort, pDisplay);
657 if (pSort)
658 *pSort = 0;
659 if (pDisplay)
660 *pDisplay = 0;
661 return S_OK;
662 }
663
664 HRESULT WINAPI CRecycleBin::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
665 {
666 TRACE("(%p, %d, %p)\n", this, iColumn, pcsFlags);
667 if (iColumn >= COLUMNS_COUNT)
668 return E_INVALIDARG;
669 *pcsFlags = RecycleBinColumns[iColumn].pcsFlags;
670 return S_OK;
671 }
672
673 HRESULT WINAPI CRecycleBin::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
674 {
675 FIXME("stub\n");
676 return E_NOTIMPL;
677 }
678
679 static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME * ft)
680 {
681 FILETIME lft;
682 SYSTEMTIME time;
683 int ret;
684
685 FileTimeToLocalFileTime(ft, &lft);
686 FileTimeToSystemTime(&lft, &time);
687
688 ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size);
689 if (ret > 0 && ret < size)
690 {
691 /* Append space + time without seconds */
692 buffer[ret-1] = ' ';
693 GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret);
694 }
695
696 return (ret != 0 ? E_FAIL : S_OK);
697 }
698
699 HRESULT WINAPI CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, LPSHELLDETAILS pDetails)
700 {
701 PIDLRecycleStruct * pFileDetails;
702 WCHAR buffer[MAX_PATH];
703 WCHAR szTypeName[100];
704 LPWSTR pszBackslash;
705 UINT Length;
706
707 TRACE("(%p, %p, %d, %p)\n", this, pidl, iColumn, pDetails);
708 if (iColumn >= COLUMNS_COUNT)
709 return E_FAIL;
710 pDetails->fmt = RecycleBinColumns[iColumn].fmt;
711 pDetails->cxChar = RecycleBinColumns[iColumn].cxChars;
712 if (pidl == NULL)
713 return SHSetStrRet(&pDetails->str, RecycleBinColumns[iColumn].column_name_id);
714
715 if (iColumn == COLUMN_NAME)
716 return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
717
718 pFileDetails = _ILGetRecycleStruct(pidl);
719 switch (iColumn)
720 {
721 case COLUMN_DATEDEL:
722 FormatDateTime(buffer, MAX_PATH, &pFileDetails->DeletionTime);
723 break;
724 case COLUMN_DELFROM:
725 pszBackslash = wcsrchr(pFileDetails->szName, L'\\');
726 Length = (pszBackslash - pFileDetails->szName);
727 memcpy((LPVOID)buffer, pFileDetails->szName, Length * sizeof(WCHAR));
728 buffer[Length] = L'\0';
729 break;
730 case COLUMN_SIZE:
731 StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH);
732 break;
733 case COLUMN_MTIME:
734 FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification);
735 break;
736 case COLUMN_TYPE:
737 // FIXME: We should in fact use a UNICODE version of _ILGetFileType
738 szTypeName[0] = L'\0';
739 wcscpy(buffer, PathFindExtensionW(pFileDetails->szName));
740 if (!( HCR_MapTypeToValueW(buffer, buffer, _countof(buffer), TRUE) &&
741 HCR_MapTypeToValueW(buffer, szTypeName, _countof(szTypeName), FALSE )))
742 {
743 /* load localized file string */
744 szTypeName[0] = '\0';
745 if(LoadStringW(shell32_hInstance, IDS_ANY_FILE, szTypeName, _countof(szTypeName)))
746 {
747 szTypeName[63] = '\0';
748 StringCchPrintfW(buffer, _countof(buffer), szTypeName, PathFindExtensionW(pFileDetails->szName));
749 }
750 }
751 return SHSetStrRet(&pDetails->str, szTypeName);
752 default:
753 return E_FAIL;
754 }
755
756 return SHSetStrRet(&pDetails->str, buffer);
757 }
758
759 HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
760 {
761 TRACE("(%p, %d, %p)\n", this, iColumn, pscid);
762 if (iColumn >= COLUMNS_COUNT)
763 return E_INVALIDARG;
764 pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
765 pscid->pid = RecycleBinColumns[iColumn].pid;
766 return S_OK;
767 }
768
769 BOOL CRecycleBin::RecycleBinIsEmpty()
770 {
771 CComPtr<IEnumIDList> spEnumFiles;
772 HRESULT hr = EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &spEnumFiles);
773 if (FAILED(hr))
774 return TRUE;
775 CComHeapPtr<ITEMIDLIST> spPidl;
776 ULONG itemcount;
777 return spEnumFiles->Next(1, &spPidl, &itemcount) != S_OK;
778 }
779
780 /*************************************************************************
781 * RecycleBin IContextMenu interface
782 */
783
784 HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
785 {
786 WCHAR szBuffer[100];
787 MENUITEMINFOW mii;
788 int id = 1;
789
790 TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
791
792 if (!hMenu)
793 return E_INVALIDARG;
794
795 memset(&mii, 0, sizeof(mii));
796 mii.cbSize = sizeof(mii);
797 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
798 mii.fState = RecycleBinIsEmpty() ? MFS_DISABLED : MFS_ENABLED;
799 szBuffer[0] = L'\0';
800 LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, sizeof(szBuffer) / sizeof(WCHAR));
801 mii.dwTypeData = szBuffer;
802 mii.cch = wcslen(mii.dwTypeData);
803 mii.wID = idCmdFirst + id++;
804 mii.fType = MFT_STRING;
805 iIdEmpty = 1;
806
807 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
808 return E_FAIL;
809
810 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
811 }
812
813 HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
814 {
815 HRESULT hr;
816 LPSHELLBROWSER lpSB;
817 IShellView * lpSV = NULL;
818
819 TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb);
820
821 if (LOWORD(lpcmi->lpVerb) == iIdEmpty)
822 {
823 // FIXME
824 // path & flags
825 hr = SHEmptyRecycleBinW(lpcmi->hwnd, L"C:\\", 0);
826 TRACE("result %x\n", hr);
827 if (hr != S_OK)
828 return hr;
829
830 lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
831 if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV)))
832 lpSV->Refresh();
833 }
834 return S_OK;
835 }
836
837 HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
838 {
839 FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
840
841 return E_NOTIMPL;
842 }
843
844 /*************************************************************************
845 * RecycleBin IShellPropSheetExt interface
846 */
847
848 HRESULT WINAPI CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
849 {
850 FIXME("%p %p %lu\n", this, pfnAddPage, lParam);
851
852 return E_NOTIMPL;
853 }
854
855 HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam)
856 {
857 FIXME("%p %lu %p %lu\n", this, uPageID, pfnReplaceWith, lParam);
858
859 return E_NOTIMPL;
860 }
861
862 /*************************************************************************
863 * RecycleBin IShellExtInit interface
864 */
865
866 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
867 {
868 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID );
869 return S_OK;
870 }
871
872 BOOL
873 TRASH_CanTrashFile(LPCWSTR wszPath)
874 {
875 LONG ret;
876 DWORD dwNukeOnDelete, dwType, VolSerialNumber, MaxComponentLength;
877 DWORD FileSystemFlags, dwSize, dwDisposition;
878 HKEY hKey;
879 WCHAR szBuffer[10];
880 WCHAR szKey[150] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume\\";
881
882 if (wszPath[1] != L':')
883 {
884 /* path is UNC */
885 return FALSE;
886 }
887
888 // Only keep the base path.
889 WCHAR wszRootPathName[MAX_PATH];
890 strcpyW(wszRootPathName, wszPath);
891 PathRemoveFileSpecW(wszRootPathName);
892 PathAddBackslashW(wszRootPathName);
893
894 if (GetDriveTypeW(wszRootPathName) != DRIVE_FIXED)
895 {
896 /* no bitbucket on removable media */
897 return FALSE;
898 }
899
900 if (!GetVolumeInformationW(wszRootPathName, NULL, 0, &VolSerialNumber, &MaxComponentLength, &FileSystemFlags, NULL, 0))
901 {
902 ERR("GetVolumeInformationW failed with %u\n", GetLastError());
903 return FALSE;
904 }
905
906 swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber), HIWORD(VolSerialNumber));
907 wcscat(szKey, szBuffer);
908
909 if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS)
910 {
911 ERR("RegCreateKeyExW failed\n");
912 return FALSE;
913 }
914
915 if (dwDisposition & REG_CREATED_NEW_KEY)
916 {
917 /* per default move to bitbucket */
918 dwNukeOnDelete = 0;
919 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
920 /* per default unlimited size */
921 dwSize = -1;
922 RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize, sizeof(DWORD));
923 RegCloseKey(hKey);
924 return TRUE;
925 }
926 else
927 {
928 dwSize = sizeof(dwNukeOnDelete);
929 ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType, (LPBYTE)&dwNukeOnDelete, &dwSize);
930 if (ret != ERROR_SUCCESS)
931 {
932 if (ret == ERROR_FILE_NOT_FOUND)
933 {
934 /* restore key and enable bitbucket */
935 dwNukeOnDelete = 0;
936 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
937 }
938 RegCloseKey(hKey);
939 return TRUE;
940 }
941 else if (dwNukeOnDelete)
942 {
943 /* do not delete to bitbucket */
944 RegCloseKey(hKey);
945 return FALSE;
946 }
947 /* FIXME
948 * check if bitbucket is full
949 */
950 RegCloseKey(hKey);
951 return TRUE;
952 }
953 }
954
955 BOOL
956 TRASH_TrashFile(LPCWSTR wszPath)
957 {
958 TRACE("(%s)\n", debugstr_w(wszPath));
959 return DeleteFileToRecycleBin(wszPath);
960 }
961
962 /*************************************************************************
963 * SHUpdateCRecycleBinIcon [SHELL32.@]
964 *
965 * Undocumented
966 */
967 EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void)
968 {
969 FIXME("stub\n");
970
971 return S_OK;
972 }
973
974 /*************************************************************************
975 * SHEmptyRecycleBinA (SHELL32.@)
976 */
977 HRESULT WINAPI SHEmptyRecycleBinA(HWND hwnd, LPCSTR pszRootPath, DWORD dwFlags)
978 {
979 LPWSTR szRootPathW = NULL;
980 int len;
981 HRESULT hr;
982
983 TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_a(pszRootPath), dwFlags);
984
985 if (pszRootPath)
986 {
987 len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
988 if (len == 0)
989 return HRESULT_FROM_WIN32(GetLastError());
990 szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
991 if (!szRootPathW)
992 return E_OUTOFMEMORY;
993 if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
994 {
995 HeapFree(GetProcessHeap(), 0, szRootPathW);
996 return HRESULT_FROM_WIN32(GetLastError());
997 }
998 }
999
1000 hr = SHEmptyRecycleBinW(hwnd, szRootPathW, dwFlags);
1001 HeapFree(GetProcessHeap(), 0, szRootPathW);
1002
1003 return hr;
1004 }
1005
1006 HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags)
1007 {
1008 WCHAR szPath[MAX_PATH] = {0}, szBuffer[MAX_PATH];
1009 DWORD dwSize, dwType, count;
1010 LONG ret;
1011 IShellFolder *pDesktop, *pRecycleBin;
1012 PIDLIST_ABSOLUTE pidlRecycleBin;
1013 PITEMID_CHILD pidl;
1014 HRESULT hr = S_OK;
1015 LPENUMIDLIST penumFiles;
1016 STRRET StrRet;
1017
1018 TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_w(pszRootPath), dwFlags);
1019
1020 if (!(dwFlags & SHERB_NOCONFIRMATION))
1021 {
1022 hr = SHGetDesktopFolder(&pDesktop);
1023 if (FAILED(hr))
1024 return hr;
1025 hr = SHGetFolderLocation(NULL, CSIDL_BITBUCKET, NULL, 0, &pidlRecycleBin);
1026 if (FAILED(hr))
1027 {
1028 pDesktop->Release();
1029 return hr;
1030 }
1031 hr = pDesktop->BindToObject(pidlRecycleBin, NULL, IID_PPV_ARG(IShellFolder, &pRecycleBin));
1032 CoTaskMemFree(pidlRecycleBin);
1033 pDesktop->Release();
1034 if (FAILED(hr))
1035 return hr;
1036 hr = pRecycleBin->EnumObjects(hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penumFiles);
1037 if (FAILED(hr))
1038 {
1039 pRecycleBin->Release();
1040 return hr;
1041 }
1042
1043 count = 0;
1044 if (hr != S_FALSE)
1045 {
1046 while (penumFiles->Next(1, &pidl, NULL) == S_OK)
1047 {
1048 count++;
1049 pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL, &StrRet);
1050 StrRetToBuf(&StrRet, pidl, szBuffer, _countof(szBuffer));
1051 CoTaskMemFree(pidl);
1052 }
1053 penumFiles->Release();
1054 }
1055 pRecycleBin->Release();
1056
1057 switch (count)
1058 {
1059 case 0:
1060 /* no files, don't need confirmation */
1061 break;
1062
1063 case 1:
1064 /* we have only one item inside the bin, so show a message box with its name */
1065 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEITEM_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1066 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1067 {
1068 return S_OK;
1069 }
1070 break;
1071
1072 default:
1073 /* we have more than one item, so show a message box with the count of the items */
1074 StringCbPrintfW(szBuffer, sizeof(szBuffer), L"%u", count);
1075 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEMULTIPLE_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET),
1076 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO)
1077 {
1078 return S_OK;
1079 }
1080 break;
1081 }
1082 }
1083
1084 if (dwFlags & SHERB_NOPROGRESSUI)
1085 {
1086 ret = EmptyRecycleBinW(pszRootPath);
1087 }
1088 else
1089 {
1090 /* FIXME
1091 * show a progress dialog
1092 */
1093 ret = EmptyRecycleBinW(pszRootPath);
1094 }
1095
1096 if (!ret)
1097 return HRESULT_FROM_WIN32(GetLastError());
1098
1099 if (!(dwFlags & SHERB_NOSOUND))
1100 {
1101 dwSize = sizeof(szPath);
1102 ret = RegGetValueW(HKEY_CURRENT_USER,
1103 L"AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current",
1104 NULL,
1105 RRF_RT_REG_SZ,
1106 &dwType,
1107 (PVOID)szPath,
1108 &dwSize);
1109 if (ret != ERROR_SUCCESS)
1110 return S_OK;
1111
1112 if (dwType != REG_EXPAND_SZ) /* type dismatch */
1113 return S_OK;
1114
1115 szPath[(sizeof(szPath)/sizeof(WCHAR))-1] = L'\0';
1116 PlaySoundW(szPath, NULL, SND_FILENAME);
1117 }
1118 return S_OK;
1119 }
1120
1121 HRESULT WINAPI SHQueryRecycleBinA(LPCSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1122 {
1123 LPWSTR szRootPathW = NULL;
1124 int len;
1125 HRESULT hr;
1126
1127 TRACE("%s, %p\n", debugstr_a(pszRootPath), pSHQueryRBInfo);
1128
1129 if (pszRootPath)
1130 {
1131 len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0);
1132 if (len == 0)
1133 return HRESULT_FROM_WIN32(GetLastError());
1134 szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1135 if (!szRootPathW)
1136 return E_OUTOFMEMORY;
1137 if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0)
1138 {
1139 HeapFree(GetProcessHeap(), 0, szRootPathW);
1140 return HRESULT_FROM_WIN32(GetLastError());
1141 }
1142 }
1143
1144 hr = SHQueryRecycleBinW(szRootPathW, pSHQueryRBInfo);
1145 HeapFree(GetProcessHeap(), 0, szRootPathW);
1146
1147 return hr;
1148 }
1149
1150 HRESULT WINAPI SHQueryRecycleBinW(LPCWSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
1151 {
1152 FIXME("%s, %p - stub\n", debugstr_w(pszRootPath), pSHQueryRBInfo);
1153
1154 if (!(pszRootPath) || (pszRootPath[0] == 0) ||
1155 !(pSHQueryRBInfo) || (pSHQueryRBInfo->cbSize < sizeof(SHQUERYRBINFO)))
1156 {
1157 return E_INVALIDARG;
1158 }
1159
1160 pSHQueryRBInfo->i64Size = 0;
1161 pSHQueryRBInfo->i64NumItems = 0;
1162
1163 return S_OK;
1164 }