[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
This commit is contained in:
Whindmar Saksit 2024-12-19 14:38:27 +01:00 committed by GitHub
parent a18424267b
commit 28399a216b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 1151 additions and 951 deletions

View file

@ -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

View file

@ -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:

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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)
{
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)
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;
/* 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);
}
LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
LPWSTR pExtension1, pExtension2;
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;
}

View file

@ -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_ */

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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);

View file

@ -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;
const RECYCLEBINFILEIDENTITY *pFI;
HDELFILE hDelFile;
} BBENUMFILECONTEXT;
HRESULT hr = IRecycleBinFile_GetTypeName(prbf, BufferSize, pTypeName, &FinalSize);
if (SUCCEEDED(hr))
static BOOL CALLBACK
GetRecycleBinFileHandleCallback(IN PVOID Context, IN HDELFILE hDeletedFile)
{
BBENUMFILECONTEXT *pCtx = (BBENUMFILECONTEXT*)Context;
IRecycleBinFile *pRBF = IRecycleBinFileFromHDELFILE(hDeletedFile);
if (IRecycleBinFile_IsEqualIdentity(pRBF, pCtx->pFI) == S_OK)
{
if (RequiredSize)
*RequiredSize = (DWORD)FinalSize;
return TRUE;
pCtx->hDelFile = hDeletedFile;
return FALSE;
}
if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
SetLastError(HRESULT_CODE(hr));
else
SetLastError(ERROR_GEN_FAILURE);
return FALSE;
CloseRecycleBinHandle(hDeletedFile);
return TRUE;
}
EXTERN_C HDELFILE
GetRecycleBinFileHandle(
IN LPCWSTR pszRoot OPTIONAL,
IN const RECYCLEBINFILEIDENTITY *pFI)
{
BBENUMFILECONTEXT context = { pFI, NULL };
EnumerateRecycleBinW(pszRoot, GetRecycleBinFileHandleCallback, &context);
return context.hDelFile;
}
BOOL WINAPI
GetDeletedFileDetailsA(
IN HDELFILE hDeletedFile,
IN DWORD BufferSize,
IN OUT PDELETED_FILE_DETAILS_A FileDetails OPTIONAL,
OUT LPDWORD RequiredSize OPTIONAL)
{
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))
{
BufferSizeW = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName)
+ (BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName)) * sizeof(WCHAR);
}
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;
}
BOOL WINAPI
GetDeletedFileDetailsW(
IN HDELFILE hDeletedFile,
IN DWORD BufferSize,
IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL,
OUT LPDWORD RequiredSize OPTIONAL)
{
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;
}
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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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)

View file

@ -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)(

View file

@ -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;

View file

@ -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))
{

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
LPPIDLDATA pdata = _ILGetDataPointer(pidl);
return DosDateTimeToFileTime(pdata->u.file.uFileDate, pdata->u.file.uFileTime, pFt);
}
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;
}
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;

View file

@ -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;

View file

@ -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)