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;