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