https://git.reactos.org/?p=reactos.git;a=commitdiff;h=28399a216b744e6263c772754644743e7a6682f1

commit 28399a216b744e6263c772754644743e7a6682f1
Author:     Whindmar Saksit <[email protected]>
AuthorDate: Thu Dec 19 14:38:27 2024 +0100
Commit:     GitHub <[email protected]>
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)

Reply via email to