https://git.reactos.org/?p=reactos.git;a=commitdiff;h=e5fc4de8c9254ccd1062b8309d2965627fb4c5b6

commit e5fc4de8c9254ccd1062b8309d2965627fb4c5b6
Author:     Whindmar Saksit <whinds...@proton.me>
AuthorDate: Sat Feb 22 13:03:11 2025 +0100
Commit:     GitHub <nore...@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;

Reply via email to