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 DWORD attrs = SFGAO_FOLDER | SFGAO_FILESYSTEM; 250 LPCITEMIDLIST pidlRef = pidlChild; 251 hr = m_pShellFolder->GetAttributesOf(1, &pidlRef, &attrs); 252 if (FAILED_UNEXPECTEDLY(hr)) 253 continue; 254 255 if ((m_dwOptions & ACLO_FILESYSDIRS) && !(attrs & SFGAO_FOLDER)) 256 continue; 257 if ((m_dwOptions & (ACLO_FILESYSONLY | ACLO_FILESYSDIRS)) && !(attrs & SFGAO_FILESYSTEM)) 258 continue; 259 260 hr = S_OK; 261 break; 262 } 263 } while (hr == S_FALSE && NextLocation() == S_OK); 264 265 if (hr == S_OK) 266 { 267 *rgelt = pszRawPath.Detach(); 268 if (pceltFetched) 269 *pceltFetched = 1; 270 } 271 else 272 { 273 hr = S_FALSE; 274 } 275 276 TRACE("*rgelt: %S\n", *rgelt); 277 return hr; 278 } 279 280 STDMETHODIMP CACListISF::Reset() 281 { 282 TRACE("(%p)\n", this); 283 284 m_iNextLocation = LT_DIRECTORY; 285 m_szRawPath = L""; 286 287 SHELLSTATE ss = { 0 }; 288 SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE); 289 m_fShowHidden = ss.fShowAllObjects; 290 291 if (m_dwOptions & ACLO_CURRENTDIR) 292 { 293 CComHeapPtr<ITEMIDLIST> pidl; 294 if (m_pBrowserService) 295 { 296 m_pBrowserService->GetPidl(&pidl); 297 if (pidl) 298 Initialize(pidl); 299 } 300 HRESULT hr = SetLocation(pidl.Detach()); 301 if (FAILED_UNEXPECTEDLY(hr)) 302 return S_FALSE; 303 } 304 return S_OK; 305 } 306 307 STDMETHODIMP CACListISF::Skip(ULONG celt) 308 { 309 TRACE("(%p, %d)\n", this, celt); 310 return E_NOTIMPL; 311 } 312 313 STDMETHODIMP CACListISF::Clone(IEnumString **ppOut) 314 { 315 TRACE("(%p, %p)\n", this, ppOut); 316 *ppOut = NULL; 317 return E_NOTIMPL; 318 } 319 320 // *** IACList methods *** 321 STDMETHODIMP CACListISF::Expand(LPCOLESTR pszExpand) 322 { 323 TRACE("(%p, %ls)\n", this, pszExpand); 324 325 m_szRawPath = pszExpand; 326 m_iNextLocation = LT_DIRECTORY; 327 328 // skip left space 329 while (*pszExpand == L' ') 330 ++pszExpand; 331 332 // expand environment variables (%WINDIR% etc.) 333 WCHAR szExpanded[MAX_PATH], szPath1[MAX_PATH], szPath2[MAX_PATH]; 334 ExpandEnvironmentStringsW(pszExpand, szExpanded, _countof(szExpanded)); 335 pszExpand = szExpanded; 336 337 // get full path 338 if (szExpanded[0] && szExpanded[1] == L':' && szExpanded[2] == 0) 339 { 340 // 'C:' --> 'C:\' 341 szExpanded[2] = L'\\'; 342 szExpanded[3] = 0; 343 } 344 else 345 { 346 if (PathIsRelativeW(pszExpand) && 347 SHGetPathFromIDListW(m_pidlCurDir, szPath1) && 348 PathCombineW(szPath2, szPath1, pszExpand) && 349 PathFileExistsW(szPath2)) 350 { 351 pszExpand = szPath2; 352 } 353 else if (PathFileExistsW(pszExpand)) 354 { 355 GetFullPathNameW(pszExpand, _countof(szPath1), szPath1, NULL); 356 pszExpand = szPath1; 357 } 358 } 359 360 CComHeapPtr<ITEMIDLIST> pidl; 361 m_szExpanded = pszExpand; 362 HRESULT hr = SHParseDisplayName(m_szExpanded, NULL, &pidl, NULL, NULL); 363 if (SUCCEEDED(hr)) 364 { 365 hr = SetLocation(pidl.Detach()); 366 if (FAILED_UNEXPECTEDLY(hr)) 367 { 368 m_szRawPath = L""; 369 m_szExpanded = L""; 370 } 371 } 372 return hr; 373 } 374 375 // *** IACList2 methods *** 376 STDMETHODIMP CACListISF::SetOptions(DWORD dwFlag) 377 { 378 TRACE("(%p, %lu)\n", this, dwFlag); 379 m_dwOptions = dwFlag; 380 return S_OK; 381 } 382 383 STDMETHODIMP CACListISF::GetOptions(DWORD* pdwFlag) 384 { 385 TRACE("(%p, %p)\n", this, pdwFlag); 386 if (pdwFlag) 387 { 388 *pdwFlag = m_dwOptions; 389 return S_OK; 390 } 391 return E_INVALIDARG; 392 } 393 394 // *** IShellService methods *** 395 STDMETHODIMP CACListISF::SetOwner(IUnknown *punkOwner) 396 { 397 TRACE("(%p, %p)\n", this, punkOwner); 398 m_pBrowserService.Release(); 399 punkOwner->QueryInterface(IID_PPV_ARG(IBrowserService, &m_pBrowserService)); 400 return S_OK; 401 } 402 403 // *** IPersist methods *** 404 STDMETHODIMP CACListISF::GetClassID(CLSID *pClassID) 405 { 406 TRACE("(%p, %p)\n", this, pClassID); 407 if (pClassID == NULL) 408 return E_POINTER; 409 *pClassID = CLSID_ACListISF; 410 return S_OK; 411 } 412 413 // *** IPersistFolder methods *** 414 STDMETHODIMP CACListISF::Initialize(PCIDLIST_ABSOLUTE pidl) 415 { 416 TRACE("(%p, %p)\n", this, pidl); 417 m_pidlCurDir.Free(); 418 if (!pidl) 419 return S_OK; 420 421 LPITEMIDLIST pidlClone = ILClone(pidl); 422 if (!pidlClone) 423 { 424 ERR("Out of memory\n"); 425 return E_OUTOFMEMORY; 426 } 427 m_pidlCurDir.Attach(pidlClone); 428 return S_OK; 429 } 430 431 // *** ICurrentWorkingDirectory methods *** 432 STDMETHODIMP CACListISF::GetDirectory(LPWSTR pwzPath, DWORD cchSize) 433 { 434 TRACE("(%p, %p, %ld)\n", this, pwzPath, cchSize); 435 return E_NOTIMPL; 436 } 437 438 STDMETHODIMP CACListISF::SetDirectory(LPCWSTR pwzPath) 439 { 440 TRACE("(%p, %ls, %ld)\n", this, pwzPath); 441 LPITEMIDLIST pidl = ILCreateFromPathW(pwzPath); 442 if (!pidl) 443 { 444 ERR("Out of memory\n"); 445 return E_OUTOFMEMORY; 446 } 447 m_pidlCurDir.Attach(pidl); 448 return S_OK; 449 } 450