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 HDELFILE hDeletedFile); 66 67 static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl); 68 69 typedef struct _SEARCH_CONTEXT 70 { 71 PIDLRecycleStruct *pFileDetails; 72 HDELFILE 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 HDELFILE hDeletedFile); 126 BOOL WINAPI CBEnumRecycleBin(IN HDELFILE 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 STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override; 146 STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpcmi) override; 147 STDMETHOD(GetCommandString)(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) override; 148 149 // IContextMenu2 150 STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam) override; 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 HDELFILE 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 HDELFILE hDeletedFile) 277 { 278 return static_cast<CRecycleBinEnum *>(Context)->CBEnumRecycleBin(hDeletedFile); 279 } 280 281 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN HDELFILE 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] = UNICODE_NULL; 772 if (buffer[0] && buffer[1] == L':' && !buffer[2]) 773 { 774 buffer[2] = L'\\'; 775 buffer[3] = UNICODE_NULL; 776 } 777 break; 778 case COLUMN_SIZE: 779 StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH); 780 break; 781 case COLUMN_MTIME: 782 FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification); 783 break; 784 case COLUMN_TYPE: 785 { 786 SEARCH_CONTEXT Context; 787 Context.pFileDetails = pFileDetails; 788 Context.bFound = FALSE; 789 EnumerateRecycleBinW(NULL, CBSearchRecycleBin, (PVOID)&Context); 790 791 if (Context.bFound) 792 { 793 GetDeletedFileTypeNameW(Context.hDeletedFile, buffer, _countof(buffer), NULL); 794 795 CloseRecycleBinHandle(Context.hDeletedFile); 796 } 797 /* load localized file string */ 798 else if (LoadStringW(shell32_hInstance, IDS_ANY_FILE, szTypeName, _countof(szTypeName))) 799 { 800 StringCchPrintfW(buffer, _countof(buffer), szTypeName, PathFindExtensionW(pFileDetails->szName)); 801 } 802 803 return SHSetStrRet(&pDetails->str, buffer); 804 } 805 default: 806 return E_FAIL; 807 } 808 809 return SHSetStrRet(&pDetails->str, buffer); 810 } 811 812 HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) 813 { 814 TRACE("(%p, %d, %p)\n", this, iColumn, pscid); 815 if (iColumn >= COLUMNS_COUNT) 816 return E_INVALIDARG; 817 pscid->fmtid = *RecycleBinColumns[iColumn].fmtId; 818 pscid->pid = RecycleBinColumns[iColumn].pid; 819 return S_OK; 820 } 821 822 BOOL CRecycleBin::RecycleBinIsEmpty() 823 { 824 CComPtr<IEnumIDList> spEnumFiles; 825 HRESULT hr = EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &spEnumFiles); 826 if (FAILED(hr)) 827 return TRUE; 828 CComHeapPtr<ITEMIDLIST> spPidl; 829 ULONG itemcount; 830 return spEnumFiles->Next(1, &spPidl, &itemcount) != S_OK; 831 } 832 833 /************************************************************************* 834 * RecycleBin IContextMenu interface 835 */ 836 837 HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 838 { 839 WCHAR szBuffer[100]; 840 MENUITEMINFOW mii; 841 int id = 1; 842 843 TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags ); 844 845 if (!hMenu) 846 return E_INVALIDARG; 847 848 ZeroMemory(&mii, sizeof(mii)); 849 mii.cbSize = sizeof(mii); 850 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE; 851 mii.fState = RecycleBinIsEmpty() ? MFS_DISABLED : MFS_ENABLED; 852 szBuffer[0] = L'\0'; 853 LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, _countof(szBuffer)); 854 mii.dwTypeData = szBuffer; 855 mii.cch = wcslen(mii.dwTypeData); 856 mii.wID = idCmdFirst + id++; 857 mii.fType = MFT_STRING; 858 iIdEmpty = 1; 859 860 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 861 return E_FAIL; 862 863 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id); 864 } 865 866 HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) 867 { 868 HRESULT hr; 869 LPSHELLBROWSER lpSB; 870 IShellView * lpSV = NULL; 871 WCHAR szDrive[8]; 872 873 TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb); 874 875 if (LOWORD(lpcmi->lpVerb) == iIdEmpty) 876 { 877 if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive) - 1)) 878 { 879 ERR("GetEnvironmentVariableW failed\n"); 880 return E_FAIL; 881 } 882 PathAddBackslashW(szDrive); 883 884 hr = SHEmptyRecycleBinW(lpcmi->hwnd, szDrive, 0); 885 TRACE("result %x\n", hr); 886 if (hr != S_OK) 887 return hr; 888 889 lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0); 890 if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV))) 891 lpSV->Refresh(); 892 } 893 return S_OK; 894 } 895 896 HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) 897 { 898 FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); 899 900 return E_NOTIMPL; 901 } 902 903 /************************************************************************* 904 * RecycleBin IShellPropSheetExt interface 905 */ 906 907 HRESULT WINAPI CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) 908 { 909 FIXME("%p %p %lu\n", this, pfnAddPage, lParam); 910 911 return E_NOTIMPL; 912 } 913 914 HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam) 915 { 916 FIXME("%p %lu %p %lu\n", this, uPageID, pfnReplaceWith, lParam); 917 918 return E_NOTIMPL; 919 } 920 921 /************************************************************************* 922 * RecycleBin IShellExtInit interface 923 */ 924 925 HRESULT WINAPI CRecycleBin::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) 926 { 927 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID ); 928 return S_OK; 929 } 930 931 /** 932 * Tests whether a file can be trashed 933 * @param wszPath Path to the file to be trash 934 * @returns TRUE if the file can be trashed, FALSE otherwise 935 */ 936 BOOL 937 TRASH_CanTrashFile(LPCWSTR wszPath) 938 { 939 LONG ret; 940 DWORD dwNukeOnDelete, dwType, VolSerialNumber, MaxComponentLength; 941 DWORD FileSystemFlags, dwSize, dwDisposition; 942 HKEY hKey; 943 WCHAR szBuffer[10]; 944 WCHAR szKey[150] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume\\"; 945 946 if (wszPath[1] != L':') 947 { 948 /* path is UNC */ 949 return FALSE; 950 } 951 952 // Copy and retrieve the root path from get given string 953 WCHAR wszRootPathName[MAX_PATH]; 954 StringCbCopyW(wszRootPathName, sizeof(wszRootPathName), wszPath); 955 PathStripToRootW(wszRootPathName); 956 957 // Test to see if the drive is fixed (non removable) 958 if (GetDriveTypeW(wszRootPathName) != DRIVE_FIXED) 959 { 960 /* no bitbucket on removable media */ 961 return FALSE; 962 } 963 964 if (!GetVolumeInformationW(wszRootPathName, NULL, 0, &VolSerialNumber, &MaxComponentLength, &FileSystemFlags, NULL, 0)) 965 { 966 ERR("GetVolumeInformationW failed with %u wszRootPathName=%s\n", GetLastError(), debugstr_w(wszRootPathName)); 967 return FALSE; 968 } 969 970 swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber), HIWORD(VolSerialNumber)); 971 wcscat(szKey, szBuffer); 972 973 if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS) 974 { 975 ERR("RegCreateKeyExW failed\n"); 976 return FALSE; 977 } 978 979 if (dwDisposition & REG_CREATED_NEW_KEY) 980 { 981 /* per default move to bitbucket */ 982 dwNukeOnDelete = 0; 983 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD)); 984 /* per default unlimited size */ 985 dwSize = -1; 986 RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize, sizeof(DWORD)); 987 RegCloseKey(hKey); 988 return TRUE; 989 } 990 else 991 { 992 dwSize = sizeof(dwNukeOnDelete); 993 ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType, (LPBYTE)&dwNukeOnDelete, &dwSize); 994 if (ret != ERROR_SUCCESS) 995 { 996 if (ret == ERROR_FILE_NOT_FOUND) 997 { 998 /* restore key and enable bitbucket */ 999 dwNukeOnDelete = 0; 1000 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD)); 1001 } 1002 RegCloseKey(hKey); 1003 return TRUE; 1004 } 1005 else if (dwNukeOnDelete) 1006 { 1007 /* do not delete to bitbucket */ 1008 RegCloseKey(hKey); 1009 return FALSE; 1010 } 1011 /* FIXME 1012 * check if bitbucket is full 1013 */ 1014 RegCloseKey(hKey); 1015 return TRUE; 1016 } 1017 } 1018 1019 BOOL 1020 TRASH_TrashFile(LPCWSTR wszPath) 1021 { 1022 TRACE("(%s)\n", debugstr_w(wszPath)); 1023 return DeleteFileToRecycleBin(wszPath); 1024 } 1025 1026 static void TRASH_PlayEmptyRecycleBinSound() 1027 { 1028 CRegKey regKey; 1029 CHeapPtr<WCHAR> pszValue; 1030 CHeapPtr<WCHAR> pszSndPath; 1031 DWORD dwType, dwSize; 1032 LONG lError; 1033 1034 lError = regKey.Open(HKEY_CURRENT_USER, 1035 L"AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current", 1036 KEY_READ); 1037 if (lError != ERROR_SUCCESS) 1038 return; 1039 1040 lError = regKey.QueryValue(NULL, &dwType, NULL, &dwSize); 1041 if (lError != ERROR_SUCCESS) 1042 return; 1043 1044 if (!pszValue.AllocateBytes(dwSize)) 1045 return; 1046 1047 lError = regKey.QueryValue(NULL, &dwType, pszValue, &dwSize); 1048 if (lError != ERROR_SUCCESS) 1049 return; 1050 1051 if (dwType == REG_EXPAND_SZ) 1052 { 1053 dwSize = ExpandEnvironmentStringsW(pszValue, NULL, 0); 1054 if (dwSize == 0) 1055 return; 1056 1057 if (!pszSndPath.Allocate(dwSize)) 1058 return; 1059 1060 if (ExpandEnvironmentStringsW(pszValue, pszSndPath, dwSize) == 0) 1061 return; 1062 } 1063 else if (dwType == REG_SZ) 1064 { 1065 /* The type is REG_SZ, no need to expand */ 1066 pszSndPath.Attach(pszValue.Detach()); 1067 } 1068 else 1069 { 1070 /* Invalid type */ 1071 return; 1072 } 1073 1074 PlaySoundW(pszSndPath, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT); 1075 } 1076 1077 /************************************************************************* 1078 * SHUpdateCRecycleBinIcon [SHELL32.@] 1079 * 1080 * Undocumented 1081 */ 1082 EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void) 1083 { 1084 FIXME("stub\n"); 1085 1086 // HACK! This dwItem2 should be the icon index in the system image list that has changed. 1087 // FIXME: Call SHMapPIDLToSystemImageListIndex 1088 DWORD dwItem2 = -1; 1089 1090 SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD, NULL, &dwItem2); 1091 return S_OK; 1092 } 1093 1094 /************************************************************************* 1095 * SHEmptyRecycleBinA (SHELL32.@) 1096 */ 1097 HRESULT WINAPI SHEmptyRecycleBinA(HWND hwnd, LPCSTR pszRootPath, DWORD dwFlags) 1098 { 1099 LPWSTR szRootPathW = NULL; 1100 int len; 1101 HRESULT hr; 1102 1103 TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_a(pszRootPath), dwFlags); 1104 1105 if (pszRootPath) 1106 { 1107 len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0); 1108 if (len == 0) 1109 return HRESULT_FROM_WIN32(GetLastError()); 1110 szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1111 if (!szRootPathW) 1112 return E_OUTOFMEMORY; 1113 if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0) 1114 { 1115 HeapFree(GetProcessHeap(), 0, szRootPathW); 1116 return HRESULT_FROM_WIN32(GetLastError()); 1117 } 1118 } 1119 1120 hr = SHEmptyRecycleBinW(hwnd, szRootPathW, dwFlags); 1121 HeapFree(GetProcessHeap(), 0, szRootPathW); 1122 1123 return hr; 1124 } 1125 1126 HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags) 1127 { 1128 WCHAR szBuffer[MAX_PATH]; 1129 DWORD count; 1130 LONG ret; 1131 IShellFolder *pDesktop, *pRecycleBin; 1132 PIDLIST_ABSOLUTE pidlRecycleBin; 1133 PITEMID_CHILD pidl; 1134 HRESULT hr = S_OK; 1135 LPENUMIDLIST penumFiles; 1136 STRRET StrRet; 1137 1138 TRACE("%p, %s, 0x%08x\n", hwnd, debugstr_w(pszRootPath), dwFlags); 1139 1140 if (!(dwFlags & SHERB_NOCONFIRMATION)) 1141 { 1142 hr = SHGetDesktopFolder(&pDesktop); 1143 if (FAILED(hr)) 1144 return hr; 1145 hr = SHGetFolderLocation(NULL, CSIDL_BITBUCKET, NULL, 0, &pidlRecycleBin); 1146 if (FAILED(hr)) 1147 { 1148 pDesktop->Release(); 1149 return hr; 1150 } 1151 hr = pDesktop->BindToObject(pidlRecycleBin, NULL, IID_PPV_ARG(IShellFolder, &pRecycleBin)); 1152 CoTaskMemFree(pidlRecycleBin); 1153 pDesktop->Release(); 1154 if (FAILED(hr)) 1155 return hr; 1156 hr = pRecycleBin->EnumObjects(hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penumFiles); 1157 if (FAILED(hr)) 1158 { 1159 pRecycleBin->Release(); 1160 return hr; 1161 } 1162 1163 count = 0; 1164 if (hr != S_FALSE) 1165 { 1166 while (penumFiles->Next(1, &pidl, NULL) == S_OK) 1167 { 1168 count++; 1169 pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL, &StrRet); 1170 StrRetToBuf(&StrRet, pidl, szBuffer, _countof(szBuffer)); 1171 CoTaskMemFree(pidl); 1172 } 1173 penumFiles->Release(); 1174 } 1175 pRecycleBin->Release(); 1176 1177 switch (count) 1178 { 1179 case 0: 1180 /* no files, don't need confirmation */ 1181 break; 1182 1183 case 1: 1184 /* we have only one item inside the bin, so show a message box with its name */ 1185 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEITEM_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET), 1186 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO) 1187 { 1188 return S_OK; 1189 } 1190 break; 1191 1192 default: 1193 /* we have more than one item, so show a message box with the count of the items */ 1194 StringCbPrintfW(szBuffer, sizeof(szBuffer), L"%u", count); 1195 if (ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(IDS_DELETEMULTIPLE_TEXT), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET), 1196 MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, szBuffer) == IDNO) 1197 { 1198 return S_OK; 1199 } 1200 break; 1201 } 1202 } 1203 1204 if (dwFlags & SHERB_NOPROGRESSUI) 1205 { 1206 ret = EmptyRecycleBinW(pszRootPath); 1207 } 1208 else 1209 { 1210 /* FIXME 1211 * show a progress dialog 1212 */ 1213 ret = EmptyRecycleBinW(pszRootPath); 1214 } 1215 1216 if (!ret) 1217 return HRESULT_FROM_WIN32(GetLastError()); 1218 1219 if (!(dwFlags & SHERB_NOSOUND)) 1220 { 1221 TRASH_PlayEmptyRecycleBinSound(); 1222 } 1223 return S_OK; 1224 } 1225 1226 HRESULT WINAPI SHQueryRecycleBinA(LPCSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo) 1227 { 1228 LPWSTR szRootPathW = NULL; 1229 int len; 1230 HRESULT hr; 1231 1232 TRACE("%s, %p\n", debugstr_a(pszRootPath), pSHQueryRBInfo); 1233 1234 if (pszRootPath) 1235 { 1236 len = MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, NULL, 0); 1237 if (len == 0) 1238 return HRESULT_FROM_WIN32(GetLastError()); 1239 szRootPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1240 if (!szRootPathW) 1241 return E_OUTOFMEMORY; 1242 if (MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, szRootPathW, len) == 0) 1243 { 1244 HeapFree(GetProcessHeap(), 0, szRootPathW); 1245 return HRESULT_FROM_WIN32(GetLastError()); 1246 } 1247 } 1248 1249 hr = SHQueryRecycleBinW(szRootPathW, pSHQueryRBInfo); 1250 HeapFree(GetProcessHeap(), 0, szRootPathW); 1251 1252 return hr; 1253 } 1254 1255 HRESULT WINAPI SHQueryRecycleBinW(LPCWSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo) 1256 { 1257 TRACE("%s, %p\n", debugstr_w(pszRootPath), pSHQueryRBInfo); 1258 1259 if (!pszRootPath || (pszRootPath[0] == 0) || 1260 !pSHQueryRBInfo || (pSHQueryRBInfo->cbSize < sizeof(SHQUERYRBINFO))) 1261 { 1262 return E_INVALIDARG; 1263 } 1264 1265 pSHQueryRBInfo->i64Size = 0; 1266 pSHQueryRBInfo->i64NumItems = 0; 1267 1268 CComPtr<IRecycleBin> spRecycleBin; 1269 HRESULT hr; 1270 if (FAILED_UNEXPECTEDLY((hr = GetDefaultRecycleBin(pszRootPath, &spRecycleBin)))) 1271 return hr; 1272 1273 CComPtr<IRecycleBinEnumList> spEnumList; 1274 hr = spRecycleBin->EnumObjects(&spEnumList); 1275 if (!SUCCEEDED(hr)) 1276 return hr; 1277 1278 while (TRUE) 1279 { 1280 CComPtr<IRecycleBinFile> spFile; 1281 hr = spEnumList->Next(1, &spFile, NULL); 1282 if (hr == S_FALSE) 1283 return S_OK; 1284 1285 if (FAILED_UNEXPECTEDLY(hr)) 1286 return hr; 1287 1288 ULARGE_INTEGER Size = {}; 1289 if (FAILED_UNEXPECTEDLY((hr = spFile->GetFileSize(&Size)))) 1290 return hr; 1291 1292 pSHQueryRBInfo->i64Size += Size.QuadPart; 1293 pSHQueryRBInfo->i64NumItems++; 1294 } 1295 1296 return S_OK; 1297 } 1298