mirror of
https://github.com/reactos/reactos.git
synced 2025-08-07 23:43:07 +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++;
|
m_idCmdLast++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const CMVERBMAP g_VerbMap[] = {
|
static const CMVERBMAP g_VerbMap[] =
|
||||||
|
{
|
||||||
{ "openas", 0 },
|
{ "openas", 0 },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
|
@ -210,12 +210,6 @@ static HRESULT GetItemTypeName(PCUITEMID_CHILD pidl, const BBITEMDATA &Data, SHF
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HDELFILE GetRecycleBinFileHandleFromItem(const BBITEMDATA &Data)
|
|
||||||
{
|
|
||||||
RECYCLEBINFILEIDENTITY identity = { Data.DeletionTime, GetItemRecycledFullPath(Data) };
|
|
||||||
return GetRecycleBinFileHandle(NULL, &identity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recycle Bin folder
|
* Recycle Bin folder
|
||||||
*/
|
*/
|
||||||
|
@ -285,14 +279,6 @@ EXTERN_C void CRecycleBin_NotifyRecycled(LPCWSTR OrigPath, const WIN32_FIND_DATA
|
||||||
static void CRecycleBin_NotifyRemovedFromRecycleBin(LPCITEMIDLIST BBItem)
|
static void CRecycleBin_NotifyRemovedFromRecycleBin(LPCITEMIDLIST BBItem)
|
||||||
{
|
{
|
||||||
CRecycleBin_ChangeNotifyBBItem(IsFolder(BBItem) ? SHCNE_RMDIR : SHCNE_DELETE, 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(
|
static HRESULT CRecyclerExtractIcon_CreateInstance(
|
||||||
|
@ -316,11 +302,12 @@ class CRecycleBinItemContextMenu :
|
||||||
public IContextMenu2
|
public IContextMenu2
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
LPITEMIDLIST apidl;
|
PITEMID_CHILD* m_apidl;
|
||||||
|
UINT m_cidl;
|
||||||
public:
|
public:
|
||||||
CRecycleBinItemContextMenu();
|
CRecycleBinItemContextMenu();
|
||||||
~CRecycleBinItemContextMenu();
|
virtual ~CRecycleBinItemContextMenu();
|
||||||
HRESULT WINAPI Initialize(LPCITEMIDLIST pidl);
|
HRESULT WINAPI Initialize(UINT cidl, PCUITEMID_CHILD_ARRAY apidl);
|
||||||
|
|
||||||
// IContextMenu
|
// IContextMenu
|
||||||
STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override;
|
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()
|
CRecycleBinItemContextMenu::CRecycleBinItemContextMenu()
|
||||||
{
|
{
|
||||||
apidl = NULL;
|
m_apidl = NULL;
|
||||||
|
m_cidl = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CRecycleBinItemContextMenu::~CRecycleBinItemContextMenu()
|
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);
|
m_apidl = _ILCopyaPidl(apidl, cidl);
|
||||||
if (apidl == NULL)
|
if (m_apidl == NULL)
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
|
m_cidl = cidl;
|
||||||
return S_OK;
|
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;
|
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)
|
if (lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)
|
||||||
{
|
{
|
||||||
return TRUE;
|
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;
|
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);
|
wsprintfW(buf, L"%d", cidl);
|
||||||
return SHELL_ConfirmYesNoW(lpcmi->hwnd, ASK_DELETE_MULTIPLE_ITEM, buf);
|
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)
|
HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
|
||||||
{
|
{
|
||||||
TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi, lpcmi->lpVerb, lpcmi->hwnd);
|
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)
|
if (CmdId == IDC_BB_RESTORE || CmdId == IDC_BB_DELETE)
|
||||||
{
|
{
|
||||||
BBITEMDATA *pData = ValidateItem(apidl);
|
HRESULT hr = S_OK;
|
||||||
if (!pData && FAILED_UNEXPECTEDLY(E_FAIL))
|
if (CmdId == IDC_BB_DELETE && !ConfirmDelete(lpcmi, m_cidl, m_apidl[0]))
|
||||||
return E_FAIL;
|
return S_OK;
|
||||||
HDELFILE hDelFile = GetRecycleBinFileHandleFromItem(*pData);
|
|
||||||
if (!hDelFile && FAILED_UNEXPECTEDLY(E_FAIL))
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
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)
|
if (CmdId == IDC_BB_RESTORE)
|
||||||
hr = RestoreFileFromRecycleBin(hDelFile) ? S_OK : E_FAIL;
|
{
|
||||||
else if (ConfirmDelete(lpcmi, 1, apidl, *pData))
|
pszzDst = CreateFileOpStrings(m_cidl, m_apidl, FALSE);
|
||||||
hr = DeleteFileInRecycleBin(hDelFile) ? S_OK : E_FAIL;
|
if (!pszzDst)
|
||||||
|
hr = E_OUTOFMEMORY;
|
||||||
if (hr == S_OK)
|
shfos.wFunc = FO_MOVE;
|
||||||
CRecycleBin_NotifyRemovedFromRecycleBin(apidl);
|
shfos.pTo = pszzDst;
|
||||||
|
shfos.fFlags |= FOF_MULTIDESTFILES;
|
||||||
CloseRecycleBinHandle(hDelFile);
|
}
|
||||||
|
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;
|
return hr;
|
||||||
}
|
}
|
||||||
else if (CmdId == IDC_BB_CUT)
|
else if (CmdId == IDC_BB_CUT)
|
||||||
{
|
{
|
||||||
FIXME("implement cut\n");
|
FIXME("implement cut\n");
|
||||||
SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED);
|
SHELL_ErrorBox(*lpcmi, ERROR_NOT_SUPPORTED);
|
||||||
return E_NOTIMPL;
|
return E_NOTIMPL;
|
||||||
}
|
}
|
||||||
else if (CmdId == IDC_BB_PROPERTIES)
|
else if (CmdId == IDC_BB_PROPERTIES)
|
||||||
{
|
{
|
||||||
FIXME("implement properties\n");
|
FIXME("implement properties\n");
|
||||||
SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED);
|
SHELL_ErrorBox(*lpcmi, ERROR_NOT_SUPPORTED);
|
||||||
return E_NOTIMPL;
|
return E_NOTIMPL;
|
||||||
}
|
}
|
||||||
return E_UNEXPECTED;
|
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))
|
if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2)) && (cidl >= 1))
|
||||||
{
|
{
|
||||||
// FIXME: Handle multiple items
|
hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(cidl, apidl, riid, &pObj);
|
||||||
hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(apidl[0], riid, &pObj);
|
|
||||||
}
|
}
|
||||||
else if((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1))
|
else if((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1))
|
||||||
{
|
{
|
||||||
|
|
|
@ -322,4 +322,16 @@ InvokeIExecuteCommandWithDataObject(
|
||||||
_In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
|
_In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
|
||||||
_In_opt_ IUnknown *pSite);
|
_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__ */
|
#endif /* _PRECOMP_H__ */
|
||||||
|
|
|
@ -93,7 +93,7 @@ BOOL WINAPI
|
||||||
DeleteFileInRecycleBin(
|
DeleteFileInRecycleBin(
|
||||||
IN HDELFILE hDeletedFile)
|
IN HDELFILE hDeletedFile)
|
||||||
{
|
{
|
||||||
IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
|
IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDeletedFile);
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
TRACE("(%p)\n", hDeletedFile);
|
TRACE("(%p)\n", hDeletedFile);
|
||||||
|
@ -283,11 +283,26 @@ GetRecycleBinFileHandle(
|
||||||
return context.hDelFile;
|
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
|
BOOL WINAPI
|
||||||
RestoreFileFromRecycleBin(
|
RestoreFileFromRecycleBin(
|
||||||
IN HDELFILE hDeletedFile)
|
IN HDELFILE hDeletedFile)
|
||||||
{
|
{
|
||||||
IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
|
IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDeletedFile);
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
TRACE("(%p)\n", hDeletedFile);
|
TRACE("(%p)\n", hDeletedFile);
|
||||||
|
|
|
@ -155,6 +155,10 @@ GetRecycleBinFileHandle(
|
||||||
IN LPCWSTR pszRoot OPTIONAL,
|
IN LPCWSTR pszRoot OPTIONAL,
|
||||||
IN const RECYCLEBINFILEIDENTITY *pFI);
|
IN const RECYCLEBINFILEIDENTITY *pFI);
|
||||||
|
|
||||||
|
EXTERN_C BOOL
|
||||||
|
RemoveFromRecycleBinDatabase(
|
||||||
|
IN const RECYCLEBINFILEIDENTITY *pFI);
|
||||||
|
|
||||||
/* Restores a deleted file
|
/* Restores a deleted file
|
||||||
* hDeletedFile: handle of the deleted file to restore
|
* hDeletedFile: handle of the deleted file to restore
|
||||||
* Returns TRUE if operation succeeded, FALSE otherwise.
|
* 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(GetFileName)(THIS_ SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) PURE;
|
||||||
STDMETHOD(Delete)(THIS) PURE;
|
STDMETHOD(Delete)(THIS) PURE;
|
||||||
STDMETHOD(Restore)(THIS) PURE;
|
STDMETHOD(Restore)(THIS) PURE;
|
||||||
|
STDMETHOD(RemoveFromDatabase)(THIS) PURE;
|
||||||
|
|
||||||
END_INTERFACE
|
END_INTERFACE
|
||||||
};
|
};
|
||||||
|
@ -262,6 +267,8 @@ EXTERN_C const IID IID_IRecycleBin;
|
||||||
(This)->lpVtbl->Delete(This)
|
(This)->lpVtbl->Delete(This)
|
||||||
#define IRecycleBinFile_Restore(This) \
|
#define IRecycleBinFile_Restore(This) \
|
||||||
(This)->lpVtbl->Restore(This)
|
(This)->lpVtbl->Restore(This)
|
||||||
|
#define IRecycleBinFile_RemoveFromDatabase(This) \
|
||||||
|
(This)->lpVtbl->RemoveFromDatabase(This)
|
||||||
|
|
||||||
#define IRecycleBinEnumList_QueryInterface(This, riid, ppvObject) \
|
#define IRecycleBinEnumList_QueryInterface(This, riid, ppvObject) \
|
||||||
(This)->lpVtbl->QueryInterface(This, riid, ppvObject)
|
(This)->lpVtbl->QueryInterface(This, riid, ppvObject)
|
||||||
|
|
|
@ -158,6 +158,9 @@ public:
|
||||||
STDMETHODIMP Restore(
|
STDMETHODIMP Restore(
|
||||||
_In_ LPCWSTR pDeletedFileName,
|
_In_ LPCWSTR pDeletedFileName,
|
||||||
_In_ DELETED_FILE_RECORD *pDeletedFile) override;
|
_In_ DELETED_FILE_RECORD *pDeletedFile) override;
|
||||||
|
STDMETHODIMP RemoveFromDatabase(
|
||||||
|
_In_ LPCWSTR pDeletedFileName,
|
||||||
|
_In_ DELETED_FILE_RECORD *pDeletedFile) override;
|
||||||
STDMETHODIMP OnClosing(_In_ IRecycleBinEnumList *prbel) override;
|
STDMETHODIMP OnClosing(_In_ IRecycleBinEnumList *prbel) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -454,70 +457,45 @@ STDMETHODIMP RecycleBin5::Delete(
|
||||||
_In_ LPCWSTR pDeletedFileName,
|
_In_ LPCWSTR pDeletedFileName,
|
||||||
_In_ DELETED_FILE_RECORD *pDeletedFile)
|
_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);
|
TRACE("(%p, %s, %p)\n", this, debugstr_w(pDeletedFileName), pDeletedFile);
|
||||||
|
|
||||||
if (m_EnumeratorCount != 0)
|
int res = IntDeleteRecursive(pDeletedFileName);
|
||||||
return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
|
if (!res)
|
||||||
|
return HResultFromWin32(GetLastError());
|
||||||
pHeader = (PINFO2_HEADER)MapViewOfFile(m_hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
|
res = RemoveFromDatabase(pDeletedFileName, pDeletedFile);
|
||||||
if (!pHeader)
|
if (res == 0)
|
||||||
return HRESULT_FROM_WIN32(GetLastError());
|
SHUpdateRecycleBinIcon(); // Full --> Empty
|
||||||
|
return res;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
STDMETHODIMP RecycleBin5::Restore(
|
STDMETHODIMP RecycleBin5::Restore(
|
||||||
_In_ LPCWSTR pDeletedFileName,
|
_In_ LPCWSTR pDeletedFileName,
|
||||||
_In_ DELETED_FILE_RECORD *pDeletedFile)
|
_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;
|
ULARGE_INTEGER FileSize;
|
||||||
PINFO2_HEADER pHeader;
|
PINFO2_HEADER pHeader;
|
||||||
DELETED_FILE_RECORD *pRecord, *pLast;
|
DELETED_FILE_RECORD *pRecord, *pLast;
|
||||||
DWORD dwEntries, i;
|
DWORD dwEntries, i;
|
||||||
int res;
|
|
||||||
|
|
||||||
TRACE("(%p, %s, %p)\n", this, debugstr_w(pDeletedFileName), pDeletedFile);
|
TRACE("(%p, %s, %p)\n", this, debugstr_w(pDeletedFileName), pDeletedFile);
|
||||||
|
|
||||||
|
@ -541,14 +519,6 @@ STDMETHODIMP RecycleBin5::Restore(
|
||||||
{
|
{
|
||||||
if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId)
|
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 */
|
/* Clear last entry in the file */
|
||||||
MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD));
|
MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD));
|
||||||
pLast = pRecord + (dwEntries - i - 1);
|
pLast = pRecord + (dwEntries - i - 1);
|
||||||
|
@ -561,10 +531,9 @@ STDMETHODIMP RecycleBin5::Restore(
|
||||||
SetEndOfFile(m_hInfo);
|
SetEndOfFile(m_hInfo);
|
||||||
m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
|
m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
|
||||||
if (!m_hInfoMapped)
|
if (!m_hInfoMapped)
|
||||||
return HRESULT_FROM_WIN32(GetLastError());
|
return HResultFromWin32(GetLastError());
|
||||||
if (dwEntries == 1)
|
dwEntries--;
|
||||||
SHUpdateRecycleBinIcon(); // Full --> Empty
|
return FAILED((int)dwEntries) ? INT_MAX : dwEntries;
|
||||||
return S_OK;
|
|
||||||
}
|
}
|
||||||
pRecord++;
|
pRecord++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,10 @@ DECLARE_INTERFACE_(IRecycleBin5, IRecycleBin)
|
||||||
THIS_
|
THIS_
|
||||||
IN LPCWSTR pDeletedFileName,
|
IN LPCWSTR pDeletedFileName,
|
||||||
IN DELETED_FILE_RECORD *pDeletedFile) PURE;
|
IN DELETED_FILE_RECORD *pDeletedFile) PURE;
|
||||||
|
STDMETHOD(RemoveFromDatabase)(
|
||||||
|
THIS_
|
||||||
|
IN LPCWSTR pDeletedFileName,
|
||||||
|
IN DELETED_FILE_RECORD *pDeletedFile) PURE;
|
||||||
STDMETHOD(OnClosing)(
|
STDMETHOD(OnClosing)(
|
||||||
THIS_
|
THIS_
|
||||||
IN IRecycleBinEnumList *prbel) PURE;
|
IN IRecycleBinEnumList *prbel) PURE;
|
||||||
|
|
|
@ -42,6 +42,7 @@ public:
|
||||||
STDMETHODIMP GetFileName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) override;
|
STDMETHODIMP GetFileName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) override;
|
||||||
STDMETHODIMP Delete() override;
|
STDMETHODIMP Delete() override;
|
||||||
STDMETHODIMP Restore() override;
|
STDMETHODIMP Restore() override;
|
||||||
|
STDMETHODIMP RemoveFromDatabase() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LONG m_ref;
|
LONG m_ref;
|
||||||
|
@ -226,6 +227,12 @@ STDMETHODIMP RecycleBin5File::Restore()
|
||||||
return m_recycleBin->Restore(m_FullName, &m_deletedFile);
|
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()
|
RecycleBin5File::RecycleBin5File()
|
||||||
: m_ref(1)
|
: m_ref(1)
|
||||||
, m_recycleBin(NULL)
|
, m_recycleBin(NULL)
|
||||||
|
|
|
@ -45,6 +45,8 @@ typedef struct
|
||||||
ULARGE_INTEGER completedSize;
|
ULARGE_INTEGER completedSize;
|
||||||
ULARGE_INTEGER totalSize;
|
ULARGE_INTEGER totalSize;
|
||||||
WCHAR szBuilderString[50];
|
WCHAR szBuilderString[50];
|
||||||
|
FILEOPCALLBACK Callback;
|
||||||
|
void *CallerCallbackData;
|
||||||
} FILE_OPERATION;
|
} FILE_OPERATION;
|
||||||
|
|
||||||
#define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
|
#define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
|
||||||
|
@ -361,6 +363,19 @@ EXTERN_C HRESULT WINAPI SHIsFileAvailableOffline(LPCWSTR path, LPDWORD status)
|
||||||
return E_FAIL;
|
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]
|
* SHELL_DeleteDirectory() [internal]
|
||||||
*
|
*
|
||||||
|
@ -380,6 +395,9 @@ BOOL SHELL_DeleteDirectoryW(FILE_OPERATION *op, LPCWSTR pszDir, BOOL bShowUI)
|
||||||
if (hFind == INVALID_HANDLE_VALUE)
|
if (hFind == INVALID_HANDLE_VALUE)
|
||||||
return FALSE;
|
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)))
|
if (!bShowUI || (ret = SHELL_ConfirmDialogW(op->req->hwnd, ASK_DELETE_FOLDER, pszDir, NULL)))
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
|
@ -399,6 +417,7 @@ BOOL SHELL_DeleteDirectoryW(FILE_OPERATION *op, LPCWSTR pszDir, BOOL bShowUI)
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
if (ret)
|
if (ret)
|
||||||
ret = (SHNotifyRemoveDirectoryW(pszDir) == ERROR_SUCCESS);
|
ret = (SHNotifyRemoveDirectoryW(pszDir) == ERROR_SUCCESS);
|
||||||
|
FileOpCallback(op, FOCE_POSTDELETEITEM, pszDir, NULL, wfd.dwFileAttributes, ret ? S_OK : E_FAIL);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,7 +641,11 @@ static DWORD SHNotifyDeleteFileW(FILE_OPERATION *op, LPCWSTR path)
|
||||||
tmp.u.HighPart = wfd.nFileSizeHigh;
|
tmp.u.HighPart = wfd.nFileSizeHigh;
|
||||||
FileSize.QuadPart = tmp.QuadPart;
|
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);
|
FindClose(hFile);
|
||||||
|
if (aborted)
|
||||||
|
return ERROR_CANCELLED;
|
||||||
|
|
||||||
ret = DeleteFileW(path);
|
ret = DeleteFileW(path);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
|
@ -633,6 +656,7 @@ static DWORD SHNotifyDeleteFileW(FILE_OPERATION *op, LPCWSTR path)
|
||||||
if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
|
if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
|
||||||
ret = DeleteFileW(path);
|
ret = DeleteFileW(path);
|
||||||
}
|
}
|
||||||
|
FileOpCallback(op, FOCE_POSTDELETEITEM, path, NULL, attrib, ret ? S_OK : E_FAIL);
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
// Bit of a hack to make the progress bar move. We don't have progress inside the file, so inform when done.
|
// 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);
|
_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);
|
ret = MoveFileWithProgressW(src, dest, SHCopyProgressRoutine, op, MOVEFILE_REPLACE_EXISTING);
|
||||||
|
|
||||||
/* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
|
/* 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);
|
ret = MoveFileW(src, dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
FileOpCallback(op, FOCE_POSTMOVEITEM, src, dest, attrib, ret ? S_OK : E_FAIL);
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
SHChangeNotify(isdir ? SHCNE_MKDIR : SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
|
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))
|
if (feFrom->szFilename && IsDotDir(feFrom->szFilename))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
UINT attrib = FILE_ATTRIBUTE_DIRECTORY;
|
||||||
|
if (FAILED(FileOpCallback(op, FOCE_PREMOVEITEM, feFrom->szFullPath, szDestPath, attrib)))
|
||||||
|
return;
|
||||||
|
|
||||||
SHNotifyCreateDirectoryW(szDestPath, NULL);
|
SHNotifyCreateDirectoryW(szDestPath, NULL);
|
||||||
|
|
||||||
PathCombineW(szFrom, feFrom->szFullPath, L"*.*");
|
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(&flFromNew);
|
||||||
destroy_file_list(&flToNew);
|
destroy_file_list(&flToNew);
|
||||||
|
|
||||||
|
BOOL success = FALSE;
|
||||||
if (PathIsDirectoryEmptyW(feFrom->szFullPath))
|
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)
|
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;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
int SHELL32_FileOperation(LPSHFILEOPSTRUCTW lpFileOp, FILEOPCALLBACK Callback, void *CallerData)
|
||||||
* SHFileOperationW [SHELL32.@]
|
|
||||||
*
|
|
||||||
* See SHFileOperationA
|
|
||||||
*/
|
|
||||||
int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
|
|
||||||
{
|
{
|
||||||
FILE_OPERATION op;
|
FILE_OPERATION op;
|
||||||
FILE_LIST flFrom, flTo;
|
FILE_LIST flFrom, flTo;
|
||||||
|
@ -2017,6 +2047,8 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
|
||||||
op.totalSize.QuadPart = 0ull;
|
op.totalSize.QuadPart = 0ull;
|
||||||
op.completedSize.QuadPart = 0ull;
|
op.completedSize.QuadPart = 0ull;
|
||||||
op.bManyItems = (flFrom.dwNumFiles > 1);
|
op.bManyItems = (flFrom.dwNumFiles > 1);
|
||||||
|
op.Callback = Callback;
|
||||||
|
op.CallerCallbackData = CallerData;
|
||||||
|
|
||||||
ret = validate_operation(lpFileOp, &flFrom, &flTo);
|
ret = validate_operation(lpFileOp, &flFrom, &flTo);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -2035,6 +2067,8 @@ int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
|
||||||
_FileOpCountManager(&op, &flFrom);
|
_FileOpCountManager(&op, &flFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileOpCallback(&op, FOCE_STARTOPERATIONS, NULL, NULL, 0);
|
||||||
|
|
||||||
switch (lpFileOp->wFunc)
|
switch (lpFileOp->wFunc)
|
||||||
{
|
{
|
||||||
case FO_COPY:
|
case FO_COPY:
|
||||||
|
@ -2070,11 +2104,23 @@ cleanup:
|
||||||
if (ret == ERROR_CANCELLED)
|
if (ret == ERROR_CANCELLED)
|
||||||
lpFileOp->fAnyOperationsAborted = TRUE;
|
lpFileOp->fAnyOperationsAborted = TRUE;
|
||||||
|
|
||||||
|
FileOpCallback(&op, FOCE_FINISHOPERATIONS, NULL, NULL, 0, HRESULT_FROM_WIN32(ret));
|
||||||
|
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* SHFileOperationW [SHELL32.@]
|
||||||
|
*
|
||||||
|
* See SHFileOperationA
|
||||||
|
*/
|
||||||
|
int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
|
||||||
|
{
|
||||||
|
return SHELL32_FileOperation(lpFileOp, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// Used by SHFreeNameMappings
|
// Used by SHFreeNameMappings
|
||||||
static int CALLBACK _DestroyCallback(void *p, void *pData)
|
static int CALLBACK _DestroyCallback(void *p, void *pData)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,6 +14,14 @@ SHStrDupW(LPCWSTR Src)
|
||||||
LPWSTR Dup;
|
LPWSTR Dup;
|
||||||
return SUCCEEDED(SHStrDupW(Src, &Dup)) ? Dup : NULL;
|
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
|
#endif
|
||||||
|
|
||||||
static inline BOOL
|
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));
|
return RegSetValueExW(hKey, Name, 0, Type, LPBYTE(Str), (lstrlenW(Str) + 1) * sizeof(WCHAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
LPCSTR Verb;
|
LPCSTR Verb;
|
||||||
WORD CmdId;
|
WORD CmdId;
|
||||||
} CMVERBMAP;
|
} CMVERBMAP;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue