[SHELL32] Handle multiple files in recycle bin delete/restore operations (#7568)

CORE-19895 CORE-19231
This commit is contained in:
Whindmar Saksit 2025-02-22 13:03:11 +01:00 committed by GitHub
parent 924592dc47
commit e5fc4de8c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 264 additions and 119 deletions

View file

@ -1297,7 +1297,8 @@ VOID COpenWithMenu::AddApp(PVOID pApp)
m_idCmdLast++;
}
static const CMVERBMAP g_VerbMap[] = {
static const CMVERBMAP g_VerbMap[] =
{
{ "openas", 0 },
{ NULL }
};

View file

@ -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))
{

View file

@ -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__ */

View file

@ -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);

View file

@ -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)

View file

@ -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,70 +457,45 @@ 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);
pHeader = (PINFO2_HEADER)MapViewOfFile(m_hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
if (!pHeader)
return HRESULT_FROM_WIN32(GetLastError());
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));
pRecord = (DELETED_FILE_RECORD *)(pHeader + 1);
for (i = 0; i < dwEntries; i++)
{
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++;
}
UnmapViewOfFile(pHeader);
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
int res = IntDeleteRecursive(pDeletedFileName);
if (!res)
return HResultFromWin32(GetLastError());
res = RemoveFromDatabase(pDeletedFileName, pDeletedFile);
if (res == 0)
SHUpdateRecycleBinIcon(); // Full --> Empty
return res;
}
STDMETHODIMP RecycleBin5::Restore(
_In_ LPCWSTR pDeletedFileName,
_In_ DELETED_FILE_RECORD *pDeletedFile)
{
TRACE("(%p, %s, %p)\n", this, debugstr_w(pDeletedFileName), pDeletedFile);
int res = SHELL_SingleFileOperation(NULL, FO_MOVE, pDeletedFileName, pDeletedFile->FileNameW, 0);
if (res)
{
ERR("SHFileOperationW failed with 0x%x\n", res);
return E_FAIL;
}
res = RemoveFromDatabase(pDeletedFileName, pDeletedFile);
if (res == 0)
SHUpdateRecycleBinIcon(); // Full --> Empty
return res;
}
STDMETHODIMP RecycleBin5::RemoveFromDatabase(
_In_ LPCWSTR pDeletedFileName,
_In_ DELETED_FILE_RECORD *pDeletedFile)
{
ULARGE_INTEGER FileSize;
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++;
}

View file

@ -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;

View file

@ -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)

View file

@ -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)
{

View file

@ -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;