Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637/* * Shell Folder stuff * * Copyright 1997 Marcus Meissner * Copyright 1998, 1999, 2002 Juergen Schmied * Copyright 2018 Katayama Hirofumi MZ * * IShellFolder2 and related interfaces * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
#include "precomp.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
SHCONTF SHELL_GetDefaultFolderEnumSHCONTF(){ SHCONTF Flags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS; SHELLSTATE ss; SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS | SSF_SHOWSUPERHIDDEN, FALSE); if (ss.fShowAllObjects) Flags |= SHCONTF_INCLUDEHIDDEN; if (ss.fShowSuperHidden) Flags |= SHCONTF_INCLUDESUPERHIDDEN; return Flags;}
BOOL SHELL_IncludeItemInFolderEnum(IShellFolder *pSF, PCUITEMID_CHILD pidl, SFGAOF Query, SHCONTF Flags){ if (SUCCEEDED(pSF->GetAttributesOf(1, &pidl, &Query))) { if (Query & SFGAO_NONENUMERATED) return FALSE; if ((Query & SFGAO_HIDDEN) && !(Flags & SHCONTF_INCLUDEHIDDEN)) return FALSE; if ((Query & (SFGAO_HIDDEN | SFGAO_SYSTEM)) == (SFGAO_HIDDEN | SFGAO_SYSTEM) && !(Flags & SHCONTF_INCLUDESUPERHIDDEN)) return FALSE; if ((Flags & (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS)) != (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS)) return (Flags & SHCONTF_FOLDERS) ? (Query & SFGAO_FOLDER) : !(Query & SFGAO_FOLDER); } return TRUE;}
static HRESULTMapSCIDToShell32FsColumn(const SHCOLUMNID *pscid, VARTYPE &vt){ if (pscid->fmtid == FMTID_Storage) { switch (pscid->pid) { case PID_STG_NAME: vt = VT_BSTR; return SHFSF_COL_NAME; case PID_STG_SIZE: vt = VT_UI8; return SHFSF_COL_SIZE; case PID_STG_STORAGETYPE: vt = VT_BSTR; return SHFSF_COL_TYPE; case PID_STG_ATTRIBUTES: vt = VT_BSTR; return SHFSF_COL_FATTS; case PID_STG_WRITETIME: vt = VT_DATE; return SHFSF_COL_MDATE; } } if (pscid->fmtid == FMTID_SummaryInformation && pscid->pid == PIDSI_COMMENTS) { vt = VT_BSTR; return SHFSF_COL_COMMENT; } return E_INVALIDARG;}
HRESULTSHELL_MapSCIDToColumn(IShellFolder2 *pSF, const SHCOLUMNID *pscid){ for (UINT i = 0; i <= SHCIDS_COLUMNMASK; ++i) { SHCOLUMNID scid; HRESULT hr = pSF->MapColumnToSCID(i, &scid); if (FAILED(hr)) break; if (IsEqualPropertyKey(scid, *pscid)) return i; } return E_FAIL;}
HRESULTSHELL_GetDetailsOfAsStringVariant(IShellFolder2 *pSF, PCUITEMID_CHILD pidl, UINT Column, VARIANT *pVar){ V_VT(pVar) = VT_EMPTY; SHELLDETAILS sd; sd.str.uType = STRRET_CSTR; HRESULT hr = pSF->GetDetailsOf(pidl, Column, &sd); if (FAILED(hr)) return hr; if (FAILED(hr = StrRetToBSTR(&sd.str, pidl, &V_BSTR(pVar)))) return hr; V_VT(pVar) = VT_BSTR; return hr;}
HRESULTSHELL_GetDetailsOfColumnAsVariant(IShellFolder2 *pSF, PCUITEMID_CHILD pidl, UINT Column, VARTYPE vt, VARIANT *pVar){ HRESULT hr = SHELL_GetDetailsOfAsStringVariant(pSF, pidl, Column, pVar); if (FAILED(hr)) return hr; if (vt == VT_EMPTY) { SHCOLSTATEF state; if (FAILED(pSF->GetDefaultColumnState(Column, &state))) state = SHCOLSTATE_TYPE_STR; if ((state & SHCOLSTATE_TYPEMASK) == SHCOLSTATE_TYPE_INT) vt = VT_I8; else if ((state & SHCOLSTATE_TYPEMASK) == SHCOLSTATE_TYPE_DATE) vt = VT_DATE; else vt = VT_BSTR; } if (vt != VT_BSTR) VariantChangeType(pVar, pVar, 0, vt); return hr;}
HRESULTSH32_GetDetailsOfPKeyAsVariant(IShellFolder2 *pSF, PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pVar, BOOL UseFsColMap){ // CFSFolder and CRegFolder uses the SHFSF_COL columns and can use the faster and better version, // everyone else must ask the folder to map the SCID to a column. VARTYPE vt = VT_EMPTY; HRESULT hr = UseFsColMap ? MapSCIDToShell32FsColumn(pscid, vt) : SHELL_MapSCIDToColumn(pSF, pscid); return SUCCEEDED(hr) ? SHELL_GetDetailsOfColumnAsVariant(pSF, pidl, hr, vt, pVar) : hr;}
HRESULT SHELL_CreateAbsolutePidl(IShellFolder *pSF, PCUIDLIST_RELATIVE pidlChild, PIDLIST_ABSOLUTE *ppPidl){ PIDLIST_ABSOLUTE pidlFolder; HRESULT hr = SHGetIDListFromObject(pSF, &pidlFolder); if (SUCCEEDED(hr)) { hr = SHILCombine(pidlFolder, pidlChild, ppPidl); ILFree(pidlFolder); } return hr;}
HRESULTShell_NextElement( _Inout_ LPWSTR *ppch, _Out_ LPWSTR pszOut, _In_ INT cchOut, _In_ BOOL bValidate){ *pszOut = UNICODE_NULL;
if (!*ppch) return S_FALSE;
HRESULT hr; LPWSTR pchNext = wcschr(*ppch, L'\\'); if (pchNext) { if (*ppch < pchNext) { /* Get an element */ StringCchCopyNW(pszOut, cchOut, *ppch, pchNext - *ppch); ++pchNext;
if (!*pchNext) pchNext = NULL; /* No next */
hr = S_OK; } else /* Double backslashes found? */ { pchNext = NULL; hr = E_INVALIDARG; } } else /* No more next */ { StringCchCopyW(pszOut, cchOut, *ppch); hr = S_OK; }
*ppch = pchNext; /* Go next */
if (hr == S_OK && bValidate && !PathIsValidElement(pszOut)) { *pszOut = UNICODE_NULL; hr = E_INVALIDARG; }
return hr;}
HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc, LPITEMIDLIST * pidlInOut, LPOLESTR szNext, DWORD * pEaten, DWORD * pdwAttributes){ HRESULT hr = E_INVALIDARG; LPITEMIDLIST pidlIn = pidlInOut ? *pidlInOut : NULL; LPITEMIDLIST pidlOut = NULL; LPITEMIDLIST pidlTemp = NULL; CComPtr<IShellFolder> psfChild;
TRACE ("(%p, %p, %p, %s)\n", psf, pbc, pidlIn, debugstr_w (szNext));
/* get the shellfolder for the child pidl and let it analyse further */ hr = psf->BindToObject(pidlIn, pbc, IID_PPV_ARG(IShellFolder, &psfChild)); if (FAILED(hr)) return hr;
hr = psfChild->ParseDisplayName(hwndOwner, pbc, szNext, pEaten, &pidlOut, pdwAttributes); if (FAILED(hr)) return hr;
pidlTemp = ILCombine (pidlIn, pidlOut); if (!pidlTemp) { hr = E_OUTOFMEMORY; if (pidlOut) ILFree(pidlOut); return hr; }
if (pidlOut) ILFree (pidlOut);
if (pidlIn) ILFree (pidlIn);
*pidlInOut = pidlTemp;
TRACE ("-- pidl=%p ret=0x%08x\n", pidlInOut ? *pidlInOut : NULL, hr); return S_OK;}
/*********************************************************************** * SHELL32_CoCreateInitSF * * Creates a shell folder and initializes it with a pidl and a root folder * via IPersistFolder3 or IPersistFolder. * * NOTES * pathRoot can be NULL for Folders being a drive. * In this case the absolute path is built from pidlChild (eg. C:) */HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti, LPCITEMIDLIST pidlChild, const GUID* clsid, REFIID riid, LPVOID *ppvOut){ HRESULT hr; CComPtr<IShellFolder> pShellFolder;
hr = SHCoCreateInstance(NULL, clsid, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder)); if (FAILED(hr)) return hr;
LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild); CComPtr<IPersistFolder> ppf; CComPtr<IPersistFolder3> ppf3;
if (ppfti && SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3)))) { ppf3->InitializeEx(NULL, pidlAbsolute, ppfti); } else if (SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf)))) { ppf->Initialize(pidlAbsolute); } ILFree (pidlAbsolute);
return pShellFolder->QueryInterface(riid, ppvOut);}
HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, const GUID* clsid, int csidl, REFIID riid, LPVOID *ppvOut){ /* fill the PERSIST_FOLDER_TARGET_INFO */ PERSIST_FOLDER_TARGET_INFO pfti = {0}; pfti.dwAttributes = -1; pfti.csidl = csidl;
return SHELL32_CoCreateInitSF(pidlRoot, &pfti, NULL, clsid, riid, ppvOut);}
HRESULT SHELL32_BindToSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti, LPCITEMIDLIST pidl, const GUID* clsid, REFIID riid, LPVOID *ppvOut){ PITEMID_CHILD pidlChild = ILCloneFirst (pidl); if (!pidlChild) return E_FAIL;
CComPtr<IShellFolder> psf; HRESULT hr = SHELL32_CoCreateInitSF(pidlRoot, ppfti, pidlChild, clsid, IID_PPV_ARG(IShellFolder, &psf)); ILFree(pidlChild);
if (FAILED_UNEXPECTEDLY(hr)) return hr;
if (_ILIsPidlSimple (pidl)) return psf->QueryInterface(riid, ppvOut); else return psf->BindToObject(ILGetNext (pidl), NULL, riid, ppvOut);}
/*********************************************************************** * SHELL32_GetDisplayNameOfChild * * Retrieves the display name of a child object of a shellfolder. * * For a pidl eg. [subpidl1][subpidl2][subpidl3]: * - it binds to the child shellfolder [subpidl1] * - asks it for the displayname of [subpidl2][subpidl3] * * Is possible the pidl is a simple pidl. In this case it asks the * subfolder for the displayname of an empty pidl. The subfolder * returns the own displayname eg. "::{guid}". This is used for * virtual folders with the registry key WantsFORPARSING set. */HRESULT SHELL32_GetDisplayNameOfChild (IShellFolder2 * psf, LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet){ LPITEMIDLIST pidlFirst = ILCloneFirst(pidl); if (!pidlFirst) return E_OUTOFMEMORY;
CComPtr<IShellFolder> psfChild; HRESULT hr = psf->BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild)); if (SUCCEEDED (hr)) { hr = psfChild->GetDisplayNameOf(ILGetNext (pidl), dwFlags, strRet); } ILFree (pidlFirst);
return hr;}
HRESULT SHELL32_CompareChildren(IShellFolder2* psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2){ PUIDLIST_RELATIVE nextpidl1 = ILGetNext (pidl1); PUIDLIST_RELATIVE nextpidl2 = ILGetNext (pidl2);
bool isEmpty1 = _ILIsDesktop(nextpidl1); bool isEmpty2 = _ILIsDesktop(nextpidl2); if (isEmpty1 || isEmpty2) return MAKE_COMPARE_HRESULT(isEmpty2 - isEmpty1);
PITEMID_CHILD firstpidl = ILCloneFirst (pidl1); if (!firstpidl) return E_OUTOFMEMORY;
CComPtr<IShellFolder> psf2; HRESULT hr = psf->BindToObject(firstpidl, 0, IID_PPV_ARG(IShellFolder, &psf2)); ILFree(firstpidl); if (FAILED(hr)) return MAKE_COMPARE_HRESULT(0);
return psf2->CompareIDs(lParam, nextpidl1, nextpidl2);}
HRESULT SHELL32_CompareDetails(IShellFolder2* isf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2){ SHELLDETAILS sd; WCHAR wszItem1[MAX_PATH], wszItem2[MAX_PATH]; HRESULT hres; UINT col = LOWORD(lParam); // Column index without SHCIDS_* flags
hres = isf->GetDetailsOf(pidl1, col, &sd); if (FAILED(hres)) return MAKE_COMPARE_HRESULT(1);
hres = StrRetToBufW(&sd.str, pidl1, wszItem1, MAX_PATH); if (FAILED(hres)) return MAKE_COMPARE_HRESULT(1);
hres = isf->GetDetailsOf(pidl2, col, &sd); if (FAILED(hres)) return MAKE_COMPARE_HRESULT(1);
hres = StrRetToBufW(&sd.str, pidl2, wszItem2, MAX_PATH); if (FAILED(hres)) return MAKE_COMPARE_HRESULT(1);
int ret = CFSFolder::CompareUiStrings(wszItem1, wszItem2, lParam); if (ret == 0) return SHELL32_CompareChildren(isf, lParam, pidl1, pidl2);
return MAKE_COMPARE_HRESULT(ret);}
void CloseRegKeyArray(HKEY* array, UINT cKeys){ for (UINT i = 0; i < cKeys; ++i) RegCloseKey(array[i]);}
LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys){ if (*cKeys >= 16) return ERROR_MORE_DATA;
HKEY hkey; LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey); if (result == ERROR_SUCCESS) { array[*cKeys] = hkey; *cKeys += 1; } return result;}
LSTATUS AddClsidKeyToArray(REFCLSID clsid, HKEY* array, UINT* cKeys){ WCHAR path[6 + 38 + 1] = L"CLSID\\"; StringFromGUID2(clsid, path + 6, 38 + 1); return AddClassKeyToArray(path, array, cKeys);}
void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys){ // This function opens the association array keys in canonical order for filesystem items. // The order is documented: learn.microsoft.com/en-us/windows/win32/shell/fa-associationarray
ASSERT(cidl >= 1 && apidl); PCUITEMID_CHILD pidl = apidl[0]; if (_ILIsValue(pidl)) { WCHAR buf[MAX_PATH]; PWSTR name; FileStructW* pFileData = _ILGetFileStructW(pidl); if (pFileData) { name = pFileData->wszName; } else { _ILSimpleGetTextW(pidl, buf, _countof(buf)); name = buf; } LPCWSTR extension = PathFindExtension(name);
if (extension) { WCHAR wszClass[MAX_PATH], wszSFA[23 + _countof(wszClass)]; DWORD dwSize = sizeof(wszClass); if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) != ERROR_SUCCESS || !*wszClass || AddClassKeyToArray(wszClass, array, cKeys) != ERROR_SUCCESS) { // Only add the extension key if the ProgId is not valid AddClassKeyToArray(extension, array, cKeys);
// "Open With" becomes the default when there are no verbs in the above keys if (cidl == 1) AddClassKeyToArray(L"Unknown", array, cKeys); }
swprintf(wszSFA, L"SystemFileAssociations\\%s", extension); AddClassKeyToArray(wszSFA, array, cKeys);
dwSize = sizeof(wszClass); if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS) { swprintf(wszSFA, L"SystemFileAssociations\\%s", wszClass); AddClassKeyToArray(wszSFA, array, cKeys); } }
AddClassKeyToArray(L"*", array, cKeys); AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys); } else if (_ILIsFolder(pidl)) { // FIXME: Directory > Folder > AFO is the correct order and it's // the order Windows reports in its undocumented association array // but it is somehow not the order Windows adds the items to its menu! // Until the correct algorithm in CDefaultContextMenu can be determined, // we add the folder keys in "menu order". AddClassKeyToArray(L"Folder", array, cKeys); AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys); AddClassKeyToArray(L"Directory", array, cKeys); } else if (_ILIsDrive(pidl)) { AddClassKeyToArray(L"Drive", array, cKeys); AddClassKeyToArray(L"Folder", array, cKeys); } else { ERR("Got non FS pidl\n"); }}
void AddPidlClassKeysToArray(LPCITEMIDLIST pidl, HKEY* array, UINT* cKeys){ if ((pidl = ILFindLastID(pidl)) != NULL) AddFSClassKeysToArray(1, &pidl, array, cKeys);}
HRESULT SH_GetApidlFromDataObject(IDataObject *pDataObject, PIDLIST_ABSOLUTE* ppidlfolder, PUITEMID_CHILD **apidlItems, UINT *pcidl){ CDataObjectHIDA cida(pDataObject);
if (FAILED_UNEXPECTEDLY(cida.hr())) return cida.hr();
/* convert the data into pidl */ LPITEMIDLIST pidl; LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, cida); if (!apidl) { return E_OUTOFMEMORY; }
*ppidlfolder = pidl; *apidlItems = apidl; *pcidl = cida->cidl;
return S_OK;}
/*********************************************************************** * SHCreateLinks * * Undocumented. */HRESULT WINAPI SHCreateLinks( HWND hWnd, LPCSTR lpszDir, IDataObject * lpDataObject, UINT uFlags, LPITEMIDLIST *lppidlLinks){ FIXME("%p %s %p %08x %p\n", hWnd, lpszDir, lpDataObject, uFlags, lppidlLinks); return E_NOTIMPL;}
/*********************************************************************** * SHOpenFolderAndSelectItems * * Unimplemented. */EXTERN_C HRESULTWINAPISHOpenFolderAndSelectItems(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD dwFlags){ ERR("SHOpenFolderAndSelectItems() is hackplemented\n"); CComHeapPtr<ITEMIDLIST> freeItem; PCIDLIST_ABSOLUTE pidlItem; if (cidl) { /* Firefox sends a full pidl here dispite the fact it is a PCUITEMID_CHILD_ARRAY -_- */ if (!ILIsSingle(apidl[0])) { pidlItem = apidl[0]; } else { HRESULT hr = SHILCombine(pidlFolder, apidl[0], &pidlItem); if (FAILED_UNEXPECTEDLY(hr)) return hr; freeItem.Attach(const_cast<PIDLIST_ABSOLUTE>(pidlItem)); } } else { pidlItem = pidlFolder; }
CComPtr<IShellFolder> psfDesktop;
HRESULT hr = SHGetDesktopFolder(&psfDesktop); if (FAILED_UNEXPECTEDLY(hr)) return hr;
STRRET strret; hr = psfDesktop->GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &strret); if (FAILED_UNEXPECTEDLY(hr)) return hr;
WCHAR wszBuf[MAX_PATH]; hr = StrRetToBufW(&strret, pidlItem, wszBuf, _countof(wszBuf)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
WCHAR wszParams[MAX_PATH]; wcscpy(wszParams, L"/select,"); wcscat(wszParams, wszBuf);
SHELLEXECUTEINFOW sei; memset(&sei, 0, sizeof sei); sei.cbSize = sizeof sei; sei.fMask = SEE_MASK_WAITFORINPUTIDLE; sei.lpFile = L"explorer.exe"; sei.lpParameters = wszParams;
if (ShellExecuteExW(&sei)) return S_OK; else return E_FAIL;}
/* * for internal use */HRESULTSHELL32_ShowPropertiesDialog(IDataObject *pdtobj){ if (!pdtobj) return E_INVALIDARG; return SHELL32_ShowFilesystemItemsPropertiesDialogAsync(NULL, pdtobj);}
HRESULTSHELL32_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdo, UINT msg){ switch (msg) { case DFM_MERGECONTEXTMENU: return S_OK; // Yes, I want verbs case DFM_INVOKECOMMAND: return S_FALSE; // Do it for me please case DFM_GETDEFSTATICID: return S_FALSE; // Supposedly "required for Windows 7 to pick a default" } return E_NOTIMPL;}