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