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(LPCITEMIDLIST 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(LPITEMIDLIST *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 }
1169