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