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 }
1165