Skip to content

Commit e5fc4de

Browse files
authored
[SHELL32] Handle multiple files in recycle bin delete/restore operations (reactos#7568)
CORE-19895 CORE-19231
1 parent 924592d commit e5fc4de

File tree

10 files changed

+260
-115
lines changed

10 files changed

+260
-115
lines changed

dll/win32/shell32/COpenWithMenu.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,8 @@ VOID COpenWithMenu::AddApp(PVOID pApp)
12971297
m_idCmdLast++;
12981298
}
12991299

1300-
static const CMVERBMAP g_VerbMap[] = {
1300+
static const CMVERBMAP g_VerbMap[] =
1301+
{
13011302
{ "openas", 0 },
13021303
{ NULL }
13031304
};

dll/win32/shell32/folders/CRecycleBin.cpp

Lines changed: 120 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,6 @@ static HRESULT GetItemTypeName(PCUITEMID_CHILD pidl, const BBITEMDATA &Data, SHF
210210
return E_FAIL;
211211
}
212212

213-
static HDELFILE GetRecycleBinFileHandleFromItem(const BBITEMDATA &Data)
214-
{
215-
RECYCLEBINFILEIDENTITY identity = { Data.DeletionTime, GetItemRecycledFullPath(Data) };
216-
return GetRecycleBinFileHandle(NULL, &identity);
217-
}
218-
219213
/*
220214
* Recycle Bin folder
221215
*/
@@ -285,14 +279,6 @@ EXTERN_C void CRecycleBin_NotifyRecycled(LPCWSTR OrigPath, const WIN32_FIND_DATA
285279
static void CRecycleBin_NotifyRemovedFromRecycleBin(LPCITEMIDLIST BBItem)
286280
{
287281
CRecycleBin_ChangeNotifyBBItem(IsFolder(BBItem) ? SHCNE_RMDIR : SHCNE_DELETE, BBItem);
288-
289-
CComHeapPtr<ITEMIDLIST> pidlBB(SHCloneSpecialIDList(NULL, CSIDL_BITBUCKET, FALSE));
290-
CComPtr<IShellFolder> pSF;
291-
if (pidlBB && SUCCEEDED(SHBindToObject(NULL, pidlBB, IID_PPV_ARG(IShellFolder, &pSF))))
292-
{
293-
if (IsRecycleBinEmpty(pSF))
294-
SHUpdateRecycleBinIcon();
295-
}
296282
}
297283

298284
static HRESULT CRecyclerExtractIcon_CreateInstance(
@@ -316,11 +302,12 @@ class CRecycleBinItemContextMenu :
316302
public IContextMenu2
317303
{
318304
private:
319-
LPITEMIDLIST apidl;
305+
PITEMID_CHILD* m_apidl;
306+
UINT m_cidl;
320307
public:
321308
CRecycleBinItemContextMenu();
322-
~CRecycleBinItemContextMenu();
323-
HRESULT WINAPI Initialize(LPCITEMIDLIST pidl);
309+
virtual ~CRecycleBinItemContextMenu();
310+
HRESULT WINAPI Initialize(UINT cidl, PCUITEMID_CHILD_ARRAY apidl);
324311

325312
// IContextMenu
326313
STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override;
@@ -411,19 +398,21 @@ BOOL CRecycleBinEnum::CBEnumRecycleBin(IN HDELFILE hDeletedFile)
411398

412399
CRecycleBinItemContextMenu::CRecycleBinItemContextMenu()
413400
{
414-
apidl = NULL;
401+
m_apidl = NULL;
402+
m_cidl = 0;
415403
}
416404

417405
CRecycleBinItemContextMenu::~CRecycleBinItemContextMenu()
418406
{
419-
ILFree(apidl);
407+
_ILFreeaPidl(m_apidl, m_cidl);
420408
}
421409

422-
HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST pidl)
410+
HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(UINT cidl, PCUITEMID_CHILD_ARRAY apidl)
423411
{
424-
apidl = ILClone(pidl);
425-
if (apidl == NULL)
412+
m_apidl = _ILCopyaPidl(apidl, cidl);
413+
if (m_apidl == NULL)
426414
return E_OUTOFMEMORY;
415+
m_cidl = cidl;
427416
return S_OK;
428417
}
429418

@@ -473,22 +462,93 @@ HRESULT WINAPI CRecycleBinItemContextMenu::QueryContextMenu(HMENU hMenu, UINT in
473462
return idHigh ? MAKE_HRESULT(SEVERITY_SUCCESS, 0, idHigh - idCmdFirst + 1) : S_OK;
474463
}
475464

476-
static BOOL ConfirmDelete(LPCMINVOKECOMMANDINFO lpcmi, UINT cidl, LPCITEMIDLIST pidl, const BBITEMDATA &Data)
465+
static BOOL ConfirmDelete(LPCMINVOKECOMMANDINFO lpcmi, UINT cidl, LPCITEMIDLIST pidl)
477466
{
467+
BBITEMDATA *pData;
478468
if (lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)
479469
{
480470
return TRUE;
481471
}
482-
else if (cidl == 1)
472+
else if (cidl == 1 && (pData = ValidateItem(pidl)) != NULL)
483473
{
484474
const UINT ask = IsFolder(pidl) ? ASK_DELETE_FOLDER : ASK_DELETE_FILE;
485-
return SHELL_ConfirmYesNoW(lpcmi->hwnd, ask, GetItemOriginalFileName(Data));
475+
return SHELL_ConfirmYesNoW(lpcmi->hwnd, ask, GetItemOriginalFileName(*pData));
486476
}
487-
WCHAR buf[MAX_PATH];
477+
WCHAR buf[42];
488478
wsprintfW(buf, L"%d", cidl);
489479
return SHELL_ConfirmYesNoW(lpcmi->hwnd, ASK_DELETE_MULTIPLE_ITEM, buf);
490480
}
491481

482+
static LPWSTR CreateFileOpStrings(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, BOOL RecycledPath)
483+
{
484+
PWSTR mem = NULL, newmem;
485+
for (SIZE_T i = 0, cb = 0, cb2, cbPath; i < cidl; ++i, cb = cb2)
486+
{
487+
BBITEMDATA *pData = ValidateItem(apidl[i]);
488+
if (!pData)
489+
{
490+
fail:
491+
LocalFree(mem);
492+
return NULL;
493+
}
494+
LPCWSTR path = RecycledPath ? GetItemRecycledFullPath(*pData) : GetItemOriginalFullPath(*pData);
495+
cbPath = (lstrlenW(path) + 1) * sizeof(WCHAR);
496+
cb2 = cb + cbPath;
497+
SIZE_T cbTot = cb2 + sizeof(WCHAR); // \0\0 termination
498+
newmem = (PWSTR)(i ? LocalReAlloc(mem, cbTot, LMEM_MOVEABLE) : LocalAlloc(LPTR, cbTot));
499+
if (!newmem)
500+
goto fail;
501+
mem = newmem;
502+
CopyMemory((char*)mem + cb, path, cbPath);
503+
*(PWSTR)((char*)mem + cb + cbPath) = UNICODE_NULL;
504+
}
505+
return mem;
506+
}
507+
508+
typedef struct
509+
{
510+
PCUITEMID_CHILD_ARRAY apidl;
511+
UINT cidl, index;
512+
BBITEMDATA *pItem;
513+
} FILEOPDATA;
514+
515+
static HRESULT CALLBACK FileOpCallback(FILEOPCALLBACKEVENT Event, LPCWSTR Src, LPCWSTR Dst, UINT Attrib, HRESULT hrOp, void *CallerData)
516+
{
517+
FILEOPDATA &data = *(FILEOPDATA*)CallerData;
518+
if (Event == FOCE_PREMOVEITEM || Event == FOCE_PREDELETEITEM)
519+
{
520+
data.pItem = NULL;
521+
for (UINT i = 0; i < data.cidl; ++i)
522+
{
523+
BBITEMDATA *pItem = ValidateItem(data.apidl[i]);
524+
if (pItem && !_wcsicmp(Src, GetItemRecycledFullPath(*pItem)))
525+
{
526+
data.pItem = pItem;
527+
data.index = i;
528+
break;
529+
}
530+
}
531+
}
532+
else if ((Event == FOCE_POSTDELETEITEM || Event == FOCE_POSTMOVEITEM) && SUCCEEDED(hrOp) && data.pItem)
533+
{
534+
RECYCLEBINFILEIDENTITY identity = { data.pItem->DeletionTime, GetItemRecycledFullPath(*data.pItem) };
535+
RemoveFromRecycleBinDatabase(&identity);
536+
CRecycleBin_NotifyRemovedFromRecycleBin(data.apidl[data.index]);
537+
data.pItem = NULL;
538+
}
539+
else if (Event == FOCE_FINISHOPERATIONS)
540+
{
541+
CComHeapPtr<ITEMIDLIST> pidlBB(SHCloneSpecialIDList(NULL, CSIDL_BITBUCKET, FALSE));
542+
CComPtr<IShellFolder> pSF;
543+
if (pidlBB && SUCCEEDED(SHBindToObject(NULL, pidlBB, IID_PPV_ARG(IShellFolder, &pSF))))
544+
{
545+
if (IsRecycleBinEmpty(pSF))
546+
SHUpdateRecycleBinIcon();
547+
}
548+
}
549+
return S_OK;
550+
}
551+
492552
HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
493553
{
494554
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
505565

506566
if (CmdId == IDC_BB_RESTORE || CmdId == IDC_BB_DELETE)
507567
{
508-
BBITEMDATA *pData = ValidateItem(apidl);
509-
if (!pData && FAILED_UNEXPECTEDLY(E_FAIL))
510-
return E_FAIL;
511-
HDELFILE hDelFile = GetRecycleBinFileHandleFromItem(*pData);
512-
if (!hDelFile && FAILED_UNEXPECTEDLY(E_FAIL))
513-
return E_FAIL;
568+
HRESULT hr = S_OK;
569+
if (CmdId == IDC_BB_DELETE && !ConfirmDelete(lpcmi, m_cidl, m_apidl[0]))
570+
return S_OK;
514571

515-
HRESULT hr = S_FALSE;
572+
LPWSTR pszzDst = NULL;
573+
LPWSTR pszzSrc = CreateFileOpStrings(m_cidl, m_apidl, TRUE);
574+
if (!pszzSrc)
575+
return E_OUTOFMEMORY;
576+
SHFILEOPSTRUCTW shfos = { lpcmi->hwnd, FO_DELETE, pszzSrc, NULL, FOF_NOCONFIRMMKDIR };
516577
if (CmdId == IDC_BB_RESTORE)
517-
hr = RestoreFileFromRecycleBin(hDelFile) ? S_OK : E_FAIL;
518-
else if (ConfirmDelete(lpcmi, 1, apidl, *pData))
519-
hr = DeleteFileInRecycleBin(hDelFile) ? S_OK : E_FAIL;
520-
521-
if (hr == S_OK)
522-
CRecycleBin_NotifyRemovedFromRecycleBin(apidl);
523-
524-
CloseRecycleBinHandle(hDelFile);
578+
{
579+
pszzDst = CreateFileOpStrings(m_cidl, m_apidl, FALSE);
580+
if (!pszzDst)
581+
hr = E_OUTOFMEMORY;
582+
shfos.wFunc = FO_MOVE;
583+
shfos.pTo = pszzDst;
584+
shfos.fFlags |= FOF_MULTIDESTFILES;
585+
}
586+
else // IDC_BB_DELETE
587+
{
588+
shfos.fFlags |= FOF_NOCONFIRMATION;
589+
}
590+
if (SUCCEEDED(hr))
591+
{
592+
if (lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)
593+
shfos.fFlags |= FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMATION;
594+
FILEOPDATA data = { m_apidl, m_cidl };
595+
int res = SHELL32_FileOperation(&shfos, FileOpCallback, &data);
596+
if (res && res != DE_OPCANCELLED && res != ERROR_CANCELLED)
597+
hr = SHELL_ErrorBox(*lpcmi, E_FAIL);
598+
}
599+
LocalFree(pszzDst);
600+
LocalFree(pszzSrc);
525601
return hr;
526602
}
527603
else if (CmdId == IDC_BB_CUT)
528604
{
529605
FIXME("implement cut\n");
530-
SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED);
606+
SHELL_ErrorBox(*lpcmi, ERROR_NOT_SUPPORTED);
531607
return E_NOTIMPL;
532608
}
533609
else if (CmdId == IDC_BB_PROPERTIES)
534610
{
535611
FIXME("implement properties\n");
536-
SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED);
612+
SHELL_ErrorBox(*lpcmi, ERROR_NOT_SUPPORTED);
537613
return E_NOTIMPL;
538614
}
539615
return E_UNEXPECTED;
@@ -814,8 +890,7 @@ HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_C
814890

815891
if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2)) && (cidl >= 1))
816892
{
817-
// FIXME: Handle multiple items
818-
hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(apidl[0], riid, &pObj);
893+
hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(cidl, apidl, riid, &pObj);
819894
}
820895
else if((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && (cidl == 1))
821896
{

dll/win32/shell32/precomp.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,16 @@ InvokeIExecuteCommandWithDataObject(
322322
_In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
323323
_In_opt_ IUnknown *pSite);
324324

325+
typedef enum {
326+
FOCE_STARTOPERATIONS,
327+
FOCE_FINISHOPERATIONS,
328+
FOCE_PREMOVEITEM,
329+
FOCE_POSTMOVEITEM,
330+
FOCE_PREDELETEITEM,
331+
FOCE_POSTDELETEITEM
332+
} FILEOPCALLBACKEVENT;
333+
typedef HRESULT (CALLBACK *FILEOPCALLBACK)(FILEOPCALLBACKEVENT Event, LPCWSTR Source, LPCWSTR Destination,
334+
UINT Attributes, HRESULT hr, void *CallerData);
335+
int SHELL32_FileOperation(LPSHFILEOPSTRUCTW lpFileOp, FILEOPCALLBACK Callback, void *CallerData);
336+
325337
#endif /* _PRECOMP_H__ */

dll/win32/shell32/shellrecyclebin/recyclebin.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ BOOL WINAPI
9393
DeleteFileInRecycleBin(
9494
IN HDELFILE hDeletedFile)
9595
{
96-
IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
96+
IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDeletedFile);
9797
HRESULT hr;
9898

9999
TRACE("(%p)\n", hDeletedFile);
@@ -283,11 +283,26 @@ GetRecycleBinFileHandle(
283283
return context.hDelFile;
284284
}
285285

286+
EXTERN_C BOOL
287+
RemoveFromRecycleBinDatabase(
288+
IN const RECYCLEBINFILEIDENTITY *pFI)
289+
{
290+
BOOL ret = FALSE;
291+
HDELFILE hDelFile = GetRecycleBinFileHandle(NULL, pFI);
292+
if (hDelFile)
293+
{
294+
IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDelFile);
295+
ret = SUCCEEDED(IRecycleBinFile_RemoveFromDatabase(rbf));
296+
CloseRecycleBinHandle(hDelFile);
297+
}
298+
return ret;
299+
}
300+
286301
BOOL WINAPI
287302
RestoreFileFromRecycleBin(
288303
IN HDELFILE hDeletedFile)
289304
{
290-
IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
305+
IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDeletedFile);
291306
HRESULT hr;
292307

293308
TRACE("(%p)\n", hDeletedFile);

dll/win32/shell32/shellrecyclebin/recyclebin.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ GetRecycleBinFileHandle(
155155
IN LPCWSTR pszRoot OPTIONAL,
156156
IN const RECYCLEBINFILEIDENTITY *pFI);
157157

158+
EXTERN_C BOOL
159+
RemoveFromRecycleBinDatabase(
160+
IN const RECYCLEBINFILEIDENTITY *pFI);
161+
158162
/* Restores a deleted file
159163
* hDeletedFile: handle of the deleted file to restore
160164
* Returns TRUE if operation succeeded, FALSE otherwise.
@@ -187,6 +191,7 @@ DECLARE_INTERFACE_(IRecycleBinFile, IUnknown)
187191
STDMETHOD(GetFileName)(THIS_ SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) PURE;
188192
STDMETHOD(Delete)(THIS) PURE;
189193
STDMETHOD(Restore)(THIS) PURE;
194+
STDMETHOD(RemoveFromDatabase)(THIS) PURE;
190195

191196
END_INTERFACE
192197
};
@@ -262,6 +267,8 @@ EXTERN_C const IID IID_IRecycleBin;
262267
(This)->lpVtbl->Delete(This)
263268
#define IRecycleBinFile_Restore(This) \
264269
(This)->lpVtbl->Restore(This)
270+
#define IRecycleBinFile_RemoveFromDatabase(This) \
271+
(This)->lpVtbl->RemoveFromDatabase(This)
265272

266273
#define IRecycleBinEnumList_QueryInterface(This, riid, ppvObject) \
267274
(This)->lpVtbl->QueryInterface(This, riid, ppvObject)

0 commit comments

Comments
 (0)