Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

672 lines
18 KiB

  1. /* Copyright 1996 Microsoft */
  2. #include <priv.h>
  3. #include "sccls.h"
  4. #include "aclisf.h"
  5. #include "shellurl.h"
  6. #define AC_GENERAL TF_GENERAL + TF_AUTOCOMPLETE
  7. //
  8. // CACLIShellFolder -- An AutoComplete List COM object that
  9. // opens an IShellFolder for enumeration.
  10. //
  11. /* IUnknown methods */
  12. HRESULT CACLIShellFolder::QueryInterface(REFIID riid, void **ppvObj)
  13. {
  14. static const QITAB qit[] = {
  15. QITABENT(CACLIShellFolder, IEnumString),
  16. QITABENT(CACLIShellFolder, IACList),
  17. QITABENT(CACLIShellFolder, IACList2),
  18. QITABENT(CACLIShellFolder, IShellService),
  19. QITABENT(CACLIShellFolder, ICurrentWorkingDirectory),
  20. QITABENT(CACLIShellFolder, IPersistFolder),
  21. { 0 },
  22. };
  23. return QISearch(this, qit, riid, ppvObj);
  24. }
  25. ULONG CACLIShellFolder::AddRef(void)
  26. {
  27. _cRef++;
  28. return _cRef;
  29. }
  30. ULONG CACLIShellFolder::Release(void)
  31. {
  32. ASSERT(_cRef > 0);
  33. _cRef--;
  34. if (_cRef > 0)
  35. {
  36. return _cRef;
  37. }
  38. delete this;
  39. return 0;
  40. }
  41. /* ICurrentWorkingDirectory methods */
  42. HRESULT CACLIShellFolder::SetDirectory(LPCWSTR pwzPath)
  43. {
  44. HRESULT hr;
  45. LPITEMIDLIST pidl = NULL;
  46. hr = IECreateFromPathW(pwzPath, &pidl);
  47. if (SUCCEEDED(hr))
  48. {
  49. hr = Initialize(pidl);
  50. ILFree(pidl);
  51. }
  52. return hr;
  53. }
  54. /* IPersistFolder methods */
  55. HRESULT CACLIShellFolder::Initialize(LPCITEMIDLIST pidl)
  56. {
  57. HRESULT hr = S_OK;
  58. hr = _Init();
  59. if (FAILED(hr))
  60. return hr;
  61. if (pidl)
  62. {
  63. #ifdef DEBUG
  64. TCHAR szPath[MAX_URL_STRING];
  65. hr = IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, SIZECHARS(szPath), NULL);
  66. TraceMsg(AC_GENERAL, "ACListISF::Initialize(%s), SetCurrentWorking Directory happening", szPath);
  67. #endif // DEBUG
  68. hr = _pshuLocation->SetCurrentWorkingDir(pidl);
  69. SetDefaultShellPath(_pshuLocation);
  70. }
  71. Pidl_Set(&_pidlCWD, pidl);
  72. return hr;
  73. }
  74. HRESULT CACLIShellFolder::GetClassID(CLSID *pclsid)
  75. {
  76. *pclsid = CLSID_ACListISF;
  77. return S_OK;
  78. }
  79. /* IEnumString methods */
  80. HRESULT CACLIShellFolder::Reset(void)
  81. {
  82. HRESULT hr;
  83. LPITEMIDLIST pidl = NULL;
  84. TraceMsg(AC_GENERAL, "ACListISF::Reset()");
  85. _fExpand = FALSE;
  86. _nPathIndex = 0;
  87. hr = _Init();
  88. // See if we should show hidden files
  89. SHELLSTATE ss;
  90. ss.fShowAllObjects = FALSE;
  91. SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS /*| SSF_SHOWSYSFILES*/, FALSE);
  92. _fShowHidden = BOOLIFY(ss.fShowAllObjects);
  93. // _fShowSysFiles = BOOLIFY(ss.fShowSysFiles);
  94. if (SUCCEEDED(hr) && IsFlagSet(_dwOptions, ACLO_CURRENTDIR))
  95. {
  96. // Set the Browser's Current Directory.
  97. if (_pbs)
  98. {
  99. _pbs->GetPidl(&pidl);
  100. if (pidl)
  101. Initialize(pidl);
  102. }
  103. hr = _SetLocation(pidl);
  104. if (FAILED(hr))
  105. hr = S_FALSE; // If we failed, keep going, we will just end up now doing anything.
  106. ILFree(pidl);
  107. }
  108. return hr;
  109. }
  110. // If this is an FTP URL, skip it if:
  111. // 1) It's absolute (has a FTP scheme), and
  112. // 2) it contains a '/' after the server name.
  113. BOOL CACLIShellFolder::_SkipForPerf(LPCWSTR pwzExpand)
  114. {
  115. BOOL fSkip = FALSE;
  116. if ((URL_SCHEME_FTP == GetUrlScheme(pwzExpand)))
  117. {
  118. // If it's FTP, we want to prevent from hitting the server until
  119. // after the user has finished AutoCompleting the Server name.
  120. // Since we can't enum server names, the server names will need
  121. // to come from the MRU.
  122. if ((7 >= lstrlen(pwzExpand)) || // There's more than 7 chars "ftp://"
  123. (NULL == StrChr(&(pwzExpand[7]), TEXT('/')))) // There is a '/' after the server, "ftp://serv/"
  124. {
  125. fSkip = TRUE;
  126. }
  127. }
  128. return fSkip;
  129. }
  130. /* IACList methods */
  131. /****************************************************\
  132. FUNCTION: Expand
  133. DESCRIPTION:
  134. This function will attempt to use the pszExpand
  135. parameter to bind to a location in the Shell Name Space.
  136. If that succeeds, this AutoComplete List will then
  137. contain entries which are the display names in that ISF.
  138. \****************************************************/
  139. HRESULT CACLIShellFolder::Expand(LPCOLESTR pszExpand)
  140. {
  141. HRESULT hr = S_OK;
  142. LPITEMIDLIST pidl = NULL;
  143. DWORD dwParseFlags = SHURL_FLAGS_NOUI;
  144. TraceMsg(AC_GENERAL, "ACListISF::Expand(%ls)", pszExpand);
  145. _fExpand = FALSE;
  146. _nPathIndex = 0;
  147. hr = StringCchCopy( _szExpandStr, ARRAYSIZE(_szExpandStr), pszExpand);
  148. if (SUCCEEDED(hr))
  149. {
  150. if (_SkipForPerf(pszExpand)) // Do we want to skip this item for perf reasons?
  151. {
  152. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  153. }
  154. else
  155. {
  156. hr = _Init();
  157. }
  158. }
  159. if (FAILED(hr))
  160. return hr;
  161. // See if the string points to a location in the Shell Name Space
  162. hr = _pshuLocation->ParseFromOutsideSource(_szExpandStr, dwParseFlags);
  163. if (SUCCEEDED(hr))
  164. {
  165. // Yes it did, so now AutoComplete from that ISF
  166. hr = _pshuLocation->GetPidl(&pidl);
  167. // This may fail if it's something like "ftp:/" and not yet valid".
  168. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  169. TraceMsg(AC_GENERAL, "ACListISF::Expand() Pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  170. }
  171. // Set the ISF that we need to enumerate for AutoComplete.
  172. hr = _SetLocation(pidl);
  173. if (pidl)
  174. {
  175. ILFree(pidl);
  176. if (SUCCEEDED(hr))
  177. {
  178. _fExpand = TRUE;
  179. }
  180. }
  181. return hr;
  182. }
  183. /* IACList2 methods */
  184. //+-------------------------------------------------------------------------
  185. // Enables/disables various autocomplete features (see ACLO_* flags)
  186. //--------------------------------------------------------------------------
  187. HRESULT CACLIShellFolder::SetOptions(DWORD dwOptions)
  188. {
  189. _dwOptions = dwOptions;
  190. return S_OK;
  191. }
  192. //+-------------------------------------------------------------------------
  193. // Returns the current option settings
  194. //--------------------------------------------------------------------------
  195. HRESULT CACLIShellFolder::GetOptions(DWORD* pdwOptions)
  196. {
  197. HRESULT hr = E_INVALIDARG;
  198. if (pdwOptions)
  199. {
  200. *pdwOptions = _dwOptions;
  201. hr = S_OK;
  202. }
  203. return hr;
  204. }
  205. HRESULT CACLIShellFolder::_GetNextWrapper(LPWSTR pszName, DWORD cchSize)
  206. {
  207. HRESULT hr = S_OK;
  208. LPITEMIDLIST pidl = NULL;
  209. ULONG celtFetched = 0;
  210. // If this directory (ISF) doesn't contain any more items to enum,
  211. // then go on to the next directory (ISF) to enum.
  212. do
  213. {
  214. BOOL fFilter;
  215. do
  216. {
  217. fFilter = FALSE;
  218. hr = _peidl->Next(1, &pidl, &celtFetched);
  219. if (S_OK == hr)
  220. {
  221. hr = _PassesFilter(pidl, pszName, cchSize);
  222. if (FAILED(hr))
  223. {
  224. fFilter = TRUE;
  225. }
  226. ILFree(pidl);
  227. }
  228. }
  229. while (fFilter);
  230. }
  231. while ((S_OK != hr) && (S_OK == _TryNextPath()));
  232. return hr;
  233. }
  234. HRESULT CACLIShellFolder::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
  235. {
  236. HRESULT hr = S_OK;
  237. LPITEMIDLIST pidl;
  238. ULONG celtFetched;
  239. BOOL fUsingCachePidl = FALSE;
  240. WCHAR szDisplayName[MAX_PATH];
  241. *pceltFetched = 0;
  242. if (!celt)
  243. return S_OK;
  244. // If there isn't a Current Working Directory, skip to another
  245. // Path to enum.
  246. if (!_peidl)
  247. hr = _TryNextPath();
  248. if ((!_peidl) || (!rgelt))
  249. return S_FALSE;
  250. // Get the next PIDL.
  251. if (_pidlInFolder)
  252. {
  253. // We have a cached, SHGDN_INFOLDER, so lets try that.
  254. pidl = _pidlInFolder;
  255. celtFetched = 1;
  256. _pidlInFolder = NULL;
  257. fUsingCachePidl = TRUE;
  258. hr = _GetPidlName(pidl, fUsingCachePidl, szDisplayName, ARRAYSIZE(szDisplayName));
  259. ILFree(pidl);
  260. AssertMsg((S_OK == hr), TEXT("CACLIShellFolder::Next() hr doesn't equal S_OK, so we need to call _GetNextWrapper() but we aren't. Please call BryanSt."));
  261. }
  262. else
  263. {
  264. hr = _GetNextWrapper(szDisplayName, ARRAYSIZE(szDisplayName));
  265. }
  266. // This is giving us entries (favorites without .url extension) that cannot be navigated to.
  267. // So I'm disabling this for IE5B2. (stevepro)
  268. //
  269. // else
  270. // Pidl_Set(&_pidlInFolder, pidl); // We will try (SHGDN_INFOLDER) next time.
  271. if (SUCCEEDED(hr))
  272. {
  273. LPOLESTR pwszPath;
  274. // Allocate a return buffer (caller will free it).
  275. DWORD cch = lstrlenW(szDisplayName) + 1;
  276. pwszPath = (LPOLESTR)CoTaskMemAlloc(cch * SIZEOF(WCHAR));
  277. if (pwszPath)
  278. {
  279. StringCchCopy(pwszPath, cch, szDisplayName);
  280. TraceMsg(AC_GENERAL, "ACListISF::Next() Str=%s, _nPathIndex=%d", pwszPath, _nPathIndex);
  281. if (SUCCEEDED(hr))
  282. {
  283. rgelt[0] = pwszPath;
  284. *pceltFetched = 1;
  285. }
  286. }
  287. else
  288. {
  289. hr = E_OUTOFMEMORY;
  290. }
  291. }
  292. return hr;
  293. }
  294. HRESULT CACLIShellFolder::_GetPidlName(LPCITEMIDLIST pidl, BOOL fUsingCachePidl, LPWSTR pszName, DWORD cchSize)
  295. {
  296. HRESULT hr = S_OK;
  297. WCHAR szName[MAX_PATH];
  298. // Get the display name of the PIDL.
  299. if (!fUsingCachePidl)
  300. {
  301. hr = DisplayNameOf(_psf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szName, ARRAYSIZE(szName));
  302. // some namespaces don't understand _FORADDRESSBAR -- default to IE4 behavior
  303. if (FAILED(hr))
  304. hr = DisplayNameOf(_psf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szName, ARRAYSIZE(szName));
  305. }
  306. if (fUsingCachePidl || FAILED(hr))
  307. {
  308. hr = DisplayNameOf(_psf, pidl, SHGDN_INFOLDER | SHGDN_FORADDRESSBAR, szName, ARRAYSIZE(szName));
  309. // some namespaces don't understand _FORADDRESSBAR -- default to IE4 behavior
  310. if (FAILED(hr))
  311. hr = DisplayNameOf(_psf, pidl, SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  312. }
  313. if (SUCCEEDED(hr))
  314. {
  315. pszName[0] = 0; // Init the out buffer.
  316. // First, prepend the _szExpandStr if necessary.
  317. // This is needed for sections that don't give
  318. // the entire path, like "My Computer" items
  319. // which is (3 == _nPathIndex)
  320. if (_fExpand && ((_nPathIndex == 0) /*|| (_nPathIndex == 3)*/))
  321. {
  322. DWORD cchExpand = lstrlen(_szExpandStr);
  323. // Make sure that for UNC paths the "\\share" is not already
  324. // prepended. NT5 returns the name in this final form.
  325. if ((StrCmpNI(szName, _szExpandStr, cchExpand) != 0) ||
  326. (szName[0] != L'\\') || (szName[1] != L'\\'))
  327. {
  328. hr = StringCchCopy(pszName, cchSize, _szExpandStr);
  329. }
  330. }
  331. if(SUCCEEDED(hr))
  332. {
  333. // Next, append the display name.
  334. hr = StringCchCat(pszName, cchSize, szName);
  335. TraceMsg(AC_GENERAL, "ACListISF::_GetPidlName() Str=%s, _nPathIndex=%d", szName, _nPathIndex);
  336. }
  337. }
  338. return hr;
  339. }
  340. HRESULT CACLIShellFolder::_PassesFilter(LPCITEMIDLIST pidl, LPWSTR pszName, DWORD cchSize)
  341. {
  342. HRESULT hr = S_OK;
  343. DWORD dwAttributes = (SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM);
  344. hr = _GetPidlName(pidl, FALSE, pszName, cchSize);
  345. if (SUCCEEDED(hr))
  346. {
  347. if (((ACLO_FILESYSONLY & _dwOptions) || (ACLO_FILESYSDIRS & _dwOptions)) &&
  348. SUCCEEDED(_psf->GetAttributesOf(1, (LPCITEMIDLIST *) &pidl, &dwAttributes)))
  349. {
  350. if (!(dwAttributes & (SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM)))
  351. {
  352. // We reject it because it's not in the file system.
  353. hr = E_FAIL; // Skip this item.
  354. TraceMsg(AC_GENERAL, "ACListISF::_PassesFilter() We are skipping\"%s\" because it doesn't match the filter", pszName);
  355. }
  356. else
  357. {
  358. if ((ACLO_FILESYSDIRS & _dwOptions) && !PathIsDirectory(pszName))
  359. {
  360. hr = E_FAIL; // Skip this item since it's not a directory
  361. }
  362. }
  363. }
  364. }
  365. return hr;
  366. }
  367. HRESULT CACLIShellFolder::_Init(void)
  368. {
  369. HRESULT hr = S_OK;
  370. if (!_pshuLocation)
  371. {
  372. _pshuLocation = new CShellUrl();
  373. if (!_pshuLocation)
  374. return E_OUTOFMEMORY;
  375. }
  376. return hr;
  377. }
  378. HRESULT CACLIShellFolder::_SetLocation(LPCITEMIDLIST pidl)
  379. {
  380. HRESULT hr;
  381. // Free old location
  382. ATOMICRELEASE(_peidl);
  383. ATOMICRELEASE(_psf);
  384. // Set to new location (Valid if NULL)
  385. Pidl_Set(&_pidl, pidl);
  386. if (_pidl)
  387. {
  388. hr = IEBindToObject(_pidl, &_psf);
  389. if (SUCCEEDED(hr))
  390. {
  391. DWORD grfFlags = (_fShowHidden ? SHCONTF_INCLUDEHIDDEN : 0) |
  392. SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  393. hr = IShellFolder_EnumObjects(_psf, NULL, grfFlags, &_peidl);
  394. if (hr != S_OK)
  395. {
  396. hr = E_FAIL; // S_FALSE -> empty enumerator
  397. }
  398. }
  399. }
  400. else
  401. hr = E_OUTOFMEMORY;
  402. if (FAILED(hr))
  403. {
  404. // Clear if we could not get all the info
  405. ATOMICRELEASE(_peidl);
  406. ATOMICRELEASE(_psf);
  407. Pidl_Set(&_pidl, NULL);
  408. }
  409. //
  410. // NOTE: This is necessary because this memory is alloced in a ACBG thread, but not
  411. // freed until the next call to Reset() or the destructor, which will
  412. // happen in the main thread or another ACBG thread.
  413. //
  414. return hr;
  415. }
  416. HRESULT CACLIShellFolder::_TryNextPath(void)
  417. {
  418. HRESULT hr = S_FALSE;
  419. if (0 == _nPathIndex)
  420. {
  421. _nPathIndex = 1;
  422. if (_pidlCWD && IsFlagSet(_dwOptions, ACLO_CURRENTDIR))
  423. {
  424. hr = _SetLocation(_pidlCWD);
  425. if (SUCCEEDED(hr))
  426. {
  427. goto done;
  428. }
  429. }
  430. }
  431. if (1 == _nPathIndex)
  432. {
  433. _nPathIndex = 2;
  434. if(IsFlagSet(_dwOptions, ACLO_DESKTOP))
  435. {
  436. // we used to autocomplete g_pidlRoot in the rooted explorer
  437. // case, but this was a little weird. if we want to add this,
  438. // we should add ACLO_ROOTED or something.
  439. // use the desktop...
  440. hr = _SetLocation(&s_idlNULL);
  441. if (SUCCEEDED(hr))
  442. {
  443. goto done;
  444. }
  445. }
  446. }
  447. if (2 == _nPathIndex)
  448. {
  449. _nPathIndex = 3;
  450. if (IsFlagSet(_dwOptions, ACLO_MYCOMPUTER))
  451. {
  452. LPITEMIDLIST pidlMyComputer;
  453. if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlMyComputer)))
  454. {
  455. hr = _SetLocation(pidlMyComputer);
  456. ILFree(pidlMyComputer);
  457. if (SUCCEEDED(hr))
  458. {
  459. goto done;
  460. }
  461. }
  462. }
  463. }
  464. // Also search favorites
  465. if (3 == _nPathIndex)
  466. {
  467. _nPathIndex = 4;
  468. if (IsFlagSet(_dwOptions, ACLO_FAVORITES))
  469. {
  470. LPITEMIDLIST pidlFavorites;
  471. if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidlFavorites)))
  472. {
  473. hr = _SetLocation(pidlFavorites);
  474. ILFree(pidlFavorites);
  475. if (SUCCEEDED(hr))
  476. {
  477. goto done;
  478. }
  479. }
  480. }
  481. }
  482. if (FAILED(hr))
  483. hr = S_FALSE; // This is how we want our errors returned.
  484. done:
  485. return hr;
  486. }
  487. //================================
  488. // *** IShellService Interface ***
  489. /****************************************************\
  490. FUNCTION: SetOwner
  491. DESCRIPTION:
  492. Update the connection to the Browser window so
  493. we can always get the PIDL of the current location.
  494. \****************************************************/
  495. HRESULT CACLIShellFolder::SetOwner(IUnknown* punkOwner)
  496. {
  497. HRESULT hr = S_OK;
  498. IBrowserService * pbs = NULL;
  499. ATOMICRELEASE(_pbs);
  500. if (punkOwner)
  501. hr = punkOwner->QueryInterface(IID_IBrowserService, (LPVOID *) &pbs);
  502. if (EVAL(SUCCEEDED(hr)))
  503. _pbs = pbs;
  504. return S_OK;
  505. }
  506. /* Constructor / Destructor / CreateInstance */
  507. CACLIShellFolder::CACLIShellFolder()
  508. {
  509. DllAddRef();
  510. ASSERT(!_peidl);
  511. ASSERT(!_psf);
  512. ASSERT(!_pbs);
  513. ASSERT(!_pidl);
  514. ASSERT(!_pidlCWD);
  515. ASSERT(!_fExpand);
  516. ASSERT(!_pshuLocation);
  517. ASSERT(0==_szExpandStr[0]);
  518. _cRef = 1;
  519. // Default search paths
  520. _dwOptions = ACLO_CURRENTDIR | ACLO_MYCOMPUTER;
  521. }
  522. CACLIShellFolder::~CACLIShellFolder()
  523. {
  524. ATOMICRELEASE(_peidl);
  525. ATOMICRELEASE(_psf);
  526. ATOMICRELEASE(_pbs);
  527. Pidl_Set(&_pidl, NULL);
  528. Pidl_Set(&_pidlCWD, NULL);
  529. Pidl_Set(&_pidlInFolder, NULL);
  530. if (_pshuLocation)
  531. delete _pshuLocation;
  532. DllRelease();
  533. }
  534. HRESULT CACLIShellFolder_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  535. {
  536. *ppunk = NULL;
  537. CACLIShellFolder *paclSF = new CACLIShellFolder();
  538. if (paclSF)
  539. {
  540. *ppunk = SAFECAST(paclSF, IEnumString *);
  541. return S_OK;
  542. }
  543. return E_OUTOFMEMORY;
  544. }