From d05dcf6a0227e6a4f9598b72ff54cc6f999b553e Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Wed, 10 Apr 2024 09:32:13 +0900 Subject: [PATCH] [SHELL32][SDK] Fix ParseDisplayName Part 1 (#6721) JIRA issue: CORE-19495 - Implement SHParseDisplayName and CDesktopFolder::ParseDisplayName. - Add CStubFolderBase, CShellUrlStub, CFileUrlStub, CIDListUrlStub, and CHttpUrlStub helper classes. - Add SHGetSpecialFolderID and Shell_ParseSpecialFolder helper functions. - Add BindCtx_ContainsObject, BindCtx_GetUIWindow, BindCtx_RegisterObjectParam, SHBindToObject, SHBindToObjectEx, SHCoInitializeAnyApartment, SHGetAttributes, SHGetNameAndFlagsW, SHIsFileSysBindCtx, SHSkipJunctionBinding, Shell_DisplayNameOf, and Shell_FailForceReturn helper functions. - Modify CSIDL data. --- dll/win32/shell32/folders/CDesktopFolder.cpp | 362 +++++++++++++++---- dll/win32/shell32/folders/CDesktopFolder.h | 93 +++++ dll/win32/shell32/precomp.h | 58 +++ dll/win32/shell32/utils.cpp | 310 ++++++++++++++++ dll/win32/shell32/wine/pidl.c | 65 +++- dll/win32/shell32/wine/shellpath.c | 61 +++- sdk/include/psdk/shobjidl.idl | 6 + sdk/include/reactos/shlwapi_undoc.h | 2 + 8 files changed, 862 insertions(+), 95 deletions(-) diff --git a/dll/win32/shell32/folders/CDesktopFolder.cpp b/dll/win32/shell32/folders/CDesktopFolder.cpp index 37dc4cb6118..51695e196f4 100644 --- a/dll/win32/shell32/folders/CDesktopFolder.cpp +++ b/dll/win32/shell32/folders/CDesktopFolder.cpp @@ -24,6 +24,223 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); +STDMETHODIMP +CShellUrlStub::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, + PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) +{ + LPWSTR pch; + INT cch, csidl; + HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + PARSEDURLW ParsedURL = { sizeof(ParsedURL) }; + + ::ParseURLW(lpszDisplayName, &ParsedURL); + + DWORD attrs = (pdwAttributes ? *pdwAttributes : 0) | SFGAO_STREAM; + if (ParsedURL.pszSuffix[0] == L':' && ParsedURL.pszSuffix[1] == L':') + { + CComPtr psfDesktop; + hr = SHGetDesktopFolder(&psfDesktop); + if (SUCCEEDED(hr)) + { + CComPtr pBindCtx; + hr = ::CreateBindCtx(0, &pBindCtx); + if (SUCCEEDED(hr)) + { + BIND_OPTS BindOps = { sizeof(BindOps) }; + BindOps.grfMode = STGM_CREATE; + pBindCtx->SetBindOptions(&BindOps); + hr = psfDesktop->ParseDisplayName(hwndOwner, pBindCtx, + (LPWSTR)ParsedURL.pszSuffix, + pchEaten, ppidl, &attrs); + } + } + } + else + { + csidl = Shell_ParseSpecialFolder(ParsedURL.pszSuffix, &pch, &cch); + if (csidl == -1) + { + ERR("\n"); + return hr; + } + + CComHeapPtr pidlLocation; + hr = SHGetFolderLocation(hwndOwner, (csidl | CSIDL_FLAG_CREATE), NULL, 0, &pidlLocation); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + if (pch && *pch) + { + CComPtr psfFolder; + hr = SHBindToObject(NULL, pidlLocation, IID_PPV_ARG(IShellFolder, &psfFolder)); + if (SUCCEEDED(hr)) + { + CComHeapPtr pidlNew; + hr = psfFolder->ParseDisplayName(hwndOwner, pbc, pch, pchEaten, &pidlNew, &attrs); + if (SUCCEEDED(hr)) + { + hr = SHILCombine(pidlLocation, pidlNew, ppidl); + if (pchEaten) + *pchEaten += cch; + } + } + } + else + { + if (attrs) + hr = SHGetNameAndFlagsW(pidlLocation, 0, NULL, 0, &attrs); + + if (SUCCEEDED(hr)) + { + if (pchEaten) + *pchEaten = cch; + *ppidl = pidlLocation.Detach(); + } + } + } + + // FIXME: SHWindowsPolicy + if (SUCCEEDED(hr) && (attrs & SFGAO_STREAM) && + !BindCtx_ContainsObject(pbc, STR_PARSE_SHELL_PROTOCOL_TO_FILE_OBJECTS)) + { + ILFree(*ppidl); + *ppidl = NULL; + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + if (pdwAttributes) + *pdwAttributes = attrs; + + // FIXME: SHWindowsPolicy + if (FAILED(hr) && !Shell_FailForceReturn(hr)) + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + return hr; +} + +STDMETHODIMP +CFileUrlStub::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, + PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) +{ + WCHAR szPath[MAX_PATH]; + DWORD cchPath = _countof(szPath); + HRESULT hr = PathCreateFromUrlW(lpszDisplayName, szPath, &cchPath, 0); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + CComPtr psfDesktop; + hr = SHGetDesktopFolder(&psfDesktop); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + return psfDesktop->ParseDisplayName(hwndOwner, pbc, szPath, pchEaten, ppidl, pdwAttributes); +} + +STDMETHODIMP +CIDListUrlStub::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, + PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) +{ + return E_NOTIMPL; // FIXME +} + +STDMETHODIMP +CHttpUrlStub::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, + PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) +{ + return E_NOTIMPL; // FIXME +} + +BOOL CDesktopFolder::_TryUrlJunctions( + LPCWSTR pcszURL, + IBindCtx *pBindCtx, + IShellFolder **ppShellFolder, + LPITEMIDLIST *ppidl) +{ + PARSEDURLW ParsedURL = { sizeof(ParsedURL) }; + ::ParseURLW(pcszURL, &ParsedURL); + + *ppShellFolder = NULL; + switch (ParsedURL.nScheme) + { + case URL_SCHEME_FILE: + *ppShellFolder = &m_FileUrlStub; + break; + + case URL_SCHEME_HTTP: + case URL_SCHEME_HTTPS: + if (!BindCtx_ContainsObject(pBindCtx, STR_PARSE_PREFER_FOLDER_BROWSING)) + break; + *ppShellFolder = &m_HttpUrlStub; + break; + + case URL_SCHEME_SHELL: + *ppShellFolder = &m_ShellUrlStub; + break; + + case URL_SCHEME_MSSHELLROOTED: + *ppShellFolder = NULL; // FIXME + break; + + case URL_SCHEME_MSSHELLIDLIST: + *ppShellFolder = &m_IDListUrlStub; + break; + + default: + break; + } + + return !!*ppShellFolder; +} + +BOOL CDesktopFolder::_GetParentForParsing( + LPCWSTR pszPath, + IBindCtx *pbc, + IShellFolder **ppParentFolder, + LPITEMIDLIST *ppidlParent) +{ + WCHAR wch = *pszPath; + if (((L'A' <= wch && wch <= L'Z') || (L'a' <= wch && wch <= L'z')) && (pszPath[1] == ':')) + *ppidlParent = _ILCreateMyComputer(); + else if (PathIsUNCW(pszPath)) + *ppidlParent = _ILCreateNetwork(); + else if (UrlIsW(pszPath, URLIS_URL) && !SHSkipJunctionBinding(pbc, NULL)) + _TryUrlJunctions(pszPath, pbc, ppParentFolder, ppidlParent); + + if (!*ppParentFolder && *ppidlParent) + SHBindToObject(NULL, *ppidlParent, IID_PPV_ARG(IShellFolder, ppParentFolder)); + + return *ppParentFolder != NULL; +} + +HRESULT CDesktopFolder::_ChildParseDisplayName( + IShellFolder *pParentFolder, + LPCITEMIDLIST pidlParent, + HWND hwndOwner, + IBindCtx *pBindCtx, + LPWSTR lpszDisplayName, + DWORD *pchEaten, + LPITEMIDLIST *ppidl, + DWORD *pdwAttributes) +{ + LPITEMIDLIST pidlChild; + HRESULT hr = pParentFolder->ParseDisplayName(hwndOwner, pBindCtx, lpszDisplayName, + pchEaten, &pidlChild, pdwAttributes); + if (FAILED(hr)) + return hr; + + if (pidlParent) + { + *ppidl = ILCombine(pidlParent, pidlChild); + ILFree(pidlChild); + } + else + { + *ppidl = pidlChild; + } + + return (*ppidl ? S_OK : E_OUTOFMEMORY); +} + /* CDesktopFolder should create two file system folders internally, one representing the user's desktop folder, and the other representing the common desktop folder. It should @@ -285,11 +502,6 @@ HRESULT WINAPI CDesktopFolder::ParseDisplayName( PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) { - LPCWSTR szNext = NULL; - LPITEMIDLIST pidlTemp = NULL; - PARSEDURLW urldata; - HRESULT hr = S_OK; - TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n", this, hwndOwner, pbc, lpszDisplayName, debugstr_w(lpszDisplayName), pchEaten, ppidl, pdwAttributes); @@ -302,78 +514,98 @@ HRESULT WINAPI CDesktopFolder::ParseDisplayName( if (!lpszDisplayName) return E_INVALIDARG; - if (pchEaten) - *pchEaten = 0; /* strange but like the original */ - - urldata.cbSize = sizeof(urldata); + if (!*lpszDisplayName) + { + *ppidl = _ILCreateMyComputer(); + return (*ppidl ? S_OK : E_OUTOFMEMORY); + } if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':') { - return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes); - } - else if (PathGetDriveNumberW (lpszDisplayName) >= 0) - { - /* it's a filesystem path with a drive. Let MyComputer/UnixDosFolder parse it */ - pidlTemp = _ILCreateMyComputer (); - szNext = lpszDisplayName; - } - else if (PathIsUNCW(lpszDisplayName)) - { - pidlTemp = _ILCreateNetwork(); - szNext = lpszDisplayName; - } - else if( (pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, lpszDisplayName)) ) - { - *ppidl = pidlTemp; - return S_OK; - } - else if (SUCCEEDED(ParseURLW(lpszDisplayName, &urldata))) - { - if (urldata.nScheme == URL_SCHEME_SHELL) /* handle shell: urls */ - { - TRACE ("-- shell url: %s\n", debugstr_w(urldata.pszSuffix)); - pidlTemp = _ILCreateGuidFromStrW(urldata.pszSuffix + 2); - } - else - return IEParseDisplayNameWithBCW(CP_ACP, lpszDisplayName, pbc, ppidl); - } - else - { - if (*lpszDisplayName) - { - /* it's a filesystem path on the desktop. Let a FSFolder parse it */ - hr = m_DesktopFSFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes); - if (SUCCEEDED(hr)) - return hr; - - return m_SharedDesktopFSFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes); - } - else - pidlTemp = _ILCreateMyComputer(); - - szNext = NULL; + return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, + pdwAttributes); } - if (SUCCEEDED(hr) && pidlTemp) + HRESULT hr = E_INVALIDARG; + CComHeapPtr pidlParent; + CComPtr pParentFolder; + if (_GetParentForParsing(lpszDisplayName, pbc, &pParentFolder, &pidlParent)) { - if (szNext && *szNext) + if (pchEaten) + *pchEaten = 0; + + hr = _ChildParseDisplayName(pParentFolder, + pidlParent, + hwndOwner, + pbc, + lpszDisplayName, + pchEaten, + ppidl, + pdwAttributes); + if (SUCCEEDED(hr)) { - hr = SHELL32_ParseNextElement(this, hwndOwner, pbc, - &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes); - } - else - { - if (pdwAttributes && *pdwAttributes) + if (BindCtx_ContainsObject(pbc, STR_PARSE_TRANSLATE_ALIASES)) { - GetAttributesOf(1, &pidlTemp, pdwAttributes); + LPITEMIDLIST pidlAlias; + if (SUCCEEDED(Shell_TranslateIDListAlias(*ppidl, NULL, &pidlAlias, 0xFFFF))) + { + ILFree(*ppidl); + *ppidl = pidlAlias; + } } + return hr; } } - if (SUCCEEDED(hr)) - *ppidl = pidlTemp; - else - *ppidl = NULL; + if (Shell_FailForceReturn(hr)) + return hr; + + if (BindCtx_ContainsObject(pbc, STR_DONT_PARSE_RELATIVE)) + return E_INVALIDARG; + + if (SHIsFileSysBindCtx(pbc, NULL) == S_OK) + return hr; + + BIND_OPTS BindOps = { sizeof(BindOps) }; + BOOL bCreate = FALSE; + if (pbc && SUCCEEDED(pbc->GetBindOptions(&BindOps)) && (BindOps.grfMode & STGM_CREATE)) + { + BindOps.grfMode &= ~STGM_CREATE; + bCreate = TRUE; + pbc->SetBindOptions(&BindOps); + } + + if (m_DesktopFSFolder) + { + hr = m_DesktopFSFolder->ParseDisplayName(hwndOwner, + pbc, + lpszDisplayName, + pchEaten, + ppidl, + pdwAttributes); + } + + if (FAILED(hr) && m_SharedDesktopFSFolder) + { + hr = m_SharedDesktopFSFolder->ParseDisplayName(hwndOwner, + pbc, + lpszDisplayName, + pchEaten, + ppidl, + pdwAttributes); + } + + if (FAILED(hr) && bCreate && m_DesktopFSFolder) + { + BindOps.grfMode |= STGM_CREATE; + pbc->SetBindOptions(&BindOps); + hr = m_DesktopFSFolder->ParseDisplayName(hwndOwner, + pbc, + lpszDisplayName, + pchEaten, + ppidl, + pdwAttributes); + } TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr); diff --git a/dll/win32/shell32/folders/CDesktopFolder.h b/dll/win32/shell32/folders/CDesktopFolder.h index 4d77cb672f8..ea68c0c0315 100644 --- a/dll/win32/shell32/folders/CDesktopFolder.h +++ b/dll/win32/shell32/folders/CDesktopFolder.h @@ -23,6 +23,73 @@ #ifndef _CDESKTOPFOLDER_H_ #define _CDESKTOPFOLDER_H_ +class CStubFolderBase : public IShellFolder +{ +public: + CStubFolderBase() { } + + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override { return E_NOTIMPL; } + STDMETHODIMP_(ULONG) AddRef() override { return 3; } + STDMETHODIMP_(ULONG) Release() override { return 2; } + + // IShellFolder methods + STDMETHODIMP ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, + DWORD *pchEaten, PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) override + { return E_NOTIMPL; } + STDMETHODIMP EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList) override + { return E_NOTIMPL; } + STDMETHODIMP BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) override + { return E_NOTIMPL; } + STDMETHODIMP BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut) override + { return E_NOTIMPL; } + STDMETHODIMP CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) override + { return E_NOTIMPL; } + STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut) override + { return E_NOTIMPL; } + STDMETHODIMP GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut) override + { return E_NOTIMPL; } + STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, + REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) override + { return E_NOTIMPL; } + STDMETHODIMP GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet) override + { return E_NOTIMPL; } + STDMETHODIMP SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, + DWORD dwFlags, PITEMID_CHILD *pPidlOut) override + { return E_NOTIMPL; } +}; + +class CShellUrlStub : public CStubFolderBase +{ +public: + STDMETHODIMP + ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, + PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) override; +}; + +class CFileUrlStub : public CStubFolderBase +{ +public: + STDMETHODIMP + ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, + PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) override; +}; + +class CIDListUrlStub : public CStubFolderBase +{ +public: + STDMETHODIMP + ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, + PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) override; +}; + +class CHttpUrlStub : public CStubFolderBase +{ +public: + STDMETHODIMP + ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, + PIDLIST_RELATIVE *ppidl, DWORD *pdwAttributes) override; +}; + class CDesktopFolder : public CComCoClass, public CComObjectRootEx, @@ -37,11 +104,37 @@ class CDesktopFolder : CComPtr m_SharedDesktopFSFolder; CComPtr m_regFolder; + // Stub URL objects + CShellUrlStub m_ShellUrlStub; + CFileUrlStub m_FileUrlStub; + CIDListUrlStub m_IDListUrlStub; + CHttpUrlStub m_HttpUrlStub; + LPWSTR sPathTarget; /* complete path to target used for enumeration and ChangeNotify */ LPITEMIDLIST pidlRoot; /* absolute pidl */ HRESULT _GetSFFromPidl(LPCITEMIDLIST pidl, IShellFolder2** psf); + BOOL _TryUrlJunctions( + LPCWSTR pcszURL, + IBindCtx *pBindCtx, + IShellFolder **ppShellFolder, + LPITEMIDLIST *ppidl); + BOOL _GetParentForParsing( + LPCWSTR pszPath, + IBindCtx *pbc, + IShellFolder **ppParentFolder, + LPITEMIDLIST *ppidlParent); + HRESULT _ChildParseDisplayName( + IShellFolder *pParentFolder, + LPCITEMIDLIST pidlParent, + HWND hwndOwner, + IBindCtx *pBindCtx, + LPWSTR lpszDisplayName, + DWORD *pchEaten, + LPITEMIDLIST *ppidl, + DWORD *pdwAttributes); + public: CDesktopFolder(); ~CDesktopFolder(); diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h index 393693885ac..74765c7c2e9 100644 --- a/dll/win32/shell32/precomp.h +++ b/dll/win32/shell32/precomp.h @@ -153,4 +153,62 @@ public: END_MSG_MAP() }; +HRESULT +Shell_TranslateIDListAlias( + _In_ LPCITEMIDLIST pidl, + _In_ HANDLE hToken, + _Out_ LPITEMIDLIST *ppidlAlias, + _In_ DWORD dwFlags); + +BOOL BindCtx_ContainsObject(_In_ IBindCtx *pBindCtx, _In_ LPCWSTR pszName); +BOOL SHSkipJunctionBinding(_In_ IBindCtx *pbc, _In_ CLSID *pclsid); +HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW **ppFindData); +BOOL Shell_FailForceReturn(_In_ HRESULT hr); + +EXTERN_C INT +Shell_ParseSpecialFolder(_In_ LPCWSTR pszStart, _Out_ LPWSTR *ppch, _Out_ INT *pcch); + +HRESULT +Shell_DisplayNameOf( + _In_ IShellFolder *psf, + _In_ LPCITEMIDLIST pidl, + _In_ DWORD dwFlags, + _Out_ LPWSTR pszBuf, + _In_ UINT cchBuf); + +HRESULT SHBindToObject( + _In_opt_ IShellFolder *psf, + _In_ LPCITEMIDLIST pidl, + _In_ REFIID riid, + _Out_ void **ppvObj); + +HRESULT +SHBindToObjectEx( + _In_opt_ IShellFolder *pShellFolder, + _In_ LPCITEMIDLIST pidl, + _In_opt_ IBindCtx *pBindCtx, + _In_ REFIID riid, + _Out_ void **ppvObj); + +DWORD +SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes); +HRESULT SHCoInitializeAnyApartment(VOID); + +HRESULT +SHGetNameAndFlagsW( + _In_ LPCITEMIDLIST pidl, + _In_ DWORD dwFlags, + _Out_opt_ LPWSTR pszText, + _In_ UINT cchBuf, + _Inout_opt_ DWORD *pdwAttributes); + +EXTERN_C HWND BindCtx_GetUIWindow(_In_ IBindCtx *pBindCtx); + +EXTERN_C HRESULT +BindCtx_RegisterObjectParam( + _In_ IBindCtx *pBindCtx, + _In_ LPOLESTR pszKey, + _In_opt_ IUnknown *punk, + _Out_ LPBC *ppbc); + #endif /* _PRECOMP_H__ */ diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp index 9f9309b645c..0909b235573 100644 --- a/dll/win32/shell32/utils.cpp +++ b/dll/win32/shell32/utils.cpp @@ -31,6 +31,316 @@ OpenEffectiveToken( return ret; } +HRESULT +Shell_TranslateIDListAlias( + _In_ LPCITEMIDLIST pidl, + _In_ HANDLE hToken, + _Out_ LPITEMIDLIST *ppidlAlias, + _In_ DWORD dwFlags) +{ + return E_FAIL; //FIXME +} + +BOOL BindCtx_ContainsObject(_In_ IBindCtx *pBindCtx, _In_ LPCWSTR pszName) +{ + CComPtr punk; + if (!pBindCtx || FAILED(pBindCtx->GetObjectParam(const_cast(pszName), &punk))) + return FALSE; + return TRUE; +} + +BOOL SHSkipJunctionBinding(_In_ IBindCtx *pbc, _In_ CLSID *pclsid) +{ + if (!pbc) + return FALSE; + + BIND_OPTS BindOps = { sizeof(BindOps) }; + if (SUCCEEDED(pbc->GetBindOptions(&BindOps)) && BindOps.grfFlags == OLECONTF_LINKS) + return TRUE; + + return pclsid && SHSkipJunction(pbc, pclsid); +} + +HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW **ppFindData) +{ + CComPtr punk; + CComPtr pBindData; + + if (!pBindCtx || FAILED(pBindCtx->GetObjectParam((LPWSTR)STR_FILE_SYS_BIND_DATA, &punk))) + return S_FALSE; + + if (FAILED(punk->QueryInterface(IID_PPV_ARG(IFileSystemBindData, &pBindData)))) + return S_FALSE; + + HRESULT hr = S_OK; + if (ppFindData) + { + *ppFindData = (WIN32_FIND_DATAW*)LocalAlloc(LPTR, sizeof(WIN32_FIND_DATAW)); + if (*ppFindData) + pBindData->GetFindData(*ppFindData); + else + hr = E_OUTOFMEMORY; + } + + return hr; +} + +BOOL Shell_FailForceReturn(_In_ HRESULT hr) +{ + DWORD code = HRESULT_CODE(hr); + + switch (code) + { + case ERROR_BAD_NETPATH: + case ERROR_BAD_NET_NAME: + case ERROR_CANCELLED: + return TRUE; + + default: + return (ERROR_FILE_NOT_FOUND <= code && code <= ERROR_PATH_NOT_FOUND); + } +} + +HRESULT +SHBindToObjectEx( + _In_opt_ IShellFolder *pShellFolder, + _In_ LPCITEMIDLIST pidl, + _In_opt_ IBindCtx *pBindCtx, + _In_ REFIID riid, + _Out_ void **ppvObj) +{ + CComPtr psfDesktop; + + *ppvObj = NULL; + + if (!pShellFolder) + { + SHGetDesktopFolder(&psfDesktop); + if (!psfDesktop) + return E_FAIL; + + pShellFolder = psfDesktop; + } + + HRESULT hr; + if (_ILIsDesktop(pidl)) + hr = pShellFolder->QueryInterface(riid, ppvObj); + else + hr = pShellFolder->BindToObject(pidl, pBindCtx, riid, ppvObj); + + if (SUCCEEDED(hr) && !*ppvObj) + hr = E_FAIL; + + return hr; +} + +HRESULT SHBindToObject( + _In_opt_ IShellFolder *psf, + _In_ LPCITEMIDLIST pidl, + _In_ REFIID riid, + _Out_ void **ppvObj) +{ + return SHBindToObjectEx(psf, pidl, NULL, riid, ppvObj); +} + +HRESULT +Shell_DisplayNameOf( + _In_ IShellFolder *psf, + _In_ LPCITEMIDLIST pidl, + _In_ DWORD dwFlags, + _Out_ LPWSTR pszBuf, + _In_ UINT cchBuf) +{ + *pszBuf = UNICODE_NULL; + STRRET sr; + HRESULT hr = psf->GetDisplayNameOf(pidl, dwFlags, &sr); + if (FAILED(hr)) + return hr; + return StrRetToBufW(&sr, pidl, pszBuf, cchBuf); +} + +DWORD +SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes) +{ + LPCITEMIDLIST pidlLast; + + if (psf) + { + psf->AddRef(); + pidlLast = pidl; + } + else + { + SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); + } + + if (!psf) + return 0; + + DWORD oldAttrs = dwAttributes; + if (FAILED(psf->GetAttributesOf(1, &pidlLast, &dwAttributes))) + dwAttributes = 0; + else + dwAttributes &= oldAttrs; + + if ((dwAttributes & SFGAO_FOLDER) && + (dwAttributes & SFGAO_STREAM) && + !(dwAttributes & SFGAO_STORAGEANCESTOR) && + (oldAttrs & SFGAO_STORAGEANCESTOR) && + (SHGetObjectCompatFlags(psf, NULL) & 0x200)) + { + dwAttributes &= ~(SFGAO_STREAM | SFGAO_STORAGEANCESTOR); + dwAttributes |= SFGAO_STORAGEANCESTOR; + } + + if (psf) + psf->Release(); + + return dwAttributes; +} + +HRESULT SHCoInitializeAnyApartment(VOID) +{ + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if (FAILED(hr)) + hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE); + return hr; +} + +HRESULT +SHGetNameAndFlagsW( + _In_ LPCITEMIDLIST pidl, + _In_ DWORD dwFlags, + _Out_opt_ LPWSTR pszText, + _In_ UINT cchBuf, + _Inout_opt_ DWORD *pdwAttributes) +{ + if (pszText) + *pszText = UNICODE_NULL; + + HRESULT hrCoInit = SHCoInitializeAnyApartment(); + + CComPtr psfFolder; + LPCITEMIDLIST ppidlLast; + HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psfFolder), &ppidlLast); + if (SUCCEEDED(hr)) + { + if (pszText) + hr = Shell_DisplayNameOf(psfFolder, ppidlLast, dwFlags, pszText, cchBuf); + + if (SUCCEEDED(hr)) + { + if (pdwAttributes) + *pdwAttributes = SHGetAttributes(psfFolder, ppidlLast, *pdwAttributes); + } + } + + if (SUCCEEDED(hrCoInit)) + CoUninitialize(); + + return hr; +} + +EXTERN_C HWND +BindCtx_GetUIWindow(_In_ IBindCtx *pBindCtx) +{ + HWND hWnd = NULL; + + CComPtr punk; + if (pBindCtx && SUCCEEDED(pBindCtx->GetObjectParam((LPWSTR)L"UI During Binding", &punk))) + IUnknown_GetWindow(punk, &hWnd); + + return hWnd; +} + +class CDummyOleWindow : public IOleWindow +{ +protected: + LONG m_cRefs; + HWND m_hWnd; + +public: + CDummyOleWindow() : m_cRefs(1), m_hWnd(NULL) { } + virtual ~CDummyOleWindow() { } + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj) override + { + static const QITAB c_tab[] = + { + QITABENT(CDummyOleWindow, IOleWindow), + { NULL } + }; + return ::QISearch(this, c_tab, riid, ppvObj); + } + STDMETHODIMP_(ULONG) AddRef() override + { + return ++m_cRefs; + } + STDMETHODIMP_(ULONG) Release() override + { + if (--m_cRefs == 0) + { + delete this; + return 0; + } + return m_cRefs; + } + + // IOleWindow methods + STDMETHODIMP GetWindow(HWND *phWnd) override + { + *phWnd = m_hWnd; + if (!m_hWnd) + return E_NOTIMPL; + return S_OK; + } + STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override + { + return E_NOTIMPL; + } +}; + +EXTERN_C HRESULT +BindCtx_RegisterObjectParam( + _In_ IBindCtx *pBindCtx, + _In_ LPOLESTR pszKey, + _In_opt_ IUnknown *punk, + _Out_ LPBC *ppbc) +{ + HRESULT hr = S_OK; + CDummyOleWindow *pUnknown = NULL; + + *ppbc = pBindCtx; + + if (pBindCtx) + { + pBindCtx->AddRef(); + } + else + { + hr = CreateBindCtx(0, ppbc); + if (FAILED(hr)) + return hr; + } + + if (!punk) + punk = pUnknown = new CDummyOleWindow(); + + hr = (*ppbc)->RegisterObjectParam(pszKey, punk); + + if (pUnknown) + pUnknown->Release(); + + if (FAILED(hr)) + { + (*ppbc)->Release(); + *ppbc = NULL; + } + + return hr; +} + /************************************************************************* * SHSetFolderPathA (SHELL32.231) * diff --git a/dll/win32/shell32/wine/pidl.c b/dll/win32/shell32/wine/pidl.c index 7279737c8eb..824af18984a 100644 --- a/dll/win32/shell32/wine/pidl.c +++ b/dll/win32/shell32/wine/pidl.c @@ -54,6 +54,15 @@ extern BOOL WINAPI Free(LPVOID); static LPSTR _ILGetSTextPointer(LPCITEMIDLIST pidl); static LPWSTR _ILGetTextPointerW(LPCITEMIDLIST pidl); +EXTERN_C HWND BindCtx_GetUIWindow(_In_ IBindCtx *pBindCtx); + +EXTERN_C HRESULT +BindCtx_RegisterObjectParam( + _In_ IBindCtx *pBindCtx, + _In_ LPOLESTR pszKey, + _In_opt_ IUnknown *punk, + _Out_ LPBC *ppbc); + /************************************************************************* * ILGetDisplayNameExA * @@ -1396,39 +1405,59 @@ HRESULT WINAPI SHBindToParent(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv, LPCI HRESULT WINAPI SHParseDisplayName(LPCWSTR pszName, IBindCtx *pbc, LPITEMIDLIST *ppidl, SFGAOF sfgaoIn, SFGAOF *psfgaoOut) { + HRESULT hr; + LPWSTR pszNameDup; IShellFolder *psfDesktop; - HRESULT hr=E_FAIL; - ULONG dwAttr=sfgaoIn; + IBindCtx *pBindCtx = NULL; - if(!ppidl) - return E_INVALIDARG; + TRACE("(%s, %p, %p, 0x%X, %p)\n", pszName, pbc, ppidl, sfgaoIn, psfgaoOut); - if (!pszName) - { - *ppidl = NULL; - return E_INVALIDARG; - } + *ppidl = NULL; + if (psfgaoOut) + *psfgaoOut = 0; + + pszNameDup = StrDupW(pszName); + if (!pszNameDup) + return E_OUTOFMEMORY; + + psfDesktop = NULL; hr = SHGetDesktopFolder(&psfDesktop); if (FAILED(hr)) { - *ppidl = NULL; + LocalFree(pszNameDup); return hr; } - hr = IShellFolder_ParseDisplayName(psfDesktop, (HWND)NULL, pbc, (LPOLESTR)pszName, (ULONG *)NULL, ppidl, &dwAttr); - - IShellFolder_Release(psfDesktop); + if (!pbc) + { + hr = BindCtx_RegisterObjectParam(NULL, STR_PARSE_TRANSLATE_ALIASES, NULL, &pBindCtx); + pbc = pBindCtx; + } if (SUCCEEDED(hr)) { - if (psfgaoOut) *psfgaoOut = dwAttr; - } - else - { - *ppidl = NULL; + ULONG sfgao = sfgaoIn, cchEaten; + HWND hwndUI = BindCtx_GetUIWindow(pbc); + hr = psfDesktop->lpVtbl->ParseDisplayName(psfDesktop, + hwndUI, + pbc, + pszNameDup, + &cchEaten, + ppidl, + (psfgaoOut ? &sfgao : NULL)); + if (SUCCEEDED(hr) && psfgaoOut) + *psfgaoOut = (sfgao & sfgaoIn); } + LocalFree(pszNameDup); + + if (psfDesktop) + psfDesktop->lpVtbl->Release(psfDesktop); + + if (pBindCtx) + pBindCtx->lpVtbl->Release(pBindCtx); + return hr; } diff --git a/dll/win32/shell32/wine/shellpath.c b/dll/win32/shell32/wine/shellpath.c index 160e25c03d1..dc4e94e2716 100644 --- a/dll/win32/shell32/wine/shellpath.c +++ b/dll/win32/shell32/wine/shellpath.c @@ -1104,14 +1104,14 @@ static const CSIDL_DATA CSIDL_Data[] = { /* 0x03 - CSIDL_CONTROLS (.CPL files) */ &FOLDERID_ControlPanelFolder, CSIDL_Type_SystemPath, - NULL, + L"ControlPanelFolder", NULL, -IDI_SHELL_CONTROL_PANEL }, { /* 0x04 - CSIDL_PRINTERS */ &FOLDERID_PrintersFolder, CSIDL_Type_SystemPath, - NULL, + L"PrintersFolder", NULL, -IDI_SHELL_PRINTERS_FOLDER }, @@ -1151,7 +1151,7 @@ static const CSIDL_DATA CSIDL_Data[] = { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */ &FOLDERID_RecycleBinFolder, CSIDL_Type_Disallowed, - NULL, + L"RecycleBinFolder", NULL }, { /* 0x0b - CSIDL_STARTMENU */ @@ -1210,14 +1210,14 @@ static const CSIDL_DATA CSIDL_Data[] = { /* 0x11 - CSIDL_DRIVES */ &FOLDERID_ComputerFolder, CSIDL_Type_Disallowed, - NULL, + L"MyComputerFolder", NULL, -IDI_SHELL_COMPUTER_FOLDER }, { /* 0x12 - CSIDL_NETWORK */ &FOLDERID_NetworkFolder, CSIDL_Type_Disallowed, - NULL, + L"NetworkPlacesFolder", NULL, -IDI_SHELL_NETWORK_FOLDER }, @@ -1341,21 +1341,21 @@ static const CSIDL_DATA CSIDL_Data[] = { /* 0x24 - CSIDL_WINDOWS */ &FOLDERID_Windows, CSIDL_Type_WindowsPath, - NULL, + L"Windows", NULL, -IDI_SHELL_SYSTEM_GEAR }, { /* 0x25 - CSIDL_SYSTEM */ &FOLDERID_System, CSIDL_Type_SystemPath, - NULL, + L"System", NULL, -IDI_SHELL_SYSTEM_GEAR }, { /* 0x26 - CSIDL_PROGRAM_FILES */ &FOLDERID_ProgramFiles, CSIDL_Type_CurrVer, - L"ProgramFilesDir", + L"ProgramFiles", MAKEINTRESOURCEW(IDS_PROGRAM_FILES), #ifdef __REACTOS__ 0 @@ -1390,21 +1390,21 @@ static const CSIDL_DATA CSIDL_Data[] = { /* 0x2a - CSIDL_PROGRAM_FILESX86 */ &FOLDERID_ProgramFilesX86, CSIDL_Type_CurrVer, - L"ProgramFilesDir (x86)", + L"ProgramFilesX86", L"Program Files (x86)", -IDI_SHELL_PROGRAMS_FOLDER }, { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */ &FOLDERID_ProgramFilesCommon, CSIDL_Type_CurrVer, - L"CommonFilesDir", + L"ProgramFilesCommon", MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON), -IDI_SHELL_PROGRAMS_FOLDER }, { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */ &FOLDERID_ProgramFilesCommonX86, CSIDL_Type_CurrVer, - L"CommonFilesDir (x86)", + L"ProgramFilesCommonX86", L"Program Files (x86)\\Common Files", -IDI_SHELL_PROGRAMS_FOLDER }, @@ -1436,7 +1436,7 @@ static const CSIDL_DATA CSIDL_Data[] = { /* 0x31 - CSIDL_CONNECTIONS */ &FOLDERID_ConnectionsFolder, CSIDL_Type_Disallowed, - NULL, + L"ConnectionsFolder", NULL, -IDI_SHELL_NETWORK_CONNECTIONS }, @@ -1842,6 +1842,43 @@ static const CSIDL_DATA CSIDL_Data[] = #endif }; +INT SHGetSpecialFolderID(_In_ LPCWSTR pszName) +{ + UINT csidl; + + for (csidl = 0; csidl < _countof(CSIDL_Data); ++csidl) + { + const CSIDL_DATA *pData = &CSIDL_Data[csidl]; + if (pData->szValueName && lstrcmpiW(pszName, pData->szValueName) == 0) + return csidl; + } + + return -1; +} + +INT Shell_ParseSpecialFolder(_In_ LPCWSTR pszStart, _Out_ LPWSTR *ppch, _Out_ INT *pcch) +{ + LPCWSTR pszPath, pchBackslash; + WCHAR szPath[MAX_PATH]; + + pchBackslash = StrChrW(pszStart, L'\\'); + if (pchBackslash) + { + *ppch = (LPWSTR)(pchBackslash + 1); + *pcch = (pchBackslash - pszStart) + 1; + StrCpyNW(szPath, pszStart, max(*pcch, _countof(szPath))); + pszPath = szPath; + } + else + { + *ppch = NULL; + *pcch = lstrlenW(pszStart); + pszPath = pszStart; + } + + return SHGetSpecialFolderID(pszPath); +} + #ifndef __REACTOS__ static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest); #else diff --git a/sdk/include/psdk/shobjidl.idl b/sdk/include/psdk/shobjidl.idl index 7a75dff0f09..6b4bdadffc9 100644 --- a/sdk/include/psdk/shobjidl.idl +++ b/sdk/include/psdk/shobjidl.idl @@ -219,6 +219,12 @@ interface IShellFolder : IUnknown cpp_quote("#define SFGAO_STORAGECAPMASK 0x70C50008L") cpp_quote("#define SFGAO_PKEYSFGAOMASK 0x81044000L") + cpp_quote("#define STR_PARSE_SHELL_PROTOCOL_TO_FILE_OBJECTS L\"Parse Shell Protocol To File Objects\"") + cpp_quote("#define STR_FILE_SYS_BIND_DATA L\"File System Bind Data\"") + cpp_quote("#define STR_PARSE_PREFER_FOLDER_BROWSING L\"Parse Prefer Folder Browsing\"") + cpp_quote("#define STR_PARSE_TRANSLATE_ALIASES L\"Parse Translate Aliases\"") + cpp_quote("#define STR_DONT_PARSE_RELATIVE L\"Don't Parse Relative\"") + typedef ULONG SFGAOF; HRESULT ParseDisplayName( diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h index ff2f81f9e04..fd1ac3f3c82 100644 --- a/sdk/include/reactos/shlwapi_undoc.h +++ b/sdk/include/reactos/shlwapi_undoc.h @@ -322,6 +322,8 @@ IContextMenu_Invoke( _In_ LPCSTR lpVerb, _In_ UINT uFlags); +DWORD WINAPI SHGetObjectCompatFlags(IUnknown *pUnk, const CLSID *clsid); + #ifdef __cplusplus } /* extern "C" */ #endif /* defined(__cplusplus) */