1 /* 2 * Shell AutoComplete list 3 * 4 * Copyright 2015 Thomas Faber 5 * Copyright 2020-2021 Katayama Hirofumi MZ 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "precomp.h" 23 24 CACListISF::CACListISF() 25 : m_dwOptions(ACLO_CURRENTDIR | ACLO_MYCOMPUTER) 26 , m_iNextLocation(LT_DIRECTORY) 27 , m_fShowHidden(FALSE) 28 { 29 } 30 31 CACListISF::~CACListISF() 32 { 33 } 34 35 HRESULT CACListISF::NextLocation() 36 { 37 TRACE("(%p)\n", this); 38 HRESULT hr; 39 switch (m_iNextLocation) 40 { 41 case LT_DIRECTORY: 42 m_iNextLocation = LT_DESKTOP; 43 if (!ILIsEmpty(m_pidlCurDir) && (m_dwOptions & ACLO_CURRENTDIR)) 44 { 45 CComHeapPtr<ITEMIDLIST> pidl(ILClone(m_pidlCurDir)); 46 hr = SetLocation(pidl.Detach()); 47 if (SUCCEEDED(hr)) 48 { 49 TRACE("LT_DIRECTORY\n"); 50 return hr; 51 } 52 } 53 // FALL THROUGH 54 case LT_DESKTOP: 55 m_iNextLocation = LT_MYCOMPUTER; 56 if (m_dwOptions & ACLO_DESKTOP) 57 { 58 CComHeapPtr<ITEMIDLIST> pidl; 59 hr = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl); 60 if (FAILED_UNEXPECTEDLY(hr)) 61 return S_FALSE; 62 hr = SetLocation(pidl.Detach()); 63 if (SUCCEEDED(hr)) 64 { 65 TRACE("LT_DESKTOP\n"); 66 return hr; 67 } 68 } 69 // FALL THROUGH 70 case LT_MYCOMPUTER: 71 m_iNextLocation = LT_FAVORITES; 72 if (m_dwOptions & ACLO_MYCOMPUTER) 73 { 74 CComHeapPtr<ITEMIDLIST> pidl; 75 hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidl); 76 if (FAILED_UNEXPECTEDLY(hr)) 77 return S_FALSE; 78 hr = SetLocation(pidl.Detach()); 79 if (SUCCEEDED(hr)) 80 { 81 TRACE("LT_MYCOMPUTER\n"); 82 return hr; 83 } 84 } 85 // FALL THROUGH 86 case LT_FAVORITES: 87 m_iNextLocation = LT_MAX; 88 if (m_dwOptions & ACLO_FAVORITES) 89 { 90 CComHeapPtr<ITEMIDLIST> pidl; 91 hr = SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl); 92 if (FAILED_UNEXPECTEDLY(hr)) 93 return S_FALSE; 94 hr = SetLocation(pidl.Detach()); 95 if (SUCCEEDED(hr)) 96 { 97 TRACE("LT_FAVORITES\n"); 98 return hr; 99 } 100 } 101 // FALL THROUGH 102 case LT_MAX: 103 default: 104 TRACE("LT_MAX\n"); 105 return S_FALSE; 106 } 107 } 108 109 HRESULT CACListISF::SetLocation(LPITEMIDLIST pidl) 110 { 111 TRACE("(%p, %p)\n", this, pidl); 112 113 m_pEnumIDList.Release(); 114 m_pShellFolder.Release(); 115 m_pidlLocation.Free(); 116 117 if (!pidl) 118 return E_FAIL; 119 120 m_pidlLocation.Attach(pidl); 121 122 CComPtr<IShellFolder> pFolder; 123 HRESULT hr = SHGetDesktopFolder(&pFolder); 124 if (FAILED_UNEXPECTEDLY(hr)) 125 return hr; 126 127 if (!ILIsEmpty(pidl)) 128 { 129 hr = pFolder->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &m_pShellFolder)); 130 if (FAILED_UNEXPECTEDLY(hr)) 131 return hr; 132 } 133 else 134 { 135 m_pShellFolder.Attach(pFolder.Detach()); 136 } 137 138 SHCONTF Flags = SHCONTF_FOLDERS | SHCONTF_INIT_ON_FIRST_NEXT; 139 if (m_fShowHidden) 140 Flags |= SHCONTF_INCLUDEHIDDEN; 141 if (!(m_dwOptions & ACLO_FILESYSDIRS)) 142 Flags |= SHCONTF_NONFOLDERS; 143 144 hr = m_pShellFolder->EnumObjects(NULL, Flags, &m_pEnumIDList); 145 if (hr != S_OK) 146 { 147 ERR("EnumObjects failed: 0x%lX\n", hr); 148 hr = E_FAIL; 149 } 150 return hr; 151 } 152 153 HRESULT CACListISF::GetDisplayName(LPCITEMIDLIST pidlChild, CComHeapPtr<WCHAR>& pszChild) 154 { 155 TRACE("(%p, %p)\n", this, pidlChild); 156 pszChild.Free(); 157 158 STRRET StrRet; 159 DWORD dwFlags = SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR; 160 HRESULT hr = m_pShellFolder->GetDisplayNameOf(pidlChild, dwFlags, &StrRet); 161 if (FAILED(hr)) 162 { 163 dwFlags = SHGDN_INFOLDER | SHGDN_FORPARSING; 164 hr = m_pShellFolder->GetDisplayNameOf(pidlChild, dwFlags, &StrRet); 165 if (FAILED_UNEXPECTEDLY(hr)) 166 return hr; 167 } 168 169 hr = StrRetToStrW(&StrRet, NULL, &pszChild); 170 if (FAILED_UNEXPECTEDLY(hr)) 171 return hr; 172 173 TRACE("pszChild: '%S'\n", static_cast<LPCWSTR>(pszChild)); 174 return hr; 175 } 176 177 HRESULT 178 CACListISF::GetPaths(LPCITEMIDLIST pidlChild, CComHeapPtr<WCHAR>& pszRaw, 179 CComHeapPtr<WCHAR>& pszExpanded) 180 { 181 TRACE("(%p, %p)\n", this, pidlChild); 182 183 CComHeapPtr<WCHAR> pszChild; 184 HRESULT hr = GetDisplayName(pidlChild, pszChild); 185 if (FAILED_UNEXPECTEDLY(hr)) 186 return hr; 187 188 CStringW szRawPath, szExpanded; 189 if (m_szRawPath.GetLength() && m_iNextLocation == LT_DIRECTORY) 190 { 191 INT cchExpand = m_szRawPath.GetLength(); 192 if (StrCmpNIW(pszChild, m_szRawPath, cchExpand) != 0 || 193 pszChild[0] != L'\\' || pszChild[1] != L'\\') 194 { 195 szRawPath = m_szRawPath; 196 szExpanded = m_szExpanded; 197 } 198 } 199 szRawPath += pszChild; 200 szExpanded += pszChild; 201 202 SHStrDupW(szRawPath, &pszRaw); 203 SHStrDupW(szExpanded, &pszExpanded); 204 TRACE("pszRaw: '%S'\n", static_cast<LPCWSTR>(pszRaw)); 205 TRACE("pszExpanded: '%S'\n", static_cast<LPCWSTR>(pszExpanded)); 206 return S_OK; 207 } 208 209 // *** IEnumString methods *** 210 STDMETHODIMP CACListISF::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) 211 { 212 TRACE("(%p, %d, %p, %p)\n", this, celt, rgelt, pceltFetched); 213 214 if (celt == 0) 215 return S_OK; 216 if (!rgelt) 217 return S_FALSE; 218 219 *rgelt = NULL; 220 if (pceltFetched) 221 *pceltFetched = 0; 222 223 if (!m_pEnumIDList) 224 { 225 NextLocation(); 226 if (!m_pEnumIDList) 227 return S_FALSE; 228 } 229 230 HRESULT hr; 231 CComHeapPtr<ITEMIDLIST> pidlChild; 232 CComHeapPtr<WCHAR> pszRawPath, pszExpanded; 233 234 do 235 { 236 for (;;) 237 { 238 pidlChild.Free(); 239 hr = m_pEnumIDList->Next(1, &pidlChild, NULL); 240 if (hr != S_OK) 241 break; 242 243 pszRawPath.Free(); 244 pszExpanded.Free(); 245 GetPaths(pidlChild, pszRawPath, pszExpanded); 246 if (!pszRawPath || !pszExpanded) 247 continue; 248 249 if ((m_dwOptions & ACLO_FILESYSDIRS) && !PathIsDirectoryW(pszExpanded)) 250 continue; 251 else if ((m_dwOptions & ACLO_FILESYSONLY) && !PathFileExistsW(pszExpanded)) 252 continue; 253 254 hr = S_OK; 255 break; 256 } 257 } while (hr == S_FALSE && NextLocation() == S_OK); 258 259 if (hr == S_OK) 260 { 261 *rgelt = pszRawPath.Detach(); 262 if (pceltFetched) 263 *pceltFetched = 1; 264 } 265 else 266 { 267 hr = S_FALSE; 268 } 269 270 TRACE("*rgelt: %S\n", *rgelt); 271 return hr; 272 } 273 274 STDMETHODIMP CACListISF::Reset() 275 { 276 TRACE("(%p)\n", this); 277 278 m_iNextLocation = LT_DIRECTORY; 279 m_szRawPath = L""; 280 281 SHELLSTATE ss = { 0 }; 282 SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE); 283 m_fShowHidden = ss.fShowAllObjects; 284 285 if (m_dwOptions & ACLO_CURRENTDIR) 286 { 287 CComHeapPtr<ITEMIDLIST> pidl; 288 if (m_pBrowserService) 289 { 290 m_pBrowserService->GetPidl(&pidl); 291 if (pidl) 292 Initialize(pidl); 293 } 294 HRESULT hr = SetLocation(pidl.Detach()); 295 if (FAILED_UNEXPECTEDLY(hr)) 296 return S_FALSE; 297 } 298 return S_OK; 299 } 300 301 STDMETHODIMP CACListISF::Skip(ULONG celt) 302 { 303 TRACE("(%p, %d)\n", this, celt); 304 return E_NOTIMPL; 305 } 306 307 STDMETHODIMP CACListISF::Clone(IEnumString **ppOut) 308 { 309 TRACE("(%p, %p)\n", this, ppOut); 310 *ppOut = NULL; 311 return E_NOTIMPL; 312 } 313 314 // *** IACList methods *** 315 STDMETHODIMP CACListISF::Expand(LPCOLESTR pszExpand) 316 { 317 TRACE("(%p, %ls)\n", this, pszExpand); 318 319 m_szRawPath = pszExpand; 320 m_iNextLocation = LT_DIRECTORY; 321 322 // skip left space 323 while (*pszExpand == L' ') 324 ++pszExpand; 325 326 // expand environment variables (%WINDIR% etc.) 327 WCHAR szExpanded[MAX_PATH], szPath1[MAX_PATH], szPath2[MAX_PATH]; 328 ExpandEnvironmentStringsW(pszExpand, szExpanded, _countof(szExpanded)); 329 pszExpand = szExpanded; 330 331 // get full path 332 if (szExpanded[0] && szExpanded[1] == L':' && szExpanded[2] == 0) 333 { 334 // 'C:' --> 'C:\' 335 szExpanded[2] = L'\\'; 336 szExpanded[3] = 0; 337 } 338 else 339 { 340 if (PathIsRelativeW(pszExpand) && 341 SHGetPathFromIDListW(m_pidlCurDir, szPath1) && 342 PathCombineW(szPath2, szPath1, pszExpand)) 343 { 344 pszExpand = szPath2; 345 } 346 GetFullPathNameW(pszExpand, _countof(szPath1), szPath1, NULL); 347 pszExpand = szPath1; 348 } 349 350 CComHeapPtr<ITEMIDLIST> pidl; 351 m_szExpanded = pszExpand; 352 HRESULT hr = SHParseDisplayName(m_szExpanded, NULL, &pidl, NULL, NULL); 353 if (SUCCEEDED(hr)) 354 { 355 hr = SetLocation(pidl.Detach()); 356 if (FAILED_UNEXPECTEDLY(hr)) 357 { 358 m_szRawPath = L""; 359 m_szExpanded = L""; 360 } 361 } 362 return hr; 363 } 364 365 // *** IACList2 methods *** 366 STDMETHODIMP CACListISF::SetOptions(DWORD dwFlag) 367 { 368 TRACE("(%p, %lu)\n", this, dwFlag); 369 m_dwOptions = dwFlag; 370 return S_OK; 371 } 372 373 STDMETHODIMP CACListISF::GetOptions(DWORD* pdwFlag) 374 { 375 TRACE("(%p, %p)\n", this, pdwFlag); 376 if (pdwFlag) 377 { 378 *pdwFlag = m_dwOptions; 379 return S_OK; 380 } 381 return E_INVALIDARG; 382 } 383 384 // *** IShellService methods *** 385 STDMETHODIMP CACListISF::SetOwner(IUnknown *punkOwner) 386 { 387 TRACE("(%p, %p)\n", this, punkOwner); 388 m_pBrowserService.Release(); 389 punkOwner->QueryInterface(IID_PPV_ARG(IBrowserService, &m_pBrowserService)); 390 return S_OK; 391 } 392 393 // *** IPersist methods *** 394 STDMETHODIMP CACListISF::GetClassID(CLSID *pClassID) 395 { 396 TRACE("(%p, %p)\n", this, pClassID); 397 if (pClassID == NULL) 398 return E_POINTER; 399 *pClassID = CLSID_ACListISF; 400 return S_OK; 401 } 402 403 // *** IPersistFolder methods *** 404 STDMETHODIMP CACListISF::Initialize(PCIDLIST_ABSOLUTE pidl) 405 { 406 TRACE("(%p, %p)\n", this, pidl); 407 m_pidlCurDir.Free(); 408 if (!pidl) 409 return S_OK; 410 411 LPITEMIDLIST pidlClone = ILClone(pidl); 412 if (!pidlClone) 413 { 414 ERR("Out of memory\n"); 415 return E_OUTOFMEMORY; 416 } 417 m_pidlCurDir.Attach(pidlClone); 418 return S_OK; 419 } 420 421 // *** ICurrentWorkingDirectory methods *** 422 STDMETHODIMP CACListISF::GetDirectory(LPWSTR pwzPath, DWORD cchSize) 423 { 424 TRACE("(%p, %p, %ld)\n", this, pwzPath, cchSize); 425 return E_NOTIMPL; 426 } 427 428 STDMETHODIMP CACListISF::SetDirectory(LPCWSTR pwzPath) 429 { 430 TRACE("(%p, %ls, %ld)\n", this, pwzPath); 431 LPITEMIDLIST pidl = ILCreateFromPathW(pwzPath); 432 if (!pidl) 433 { 434 ERR("Out of memory\n"); 435 return E_OUTOFMEMORY; 436 } 437 m_pidlCurDir.Attach(pidl); 438 return S_OK; 439 } 440