mirror of
https://github.com/reactos/reactos.git
synced 2025-08-07 14:03:31 +00:00
[SHELL32] Handle multiple files in recycle bin delete/restore operations (#7568)
CORE-19895 CORE-19231
This commit is contained in:
parent
924592dc47
commit
e5fc4de8c9
10 changed files with 264 additions and 119 deletions
|
@ -1297,7 +1297,8 @@ VOID COpenWithMenu::AddApp(PVOID pApp)
|
|||
m_idCmdLast++;
|
||||
}
|
||||
|
||||
static const CMVERBMAP g_VerbMap[] = {
|
||||
static const CMVERBMAP g_VerbMap[] =
|
||||
{
|
||||
{ "openas", 0 },
|
||||
{ NULL }
|
||||
};
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue