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