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