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