https://git.reactos.org/?p=reactos.git;a=commitdiff;h=e5fc4de8c9254ccd1062b…
commit e5fc4de8c9254ccd1062b8309d2965627fb4c5b6
Author: Whindmar Saksit <whindsaks(a)proton.me>
AuthorDate: Sat Feb 22 13:03:11 2025 +0100
Commit: GitHub <noreply(a)github.com>
CommitDate: Sat Feb 22 13:03:11 2025 +0100
[SHELL32] Handle multiple files in recycle bin delete/restore operations (#7568)
CORE-19895 CORE-19231
---
dll/win32/shell32/COpenWithMenu.cpp | 3 +-
dll/win32/shell32/folders/CRecycleBin.cpp | 165 +++++++++++++++------
dll/win32/shell32/precomp.h | 12 ++
dll/win32/shell32/shellrecyclebin/recyclebin.c | 19 ++-
dll/win32/shell32/shellrecyclebin/recyclebin.h | 7 +
.../shell32/shellrecyclebin/recyclebin_v5.cpp | 87 ++++-------
dll/win32/shell32/shellrecyclebin/recyclebin_v5.h | 4 +
.../shellrecyclebin/recyclebin_v5_enumerator.cpp | 7 +
dll/win32/shell32/shlfileop.cpp | 60 +++++++-
dll/win32/shell32/utils.h | 11 +-
10 files changed, 260 insertions(+), 115 deletions(-)
diff --git a/dll/win32/shell32/COpenWithMenu.cpp b/dll/win32/shell32/COpenWithMenu.cpp
index 3e081de625b..9f646c00019 100644
--- a/dll/win32/shell32/COpenWithMenu.cpp
+++ b/dll/win32/shell32/COpenWithMenu.cpp
@@ -1297,7 +1297,8 @@ VOID COpenWithMenu::AddApp(PVOID pApp)
m_idCmdLast++;
}
-static const CMVERBMAP g_VerbMap[] = {
+static const CMVERBMAP g_VerbMap[] =
+{
{ "openas", 0 },
{ NULL }
};
diff --git a/dll/win32/shell32/folders/CRecycleBin.cpp
b/dll/win32/shell32/folders/CRecycleBin.cpp
index bd09268623b..6508e071326 100644
--- a/dll/win32/shell32/folders/CRecycleBin.cpp
+++ b/dll/win32/shell32/folders/CRecycleBin.cpp
@@ -210,12 +210,6 @@ static HRESULT GetItemTypeName(PCUITEMID_CHILD pidl, const BBITEMDATA
&Data, SHF
return E_FAIL;
}
-static HDELFILE GetRecycleBinFileHandleFromItem(const BBITEMDATA &Data)
-{
- RECYCLEBINFILEIDENTITY identity = { Data.DeletionTime, GetItemRecycledFullPath(Data)
};
- return GetRecycleBinFileHandle(NULL, &identity);
-}
-
/*
* Recycle Bin folder
*/
@@ -285,14 +279,6 @@ EXTERN_C void CRecycleBin_NotifyRecycled(LPCWSTR OrigPath, const
WIN32_FIND_DATA
static void CRecycleBin_NotifyRemovedFromRecycleBin(LPCITEMIDLIST BBItem)
{
CRecycleBin_ChangeNotifyBBItem(IsFolder(BBItem) ? SHCNE_RMDIR : SHCNE_DELETE,
BBItem);
-
- CComHeapPtr<ITEMIDLIST> pidlBB(SHCloneSpecialIDList(NULL, CSIDL_BITBUCKET,
FALSE));
- CComPtr<IShellFolder> pSF;
- if (pidlBB && SUCCEEDED(SHBindToObject(NULL, pidlBB,
IID_PPV_ARG(IShellFolder, &pSF))))
- {
- if (IsRecycleBinEmpty(pSF))
- SHUpdateRecycleBinIcon();
- }
}
static HRESULT CRecyclerExtractIcon_CreateInstance(
@@ -316,11 +302,12 @@ class CRecycleBinItemContextMenu :
public IContextMenu2
{
private:
- LPITEMIDLIST apidl;
+ PITEMID_CHILD* m_apidl;
+ UINT m_cidl;
public:
CRecycleBinItemContextMenu();
- ~CRecycleBinItemContextMenu();
- HRESULT WINAPI Initialize(LPCITEMIDLIST pidl);
+ virtual ~CRecycleBinItemContextMenu();
+ HRESULT WINAPI Initialize(UINT cidl, PCUITEMID_CHILD_ARRAY apidl);
// IContextMenu
STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT
idCmdLast, UINT uFlags) override;
@@ -411,19 +398,21 @@ BOOL CRecycleBinEnum::CBEnumRecycleBin(IN HDELFILE hDeletedFile)
CRecycleBinItemContextMenu::CRecycleBinItemContextMenu()
{
- apidl = NULL;
+ m_apidl = NULL;
+ m_cidl = 0;
}
CRecycleBinItemContextMenu::~CRecycleBinItemContextMenu()
{
- ILFree(apidl);
+ _ILFreeaPidl(m_apidl, m_cidl);
}
-HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST pidl)
+HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(UINT cidl, PCUITEMID_CHILD_ARRAY
apidl)
{
- apidl = ILClone(pidl);
- if (apidl == NULL)
+ m_apidl = _ILCopyaPidl(apidl, cidl);
+ if (m_apidl == NULL)
return E_OUTOFMEMORY;
+ m_cidl = cidl;
return S_OK;
}
@@ -473,22 +462,93 @@ HRESULT WINAPI CRecycleBinItemContextMenu::QueryContextMenu(HMENU
hMenu, UINT in
return idHigh ? MAKE_HRESULT(SEVERITY_SUCCESS, 0, idHigh - idCmdFirst + 1) : S_OK;
}
-static BOOL ConfirmDelete(LPCMINVOKECOMMANDINFO lpcmi, UINT cidl, LPCITEMIDLIST pidl,
const BBITEMDATA &Data)
+static BOOL ConfirmDelete(LPCMINVOKECOMMANDINFO lpcmi, UINT cidl, LPCITEMIDLIST pidl)
{
+ BBITEMDATA *pData;
if (lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)
{
return TRUE;
}
- else if (cidl == 1)
+ else if (cidl == 1 && (pData = ValidateItem(pidl)) != NULL)
{
const UINT ask = IsFolder(pidl) ? ASK_DELETE_FOLDER : ASK_DELETE_FILE;
- return SHELL_ConfirmYesNoW(lpcmi->hwnd, ask, GetItemOriginalFileName(Data));
+ return SHELL_ConfirmYesNoW(lpcmi->hwnd, ask,
GetItemOriginalFileName(*pData));
}
- WCHAR buf[MAX_PATH];
+ WCHAR buf[42];
wsprintfW(buf, L"%d", cidl);
return SHELL_ConfirmYesNoW(lpcmi->hwnd, ASK_DELETE_MULTIPLE_ITEM, buf);
}
+static LPWSTR CreateFileOpStrings(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, BOOL
RecycledPath)
+{
+ PWSTR mem = NULL, newmem;
+ for (SIZE_T i = 0, cb = 0, cb2, cbPath; i < cidl; ++i, cb = cb2)
+ {
+ BBITEMDATA *pData = ValidateItem(apidl[i]);
+ if (!pData)
+ {
+fail:
+ LocalFree(mem);
+ return NULL;
+ }
+ LPCWSTR path = RecycledPath ? GetItemRecycledFullPath(*pData) :
GetItemOriginalFullPath(*pData);
+ cbPath = (lstrlenW(path) + 1) * sizeof(WCHAR);
+ cb2 = cb + cbPath;
+ SIZE_T cbTot = cb2 + sizeof(WCHAR); // \0\0 termination
+ newmem = (PWSTR)(i ? LocalReAlloc(mem, cbTot, LMEM_MOVEABLE) : LocalAlloc(LPTR,
cbTot));
+ if (!newmem)
+ goto fail;
+ mem = newmem;
+ CopyMemory((char*)mem + cb, path, cbPath);
+ *(PWSTR)((char*)mem + cb + cbPath) = UNICODE_NULL;
+ }
+ return mem;
+}
+
+typedef struct
+{
+ PCUITEMID_CHILD_ARRAY apidl;
+ UINT cidl, index;
+ BBITEMDATA *pItem;
+} FILEOPDATA;
+
+static HRESULT CALLBACK FileOpCallback(FILEOPCALLBACKEVENT Event, LPCWSTR Src, LPCWSTR
Dst, UINT Attrib, HRESULT hrOp, void *CallerData)
+{
+ FILEOPDATA &data = *(FILEOPDATA*)CallerData;
+ if (Event == FOCE_PREMOVEITEM || Event == FOCE_PREDELETEITEM)
+ {
+ data.pItem = NULL;
+ for (UINT i = 0; i < data.cidl; ++i)
+ {
+ BBITEMDATA *pItem = ValidateItem(data.apidl[i]);
+ if (pItem && !_wcsicmp(Src, GetItemRecycledFullPath(*pItem)))
+ {
+ data.pItem = pItem;
+ data.index = i;
+ break;
+ }
+ }
+ }
+ else if ((Event == FOCE_POSTDELETEITEM || Event == FOCE_POSTMOVEITEM) &&
SUCCEEDED(hrOp) && data.pItem)
+ {
+ RECYCLEBINFILEIDENTITY identity = { data.pItem->DeletionTime,
GetItemRecycledFullPath(*data.pItem) };
+ RemoveFromRecycleBinDatabase(&identity);
+ CRecycleBin_NotifyRemovedFromRecycleBin(data.apidl[data.index]);
+ data.pItem = NULL;
+ }
+ else if (Event == FOCE_FINISHOPERATIONS)
+ {
+ CComHeapPtr<ITEMIDLIST> pidlBB(SHCloneSpecialIDList(NULL, CSIDL_BITBUCKET,
FALSE));
+ CComPtr<IShellFolder> pSF;
+ if (pidlBB && SUCCEEDED(SHBindToObject(NULL, pidlBB,
IID_PPV_ARG(IShellFolder, &pSF))))
+ {
+ if (IsRecycleBinEmpty(pSF))
+ SHUpdateRecycleBinIcon();
+ }
+ }
+ return S_OK;
+}
+
HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi,
lpcmi->lpVerb, lpcmi->hwnd);
@@ -505,35 +565,51 @@ HRESULT WINAPI
CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO l
if (CmdId == IDC_BB_RESTORE || CmdId == IDC_BB_DELETE)
{
- BBITEMDATA *pData = ValidateItem(apidl);
- if (!pData && FAILED_UNEXPECTEDLY(E_FAIL))
- return E_FAIL;
- HDELFILE hDelFile = GetRecycleBinFileHandleFromItem(*pData);
- if (!hDelFile && FAILED_UNEXPECTEDLY(E_FAIL))
- return E_FAIL;
+ HRESULT hr = S_OK;
+ if (CmdId == IDC_BB_DELETE && !ConfirmDelete(lpcmi, m_cidl, m_apidl[0]))
+ return S_OK;
- HRESULT hr = S_FALSE;
+ LPWSTR pszzDst = NULL;
+ LPWSTR pszzSrc = CreateFileOpStrings(m_cidl, m_apidl, TRUE);
+ if (!pszzSrc)
+ return E_OUTOFMEMORY;
+ SHFILEOPSTRUCTW shfos = { lpcmi->hwnd, FO_DELETE, pszzSrc, NULL,
FOF_NOCONFIRMMKDIR };
if (CmdId == IDC_BB_RESTORE)
- hr = RestoreFileFromRecycleBin(hDelFile) ? S_OK : E_FAIL;
- else if (ConfirmDelete(lpcmi, 1, apidl, *pData))
- hr = DeleteFileInRecycleBin(hDelFile) ? S_OK : E_FAIL;
-
- if (hr == S_OK)
- CRecycleBin_NotifyRemovedFromRecycleBin(apidl);
-
- CloseRecycleBinHandle(hDelFile);
+ {
+ pszzDst = CreateFileOpStrings(m_cidl, m_apidl, FALSE);
+ if (!pszzDst)
+ hr = E_OUTOFMEMORY;
+ shfos.wFunc = FO_MOVE;
+ shfos.pTo = pszzDst;
+ shfos.fFlags |= FOF_MULTIDESTFILES;
+ }
+ else // IDC_BB_DELETE
+ {
+ shfos.fFlags |= FOF_NOCONFIRMATION;
+ }
+ if (SUCCEEDED(hr))
+ {
+ if (lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)
+ shfos.fFlags |= FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMATION;
+ FILEOPDATA data = { m_apidl, m_cidl };
+ int res = SHELL32_FileOperation(&shfos, FileOpCallback, &data);
+ if (res && res != DE_OPCANCELLED && res != ERROR_CANCELLED)
+ hr = SHELL_ErrorBox(*lpcmi, E_FAIL);
+ }
+ LocalFree(pszzDst);
+ LocalFree(pszzSrc);
return hr;
}
else if (CmdId == IDC_BB_CUT)
{
FIXME("implement cut\n");
- SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED);
+ SHELL_ErrorBox(*lpcmi, ERROR_NOT_SUPPORTED);
return E_NOTIMPL;
}
else if (CmdId == IDC_BB_PROPERTIES)
{
FIXME("implement properties\n");
- SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED);
+ SHELL_ErrorBox(*lpcmi, ERROR_NOT_SUPPORTED);
return E_NOTIMPL;
}
return E_UNEXPECTED;
@@ -814,8 +890,7 @@ HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl,
PCUITEMID_C
if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2))
&& (cidl >= 1))
{
- // FIXME: Handle multiple items
- hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(apidl[0], riid,
&pObj);
+ hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(cidl, apidl, riid,
&pObj);
}
else if((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW))
&& (cidl == 1))
{
diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h
index 3a70cb606c8..2d9e2776d6a 100644
--- a/dll/win32/shell32/precomp.h
+++ b/dll/win32/shell32/precomp.h
@@ -322,4 +322,16 @@ InvokeIExecuteCommandWithDataObject(
_In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
_In_opt_ IUnknown *pSite);
+typedef enum {
+ FOCE_STARTOPERATIONS,
+ FOCE_FINISHOPERATIONS,
+ FOCE_PREMOVEITEM,
+ FOCE_POSTMOVEITEM,
+ FOCE_PREDELETEITEM,
+ FOCE_POSTDELETEITEM
+} FILEOPCALLBACKEVENT;
+typedef HRESULT (CALLBACK *FILEOPCALLBACK)(FILEOPCALLBACKEVENT Event, LPCWSTR Source,
LPCWSTR Destination,
+ UINT Attributes, HRESULT hr, void
*CallerData);
+int SHELL32_FileOperation(LPSHFILEOPSTRUCTW lpFileOp, FILEOPCALLBACK Callback, void
*CallerData);
+
#endif /* _PRECOMP_H__ */
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin.c
b/dll/win32/shell32/shellrecyclebin/recyclebin.c
index 5a0153bacf5..890b625ea4b 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin.c
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin.c
@@ -93,7 +93,7 @@ BOOL WINAPI
DeleteFileInRecycleBin(
IN HDELFILE hDeletedFile)
{
- IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
+ IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDeletedFile);
HRESULT hr;
TRACE("(%p)\n", hDeletedFile);
@@ -283,11 +283,26 @@ GetRecycleBinFileHandle(
return context.hDelFile;
}
+EXTERN_C BOOL
+RemoveFromRecycleBinDatabase(
+ IN const RECYCLEBINFILEIDENTITY *pFI)
+{
+ BOOL ret = FALSE;
+ HDELFILE hDelFile = GetRecycleBinFileHandle(NULL, pFI);
+ if (hDelFile)
+ {
+ IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDelFile);
+ ret = SUCCEEDED(IRecycleBinFile_RemoveFromDatabase(rbf));
+ CloseRecycleBinHandle(hDelFile);
+ }
+ return ret;
+}
+
BOOL WINAPI
RestoreFileFromRecycleBin(
IN HDELFILE hDeletedFile)
{
- IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
+ IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDeletedFile);
HRESULT hr;
TRACE("(%p)\n", hDeletedFile);
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin.h
b/dll/win32/shell32/shellrecyclebin/recyclebin.h
index b3795b04ebc..4746d0857ef 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin.h
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin.h
@@ -155,6 +155,10 @@ GetRecycleBinFileHandle(
IN LPCWSTR pszRoot OPTIONAL,
IN const RECYCLEBINFILEIDENTITY *pFI);
+EXTERN_C BOOL
+RemoveFromRecycleBinDatabase(
+ IN const RECYCLEBINFILEIDENTITY *pFI);
+
/* Restores a deleted file
* hDeletedFile: handle of the deleted file to restore
* Returns TRUE if operation succeeded, FALSE otherwise.
@@ -187,6 +191,7 @@ DECLARE_INTERFACE_(IRecycleBinFile, IUnknown)
STDMETHOD(GetFileName)(THIS_ SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize)
PURE;
STDMETHOD(Delete)(THIS) PURE;
STDMETHOD(Restore)(THIS) PURE;
+ STDMETHOD(RemoveFromDatabase)(THIS) PURE;
END_INTERFACE
};
@@ -262,6 +267,8 @@ EXTERN_C const IID IID_IRecycleBin;
(This)->lpVtbl->Delete(This)
#define IRecycleBinFile_Restore(This) \
(This)->lpVtbl->Restore(This)
+#define IRecycleBinFile_RemoveFromDatabase(This) \
+ (This)->lpVtbl->RemoveFromDatabase(This)
#define IRecycleBinEnumList_QueryInterface(This, riid, ppvObject) \
(This)->lpVtbl->QueryInterface(This, riid, ppvObject)
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp
b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp
index b040851d011..c32f9c8d630 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp
@@ -158,6 +158,9 @@ public:
STDMETHODIMP Restore(
_In_ LPCWSTR pDeletedFileName,
_In_ DELETED_FILE_RECORD *pDeletedFile) override;
+ STDMETHODIMP RemoveFromDatabase(
+ _In_ LPCWSTR pDeletedFileName,
+ _In_ DELETED_FILE_RECORD *pDeletedFile) override;
STDMETHODIMP OnClosing(_In_ IRecycleBinEnumList *prbel) override;
protected:
@@ -454,62 +457,38 @@ STDMETHODIMP RecycleBin5::Delete(
_In_ LPCWSTR pDeletedFileName,
_In_ DELETED_FILE_RECORD *pDeletedFile)
{
- ULARGE_INTEGER FileSize;
- PINFO2_HEADER pHeader;
- DELETED_FILE_RECORD *pRecord, *pLast;
- DWORD dwEntries, i;
TRACE("(%p, %s, %p)\n", this, debugstr_w(pDeletedFileName), pDeletedFile);
- if (m_EnumeratorCount != 0)
- return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
+ int res = IntDeleteRecursive(pDeletedFileName);
+ if (!res)
+ return HResultFromWin32(GetLastError());
+ res = RemoveFromDatabase(pDeletedFileName, pDeletedFile);
+ if (res == 0)
+ SHUpdateRecycleBinIcon(); // Full --> Empty
+ return res;
+}
- pHeader = (PINFO2_HEADER)MapViewOfFile(m_hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
- if (!pHeader)
- return HRESULT_FROM_WIN32(GetLastError());
+STDMETHODIMP RecycleBin5::Restore(
+ _In_ LPCWSTR pDeletedFileName,
+ _In_ DELETED_FILE_RECORD *pDeletedFile)
+{
- FileSize.u.LowPart = GetFileSize(m_hInfo, &FileSize.u.HighPart);
- if (FileSize.u.LowPart == 0)
- {
- UnmapViewOfFile(pHeader);
- return HRESULT_FROM_WIN32(GetLastError());
- }
- dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) /
sizeof(DELETED_FILE_RECORD));
+ TRACE("(%p, %s, %p)\n", this, debugstr_w(pDeletedFileName), pDeletedFile);
- pRecord = (DELETED_FILE_RECORD *)(pHeader + 1);
- for (i = 0; i < dwEntries; i++)
+ int res = SHELL_SingleFileOperation(NULL, FO_MOVE, pDeletedFileName,
pDeletedFile->FileNameW, 0);
+ if (res)
{
- if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId)
- {
- /* Delete file */
- if (!IntDeleteRecursive(pDeletedFileName))
- {
- UnmapViewOfFile(pHeader);
- return HRESULT_FROM_WIN32(GetLastError());
- }
-
- /* Clear last entry in the file */
- MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) *
sizeof(DELETED_FILE_RECORD));
- pLast = pRecord + (dwEntries - i - 1);
- ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD));
- UnmapViewOfFile(pHeader);
-
- /* Resize file */
- CloseHandle(m_hInfoMapped);
- SetFilePointer(m_hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END);
- SetEndOfFile(m_hInfo);
- m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE |
SEC_COMMIT, 0, 0, NULL);
- if (!m_hInfoMapped)
- return HRESULT_FROM_WIN32(GetLastError());
- return S_OK;
- }
- pRecord++;
+ ERR("SHFileOperationW failed with 0x%x\n", res);
+ return E_FAIL;
}
- UnmapViewOfFile(pHeader);
- return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ res = RemoveFromDatabase(pDeletedFileName, pDeletedFile);
+ if (res == 0)
+ SHUpdateRecycleBinIcon(); // Full --> Empty
+ return res;
}
-STDMETHODIMP RecycleBin5::Restore(
+STDMETHODIMP RecycleBin5::RemoveFromDatabase(
_In_ LPCWSTR pDeletedFileName,
_In_ DELETED_FILE_RECORD *pDeletedFile)
{
@@ -517,7 +496,6 @@ STDMETHODIMP RecycleBin5::Restore(
PINFO2_HEADER pHeader;
DELETED_FILE_RECORD *pRecord, *pLast;
DWORD dwEntries, i;
- int res;
TRACE("(%p, %s, %p)\n", this, debugstr_w(pDeletedFileName), pDeletedFile);
@@ -541,14 +519,6 @@ STDMETHODIMP RecycleBin5::Restore(
{
if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId)
{
- res = SHELL_SingleFileOperation(NULL, FO_MOVE, pDeletedFileName,
pDeletedFile->FileNameW, 0);
- if (res)
- {
- ERR("SHFileOperationW failed with 0x%x\n", res);
- UnmapViewOfFile(pHeader);
- return E_FAIL;
- }
-
/* Clear last entry in the file */
MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) *
sizeof(DELETED_FILE_RECORD));
pLast = pRecord + (dwEntries - i - 1);
@@ -561,10 +531,9 @@ STDMETHODIMP RecycleBin5::Restore(
SetEndOfFile(m_hInfo);
m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE |
SEC_COMMIT, 0, 0, NULL);
if (!m_hInfoMapped)
- return HRESULT_FROM_WIN32(GetLastError());
- if (dwEntries == 1)
- SHUpdateRecycleBinIcon(); // Full --> Empty
- return S_OK;
+ return HResultFromWin32(GetLastError());
+ dwEntries--;
+ return FAILED((int)dwEntries) ? INT_MAX : dwEntries;
}
pRecord++;
}
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h
b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h
index 2461e0bdbca..a55286bc7a2 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h
@@ -52,6 +52,10 @@ DECLARE_INTERFACE_(IRecycleBin5, IRecycleBin)
THIS_
IN LPCWSTR pDeletedFileName,
IN DELETED_FILE_RECORD *pDeletedFile) PURE;
+ STDMETHOD(RemoveFromDatabase)(
+ THIS_
+ IN LPCWSTR pDeletedFileName,
+ IN DELETED_FILE_RECORD *pDeletedFile) PURE;
STDMETHOD(OnClosing)(
THIS_
IN IRecycleBinEnumList *prbel) PURE;
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp
b/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp
index ae5c6d5e5e6..b9f1ff74a63 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp
@@ -42,6 +42,7 @@ public:
STDMETHODIMP GetFileName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize)
override;
STDMETHODIMP Delete() override;
STDMETHODIMP Restore() override;
+ STDMETHODIMP RemoveFromDatabase() override;
protected:
LONG m_ref;
@@ -226,6 +227,12 @@ STDMETHODIMP RecycleBin5File::Restore()
return m_recycleBin->Restore(m_FullName, &m_deletedFile);
}
+STDMETHODIMP RecycleBin5File::RemoveFromDatabase()
+{
+ TRACE("(%p)\n", this);
+ return m_recycleBin->RemoveFromDatabase(m_FullName, &m_deletedFile);
+}
+
RecycleBin5File::RecycleBin5File()
: m_ref(1)
, m_recycleBin(NULL)
diff --git a/dll/win32/shell32/shlfileop.cpp b/dll/win32/shell32/shlfileop.cpp
index f26d54ecc68..eea83780694 100644
--- a/dll/win32/shell32/shlfileop.cpp
+++ b/dll/win32/shell32/shlfileop.cpp
@@ -45,6 +45,8 @@ typedef struct
ULARGE_INTEGER completedSize;
ULARGE_INTEGER totalSize;
WCHAR szBuilderString[50];
+ FILEOPCALLBACK Callback;
+ void *CallerCallbackData;
} FILE_OPERATION;
#define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
@@ -361,6 +363,19 @@ EXTERN_C HRESULT WINAPI SHIsFileAvailableOffline(LPCWSTR path,
LPDWORD status)
return E_FAIL;
}
+static HRESULT FileOpCallback(FILE_OPERATION *op, FILEOPCALLBACKEVENT Event, LPCWSTR
Source,
+ LPCWSTR Destination, UINT Attributes, HRESULT hrOp = S_OK)
+{
+ HRESULT hr = S_OK;
+ if (op->Callback)
+ {
+ hr = op->Callback(Event, Source, Destination, Attributes, hrOp,
op->CallerCallbackData);
+ if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
+ op->bCancelled = TRUE;
+ }
+ return hr;
+}
+
/**************************************************************************
* SHELL_DeleteDirectory() [internal]
*
@@ -380,6 +395,9 @@ BOOL SHELL_DeleteDirectoryW(FILE_OPERATION *op, LPCWSTR pszDir, BOOL
bShowUI)
if (hFind == INVALID_HANDLE_VALUE)
return FALSE;
+ if (FAILED(FileOpCallback(op, FOCE_PREDELETEITEM, pszDir, NULL,
wfd.dwFileAttributes)))
+ return FALSE;
+
if (!bShowUI || (ret = SHELL_ConfirmDialogW(op->req->hwnd, ASK_DELETE_FOLDER,
pszDir, NULL)))
{
do
@@ -399,6 +417,7 @@ BOOL SHELL_DeleteDirectoryW(FILE_OPERATION *op, LPCWSTR pszDir, BOOL
bShowUI)
FindClose(hFind);
if (ret)
ret = (SHNotifyRemoveDirectoryW(pszDir) == ERROR_SUCCESS);
+ FileOpCallback(op, FOCE_POSTDELETEITEM, pszDir, NULL, wfd.dwFileAttributes, ret ?
S_OK : E_FAIL);
return ret;
}
@@ -622,7 +641,11 @@ static DWORD SHNotifyDeleteFileW(FILE_OPERATION *op, LPCWSTR path)
tmp.u.HighPart = wfd.nFileSizeHigh;
FileSize.QuadPart = tmp.QuadPart;
}
+ UINT attrib = hFile != INVALID_HANDLE_VALUE ? wfd.dwFileAttributes : 0;
+ BOOL aborted = FAILED(FileOpCallback(op, FOCE_PREDELETEITEM, path, NULL, attrib));
FindClose(hFile);
+ if (aborted)
+ return ERROR_CANCELLED;
ret = DeleteFileW(path);
if (!ret)
@@ -633,6 +656,7 @@ static DWORD SHNotifyDeleteFileW(FILE_OPERATION *op, LPCWSTR path)
if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_SYSTEM)))
ret = DeleteFileW(path);
}
+ FileOpCallback(op, FOCE_POSTDELETEITEM, path, NULL, attrib, ret ? S_OK : E_FAIL);
if (ret)
{
// Bit of a hack to make the progress bar move. We don't have progress inside
the file, so inform when done.
@@ -720,6 +744,10 @@ static DWORD SHNotifyMoveFileW(FILE_OPERATION *op, LPCWSTR src,
LPCWSTR dest, BO
_SetOperationTexts(op, src, dest);
+ UINT attrib = isdir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
+ if (FAILED(FileOpCallback(op, FOCE_PREMOVEITEM, src, dest, attrib)))
+ return ERROR_CANCELLED;
+
ret = MoveFileWithProgressW(src, dest, SHCopyProgressRoutine, op,
MOVEFILE_REPLACE_EXISTING);
/* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
@@ -740,6 +768,7 @@ static DWORD SHNotifyMoveFileW(FILE_OPERATION *op, LPCWSTR src,
LPCWSTR dest, BO
ret = MoveFileW(src, dest);
}
}
+ FileOpCallback(op, FOCE_POSTMOVEITEM, src, dest, attrib, ret ? S_OK : E_FAIL);
if (ret)
{
SHChangeNotify(isdir ? SHCNE_MKDIR : SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
@@ -1691,6 +1720,10 @@ static void move_dir_to_dir(FILE_OPERATION *op, const FILE_ENTRY
*feFrom, LPCWST
if (feFrom->szFilename && IsDotDir(feFrom->szFilename))
return;
+ UINT attrib = FILE_ATTRIBUTE_DIRECTORY;
+ if (FAILED(FileOpCallback(op, FOCE_PREMOVEITEM, feFrom->szFullPath, szDestPath,
attrib)))
+ return;
+
SHNotifyCreateDirectoryW(szDestPath, NULL);
PathCombineW(szFrom, feFrom->szFullPath, L"*.*");
@@ -1709,8 +1742,10 @@ static void move_dir_to_dir(FILE_OPERATION *op, const FILE_ENTRY
*feFrom, LPCWST
destroy_file_list(&flFromNew);
destroy_file_list(&flToNew);
+ BOOL success = FALSE;
if (PathIsDirectoryEmptyW(feFrom->szFullPath))
- Win32RemoveDirectoryW(feFrom->szFullPath);
+ success = Win32RemoveDirectoryW(feFrom->szFullPath);
+ FileOpCallback(op, FOCE_POSTMOVEITEM, feFrom->szFullPath, szDestPath, attrib,
success ? S_OK : E_FAIL);
}
static BOOL move_file_to_file(FILE_OPERATION *op, const WCHAR *szFrom, const WCHAR
*szTo)
@@ -1982,12 +2017,7 @@ validate_operation(LPSHFILEOPSTRUCTW lpFileOp, FILE_LIST *flFrom,
FILE_LIST *flT
return ERROR_SUCCESS;
}
-/*************************************************************************
- * SHFileOperationW [SHELL32.@]
- *
- * See SHFileOperationA
- */
-int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
+int SHELL32_FileOperation(LPSHFILEOPSTRUCTW lpFileOp, FILEOPCALLBACK Callback, void
*CallerData)
{
FILE_OPERATION op;
FILE_LIST flFrom, flTo;
@@ -2017,6 +2047,8 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
op.totalSize.QuadPart = 0ull;
op.completedSize.QuadPart = 0ull;
op.bManyItems = (flFrom.dwNumFiles > 1);
+ op.Callback = Callback;
+ op.CallerCallbackData = CallerData;
ret = validate_operation(lpFileOp, &flFrom, &flTo);
if (ret)
@@ -2035,6 +2067,8 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
_FileOpCountManager(&op, &flFrom);
}
+ FileOpCallback(&op, FOCE_STARTOPERATIONS, NULL, NULL, 0);
+
switch (lpFileOp->wFunc)
{
case FO_COPY:
@@ -2070,11 +2104,23 @@ cleanup:
if (ret == ERROR_CANCELLED)
lpFileOp->fAnyOperationsAborted = TRUE;
+ FileOpCallback(&op, FOCE_FINISHOPERATIONS, NULL, NULL, 0,
HRESULT_FROM_WIN32(ret));
+
CoUninitialize();
return ret;
}
+/*************************************************************************
+ * SHFileOperationW [SHELL32.@]
+ *
+ * See SHFileOperationA
+ */
+int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
+{
+ return SHELL32_FileOperation(lpFileOp, NULL, NULL);
+}
+
// Used by SHFreeNameMappings
static int CALLBACK _DestroyCallback(void *p, void *pData)
{
diff --git a/dll/win32/shell32/utils.h b/dll/win32/shell32/utils.h
index 5591eb6a463..a4effec8964 100644
--- a/dll/win32/shell32/utils.h
+++ b/dll/win32/shell32/utils.h
@@ -14,6 +14,14 @@ SHStrDupW(LPCWSTR Src)
LPWSTR Dup;
return SUCCEEDED(SHStrDupW(Src, &Dup)) ? Dup : NULL;
}
+
+static inline UINT
+SHELL_ErrorBox(CMINVOKECOMMANDINFO &cmi, UINT Error)
+{
+ if (cmi.fMask & CMIC_MASK_FLAG_NO_UI)
+ return Error ? Error : ERROR_INTERNAL_ERROR;
+ return SHELL_ErrorBox(cmi.hwnd, Error);
+}
#endif
static inline BOOL
@@ -46,7 +54,8 @@ RegSetString(HKEY hKey, LPCWSTR Name, LPCWSTR Str, DWORD Type = REG_SZ)
return RegSetValueExW(hKey, Name, 0, Type, LPBYTE(Str), (lstrlenW(Str) + 1) *
sizeof(WCHAR));
}
-typedef struct {
+typedef struct
+{
LPCSTR Verb;
WORD CmdId;
} CMVERBMAP;