2 * Trash virtual folder support. The trashing engine is implemented in trash.c
4 * Copyright (C) 2006 Mikolaj Zalewski
5 * Copyright (C) 2009 Andrew Hill
6 * Copyright (C) 2018 Russell Johnson
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.
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.
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
28 WINE_DEFAULT_DEBUG_CHANNEL(CRecycleBin
);
40 static const columninfo RecycleBinColumns
[] =
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}, */
53 #define COLUMN_DELFROM 1
54 #define COLUMN_DATEDEL 2
57 #define COLUMN_MTIME 5
59 #define COLUMNS_COUNT 6
65 HRESULT
CRecyclerExtractIcon_CreateInstance(LPCITEMIDLIST pidl
, REFIID riid
, LPVOID
* ppvOut
)
67 CComPtr
<IDefaultExtractIconInit
> initIcon
;
68 HRESULT hr
= SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit
, &initIcon
));
69 if (FAILED_UNEXPECTEDLY(hr
))
72 /* FIXME: This is completely unimplemented */
73 initIcon
->SetNormalIcon(swShell32Name
, 0);
75 return initIcon
->QueryInterface(riid
, ppvOut
);
78 class CRecycleBinEnum
:
79 public CEnumIDListBase
85 HRESULT WINAPI
Initialize(DWORD dwFlags
);
86 static BOOL WINAPI
CBEnumRecycleBin(IN PVOID Context
, IN HANDLE hDeletedFile
);
87 BOOL WINAPI
CBEnumRecycleBin(IN HANDLE hDeletedFile
);
89 BEGIN_COM_MAP(CRecycleBinEnum
)
90 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList
, IEnumIDList
)
94 class CRecycleBinItemContextMenu
:
95 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
101 CRecycleBinItemContextMenu();
102 ~CRecycleBinItemContextMenu();
103 HRESULT WINAPI
Initialize(LPCITEMIDLIST pidl
);
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
);
111 virtual HRESULT WINAPI
HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
113 BEGIN_COM_MAP(CRecycleBinItemContextMenu
)
114 COM_INTERFACE_ENTRY_IID(IID_IContextMenu
, IContextMenu
)
115 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2
, IContextMenu2
)
121 PIDLRecycleStruct
*pFileDetails
;
124 } SEARCH_CONTEXT
, *PSEARCH_CONTEXT
;
126 BOOL WINAPI
CBSearchRecycleBin(IN PVOID Context
, IN HANDLE hDeletedFile
)
128 PSEARCH_CONTEXT pContext
= (PSEARCH_CONTEXT
)Context
;
130 PDELETED_FILE_DETAILS_W pFileDetails
;
134 if (!GetDeletedFileDetailsW(hDeletedFile
,
138 GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
140 ERR("GetDeletedFileDetailsW failed\n");
144 pFileDetails
= (DELETED_FILE_DETAILS_W
*)SHAlloc(dwSize
);
151 if (!GetDeletedFileDetailsW(hDeletedFile
,
156 ERR("GetDeletedFileDetailsW failed\n");
157 SHFree(pFileDetails
);
161 ret
= memcmp(pFileDetails
, pContext
->pFileDetails
, dwSize
);
164 pContext
->hDeletedFile
= hDeletedFile
;
165 pContext
->bFound
= TRUE
;
168 CloseRecycleBinHandle(hDeletedFile
);
170 SHFree(pFileDetails
);
174 static PIDLRecycleStruct
* _ILGetRecycleStruct(LPCITEMIDLIST pidl
)
176 LPPIDLDATA pdata
= _ILGetDataPointer(pidl
);
178 if (pdata
&& pdata
->type
== 0x00)
179 return (PIDLRecycleStruct
*) & (pdata
->u
.crecycle
);
184 CRecycleBinEnum::CRecycleBinEnum()
188 CRecycleBinEnum::~CRecycleBinEnum()
192 HRESULT WINAPI
CRecycleBinEnum::Initialize(DWORD dwFlags
)
194 static LPCWSTR szDrive
= L
"C:\\";
196 if (dwFlags
& SHCONTF_NONFOLDERS
)
198 TRACE("Starting Enumeration\n");
200 if (!EnumerateRecycleBinW(szDrive
/* FIXME */ , CBEnumRecycleBin
, (PVOID
)this))
202 WARN("Error: EnumerateCRecycleBinW failed\n");
213 static LPITEMIDLIST
_ILCreateRecycleItem(PDELETED_FILE_DETAILS_W pFileDetails
)
217 PIDLRecycleStruct
* p
;
218 int size0
= (char*)&tmp
.u
.crecycle
.szName
- (char*)&tmp
.u
.crecycle
;
222 size
+= (wcslen(pFileDetails
->FileName
) + 1) * sizeof(WCHAR
);
224 pidl
= (LPITEMIDLIST
)SHAlloc(size
+ 4);
228 pidl
->mkid
.cb
= size
+ 2;
229 memcpy(pidl
->mkid
.abID
, &tmp
, 2 + size0
);
231 p
= &((PIDLDATA
*)pidl
->mkid
.abID
)->u
.crecycle
;
232 RtlCopyMemory(p
, pFileDetails
, sizeof(DELETED_FILE_DETAILS_W
));
233 wcscpy(p
->szName
, pFileDetails
->FileName
);
234 *(WORD
*)((char*)pidl
+ (size
+ 2)) = 0;
238 BOOL WINAPI
CRecycleBinEnum::CBEnumRecycleBin(IN PVOID Context
, IN HANDLE hDeletedFile
)
240 return static_cast<CRecycleBinEnum
*>(Context
)->CBEnumRecycleBin(hDeletedFile
);
243 BOOL WINAPI
CRecycleBinEnum::CBEnumRecycleBin(IN HANDLE hDeletedFile
)
245 PDELETED_FILE_DETAILS_W pFileDetails
;
247 LPITEMIDLIST pidl
= NULL
;
250 if (!GetDeletedFileDetailsW(hDeletedFile
,
254 GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
256 ERR("GetDeletedFileDetailsW failed\n");
260 pFileDetails
= (DELETED_FILE_DETAILS_W
*)SHAlloc(dwSize
);
267 if (!GetDeletedFileDetailsW(hDeletedFile
,
272 ERR("GetDeletedFileDetailsW failed\n");
273 SHFree(pFileDetails
);
277 pidl
= _ILCreateRecycleItem(pFileDetails
);
280 SHFree(pFileDetails
);
284 ret
= AddToEnumList(pidl
);
288 SHFree(pFileDetails
);
289 TRACE("Returning %d\n", ret
);
290 CloseRecycleBinHandle(hDeletedFile
);
294 /**************************************************************************
295 * IContextMenu2 Bitbucket Item Implementation
298 CRecycleBinItemContextMenu::CRecycleBinItemContextMenu()
303 CRecycleBinItemContextMenu::~CRecycleBinItemContextMenu()
308 HRESULT WINAPI
CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST pidl
)
310 apidl
= ILClone(pidl
);
312 return E_OUTOFMEMORY
;
316 HRESULT WINAPI
CRecycleBinItemContextMenu::QueryContextMenu(HMENU hMenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
318 WCHAR szBuffer
[30] = {0};
321 TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n", this, hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
323 if (LoadStringW(shell32_hInstance
, IDS_RESTORE
, szBuffer
, _countof(szBuffer
)))
325 szBuffer
[_countof(szBuffer
)-1] = L
'\0';
326 _InsertMenuItemW(hMenu
, indexMenu
++, TRUE
, idCmdFirst
+ Count
, MFT_STRING
, szBuffer
, MFS_ENABLED
);
330 if (LoadStringW(shell32_hInstance
, IDS_CUT
, szBuffer
, _countof(szBuffer
)))
332 _InsertMenuItemW(hMenu
, indexMenu
++, TRUE
, idCmdFirst
+ Count
++, MFT_SEPARATOR
, NULL
, MFS_ENABLED
);
333 szBuffer
[_countof(szBuffer
)-1] = L
'\0';
334 _InsertMenuItemW(hMenu
, indexMenu
++, TRUE
, idCmdFirst
+ Count
++, MFT_STRING
, szBuffer
, MFS_ENABLED
);
337 if (LoadStringW(shell32_hInstance
, IDS_DELETE
, szBuffer
, _countof(szBuffer
)))
339 szBuffer
[_countof(szBuffer
)-1] = L
'\0';
340 _InsertMenuItemW(hMenu
, indexMenu
++, TRUE
, idCmdFirst
+ Count
++, MFT_SEPARATOR
, NULL
, MFS_ENABLED
);
341 _InsertMenuItemW(hMenu
, indexMenu
++, TRUE
, idCmdFirst
+ Count
++, MFT_STRING
, szBuffer
, MFS_ENABLED
);
344 if (LoadStringW(shell32_hInstance
, IDS_PROPERTIES
, szBuffer
, _countof(szBuffer
)))
346 szBuffer
[_countof(szBuffer
)-1] = L
'\0';
347 _InsertMenuItemW(hMenu
, indexMenu
++, TRUE
, idCmdFirst
+ Count
++, MFT_SEPARATOR
, NULL
, MFS_ENABLED
);
348 _InsertMenuItemW(hMenu
, indexMenu
++, TRUE
, idCmdFirst
+ Count
, MFT_STRING
, szBuffer
, MFS_DEFAULT
);
351 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, Count
);
354 HRESULT WINAPI
CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi
)
356 SEARCH_CONTEXT Context
;
357 static LPCWSTR szDrive
= L
"C:\\";
359 TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi
, lpcmi
->lpVerb
, lpcmi
->hwnd
);
361 if (lpcmi
->lpVerb
== MAKEINTRESOURCEA(1) || lpcmi
->lpVerb
== MAKEINTRESOURCEA(5))
363 Context
.pFileDetails
= _ILGetRecycleStruct(apidl
);
364 Context
.bFound
= FALSE
;
366 EnumerateRecycleBinW(szDrive
, CBSearchRecycleBin
, (PVOID
)&Context
);
370 if (lpcmi
->lpVerb
== MAKEINTRESOURCEA(1))
373 if (RestoreFile(Context
.hDeletedFile
))
380 DeleteFileHandleToRecycleBin(Context
.hDeletedFile
);
384 else if (lpcmi
->lpVerb
== MAKEINTRESOURCEA(3))
386 FIXME("implement cut\n");
389 else if (lpcmi
->lpVerb
== MAKEINTRESOURCEA(7))
391 FIXME("implement properties\n");
398 HRESULT WINAPI
CRecycleBinItemContextMenu::GetCommandString(UINT_PTR idCommand
, UINT uFlags
, UINT
*lpReserved
, LPSTR lpszName
, UINT uMaxNameLen
)
400 TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand
, uFlags
, lpReserved
, lpszName
, uMaxNameLen
);
405 HRESULT WINAPI
CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
407 TRACE("CRecycleBin_IContextMenu2Item_HandleMenuMsg (%p)->(msg=%x wp=%lx lp=%lx)\n", this, uMsg
, wParam
, lParam
);
412 CRecycleBin::CRecycleBin()
417 CRecycleBin::~CRecycleBin()
422 /*************************************************************************
423 * RecycleBin IPersistFolder2 interface
426 HRESULT WINAPI
CRecycleBin::GetClassID(CLSID
*pClassID
)
428 TRACE("(%p, %p)\n", this, pClassID
);
429 if (pClassID
== NULL
)
431 memcpy(pClassID
, &CLSID_RecycleBin
, sizeof(CLSID
));
435 HRESULT WINAPI
CRecycleBin::Initialize(LPCITEMIDLIST pidl
)
437 TRACE("(%p, %p)\n", this, pidl
);
439 SHFree((LPVOID
)this->pidl
);
440 this->pidl
= ILClone(pidl
);
441 if (this->pidl
== NULL
)
442 return E_OUTOFMEMORY
;
446 HRESULT WINAPI
CRecycleBin::GetCurFolder(LPITEMIDLIST
*ppidl
)
449 *ppidl
= ILClone(pidl
);
453 /*************************************************************************
454 * RecycleBin IShellFolder2 interface
457 HRESULT WINAPI
CRecycleBin::ParseDisplayName(HWND hwnd
, LPBC pbc
,
458 LPOLESTR pszDisplayName
, ULONG
*pchEaten
, PIDLIST_RELATIVE
*ppidl
,
459 ULONG
*pdwAttributes
)
466 PDELETED_FILE_DETAILS_W
467 UnpackDetailsFromPidl(LPCITEMIDLIST pidl
)
469 return (PDELETED_FILE_DETAILS_W
)&pidl
->mkid
.abID
;
472 HRESULT WINAPI
CRecycleBin::EnumObjects(HWND hwndOwner
, DWORD dwFlags
, LPENUMIDLIST
*ppEnumIDList
)
474 return ShellObjectCreatorInit
<CRecycleBinEnum
>(dwFlags
, IID_PPV_ARG(IEnumIDList
, ppEnumIDList
));
477 HRESULT WINAPI
CRecycleBin::BindToObject(PCUIDLIST_RELATIVE pidl
, LPBC pbc
, REFIID riid
, void **ppv
)
479 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl
, pbc
, debugstr_guid(&riid
), ppv
);
483 HRESULT WINAPI
CRecycleBin::BindToStorage(PCUIDLIST_RELATIVE pidl
, LPBC pbc
, REFIID riid
, void **ppv
)
485 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl
, pbc
, debugstr_guid(&riid
), ppv
);
489 HRESULT WINAPI
CRecycleBin::CompareIDs(LPARAM lParam
, PCUIDLIST_RELATIVE pidl1
, PCUIDLIST_RELATIVE pidl2
)
491 PIDLRecycleStruct
* pData1
= _ILGetRecycleStruct(pidl1
);
492 PIDLRecycleStruct
* pData2
= _ILGetRecycleStruct(pidl2
);
493 LPWSTR pName1
, pName2
;
495 if(!pData1
|| !pData2
|| LOWORD(lParam
) >= COLUMNS_COUNT
)
500 switch (LOWORD(lParam
))
503 pName1
= PathFindFileNameW(pData1
->szName
);
504 pName2
= PathFindFileNameW(pData2
->szName
);
505 result
= wcsicmp(pName1
, pName2
);
507 case 1: /* Orig. Location */
508 result
= wcsicmp(pData1
->szName
, pData2
->szName
);
510 case 2: /* Date Deleted */
511 result
= CompareFileTime(&pData1
->DeletionTime
, &pData2
->DeletionTime
);
514 diff
= pData1
->FileSize
.QuadPart
- pData2
->FileSize
.QuadPart
;
515 return MAKE_COMPARE_HRESULT(diff
);
517 pName1
= PathFindExtensionW(pData1
->szName
);
518 pName2
= PathFindExtensionW(pData2
->szName
);
519 result
= wcsicmp(pName1
, pName2
);
521 case 5: /* Modified */
522 result
= CompareFileTime(&pData1
->LastModification
, &pData2
->LastModification
);
525 return MAKE_COMPARE_HRESULT(result
);
528 HRESULT WINAPI
CRecycleBin::CreateViewObject(HWND hwndOwner
, REFIID riid
, void **ppv
)
530 CComPtr
<IShellView
> pShellView
;
531 HRESULT hr
= E_NOINTERFACE
;
533 TRACE("(%p, %p, %s, %p)\n", this, hwndOwner
, debugstr_guid(&riid
), ppv
);
540 if (IsEqualIID (riid
, IID_IDropTarget
))
542 hr
= CRecyclerDropTarget_CreateInstance(riid
, ppv
);
544 else if (IsEqualIID (riid
, IID_IContextMenu
) || IsEqualIID (riid
, IID_IContextMenu2
))
546 hr
= this->QueryInterface(riid
, ppv
);
548 else if (IsEqualIID (riid
, IID_IShellView
))
550 SFV_CREATE sfvparams
= {sizeof(SFV_CREATE
), this};
551 hr
= SHCreateShellFolderView(&sfvparams
, (IShellView
**)ppv
);
556 TRACE ("-- (%p)->(interface=%p)\n", this, ppv
);
561 HRESULT WINAPI
CRecycleBin::GetAttributesOf(UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
,
564 TRACE("(%p, %d, {%p, ...}, {%x})\n", this, cidl
, apidl
? apidl
[0] : NULL
, (unsigned int)*rgfInOut
);
565 *rgfInOut
&= SFGAO_FOLDER
|SFGAO_DROPTARGET
|SFGAO_HASPROPSHEET
|SFGAO_CANLINK
;
569 HRESULT WINAPI
CRecycleBin::GetUIObjectOf(HWND hwndOwner
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
,
570 REFIID riid
, UINT
*prgfInOut
, void **ppv
)
573 HRESULT hr
= E_INVALIDARG
;
575 TRACE ("(%p)->(%p,%u,apidl=%p, %p %p)\n", this,
576 hwndOwner
, cidl
, apidl
, prgfInOut
, ppv
);
583 if ((IsEqualIID (riid
, IID_IContextMenu
) || IsEqualIID(riid
, IID_IContextMenu2
)) && (cidl
>= 1))
585 hr
= ShellObjectCreatorInit
<CRecycleBinItemContextMenu
>(apidl
[0], riid
, &pObj
);
587 else if((IsEqualIID(riid
, IID_IExtractIconA
) || IsEqualIID(riid
, IID_IExtractIconW
)) && (cidl
== 1))
589 hr
= CRecyclerExtractIcon_CreateInstance(apidl
[0], riid
, &pObj
);
594 if (SUCCEEDED(hr
) && !pObj
)
598 TRACE ("(%p)->hr=0x%08x\n", this, hr
);
602 HRESULT WINAPI
CRecycleBin::GetDisplayNameOf(PCUITEMID_CHILD pidl
, SHGDNF uFlags
, STRRET
*pName
)
604 PIDLRecycleStruct
*pFileDetails
;
607 TRACE("(%p, %p, %x, %p)\n", this, pidl
, (unsigned int)uFlags
, pName
);
609 pFileDetails
= _ILGetRecycleStruct(pidl
);
613 pName
->uType
= STRRET_CSTR
;
617 pFileName
= wcsrchr(pFileDetails
->szName
, L
'\\');
621 pName
->uType
= STRRET_CSTR
;
625 pName
->pOleStr
= StrDupW(pFileName
+ 1);
626 if (pName
->pOleStr
== NULL
)
627 return E_OUTOFMEMORY
;
629 pName
->uType
= STRRET_WSTR
;
633 HRESULT WINAPI
CRecycleBin::SetNameOf(HWND hwnd
, PCUITEMID_CHILD pidl
, LPCOLESTR pszName
,
634 SHGDNF uFlags
, PITEMID_CHILD
*ppidlOut
)
637 return E_FAIL
; /* not supported */
640 HRESULT WINAPI
CRecycleBin::GetDefaultSearchGUID(GUID
*pguid
)
646 HRESULT WINAPI
CRecycleBin::EnumSearches(IEnumExtraSearch
**ppEnum
)
653 HRESULT WINAPI
CRecycleBin::GetDefaultColumn(DWORD dwReserved
, ULONG
*pSort
, ULONG
*pDisplay
)
655 TRACE("(%p, %x, %p, %p)\n", this, (unsigned int)dwReserved
, pSort
, pDisplay
);
663 HRESULT WINAPI
CRecycleBin::GetDefaultColumnState(UINT iColumn
, SHCOLSTATEF
*pcsFlags
)
665 TRACE("(%p, %d, %p)\n", this, iColumn
, pcsFlags
);
666 if (iColumn
>= COLUMNS_COUNT
)
668 *pcsFlags
= RecycleBinColumns
[iColumn
].pcsFlags
;
672 HRESULT WINAPI
CRecycleBin::GetDetailsEx(PCUITEMID_CHILD pidl
, const SHCOLUMNID
*pscid
, VARIANT
*pv
)
678 static HRESULT
FormatDateTime(LPWSTR buffer
, int size
, FILETIME
* ft
)
684 FileTimeToLocalFileTime(ft
, &lft
);
685 FileTimeToSystemTime(&lft
, &time
);
687 ret
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &time
, NULL
, buffer
, size
);
688 if (ret
> 0 && ret
< size
)
690 /* Append space + time without seconds */
692 GetTimeFormatW(LOCALE_USER_DEFAULT
, TIME_NOSECONDS
, &time
, NULL
, &buffer
[ret
], size
- ret
);
695 return (ret
!= 0 ? E_FAIL
: S_OK
);
698 HRESULT WINAPI
CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl
, UINT iColumn
, LPSHELLDETAILS pDetails
)
700 PIDLRecycleStruct
* pFileDetails
;
701 WCHAR buffer
[MAX_PATH
];
702 WCHAR szTypeName
[100];
706 TRACE("(%p, %p, %d, %p)\n", this, pidl
, iColumn
, pDetails
);
707 if (iColumn
>= COLUMNS_COUNT
)
709 pDetails
->fmt
= RecycleBinColumns
[iColumn
].fmt
;
710 pDetails
->cxChar
= RecycleBinColumns
[iColumn
].cxChars
;
712 return SHSetStrRet(&pDetails
->str
, RecycleBinColumns
[iColumn
].column_name_id
);
714 if (iColumn
== COLUMN_NAME
)
715 return GetDisplayNameOf(pidl
, SHGDN_NORMAL
, &pDetails
->str
);
717 pFileDetails
= _ILGetRecycleStruct(pidl
);
721 FormatDateTime(buffer
, MAX_PATH
, &pFileDetails
->DeletionTime
);
724 pszBackslash
= wcsrchr(pFileDetails
->szName
, L
'\\');
725 Length
= (pszBackslash
- pFileDetails
->szName
);
726 memcpy((LPVOID
)buffer
, pFileDetails
->szName
, Length
* sizeof(WCHAR
));
727 buffer
[Length
] = L
'\0';
730 StrFormatKBSizeW(pFileDetails
->FileSize
.QuadPart
, buffer
, MAX_PATH
);
733 FormatDateTime(buffer
, MAX_PATH
, &pFileDetails
->LastModification
);
736 // FIXME: We should in fact use a UNICODE version of _ILGetFileType
737 szTypeName
[0] = L
'\0';
738 wcscpy(buffer
, PathFindExtensionW(pFileDetails
->szName
));
739 if (!( HCR_MapTypeToValueW(buffer
, buffer
, _countof(buffer
), TRUE
) &&
740 HCR_MapTypeToValueW(buffer
, szTypeName
, _countof(szTypeName
), FALSE
)))
742 /* load localized file string */
743 szTypeName
[0] = '\0';
744 if(LoadStringW(shell32_hInstance
, IDS_ANY_FILE
, szTypeName
, _countof(szTypeName
)))
746 szTypeName
[63] = '\0';
747 StringCchPrintfW(buffer
, _countof(buffer
), szTypeName
, PathFindExtensionW(pFileDetails
->szName
));
750 return SHSetStrRet(&pDetails
->str
, szTypeName
);
755 return SHSetStrRet(&pDetails
->str
, buffer
);
758 HRESULT WINAPI
CRecycleBin::MapColumnToSCID(UINT iColumn
, SHCOLUMNID
*pscid
)
760 TRACE("(%p, %d, %p)\n", this, iColumn
, pscid
);
761 if (iColumn
>= COLUMNS_COUNT
)
763 pscid
->fmtid
= *RecycleBinColumns
[iColumn
].fmtId
;
764 pscid
->pid
= RecycleBinColumns
[iColumn
].pid
;
768 BOOL
CRecycleBin::RecycleBinIsEmpty()
770 CComPtr
<IEnumIDList
> spEnumFiles
;
771 HRESULT hr
= EnumObjects(NULL
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
, &spEnumFiles
);
774 CComHeapPtr
<ITEMIDLIST
> spPidl
;
776 return spEnumFiles
->Next(1, &spPidl
, &itemcount
) != S_OK
;
779 /*************************************************************************
780 * RecycleBin IContextMenu interface
783 HRESULT WINAPI
CRecycleBin::QueryContextMenu(HMENU hMenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
789 TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
794 ZeroMemory(&mii
, sizeof(mii
));
795 mii
.cbSize
= sizeof(mii
);
796 mii
.fMask
= MIIM_TYPE
| MIIM_ID
| MIIM_STATE
;
797 mii
.fState
= RecycleBinIsEmpty() ? MFS_DISABLED
: MFS_ENABLED
;
799 LoadStringW(shell32_hInstance
, IDS_EMPTY_BITBUCKET
, szBuffer
, _countof(szBuffer
));
800 mii
.dwTypeData
= szBuffer
;
801 mii
.cch
= wcslen(mii
.dwTypeData
);
802 mii
.wID
= idCmdFirst
+ id
++;
803 mii
.fType
= MFT_STRING
;
806 if (!InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
809 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, id
);
812 HRESULT WINAPI
CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi
)
816 IShellView
* lpSV
= NULL
;
818 TRACE("%p %p verb %p\n", this, lpcmi
, lpcmi
->lpVerb
);
820 if (LOWORD(lpcmi
->lpVerb
) == iIdEmpty
)
824 hr
= SHEmptyRecycleBinW(lpcmi
->hwnd
, L
"C:\\", 0);
825 TRACE("result %x\n", hr
);
829 lpSB
= (LPSHELLBROWSER
)SendMessageA(lpcmi
->hwnd
, CWM_GETISHELLBROWSER
, 0, 0);
830 if (lpSB
&& SUCCEEDED(lpSB
->QueryActiveShellView(&lpSV
)))
836 HRESULT WINAPI
CRecycleBin::GetCommandString(UINT_PTR idCommand
, UINT uFlags
, UINT
*lpReserved
, LPSTR lpszName
, UINT uMaxNameLen
)
838 FIXME("%p %lu %u %p %p %u\n", this, idCommand
, uFlags
, lpReserved
, lpszName
, uMaxNameLen
);
843 /*************************************************************************
844 * RecycleBin IShellPropSheetExt interface
847 HRESULT WINAPI
CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
849 FIXME("%p %p %lu\n", this, pfnAddPage
, lParam
);
854 HRESULT WINAPI
CRecycleBin::ReplacePage(EXPPS uPageID
, LPFNSVADDPROPSHEETPAGE pfnReplaceWith
, LPARAM lParam
)
856 FIXME("%p %lu %p %lu\n", this, uPageID
, pfnReplaceWith
, lParam
);
861 /*************************************************************************
862 * RecycleBin IShellExtInit interface
865 HRESULT WINAPI
CRecycleBin::Initialize(LPCITEMIDLIST pidlFolder
, IDataObject
*pdtobj
, HKEY hkeyProgID
)
867 TRACE("%p %p %p %p\n", this, pidlFolder
, pdtobj
, hkeyProgID
);
872 * Tests whether a file can be trashed
873 * @param wszPath Path to the file to be trash
874 * @returns TRUE if the file can be trashed, FALSE otherwise
877 TRASH_CanTrashFile(LPCWSTR wszPath
)
880 DWORD dwNukeOnDelete
, dwType
, VolSerialNumber
, MaxComponentLength
;
881 DWORD FileSystemFlags
, dwSize
, dwDisposition
;
884 WCHAR szKey
[150] = L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume\\";
886 if (wszPath
[1] != L
':')
892 // Copy and retrieve the root path from get given string
893 WCHAR wszRootPathName
[MAX_PATH
];
894 StringCbCopy(wszRootPathName
, sizeof(wszRootPathName
), wszPath
);
895 PathStripToRootW(wszRootPathName
);
897 // Test to see if the drive is fixed (non removable)
898 if (GetDriveTypeW(wszRootPathName
) != DRIVE_FIXED
)
900 /* no bitbucket on removable media */
904 if (!GetVolumeInformationW(wszRootPathName
, NULL
, 0, &VolSerialNumber
, &MaxComponentLength
, &FileSystemFlags
, NULL
, 0))
906 ERR("GetVolumeInformationW failed with %u wszRootPathName=%s\n", GetLastError(), debugstr_w(wszRootPathName
));
910 swprintf(szBuffer
, L
"%04X-%04X", LOWORD(VolSerialNumber
), HIWORD(VolSerialNumber
));
911 wcscat(szKey
, szBuffer
);
913 if (RegCreateKeyExW(HKEY_CURRENT_USER
, szKey
, 0, NULL
, 0, KEY_WRITE
, NULL
, &hKey
, &dwDisposition
) != ERROR_SUCCESS
)
915 ERR("RegCreateKeyExW failed\n");
919 if (dwDisposition
& REG_CREATED_NEW_KEY
)
921 /* per default move to bitbucket */
923 RegSetValueExW(hKey
, L
"NukeOnDelete", 0, REG_DWORD
, (LPBYTE
)&dwNukeOnDelete
, sizeof(DWORD
));
924 /* per default unlimited size */
926 RegSetValueExW(hKey
, L
"MaxCapacity", 0, REG_DWORD
, (LPBYTE
)&dwSize
, sizeof(DWORD
));
932 dwSize
= sizeof(dwNukeOnDelete
);
933 ret
= RegQueryValueExW(hKey
, L
"NukeOnDelete", NULL
, &dwType
, (LPBYTE
)&dwNukeOnDelete
, &dwSize
);
934 if (ret
!= ERROR_SUCCESS
)
936 if (ret
== ERROR_FILE_NOT_FOUND
)
938 /* restore key and enable bitbucket */
940 RegSetValueExW(hKey
, L
"NukeOnDelete", 0, REG_DWORD
, (LPBYTE
)&dwNukeOnDelete
, sizeof(DWORD
));
945 else if (dwNukeOnDelete
)
947 /* do not delete to bitbucket */
952 * check if bitbucket is full
960 TRASH_TrashFile(LPCWSTR wszPath
)
962 TRACE("(%s)\n", debugstr_w(wszPath
));
963 return DeleteFileToRecycleBin(wszPath
);
966 /*************************************************************************
967 * SHUpdateCRecycleBinIcon [SHELL32.@]
971 EXTERN_C HRESULT WINAPI
SHUpdateRecycleBinIcon(void)
978 /*************************************************************************
979 * SHEmptyRecycleBinA (SHELL32.@)
981 HRESULT WINAPI
SHEmptyRecycleBinA(HWND hwnd
, LPCSTR pszRootPath
, DWORD dwFlags
)
983 LPWSTR szRootPathW
= NULL
;
987 TRACE("%p, %s, 0x%08x\n", hwnd
, debugstr_a(pszRootPath
), dwFlags
);
991 len
= MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, NULL
, 0);
993 return HRESULT_FROM_WIN32(GetLastError());
994 szRootPathW
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
996 return E_OUTOFMEMORY
;
997 if (MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, szRootPathW
, len
) == 0)
999 HeapFree(GetProcessHeap(), 0, szRootPathW
);
1000 return HRESULT_FROM_WIN32(GetLastError());
1004 hr
= SHEmptyRecycleBinW(hwnd
, szRootPathW
, dwFlags
);
1005 HeapFree(GetProcessHeap(), 0, szRootPathW
);
1010 HRESULT WINAPI
SHEmptyRecycleBinW(HWND hwnd
, LPCWSTR pszRootPath
, DWORD dwFlags
)
1012 WCHAR szPath
[MAX_PATH
] = {0}, szBuffer
[MAX_PATH
];
1013 DWORD dwSize
, dwType
, count
;
1015 IShellFolder
*pDesktop
, *pRecycleBin
;
1016 PIDLIST_ABSOLUTE pidlRecycleBin
;
1019 LPENUMIDLIST penumFiles
;
1022 TRACE("%p, %s, 0x%08x\n", hwnd
, debugstr_w(pszRootPath
), dwFlags
);
1024 if (!(dwFlags
& SHERB_NOCONFIRMATION
))
1026 hr
= SHGetDesktopFolder(&pDesktop
);
1029 hr
= SHGetFolderLocation(NULL
, CSIDL_BITBUCKET
, NULL
, 0, &pidlRecycleBin
);
1032 pDesktop
->Release();
1035 hr
= pDesktop
->BindToObject(pidlRecycleBin
, NULL
, IID_PPV_ARG(IShellFolder
, &pRecycleBin
));
1036 CoTaskMemFree(pidlRecycleBin
);
1037 pDesktop
->Release();
1040 hr
= pRecycleBin
->EnumObjects(hwnd
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
| SHCONTF_INCLUDEHIDDEN
, &penumFiles
);
1043 pRecycleBin
->Release();
1050 while (penumFiles
->Next(1, &pidl
, NULL
) == S_OK
)
1053 pRecycleBin
->GetDisplayNameOf(pidl
, SHGDN_NORMAL
, &StrRet
);
1054 StrRetToBuf(&StrRet
, pidl
, szBuffer
, _countof(szBuffer
));
1055 CoTaskMemFree(pidl
);
1057 penumFiles
->Release();
1059 pRecycleBin
->Release();
1064 /* no files, don't need confirmation */
1068 /* we have only one item inside the bin, so show a message box with its name */
1069 if (ShellMessageBoxW(shell32_hInstance
, hwnd
, MAKEINTRESOURCEW(IDS_DELETEITEM_TEXT
), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET
),
1070 MB_ICONEXCLAMATION
| MB_YESNO
| MB_DEFBUTTON2
, szBuffer
) == IDNO
)
1077 /* we have more than one item, so show a message box with the count of the items */
1078 StringCbPrintfW(szBuffer
, sizeof(szBuffer
), L
"%u", count
);
1079 if (ShellMessageBoxW(shell32_hInstance
, hwnd
, MAKEINTRESOURCEW(IDS_DELETEMULTIPLE_TEXT
), MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET
),
1080 MB_ICONEXCLAMATION
| MB_YESNO
| MB_DEFBUTTON2
, szBuffer
) == IDNO
)
1088 if (dwFlags
& SHERB_NOPROGRESSUI
)
1090 ret
= EmptyRecycleBinW(pszRootPath
);
1095 * show a progress dialog
1097 ret
= EmptyRecycleBinW(pszRootPath
);
1101 return HRESULT_FROM_WIN32(GetLastError());
1103 if (!(dwFlags
& SHERB_NOSOUND
))
1105 dwSize
= sizeof(szPath
);
1106 ret
= RegGetValueW(HKEY_CURRENT_USER
,
1107 L
"AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current",
1113 if (ret
!= ERROR_SUCCESS
)
1116 if (dwType
!= REG_EXPAND_SZ
) /* type dismatch */
1119 szPath
[_countof(szPath
)-1] = L
'\0';
1120 PlaySoundW(szPath
, NULL
, SND_FILENAME
);
1125 HRESULT WINAPI
SHQueryRecycleBinA(LPCSTR pszRootPath
, LPSHQUERYRBINFO pSHQueryRBInfo
)
1127 LPWSTR szRootPathW
= NULL
;
1131 TRACE("%s, %p\n", debugstr_a(pszRootPath
), pSHQueryRBInfo
);
1135 len
= MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, NULL
, 0);
1137 return HRESULT_FROM_WIN32(GetLastError());
1138 szRootPathW
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
1140 return E_OUTOFMEMORY
;
1141 if (MultiByteToWideChar(CP_ACP
, 0, pszRootPath
, -1, szRootPathW
, len
) == 0)
1143 HeapFree(GetProcessHeap(), 0, szRootPathW
);
1144 return HRESULT_FROM_WIN32(GetLastError());
1148 hr
= SHQueryRecycleBinW(szRootPathW
, pSHQueryRBInfo
);
1149 HeapFree(GetProcessHeap(), 0, szRootPathW
);
1154 HRESULT WINAPI
SHQueryRecycleBinW(LPCWSTR pszRootPath
, LPSHQUERYRBINFO pSHQueryRBInfo
)
1156 FIXME("%s, %p - stub\n", debugstr_w(pszRootPath
), pSHQueryRBInfo
);
1158 if (!(pszRootPath
) || (pszRootPath
[0] == 0) ||
1159 !(pSHQueryRBInfo
) || (pSHQueryRBInfo
->cbSize
< sizeof(SHQUERYRBINFO
)))
1161 return E_INVALIDARG
;
1164 pSHQueryRBInfo
->i64Size
= 0;
1165 pSHQueryRBInfo
->i64NumItems
= 0;