From 24833a6dde324dddce1a7316ffaefc0233daf903 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sat, 17 Oct 2020 17:49:24 +0900 Subject: [PATCH] [BROWSEUI] Implement CLSID_ACListISF (#3298) Implement enumeration of IShellFolder items of auto-completion. CORE-9281 --- dll/win32/browseui/aclistisf.cpp | 362 +++++++++++++++++++++++++++++-- dll/win32/browseui/aclistisf.h | 52 ++++- 2 files changed, 380 insertions(+), 34 deletions(-) diff --git a/dll/win32/browseui/aclistisf.cpp b/dll/win32/browseui/aclistisf.cpp index 72045b3a14e..533a7af814b 100644 --- a/dll/win32/browseui/aclistisf.cpp +++ b/dll/win32/browseui/aclistisf.cpp @@ -2,6 +2,7 @@ * Shell AutoComplete list * * Copyright 2015 Thomas Faber + * Copyright 2020 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,8 +21,10 @@ #include "precomp.h" -CACListISF::CACListISF() : - m_dwOptions(0) +CACListISF::CACListISF() + : m_dwOptions(ACLO_CURRENTDIR | ACLO_MYCOMPUTER) + , m_iNextLocation(LT_DIRECTORY) + , m_fShowHidden(FALSE) { } @@ -29,26 +32,290 @@ CACListISF::~CACListISF() { } -// *** IEnumString methods *** -HRESULT STDMETHODCALLTYPE CACListISF::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) -{ - TRACE("(%p, %d, %p, %p)\n", this, celt, rgelt, pceltFetched); - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE CACListISF::Reset() +HRESULT CACListISF::NextLocation() { TRACE("(%p)\n", this); - return E_NOTIMPL; + HRESULT hr; + switch (m_iNextLocation) + { + case LT_DIRECTORY: + m_iNextLocation = LT_DESKTOP; + if (!ILIsEmpty(m_pidlCurDir) && (m_dwOptions & ACLO_CURRENTDIR)) + { + CComHeapPtr pidl(ILClone(m_pidlCurDir)); + hr = SetLocation(pidl.Detach()); + if (SUCCEEDED(hr)) + { + TRACE("LT_DIRECTORY\n"); + return hr; + } + } + // FALL THROUGH + case LT_DESKTOP: + m_iNextLocation = LT_MYCOMPUTER; + if (m_dwOptions & ACLO_DESKTOP) + { + CComHeapPtr pidl; + hr = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl); + if (FAILED_UNEXPECTEDLY(hr)) + return S_FALSE; + hr = SetLocation(pidl.Detach()); + if (SUCCEEDED(hr)) + { + TRACE("LT_DESKTOP\n"); + return hr; + } + } + // FALL THROUGH + case LT_MYCOMPUTER: + m_iNextLocation = LT_FAVORITES; + if (m_dwOptions & ACLO_MYCOMPUTER) + { + CComHeapPtr pidl; + hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidl); + if (FAILED_UNEXPECTEDLY(hr)) + return S_FALSE; + hr = SetLocation(pidl.Detach()); + if (SUCCEEDED(hr)) + { + TRACE("LT_MYCOMPUTER\n"); + return hr; + } + } + // FALL THROUGH + case LT_FAVORITES: + m_iNextLocation = LT_MAX; + if (m_dwOptions & ACLO_FAVORITES) + { + CComHeapPtr pidl; + hr = SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl); + if (FAILED_UNEXPECTEDLY(hr)) + return S_FALSE; + hr = SetLocation(pidl.Detach()); + if (SUCCEEDED(hr)) + { + TRACE("LT_FAVORITES\n"); + return hr; + } + } + // FALL THROUGH + case LT_MAX: + default: + TRACE("LT_MAX\n"); + return S_FALSE; + } } -HRESULT STDMETHODCALLTYPE CACListISF::Skip(ULONG celt) +HRESULT CACListISF::SetLocation(LPITEMIDLIST pidl) +{ + TRACE("(%p, %p)\n", this, pidl); + + m_pEnumIDList.Release(); + m_pShellFolder.Release(); + m_pidlLocation.Free(); + + if (!pidl) + return E_FAIL; + + m_pidlLocation.Attach(pidl); + + CComPtr pFolder; + HRESULT hr = SHGetDesktopFolder(&pFolder); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + if (!ILIsEmpty(pidl)) + { + hr = pFolder->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &m_pShellFolder)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + } + else + { + m_pShellFolder.Attach(pFolder.Detach()); + } + + SHCONTF Flags = SHCONTF_FOLDERS | SHCONTF_INIT_ON_FIRST_NEXT; + if (m_fShowHidden) + Flags |= SHCONTF_INCLUDEHIDDEN; + if (!(m_dwOptions & ACLO_FILESYSDIRS)) + Flags |= SHCONTF_NONFOLDERS; + + hr = m_pShellFolder->EnumObjects(NULL, Flags, &m_pEnumIDList); + if (hr != S_OK) + { + ERR("EnumObjects failed: 0x%lX\n", hr); + hr = E_FAIL; + } + return hr; +} + +HRESULT CACListISF::GetDisplayName(LPCITEMIDLIST pidlChild, CComHeapPtr& pszChild) +{ + TRACE("(%p, %p)\n", this, pidlChild); + pszChild.Free(); + + STRRET StrRet; + DWORD dwFlags = SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR; + HRESULT hr = m_pShellFolder->GetDisplayNameOf(pidlChild, dwFlags, &StrRet); + if (FAILED(hr)) + { + dwFlags = SHGDN_INFOLDER | SHGDN_FORPARSING; + hr = m_pShellFolder->GetDisplayNameOf(pidlChild, dwFlags, &StrRet); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + } + + hr = StrRetToStrW(&StrRet, NULL, &pszChild); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + TRACE("pszChild: '%S'\n", static_cast(pszChild)); + return hr; +} + +HRESULT CACListISF::GetPathName(LPCITEMIDLIST pidlChild, CComHeapPtr& pszPath) +{ + TRACE("(%p, %p)\n", this, pidlChild); + + CComHeapPtr pszChild; + HRESULT hr = GetDisplayName(pidlChild, pszChild); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + CStringW szPath; + if (m_szExpand.GetLength() && m_iNextLocation == LT_DIRECTORY) + { + INT cchExpand = m_szExpand.GetLength(); + if (StrCmpNIW(pszChild, m_szExpand, cchExpand) != 0 || + pszChild[0] != L'\\' || pszChild[1] != L'\\') + { + szPath = m_szExpand; + } + } + szPath += pszChild; + + INT cchMax = szPath.GetLength() + 1; + CComHeapPtr pszFullPath; + if (!pszFullPath.Allocate(cchMax)) + { + ERR("Out of memory\n"); + return E_OUTOFMEMORY; + } + + StringCchCopyW(pszFullPath, cchMax, szPath); + pszPath.Attach(pszFullPath.Detach()); + TRACE("pszPath: '%S'\n", static_cast(pszPath)); + return S_OK; +} + +// *** IEnumString methods *** +STDMETHODIMP CACListISF::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) +{ + TRACE("(%p, %d, %p, %p)\n", this, celt, rgelt, pceltFetched); + + if (celt == 0) + return S_OK; + if (!rgelt) + return S_FALSE; + + *rgelt = NULL; + if (pceltFetched) + *pceltFetched = 0; + + if (!m_pEnumIDList) + { + NextLocation(); + if (!m_pEnumIDList) + return S_FALSE; + } + + HRESULT hr; + CComHeapPtr pidlChild; + CComHeapPtr pszPathName; + + do + { + for (;;) + { + pidlChild.Free(); + hr = m_pEnumIDList->Next(1, &pidlChild, NULL); + if (hr != S_OK) + break; + + pszPathName.Free(); + GetPathName(pidlChild, pszPathName); + if (!pszPathName) + continue; + + if (m_dwOptions & (ACLO_FILESYSONLY | ACLO_FILESYSDIRS)) + { + DWORD attrs = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM; + hr = m_pShellFolder->GetAttributesOf(1, const_cast(&pidlChild), &attrs); + if (SUCCEEDED(hr)) + { + if (!(attrs & (SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR))) + continue; + } + } + + if ((m_dwOptions & ACLO_FILESYSDIRS) && !PathIsDirectoryW(pszPathName)) + continue; + + hr = S_OK; + break; + } + } while (hr == S_FALSE && NextLocation() == S_OK); + + if (hr == S_OK) + { + *rgelt = pszPathName.Detach(); + if (pceltFetched) + *pceltFetched = 1; + } + else + { + hr = S_FALSE; + } + + TRACE("*rgelt: %S\n", *rgelt); + return hr; +} + +STDMETHODIMP CACListISF::Reset() +{ + TRACE("(%p)\n", this); + + m_iNextLocation = LT_DIRECTORY; + m_szExpand = L""; + + SHELLSTATE ss = { 0 }; + SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE); + m_fShowHidden = ss.fShowAllObjects; + + if (m_dwOptions & ACLO_CURRENTDIR) + { + CComHeapPtr pidl; + if (m_pBrowserService) + { + m_pBrowserService->GetPidl(&pidl); + if (pidl) + Initialize(pidl); + } + HRESULT hr = SetLocation(pidl.Detach()); + if (FAILED_UNEXPECTEDLY(hr)) + return S_FALSE; + } + return S_OK; +} + +STDMETHODIMP CACListISF::Skip(ULONG celt) { TRACE("(%p, %d)\n", this, celt); return E_NOTIMPL; } -HRESULT STDMETHODCALLTYPE CACListISF::Clone(IEnumString **ppOut) +STDMETHODIMP CACListISF::Clone(IEnumString **ppOut) { TRACE("(%p, %p)\n", this, ppOut); *ppOut = NULL; @@ -56,36 +323,54 @@ HRESULT STDMETHODCALLTYPE CACListISF::Clone(IEnumString **ppOut) } // *** IACList methods *** -HRESULT STDMETHODCALLTYPE CACListISF::Expand(LPCOLESTR pszExpand) +STDMETHODIMP CACListISF::Expand(LPCOLESTR pszExpand) { TRACE("(%p, %ls)\n", this, pszExpand); - return E_NOTIMPL; + + m_szExpand = pszExpand; + + m_iNextLocation = LT_DIRECTORY; + CComHeapPtr pidl; + HRESULT hr = SHParseDisplayName(m_szExpand, NULL, &pidl, NULL, NULL); + if (SUCCEEDED(hr)) + { + hr = SetLocation(pidl.Detach()); + if (FAILED_UNEXPECTEDLY(hr)) + m_szExpand = L""; + } + return hr; } // *** IACList2 methods *** -HRESULT STDMETHODCALLTYPE CACListISF::SetOptions(DWORD dwFlag) +STDMETHODIMP CACListISF::SetOptions(DWORD dwFlag) { TRACE("(%p, %lu)\n", this, dwFlag); m_dwOptions = dwFlag; return S_OK; } -HRESULT STDMETHODCALLTYPE CACListISF::GetOptions(DWORD* pdwFlag) +STDMETHODIMP CACListISF::GetOptions(DWORD* pdwFlag) { TRACE("(%p, %p)\n", this, pdwFlag); - *pdwFlag = m_dwOptions; - return S_OK; + if (pdwFlag) + { + *pdwFlag = m_dwOptions; + return S_OK; + } + return E_INVALIDARG; } // *** IShellService methods *** -HRESULT STDMETHODCALLTYPE CACListISF::SetOwner(IUnknown *punkOwner) +STDMETHODIMP CACListISF::SetOwner(IUnknown *punkOwner) { TRACE("(%p, %p)\n", this, punkOwner); - return E_NOTIMPL; + m_pBrowserService.Release(); + punkOwner->QueryInterface(IID_PPV_ARG(IBrowserService, &m_pBrowserService)); + return S_OK; } // *** IPersist methods *** -HRESULT STDMETHODCALLTYPE CACListISF::GetClassID(CLSID *pClassID) +STDMETHODIMP CACListISF::GetClassID(CLSID *pClassID) { TRACE("(%p, %p)\n", this, pClassID); if (pClassID == NULL) @@ -95,8 +380,39 @@ HRESULT STDMETHODCALLTYPE CACListISF::GetClassID(CLSID *pClassID) } // *** IPersistFolder methods *** -HRESULT STDMETHODCALLTYPE CACListISF::Initialize(PCIDLIST_ABSOLUTE pidl) +STDMETHODIMP CACListISF::Initialize(PCIDLIST_ABSOLUTE pidl) { TRACE("(%p, %p)\n", this, pidl); + m_pidlCurDir.Free(); + if (!pidl) + return S_OK; + + LPITEMIDLIST pidlClone = ILClone(pidl); + if (!pidlClone) + { + ERR("Out of memory\n"); + return E_OUTOFMEMORY; + } + m_pidlCurDir.Attach(pidlClone); + return S_OK; +} + +// *** ICurrentWorkingDirectory methods *** +STDMETHODIMP CACListISF::GetDirectory(LPWSTR pwzPath, DWORD cchSize) +{ + TRACE("(%p, %p, %ld)\n", this, pwzPath, cchSize); + return E_NOTIMPL; +} + +STDMETHODIMP CACListISF::SetDirectory(LPCWSTR pwzPath) +{ + TRACE("(%p, %ls, %ld)\n", this, pwzPath); + LPITEMIDLIST pidl = ILCreateFromPathW(pwzPath); + if (!pidl) + { + ERR("Out of memory\n"); + return E_OUTOFMEMORY; + } + m_pidlCurDir.Attach(pidl); return S_OK; } diff --git a/dll/win32/browseui/aclistisf.h b/dll/win32/browseui/aclistisf.h index ddb530946da..abc63efd45c 100644 --- a/dll/win32/browseui/aclistisf.h +++ b/dll/win32/browseui/aclistisf.h @@ -2,6 +2,7 @@ * Shell AutoComplete list * * Copyright 2015 Thomas Faber + * Copyright 2020 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,40 +26,68 @@ class CACListISF : public CComObjectRootEx, public IEnumString, public IACList2, + public ICurrentWorkingDirectory, public IShellService, public IPersistFolder { private: + enum LOCATION_TYPE + { + LT_DIRECTORY, + LT_DESKTOP, + LT_MYCOMPUTER, + LT_FAVORITES, + LT_MAX + }; + DWORD m_dwOptions; + LOCATION_TYPE m_iNextLocation; + BOOL m_fShowHidden; + CStringW m_szExpand; + CComHeapPtr m_pidlLocation; + CComHeapPtr m_pidlCurDir; + CComPtr m_pEnumIDList; + CComPtr m_pShellFolder; + CComPtr m_pBrowserService; public: CACListISF(); ~CACListISF(); + HRESULT NextLocation(); + HRESULT SetLocation(LPITEMIDLIST pidl); + HRESULT GetDisplayName(LPCITEMIDLIST pidlChild, CComHeapPtr& pszChild); + HRESULT GetPathName(LPCITEMIDLIST pidlChild, CComHeapPtr& pszPath); + // *** IEnumString methods *** - virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched); - virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt); - virtual HRESULT STDMETHODCALLTYPE Reset(); - virtual HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppenum); + STDMETHODIMP Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) override; + STDMETHODIMP Skip(ULONG celt) override; + STDMETHODIMP Reset() override; + STDMETHODIMP Clone(IEnumString **ppenum) override; // *** IACList methods *** - virtual HRESULT STDMETHODCALLTYPE Expand(LPCOLESTR pszExpand); + STDMETHODIMP Expand(LPCOLESTR pszExpand) override; // *** IACList2 methods *** - virtual HRESULT STDMETHODCALLTYPE SetOptions(DWORD dwFlag); - virtual HRESULT STDMETHODCALLTYPE GetOptions(DWORD* pdwFlag); + STDMETHODIMP SetOptions(DWORD dwFlag) override; + STDMETHODIMP GetOptions(DWORD* pdwFlag) override; + + // FIXME: These virtual keywords below should be removed. // *** IShellService methods *** - virtual HRESULT STDMETHODCALLTYPE SetOwner(IUnknown *); + virtual STDMETHODIMP SetOwner(IUnknown *punkOwner) override; // *** IPersist methods *** - virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID *pClassID); + virtual STDMETHODIMP GetClassID(CLSID *pClassID) override; // *** IPersistFolder methods *** - virtual HRESULT STDMETHODCALLTYPE Initialize(PCIDLIST_ABSOLUTE pidl); + virtual STDMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidl) override; + + // *** ICurrentWorkingDirectory methods *** + STDMETHODIMP GetDirectory(LPWSTR pwzPath, DWORD cchSize) override; + STDMETHODIMP SetDirectory(LPCWSTR pwzPath) override; public: - DECLARE_REGISTRY_RESOURCEID(IDR_ACLISTISF) DECLARE_NOT_AGGREGATABLE(CACListISF) @@ -72,5 +101,6 @@ public: // Windows doesn't return this //COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist) COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder) + COM_INTERFACE_ENTRY_IID(IID_ICurrentWorkingDirectory, ICurrentWorkingDirectory) END_COM_MAP() };