https://git.reactos.org/?p=reactos.git;a=commitdiff;h=28399a216b744e6263c77…
commit 28399a216b744e6263c772754644743e7a6682f1
Author: Whindmar Saksit <whindsaks(a)proton.me>
AuthorDate: Thu Dec 19 14:38:27 2024 +0100
Commit: GitHub <noreply(a)github.com>
CommitDate: Thu Dec 19 14:38:27 2024 +0100
[SHELL32] Use FS compatible PIDL format for Recycle Bin items (#7532)
* [SHELL32] Use FS compatible PIDL format for Recycle Bin items
This allows SHChangeNotify to handle these items and DefView will correctly update the
recycle folder.
CORE-18005 CORE-19239 CORE-13950 CORE-18435 CORE-18436 CORE-18437
---
dll/win32/shell32/CCopyAsPathMenu.cpp | 14 +-
dll/win32/shell32/CDefView.cpp | 33 +-
dll/win32/shell32/CDefaultContextMenu.cpp | 37 +-
dll/win32/shell32/CFolderItemVerbs.cpp | 8 +-
dll/win32/shell32/COpenWithMenu.cpp | 20 +-
dll/win32/shell32/CSendToMenu.cpp | 9 +-
dll/win32/shell32/CShellLink.cpp | 25 +-
dll/win32/shell32/dialogs/recycler_prop.cpp | 77 +-
dll/win32/shell32/folders/CDesktopFolder.cpp | 2 +-
dll/win32/shell32/folders/CFSFolder.cpp | 150 +--
dll/win32/shell32/folders/CFSFolder.h | 7 +
dll/win32/shell32/folders/CRecycleBin.cpp | 1008 +++++++++++---------
dll/win32/shell32/folders/CRecycleBin.h | 7 +-
dll/win32/shell32/precomp.h | 6 +
dll/win32/shell32/shellrecyclebin/recyclebin.c | 161 +---
dll/win32/shell32/shellrecyclebin/recyclebin.h | 133 ++-
.../shell32/shellrecyclebin/recyclebin_generic.cpp | 12 +
.../shell32/shellrecyclebin/recyclebin_private.h | 12 +
.../shell32/shellrecyclebin/recyclebin_v5.cpp | 40 +-
dll/win32/shell32/shellrecyclebin/recyclebin_v5.h | 3 +-
.../shellrecyclebin/recyclebin_v5_enumerator.cpp | 73 +-
dll/win32/shell32/shlexec.cpp | 10 +-
dll/win32/shell32/utils.cpp | 94 ++
dll/win32/shell32/utils.h | 40 +-
dll/win32/shell32/wine/pidl.c | 86 +-
dll/win32/shell32/wine/pidl.h | 13 +-
dll/win32/shell32/wine/shell32_main.h | 2 +-
27 files changed, 1141 insertions(+), 941 deletions(-)
diff --git a/dll/win32/shell32/CCopyAsPathMenu.cpp
b/dll/win32/shell32/CCopyAsPathMenu.cpp
index f031874c5d4..3f8f43aeb2d 100644
--- a/dll/win32/shell32/CCopyAsPathMenu.cpp
+++ b/dll/win32/shell32/CCopyAsPathMenu.cpp
@@ -103,6 +103,12 @@ CCopyAsPathMenu::DoCopyAsPath(IDataObject *pdto)
return hr;
}
+static const CMVERBMAP g_VerbMap[] =
+{
+ { "copyaspath", IDC_COPYASPATH },
+ { NULL }
+};
+
STDMETHODIMP
CCopyAsPathMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT
idCmdLast, UINT uFlags)
{
@@ -133,7 +139,8 @@ CCopyAsPathMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
TRACE("CCopyAsPathMenu::InvokeCommand(%p %p)\n", this, lpcmi);
- if (IS_INTRESOURCE(lpcmi->lpVerb) && LOWORD(lpcmi->lpVerb) ==
IDC_COPYASPATH)
+ int CmdId = SHELL_MapContextMenuVerbToCmdId(lpcmi, g_VerbMap);
+ if (CmdId == IDC_COPYASPATH)
return DoCopyAsPath(m_pDataObject);
return E_FAIL;
@@ -142,10 +149,9 @@ CCopyAsPathMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
STDMETHODIMP
CCopyAsPathMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved,
LPSTR lpszName, UINT uMaxNameLen)
{
- FIXME("CCopyAsPathMenu::GetCommandString(%p %lu %u %p %p %u)\n", this,
+ TRACE("CCopyAsPathMenu::GetCommandString(%p %lu %u %p %p %u)\n", this,
idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
-
- return E_NOTIMPL;
+ return SHELL_GetCommandStringImpl(idCommand, uFlags, lpszName, uMaxNameLen,
g_VerbMap);
}
STDMETHODIMP
diff --git a/dll/win32/shell32/CDefView.cpp b/dll/win32/shell32/CDefView.cpp
index d4c8ed82cab..0505c8e7b69 100644
--- a/dll/win32/shell32/CDefView.cpp
+++ b/dll/win32/shell32/CDefView.cpp
@@ -1318,6 +1318,7 @@ int CDefView::LV_FindItemByPidl(PCUITEMID_CHILD pidl)
{
for (i = 0; i < cItems; i++)
{
+ //FIXME: ILIsEqual needs absolute pidls!
currentpidl = _PidlByItem(i);
if (ILIsEqual(pidl, currentpidl))
return i;
@@ -2848,29 +2849,35 @@ LRESULT CDefView::OnChangeNotify(UINT uMsg, WPARAM wParam, LPARAM
lParam, BOOL &
TRACE("(%p)(%p,%p,%p)\n", this, Pidls[0], Pidls[1], lParam);
if (_DoFolderViewCB(SFVM_FSNOTIFY, (WPARAM)Pidls, lEvent) == S_FALSE)
+ {
+ SHChangeNotification_Unlock(hLock);
return FALSE;
+ }
// Translate child IDLs.
// SHSimpleIDListFromPathW creates fake PIDLs (lacking some attributes)
+ lEvent &= ~SHCNE_INTERRUPT;
HRESULT hr;
PITEMID_CHILD child0 = NULL, child1 = NULL;
CComHeapPtr<ITEMIDLIST_RELATIVE> pidl0Temp, pidl1Temp;
- if (_ILIsSpecialFolder(Pidls[0]) || ILIsParentOrSpecialParent(m_pidlParent,
Pidls[0]))
+ if (lEvent != SHCNE_UPDATEIMAGE && lEvent < SHCNE_EXTENDED_EVENT)
{
- child0 = ILFindLastID(Pidls[0]);
- hr = SHGetRealIDL(m_pSFParent, child0, &pidl0Temp);
- if (SUCCEEDED(hr))
- child0 = pidl0Temp;
- }
- if (_ILIsSpecialFolder(Pidls[1]) || ILIsParentOrSpecialParent(m_pidlParent,
Pidls[1]))
- {
- child1 = ILFindLastID(Pidls[1]);
- hr = SHGetRealIDL(m_pSFParent, child1, &pidl1Temp);
- if (SUCCEEDED(hr))
- child1 = pidl1Temp;
+ if (_ILIsSpecialFolder(Pidls[0]) || ILIsParentOrSpecialParent(m_pidlParent,
Pidls[0]))
+ {
+ child0 = ILFindLastID(Pidls[0]);
+ hr = SHGetRealIDL(m_pSFParent, child0, &pidl0Temp);
+ if (SUCCEEDED(hr))
+ child0 = pidl0Temp;
+ }
+ if (_ILIsSpecialFolder(Pidls[1]) || ILIsParentOrSpecialParent(m_pidlParent,
Pidls[1]))
+ {
+ child1 = ILFindLastID(Pidls[1]);
+ hr = SHGetRealIDL(m_pSFParent, child1, &pidl1Temp);
+ if (SUCCEEDED(hr))
+ child1 = pidl1Temp;
+ }
}
- lEvent &= ~SHCNE_INTERRUPT;
switch (lEvent)
{
case SHCNE_MKDIR:
diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp
b/dll/win32/shell32/CDefaultContextMenu.cpp
index 6b83bd8a069..90954dd87c9 100644
--- a/dll/win32/shell32/CDefaultContextMenu.cpp
+++ b/dll/win32/shell32/CDefaultContextMenu.cpp
@@ -84,6 +84,25 @@ UINT MapVerbToDfmCmd(_In_ LPCSTR verba)
return 0;
}
+static HRESULT
+MapVerbToCmdId(PVOID Verb, BOOL IsUnicode, IContextMenu *pCM, UINT idFirst, UINT idLast)
+{
+ const UINT gcs = IsUnicode ? GCS_VERBW : GCS_VERBA;
+ for (UINT id = idFirst; id <= idLast; ++id)
+ {
+ WCHAR buf[MAX_PATH];
+ *buf = UNICODE_NULL;
+ HRESULT hr = pCM->GetCommandString(id, gcs, NULL, (LPSTR)buf, _countof(buf));
+ if (FAILED(hr) || !*buf)
+ continue;
+ else if (IsUnicode && !_wcsicmp((LPWSTR)Verb, buf))
+ return id;
+ else if (!IsUnicode && !lstrcmpiA((LPSTR)Verb, (LPSTR)buf))
+ return id;
+ }
+ return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+}
+
static inline bool IsVerbListSeparator(WCHAR Ch)
{
return Ch == L' ' || Ch == L','; //
learn.microsoft.com/en-us/windows/win32/shell/context-menu-handlers
@@ -738,7 +757,7 @@ CDefaultContextMenu::AddStaticContextMenusToMenu(
return cIds;
}
-void WINAPI _InsertMenuItemW(
+BOOL WINAPI _InsertMenuItemW(
HMENU hMenu,
UINT indexMenu,
BOOL fByPosition,
@@ -764,7 +783,7 @@ void WINAPI _InsertMenuItemW(
else
{
ERR("failed to load string %p\n", dwTypeData);
- return;
+ return FALSE;
}
}
else
@@ -774,7 +793,7 @@ void WINAPI _InsertMenuItemW(
mii.wID = wID;
mii.fType = fType;
- InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
+ return InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
}
void
@@ -1212,6 +1231,18 @@ CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL
IsUnicode)
}
}
+ for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;)
+ {
+ DynamicShellEntry& entry = m_DynamicEntries.GetNext(it);
+ if (!entry.NumIds)
+ continue;
+ HRESULT hr = ::MapVerbToCmdId(Verb, IsUnicode, entry.pCM, 0, entry.NumIds - 1);
+ if (SUCCEEDED(hr))
+ {
+ *idCmd = m_iIdSHEFirst + entry.iIdCmdFirst + hr;
+ return TRUE;
+ }
+ }
return FALSE;
}
diff --git a/dll/win32/shell32/CFolderItemVerbs.cpp
b/dll/win32/shell32/CFolderItemVerbs.cpp
index 775f342f737..ce7f2266ba6 100644
--- a/dll/win32/shell32/CFolderItemVerbs.cpp
+++ b/dll/win32/shell32/CFolderItemVerbs.cpp
@@ -91,13 +91,7 @@ CFolderItemVerbs::~CFolderItemVerbs()
HRESULT CFolderItemVerbs::Init(LPITEMIDLIST idlist)
{
- CComPtr<IShellFolder> folder;
- LPCITEMIDLIST child;
- HRESULT hr = SHBindToParent(idlist, IID_PPV_ARG(IShellFolder, &folder),
&child);
- if (FAILED_UNEXPECTEDLY(hr))
- return hr;
-
- hr = folder->GetUIObjectOf(NULL, 1, &child, IID_IContextMenu, NULL,
(PVOID*)&m_contextmenu);
+ HRESULT hr = SHELL_GetUIObjectOfAbsoluteItem(NULL, idlist, IID_PPV_ARG(IContextMenu,
&m_contextmenu));
if (FAILED_UNEXPECTEDLY(hr))
return hr;
diff --git a/dll/win32/shell32/COpenWithMenu.cpp b/dll/win32/shell32/COpenWithMenu.cpp
index 42f538a7eeb..96a73dd822d 100644
--- a/dll/win32/shell32/COpenWithMenu.cpp
+++ b/dll/win32/shell32/COpenWithMenu.cpp
@@ -1250,6 +1250,11 @@ VOID COpenWithMenu::AddApp(PVOID pApp)
m_idCmdLast++;
}
+static const CMVERBMAP g_VerbMap[] = {
+ { "openas", 0 },
+ { NULL }
+};
+
HRESULT WINAPI COpenWithMenu::QueryContextMenu(
HMENU hMenu,
UINT indexMenu,
@@ -1328,14 +1333,19 @@ HRESULT WINAPI COpenWithMenu::QueryContextMenu(
HRESULT WINAPI
COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
{
+ const SIZE_T idChooseApp = m_idCmdLast;
HRESULT hr = E_FAIL;
TRACE("This %p idFirst %u idLast %u idCmd %u\n", this, m_idCmdFirst,
m_idCmdLast, m_idCmdFirst + LOWORD(lpici->lpVerb));
- if (HIWORD(lpici->lpVerb) == 0 && m_idCmdFirst + LOWORD(lpici->lpVerb)
<= m_idCmdLast)
+ if (!IS_INTRESOURCE(lpici->lpVerb) &&
SHELL_MapContextMenuVerbToCmdId(lpici, g_VerbMap) == 0)
+ goto DoChooseApp;
+
+ if (IS_INTRESOURCE(lpici->lpVerb) && m_idCmdFirst +
LOWORD(lpici->lpVerb) <= m_idCmdLast)
{
- if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdLast)
+ if (m_idCmdFirst + LOWORD(lpici->lpVerb) == idChooseApp)
{
+DoChooseApp:
OPENASINFO info;
LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
@@ -1371,9 +1381,13 @@ HRESULT WINAPI
COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType,
UINT* pwReserved, LPSTR pszName, UINT cchMax )
{
- FIXME("%p %lu %u %p %p %u\n", this,
+ TRACE("%p %lu %u %p %p %u\n", this,
idCmd, uType, pwReserved, pszName, cchMax );
+ const SIZE_T idChooseApp = m_idCmdLast;
+ if (m_idCmdFirst + idCmd == idChooseApp)
+ return SHELL_GetCommandStringImpl(0, uType, pszName, cchMax, g_VerbMap);
+
return E_NOTIMPL;
}
diff --git a/dll/win32/shell32/CSendToMenu.cpp b/dll/win32/shell32/CSendToMenu.cpp
index 131f21e2a0a..8cce580ba08 100644
--- a/dll/win32/shell32/CSendToMenu.cpp
+++ b/dll/win32/shell32/CSendToMenu.cpp
@@ -130,14 +130,7 @@ HRESULT CSendToMenu::GetUIObjectFromPidl(HWND hwnd, PIDLIST_ABSOLUTE
pidl,
REFIID riid, LPVOID *ppvOut)
{
*ppvOut = NULL;
-
- PCITEMID_CHILD pidlLast;
- CComPtr<IShellFolder> pFolder;
- HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &pFolder),
&pidlLast);
- if (FAILED_UNEXPECTEDLY(hr))
- return hr;
-
- hr = pFolder->GetUIObjectOf(hwnd, 1, &pidlLast, riid, NULL, ppvOut);
+ HRESULT hr = SHELL_GetUIObjectOfAbsoluteItem(hwnd, pidl, riid, ppvOut);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
diff --git a/dll/win32/shell32/CShellLink.cpp b/dll/win32/shell32/CShellLink.cpp
index d99db2f1470..9619efcf483 100644
--- a/dll/win32/shell32/CShellLink.cpp
+++ b/dll/win32/shell32/CShellLink.cpp
@@ -1729,18 +1729,10 @@ HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(LPWSTR
pszIconPath, INT cc
static HRESULT SHELL_PidlGetIconLocationW(PCIDLIST_ABSOLUTE pidl,
UINT uFlags, PWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
{
- LPCITEMIDLIST pidlLast;
- CComPtr<IShellFolder> psf;
-
- HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf),
&pidlLast);
- if (FAILED_UNEXPECTEDLY(hr))
- return hr;
-
CComPtr<IExtractIconW> pei;
- hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IExtractIconW,
&pei));
+ HRESULT hr = SHELL_GetUIObjectOfAbsoluteItem(NULL, pidl, IID_PPV_ARG(IExtractIconW,
&pei));
if (FAILED_UNEXPECTEDLY(hr))
return hr;
-
hr = pei->GetIconLocation(uFlags, pszIconFile, cchMax, piIndex, pwFlags);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
@@ -3142,20 +3134,9 @@ HRESULT STDMETHODCALLTYPE CShellLink::DragEnter(IDataObject
*pDataObject,
if (*pdwEffect == DROPEFFECT_NONE)
return S_OK;
- LPCITEMIDLIST pidlLast;
- CComPtr<IShellFolder> psf;
-
- HRESULT hr = SHBindToParent(m_pPidl, IID_PPV_ARG(IShellFolder, &psf),
&pidlLast);
-
+ HRESULT hr = SHELL_GetUIObjectOfAbsoluteItem(NULL, m_pPidl, IID_PPV_ARG(IDropTarget,
&m_DropTarget));
if (SUCCEEDED(hr))
- {
- hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IDropTarget,
&m_DropTarget));
-
- if (SUCCEEDED(hr))
- hr = m_DropTarget->DragEnter(pDataObject, dwKeyState, pt, pdwEffect);
- else
- *pdwEffect = DROPEFFECT_NONE;
- }
+ hr = m_DropTarget->DragEnter(pDataObject, dwKeyState, pt, pdwEffect);
else
*pdwEffect = DROPEFFECT_NONE;
diff --git a/dll/win32/shell32/dialogs/recycler_prop.cpp
b/dll/win32/shell32/dialogs/recycler_prop.cpp
index d7f79251a3e..72387de388c 100644
--- a/dll/win32/shell32/dialogs/recycler_prop.cpp
+++ b/dll/win32/shell32/dialogs/recycler_prop.cpp
@@ -45,6 +45,9 @@ static void toggleNukeOnDeleteOption(HWND hwndDlg, BOOL bEnable)
EnableWindow(GetDlgItem(hwndDlg, 14002), TRUE);
SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_UNCHECKED, 0);
}
+
+ // FIXME: Max capacity not implemented yet, disable for now (CORE-13743)
+ EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
}
static VOID
@@ -129,7 +132,8 @@ InitializeRecycleBinDlg(HWND hwndDlg, WCHAR DefaultDrive)
swprintf(szName,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume\\%04X-%04X",
LOWORD(dwSerial), HIWORD(dwSerial));
dwSize = sizeof(DWORD);
- RegGetValueW(HKEY_CURRENT_USER, szName,
L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize);
+ if (RegGetValueW(HKEY_CURRENT_USER, szName,
L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize))
+ pItem->dwMaxCapacity = ~0;
/* Check if the maximum capacity doesn't exceed the
available disk space (in megabytes), and truncate it if needed */
FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart /
(1024 * 1024));
@@ -240,7 +244,7 @@ static VOID FreeDriveItemContext(HWND hwndDlg)
}
static INT
-GetDefaultItem(HWND hwndDlg, LVITEMW* li)
+GetSelectedDriveItem(HWND hwndDlg, LVITEMW* li)
{
HWND hDlgCtrl;
UINT iItemCount, iIndex;
@@ -275,6 +279,7 @@ RecycleBinDlg(
WPARAM wParam,
LPARAM lParam)
{
+ enum { WM_NEWDRIVESELECTED = WM_APP, WM_UPDATEDRIVESETTINGS };
LPPSHNOTIFY lppsn;
LPNMLISTVIEW lppl;
LVITEMW li;
@@ -329,25 +334,9 @@ RecycleBinDlg(
ss.fNoConfirmRecycle = SendDlgItemMessage(hwndDlg, 14004, BM_GETCHECK, 0,
0) == BST_UNCHECKED;
SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, TRUE);
- if (GetDefaultItem(hwndDlg, &li) > -1)
+ if (GetSelectedDriveItem(hwndDlg, &li) > -1)
{
- pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
- if (pItem)
- {
- uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
- if (bSuccess)
- {
- /* Check if the maximum capacity doesn't exceed the
available disk space (in megabytes), and truncate it if needed */
- FreeBytesAvailable = pItem->FreeBytesAvailable;
- FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart /
(1024 * 1024));
- pItem->dwMaxCapacity = min(uResult,
FreeBytesAvailable.LowPart);
- SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity,
FALSE);
- }
- if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) ==
BST_CHECKED)
- pItem->dwNukeOnDelete = TRUE;
- else
- pItem->dwNukeOnDelete = FALSE;
- }
+ SendMessage(hwndDlg, WM_UPDATEDRIVESETTINGS, 0, li.lParam);
}
if (StoreDriveSettings(hwndDlg))
{
@@ -369,31 +358,45 @@ RecycleBinDlg(
if (!(lppl->uOldState & LVIS_FOCUSED) &&
(lppl->uNewState & LVIS_FOCUSED))
{
- /* new focused item */
- toggleNukeOnDeleteOption(lppl->hdr.hwndFrom,
pItem->dwNukeOnDelete);
- SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
+ // New focused item, delay handling until after kill focus has been
processed
+ PostMessage(hwndDlg, WM_NEWDRIVESELECTED, 0, (LPARAM)pItem);
}
else if ((lppl->uOldState & LVIS_FOCUSED) &&
!(lppl->uNewState & LVIS_FOCUSED))
{
- /* kill focus */
- uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
- if (bSuccess)
- {
- /* Check if the maximum capacity doesn't exceed the available
disk space (in megabytes), and truncate it if needed */
- FreeBytesAvailable = pItem->FreeBytesAvailable;
- FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart /
(1024 * 1024));
- pItem->dwMaxCapacity = min(uResult,
FreeBytesAvailable.LowPart);
- SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
- }
- if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) ==
BST_CHECKED)
- pItem->dwNukeOnDelete = TRUE;
- else
- pItem->dwNukeOnDelete = FALSE;
+ // Kill focus
+ SendMessage(hwndDlg, WM_UPDATEDRIVESETTINGS, 0, (LPARAM)pItem);
}
return TRUE;
}
break;
+ case WM_NEWDRIVESELECTED:
+ if (lParam)
+ {
+ pItem = (PDRIVE_ITEM_CONTEXT)lParam;
+ toggleNukeOnDeleteOption(hwndDlg, pItem->dwNukeOnDelete);
+ SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
+ }
+ break;
+ case WM_UPDATEDRIVESETTINGS:
+ if (lParam)
+ {
+ pItem = (PDRIVE_ITEM_CONTEXT)lParam;
+ uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
+ if (bSuccess)
+ {
+ /* Check if the maximum capacity doesn't exceed the available
disk space (in megabytes), and truncate it if needed */
+ FreeBytesAvailable = pItem->FreeBytesAvailable;
+ FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 *
1024));
+ pItem->dwMaxCapacity = min(uResult, FreeBytesAvailable.LowPart);
+ SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
+ }
+ if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) ==
BST_CHECKED)
+ pItem->dwNukeOnDelete = TRUE;
+ else
+ pItem->dwNukeOnDelete = FALSE;
+ }
+ break;
case WM_DESTROY:
FreeDriveItemContext(hwndDlg);
break;
diff --git a/dll/win32/shell32/folders/CDesktopFolder.cpp
b/dll/win32/shell32/folders/CDesktopFolder.cpp
index 945f3450ede..b785a28a959 100644
--- a/dll/win32/shell32/folders/CDesktopFolder.cpp
+++ b/dll/win32/shell32/folders/CDesktopFolder.cpp
@@ -695,7 +695,7 @@ HRESULT WINAPI CDesktopFolder::GetAttributesOf(
*rgfInOut &= dwMyComputerAttributes;
else if (_ILIsNetHood(apidl[i]))
*rgfInOut &= dwMyNetPlacesAttributes;
- else if (_ILIsFolder(apidl[i]) || _ILIsValue(apidl[i]) ||
_ILIsSpecialFolder(apidl[i]))
+ else if (_ILIsFolderOrFile(apidl[i]) || _ILIsSpecialFolder(apidl[i]))
{
CComPtr<IShellFolder2> psf;
HRESULT hr = _GetSFFromPidl(apidl[i], &psf);
diff --git a/dll/win32/shell32/folders/CFSFolder.cpp
b/dll/win32/shell32/folders/CFSFolder.cpp
index 9ae1f53da1f..dd4b3678771 100644
--- a/dll/win32/shell32/folders/CFSFolder.cpp
+++ b/dll/win32/shell32/folders/CFSFolder.cpp
@@ -620,7 +620,7 @@ HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST
pidl, LPDW
{
DWORD dwFileAttributes, dwShellAttributes;
- if (!_ILIsFolder(pidl) && !_ILIsValue(pidl))
+ if (!_ILIsFolderOrFile(pidl))
{
ERR("Got wrong type of pidl!\n");
*pdwAttributes &= SFGAO_CANLINK;
@@ -1007,9 +1007,9 @@ HRESULT WINAPI CFSFolder::BindToObject(
/* Get the pidl data */
FileStruct* pData = &_ILGetDataPointer(pidl)->u.file;
- FileStructW* pDataW = _ILGetFileStructW(pidl);
-
- if (!pDataW)
+ WCHAR szNameBuf[MAX_PATH];
+ LPCWSTR pszName = GetItemFileName(pidl, szNameBuf, _countof(szNameBuf));
+ if (!pszName)
{
ERR("CFSFolder::BindToObject: Invalid pidl!\n");
return E_INVALIDARG;
@@ -1021,7 +1021,7 @@ HRESULT WINAPI CFSFolder::BindToObject(
PERSIST_FOLDER_TARGET_INFO pfti = {0};
pfti.dwAttributes = -1;
pfti.csidl = -1;
- PathCombineW(pfti.szTargetParsingName, m_sPathTarget, pDataW->wszName);
+ PathCombineW(pfti.szTargetParsingName, m_sPathTarget, pszName);
/* Get the CLSID to bind to */
CLSID clsidFolder;
@@ -1088,6 +1088,20 @@ HRESULT WINAPI CFSFolder::BindToStorage(
return E_NOTIMPL;
}
+HRESULT CFSFolder::CompareSortFoldersFirst(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
+{
+ BOOL bIsFolder1 = _ILIsFolder(pidl1), bIsFolder2 = _ILIsFolder(pidl2);
+ // When sorting between a File and a Folder, the Folder gets sorted first
+ if (bIsFolder1 != bIsFolder2)
+ {
+ // ...but only if neither of them were generated by SHSimpleIDListFromPath
+ // because in that case we cannot tell if it's a file or a folder.
+ if (pidl1 && IsRealItem(*pidl1) && pidl2 &&
IsRealItem(*pidl2))
+ return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
+ }
+ return MAKE_SCODE(SEVERITY_ERROR, FACILITY_SHELL, S_EQUAL);
+}
+
/**************************************************************************
* CFSFolder::CompareIDs
*/
@@ -1096,31 +1110,24 @@ HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
PCUIDLIST_RELATIVE pidl1,
PCUIDLIST_RELATIVE pidl2)
{
+ WCHAR szNameBuf1[MAX_PATH], szNameBuf2[_countof(szNameBuf1)];
+ LPCWSTR pszName1 = GetItemFileName(pidl1, szNameBuf1, _countof(szNameBuf1));
+ LPCWSTR pszName2 = GetItemFileName(pidl2, szNameBuf2, _countof(szNameBuf2));
+ if (!pszName1 || !pszName2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
+ return E_INVALIDARG;
+
LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
- FileStructW* pDataW1 = _ILGetFileStructW(pidl1);
- FileStructW* pDataW2 = _ILGetFileStructW(pidl2);
- BOOL bIsFolder1 = _ILIsFolder(pidl1);
- BOOL bIsFolder2 = _ILIsFolder(pidl2);
LPWSTR pExtension1, pExtension2;
- if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
- return E_INVALIDARG;
-
- /* When sorting between a File and a Folder, the Folder gets sorted first */
- if (bIsFolder1 != bIsFolder2)
- {
- // ...but only if neither of them were generated by SHSimpleIDListFromPath
- // because in that case we cannot tell if it's a file or a folder.
- if (IsRealItem(*pidl1) && IsRealItem(*pidl2))
- return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
- }
-
+ HRESULT hr = CompareSortFoldersFirst(pidl1, pidl2);
+ if (SUCCEEDED(hr))
+ return hr;
int result = 0;
switch (LOWORD(lParam))
{
case SHFSF_COL_NAME:
- result = _wcsicmp(pDataW1->wszName, pDataW2->wszName);
+ result = _wcsicmp(pszName1, pszName2);
break;
case SHFSF_COL_SIZE:
if (pData1->u.file.dwFileSize > pData2->u.file.dwFileSize)
@@ -1131,8 +1138,9 @@ HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
result = 0;
break;
case SHFSF_COL_TYPE:
- pExtension1 = PathFindExtensionW(pDataW1->wszName);
- pExtension2 = PathFindExtensionW(pDataW2->wszName);
+ // FIXME: Compare the type strings from SHGetFileInfo
+ pExtension1 = PathFindExtensionW(pszName1);
+ pExtension2 = PathFindExtensionW(pszName2);
result = _wcsicmp(pExtension1, pExtension2);
break;
case SHFSF_COL_MDATE:
@@ -1273,7 +1281,7 @@ HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
{
LPCITEMIDLIST rpidl = ILFindLastID(m_pidlRoot);
- if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
+ if (_ILIsFolderOrFile(rpidl))
{
SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
}
@@ -1297,7 +1305,7 @@ HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
while (cidl > 0 && *apidl)
{
pdump(*apidl);
- if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
+ if (_ILIsFolderOrFile(*apidl))
SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
else
ERR("Got an unknown type of pidl!!!\n");
@@ -1549,14 +1557,14 @@ HRESULT WINAPI CFSFolder::SetNameOf(
DWORD dwFlags,
PITEMID_CHILD *pPidlOut)
{
- WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
+ WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1], szNameBuf[MAX_PATH];
BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
debugstr_w (lpName), dwFlags, pPidlOut);
- FileStructW* pDataW = _ILGetFileStructW(pidl);
- if (!pDataW)
+ LPCWSTR pszName = GetItemFileName(pidl, szNameBuf, _countof(szNameBuf));
+ if (!pszName)
{
ERR("Got garbage pidl:\n");
pdump_always(pidl);
@@ -1564,7 +1572,7 @@ HRESULT WINAPI CFSFolder::SetNameOf(
}
/* build source path */
- PathCombineW(szSrc, m_sPathTarget, pDataW->wszName); // FIXME: PIDLs without wide
string
+ PathCombineW(szSrc, m_sPathTarget, pszName);
/* build destination path */
if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER)
@@ -1662,6 +1670,7 @@ HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
}
else
{
+ FILETIME ft;
hr = S_OK;
psd->str.uType = STRRET_WSTR;
if (iColumn != SHFSF_COL_NAME)
@@ -1683,7 +1692,11 @@ HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
GetItemDescription(pidl, psd->str.pOleStr, MAX_PATH);
break;
case SHFSF_COL_MDATE:
- _ILGetFileDate(pidl, psd->str.pOleStr, MAX_PATH);
+ if (!_ILGetFileDateTime(pidl, &ft) || FAILED(FormatDateTime(ft,
psd->str.pOleStr, MAX_PATH)))
+ {
+ *psd->str.pOleStr = UNICODE_NULL;
+ hr = S_FALSE;
+ }
break;
case SHFSF_COL_FATTS:
_ILGetFileAttributes(pidl, psd->str.pOleStr, MAX_PATH);
@@ -1744,17 +1757,12 @@ HRESULT WINAPI CFSFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
m_sPathTarget = NULL;
/* set my path */
+ HRESULT hr = E_FAIL;
if (SHGetPathFromIDListW (pidl, wszTemp))
- {
- int len = wcslen(wszTemp);
- m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
- if (!m_sPathTarget)
- return E_OUTOFMEMORY;
- memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
- }
+ hr = SHStrDupW(wszTemp, &m_sPathTarget);
TRACE ("--(%p)->(%s)\n", this, debugstr_w(m_sPathTarget));
- return S_OK;
+ return hr;
}
/**************************************************************************
@@ -1806,44 +1814,28 @@ HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc,
LPCITEMIDLIST pidlRootx,
* the target folder is spezified in csidl OR pidlTargetFolder OR
* szTargetParsingName
*/
+ HRESULT hr = E_FAIL;
if (ppfti)
{
if (ppfti->csidl != -1)
{
- if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
- ppfti->csidl & CSIDL_FLAG_CREATE)) {
- int len = wcslen(wszTemp);
- m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
- if (!m_sPathTarget)
- return E_OUTOFMEMORY;
- memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
- }
+ BOOL create = ppfti->csidl & CSIDL_FLAG_CREATE;
+ if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl, create))
+ hr = SHStrDupW(wszTemp, &m_sPathTarget);
}
else if (ppfti->szTargetParsingName[0])
{
- int len = wcslen(ppfti->szTargetParsingName);
- m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
- if (!m_sPathTarget)
- return E_OUTOFMEMORY;
- memcpy(m_sPathTarget, ppfti->szTargetParsingName,
- (len + 1) * sizeof(WCHAR));
+ hr = SHStrDupW(ppfti->szTargetParsingName, &m_sPathTarget);
}
else if (ppfti->pidlTargetFolder)
{
if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
- {
- int len = wcslen(wszTemp);
- m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
- if (!m_sPathTarget)
- return E_OUTOFMEMORY;
- memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
- }
+ hr = SHStrDupW(wszTemp, &m_sPathTarget);
}
}
-
TRACE("--(%p)->(target=%s)\n", this, debugstr_w(m_sPathTarget));
pdump(m_pidlRoot);
- return (m_sPathTarget) ? S_OK : E_FAIL;
+ return hr;
}
HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
@@ -1923,17 +1915,16 @@ HRESULT CFSFolder::_GetIconHandler(LPCITEMIDLIST pidl, REFIID
riid, LPVOID *ppvO
HRESULT CFSFolder::_CreateShellExtInstance(const CLSID *pclsid, LPCITEMIDLIST pidl,
REFIID riid, LPVOID *ppvOut)
{
HRESULT hr;
- WCHAR wszPath[MAX_PATH];
+ WCHAR wszPath[MAX_PATH], szNameBuf[MAX_PATH];
- FileStructW* pDataW = _ILGetFileStructW(pidl);
- if (!pDataW)
+ LPCWSTR pszName = GetItemFileName(pidl, szNameBuf, _countof(szNameBuf));
+ if (!pszName)
{
ERR("Got garbage pidl\n");
pdump_always(pidl);
return E_INVALIDARG;
}
-
- PathCombineW(wszPath, m_sPathTarget, pDataW->wszName);
+ PathCombineW(wszPath, m_sPathTarget, pszName);
CComPtr<IPersistFile> pp;
hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
@@ -2104,3 +2095,28 @@ HRESULT WINAPI CFSFolder::MessageSFVCB(UINT uMsg, WPARAM wParam,
LPARAM lParam)
}
return hr;
}
+
+HRESULT CFSFolder::FormatDateTime(const FILETIME &ft, LPWSTR Buf, UINT cchBuf)
+{
+ FILETIME lft;
+ SYSTEMTIME time;
+ FileTimeToLocalFileTime(&ft, &lft);
+ FileTimeToSystemTime(&lft, &time);
+ UINT ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, Buf,
cchBuf);
+ if (ret > 0 && ret < cchBuf)
+ {
+ /* Append space + time without seconds */
+ Buf[ret-1] = ' ';
+ GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL,
&Buf[ret], cchBuf - ret);
+ }
+ return ret ? S_OK : E_FAIL;
+}
+
+HRESULT CFSFolder::FormatSize(UINT64 size, LPWSTR Buf, UINT cchBuf)
+{
+ if (StrFormatKBSizeW(size, Buf, cchBuf))
+ return S_OK;
+ if (cchBuf)
+ *Buf = UNICODE_NULL;
+ return E_FAIL;
+}
diff --git a/dll/win32/shell32/folders/CFSFolder.h
b/dll/win32/shell32/folders/CFSFolder.h
index e3db017578d..79f9c2c17e5 100644
--- a/dll/win32/shell32/folders/CFSFolder.h
+++ b/dll/win32/shell32/folders/CFSFolder.h
@@ -130,6 +130,13 @@ class CFSFolder :
// Helper functions shared with CDesktopFolder
static HRESULT GetFSColumnDetails(UINT iColumn, SHELLDETAILS &sd);
static HRESULT GetDefaultFSColumnState(UINT iColumn, SHCOLSTATEF &csFlags);
+ static HRESULT FormatDateTime(const FILETIME &ft, LPWSTR Buf, UINT cchBuf);
+ static HRESULT FormatSize(UINT64 size, LPWSTR Buf, UINT cchBuf);
+ static HRESULT CompareSortFoldersFirst(LPCITEMIDLIST pidl1, LPCITEMIDLIST
pidl2);
+ static inline int CompareUiStrings(LPCWSTR a, LPCWSTR b)
+ {
+ return StrCmpLogicalW(a, b);
+ }
};
#endif /* _CFSFOLDER_H_ */
diff --git a/dll/win32/shell32/folders/CRecycleBin.cpp
b/dll/win32/shell32/folders/CRecycleBin.cpp
index a9660e216a8..bd09268623b 100644
--- a/dll/win32/shell32/folders/CRecycleBin.cpp
+++ b/dll/win32/shell32/folders/CRecycleBin.cpp
@@ -58,78 +58,259 @@ static const columninfo RecycleBinColumns[] =
#define COLUMNS_COUNT 6
+// The ROS Recycle Bin PIDL format starts with a NT4/2000 Unicode FS PIDL followed by
+// BBITEMDATA and BBITEMFOOTER. This makes it compatible with SHChangeNotify listeners.
+#include "pshpack1.h"
+#define BBITEMFILETYPE (PT_FS | PT_FS_UNICODE_FLAG | PT_FS_FILE_FLAG)
+#define BBITEMFOLDERTYPE (PT_FS | PT_FS_UNICODE_FLAG | PT_FS_FOLDER_FLAG)
+struct BBITEMDATA
+{
+ FILETIME DeletionTime;
+#ifdef COLUMN_FATTS
+ WORD AttribsHi; // Nobody needs this yet
+#endif
+ WORD RecycledPathOffset;
+ WCHAR OriginalLocation[ANYSIZE_ARRAY];
+ // ... @RecycledPathOffset WCHAR RecycledPath[ANYSIZE_ARRAY];
+};
+struct BBITEMFOOTER
+{
+ enum { ENDSIG = MAKEWORD('K', 'I') }; // "Killed item".
MUST have the low bit set so _ILGetFileStructW returns NULL.
+ WORD DataSize;
+ WORD EndSignature;
+};
+#include "poppack.h"
+
+static inline BOOL IsFolder(LPCITEMIDLIST pidl)
+{
+ return _ILGetFSType(pidl) & PT_FS_FOLDER_FLAG;
+}
+
+static BBITEMDATA* ValidateItem(LPCITEMIDLIST pidl)
+{
+ const UINT minstringsize = sizeof(L"X") + sizeof(""); // PT_FS
strings
+ const UINT minfs = sizeof(WORD) + FIELD_OFFSET(PIDLDATA, u.file.szNames) +
minstringsize;
+ const UINT mindatasize = FIELD_OFFSET(BBITEMDATA, OriginalLocation) +
(sizeof(L"C:\\X") * 2);
+ const UINT minsize = minfs + mindatasize + sizeof(BBITEMFOOTER);
+ const BYTE type = _ILGetType(pidl);
+ if ((type == BBITEMFILETYPE || type == BBITEMFOLDERTYPE) && pidl->mkid.cb
>= minsize)
+ {
+ BBITEMFOOTER *pEnd = (BBITEMFOOTER*)((BYTE*)pidl + pidl->mkid.cb -
sizeof(BBITEMFOOTER));
+ if (pEnd->EndSignature == BBITEMFOOTER::ENDSIG && pEnd->DataSize
>= mindatasize)
+ return (BBITEMDATA*)((BYTE*)pEnd - pEnd->DataSize);
+ }
+ return NULL;
+}
+
+static LPITEMIDLIST CreateItem(LPCWSTR pszTrash, LPCWSTR pszOrig, const DELETED_FILE_INFO
&Details)
+{
+ const BOOL folder = Details.Attributes & FILE_ATTRIBUTE_DIRECTORY;
+ LPCWSTR pszName = PathFindFileNameW(pszTrash);
+ SIZE_T ofsName = (SIZE_T)(pszName - pszTrash);
+ SIZE_T cchName = wcslen(pszName) + 1, cbName = cchName * sizeof(WCHAR);
+ SIZE_T cbFSNames = cbName + sizeof("") + 1; // Empty short name + 1 for
WORD alignment
+ SIZE_T cbFS = sizeof(WORD) + FIELD_OFFSET(PIDLDATA, u.file.szNames) + cbFSNames;
+ SIZE_T cchTrash = ofsName + cchName, cbTrash = cchTrash * sizeof(WCHAR);
+ SIZE_T cchOrig = wcslen(pszOrig) + 1, cbOrig = cchOrig * sizeof(WCHAR);
+ SIZE_T cbData = FIELD_OFFSET(BBITEMDATA, OriginalLocation) + cbOrig + cbTrash;
+ SIZE_T cb = cbFS + cbData + sizeof(BBITEMFOOTER);
+ if (cb > 0xffff)
+ return NULL;
+ LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cb + sizeof(WORD));
+ if (!pidl)
+ return pidl;
+
+ pidl->mkid.cb = cb;
+ pidl->mkid.abID[0] = folder ? BBITEMFOLDERTYPE : BBITEMFILETYPE;
+ ILGetNext(pidl)->mkid.cb = 0; // Terminator
+ FileStruct &fsitem = ((PIDLDATA*)pidl->mkid.abID)->u.file;
+ fsitem.dummy = 0;
+ C_ASSERT(sizeof(RECYCLEBINFILESIZETYPE) <= sizeof(fsitem.dwFileSize));
+ fsitem.dwFileSize = Details.FileSize;
+ fsitem.uFileAttribs = LOWORD(Details.Attributes);
+ FileTimeToDosDateTime(&Details.LastModification, &fsitem.uFileDate,
&fsitem.uFileTime);
+ CopyMemory(fsitem.szNames, pszName, cbName);
+ LPSTR pszShort = const_cast<LPSTR>(&fsitem.szNames[cbName]);
+ pszShort[0] = '\0';
+ pszShort[1] = '\0'; // Fill alignment padding (for ILIsEqual memcmp)
+
+ BBITEMFOOTER *footer = (BBITEMFOOTER*)((BYTE*)pidl + cb - sizeof(BBITEMFOOTER));
+ footer->DataSize = cbData;
+ footer->EndSignature = BBITEMFOOTER::ENDSIG;
+
+ BBITEMDATA *data = (BBITEMDATA*)((BYTE*)footer - footer->DataSize);
+ data->DeletionTime = Details.DeletionTime;
+#ifdef COLUMN_FATTS
+ data->AttribsHi = HIWORD(Details.Attributes);
+#endif
+ data->RecycledPathOffset = FIELD_OFFSET(BBITEMDATA, OriginalLocation) + cbOrig;
+ CopyMemory(data->OriginalLocation, pszOrig, cbOrig);
+ CopyMemory((BYTE*)data + data->RecycledPathOffset, pszTrash, cbTrash);
+
+ assert(!(((SIZE_T)&fsitem.szNames) & 1)); // WORD aligned please
+ C_ASSERT(!(FIELD_OFFSET(BBITEMDATA, OriginalLocation) & 1)); // WORD aligned
please
+ assert(!(((SIZE_T)data) & 1)); // WORD aligned please
+ assert(_ILGetFSType(pidl));
+ assert(_ILIsPidlSimple(pidl));
+ assert(*(WORD*)((BYTE*)pidl + pidl->mkid.cb - sizeof(WORD)) & 1); // ENDSIG
bit
+ assert(_ILGetFileStructW(pidl) == NULL); // Our custom footer is incompatible with
WinXP pidl data
+ assert(ValidateItem(pidl) == data);
+ return pidl;
+}
+
+static inline UINT GetItemFileSize(LPCITEMIDLIST pidl)
+{
+ return _ILGetFSType(pidl) ? ((PIDLDATA*)pidl->mkid.abID)->u.file.dwFileSize :
0;
+}
+
+static inline LPCWSTR GetItemOriginalFullPath(const BBITEMDATA &Data)
+{
+ return Data.OriginalLocation;
+}
+
+static HRESULT GetItemOriginalFolder(const BBITEMDATA &Data, LPWSTR &Out)
+{
+ HRESULT hr = SHStrDupW(GetItemOriginalFullPath(Data), &Out);
+ if (SUCCEEDED(hr))
+ PathRemoveFileSpecW(Out);
+ return hr;
+}
+
+static LPCWSTR GetItemOriginalFileName(const BBITEMDATA &Data)
+{
+ return PathFindFileNameW(GetItemOriginalFullPath(Data));
+}
+
+static inline LPCWSTR GetItemRecycledFullPath(const BBITEMDATA &Data)
+{
+ return (LPCWSTR)((BYTE*)&Data + Data.RecycledPathOffset);
+}
+
+static inline LPCWSTR GetItemRecycledFileName(LPCITEMIDLIST pidl, const BBITEMDATA
&Data)
+{
+ C_ASSERT(BBITEMFILETYPE & PT_FS_UNICODE_FLAG);
+ return (LPCWSTR)((LPPIDLDATA)pidl->mkid.abID)->u.file.szNames;
+}
+
+static int GetItemDriveNumber(LPCITEMIDLIST pidl)
+{
+ if (BBITEMDATA *pData = ValidateItem(pidl))
+ return PathGetDriveNumberW(GetItemRecycledFullPath(*pData));
+ WCHAR buf[MAX_PATH];
+ return _ILSimpleGetTextW(pidl, buf, _countof(buf)) ? PathGetDriveNumberW(buf) : -1;
+}
+
+static HRESULT GetItemTypeName(PCUITEMID_CHILD pidl, const BBITEMDATA &Data,
SHFILEINFOW &shfi)
+{
+ LPCWSTR path = GetItemRecycledFullPath(Data);
+ UINT attribs = ((PIDLDATA*)pidl->mkid.abID)->u.file.uFileAttribs;
+ if (SHGetFileInfoW(path, attribs, &shfi, sizeof(shfi), SHGFI_TYPENAME |
SHGFI_USEFILEATTRIBUTES))
+ return S_OK;
+ shfi.szTypeName[0] = UNICODE_NULL;
+ return E_FAIL;
+}
+
+static HDELFILE GetRecycleBinFileHandleFromItem(const BBITEMDATA &Data)
+{
+ RECYCLEBINFILEIDENTITY identity = { Data.DeletionTime, GetItemRecycledFullPath(Data)
};
+ return GetRecycleBinFileHandle(NULL, &identity);
+}
+
/*
* Recycle Bin folder
*/
-BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile);
-
-static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl);
+static UINT GetDefaultRecycleDriveNumber()
+{
+ int drive = 0;
+ WCHAR buf[MAX_PATH];
+ if (GetWindowsDirectoryW(buf, _countof(buf)))
+ drive = PathGetDriveNumberW(buf);
+ return max(0, drive);
+}
-typedef struct _SEARCH_CONTEXT
+static inline LPCWSTR GetGlobalRecycleBinPath()
{
- PIDLRecycleStruct *pFileDetails;
- HDELFILE hDeletedFile;
- BOOL bFound;
-} SEARCH_CONTEXT, *PSEARCH_CONTEXT;
+ return NULL;
+}
-HRESULT CRecyclerExtractIcon_CreateInstance(
- LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut)
+static BOOL IsRecycleBinEmpty(IShellFolder *pSF)
{
- PIDLRecycleStruct *pFileDetails = _ILGetRecycleStruct(pidl);
- if (pFileDetails == NULL)
- goto fallback;
+ CComPtr<IEnumIDList> spEnumFiles;
+ HRESULT hr = pSF->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
&spEnumFiles);
+ CComHeapPtr<ITEMIDLIST> spPidl;
+ ULONG itemcount;
+ return FAILED(hr) || spEnumFiles->Next(1, &spPidl, &itemcount) != S_OK;
+}
- // Try to obtain the file
- SEARCH_CONTEXT Context;
- Context.pFileDetails = pFileDetails;
- Context.bFound = FALSE;
+static void CRecycleBin_ChangeNotifyBBItem(_In_ LONG Event, _In_opt_ LPCITEMIDLIST
BBItem)
+{
+ LPITEMIDLIST pidlFolder = SHCloneSpecialIDList(NULL, CSIDL_BITBUCKET, FALSE);
+ if (!pidlFolder)
+ return;
+ if (BBItem)
+ {
+ assert(ValidateItem(BBItem));
+ if (LPITEMIDLIST pidlFull = ILCombine(pidlFolder, BBItem))
+ {
+ // Send notification for [Desktop][RecycleBin][BBItem]
+ // FIXME: Windows monitors each RecycleBin FS folder on every drive
+ // instead of manually sending these?
+ SHChangeNotify(Event, SHCNF_IDLIST, pidlFull, NULL);
+ ILFree(pidlFull);
+ }
+ }
+ else
+ {
+ SHChangeNotify(Event, SHCNF_IDLIST, pidlFolder, NULL);
+ }
+ ILFree(pidlFolder);
+}
- EnumerateRecycleBinW(NULL, CBSearchRecycleBin, &Context);
- if (Context.bFound)
+EXTERN_C void CRecycleBin_NotifyRecycled(LPCWSTR OrigPath, const WIN32_FIND_DATAW
*pFind,
+ const RECYCLEBINFILEIDENTITY *pFI)
+{
+ DELETED_FILE_INFO info;
+ info.LastModification = pFind->ftLastWriteTime;
+ info.DeletionTime = pFI->DeletionTime;
+ info.FileSize = pFind->nFileSizeLow;
+ info.Attributes = pFind->dwFileAttributes;
+ if (LPITEMIDLIST pidl = CreateItem(pFI->RecycledFullPath, OrigPath, info))
{
- // This should be executed any time, if not, there are some errors in the
implementation
- IRecycleBinFile* pRecycleFile = (IRecycleBinFile*)Context.hDeletedFile;
+ CRecycleBin_ChangeNotifyBBItem(IsFolder(pidl) ? SHCNE_MKDIR : SHCNE_CREATE,
pidl);
+ ILFree(pidl);
+ }
+}
- // Query the interface from the private interface
- HRESULT hr = pRecycleFile->QueryInterface(riid, ppvOut);
+static void CRecycleBin_NotifyRemovedFromRecycleBin(LPCITEMIDLIST BBItem)
+{
+ CRecycleBin_ChangeNotifyBBItem(IsFolder(BBItem) ? SHCNE_RMDIR : SHCNE_DELETE,
BBItem);
- // Close the file handle as we don't need it anymore
- CloseRecycleBinHandle(Context.hDeletedFile);
+ 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(
+ IShellFolder &FSFolder, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
+{
+ HRESULT hr = FSFolder.GetUIObjectOf(NULL, 1, &pidl, riid, NULL, ppvOut);
+ if (SUCCEEDED(hr))
return hr;
- }
-fallback:
// In case the search fails we use a default icon
ERR("Recycler could not retrieve the icon, this shouldn't happen\n");
- CComPtr<IDefaultExtractIconInit> initIcon;
- HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,
&initIcon));
- if (FAILED_UNEXPECTEDLY(hr))
- return hr;
-
- initIcon->SetNormalIcon(swShell32Name, 0);
-
- return initIcon->QueryInterface(riid, ppvOut);
+ if (IsFolder(pidl))
+ return SHELL_CreateFallbackExtractIconForFolder(riid, ppvOut);
+ else
+ return SHELL_CreateFallbackExtractIconForNoAssocFile(riid, ppvOut);
}
-class CRecycleBinEnum :
- public CEnumIDListBase
-{
- private:
- public:
- CRecycleBinEnum();
- ~CRecycleBinEnum();
- HRESULT WINAPI Initialize(DWORD dwFlags);
- static BOOL WINAPI CBEnumRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile);
- BOOL WINAPI CBEnumRecycleBin(IN HDELFILE hDeletedFile);
-
- BEGIN_COM_MAP(CRecycleBinEnum)
- COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
- END_COM_MAP()
-};
-
class CRecycleBinItemContextMenu :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IContextMenu2
@@ -155,63 +336,23 @@ class CRecycleBinItemContextMenu :
END_COM_MAP()
};
-BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HDELFILE hDeletedFile)
-{
- PSEARCH_CONTEXT pContext = (PSEARCH_CONTEXT)Context;
-
- PDELETED_FILE_DETAILS_W pFileDetails;
- DWORD dwSize;
- BOOL ret;
-
- if (!GetDeletedFileDetailsW(hDeletedFile,
- 0,
- NULL,
- &dwSize) &&
- GetLastError() != ERROR_INSUFFICIENT_BUFFER)
- {
- ERR("GetDeletedFileDetailsW failed\n");
- return FALSE;
- }
-
- pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
- if (!pFileDetails)
- {
- ERR("No memory\n");
- return FALSE;
- }
-
- if (!GetDeletedFileDetailsW(hDeletedFile,
- dwSize,
- pFileDetails,
- NULL))
- {
- ERR("GetDeletedFileDetailsW failed\n");
- SHFree(pFileDetails);
- return FALSE;
- }
-
- ret = memcmp(pFileDetails, pContext->pFileDetails, dwSize);
- if (!ret)
- {
- pContext->hDeletedFile = hDeletedFile;
- pContext->bFound = TRUE;
- }
- else
- CloseRecycleBinHandle(hDeletedFile);
-
- SHFree(pFileDetails);
- return ret;
-}
-
-static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl)
+class CRecycleBinEnum :
+ public CEnumIDListBase
{
- LPPIDLDATA pdata = _ILGetDataPointer(pidl);
-
- if (pdata && pdata->type == 0x00)
- return (PIDLRecycleStruct*) & (pdata->u.crecycle);
+ public:
+ CRecycleBinEnum();
+ ~CRecycleBinEnum();
+ HRESULT WINAPI Initialize(DWORD dwFlags);
+ BOOL CBEnumRecycleBin(IN HDELFILE hDeletedFile);
+ static BOOL CALLBACK CBEnumRecycleBin(IN PVOID Context, IN HDELFILE
hDeletedFile)
+ {
+ return
static_cast<CRecycleBinEnum*>(Context)->CBEnumRecycleBin(hDeletedFile);
+ }
- return NULL;
-}
+ BEGIN_COM_MAP(CRecycleBinEnum)
+ COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
+ END_COM_MAP()
+};
CRecycleBinEnum::CRecycleBinEnum()
{
@@ -223,14 +364,7 @@ CRecycleBinEnum::~CRecycleBinEnum()
HRESULT WINAPI CRecycleBinEnum::Initialize(DWORD dwFlags)
{
- WCHAR szDrive[8];
- if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive) -
1))
- {
- ERR("GetEnvironmentVariableW failed\n");
- return E_FAIL;
- }
- PathAddBackslashW(szDrive);
-
+ LPCWSTR szDrive = GetGlobalRecycleBinPath();
if (dwFlags & SHCONTF_NONFOLDERS)
{
TRACE("Starting Enumeration\n");
@@ -248,83 +382,25 @@ HRESULT WINAPI CRecycleBinEnum::Initialize(DWORD dwFlags)
return S_OK;
}
-static LPITEMIDLIST _ILCreateRecycleItem(PDELETED_FILE_DETAILS_W pFileDetails)
-{
- PIDLDATA tmp;
- LPITEMIDLIST pidl;
- PIDLRecycleStruct * p;
- int size0 = (char*)&tmp.u.crecycle.szName - (char*)&tmp.u.crecycle;
- int size = size0;
-
- tmp.type = 0x00;
- size += (wcslen(pFileDetails->FileName) + 1) * sizeof(WCHAR);
-
- pidl = (LPITEMIDLIST)SHAlloc(size + 4);
- if (!pidl)
- return pidl;
-
- pidl->mkid.cb = size + 2;
- memcpy(pidl->mkid.abID, &tmp, 2 + size0);
-
- p = &((PIDLDATA*)pidl->mkid.abID)->u.crecycle;
- RtlCopyMemory(p, pFileDetails, sizeof(DELETED_FILE_DETAILS_W));
- wcscpy(p->szName, pFileDetails->FileName);
- *(WORD*)((char*)pidl + (size + 2)) = 0;
- return pidl;
-}
-
-BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN PVOID Context, IN HDELFILE
hDeletedFile)
+BOOL CRecycleBinEnum::CBEnumRecycleBin(IN HDELFILE hDeletedFile)
{
- return static_cast<CRecycleBinEnum
*>(Context)->CBEnumRecycleBin(hDeletedFile);
-}
-
-BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN HDELFILE hDeletedFile)
-{
- PDELETED_FILE_DETAILS_W pFileDetails;
- DWORD dwSize;
LPITEMIDLIST pidl = NULL;
- BOOL ret;
-
- if (!GetDeletedFileDetailsW(hDeletedFile,
- 0,
- NULL,
- &dwSize) &&
- GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ DELETED_FILE_INFO info;
+ IRecycleBinFile *pRBF = IRecycleBinFileFromHDELFILE(hDeletedFile);
+ BOOL ret = SUCCEEDED(pRBF->GetInfo(&info));
+ if (ret)
{
- ERR("GetDeletedFileDetailsW failed\n");
- return FALSE;
- }
-
- pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
- if (!pFileDetails)
- {
- ERR("No memory\n");
- return FALSE;
+ pidl = CreateItem(info.RecycledFullPath.String, info.OriginalFullPath.String,
info);
+ ret = pidl != NULL;
+ FreeRecycleBinString(&info.OriginalFullPath);
+ FreeRecycleBinString(&info.RecycledFullPath);
}
-
- if (!GetDeletedFileDetailsW(hDeletedFile,
- dwSize,
- pFileDetails,
- NULL))
+ if (pidl)
{
- ERR("GetDeletedFileDetailsW failed\n");
- SHFree(pFileDetails);
- return FALSE;
+ ret = AddToEnumList(pidl);
+ if (!ret)
+ ILFree(pidl);
}
-
- pidl = _ILCreateRecycleItem(pFileDetails);
- if (!pidl)
- {
- SHFree(pFileDetails);
- return FALSE;
- }
-
- ret = AddToEnumList(pidl);
-
- if (!ret)
- SHFree(pidl);
- SHFree(pFileDetails);
- TRACE("Returning %d\n", ret);
CloseRecycleBinHandle(hDeletedFile);
return ret;
}
@@ -351,99 +427,122 @@ HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST
pidl)
return S_OK;
}
+enum { IDC_BB_RESTORE = 1, IDC_BB_CUT, IDC_BB_DELETE, IDC_BB_PROPERTIES };
+static const CMVERBMAP g_BBItemVerbMap[] =
+{
+ { "undelete", IDC_BB_RESTORE },
+ { "cut", IDC_BB_CUT },
+ { "delete", IDC_BB_DELETE },
+ { "properties", IDC_BB_PROPERTIES },
+ { NULL }
+};
+
HRESULT WINAPI CRecycleBinItemContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu,
UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
- WCHAR szBuffer[30] = {0};
- ULONG Count = 1;
+ UINT idHigh = 0, id;
TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x
)\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
- if (LoadStringW(shell32_hInstance, IDS_RESTORE, szBuffer, _countof(szBuffer)))
+ id = idCmdFirst + IDC_BB_RESTORE;
+ if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING,
MAKEINTRESOURCEW(IDS_RESTORE), 0))
{
- szBuffer[_countof(szBuffer)-1] = L'\0';
- _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING,
szBuffer, MFS_ENABLED);
- Count++;
+ idHigh = max(idHigh, id);
+ indexMenu++;
}
-
- if (LoadStringW(shell32_hInstance, IDS_CUT, szBuffer, _countof(szBuffer)))
+ id = idCmdFirst + IDC_BB_CUT;
+ if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING,
MAKEINTRESOURCEW(IDS_CUT), MFS_DISABLED))
{
- _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR,
NULL, MFS_ENABLED);
- szBuffer[_countof(szBuffer)-1] = L'\0';
- _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING,
szBuffer, MFS_ENABLED);
+ idHigh = max(idHigh, id);
+ if (_InsertMenuItemW(hMenu, indexMenu++, TRUE, -1, MFT_SEPARATOR, NULL, 0))
+ indexMenu++;
}
-
- if (LoadStringW(shell32_hInstance, IDS_DELETE, szBuffer, _countof(szBuffer)))
+ id = idCmdFirst + IDC_BB_DELETE;
+ if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING,
MAKEINTRESOURCEW(IDS_DELETE), 0))
{
- szBuffer[_countof(szBuffer)-1] = L'\0';
- _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR,
NULL, MFS_ENABLED);
- _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING,
szBuffer, MFS_ENABLED);
+ idHigh = max(idHigh, id);
+ if (_InsertMenuItemW(hMenu, indexMenu++, TRUE, -1, MFT_SEPARATOR, NULL, 0))
+ indexMenu++;
}
-
- if (LoadStringW(shell32_hInstance, IDS_PROPERTIES, szBuffer, _countof(szBuffer)))
+ id = idCmdFirst + IDC_BB_PROPERTIES;
+ if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING,
MAKEINTRESOURCEW(IDS_PROPERTIES), 0))
{
- szBuffer[_countof(szBuffer)-1] = L'\0';
- _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR,
NULL, MFS_ENABLED);
- _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING,
szBuffer, MFS_DEFAULT);
+ idHigh = max(idHigh, id);
+ if (_InsertMenuItemW(hMenu, indexMenu++, TRUE, -1, MFT_SEPARATOR, NULL, 0))
+ indexMenu++;
}
+ return idHigh ? MAKE_HRESULT(SEVERITY_SUCCESS, 0, idHigh - idCmdFirst + 1) : S_OK;
+}
- return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Count);
+static BOOL ConfirmDelete(LPCMINVOKECOMMANDINFO lpcmi, UINT cidl, LPCITEMIDLIST pidl,
const BBITEMDATA &Data)
+{
+ if (lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)
+ {
+ return TRUE;
+ }
+ else if (cidl == 1)
+ {
+ const UINT ask = IsFolder(pidl) ? ASK_DELETE_FOLDER : ASK_DELETE_FILE;
+ return SHELL_ConfirmYesNoW(lpcmi->hwnd, ask, GetItemOriginalFileName(Data));
+ }
+ WCHAR buf[MAX_PATH];
+ wsprintfW(buf, L"%d", cidl);
+ return SHELL_ConfirmYesNoW(lpcmi->hwnd, ASK_DELETE_MULTIPLE_ITEM, buf);
}
HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
- SEARCH_CONTEXT Context;
- WCHAR szDrive[8];
-
TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi,
lpcmi->lpVerb, lpcmi->hwnd);
- if (lpcmi->lpVerb == MAKEINTRESOURCEA(1) || lpcmi->lpVerb ==
MAKEINTRESOURCEA(5))
- {
- Context.pFileDetails = _ILGetRecycleStruct(apidl);
- Context.bFound = FALSE;
+ int CmdId = SHELL_MapContextMenuVerbToCmdId(lpcmi, g_BBItemVerbMap);
- if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive)
- 1))
- {
- ERR("GetEnvironmentVariableW failed\n");
- return E_FAIL;
- }
- PathAddBackslashW(szDrive);
+ // Handle DefView accelerators
+ if ((SIZE_T)lpcmi->lpVerb == FCIDM_SHVIEW_CUT)
+ CmdId = IDC_BB_CUT;
+ if ((SIZE_T)lpcmi->lpVerb == FCIDM_SHVIEW_DELETE)
+ CmdId = IDC_BB_DELETE;
+ if ((SIZE_T)lpcmi->lpVerb == FCIDM_SHVIEW_PROPERTIES)
+ CmdId = IDC_BB_PROPERTIES;
- EnumerateRecycleBinW(szDrive, CBSearchRecycleBin, (PVOID)&Context);
- if (!Context.bFound)
+ 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;
- BOOL ret = TRUE;
-
- /* restore file */
- if (lpcmi->lpVerb == MAKEINTRESOURCEA(1))
- ret = RestoreFile(Context.hDeletedFile);
- /* delete file */
- else
- DeleteFileHandleToRecycleBin(Context.hDeletedFile);
+ HRESULT hr = S_FALSE;
+ 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;
- CloseRecycleBinHandle(Context.hDeletedFile);
+ if (hr == S_OK)
+ CRecycleBin_NotifyRemovedFromRecycleBin(apidl);
- return (ret ? S_OK : E_FAIL);
+ CloseRecycleBinHandle(hDelFile);
+ return hr;
}
- else if (lpcmi->lpVerb == MAKEINTRESOURCEA(3))
+ else if (CmdId == IDC_BB_CUT)
{
FIXME("implement cut\n");
+ SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED);
return E_NOTIMPL;
}
- else if (lpcmi->lpVerb == MAKEINTRESOURCEA(7))
+ else if (CmdId == IDC_BB_PROPERTIES)
{
FIXME("implement properties\n");
+ SHELL_ErrorBox(lpcmi->hwnd, ERROR_NOT_SUPPORTED);
return E_NOTIMPL;
}
-
- return S_OK;
+ return E_UNEXPECTED;
}
HRESULT WINAPI CRecycleBinItemContextMenu::GetCommandString(UINT_PTR idCommand, UINT
uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
{
TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand,
uFlags, lpReserved, lpszName, uMaxNameLen);
-
- return E_FAIL;
+ return SHELL_GetCommandStringImpl(idCommand, uFlags, lpszName, uMaxNameLen,
g_BBItemVerbMap);
}
HRESULT WINAPI CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM
lParam)
@@ -456,11 +555,47 @@ HRESULT WINAPI CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg,
WPARAM wPara
CRecycleBin::CRecycleBin()
{
pidl = NULL;
+ ZeroMemory(m_pFSFolders, sizeof(m_pFSFolders));
}
CRecycleBin::~CRecycleBin()
{
SHFree(pidl);
+ for (SIZE_T i = 0; i < _countof(m_pFSFolders); ++i)
+ {
+ if (m_pFSFolders[i])
+ m_pFSFolders[i]->Release();
+ }
+}
+
+IShellFolder* CRecycleBin::GetFSFolderForItem(LPCITEMIDLIST pidl)
+{
+ int drive = GetItemDriveNumber(pidl);
+ if (drive < 0)
+ drive = GetDefaultRecycleDriveNumber();
+ if ((UINT)drive >= _countof(m_pFSFolders) && FAILED_UNEXPECTEDLY(E_FAIL))
+ return NULL;
+
+ if (!m_pFSFolders[drive])
+ {
+ HRESULT hr;
+ PERSIST_FOLDER_TARGET_INFO pfti = {};
+ if (FAILED_UNEXPECTEDLY(hr = GetRecycleBinPathFromDriveNumber(drive,
pfti.szTargetParsingName)))
+ return NULL;
+ pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY;
+ pfti.csidl = -1;
+ CComHeapPtr<ITEMIDLIST> pidlRoot;
+ pidlRoot.Attach(SHELL32_CreateSimpleIDListFromPath(pfti.szTargetParsingName,
pfti.dwAttributes));
+ if (!pidlRoot && FAILED_UNEXPECTEDLY(E_FAIL))
+ return NULL;
+ IShellFolder *psf;
+ hr = SHELL32_CoCreateInitSF(pidlRoot, &pfti, NULL, &CLSID_ShellFSFolder,
IID_PPV_ARG(IShellFolder, &psf));
+ if (FAILED(hr))
+ return NULL;
+ m_pFSFolders[drive] = psf; // Reference count is 1 (for the m_pFSFolders cache)
+ }
+ m_pFSFolders[drive]->AddRef(); // AddRef for the caller
+ return m_pFSFolders[drive];
}
/*************************************************************************
@@ -472,7 +607,7 @@ HRESULT WINAPI CRecycleBin::GetClassID(CLSID *pClassID)
TRACE("(%p, %p)\n", this, pClassID);
if (pClassID == NULL)
return E_INVALIDARG;
- memcpy(pClassID, &CLSID_RecycleBin, sizeof(CLSID));
+ *pClassID = GetClassID();
return S_OK;
}
@@ -490,8 +625,7 @@ HRESULT WINAPI CRecycleBin::Initialize(PCIDLIST_ABSOLUTE pidl)
HRESULT WINAPI CRecycleBin::GetCurFolder(PIDLIST_ABSOLUTE *ppidl)
{
TRACE("\n");
- *ppidl = ILClone(pidl);
- return S_OK;
+ return SHILClone((LPCITEMIDLIST)pidl, ppidl);
}
/*************************************************************************
@@ -503,14 +637,7 @@ HRESULT WINAPI CRecycleBin::ParseDisplayName(HWND hwnd, LPBC pbc,
ULONG *pdwAttributes)
{
FIXME("stub\n");
- return E_NOTIMPL;
-}
-
-
-PDELETED_FILE_DETAILS_W
-UnpackDetailsFromPidl(LPCITEMIDLIST pidl)
-{
- return (PDELETED_FILE_DETAILS_W)&pidl->mkid.abID;
+ return E_NOTIMPL; // FIXME: Parse "D<Drive><UniqueId>.ext"
}
HRESULT WINAPI CRecycleBin::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST
*ppEnumIDList)
@@ -530,40 +657,82 @@ HRESULT WINAPI CRecycleBin::BindToStorage(PCUIDLIST_RELATIVE pidl,
LPBC pbc, REF
return E_NOTIMPL;
}
-HRESULT WINAPI CRecycleBin::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1,
PCUIDLIST_RELATIVE pidl2)
+static HRESULT CompareCanonical(const BBITEMDATA &Data1, const BBITEMDATA
&Data2)
{
- PIDLRecycleStruct* pData1 = _ILGetRecycleStruct(pidl1);
- PIDLRecycleStruct* pData2 = _ILGetRecycleStruct(pidl2);
- LPWSTR pName1, pName2;
+ // This assumes two files with the same original path cannot be deleted at
+ // the same time (within the FAT/NTFS FILETIME resolution).
+ int result = CompareFileTime(&Data1.DeletionTime, &Data2.DeletionTime);
+ if (result == 0)
+ result = _wcsicmp(GetItemOriginalFullPath(Data1),
GetItemOriginalFullPath(Data2));
+ return MAKE_COMPARE_HRESULT(result);
+}
- if(!pData1 || !pData2 || LOWORD(lParam) >= COLUMNS_COUNT)
+HRESULT WINAPI CRecycleBin::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1,
PCUIDLIST_RELATIVE pidl2)
+{
+ UINT column = UINT(lParam & SHCIDS_COLUMNMASK);
+ if (column >= COLUMNS_COUNT || !_ILGetFSType(pidl1) || !_ILGetFSType(pidl2))
+ return E_INVALIDARG;
+ BBITEMDATA *pData1 = ValidateItem(pidl1), *pData2 = ValidateItem(pidl2);
+ if ((!pData1 || !pData2) && column != COLUMN_NAME)
return E_INVALIDARG;
- SHORT result;
- LONGLONG diff;
- switch (LOWORD(lParam))
+ LPCWSTR pName1, pName2;
+ FILETIME ft1, ft2;
+ SHFILEINFOW shfi1, shfi2;
+ int result;
+ HRESULT hr = CFSFolder::CompareSortFoldersFirst(pidl1, pidl2);
+ if (SUCCEEDED(hr))
+ return hr;
+ switch (column)
{
- case 0: /* Name */
- pName1 = PathFindFileNameW(pData1->szName);
- pName2 = PathFindFileNameW(pData2->szName);
- result = _wcsicmp(pName1, pName2);
- break;
- case 1: /* Orig. Location */
- result = _wcsicmp(pData1->szName, pData2->szName);
+ case COLUMN_NAME:
+ if (pData1 && pData2)
+ {
+ if (lParam & SHCIDS_CANONICALONLY)
+ return CompareCanonical(*pData1, *pData2);
+ pName1 = GetItemOriginalFileName(*pData1);
+ pName2 = GetItemOriginalFileName(*pData2);
+ result = CFSFolder::CompareUiStrings(pName1, pName2);
+ }
+ else
+ {
+ // We support comparing names even for non-Recycle items because
+ // SHChangeNotify can broadcast regular FS items.
+ if (IShellFolder *pSF = GetFSFolderForItem(pidl1))
+ {
+ hr = pSF->CompareIDs(lParam, pidl1, pidl2);
+ pSF->Release();
+ return hr;
+ }
+ return E_INVALIDARG;
+ }
break;
- case 2: /* Date Deleted */
+ case COLUMN_DELFROM:
+ if (SUCCEEDED(hr = GetItemOriginalFolder(*pData1,
const_cast<LPWSTR&>(pName1))))
+ {
+ if (SUCCEEDED(hr = GetItemOriginalFolder(*pData2,
const_cast<LPWSTR&>(pName2))))
+ {
+ result = CFSFolder::CompareUiStrings(pName1, pName2);
+ SHFree(const_cast<LPWSTR>(pName2));
+ }
+ SHFree(const_cast<LPWSTR>(pName1));
+ }
+ return SUCCEEDED(hr) ? MAKE_COMPARE_HRESULT(result) : hr;
+ case COLUMN_DATEDEL:
result = CompareFileTime(&pData1->DeletionTime,
&pData2->DeletionTime);
break;
- case 3: /* Size */
- diff = pData1->FileSize.QuadPart - pData2->FileSize.QuadPart;
- return MAKE_COMPARE_HRESULT(diff);
- case 4: /* Type */
- pName1 = PathFindExtensionW(pData1->szName);
- pName2 = PathFindExtensionW(pData2->szName);
- result = _wcsicmp(pName1, pName2);
+ case COLUMN_SIZE:
+ result = GetItemFileSize(pidl1) - GetItemFileSize(pidl2);
+ break;
+ case COLUMN_TYPE:
+ GetItemTypeName(pidl1, *pData1, shfi1);
+ GetItemTypeName(pidl2, *pData2, shfi2);
+ result = CFSFolder::CompareUiStrings(shfi1.szTypeName, shfi2.szTypeName);
break;
- case 5: /* Modified */
- result = CompareFileTime(&pData1->LastModification,
&pData2->LastModification);
+ case COLUMN_MTIME:
+ _ILGetFileDateTime(pidl1, &ft1);
+ _ILGetFileDateTime(pidl2, &ft2);
+ result = CompareFileTime(&ft1, &ft2);
break;
}
return MAKE_COMPARE_HRESULT(result);
@@ -578,7 +747,6 @@ HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID
riid, void *
if (!ppv)
return hr;
-
*ppv = NULL;
if (IsEqualIID (riid, IID_IDropTarget))
@@ -587,11 +755,12 @@ HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID
riid, void *
}
else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid,
IID_IContextMenu2))
{
+ m_IsBackgroundMenu = true;
hr = this->QueryInterface(riid, ppv);
}
else if (IsEqualIID (riid, IID_IShellView))
{
- SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
+ SFV_CREATE sfvparams = { sizeof(SFV_CREATE), this };
hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppv);
}
else
@@ -606,8 +775,26 @@ HRESULT WINAPI CRecycleBin::GetAttributesOf(UINT cidl,
PCUITEMID_CHILD_ARRAY api
SFGAOF *rgfInOut)
{
TRACE("(%p, %d, {%p, ...}, {%x})\n", this, cidl, apidl ? apidl[0] : NULL,
(unsigned int)*rgfInOut);
- *rgfInOut &= SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANLINK;
- return S_OK;
+ HRESULT hr = S_OK;
+ const SFGAOF ThisFolder = SFGAO_FOLDER | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
SFGAO_CANRENAME | SFGAO_CANLINK;
+ if (!cidl)
+ {
+ *rgfInOut &= ThisFolder;
+ if (SHRestricted(REST_BITBUCKNOPROP))
+ *rgfInOut &= ~SFGAO_HASPROPSHEET;
+ return hr;
+ }
+ SFGAOF remain = SFGAO_LINK & *rgfInOut;
+ *rgfInOut &= remain | SFGAO_HASPROPSHEET | SFGAO_CANDELETE | SFGAO_FILESYSTEM; //
TODO: SFGAO_CANMOVE
+ for (UINT i = 0; (*rgfInOut & remain) && i < cidl &&
SUCCEEDED(hr); ++i)
+ {
+ if (IShellFolder* pSF = GetFSFolderForItem(apidl[i]))
+ {
+ hr = pSF->GetAttributesOf(1, &apidl[i], rgfInOut);
+ pSF->Release();
+ }
+ }
+ return hr;
}
HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl,
PCUITEMID_CHILD_ARRAY apidl,
@@ -623,14 +810,20 @@ HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl,
PCUITEMID_C
return hr;
*ppv = NULL;
+ assert(!cidl || (apidl && apidl[0]));
if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2))
&& (cidl >= 1))
{
+ // FIXME: Handle multiple items
hr = ShellObjectCreatorInit<CRecycleBinItemContextMenu>(apidl[0], riid,
&pObj);
}
else if((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW))
&& (cidl == 1))
{
- hr = CRecyclerExtractIcon_CreateInstance(apidl[0], riid, &pObj);
+ if (IShellFolder *pSF = GetFSFolderForItem(apidl[0]))
+ {
+ hr = CRecyclerExtractIcon_CreateInstance(*pSF, apidl[0], riid, &pObj);
+ pSF->Release();
+ }
}
else
hr = E_NOINTERFACE;
@@ -645,33 +838,36 @@ HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl,
PCUITEMID_C
HRESULT WINAPI CRecycleBin::GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF uFlags, STRRET
*pName)
{
- PIDLRecycleStruct *pFileDetails;
- LPWSTR pFileName;
-
TRACE("(%p, %p, %x, %p)\n", this, pidl, (unsigned int)uFlags, pName);
+ const BBITEMDATA *pData = ValidateItem(pidl);
+ if (!pData)
+ return E_INVALIDARG;
- pFileDetails = _ILGetRecycleStruct(pidl);
- if (!pFileDetails)
+ if (IS_SHGDN_FOR_PARSING(uFlags))
{
- pName->cStr[0] = 0;
- pName->uType = STRRET_CSTR;
- return E_INVALIDARG;
+ LPCWSTR pszName = GetItemRecycledFullPath(*pData);
+ if (uFlags & SHGDN_INFOLDER)
+ pszName = PathFindFileNameW(pszName);
+ pName->pOleStr = SHStrDupW(pszName);
}
-
- pFileName = wcsrchr(pFileDetails->szName, L'\\');
- if (!pFileName)
+ else
{
- pName->cStr[0] = 0;
- pName->uType = STRRET_CSTR;
- return E_UNEXPECTED;
+ if (uFlags & SHGDN_INFOLDER)
+ pName->pOleStr = SHStrDupW(GetItemOriginalFileName(*pData));
+ else
+ pName->pOleStr = SHStrDupW(GetItemOriginalFullPath(*pData));
}
- pName->pOleStr = StrDupW(pFileName + 1);
- if (pName->pOleStr == NULL)
- return E_OUTOFMEMORY;
-
- pName->uType = STRRET_WSTR;
- return S_OK;
+ if (pName->pOleStr)
+ {
+ pName->uType = STRRET_WSTR;
+ if (!IsFolder(pidl))
+ SHELL_FS_ProcessDisplayFilename(pName->pOleStr, uFlags);
+ return S_OK;
+ }
+ pName->uType = STRRET_CSTR;
+ pName->cStr[0] = '\0';
+ return E_OUTOFMEMORY;
}
HRESULT WINAPI CRecycleBin::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR
pszName,
@@ -719,33 +915,12 @@ HRESULT WINAPI CRecycleBin::GetDetailsEx(PCUITEMID_CHILD pidl, const
SHCOLUMNID
return E_NOTIMPL;
}
-static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME * ft)
-{
- FILETIME lft;
- SYSTEMTIME time;
- int ret;
-
- FileTimeToLocalFileTime(ft, &lft);
- FileTimeToSystemTime(&lft, &time);
-
- ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer,
size);
- if (ret > 0 && ret < size)
- {
- /* Append space + time without seconds */
- buffer[ret-1] = ' ';
- GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL,
&buffer[ret], size - ret);
- }
-
- return (ret != 0 ? E_FAIL : S_OK);
-}
-
HRESULT WINAPI CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn,
LPSHELLDETAILS pDetails)
{
- PIDLRecycleStruct * pFileDetails;
+ HRESULT hr;
+ FILETIME ft;
+ SHFILEINFOW shfi;
WCHAR buffer[MAX_PATH];
- WCHAR szTypeName[100];
- LPWSTR pszBackslash;
- UINT Length;
TRACE("(%p, %p, %d, %p)\n", this, pidl, iColumn, pDetails);
if (iColumn >= COLUMNS_COUNT)
@@ -759,66 +934,36 @@ HRESULT WINAPI CRecycleBin::GetDetailsOf(PCUITEMID_CHILD pidl, UINT
iColumn, LPS
}
if (iColumn == COLUMN_NAME)
- return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
+ return GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER,
&pDetails->str);
- pFileDetails = _ILGetRecycleStruct(pidl);
- if (!pFileDetails && FAILED_UNEXPECTEDLY(E_INVALIDARG))
+ const BBITEMDATA *pData = ValidateItem(pidl);
+ if (!pData && FAILED_UNEXPECTEDLY(E_INVALIDARG))
return E_INVALIDARG;
+
switch (iColumn)
{
case COLUMN_DATEDEL:
- FormatDateTime(buffer, MAX_PATH, &pFileDetails->DeletionTime);
+ CFSFolder::FormatDateTime(pData->DeletionTime, buffer, _countof(buffer));
break;
case COLUMN_DELFROM:
- pszBackslash = wcsrchr(pFileDetails->szName, L'\\');
- if (!pszBackslash)
- {
- ERR("Filename '%ls' not a valid path?\n",
pFileDetails->szName);
- Length = 0;
- }
- else
- {
- Length = (pszBackslash - pFileDetails->szName);
- memcpy((LPVOID)buffer, pFileDetails->szName, Length * sizeof(WCHAR));
- }
- buffer[Length] = UNICODE_NULL;
- if (buffer[0] && buffer[1] == L':' && !buffer[2])
- {
- buffer[2] = L'\\';
- buffer[3] = UNICODE_NULL;
- }
- break;
+ if (SUCCEEDED(hr = GetItemOriginalFolder(*pData, pDetails->str.pOleStr)))
+ pDetails->str.uType = STRRET_WSTR;
+ return hr;
case COLUMN_SIZE:
- StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH);
+ *buffer = UNICODE_NULL;
+ if (!IsFolder(pidl))
+ CFSFolder::FormatSize(GetItemFileSize(pidl), buffer, _countof(buffer));
break;
case COLUMN_MTIME:
- FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification);
+ _ILGetFileDateTime(pidl, &ft);
+ CFSFolder::FormatDateTime(ft, buffer, _countof(buffer));
break;
case COLUMN_TYPE:
- {
- SEARCH_CONTEXT Context;
- Context.pFileDetails = pFileDetails;
- Context.bFound = FALSE;
- EnumerateRecycleBinW(NULL, CBSearchRecycleBin, (PVOID)&Context);
-
- if (Context.bFound)
- {
- GetDeletedFileTypeNameW(Context.hDeletedFile, buffer,
_countof(buffer), NULL);
-
- CloseRecycleBinHandle(Context.hDeletedFile);
- }
- /* load localized file string */
- else if (LoadStringW(shell32_hInstance, IDS_ANY_FILE, szTypeName,
_countof(szTypeName)))
- {
- StringCchPrintfW(buffer, _countof(buffer), szTypeName,
PathFindExtensionW(pFileDetails->szName));
- }
-
- return SHSetStrRet(&pDetails->str, buffer);
- }
+ GetItemTypeName(pidl, *pData, shfi);
+ return SHSetStrRet(&pDetails->str, shfi.szTypeName);
default:
return E_FAIL;
}
-
return SHSetStrRet(&pDetails->str, buffer);
}
@@ -832,85 +977,75 @@ HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID
*pscid)
return S_OK;
}
-BOOL CRecycleBin::RecycleBinIsEmpty()
-{
- CComPtr<IEnumIDList> spEnumFiles;
- HRESULT hr = EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
&spEnumFiles);
- if (FAILED(hr))
- return TRUE;
- CComHeapPtr<ITEMIDLIST> spPidl;
- ULONG itemcount;
- return spEnumFiles->Next(1, &spPidl, &itemcount) != S_OK;
- }
-
/*************************************************************************
* RecycleBin IContextMenu interface
*/
-HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT
idCmdFirst, UINT idCmdLast, UINT uFlags)
+enum { IDC_EMPTYRECYCLEBIN = 1, IDC_PROPERTIES };
+static const CMVERBMAP g_BBFolderVerbMap[] =
{
- WCHAR szBuffer[100];
- MENUITEMINFOW mii;
- int id = 1;
+ { "empty", IDC_EMPTYRECYCLEBIN },
+ { "properties", IDC_PROPERTIES },
+ { NULL }
+};
+HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT
idCmdFirst, UINT idCmdLast, UINT uFlags)
+{
TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu,
idCmdFirst, idCmdLast, uFlags );
if (!hMenu)
return E_INVALIDARG;
- ZeroMemory(&mii, sizeof(mii));
- mii.cbSize = sizeof(mii);
- mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
- mii.fState = RecycleBinIsEmpty() ? MFS_DISABLED : MFS_ENABLED;
- szBuffer[0] = L'\0';
- LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, _countof(szBuffer));
- mii.dwTypeData = szBuffer;
- mii.cch = wcslen(mii.dwTypeData);
- mii.wID = idCmdFirst + id++;
- mii.fType = MFT_STRING;
- iIdEmpty = 1;
-
- if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
- return E_FAIL;
+ UINT idHigh = 0, id;
- return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
+ WORD state = IsRecycleBinEmpty(this) ? MFS_DISABLED : MFS_ENABLED;
+ id = idCmdFirst + IDC_EMPTYRECYCLEBIN;
+ if (_InsertMenuItemW(hMenu, indexMenu, TRUE, id, MFT_STRING,
MAKEINTRESOURCEW(IDS_EMPTY_BITBUCKET), state))
+ {
+ idHigh = max(idHigh, id);
+ if (m_IsBackgroundMenu && !SHRestricted(REST_BITBUCKNOPROP))
+ {
+ id = idCmdFirst + IDC_PROPERTIES;
+ if (_InsertMenuItemW(hMenu, ++indexMenu, TRUE, id, MFT_STRING,
MAKEINTRESOURCEW(IDS_PROPERTIES), 0))
+ {
+ idHigh = max(idHigh, id);
+ _InsertMenuItemW(hMenu, indexMenu++, TRUE, -1, MFT_SEPARATOR, NULL, 0);
+ }
+ }
+ }
+ return idHigh ? MAKE_HRESULT(SEVERITY_SUCCESS, 0, idHigh - idCmdFirst + 1) : S_OK;
}
HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
- HRESULT hr;
- LPSHELLBROWSER lpSB;
- IShellView * lpSV = NULL;
- WCHAR szDrive[8];
-
- TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb);
-
- if (LOWORD(lpcmi->lpVerb) == iIdEmpty)
+ TRACE("%p %p verb %p\n", this, lpcmi, lpcmi ? lpcmi->lpVerb : NULL);
+ int CmdId = SHELL_MapContextMenuVerbToCmdId(lpcmi, g_BBFolderVerbMap);
+ if (CmdId == IDC_EMPTYRECYCLEBIN)
{
- if (!GetEnvironmentVariableW(L"SystemDrive", szDrive, _countof(szDrive)
- 1))
- {
- ERR("GetEnvironmentVariableW failed\n");
- return E_FAIL;
- }
- PathAddBackslashW(szDrive);
-
- hr = SHEmptyRecycleBinW(lpcmi->hwnd, szDrive, 0);
+ HRESULT hr = SHEmptyRecycleBinW(lpcmi->hwnd, NULL, 0);
TRACE("result %x\n", hr);
if (hr != S_OK)
return hr;
-
- lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
- if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV)))
- lpSV->Refresh();
+#if 0 // This is a nasty hack because lpcmi->hwnd might not be a shell browser.
+ // Not required with working SHChangeNotify.
+ CComPtr<IShellView> pSV;
+ LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessage(lpcmi->hwnd,
CWM_GETISHELLBROWSER, 0, 0);
+ if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&pSV)))
+ pSV->Refresh();
+#endif
+ return hr;
}
- return S_OK;
+ else if (CmdId == IDC_PROPERTIES)
+ {
+ return SHELL_ShowItemIDListProperties((LPITEMIDLIST)CSIDL_BITBUCKET);
+ }
+ return E_INVALIDARG;
}
HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT
*lpReserved, LPSTR lpszName, UINT uMaxNameLen)
{
- FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved,
lpszName, uMaxNameLen);
-
- return E_NOTIMPL;
+ TRACE("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved,
lpszName, uMaxNameLen);
+ return SHELL_GetCommandStringImpl(idCommand, uFlags, lpszName, uMaxNameLen,
g_BBFolderVerbMap);
}
/*************************************************************************
@@ -937,6 +1072,7 @@ HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID,
LPFNSVADDPROPSHEETPAGE pf
HRESULT WINAPI CRecycleBin::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj,
HKEY hkeyProgID)
{
TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID );
+ m_IsBackgroundMenu = false;
return S_OK;
}
@@ -982,7 +1118,7 @@ TRASH_CanTrashFile(LPCWSTR wszPath)
swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber),
HIWORD(VolSerialNumber));
wcscat(szKey, szBuffer);
- if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey,
&dwDisposition) != ERROR_SUCCESS)
+ if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL,
&hKey, &dwDisposition) != ERROR_SUCCESS)
{
ERR("RegCreateKeyExW failed\n");
return FALSE;
@@ -996,8 +1132,6 @@ TRASH_CanTrashFile(LPCWSTR wszPath)
/* per default unlimited size */
dwSize = -1;
RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize,
sizeof(DWORD));
- RegCloseKey(hKey);
- return TRUE;
}
else
{
@@ -1005,27 +1139,18 @@ TRASH_CanTrashFile(LPCWSTR wszPath)
ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType,
(LPBYTE)&dwNukeOnDelete, &dwSize);
if (ret != ERROR_SUCCESS)
{
- if (ret == ERROR_FILE_NOT_FOUND)
+ dwNukeOnDelete = 0;
+ if (ret == ERROR_FILE_NOT_FOUND)
{
/* restore key and enable bitbucket */
- dwNukeOnDelete = 0;
RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD,
(LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
}
- RegCloseKey(hKey);
- return TRUE;
}
- else if (dwNukeOnDelete)
- {
- /* do not delete to bitbucket */
- RegCloseKey(hKey);
- return FALSE;
- }
- /* FIXME
- * check if bitbucket is full
- */
- RegCloseKey(hKey);
- return TRUE;
}
+ BOOL bCanTrash = !dwNukeOnDelete;
+ // FIXME: Check if bitbucket is full (CORE-13743)
+ RegCloseKey(hKey);
+ return bCanTrash;
}
BOOL
@@ -1178,7 +1303,7 @@ HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath,
DWORD dwFlags)
while (penumFiles->Next(1, &pidl, NULL) == S_OK)
{
count++;
- pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL, &StrRet);
+ pRecycleBin->GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER,
&StrRet);
StrRetToBuf(&StrRet, pidl, szBuffer, _countof(szBuffer));
CoTaskMemFree(pidl);
}
@@ -1228,6 +1353,7 @@ HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath,
DWORD dwFlags)
if (!ret)
return HRESULT_FROM_WIN32(GetLastError());
+ CRecycleBin_ChangeNotifyBBItem(SHCNE_UPDATEDIR, NULL);
if (!(dwFlags & SHERB_NOSOUND))
{
TRASH_PlayEmptyRecycleBinSound();
diff --git a/dll/win32/shell32/folders/CRecycleBin.h
b/dll/win32/shell32/folders/CRecycleBin.h
index ecf56699447..7d676cd6875 100644
--- a/dll/win32/shell32/folders/CRecycleBin.h
+++ b/dll/win32/shell32/folders/CRecycleBin.h
@@ -37,12 +37,15 @@ class CRecycleBin :
{
private:
LPITEMIDLIST pidl;
- INT iIdEmpty;
- BOOL RecycleBinIsEmpty();
+ IShellFolder *m_pFSFolders[RECYCLEBINMAXDRIVECOUNT];
+ bool m_IsBackgroundMenu;
+
+ IShellFolder* GetFSFolderForItem(LPCITEMIDLIST pidl);
public:
CRecycleBin();
~CRecycleBin();
+ static inline REFCLSID GetClassID() { return CLSID_RecycleBin; }
// IPersistFolder
STDMETHOD(GetClassID)(CLSID *pClassID) override;
diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h
index a28c023d244..48c636fe9f1 100644
--- a/dll/win32/shell32/precomp.h
+++ b/dll/win32/shell32/precomp.h
@@ -262,6 +262,12 @@ SHBindToObjectEx(
_In_ REFIID riid,
_Out_ void **ppvObj);
+EXTERN_C HRESULT
+SHELL_GetUIObjectOfAbsoluteItem(
+ _In_opt_ HWND hWnd,
+ _In_ PCIDLIST_ABSOLUTE pidl,
+ _In_ REFIID riid, _Out_ void **ppvObj);
+
DWORD
SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD
dwAttributes);
HRESULT SHELL_GetIDListTarget(_In_ LPCITEMIDLIST pidl, _Out_ PIDLIST_ABSOLUTE *ppidl);
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin.c
b/dll/win32/shell32/shellrecyclebin/recyclebin.c
index 88221b0da99..5a0153bacf5 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin.c
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin.c
@@ -12,7 +12,7 @@ BOOL WINAPI
CloseRecycleBinHandle(
IN HDELFILE hDeletedFile)
{
- IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
+ IRecycleBinFile *rbf = IRecycleBinFileFromHDELFILE(hDeletedFile);
HRESULT hr;
TRACE("(%p)\n", hDeletedFile);
@@ -90,7 +90,7 @@ cleanup:
}
BOOL WINAPI
-DeleteFileHandleToRecycleBin(
+DeleteFileInRecycleBin(
IN HDELFILE hDeletedFile)
{
IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
@@ -231,9 +231,10 @@ EnumerateRecycleBinW(
}
else if (!SUCCEEDED(hr))
goto cleanup;
- if (!pFnCallback(Context, (HANDLE)prbf))
+ if (!pFnCallback(Context, (HDELFILE)prbf))
{
- hr = HRESULT_FROM_WIN32(GetLastError());
+ UINT error = GetLastError();
+ hr = HRESULT_FROM_WIN32(error);
goto cleanup;
}
}
@@ -252,132 +253,38 @@ cleanup:
return FALSE;
}
-BOOL WINAPI
-GetDeletedFileTypeNameW(
- IN HDELFILE hDeletedFile,
- OUT LPWSTR pTypeName,
- IN DWORD BufferSize,
- OUT LPDWORD RequiredSize OPTIONAL)
+typedef struct _BBENUMFILECONTEXT
{
- IRecycleBinFile *prbf = (IRecycleBinFile *)hDeletedFile;
- SIZE_T FinalSize;
-
- HRESULT hr = IRecycleBinFile_GetTypeName(prbf, BufferSize, pTypeName,
&FinalSize);
-
- if (SUCCEEDED(hr))
- {
- if (RequiredSize)
- *RequiredSize = (DWORD)FinalSize;
+ const RECYCLEBINFILEIDENTITY *pFI;
+ HDELFILE hDelFile;
+} BBENUMFILECONTEXT;
- return TRUE;
- }
- if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
- SetLastError(HRESULT_CODE(hr));
- else
- SetLastError(ERROR_GEN_FAILURE);
- return FALSE;
-}
-
-BOOL WINAPI
-GetDeletedFileDetailsA(
- IN HDELFILE hDeletedFile,
- IN DWORD BufferSize,
- IN OUT PDELETED_FILE_DETAILS_A FileDetails OPTIONAL,
- OUT LPDWORD RequiredSize OPTIONAL)
+static BOOL CALLBACK
+GetRecycleBinFileHandleCallback(IN PVOID Context, IN HDELFILE hDeletedFile)
{
- PDELETED_FILE_DETAILS_W FileDetailsW = NULL;
- DWORD BufferSizeW = 0;
- BOOL ret = FALSE;
-
- TRACE("(%p, %lu, %p, %p)\n", hDeletedFile, BufferSize, FileDetails,
RequiredSize);
-
- if (BufferSize >= FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName))
+ BBENUMFILECONTEXT *pCtx = (BBENUMFILECONTEXT*)Context;
+ IRecycleBinFile *pRBF = IRecycleBinFileFromHDELFILE(hDeletedFile);
+ if (IRecycleBinFile_IsEqualIdentity(pRBF, pCtx->pFI) == S_OK)
{
- BufferSizeW = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName)
- + (BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName)) *
sizeof(WCHAR);
+ pCtx->hDelFile = hDeletedFile;
+ return FALSE;
}
- if (FileDetails && BufferSizeW)
- {
- FileDetailsW = HeapAlloc(GetProcessHeap(), 0, BufferSizeW);
- if (!FileDetailsW)
- {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto cleanup;
- }
- }
-
- ret = GetDeletedFileDetailsW(hDeletedFile, BufferSizeW, FileDetailsW, RequiredSize);
- if (!ret)
- goto cleanup;
-
- if (FileDetails)
- {
- CopyMemory(FileDetails, FileDetailsW, FIELD_OFFSET(DELETED_FILE_DETAILS_A,
FileName));
- if (0 == WideCharToMultiByte(CP_ACP, 0, FileDetailsW->FileName, -1,
FileDetails->FileName, BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName),
NULL, NULL))
- goto cleanup;
- }
- ret = TRUE;
-
-cleanup:
- HeapFree(GetProcessHeap(), 0, FileDetailsW);
- return ret;
+ CloseRecycleBinHandle(hDeletedFile);
+ return TRUE;
}
-BOOL WINAPI
-GetDeletedFileDetailsW(
- IN HDELFILE hDeletedFile,
- IN DWORD BufferSize,
- IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL,
- OUT LPDWORD RequiredSize OPTIONAL)
+EXTERN_C HDELFILE
+GetRecycleBinFileHandle(
+ IN LPCWSTR pszRoot OPTIONAL,
+ IN const RECYCLEBINFILEIDENTITY *pFI)
{
- IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
- HRESULT hr;
- SIZE_T NameSize, Needed;
-
- TRACE("(%p, %lu, %p, %p)\n", hDeletedFile, BufferSize, FileDetails,
RequiredSize);
-
- hr = IRecycleBinFile_GetFileName(rbf, 0, NULL, &NameSize);
- if (!SUCCEEDED(hr))
- goto cleanup;
- Needed = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName) + NameSize;
- if (RequiredSize)
- *RequiredSize = (DWORD)Needed;
- if (Needed > BufferSize)
- {
- hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
- goto cleanup;
- }
- hr = IRecycleBinFile_GetFileName(rbf, NameSize, FileDetails->FileName, NULL);
- if (!SUCCEEDED(hr))
- goto cleanup;
- hr = IRecycleBinFile_GetLastModificationTime(rbf,
&FileDetails->LastModification);
- if (!SUCCEEDED(hr))
- goto cleanup;
- hr = IRecycleBinFile_GetDeletionTime(rbf, &FileDetails->DeletionTime);
- if (!SUCCEEDED(hr))
- goto cleanup;
- hr = IRecycleBinFile_GetFileSize(rbf, &FileDetails->FileSize);
- if (!SUCCEEDED(hr))
- goto cleanup;
- hr = IRecycleBinFile_GetPhysicalFileSize(rbf,
&FileDetails->PhysicalFileSize);
- if (!SUCCEEDED(hr))
- goto cleanup;
- hr = IRecycleBinFile_GetAttributes(rbf, &FileDetails->Attributes);
- if (!SUCCEEDED(hr))
- goto cleanup;
-
-cleanup:
- if (SUCCEEDED(hr))
- return TRUE;
- if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
- SetLastError(HRESULT_CODE(hr));
- else
- SetLastError(ERROR_GEN_FAILURE);
- return FALSE;
+ BBENUMFILECONTEXT context = { pFI, NULL };
+ EnumerateRecycleBinW(pszRoot, GetRecycleBinFileHandleCallback, &context);
+ return context.hDelFile;
}
BOOL WINAPI
-RestoreFile(
+RestoreFileFromRecycleBin(
IN HDELFILE hDeletedFile)
{
IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
@@ -395,7 +302,7 @@ RestoreFile(
return FALSE;
}
-HRESULT WINAPI
+EXTERN_C HRESULT
GetDefaultRecycleBin(
IN LPCWSTR pszVolume OPTIONAL,
OUT IRecycleBin **pprb)
@@ -425,3 +332,17 @@ GetDefaultRecycleBin(
IUnknown_Release(pUnk);
return hr;
}
+
+EXTERN_C HRESULT
+GetRecycleBinPathFromDriveNumber(UINT Drive, LPWSTR Path)
+{
+ const WCHAR volume[] = { LOWORD('A' + Drive), ':', '\\',
'\0' };
+ IRecycleBin *pRB;
+ HRESULT hr = GetDefaultRecycleBin(volume, &pRB);
+ if (SUCCEEDED(hr))
+ {
+ hr = IRecycleBin_GetDirectory(pRB, Path);
+ IRecycleBin_Release(pRB);
+ }
+ return hr;
+}
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin.h
b/dll/win32/shell32/shellrecyclebin/recyclebin.h
index a48f49264cf..b3795b04ebc 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin.h
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin.h
@@ -17,48 +17,64 @@ extern "C" {
#include <shellapi.h>
#include <objbase.h>
-#define ANY_SIZE 1
+#define RECYCLEBINMAXDRIVECOUNT 26
/* Structures used by the API Interface */
-typedef struct _DELETED_FILE_DETAILS_A
+typedef UINT RECYCLEBINFILESIZETYPE;
+
+typedef struct _RECYCLEBINFILEIDENTITY
{
- FILETIME LastModification;
- FILETIME DeletionTime;
- ULARGE_INTEGER FileSize;
- ULARGE_INTEGER PhysicalFileSize;
- DWORD Attributes;
- CHAR FileName[ANY_SIZE];
-} DELETED_FILE_DETAILS_A, *PDELETED_FILE_DETAILS_A;
-typedef struct _DELETED_FILE_DETAILS_W
+ FILETIME DeletionTime;
+ LPCWSTR RecycledFullPath; /* "C:\Recycled\Dc1.ext" etc. */
+} RECYCLEBINFILEIDENTITY, *PRECYCLEBINFILEIDENTITY;
+
+typedef struct _RECYCLEBINSTRING
{
- FILETIME LastModification;
- FILETIME DeletionTime;
- ULARGE_INTEGER FileSize;
- ULARGE_INTEGER PhysicalFileSize;
- DWORD Attributes;
- WCHAR FileName[ANY_SIZE];
-} DELETED_FILE_DETAILS_W, *PDELETED_FILE_DETAILS_W;
-#ifdef UNICODE
-#define DELETED_FILE_DETAILS DELETED_FILE_DETAILS_W
-#define PDELETED_FILE_DETAILS PDELETED_FILE_DETAILS_W
-#else
-#define DELETED_FILE_DETAILS DELETED_FILE_DETAILS_A
-#define PDELETED_FILE_DETAILS PDELETED_FILE_DETAILS_A
-#endif
+ LPCWSTR String;
+ LPWSTR Alloc;
+} RECYCLEBINSTRING, *PRECYCLEBINSTRING;
+
+typedef struct _DELETED_FILE_INFO
+{
+ FILETIME LastModification;
+ FILETIME DeletionTime;
+ RECYCLEBINFILESIZETYPE FileSize;
+ DWORD Attributes;
+ RECYCLEBINSTRING OriginalFullPath;
+ RECYCLEBINSTRING RecycledFullPath;
+} DELETED_FILE_INFO, *PDELETED_FILE_INFO;
/* Distinct handle type for deleted file/folder */
DECLARE_HANDLE(HDELFILE);
+#define IRecycleBinFileFromHDELFILE(hDF) ( (IRecycleBinFile*)(hDF) )
/* API Interface */
+static inline void
+FreeRecycleBinString(PRECYCLEBINSTRING pRBS)
+{
+ SHFree(pRBS->Alloc);
+ pRBS->String = pRBS->Alloc = NULL;
+}
+
+static inline void
+InitializeRecycleBinStringRef(PRECYCLEBINSTRING pRBS, LPCWSTR String)
+{
+ pRBS->String = String;
+ pRBS->Alloc = NULL;
+}
+
+EXTERN_C HRESULT
+GetRecycleBinPathFromDriveNumber(UINT Drive, LPWSTR Path);
+
/* Function called for each deleted file in the recycle bin
* Context: value given by the caller of the EnumerateRecycleBin function
* hDeletedFile: a handle to the deleted file
* Returning FALSE stops the enumeration.
* Remarks: the handle must be closed with the CloseRecycleBinHandle function
*/
-typedef BOOL (WINAPI *PENUMERATE_RECYCLEBIN_CALLBACK)(IN PVOID Context, IN HDELFILE
hDeletedFile);
+typedef BOOL (CALLBACK *PENUMERATE_RECYCLEBIN_CALLBACK)(IN PVOID Context, IN HDELFILE
hDeletedFile);
/* Closes a file deleted handle.
* hDeletedFile: the handle to close
@@ -85,13 +101,13 @@ DeleteFileToRecycleBinW(
#define DeleteFileToRecycleBin DeleteFileToRecycleBinA
#endif
-/* Moves a file to the recycle bin.
+/* Deletes a file in the recycle bin.
* hDeletedFile: handle of the deleted file to delete
* Returns TRUE if operation succeeded, FALSE otherwise.
* Remark: The handle is obtained in the PENUMERATE_RECYCLEBIN_CALLBACK callback
*/
BOOL WINAPI
-DeleteFileHandleToRecycleBin(
+DeleteFileInRecycleBin(
IN HDELFILE hDeletedFile);
/* Removes all elements contained in a recycle bin
@@ -134,38 +150,10 @@ EnumerateRecycleBinW(
#define EnumerateRecycleBin EnumerateRecycleBinA
#endif
-BOOL WINAPI
-GetDeletedFileTypeNameW(
- IN HDELFILE hDeletedFile,
- OUT LPWSTR pTypeName,
- IN DWORD BufferSize,
- OUT LPDWORD RequiredSize OPTIONAL);
-
-/* Gets details about a deleted file
- * hDeletedFile: handle of the deleted file to get details about
- * BufferSize: size of the 'FileDetails' buffer, in bytes
- * FileDetails: if the function succeeded, contains details about the deleted file
- * RequiredSize: contains the minimal buffer size required to get file information
details
- * Returns TRUE if operation succeeded, FALSE otherwise.
- * Remark: The handle is obtained in the PENUMERATE_RECYCLEBIN_CALLBACK callback
- */
-BOOL WINAPI
-GetDeletedFileDetailsA(
- IN HDELFILE hDeletedFile,
- IN DWORD BufferSize,
- IN OUT PDELETED_FILE_DETAILS_A FileDetails OPTIONAL,
- OUT LPDWORD RequiredSize OPTIONAL);
-BOOL WINAPI
-GetDeletedFileDetailsW(
- IN HDELFILE hDeletedFile,
- IN DWORD BufferSize,
- IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL,
- OUT LPDWORD RequiredSize OPTIONAL);
-#ifdef UNICODE
-#define GetDeletedFileDetails GetDeletedFileDetailsW
-#else
-#define GetDeletedFileDetails GetDeletedFileDetailsA
-#endif
+EXTERN_C HDELFILE
+GetRecycleBinFileHandle(
+ IN LPCWSTR pszRoot OPTIONAL,
+ IN const RECYCLEBINFILEIDENTITY *pFI);
/* Restores a deleted file
* hDeletedFile: handle of the deleted file to restore
@@ -173,7 +161,7 @@ GetDeletedFileDetailsW(
* Remarks: if the function succeeds, the handle is not valid anymore.
*/
BOOL WINAPI
-RestoreFile(
+RestoreFileFromRecycleBin(
IN HDELFILE hDeletedFile);
/* COM interface */
@@ -189,13 +177,14 @@ DECLARE_INTERFACE_(IRecycleBinFile, IUnknown)
STDMETHOD_(ULONG, Release)(THIS) PURE;
/* IRecycleBinFile methods */
+ STDMETHOD(IsEqualIdentity)(THIS_ const RECYCLEBINFILEIDENTITY *pFI) PURE;
+ STDMETHOD(GetInfo)(THIS_ PDELETED_FILE_INFO pInfo) PURE;
STDMETHOD(GetLastModificationTime)(THIS_ FILETIME *pLastModificationTime) PURE;
STDMETHOD(GetDeletionTime)(THIS_ FILETIME *pDeletionTime) PURE;
STDMETHOD(GetFileSize)(THIS_ ULARGE_INTEGER *pFileSize) PURE;
STDMETHOD(GetPhysicalFileSize)(THIS_ ULARGE_INTEGER *pPhysicalFileSize) PURE;
STDMETHOD(GetAttributes)(THIS_ DWORD *pAttributes) PURE;
STDMETHOD(GetFileName)(THIS_ SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize)
PURE;
- STDMETHOD(GetTypeName)(THIS_ SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize)
PURE;
STDMETHOD(Delete)(THIS) PURE;
STDMETHOD(Restore)(THIS) PURE;
@@ -233,9 +222,10 @@ DECLARE_INTERFACE_(IRecycleBin, IUnknown)
STDMETHOD_(ULONG, Release)(THIS) PURE;
/* IRecycleBin methods */
- STDMETHOD(DeleteFile)(THIS_ LPCWSTR szFileName);
- STDMETHOD(EmptyRecycleBin)(THIS);
- STDMETHOD(EnumObjects)(THIS_ IRecycleBinEnumList **ppEnumList);
+ STDMETHOD(DeleteFile)(THIS_ LPCWSTR szFileName) PURE;
+ STDMETHOD(EmptyRecycleBin)(THIS) PURE;
+ STDMETHOD(EnumObjects)(THIS_ IRecycleBinEnumList **ppEnumList) PURE;
+ STDMETHOD(GetDirectory)(THIS_ LPWSTR szPath) PURE;
END_INTERFACE
};
@@ -252,6 +242,10 @@ EXTERN_C const IID IID_IRecycleBin;
(This)->lpVtbl->AddRef(This)
#define IRecycleBinFile_Release(This) \
(This)->lpVtbl->Release(This)
+#define IRecycleBinFile_IsEqualIdentity(This, pFI) \
+ (This)->lpVtbl->IsEqualIdentity(This, pFI)
+#define IRecycleBinFile_GetInfo(This, pInfo) \
+ (This)->lpVtbl->GetInfo(This, pInfo)
#define IRecycleBinFile_GetLastModificationTime(This, pLastModificationTime) \
(This)->lpVtbl->GetLastModificationTime(This, pLastModificationTime)
#define IRecycleBinFile_GetDeletionTime(This, pDeletionTime) \
@@ -264,8 +258,6 @@ EXTERN_C const IID IID_IRecycleBin;
(This)->lpVtbl->GetAttributes(This, pAttributes)
#define IRecycleBinFile_GetFileName(This, BufferSize, Buffer, RequiredSize) \
(This)->lpVtbl->GetFileName(This, BufferSize, Buffer, RequiredSize)
-#define IRecycleBinFile_GetTypeName(This, BufferSize, Buffer, RequiredSize) \
- (This)->lpVtbl->GetTypeName(This, BufferSize, Buffer, RequiredSize)
#define IRecycleBinFile_Delete(This) \
(This)->lpVtbl->Delete(This)
#define IRecycleBinFile_Restore(This) \
@@ -296,13 +288,20 @@ EXTERN_C const IID IID_IRecycleBin;
(This)->lpVtbl->EmptyRecycleBin(This)
#define IRecycleBin_EnumObjects(This, ppEnumList) \
(This)->lpVtbl->EnumObjects(This, ppEnumList)
+#define IRecycleBin_GetDirectory(This, szPath) \
+ (This)->lpVtbl->GetDirectory(This, szPath)
#endif
-HRESULT WINAPI
+EXTERN_C HRESULT
GetDefaultRecycleBin(
IN LPCWSTR pszVolume OPTIONAL,
OUT IRecycleBin **pprb);
+/* Recycle Bin shell folder internal API */
+void CRecycleBin_NotifyRecycled(LPCWSTR OrigPath, const WIN32_FIND_DATAW *pFind,
+ const RECYCLEBINFILEIDENTITY *pFI);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_generic.cpp
b/dll/win32/shell32/shellrecyclebin/recyclebin_generic.cpp
index a7ad1b00de0..09f561f40b6 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin_generic.cpp
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin_generic.cpp
@@ -23,6 +23,10 @@ public:
STDMETHODIMP DeleteFile(LPCWSTR szFileName) override;
STDMETHODIMP EmptyRecycleBin() override;
STDMETHODIMP EnumObjects(IRecycleBinEnumList **ppEnumList) override;
+ STDMETHODIMP GetDirectory(LPWSTR szPath) override
+ {
+ return E_UNEXPECTED;
+ }
protected:
LONG m_ref;
@@ -183,3 +187,11 @@ HRESULT RecycleBinGeneric_Constructor(OUT IUnknown **ppUnknown)
*ppUnknown = static_cast<IRecycleBin *>(pThis);
return S_OK;
}
+
+EXTERN_C
+BOOL RecycleBinGeneric_IsEqualFileIdentity(const RECYCLEBINFILEIDENTITY *p1, const
RECYCLEBINFILEIDENTITY *p2)
+{
+ return p1->DeletionTime.dwLowDateTime == p2->DeletionTime.dwLowDateTime
&&
+ p1->DeletionTime.dwHighDateTime == p2->DeletionTime.dwHighDateTime
&&
+ _wcsicmp(p1->RecycledFullPath, p2->RecycledFullPath) == 0;
+}
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_private.h
b/dll/win32/shell32/shellrecyclebin/recyclebin_private.h
index 42cffd22656..57e440d3899 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin_private.h
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin_private.h
@@ -13,11 +13,20 @@
#include <wine/debug.h>
WINE_DEFAULT_DEBUG_CHANNEL(recyclebin);
+#ifdef __cplusplus
+static inline HRESULT HResultFromWin32(DWORD hr)
+{
+ // HRESULT_FROM_WIN32 will evaluate its parameter twice, this function will not.
+ return HRESULT_FROM_WIN32(hr);
+}
+#endif
+
/* Defines */
#define RECYCLE_BIN_DIRECTORY_WITH_ACL L"RECYCLER"
#define RECYCLE_BIN_DIRECTORY_WITHOUT_ACL L"RECYCLED"
#define RECYCLE_BIN_FILE_NAME L"INFO2"
+#define RECYCLE_BIN_FILE_NAME_V1 L"INFO"
#define ROUND_UP(N, S) ((( (N) + (S) - 1) / (S) ) * (S) )
@@ -43,6 +52,9 @@ typedef struct _INFO2_HEADER
EXTERN_C
HRESULT RecycleBinGeneric_Constructor(OUT IUnknown **ppUnknown);
+EXTERN_C
+BOOL RecycleBinGeneric_IsEqualFileIdentity(const RECYCLEBINFILEIDENTITY *p1, const
RECYCLEBINFILEIDENTITY *p2);
+
/* recyclebin_generic_enumerator.c */
EXTERN_C
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp
b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp
index c7009f94d9d..b040851d011 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.cpp
@@ -143,6 +143,13 @@ public:
STDMETHODIMP DeleteFile(_In_ LPCWSTR szFileName) override;
STDMETHODIMP EmptyRecycleBin() override;
STDMETHODIMP EnumObjects(_Out_ IRecycleBinEnumList **ppEnumList) override;
+ STDMETHODIMP GetDirectory(LPWSTR szPath) override
+ {
+ if (!m_Folder[0])
+ return E_UNEXPECTED;
+ lstrcpynW(szPath, m_Folder, MAX_PATH);
+ return S_OK;
+ }
/* IRecycleBin5 interface */
STDMETHODIMP Delete(
@@ -226,6 +233,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
SYSTEMTIME SystemTime;
DWORD ClusterSize, BytesPerSector, SectorsPerCluster;
HRESULT hr;
+ WIN32_FIND_DATAW wfd = {};
TRACE("(%p, %s)\n", this, debugstr_w(szFileName));
@@ -240,7 +248,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
{
if (szFullName)
CoTaskMemFree(szFullName);
- return HRESULT_FROM_WIN32(GetLastError());
+ return HResultFromWin32(GetLastError());
}
else if (len < dwBufferLength)
break;
@@ -257,7 +265,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
if (dwAttributes == INVALID_FILE_ATTRIBUTES)
{
CoTaskMemFree(szFullName);
- return HRESULT_FROM_WIN32(GetLastError());
+ return HResultFromWin32(GetLastError());
}
if (dwBufferLength < 2 || szFullName[1] != ':')
@@ -270,7 +278,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
hFile = CreateFileW(szFullName, 0, 0, NULL, OPEN_EXISTING, (dwAttributes &
FILE_ATTRIBUTE_DIRECTORY) ? FILE_FLAG_BACKUP_SEMANTICS : 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
goto cleanup;
}
@@ -281,7 +289,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0,
NULL);
if (!m_hInfoMapped)
{
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
goto cleanup;
}
@@ -289,7 +297,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
pHeader = (PINFO2_HEADER)MapViewOfFile(m_hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
if (!pHeader)
{
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
goto cleanup;
}
@@ -297,7 +305,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
FileSize.u.LowPart = GetFileSize(m_hInfo, &FileSize.u.HighPart);
if (FileSize.u.LowPart < sizeof(INFO2_HEADER))
{
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
goto cleanup;
}
dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) /
sizeof(DELETED_FILE_RECORD)) - 1;
@@ -307,14 +315,14 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
#if 0
if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&FileSize))
{
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
goto cleanup;
}
#else
FileSize.u.LowPart = GetFileSize(hFile, &FileSize.u.HighPart);
if (FileSize.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
{
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
goto cleanup;
}
#endif
@@ -350,7 +358,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
/* Get cluster size */
if (!GetDiskFreeSpaceW(m_VolumePath, &SectorsPerCluster, &BytesPerSector,
NULL, NULL))
{
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
goto cleanup;
}
ClusterSize = BytesPerSector * SectorsPerCluster;
@@ -359,7 +367,7 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
GetSystemTime(&SystemTime);
if (!SystemTimeToFileTime(&SystemTime, &pDeletedFile->DeletionTime))
{
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
goto cleanup;
}
pDeletedFile->dwPhysicalFileSize = ROUND_UP(FileSize.u.LowPart, ClusterSize);
@@ -373,11 +381,21 @@ STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName)
goto cleanup;
}
+ wfd.dwFileAttributes = dwAttributes;
+ wfd.nFileSizeLow = FileSize.u.LowPart;
+ GetFileTime(hFile, &wfd.ftCreationTime, &wfd.ftLastAccessTime,
&wfd.ftLastWriteTime);
+
/* Move file */
if (MoveFileW(szFullName, DeletedFileName))
hr = S_OK;
else
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
+
+ if (SUCCEEDED(hr))
+ {
+ RECYCLEBINFILEIDENTITY ident = { pDeletedFile->DeletionTime, DeletedFileName
};
+ CRecycleBin_NotifyRecycled(szFullName, &wfd, &ident);
+ }
cleanup:
if (pHeader)
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h
b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h
index f33b3adbe54..2461e0bdbca 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin_v5.h
@@ -28,7 +28,7 @@ typedef interface IRecycleBin5 IRecycleBin5;
EXTERN_C const IID IID_IRecycleBin5;
#define INTERFACE IRecycleBin5
-DECLARE_INTERFACE_(IRecycleBin5, IUnknown)
+DECLARE_INTERFACE_(IRecycleBin5, IRecycleBin)
{
BEGIN_INTERFACE
@@ -41,6 +41,7 @@ DECLARE_INTERFACE_(IRecycleBin5, IUnknown)
STDMETHOD(DeleteFile)(THIS_ IN LPCWSTR szFileName) PURE;
STDMETHOD(EmptyRecycleBin)(THIS);
STDMETHOD(EnumObjects)(THIS_ OUT IRecycleBinEnumList **ppEnumList) PURE;
+ STDMETHOD(GetDirectory)(THIS_ LPWSTR szPath) PURE;
/* IRecycleBin5 interface */
STDMETHOD(Delete)(
diff --git a/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp
b/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp
index ff2593fe84d..ae5c6d5e5e6 100644
--- a/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp
+++ b/dll/win32/shell32/shellrecyclebin/recyclebin_v5_enumerator.cpp
@@ -28,13 +28,18 @@ public:
STDMETHODIMP_(ULONG) Release() override;
/* IRecycleBinFile methods */
+ STDMETHODIMP IsEqualIdentity(const RECYCLEBINFILEIDENTITY *pFI) override
+ {
+ RECYCLEBINFILEIDENTITY self = { m_deletedFile.DeletionTime, m_FullName };
+ return RecycleBinGeneric_IsEqualFileIdentity(pFI, &self) ? S_OK : S_FALSE;
+ }
+ STDMETHODIMP GetInfo(PDELETED_FILE_INFO pInfo) override;
STDMETHODIMP GetLastModificationTime(FILETIME *pLastModificationTime) override;
STDMETHODIMP GetDeletionTime(FILETIME *pDeletionTime) override;
STDMETHODIMP GetFileSize(ULARGE_INTEGER *pFileSize) override;
STDMETHODIMP GetPhysicalFileSize(ULARGE_INTEGER *pPhysicalFileSize) override;
STDMETHODIMP GetAttributes(DWORD *pAttributes) override;
STDMETHODIMP GetFileName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize)
override;
- STDMETHODIMP GetTypeName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize)
override;
STDMETHODIMP Delete() override;
STDMETHODIMP Restore() override;
@@ -53,14 +58,8 @@ STDMETHODIMP RecycleBin5File::QueryInterface(REFIID riid, void
**ppvObject)
return E_POINTER;
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IRecycleBinFile))
- *ppvObject = static_cast<IRecycleBinFile *>(this);
- else if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW))
{
- DWORD dwAttributes;
- if (GetAttributes(&dwAttributes) == S_OK)
- return SHCreateFileExtractIconW(m_FullName, dwAttributes, riid, ppvObject);
- else
- return S_FALSE;
+ *ppvObject = static_cast<IRecycleBinFile *>(this);
}
else
{
@@ -94,13 +93,29 @@ STDMETHODIMP_(ULONG) RecycleBin5File::Release()
return refCount;
}
+STDMETHODIMP RecycleBin5File::GetInfo(PDELETED_FILE_INFO pInfo)
+{
+ HRESULT hr = S_OK;
+ ULARGE_INTEGER uli;
+ if (FAILED(GetLastModificationTime(&pInfo->LastModification)))
+ ZeroMemory(&pInfo->LastModification, sizeof(pInfo->LastModification));
+ pInfo->DeletionTime = m_deletedFile.DeletionTime;
+ C_ASSERT(sizeof(pInfo->FileSize) <= sizeof(UINT));
+ pInfo->FileSize = FAILED(GetFileSize(&uli)) ? INVALID_FILE_SIZE :
uli.LowPart;
+ if (FAILED(hr = GetAttributes(&pInfo->Attributes)))
+ pInfo->Attributes = 0;
+ InitializeRecycleBinStringRef(&pInfo->OriginalFullPath,
m_deletedFile.FileNameW);
+ InitializeRecycleBinStringRef(&pInfo->RecycledFullPath, m_FullName);
+ return hr;
+}
+
STDMETHODIMP RecycleBin5File::GetLastModificationTime(FILETIME *pLastModificationTime)
{
TRACE("(%p, %p)\n", this, pLastModificationTime);
DWORD dwAttributes = ::GetFileAttributesW(m_FullName);
if (dwAttributes == INVALID_FILE_ATTRIBUTES)
- return HRESULT_FROM_WIN32(GetLastError());
+ return HResultFromWin32(GetLastError());
HANDLE hFile;
hFile = CreateFileW(m_FullName,
@@ -112,13 +127,13 @@ STDMETHODIMP RecycleBin5File::GetLastModificationTime(FILETIME
*pLastModificatio
(dwAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
FILE_FLAG_BACKUP_SEMANTICS : 0,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
- return HRESULT_FROM_WIN32(GetLastError());
+ return HResultFromWin32(GetLastError());
HRESULT hr;
if (GetFileTime(hFile, NULL, NULL, pLastModificationTime))
hr = S_OK;
else
- hr = HRESULT_FROM_WIN32(GetLastError());
+ hr = HResultFromWin32(GetLastError());
CloseHandle(hFile);
return hr;
}
@@ -174,7 +189,7 @@ STDMETHODIMP RecycleBin5File::GetAttributes(DWORD *pAttributes)
dwAttributes = GetFileAttributesW(m_FullName);
if (dwAttributes == INVALID_FILE_ATTRIBUTES)
- return HRESULT_FROM_WIN32(GetLastError());
+ return HResultFromWin32(GetLastError());
*pAttributes = dwAttributes;
return S_OK;
@@ -199,36 +214,6 @@ STDMETHODIMP RecycleBin5File::GetFileName(SIZE_T BufferSize, LPWSTR
Buffer, SIZE
return S_OK;
}
-STDMETHODIMP RecycleBin5File::GetTypeName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T
*RequiredSize)
-{
- HRESULT hr;
- DWORD dwRequired;
- DWORD dwAttributes;
- SHFILEINFOW shFileInfo;
-
- TRACE("(%p, %u, %p, %p)\n", this, BufferSize, Buffer, RequiredSize);
-
- hr = GetAttributes(&dwAttributes);
- if (!SUCCEEDED(hr))
- return hr;
-
- hr = SHGetFileInfoW(m_FullName, dwAttributes, &shFileInfo, sizeof(shFileInfo),
SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES);
- if (!SUCCEEDED(hr))
- return hr;
-
- dwRequired = (DWORD)(wcslen(shFileInfo.szTypeName) + 1) * sizeof(WCHAR);
- if (RequiredSize)
- *RequiredSize = dwRequired;
-
- if (BufferSize == 0 && !Buffer)
- return S_OK;
-
- if (BufferSize < dwRequired)
- return E_OUTOFMEMORY;
- CopyMemory(Buffer, shFileInfo.szTypeName, dwRequired);
- return S_OK;
-}
-
STDMETHODIMP RecycleBin5File::Delete()
{
TRACE("(%p)\n", this);
@@ -390,7 +375,7 @@ STDMETHODIMP RecycleBin5Enum::Next(DWORD celt, IRecycleBinFile
**rgelt, DWORD *p
ULARGE_INTEGER FileSize;
FileSize.u.LowPart = GetFileSize(m_hInfo, &FileSize.u.HighPart);
if (FileSize.u.LowPart == 0)
- return HRESULT_FROM_WIN32(GetLastError());
+ return HResultFromWin32(GetLastError());
DWORD dwEntries =
(DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) /
sizeof(DELETED_FILE_RECORD));
@@ -455,7 +440,7 @@ RecycleBin5Enum::Init(
m_hInfo = hInfo;
m_pInfo = (PINFO2_HEADER)MapViewOfFile(hInfoMapped, FILE_MAP_READ, 0, 0, 0);
if (!m_pInfo)
- return HRESULT_FROM_WIN32(GetLastError());
+ return HResultFromWin32(GetLastError());
if (m_pInfo->dwVersion != 5 || m_pInfo->dwRecordSize !=
sizeof(DELETED_FILE_RECORD))
return E_FAIL;
diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp
index 3a665288535..68befd32925 100644
--- a/dll/win32/shell32/shlexec.cpp
+++ b/dll/win32/shell32/shlexec.cpp
@@ -1390,14 +1390,7 @@ static HRESULT shellex_get_dataobj( LPSHELLEXECUTEINFOW sei,
CComPtr<IDataObject
pidl = ILCreateFromPathW(fullpath);
allocatedPidl.Attach(pidl);
}
-
- CComPtr<IShellFolder> shf;
- LPCITEMIDLIST pidllast = NULL;
- HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf),
&pidllast);
- if (FAILED_UNEXPECTEDLY(hr))
- return hr;
-
- return shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IDataObject,
&dataObj));
+ return SHELL_GetUIObjectOfAbsoluteItem(NULL, pidl, IID_PPV_ARG(IDataObject,
&dataObj));
}
static HRESULT shellex_run_context_menu_default(IShellExtInit *obj,
@@ -1582,6 +1575,7 @@ static HRESULT ShellExecute_ContextMenuVerb(LPSHELLEXECUTEINFOW
sei)
enum { idFirst = 1, idLast = 0x7fff };
HMENU hMenu = CreatePopupMenu();
+ // Note: Windows does not pass CMF_EXTENDEDVERBS so "hidden" verbs cannot
be executed
hr = cm->QueryContextMenu(hMenu, 0, idFirst, idLast, fDefault ? CMF_DEFAULTONLY :
0);
if (!FAILED_UNEXPECTEDLY(hr))
{
diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp
index ce2f6f40834..a384c6c4925 100644
--- a/dll/win32/shell32/utils.cpp
+++ b/dll/win32/shell32/utils.cpp
@@ -259,6 +259,32 @@ HRESULT SHBindToObject(
return SHBindToObjectEx(psf, pidl, NULL, riid, ppvObj);
}
+EXTERN_C HRESULT
+SHELL_GetUIObjectOfAbsoluteItem(
+ _In_opt_ HWND hWnd,
+ _In_ PCIDLIST_ABSOLUTE pidl,
+ _In_ REFIID riid, _Out_ void **ppvObj)
+{
+ if (!ppvObj)
+ return E_INVALIDARG;
+ *ppvObj = NULL;
+ IShellFolder *psf;
+ PCUITEMID_CHILD pidlChild;
+ HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf),
&pidlChild);
+ if (SUCCEEDED(hr))
+ {
+ hr = psf->GetUIObjectOf(hWnd, 1, &pidlChild, riid, NULL, ppvObj);
+ psf->Release();
+ if (SUCCEEDED(hr))
+ {
+ if (*ppvObj)
+ return hr;
+ hr = E_FAIL;
+ }
+ }
+ return hr;
+}
+
HRESULT
Shell_DisplayNameOf(
_In_ IShellFolder *psf,
@@ -1429,3 +1455,71 @@ GetDfmCmd(_In_ IContextMenu *pCM, _In_ LPCSTR verba)
}
return MapVerbToDfmCmd(verba); // Returns DFM_CMD_* or 0
}
+
+HRESULT
+SHELL_MapContextMenuVerbToCmdId(LPCMINVOKECOMMANDINFO pICI, const CMVERBMAP *pMap)
+{
+ LPCSTR pVerbA = pICI->lpVerb;
+ CHAR buf[MAX_PATH];
+ LPCMINVOKECOMMANDINFOEX pICIX = (LPCMINVOKECOMMANDINFOEX)pICI;
+ if (IsUnicode(*pICIX) && !IS_INTRESOURCE(pICIX->lpVerbW))
+ {
+ if (SHUnicodeToAnsi(pICIX->lpVerbW, buf, _countof(buf)))
+ pVerbA = buf;
+ }
+
+ if (IS_INTRESOURCE(pVerbA))
+ return LOWORD(pVerbA);
+ for (SIZE_T i = 0; pMap[i].Verb; ++i)
+ {
+ assert(SUCCEEDED((int)(pMap[i].CmdId))); // The id must be >= 0 and ideally in
the 0..0x7fff range
+ if (!lstrcmpiA(pMap[i].Verb, pVerbA) && pVerbA[0])
+ return pMap[i].CmdId;
+ }
+ return E_FAIL;
+}
+
+static const CMVERBMAP*
+FindVerbMapEntry(UINT_PTR CmdId, const CMVERBMAP *pMap)
+{
+ for (SIZE_T i = 0; pMap[i].Verb; ++i)
+ {
+ if (pMap[i].CmdId == CmdId)
+ return &pMap[i];
+ }
+ return NULL;
+}
+
+HRESULT
+SHELL_GetCommandStringImpl(SIZE_T CmdId, UINT uFlags, LPSTR Buf, UINT cchBuf, const
CMVERBMAP *pMap)
+{
+ const CMVERBMAP* pEntry;
+ switch (uFlags | GCS_UNICODE)
+ {
+ case GCS_VALIDATEW:
+ case GCS_VERBW:
+ pEntry = FindVerbMapEntry(CmdId, pMap);
+ if ((uFlags | GCS_UNICODE) == GCS_VERBW)
+ {
+ if (!pEntry)
+ return E_INVALIDARG;
+ else if (uFlags & GCS_UNICODE)
+ return SHAnsiToUnicode(pEntry->Verb, (LPWSTR)Buf, cchBuf) ? S_OK :
E_FAIL;
+ else
+ return StringCchCopyA(Buf, cchBuf, pEntry->Verb);
+ }
+ return pEntry ? S_OK : S_FALSE; // GCS_VALIDATE
+ }
+ return E_NOTIMPL;
+}
+
+HRESULT
+SHELL_CreateShell32DefaultExtractIcon(int IconIndex, REFIID riid, LPVOID *ppvOut)
+{
+ CComPtr<IDefaultExtractIconInit> initIcon;
+ HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,
&initIcon));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ initIcon->SetNormalIcon(swShell32Name, IconIndex);
+ return initIcon->QueryInterface(riid, ppvOut);
+}
diff --git a/dll/win32/shell32/utils.h b/dll/win32/shell32/utils.h
index 92a96888c5d..5591eb6a463 100644
--- a/dll/win32/shell32/utils.h
+++ b/dll/win32/shell32/utils.h
@@ -7,7 +7,16 @@
#pragma once
-inline BOOL
+#ifdef __cplusplus
+static inline LPWSTR
+SHStrDupW(LPCWSTR Src)
+{
+ LPWSTR Dup;
+ return SUCCEEDED(SHStrDupW(Src, &Dup)) ? Dup : NULL;
+}
+#endif
+
+static inline BOOL
RegValueExists(HKEY hKey, LPCWSTR Name)
{
return RegQueryValueExW(hKey, Name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS;
@@ -31,12 +40,22 @@ RegSetOrDelete(HKEY hKey, LPCWSTR Name, DWORD Type, LPCVOID Data,
DWORD Size)
return RegDeleteValueW(hKey, Name);
}
-inline DWORD
+static inline DWORD
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 {
+ LPCSTR Verb;
+ WORD CmdId;
+} CMVERBMAP;
+
+HRESULT
+SHELL_MapContextMenuVerbToCmdId(LPCMINVOKECOMMANDINFO pICI, const CMVERBMAP *pMap);
+HRESULT
+SHELL_GetCommandStringImpl(SIZE_T CmdId, UINT uFlags, LPSTR Buf, UINT cchBuf, const
CMVERBMAP *pMap);
+
// SHExtractIconsW is a forward, use this function instead inside shell32
inline HICON
SHELL32_SHExtractIcon(LPCWSTR File, int Index, int cx, int cy)
@@ -45,3 +64,20 @@ SHELL32_SHExtractIcon(LPCWSTR File, int Index, int cx, int cy)
int r = PrivateExtractIconsW(File, Index, cx, cy, &hIco, NULL, 1, 0);
return r > 0 ? hIco : NULL;
}
+
+HRESULT
+SHELL_CreateShell32DefaultExtractIcon(int IconIndex, REFIID riid, LPVOID *ppvOut);
+
+static inline HRESULT
+SHELL_CreateFallbackExtractIconForFolder(REFIID riid, LPVOID *ppvOut)
+{
+ const int id = IDI_SHELL_FOLDER;
+ return SHELL_CreateShell32DefaultExtractIcon(id > 1 ? -id : 0, riid, ppvOut);
+}
+
+static inline HRESULT
+SHELL_CreateFallbackExtractIconForNoAssocFile(REFIID riid, LPVOID *ppvOut)
+{
+ const int id = IDI_SHELL_DOCUMENT;
+ return SHELL_CreateShell32DefaultExtractIcon(id > 1 ? -id : 0, riid, ppvOut);
+}
diff --git a/dll/win32/shell32/wine/pidl.c b/dll/win32/shell32/wine/pidl.c
index de484982b15..2bca7cdd436 100644
--- a/dll/win32/shell32/wine/pidl.c
+++ b/dll/win32/shell32/wine/pidl.c
@@ -2112,32 +2112,22 @@ BOOL _ILIsSpecialFolder (LPCITEMIDLIST pidl)
BOOL _ILIsDrive(LPCITEMIDLIST pidl)
{
- LPPIDLDATA lpPData = _ILGetDataPointer(pidl);
-
- TRACE("(%p)\n",pidl);
-
- return (pidl && lpPData && (PT_DRIVE == lpPData->type ||
- PT_DRIVE1 == lpPData->type ||
- PT_DRIVE2 == lpPData->type ||
- PT_DRIVE3 == lpPData->type));
+ const BYTE type = _ILGetType(pidl);
+ const BYTE fldrtype = (PT_DRIVE & PT_FOLDERTYPEMASK);
+ return (type & PT_FOLDERTYPEMASK) == fldrtype && type !=
PT_COMPUTER_REGITEM;
}
BOOL _ILIsFolder(LPCITEMIDLIST pidl)
{
- LPPIDLDATA lpPData = _ILGetDataPointer(pidl);
-
- TRACE("(%p)\n",pidl);
-
- return (pidl && lpPData && (PT_FOLDER == lpPData->type ||
PT_FOLDER1 == lpPData->type));
+ /* A folder or a simple PT_FS with a child */
+ const BYTE type = _ILGetFSType(pidl);
+ return (type & PT_FS_FOLDER_FLAG) != 0 || (type == PT_FS &&
ILGetNext(pidl));
}
BOOL _ILIsValue(LPCITEMIDLIST pidl)
{
- LPPIDLDATA lpPData = _ILGetDataPointer(pidl);
-
- TRACE("(%p)\n",pidl);
-
- return (pidl && lpPData && PT_VALUE == lpPData->type);
+ const BYTE type = _ILGetFSType(pidl);
+ return type && !(type & PT_FS_FOLDER_FLAG);
}
BOOL _ILIsCPanelStruct(LPCITEMIDLIST pidl)
@@ -2281,6 +2271,9 @@ static LPWSTR _ILGetTextPointerW(LPCITEMIDLIST pidl)
if (!pdata)
return NULL;
+ if (_ILGetFSType(pidl) & PT_FS_UNICODE_FLAG)
+ return (LPWSTR)pdata->u.file.szNames;
+
switch (pdata->type)
{
case PT_GUID:
@@ -2311,9 +2304,6 @@ static LPWSTR _ILGetTextPointerW(LPCITEMIDLIST pidl)
/*return (LPSTR)&(pdata->u.network.szNames);*/
return NULL;
- case PT_VALUEW:
- return (LPWSTR)pdata->u.file.szNames;
-
#ifdef __REACTOS__ /* r54423 */
case PT_CPLAPPLET:
return pdata->u.cpanel.szName;
@@ -2439,7 +2429,7 @@ FileStructW* _ILGetFileStructW(LPCITEMIDLIST pidl) {
FileStructW *pFileStructW;
WORD cbOffset;
- if (!(_ILIsValue(pidl) || _ILIsFolder(pidl)))
+ if (!_ILIsFolderOrFile(pidl))
return NULL;
cbOffset = *(const WORD *)((const BYTE *)pidl + pidl->mkid.cb - sizeof(WORD));
@@ -2479,48 +2469,12 @@ FileStructW* _ILGetFileStructW(LPCITEMIDLIST pidl) {
*/
BOOL _ILGetFileDateTime(LPCITEMIDLIST pidl, FILETIME *pFt)
{
- LPPIDLDATA pdata = _ILGetDataPointer(pidl);
-
- if (!pdata)
- return FALSE;
-
- switch (pdata->type)
+ if (_ILGetFSType(pidl) > PT_FS) /* Only non-simple FS items have a date */
{
- case PT_FOLDER:
- case PT_VALUE:
- DosDateTimeToFileTime(pdata->u.file.uFileDate, pdata->u.file.uFileTime,
pFt);
- break;
- default:
- return FALSE;
- }
- return TRUE;
-}
-
-BOOL _ILGetFileDate(LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
-{
- FILETIME ft,lft;
- SYSTEMTIME time;
- BOOL ret;
-
- if (_ILGetFileDateTime( pidl, &ft ))
- {
- FileTimeToLocalFileTime(&ft, &lft);
- FileTimeToSystemTime (&lft, &time);
-
- ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, pOut,
uOutSize);
- if (ret)
- {
- /* Append space + time without seconds */
- pOut[ret - 1] = L' ';
- GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL,
&pOut[ret], uOutSize - ret);
- }
- }
- else
- {
- pOut[0] = UNICODE_NULL;
- ret = FALSE;
+ LPPIDLDATA pdata = _ILGetDataPointer(pidl);
+ return DosDateTimeToFileTime(pdata->u.file.uFileDate,
pdata->u.file.uFileTime, pFt);
}
- return ret;
+ return FALSE;
}
/*************************************************************************
@@ -2543,15 +2497,13 @@ BOOL _ILGetFileDate(LPCITEMIDLIST pidl, LPWSTR pOut, UINT
uOutSize)
DWORD _ILGetFileSize(LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
{
LPPIDLDATA pdata = _ILGetDataPointer(pidl);
- DWORD dwSize;
-
if (!pdata)
return 0;
- switch (pdata->type)
+ if (_ILGetFSType(pidl) & PT_FS_FILE_FLAG)
{
- case PT_VALUE:
- dwSize = pdata->u.file.dwFileSize;
+ /* FIXME: Handle INVALID_FILE_SIZE (get size from disk) */
+ DWORD dwSize = pdata->u.file.dwFileSize;
if (pOut)
StrFormatKBSizeW(dwSize, pOut, uOutSize);
return dwSize;
diff --git a/dll/win32/shell32/wine/pidl.h b/dll/win32/shell32/wine/pidl.h
index 28fff42feb4..bd8e34ea4bd 100644
--- a/dll/win32/shell32/wine/pidl.h
+++ b/dll/win32/shell32/wine/pidl.h
@@ -157,16 +157,6 @@ typedef struct tagPIDLPrinterStruct
WCHAR szName[1];
} PIDLPrinterStruct;
-typedef struct tagPIDLRecycleStruct
-{
- FILETIME LastModification;
- FILETIME DeletionTime;
- ULARGE_INTEGER FileSize;
- ULARGE_INTEGER PhysicalFileSize;
- DWORD Attributes;
- WCHAR szName[1];
-} PIDLRecycleStruct;
-
#endif /* !__REACTOS__ */
typedef struct tagGUIDStruct
@@ -233,7 +223,6 @@ typedef struct tagPIDLDATA
#ifdef __REACTOS__
struct tagPIDLFontStruct cfont;
struct tagPIDLPrinterStruct cprinter;
- struct tagPIDLRecycleStruct crecycle;
#endif
}u;
} PIDLDATA, *LPPIDLDATA;
@@ -243,7 +232,6 @@ typedef struct tagPIDLDATA
* getting special values from simple pidls
*/
DWORD _ILSimpleGetTextW (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
-BOOL _ILGetFileDate (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
DWORD _ILGetFileSize (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
BOOL _ILGetExtension (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
DWORD _ILGetFileAttributes(LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
@@ -261,6 +249,7 @@ BOOL _ILIsMyDocuments (LPCITEMIDLIST pidl);
BOOL _ILIsBitBucket (LPCITEMIDLIST pidl);
BOOL _ILIsNetHood (LPCITEMIDLIST pidl);
BOOL _ILIsControlPanel (LPCITEMIDLIST pidl);
+#define _ILIsFolderOrFile _ILGetFSType
#endif
BOOL _ILIsDrive (LPCITEMIDLIST pidl) DECLSPEC_HIDDEN;
BOOL _ILIsFolder (LPCITEMIDLIST pidl) DECLSPEC_HIDDEN;
diff --git a/dll/win32/shell32/wine/shell32_main.h
b/dll/win32/shell32/wine/shell32_main.h
index d40e1a164b3..fa5bafd8880 100644
--- a/dll/win32/shell32/wine/shell32_main.h
+++ b/dll/win32/shell32/wine/shell32_main.h
@@ -139,7 +139,7 @@ void FreeChangeNotifications(void) DECLSPEC_HIDDEN;
BOOL SHELL_DeleteDirectoryW(HWND hwnd, LPCWSTR pwszDir, BOOL bShowUI);
BOOL SHELL_ConfirmYesNoW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir);
-void WINAPI _InsertMenuItemW (HMENU hmenu, UINT indexMenu, BOOL fByPosition,
+BOOL WINAPI _InsertMenuItemW (HMENU hmenu, UINT indexMenu, BOOL fByPosition,
UINT wID, UINT fType, LPCWSTR dwTypeData, UINT fState);
static __inline BOOL SHELL_OsIsUnicode(void)