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