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.

2588 lines
93 KiB

  1. /**************************************************************\
  2. FILE: shellurl.cpp
  3. DESCRIPTION:
  4. Implements CShellUrl.
  5. \**************************************************************/
  6. #include "priv.h"
  7. #include "resource.h"
  8. #include "util.h"
  9. #include "shellurl.h"
  10. #include "bandprxy.h"
  11. #include "mluisupp.h"
  12. // We need to reroute radio urls
  13. #define WZ_RADIO_PROTOCOL L"vnd.ms.radio:"
  14. //#define FEATURE_WILDCARD_SUPPORT
  15. #define CH_DOT TEXT('.')
  16. #define CH_SPACE TEXT(' ')
  17. #define CH_SEPARATOR TEXT('/')
  18. #define CH_FRAGMENT TEXT('#')
  19. #ifdef FEATURE_WILDCARD_SUPPORT
  20. #define CH_ASTRISK TEXT('*')
  21. #define CH_QUESTIONMARK TEXT('?')
  22. #endif // FEATURE_WILDCARD_SUPPORT
  23. #define SZ_SPACE TEXT(" ")
  24. #define SZ_SEPARATOR TEXT("/")
  25. #define SZ_UNC TEXT("\\\\")
  26. #define CH_FILESEPARATOR TEXT('\\')
  27. #define SZ_FILESEPARATOR TEXT("\\")
  28. #define CE_PATHGROW 1
  29. #define IS_SHELL_SEPARATOR(ch) ((CH_SEPARATOR == ch) || (CH_FILESEPARATOR == ch))
  30. // Private Functions
  31. BOOL _FixDriveDisplayName(LPCTSTR pszStart, LPCTSTR pszCurrent, LPCITEMIDLIST pidl);
  32. #define TF_CHECKITEM 0 // TF_BAND|TF_GENERAL
  33. /****************************************************\
  34. CShellUrl Constructor
  35. \****************************************************/
  36. CShellUrl::CShellUrl()
  37. {
  38. TraceMsg(TF_SHDLIFE, "ctor CShellUrl %x", this);
  39. // Don't want this object to be on the stack
  40. ASSERT(!m_pszURL);
  41. ASSERT(!m_pszArgs);
  42. ASSERT(!m_pstrRoot);
  43. ASSERT(!m_pidl);
  44. ASSERT(!m_pidlWorkingDir);
  45. ASSERT(!m_hdpaPath);
  46. ASSERT(!m_dwGenType);
  47. ASSERT(!m_hwnd);
  48. }
  49. /****************************************************\
  50. CShellUrl destructor
  51. \****************************************************/
  52. CShellUrl::~CShellUrl()
  53. {
  54. Reset();
  55. if (m_pstrRoot)
  56. {
  57. LocalFree(m_pstrRoot);
  58. m_pstrRoot = NULL;
  59. }
  60. if (m_pidlWorkingDir)
  61. ILFree(m_pidlWorkingDir);
  62. _DeletePidlDPA(m_hdpaPath);
  63. TraceMsg(TF_SHDLIFE, "dtor CShellUrl %x", this);
  64. }
  65. //*** CShellUrl::IUnknown::* {
  66. ULONG CShellUrl::AddRef()
  67. {
  68. return InterlockedIncrement(&_cRef);
  69. }
  70. ULONG CShellUrl::Release()
  71. {
  72. ASSERT( 0 != _cRef );
  73. ULONG cRef = InterlockedDecrement(&_cRef);
  74. if ( 0 == cRef )
  75. {
  76. delete this;
  77. }
  78. return cRef;
  79. }
  80. HRESULT CShellUrl::QueryInterface(REFIID riid, void **ppvObj)
  81. {
  82. static const QITAB qit[] = {
  83. QITABENT(CShellUrl, IAddressBarParser), // IID_IUserAssist
  84. { 0 },
  85. };
  86. return QISearch(this, qit, riid, ppvObj);
  87. }
  88. /****************************************************\
  89. FUNCTION: Clone
  90. PARAMETERS
  91. pShellUrl - This is the pointer to object that
  92. we want to clone
  93. DESCRIPTION:
  94. This function will make a deep copy of the passed
  95. object into 'this'
  96. \****************************************************/
  97. HRESULT CShellUrl::Clone(CShellUrl * pShellUrl)
  98. {
  99. HRESULT hr = S_OK;
  100. if (!pShellUrl)
  101. {
  102. hr = E_POINTER;
  103. goto exit;
  104. }
  105. Str_SetPtr(&m_pszURL, pShellUrl->m_pszURL);
  106. Str_SetPtr(&m_pszDisplayName, pShellUrl->m_pszDisplayName);
  107. Str_SetPtr(&m_pszArgs, pShellUrl->m_pszArgs);
  108. Str_SetPtr(&m_pstrRoot, pShellUrl->m_pstrRoot);
  109. m_dwGenType = pShellUrl->m_dwGenType;
  110. m_dwFlags = pShellUrl->m_dwFlags;
  111. m_hwnd = pShellUrl->m_hwnd;
  112. if (m_pidl)
  113. {
  114. ILFree(m_pidl);
  115. m_pidl = NULL;
  116. }
  117. if (pShellUrl->m_pidl)
  118. {
  119. m_pidl = ILClone(pShellUrl->m_pidl);
  120. if (!m_pidl)
  121. {
  122. hr = E_OUTOFMEMORY;
  123. goto exit;
  124. }
  125. }
  126. if (m_pidlWorkingDir)
  127. {
  128. ILFree(m_pidlWorkingDir);
  129. m_pidlWorkingDir = NULL;
  130. }
  131. if (pShellUrl->m_pidlWorkingDir)
  132. {
  133. m_pidlWorkingDir = ILClone(pShellUrl->m_pidlWorkingDir);
  134. if (!m_pidlWorkingDir)
  135. {
  136. hr = E_OUTOFMEMORY;
  137. goto exit;
  138. }
  139. }
  140. _DeletePidlDPA(m_hdpaPath);
  141. m_hdpaPath = NULL;
  142. if (pShellUrl->m_hdpaPath)
  143. {
  144. m_hdpaPath = DPA_Create(CE_PATHGROW);
  145. for(int nPathIndex = 0; nPathIndex < DPA_GetPtrCount(pShellUrl->m_hdpaPath); nPathIndex++)
  146. {
  147. LPITEMIDLIST pidlCurrPath = (LPITEMIDLIST) DPA_GetPtr(pShellUrl->m_hdpaPath, nPathIndex);
  148. LPITEMIDLIST pidlNew = ILClone(pidlCurrPath);
  149. if (pidlNew)
  150. DPA_AppendPtr(m_hdpaPath, pidlNew);
  151. else
  152. {
  153. hr = E_OUTOFMEMORY;
  154. goto exit;
  155. }
  156. }
  157. }
  158. exit:
  159. return hr;
  160. }
  161. /****************************************************\
  162. FUNCTION: Execute
  163. PARAMETERS
  164. pbp - This is the pointer to the interface
  165. which is needed to find a new topmost
  166. window or the associated browser window.
  167. pfDidShellExec (Out Optional) - This parameter
  168. can be NULL. If not NULL, it will be set
  169. to TRUE if this Execute() called ShellExec.
  170. This is needed by callers that wait for
  171. DISPID_NAVIGATECOMPLETE which will never happen
  172. in this case.
  173. DESCRIPTION:
  174. This command will determine if the current
  175. shell url needs to be shell executed or navigated
  176. to. If it needs to be navigated to, it will try
  177. to navigate to the PIDL, otherwise, it will navigate
  178. to the string version.
  179. \****************************************************/
  180. HRESULT CShellUrl::Execute(IBandProxy * pbp, BOOL * pfDidShellExec, DWORD dwExecFlags)
  181. {
  182. HRESULT hr = S_FALSE; // S_FALSE until navigation occurs.
  183. ULONG ulShellExecFMask = (IsFlagSet(dwExecFlags, SHURL_EXECFLAGS_SEPVDM)) ? SEE_MASK_FLAG_SEPVDM : 0;
  184. ASSERT(IS_VALID_CODE_PTR(pbp, IBandProxy *));
  185. ASSERT(!pfDidShellExec || IS_VALID_WRITE_PTR(pfDidShellExec, BOOL));
  186. if (!EVAL(pbp))
  187. return E_INVALIDARG;
  188. // Is the following true: 1) The caller wants other browsers to be able to handle the URLs,
  189. // 2) The ShellUrl is a Web Url, and 3) IE doesn't own HTML files.
  190. // If all of these are true, then we will just ShellExec() the URL String so the
  191. // default handler can handle it.
  192. // Also if the user wants us to browse in a new process and we are currently in the shell process,
  193. // we will launch IE to handle the url.
  194. if ((IsFlagSet(dwExecFlags, SHURL_EXECFLAGS_DONTFORCEIE) && IsWebUrl() && !IsIEDefaultBrowser())
  195. #ifdef BROWSENEWPROCESS_STRICT // "Nav in new process" has become "Launch in new process", so this is no longer needed
  196. || (IsWebUrl() && IsBrowseNewProcessAndExplorer())
  197. #endif
  198. )
  199. {
  200. hr = _UrlShellExec();
  201. ASSERT(S_OK == hr);
  202. }
  203. if ((S_OK != hr) && m_pidl && _CanUseAdvParsing())
  204. {
  205. // We will only Shell Exec it if:
  206. // 1. We want to Force IE (over other web browsers) and it's not browsable, even by non-default owners.
  207. // 2. It's not browsable by default owners.
  208. if (!ILIsBrowsable(m_pidl, NULL))
  209. {
  210. if (pfDidShellExec)
  211. *pfDidShellExec = TRUE;
  212. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  213. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: Execute() Going to _PidlShellExec(>%s<)", Dbg_PidlStr(m_pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  214. // If NULL == m_pidl, then the String will be used.
  215. hr = _PidlShellExec(m_pidl, ulShellExecFMask);
  216. }
  217. }
  218. if (S_OK != hr)
  219. {
  220. VARIANT vFlags = {0};
  221. vFlags.vt = VT_I4;
  222. vFlags.lVal = navAllowAutosearch;
  223. if (pfDidShellExec)
  224. *pfDidShellExec = FALSE;
  225. // We prefer pidls, thank you
  226. if (m_pidl)
  227. {
  228. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  229. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: Execute() Going to pbp->NavigateToPIDL(>%s<)", Dbg_PidlStr(m_pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  230. hr = pbp->NavigateToPIDL(m_pidl);
  231. }
  232. else
  233. {
  234. ASSERT(m_pszURL);
  235. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: Execute() Going to pbp->NavigateToURL(%s)", m_pszURL);
  236. #ifdef UNICODE
  237. hr = pbp->NavigateToURL(m_pszURL, &vFlags);
  238. #else
  239. WCHAR wszURL[MAX_URL_STRING];
  240. SHTCharToUnicode(m_pszURL, wszURL, ARRAYSIZE(wszURL));
  241. hr = pbp->NavigateToURL(wszURL, &vFlags);
  242. #endif
  243. }
  244. VariantClearLazy(&vFlags);
  245. }
  246. return hr;
  247. }
  248. /****************************************************\
  249. FUNCTION: _PidlShellExec
  250. PARAMETERS
  251. pidl - The Pidl to execute.
  252. DESCRIPTION:
  253. This function will call ShellExecEx() on the
  254. pidl specified. It will also fill in the Current
  255. Working Directory and Command Line Arguments if there
  256. are any.
  257. \****************************************************/
  258. HRESULT CShellUrl::_PidlShellExec(LPCITEMIDLIST pidl, ULONG ulShellExecFMask)
  259. {
  260. HRESULT hr = E_FAIL;
  261. SHELLEXECUTEINFO sei = {0};
  262. ASSERT(IS_VALID_PIDL(pidl));
  263. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  264. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _PidlShellExec() Going to execute pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  265. if (m_pidlWorkingDir)
  266. {
  267. // note, this must be MAX_URL_STRING since IEGetDisplayName can return a URL.
  268. WCHAR szCWD[MAX_URL_STRING];
  269. IEGetDisplayName(m_pidlWorkingDir, szCWD, SHGDN_FORPARSING);
  270. if (PathIsFilePath(szCWD))
  271. {
  272. sei.lpDirectory = szCWD;
  273. }
  274. }
  275. /**** TODO: Get the Current Working Directory of top most window
  276. if (!sei.lpDirectory || !sei.lpDirectory[0])
  277. {
  278. GetCurrentDirectory(SIZECHARS(szCurrWorkDir), szCurrWorkDir);
  279. sei.lpDirectory = szCurrWorkDir;
  280. }
  281. *****/
  282. sei.cbSize = sizeof(SHELLEXECUTEINFO);
  283. sei.lpIDList = (LPVOID) pidl;
  284. sei.lpParameters = m_pszArgs;
  285. sei.nShow = SW_SHOWNORMAL;
  286. sei.fMask = SEE_MASK_FLAG_NO_UI | (pidl ? SEE_MASK_INVOKEIDLIST : 0) | ulShellExecFMask;
  287. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _PidlShellExec() Cmd=>%s<, Args=>%s<, WorkDir=>%s<",
  288. GEN_DEBUGSTR(sei.lpFile), GEN_DEBUGSTR(sei.lpParameters), GEN_DEBUGSTR(sei.lpDirectory));
  289. if (ShellExecuteEx(&sei))
  290. hr = S_OK;
  291. else
  292. {
  293. #ifdef DEBUG
  294. DWORD dwGetLastError = GetLastError();
  295. TraceMsg(TF_ERROR, "ShellUrl: _PidlShellExec() ShellExecuteEx() failed for this item. Cmd=>%s<; dwGetLastError=%lx", GEN_DEBUGSTR(sei.lpParameters), dwGetLastError);
  296. #endif // DEBUG
  297. hr = E_FAIL;
  298. }
  299. return hr;
  300. }
  301. /****************************************************\
  302. FUNCTION: _UrlShellExec
  303. DESCRIPTION:
  304. This function will call ShellExecEx() on the
  305. URL. This is so other popular browsers can handle
  306. the URL if they own HTML and other web files.
  307. \****************************************************/
  308. HRESULT CShellUrl::_UrlShellExec(void)
  309. {
  310. HRESULT hr = E_FAIL;
  311. SHELLEXECUTEINFO sei = {0};
  312. ASSERT(m_pszURL);
  313. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _UrlShellExec() Going to execute URL=>%s<", m_pszURL);
  314. sei.cbSize = sizeof(sei);
  315. sei.lpFile = m_pszURL;
  316. sei.nShow = SW_SHOWNORMAL;
  317. sei.fMask = SEE_MASK_FLAG_NO_UI;
  318. if (m_pszURL && ShellExecuteEx(&sei))
  319. hr = S_OK;
  320. else
  321. hr = E_FAIL;
  322. return hr;
  323. }
  324. // The following function is identical to ParseURLFromOutsideSource except that it
  325. // enables autocorrect and sets pbWasCorrected to TRUE if the string was corrected
  326. BOOL CShellUrl::_ParseURLFromOutsideSource
  327. (
  328. LPCWSTR psz,
  329. LPWSTR pszOut,
  330. LPDWORD pcchOut,
  331. LPBOOL pbWasSearchURL, // if converted to a search string
  332. LPBOOL pbWasCorrected // if url was autocorrected
  333. )
  334. {
  335. // This is our hardest case. Users and outside applications might
  336. // type fully-escaped, partially-escaped, or unescaped URLs at us.
  337. // We need to handle all these correctly. This API will attempt to
  338. // determine what sort of URL we've got, and provide us a returned URL
  339. // that is guaranteed to be FULLY escaped.
  340. IURLQualify(psz, UQF_DEFAULT | UQF_AUTOCORRECT, pszOut, pbWasSearchURL, pbWasCorrected);
  341. //
  342. // Go ahead and canonicalize this appropriately
  343. //
  344. if (FAILED(UrlCanonicalize(pszOut, pszOut, pcchOut, URL_ESCAPE_SPACES_ONLY)))
  345. {
  346. //
  347. // we cant resize from here.
  348. // NOTE UrlCan will return E_POINTER if it is an insufficient buffer
  349. //
  350. return FALSE;
  351. }
  352. return TRUE;
  353. }
  354. #ifdef UNICODE
  355. HRESULT CShellUrl::ParseFromOutsideSource(LPCSTR pcszUrlIn, DWORD dwParseFlags, PBOOL pfWasCorrected)
  356. {
  357. WCHAR wzUrl[MAX_URL_STRING];
  358. SHAnsiToUnicode(pcszUrlIn, wzUrl, ARRAYSIZE(wzUrl));
  359. return ParseFromOutsideSource(wzUrl, dwParseFlags, pfWasCorrected);
  360. }
  361. #endif // UNICODE
  362. /****************************************************\
  363. FUNCTION: _TryQuickParse
  364. PARAMETERS
  365. pcszUrlIn - String to parse.
  366. dwParseFlags - Flags to modify parsing. (Defined in iedev\inc\shlobj.w)
  367. DESCRIPTION:
  368. We prefer to call g_psfDesktop->ParseDisplayName()
  369. and have it do the parse really quickly and without
  370. enumerating the name space. We need this for things
  371. that are parsed but not enumerated, which includes:
  372. a) hidden files, b) other.
  373. However, we need to not parse URLs if the caller
  374. doesn't want to accept them.
  375. \****************************************************/
  376. HRESULT CShellUrl::_TryQuickParse(LPCTSTR pszUrl, DWORD dwParseFlags)
  377. {
  378. HRESULT hr = E_FAIL; // E_FAIL means we don't know yet.
  379. int nScheme = GetUrlScheme(pszUrl);
  380. // Don't parse unknown schemes because we may
  381. // want to "AutoCorrect" them later.
  382. if (URL_SCHEME_UNKNOWN != nScheme)
  383. {
  384. if ((dwParseFlags & SHURL_FLAGS_NOWEB) &&
  385. (URL_SCHEME_INVALID != nScheme) &&
  386. (URL_SCHEME_UNKNOWN != nScheme) &&
  387. (URL_SCHEME_MK != nScheme) &&
  388. (URL_SCHEME_SHELL != nScheme) &&
  389. (URL_SCHEME_LOCAL != nScheme) &&
  390. (URL_SCHEME_RES != nScheme) &&
  391. (URL_SCHEME_ABOUT != nScheme))
  392. {
  393. // Skip parsing this because it's a web item, and
  394. // the caller wants to filter those out.
  395. }
  396. else
  397. {
  398. hr = IEParseDisplayNameWithBCW(CP_ACP, pszUrl, NULL, &m_pidl);
  399. }
  400. }
  401. return hr;
  402. }
  403. /****************************************************\
  404. FUNCTION: ParseFromOutsideSource
  405. PARAMETERS
  406. pcszUrlIn - String to parse.
  407. dwParseFlags - Flags to modify parsing. (Defined in iedev\inc\shlobj.w)
  408. pfWasCorrected - [out] if url was autocorrected (can be null)
  409. DESCRIPTION:
  410. Convert a string to a fully qualified shell url. Parsing
  411. falls into one of the following categories:
  412. 1. If the URL starts with "\\", check if it's a UNC Path.
  413. 2. If the URL starts something that appears to indicate that it starts
  414. from the root of the shell name space (Desktop), then check if it
  415. is an absolute ShellUrl.
  416. (Only do #3 and #4 if #2 was false)
  417. 3. Check if the string is relative to the Current Working Directory.
  418. 4. Check if the string is relative to one of the items in the
  419. "Shell Path".
  420. 5. Check if the string is in the system's AppPath or DOS Path.
  421. 6. Check if this is a URL to Navigate to. This call will pretty much
  422. always succeeded, because it will accept anything as an AutoSearch
  423. URL.
  424. \****************************************************/
  425. HRESULT CShellUrl::ParseFromOutsideSource(LPCTSTR pcszUrlIn, DWORD dwParseFlags, PBOOL pfWasCorrected, PBOOL pfWasCanceled)
  426. {
  427. HRESULT hr = E_FAIL; // E_FAIL means we don't know yet.
  428. TCHAR szUrlExpanded[MAX_URL_STRING];
  429. LPTSTR pszUrlInMod = (LPTSTR) szUrlExpanded; // For iteration only
  430. LPTSTR pszErrorURL = NULL;
  431. BOOL fPossibleWebUrl = FALSE;
  432. int nScheme;
  433. BOOL fDisable = SHRestricted(REST_NORUN);
  434. m_dwFlags = dwParseFlags;
  435. if (pfWasCorrected)
  436. *pfWasCorrected = FALSE;
  437. if (!pcszUrlIn[0])
  438. return E_FAIL;
  439. if (!StrCmpNIW(WZ_RADIO_PROTOCOL, pcszUrlIn, ARRAYSIZE(WZ_RADIO_PROTOCOL)-1))
  440. {
  441. // We need to reroute vnd.ms.radio: urls to the regular player, since we don't support the radio bar anymore.
  442. // (Media bar or the external player)
  443. StringCchCopy(szUrlExpanded, SIZECHARS(szUrlExpanded), pcszUrlIn+ARRAYSIZE(WZ_RADIO_PROTOCOL)-1);
  444. }
  445. else
  446. {
  447. SHExpandEnvironmentStrings(pcszUrlIn, szUrlExpanded, SIZECHARS(szUrlExpanded));
  448. }
  449. PathRemoveBlanks(pszUrlInMod);
  450. Reset(); // Empty info because we will fill it in if successful or leave empty if we fail.
  451. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: ParseFromOutsideSource() Begin. pszUrlInMod=%s", pszUrlInMod);
  452. // The display Name will be exactly what the user entered.
  453. Str_SetPtr(&m_pszDisplayName, pszUrlInMod);
  454. nScheme = GetUrlScheme(pszUrlInMod);
  455. if ((URL_SCHEME_FILE != nScheme) || !fDisable) // Don't parse FILE: URLs if Start->Run is disabled.
  456. {
  457. // For HTTP and FTP we can make a few minor corrections
  458. if (IsFlagSet(dwParseFlags, SHURL_FLAGS_AUTOCORRECT) &&
  459. (URL_SCHEME_HTTP == nScheme || URL_SCHEME_FTP == nScheme || URL_SCHEME_HTTPS == nScheme))
  460. {
  461. if (S_OK == UrlFixupW(szUrlExpanded, szUrlExpanded, ARRAYSIZE(szUrlExpanded)) &&
  462. pfWasCorrected)
  463. {
  464. *pfWasCorrected = TRUE;
  465. }
  466. }
  467. hr = _TryQuickParse(szUrlExpanded, dwParseFlags);
  468. if (FAILED(hr))
  469. {
  470. // Does this string refer to something in the shell namespace that is
  471. // not a standard URL AND can we do shell namespace parsing AND
  472. // can we use advanced parsing on it?
  473. if (((URL_SCHEME_UNKNOWN == nScheme) ||
  474. (URL_SCHEME_SHELL == nScheme) ||
  475. (URL_SCHEME_INVALID == nScheme)) &&
  476. !(SHURL_FLAGS_NOSNS & dwParseFlags) && _CanUseAdvParsing())
  477. {
  478. fPossibleWebUrl = TRUE;
  479. // Yes; is this URL absolute (e.g., "\foo" or "Desktop\foo")?
  480. if (IS_SHELL_SEPARATOR(pszUrlInMod[0]) ||
  481. (S_OK == StrCmpIWithRoot(pszUrlInMod, FALSE, &m_pstrRoot)))
  482. {
  483. // Yes
  484. // CASE #1.
  485. // It starts with "\\", so it's probably a UNC,
  486. // so _ParseUNC() will call _ParseRelativePidl() with the Network
  487. // Neighborhood PIDL as the relative location. This is needed
  488. // because commands like this "\\bryanst2\public\program.exe Arg1 Arg2"
  489. // that need to be shell executed.
  490. if (PathIsUNC(pszUrlInMod))
  491. {
  492. hr = _ParseUNC(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, FALSE);
  493. // If we got this far, don't pass off to Navigation if _ParseUNC() failed.
  494. fPossibleWebUrl = FALSE;
  495. }
  496. if (FAILED(hr))
  497. {
  498. if (IS_SHELL_SEPARATOR(pszUrlInMod[0]))
  499. {
  500. pszErrorURL = pszUrlInMod; // We want to keep the '\' for the error message.
  501. pszUrlInMod++; // Skip past '\'.
  502. }
  503. // See if we need to advance past a "Desktop".
  504. if (S_OK == StrCmpIWithRoot(pszUrlInMod, FALSE, &m_pstrRoot))
  505. {
  506. pszUrlInMod += lstrlen(m_pstrRoot);
  507. if (IS_SHELL_SEPARATOR(pszUrlInMod[0]))
  508. pszUrlInMod++;
  509. if (!pszUrlInMod[0])
  510. {
  511. // The only thing the user entered was [...]"desktop"[\]
  512. // so just clone the Root pidl.
  513. return _SetPidl(&s_idlNULL);
  514. }
  515. }
  516. // CASE #2. Passing NULL indicates that it should test relative
  517. // to the root.
  518. hr = _ParseRelativePidl(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, NULL, FALSE, FALSE);
  519. }
  520. }
  521. else
  522. {
  523. // No; it is relative
  524. int nPathCount = 0;
  525. int nPathIndex;
  526. if (m_hdpaPath)
  527. nPathCount = DPA_GetPtrCount(m_hdpaPath);
  528. // CASE #3. Parse relative to the Current Working Directory.
  529. // Only valid if this object's ::SetCurrentWorkingDir()
  530. // method was called.
  531. if (m_pidlWorkingDir)
  532. {
  533. hr = _ParseRelativePidl(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, m_pidlWorkingDir, TRUE, TRUE);
  534. #ifdef FEATURE_WILDCARD_SUPPORT
  535. if (FAILED(hr) && m_pidlWorkingDir &&
  536. !StrChr(pszUrlInMod, CH_SEPARATOR) && !StrChr(pszUrlInMod, CH_FILESEPARATOR))
  537. {
  538. LPTSTR pszWildCard = StrChr(pszUrlInMod, CH_ASTRISK);
  539. if (!pszWildCard)
  540. pszWildCard = StrChr(pszUrlInMod, CH_QUESTIONMARK);
  541. if (pszWildCard)
  542. {
  543. IOleWindow * pow;
  544. m_pidlWorkingDir
  545. }
  546. }
  547. #endif // FEATURE_WILDCARD_SUPPORT
  548. if (FAILED(hr))
  549. {
  550. //
  551. // Check if the place we are navigating to is the same as the current
  552. // working directory. If so then there is a good chance that the user just
  553. // pressed the enter key / go button in the addressbar and we should simply
  554. // refresh the current directory.
  555. //
  556. WCHAR szCurrentDir[MAX_URL_STRING];
  557. HRESULT hr2 = IEGetNameAndFlags(m_pidlWorkingDir, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szCurrentDir, ARRAYSIZE(szCurrentDir), NULL);
  558. if (FAILED(hr2))
  559. {
  560. // Sometimes SHGDN_FORPARSING fails and the addressbar then tries SHGDN_NORMAL
  561. hr2 = IEGetNameAndFlags(m_pidlWorkingDir, SHGDN_NORMAL | SHGDN_FORADDRESSBAR, szCurrentDir, ARRAYSIZE(szCurrentDir), NULL);
  562. }
  563. if (SUCCEEDED(hr2))
  564. {
  565. if (0 == StrCmpI(pszUrlInMod, szCurrentDir))
  566. {
  567. // It matches so stay in the current working directory
  568. _SetPidl(m_pidlWorkingDir);
  569. hr = S_OK;
  570. }
  571. }
  572. }
  573. }
  574. else
  575. {
  576. // TODO: Get the Current Working Directory of the top most window.
  577. // hr = _ParseRelativePidl(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, pshurlCWD, TRUE, TRUE);
  578. }
  579. // CASE #4. Parse relative to the entries in the "Shell Path".
  580. // Only valid if this object's ::AddPath() method was
  581. // called at least once.
  582. for (nPathIndex = 0; FAILED(hr) && nPathIndex < nPathCount; nPathIndex++)
  583. {
  584. LPITEMIDLIST pidlCurrPath = (LPITEMIDLIST) DPA_GetPtr(m_hdpaPath, nPathIndex);
  585. if (EVAL(pidlCurrPath))
  586. {
  587. ASSERT(IS_VALID_PIDL(pidlCurrPath));
  588. hr = _ParseRelativePidl(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, pidlCurrPath, FALSE, FALSE);
  589. }
  590. }
  591. // CASE #5. We need to see if the beginning of the string matches
  592. // the entry in the AppPaths or DOS Path
  593. if (FAILED(hr) && IsFlagClear(dwParseFlags, SHURL_FLAGS_NOPATHSEARCH))
  594. hr = _QualifyFromPath(pszUrlInMod, dwParseFlags);
  595. }
  596. }
  597. else
  598. {
  599. if (URL_SCHEME_FILE != nScheme)
  600. fPossibleWebUrl = TRUE;
  601. }
  602. }
  603. }
  604. if (FAILED(hr) && !fPossibleWebUrl && !fDisable)
  605. {
  606. // Did the caller want to suppress UI (Error Messages)
  607. if (IsFlagClear(dwParseFlags, SHURL_FLAGS_NOUI))
  608. {
  609. if(!(pfWasCanceled && *pfWasCanceled))
  610. {
  611. if (!pszErrorURL)
  612. pszErrorURL = pszUrlInMod;
  613. ASSERT(pszErrorURL);
  614. // We were able to parse part of it, but failed parsing the second or
  615. // later segment. This means we need to inform the user of their
  616. // misspelling. They can force AutoSearch with "go xxx" or "? xxx"
  617. // if they are trying to AutoSearch something that appears in their
  618. // Shell Name Space.
  619. MLShellMessageBox(m_hwnd, MAKEINTRESOURCE(IDS_SHURL_ERR_PARSE_FAILED),
  620. MAKEINTRESOURCE(IDS_SHURL_ERR_TITLE),
  621. (MB_OK | MB_ICONERROR), pszErrorURL);
  622. }
  623. }
  624. }
  625. else if (S_OK != hr)
  626. {
  627. if (!(dwParseFlags & SHURL_FLAGS_NOWEB))
  628. {
  629. TCHAR szQualifiedUrl[MAX_URL_STRING];
  630. DWORD cchSize = SIZECHARS(szQualifiedUrl);
  631. SHExpandEnvironmentStrings(pcszUrlIn, szUrlExpanded, SIZECHARS(szUrlExpanded));
  632. PathRemoveBlanks(szUrlExpanded);
  633. // Unintialized szQualifiedUrl causes junk characters to appear on
  634. // addressbar.
  635. szQualifiedUrl[0] = TEXT('\0');
  636. // CASE #6. Just check if this is a URL to Navigate to. This call will
  637. // pretty much always succeeded, because it will accept
  638. // anything as a search URL.
  639. if (IsFlagSet(dwParseFlags, SHURL_FLAGS_AUTOCORRECT))
  640. {
  641. hr = (_ParseURLFromOutsideSource(szUrlExpanded, szQualifiedUrl, &cchSize, NULL, pfWasCorrected) ? S_OK : E_FAIL);
  642. }
  643. else
  644. {
  645. hr = (ParseURLFromOutsideSource(szUrlExpanded, szQualifiedUrl, &cchSize, NULL) ? S_OK : E_FAIL);
  646. }
  647. if (SUCCEEDED(hr))
  648. {
  649. SetUrl(szQualifiedUrl, GENTYPE_FROMURL);
  650. Str_SetPtr(&m_pszDisplayName, szQualifiedUrl); // The display Name will be exactly what the user entered.
  651. }
  652. ASSERT(!m_pidl);
  653. if (fDisable && SUCCEEDED(hr))
  654. {
  655. nScheme = GetUrlScheme(szQualifiedUrl);
  656. // We will allow all but the following schemes:
  657. if ((URL_SCHEME_SHELL != nScheme) &&
  658. (URL_SCHEME_FILE != nScheme) &&
  659. (URL_SCHEME_UNKNOWN != nScheme) &&
  660. (URL_SCHEME_INVALID != nScheme))
  661. {
  662. fDisable = FALSE;
  663. }
  664. }
  665. }
  666. }
  667. if (fDisable && ((URL_SCHEME_FILE == nScheme) || (URL_SCHEME_INVALID == nScheme) || (URL_SCHEME_UNKNOWN == nScheme)))
  668. {
  669. if (IsFlagClear(dwParseFlags, SHURL_FLAGS_NOUI))
  670. {
  671. MLShellMessageBox(m_hwnd, MAKEINTRESOURCE(IDS_SHURL_ERR_PARSE_NOTALLOWED),
  672. MAKEINTRESOURCE(IDS_SHURL_ERR_TITLE),
  673. (MB_OK | MB_ICONERROR), pszUrlInMod);
  674. }
  675. hr = E_ACCESSDENIED;
  676. Reset(); // Just in case the caller ignores the return value.
  677. }
  678. return hr;
  679. }
  680. /****************************************************\
  681. FUNCTION: _QualifyFromPath
  682. PARAMETERS:
  683. pcszFilePathIn - String that may be in the Path.
  684. dwFlags - Parse Flags, not currently used.
  685. DESCRIPTION:
  686. This function will call _QualifyFromAppPath()
  687. to see if the item exists in the AppPaths. If not,
  688. it will check in the DOS Path Env. variable with a
  689. call to _QualifyFromDOSPath().
  690. \****************************************************/
  691. HRESULT CShellUrl::_QualifyFromPath(LPCTSTR pcszFilePathIn, DWORD dwFlags)
  692. {
  693. HRESULT hr = _QualifyFromAppPath(pcszFilePathIn, dwFlags);
  694. if (FAILED(hr))
  695. hr = _QualifyFromDOSPath(pcszFilePathIn, dwFlags);
  696. return hr;
  697. }
  698. /****************************************************\
  699. FUNCTION: _QualifyFromDOSPath
  700. PARAMETERS:
  701. pcszFilePathIn - String that may be in the Path.
  702. dwFlags - Parse Flags, not currently used.
  703. DESCRIPTION:
  704. See if pcszFilePathIn exists in the DOS Path Env
  705. variable. If so, set the ShellUrl to that location.
  706. \****************************************************/
  707. HRESULT CShellUrl::_QualifyFromDOSPath(LPCTSTR pcszFilePathIn, DWORD dwFlags)
  708. {
  709. HRESULT hr = E_FAIL;
  710. TCHAR szPath[MAX_PATH];
  711. LPTSTR pszEnd = (LPTSTR) pcszFilePathIn;
  712. BOOL fContinue = TRUE;
  713. do
  714. {
  715. hr = _GetNextPossibleFullPath(pcszFilePathIn, &pszEnd, szPath, SIZECHARS(szPath), &fContinue);
  716. if (SUCCEEDED(hr))
  717. {
  718. if (PathFindOnPathEx(szPath, NULL, (PFOPEX_OPTIONAL | PFOPEX_COM | PFOPEX_BAT | PFOPEX_PIF | PFOPEX_EXE)))
  719. {
  720. _GeneratePidl(szPath, GENTYPE_FROMPATH);
  721. if (!ILIsFileSysFolder(m_pidl))
  722. {
  723. Str_SetPtr(&m_pszArgs, pszEnd); // Set aside Args
  724. break;
  725. }
  726. }
  727. if (fContinue)
  728. pszEnd = CharNext(pszEnd);
  729. hr = E_FAIL;
  730. }
  731. }
  732. while (FAILED(hr) && fContinue);
  733. return hr;
  734. }
  735. /****************************************************\
  736. FUNCTION: _QualifyFromAppPath
  737. PARAMETERS:
  738. pcszFilePathIn - String that may be in the Path.
  739. dwFlags - Parse Flags, not currently used.
  740. DESCRIPTION:
  741. See if pcszFilePathIn exists in the AppPaths
  742. Registry Section. If so, set the ShellUrl to that location.
  743. \****************************************************/
  744. HRESULT CShellUrl::_QualifyFromAppPath(LPCTSTR pcszFilePathIn, DWORD dwFlags)
  745. {
  746. HRESULT hr = E_FAIL;
  747. TCHAR szFileName[MAX_PATH];
  748. TCHAR szRegKey[MAX_PATH];
  749. DWORD dwType;
  750. DWORD cbData = sizeof(szFileName);
  751. DWORD cchNewPathSize;
  752. StringCchCopy(szFileName, SIZECHARS(szFileName), pcszFilePathIn);
  753. PathRemoveArgs(szFileName); // Get Rid of Args (Will be added later)
  754. cchNewPathSize = lstrlen(szFileName); // Get size so we known where to find args in pcszFilePathIn
  755. PathAddExtension(szFileName, TEXT(".exe")); // Add extension if needed.
  756. StringCchPrintf(szRegKey, ARRAYSIZE(szRegKey), TEXT("%s\\%s"), STR_REGKEY_APPPATH, szFileName);
  757. if (NOERROR == SHGetValue(HKEY_LOCAL_MACHINE, szRegKey, TEXT(""), &dwType, (LPVOID) szFileName, &cbData))
  758. {
  759. // 1. Create Pidl from String.
  760. hr = _GeneratePidl(szFileName, GENTYPE_FROMPATH);
  761. // 2. Set aside Args
  762. ASSERT((DWORD)lstrlen(pcszFilePathIn) >= cchNewPathSize);
  763. Str_SetPtr(&m_pszArgs, &(pcszFilePathIn[cchNewPathSize]));
  764. }
  765. return hr;
  766. }
  767. /****************************************************\
  768. FUNCTION: _ParseUNC
  769. PARAMETERS:
  770. pcszUrlIn - URL, which can be a UNC path.
  771. pfPossibleWebUrl - Set to FALSE if we find that the user has attempted
  772. to enter a Shell Url or File url but misspelled one
  773. of the segments.
  774. dwFlags - Parse Flags
  775. fQualifyDispName - If TRUE when we known that we need to force the
  776. URL to be fully qualified if we bind to the destination.
  777. This is needed because we are using state information to
  778. find the destination URL and that state information won't
  779. be available later.
  780. DESCRIPTION:
  781. See if the URL passed in is a valid path
  782. relative to "SHELL:Desktop/Network Neighborhood".
  783. \****************************************************/
  784. HRESULT CShellUrl::_ParseUNC(LPCTSTR pcszUrlIn, BOOL * pfPossibleWebUrl, DWORD dwFlags, BOOL fQualifyDispName)
  785. {
  786. HRESULT hr = E_FAIL;
  787. LPITEMIDLIST pidlNN = NULL;
  788. SHGetSpecialFolderLocation(NULL, CSIDL_NETWORK, &pidlNN); // Get Pidl for "Network Neighborhood"
  789. if (pidlNN)
  790. {
  791. hr = _ParseRelativePidl(pcszUrlIn, pfPossibleWebUrl, dwFlags, pidlNN, FALSE, fQualifyDispName);
  792. ILFree(pidlNN);
  793. }
  794. return hr;
  795. }
  796. /****************************************************\
  797. FUNCTION: _ParseSeparator
  798. PARAMETERS:
  799. pidl - PIDL to ISF that has been parsed so far.
  800. pcszSeg - Str of rest of Url to parse.
  801. pfPossibleWebUrl - Set to FALSE if we know that the user attempted
  802. but failed to enter a correct Shell Url.
  803. fQualifyDispName - If TRUE when we known that we need to force the
  804. URL to be fully qualified if we bind to the
  805. destination. This is needed because we are using
  806. state information to find the destination URL and
  807. that state information won't be available later.
  808. DESCRIPTION:
  809. This function is called after at least one
  810. segment in the SHELL URL has bound to a valid
  811. Shell Item/Folder (i.e., ITEMID). It is called
  812. each time a segment in the Shell Url binds to a PIDL.
  813. It will then evaluate the rest of the string and
  814. determine if:
  815. 1. The URL has been completely parsed
  816. and is valid. This will include getting
  817. the command line arguments if appropriate.
  818. 2. More segments in the URL exist and ::_ParseNextSegment()
  819. needs to be called to continue the recursive parsing
  820. of the URL.
  821. 3. The rest of the URL indicates that it's an invalid url.
  822. This function is always called by ::_ParseNextSegment() and basically
  823. decides if it wants to continue the recursion by calling back into
  824. ::_ParseNextSegment() or not. Recursion is used because it's necessary
  825. to back out of parsing something and go down a path if we received
  826. a false positive.
  827. \****************************************************/
  828. HRESULT CShellUrl::_ParseSeparator(LPCITEMIDLIST pidl, LPCTSTR pcszSeg, BOOL * pfPossibleWebUrl, BOOL fAllowRelative, BOOL fQualifyDispName)
  829. {
  830. HRESULT hr = S_OK;
  831. BOOL fIgnoreArgs = FALSE;
  832. ASSERT(pidl && IS_VALID_PIDL(pidl));
  833. // Does anything follow this separator?
  834. if ((CH_FRAGMENT == pcszSeg[0]) || (IS_SHELL_SEPARATOR(pcszSeg[0]) && pcszSeg[1]))
  835. {
  836. // Yes, continue parsing recursively.
  837. // Do we need to skip the '/' or '\' separator?
  838. if (CH_FRAGMENT != pcszSeg[0])
  839. pcszSeg++; // Skip separator
  840. hr = _ParseNextSegment(pidl, pcszSeg, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
  841. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  842. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseSeparator() Current Level pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  843. if (FAILED(hr) && pfPossibleWebUrl)
  844. {
  845. *pfPossibleWebUrl = FALSE;
  846. // We bound to at least one level when parsing, so don't do a web search because
  847. // of a failure.
  848. }
  849. }
  850. else
  851. {
  852. // No, we will see if we have reached a valid Shell Item.
  853. // Is the remaining string args?
  854. if (CH_SPACE == pcszSeg[0])
  855. {
  856. // If there are still chars left in the string, we need to
  857. // verify the first one is a space to indicate Command line args.
  858. // Also, we need to make sure the PIDL isn't browsable because browsable
  859. // Shell folders/items don't take Cmd Line Args.
  860. if (ILIsBrowsable(pidl, NULL))
  861. {
  862. // No
  863. //
  864. // The remaining chars cannot be Command Line Args if the PIDL
  865. // doesn't point to something that is shell executable. This
  866. // case actually happens often.
  867. // Example: (\\bryanst\... and \\bryanst2\.. both exist and
  868. // user enters \\bryanst2\... but parsing attempts
  869. // to use \\bryanst because it was found first. This
  870. // will cause recursion to crawl back up the stack and try \\bryanst2.
  871. hr = E_FAIL;
  872. }
  873. }
  874. else if (pcszSeg[0])
  875. {
  876. // No
  877. // The only time we allow a char after a folder segment is if it is a Shell Separator
  878. // Example: "E:\dir1\"
  879. if (IS_SHELL_SEPARATOR(*pcszSeg) && 0 == pcszSeg[1])
  880. fIgnoreArgs = TRUE;
  881. else
  882. hr = E_FAIL; // Invalid because there is more to be parsed.
  883. }
  884. if (SUCCEEDED(hr))
  885. {
  886. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  887. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseSeparator() Parsing Finished. pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  888. _SetPidl(pidl);
  889. if (!fIgnoreArgs && pcszSeg[0])
  890. Str_SetPtr(&m_pszArgs, pcszSeg);
  891. if (fQualifyDispName)
  892. _GenDispNameFromPidl(pidl, pcszSeg);
  893. }
  894. }
  895. return hr;
  896. }
  897. //
  898. // Returns TRUE is the pidl is a network server
  899. //
  900. BOOL _IsNetworkServer(LPCITEMIDLIST pidl)
  901. {
  902. BOOL fRet = FALSE;
  903. // First see if this is a network pidl
  904. if (IsSpecialFolderChild(pidl, CSIDL_NETWORK, FALSE))
  905. {
  906. // See if it ends in a share name
  907. WCHAR szUrl[MAX_URL_STRING];
  908. HRESULT hr = IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), NULL);
  909. if (FAILED(hr))
  910. {
  911. // On non-integrated browsers SHGDN_FORPARSING may fail so try
  912. // again without this flag. The preceeding back slashes will be
  913. // missing so we add them ourselves
  914. szUrl[0] = CH_FILESEPARATOR;
  915. szUrl[1] = CH_FILESEPARATOR;
  916. hr = IEGetNameAndFlags(pidl, SHGDN_NORMAL | SHGDN_FORADDRESSBAR, szUrl+2, ARRAYSIZE(szUrl)-2, NULL);
  917. }
  918. fRet = SUCCEEDED(hr) && PathIsUNCServer(szUrl);
  919. }
  920. return fRet;
  921. }
  922. /****************************************************\
  923. FUNCTION: _ParseNextSegment
  924. PARAMETERS:
  925. pidlParent - Fully Qualified PIDL to ISF to find next ITEMID in pcszStrToParse.
  926. pcszStrToParse - pcszStrToParse will begin with either
  927. a valid display name of a child ITEMID of pidlParent
  928. or the Shell URL is invalid relative to pidlParent.
  929. fAllowRelative - Should relative moves be allowed?
  930. fQualifyDispName - If TRUE when we known that we need to force the
  931. URL to be fully qualified if we bind to the destination.
  932. This is needed because we are using state information to
  933. find the destination URL and that state information won't
  934. be available later.
  935. DESCRIPTION/PERF:
  936. This function exists to take the string (pcszStrToParse)
  937. passed in and attempt to bind to a ITEMID which
  938. has a DisplayName that matches the beginning of
  939. pcszStrToParse. This function will check all the
  940. ITEMIDs under the pidlParent section of the Shell
  941. Name Space.
  942. The only two exceptions to the above method is if
  943. 1) the string begins with "..", in which case, we
  944. bind to the pidlParent's Parent ITEMID. - or -
  945. 2) The pidlParent passes the ::_IsFilePidl()
  946. test and we are guaranteed the item is in the
  947. File System or a UNC item. This will allow us
  948. to call IShellFolder::ParseDisplayName() to
  949. find the child ITEMID of pidlParent.
  950. This function will iterate through the items under
  951. pidlParent instead of call IShellFolder::ParseDisplayName
  952. for two reasons: 1) The ::ParseDisplayName for "The Internet"
  953. will accept any string because of AutoSearch, and
  954. 2) We never know the location of the end of one segment and
  955. the beginning of the next segment in pcszStrToParse. This is
  956. because DisplayNames for ISFs can contain almost any character.
  957. If this function has successfully bind to a child ITEMID
  958. of pidlParent, it will call ::_ParseSeparator() with the
  959. rest of pcszStrToParse to parse. _ParseSeparator() will determine
  960. if the end of the URL has been parsed or call back into this function
  961. recursively to continue parsing segments. In the former case,
  962. _ParseSeparator() will set this object's PIDL and arguments which
  963. can be used later. In the latter case, the recursion stack will
  964. unwind and my take a different path (Cases exists that require this).
  965. \****************************************************/
  966. HRESULT CShellUrl::_ParseNextSegment(LPCITEMIDLIST pidlParent,
  967. LPCTSTR pcszStrToParse, BOOL * pfPossibleWebUrl,
  968. BOOL fAllowRelative, BOOL fQualifyDispName)
  969. {
  970. HRESULT hr = E_FAIL;
  971. if (!pidlParent || !pcszStrToParse)
  972. return E_INVALIDARG;
  973. // Is this ".."?
  974. if (fAllowRelative && CH_DOT == pcszStrToParse[0] && CH_DOT == pcszStrToParse[1])
  975. {
  976. // Yes
  977. LPITEMIDLIST pidl = ILClone(pidlParent);
  978. if (pidl && !ILIsEmpty(pidl))
  979. {
  980. ILRemoveLastID(pidl); // pidl/psfFolder now point to the new shell item, which is the parent in this case.
  981. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  982. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseNextSegment() Nav '..'. PIDL=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  983. // Parse the next segment or finish up if we reached the end
  984. // (we're skipping the ".." here)
  985. hr = _ParseSeparator(pidl, &(pcszStrToParse[2]), pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
  986. ILFree(pidl);
  987. }
  988. }
  989. else
  990. {
  991. // No
  992. LPTSTR pszNext = NULL; // Remove const because we will iterate only.
  993. long i = 0;
  994. // Can we parse this display name quickly?
  995. if (!ILIsRooted(pidlParent) && _IsFilePidl(pidlParent) &&
  996. // Quick way fails for shares right off of the network server
  997. !_IsNetworkServer(pidlParent))
  998. {
  999. // Yes
  1000. TCHAR szParseChunk[MAX_PATH+1];
  1001. do
  1002. {
  1003. ++i;
  1004. hr = _GetNextPossibleSegment(pcszStrToParse, &pszNext, szParseChunk, SIZECHARS(szParseChunk), TRUE);
  1005. if (S_OK == hr)
  1006. {
  1007. hr = _QuickParse(pidlParent, szParseChunk, pszNext, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
  1008. //
  1009. // Certain network shares like \\foo\Printers and "\\foo\Scheduled Tasks" will fail the if we
  1010. // combine the server and share in a segment. So we try parsing the server separately.
  1011. //
  1012. if ((S_OK != hr) && (i == 1) && PathIsUNCServerShare(szParseChunk))
  1013. {
  1014. pszNext = NULL;
  1015. hr = _GetNextPossibleSegment(pcszStrToParse, &pszNext, szParseChunk, SIZECHARS(szParseChunk), FALSE);
  1016. if (S_OK == hr)
  1017. {
  1018. hr = _QuickParse(pidlParent, szParseChunk, pszNext, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
  1019. }
  1020. }
  1021. #ifdef FEATURE_SUPPORT_FRAGS_INFILEURLS
  1022. // Did we fail to parse the traditional way and the first char of this
  1023. // next chunk indicates it's probably a URL Fragment?
  1024. if (FAILED(hr) && (CH_FRAGMENT == pcszStrToParse[0]))
  1025. {
  1026. TCHAR szUrl[MAX_URL_STRING];
  1027. // Yes, so try parsing in another way that will work
  1028. // with URL fragments.
  1029. hr = ::IEGetDisplayName(pidlParent, szUrl, SHGDN_FORPARSING);
  1030. if (EVAL(SUCCEEDED(hr)))
  1031. {
  1032. TCHAR szFullUrl[MAX_URL_STRING];
  1033. DWORD cchFullUrlSize = ARRAYSIZE(szFullUrl);
  1034. hr = UrlCombine(szUrl, szParseChunk, szFullUrl, &cchFullUrlSize, 0);
  1035. if (EVAL(SUCCEEDED(hr)))
  1036. {
  1037. LPITEMIDLIST pidl = NULL;
  1038. hr = IEParseDisplayName(CP_ACP, szFullUrl, &pidl);
  1039. if (SUCCEEDED(hr))
  1040. {
  1041. _SetPidl(pidl);
  1042. if (fQualifyDispName)
  1043. _GenDispNameFromPidl(pidl, szFullUrl);
  1044. ILFree(pidl);
  1045. }
  1046. else
  1047. ASSERT(!pidl); // Verify IEParseDisplayName() didn't fail but return a pidl.
  1048. }
  1049. }
  1050. }
  1051. #endif // FEATURE_SUPPORT_FRAGS_INFILEURLS
  1052. }
  1053. }
  1054. while (FAILED(hr));
  1055. if (S_OK != hr)
  1056. hr = E_FAIL; // Not Found
  1057. }
  1058. else if (FAILED(hr))
  1059. {
  1060. // No; use the slow method
  1061. IShellFolder * psfFolder = NULL;
  1062. DWORD dwAttrib = SFGAO_FOLDER;
  1063. IEGetAttributesOf(pidlParent, &dwAttrib);
  1064. if (IsFlagSet(dwAttrib, SFGAO_FOLDER))
  1065. {
  1066. IEBindToObject(pidlParent, &psfFolder);
  1067. ASSERT(psfFolder);
  1068. }
  1069. if (psfFolder)
  1070. {
  1071. LPENUMIDLIST penumIDList = NULL;
  1072. HWND hwnd = _GetWindow();
  1073. // Is this an FTP Pidl?
  1074. if (IsFTPFolder(psfFolder))
  1075. {
  1076. // NT #274795: Yes so, we need to NULL out the hwnd to prevent
  1077. // displaying UI because enumerator of that folder may need to display
  1078. // UI (to collect passwords, etc.). This is not valid because pcszStrToParse
  1079. // may be an absolute path and psfFolder points to the current location which
  1080. // isn't valid. This should probaby be done for all IShellFolder::EnumObjects()
  1081. // calls, but it's too risky right before ship.
  1082. hwnd = NULL;
  1083. }
  1084. // Warning Docfind returns S_FALSE to indicate no enumerator and returns NULL..
  1085. if (S_OK == IShellFolder_EnumObjects(psfFolder, hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penumIDList))
  1086. {
  1087. LPITEMIDLIST pidlRelative; // NOT a FULLY Qualified Pidl
  1088. LPITEMIDLIST pidlResult; // PIDL after it has been made Fully Qualified
  1089. ULONG cFetched;
  1090. LPTSTR pszRemaining = NULL;
  1091. while (FAILED(hr) && NOERROR == penumIDList->Next(1, &pidlRelative, &cFetched) && cFetched)
  1092. {
  1093. // The user will have entered the name in one of the three formats and they need to be
  1094. // checked from the longest string to the smallest. This is necessary because the
  1095. // parser will check to see if the item's DisplayName is the first part of the user
  1096. // string.
  1097. //
  1098. // #1. (FORPARSING): This will be the full name.
  1099. // Example: razzle.lnk on desktop = D:\nt\public\tools\razzle.lnk.
  1100. // #2. (FORPARSING | SHGDN_INFOLDER): This will be only the full name w/Extension.
  1101. // Example: razzle.lnk on desktop = razzle.lnk
  1102. // #3. (SHGDN_INFOLDER): This will be the full name w/o extension if "Hide File Extensions for Known File Types" is on.
  1103. // Example: razzle.lnk on desktop = D:\nt\public\tools\razzle.lnk.
  1104. // The user may have entered the "SHGDN_FORPARSING" Display Name or the "SHGDN_INFOLDER", so we need
  1105. // to check both.
  1106. hr = _CheckItem(psfFolder, pidlParent, pidlRelative, &pidlResult, pcszStrToParse, &pszRemaining, SHGDN_FORPARSING);
  1107. if (FAILED(hr)) // Used for file items w/extensions. (Like razzle.lnk on the Desktop)
  1108. hr = _CheckItem(psfFolder, pidlParent, pidlRelative, &pidlResult, pcszStrToParse, &pszRemaining, SHGDN_FORPARSING | SHGDN_INFOLDER);
  1109. if (FAILED(hr))
  1110. hr = _CheckItem(psfFolder, pidlParent, pidlRelative, &pidlResult, pcszStrToParse, &pszRemaining, SHGDN_INFOLDER);
  1111. if (SUCCEEDED(hr))
  1112. {
  1113. // See if the Display Name for a Drive ate the separator for the next segment.
  1114. if (_FixDriveDisplayName(pcszStrToParse, pszRemaining, pidlResult))
  1115. {
  1116. // FIX: "E:\dir1\dir2". We expent display names to not claim the '\' separator between
  1117. // names. The problem is that drive letters claim to be "E:\" instead
  1118. // of "E:". So, we need to back up so we use the '\' as a separator.
  1119. pszRemaining--;
  1120. }
  1121. // Our root is equal to a separator.
  1122. ASSERT(pcszStrToParse != pszRemaining);
  1123. // Parse the next segment or finish up if we reached the end.
  1124. hr = _ParseSeparator(pidlResult, pszRemaining, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
  1125. if (pidlResult)
  1126. ILFree(pidlResult);
  1127. }
  1128. ILFree(pidlRelative);
  1129. }
  1130. penumIDList->Release();
  1131. }
  1132. psfFolder->Release();
  1133. }
  1134. }
  1135. }
  1136. return hr;
  1137. }
  1138. /****************************************************\
  1139. FUNCTION: _GetNextPossibleSegment
  1140. PARAMETERS:
  1141. pcszFullPath - Full Path
  1142. ppszSegIterator - Pointer to iterator to maintain state.
  1143. WARNING: This needs to be NULL on first call.
  1144. pszSegOut - Of S_OK is returned, this will contain the next possible segment
  1145. cchSegOutSize - char Size of pszSegOut buffer
  1146. DESCRIPTION:
  1147. Generate the next possible segment that can
  1148. be parsed. If "one two three/four five" is passed
  1149. in, this function will return S_OK three times
  1150. with these values in pszSegOut:
  1151. 1) "one two three",
  1152. 2) "one two", and
  1153. 3) "one".
  1154. In this example, S_OK will be returned for the first
  1155. three calls, and S_FALSE will be returned for the
  1156. fourth to indicate that no more possible segments can be obtained
  1157. from that string.
  1158. \****************************************************/
  1159. HRESULT CShellUrl::_GetNextPossibleSegment(LPCTSTR pcszFullPath,
  1160. LPTSTR * ppszSegIterator, LPTSTR pszSegOut, DWORD cchSegOutSize, BOOL fSkipShare)
  1161. {
  1162. HRESULT hr = S_OK;
  1163. LPTSTR szStart = (LPTSTR) pcszFullPath;
  1164. // We need to treat UNCs Specially.
  1165. if (PathIsUNC(szStart))
  1166. {
  1167. LPTSTR szUNCShare;
  1168. // This is a UNC so we need to make the "Segment" include
  1169. // the "\\server\share" because Network Neighborhood's
  1170. // IShellFolder::ParseDisplayName() is increadibly slow
  1171. // and makes mistakes when it parses "server" and then "share"
  1172. // separately.
  1173. // This if clause will advance szStart past the Server
  1174. // section of the UNC path so the rest of the algorithm will
  1175. // naturally continue working on the share section of the UNC.
  1176. szStart += 2; // Skip past the "\\" UNC header.
  1177. // Is there a share?
  1178. if (fSkipShare && (szUNCShare = StrChr(szStart, CH_FILESEPARATOR)))
  1179. {
  1180. // Yes, so advanced to the first char in the share
  1181. // name so the algorithm below works correctly.
  1182. szStart = szUNCShare + 1;
  1183. }
  1184. }
  1185. // Do we need to initialize the iterator? If so, set it to the
  1186. // largest possible segment in the string because we will be
  1187. // working backwards.
  1188. ASSERT(ppszSegIterator);
  1189. if (*ppszSegIterator)
  1190. {
  1191. *ppszSegIterator = StrRChr(szStart, *ppszSegIterator, CH_SPACE);
  1192. if (!*ppszSegIterator)
  1193. {
  1194. pszSegOut[0] = TEXT('\0'); // Make sure caller doesn't ignore return and recurse infinitely.
  1195. return S_FALSE;
  1196. }
  1197. }
  1198. else
  1199. {
  1200. // We have not yet started the iteration, so set the ppszSegIterator to the end of the possible
  1201. // segment. This will be a segment separator character ('\' || '/') or the end of the string
  1202. // if either of those don't exist. This will be the first segment to try.
  1203. *ppszSegIterator = StrChr(szStart, CH_FILESEPARATOR);
  1204. if (!*ppszSegIterator)
  1205. *ppszSegIterator = StrChr(szStart, CH_SEPARATOR);
  1206. LPTSTR pszFrag = StrChr(szStart, CH_FRAGMENT);
  1207. // Is the next separator a fragment?
  1208. if (pszFrag && (!*ppszSegIterator || (pszFrag < *ppszSegIterator)))
  1209. {
  1210. TCHAR szFile[MAX_URL_STRING];
  1211. StringCchCopy(szFile, (int)(pszFrag - szStart + 1), szStart);
  1212. if (PathIsHTMLFile(szFile))
  1213. *ppszSegIterator = pszFrag;
  1214. }
  1215. if (!*ppszSegIterator)
  1216. {
  1217. // Go to end of the string because this is the last seg.
  1218. *ppszSegIterator = (LPTSTR) &((szStart)[lstrlen(szStart)]);
  1219. }
  1220. }
  1221. // Fill the pszSegOut parameter.
  1222. ASSERT(*ppszSegIterator);
  1223. // This is weird but correct. pszEnd - pszBeginning results count of chars, not
  1224. // count of bytes.
  1225. if (cchSegOutSize >= (DWORD)((*ppszSegIterator - pcszFullPath) + 1))
  1226. StringCchCopy(pszSegOut, (int)(*ppszSegIterator - pcszFullPath + 1), pcszFullPath);
  1227. else
  1228. StringCchCopy(pszSegOut, cchSegOutSize - 1, pcszFullPath);
  1229. return hr;
  1230. }
  1231. /****************************************************\
  1232. FUNCTION: _GetNextPossibleFullPath
  1233. DESCRIPTION:
  1234. This function will attempt to see if strParseChunk
  1235. is a Parsible DisplayName under pidlParent.
  1236. \****************************************************/
  1237. HRESULT CShellUrl::_GetNextPossibleFullPath(LPCTSTR pcszFullPath,
  1238. LPTSTR * ppszSegIterator, LPTSTR pszSegOut, DWORD cchSegOutSize,
  1239. BOOL * pfContinue)
  1240. {
  1241. HRESULT hr = S_OK;
  1242. LPTSTR pszNext = StrChr(*ppszSegIterator, CH_SPACE);
  1243. DWORD cchAmountToCopy = cchSegOutSize;
  1244. if (TEXT('\0') == (*ppszSegIterator)[0])
  1245. {
  1246. if (pfContinue)
  1247. *pfContinue = FALSE;
  1248. return E_FAIL; // Nothing Left.
  1249. }
  1250. if (!pszNext)
  1251. pszNext = &((*ppszSegIterator)[lstrlen(*ppszSegIterator)]); // Go to end of the string because this is the last seg.
  1252. // Copy as much of the string as we have room for.
  1253. // The compiler will take care of adding '/ sizeof(TCHAR)'.
  1254. if ((cchAmountToCopy-1) > (DWORD)(pszNext - pcszFullPath + 1))
  1255. cchAmountToCopy = (int)(pszNext - pcszFullPath + 1);
  1256. StringCchCopy(pszSegOut, cchAmountToCopy, pcszFullPath);
  1257. if (CH_SPACE == pszNext[0])
  1258. {
  1259. *pfContinue = TRUE;
  1260. }
  1261. else
  1262. *pfContinue = FALSE;
  1263. *ppszSegIterator = pszNext;
  1264. return hr;
  1265. }
  1266. /****************************************************\
  1267. FUNCTION: _QuickParse
  1268. PARAMETERS:
  1269. pidlParent - Pidl to ISF to parse from.
  1270. pszParseChunk - Display Name of item in pidlParent.
  1271. pszNext - Rest of string to parse if we succeed at parsing pszParseChunk.
  1272. pfPossibleWebUrl - Set to FALSE if we find that the user has attempted to enter
  1273. a Shell Url or File url but misspelled one of the segments.
  1274. fAllowRelative - Allow relative parsing. ("..")
  1275. fQualifyDispName - If TRUE when we known that we need to force the
  1276. URL to be fully qualified if we bind to the destination.
  1277. This is needed because we are using state information to
  1278. find the destination URL and that state information won't
  1279. be available later.
  1280. DESCRIPTION:
  1281. This function will attempt to see if strParseChunk
  1282. is a Parsible DisplayName under pidlParent.
  1283. \****************************************************/
  1284. HRESULT CShellUrl::_QuickParse(LPCITEMIDLIST pidlParent, LPTSTR pszParseChunk,
  1285. LPTSTR pszNext, BOOL * pfPossibleWebUrl, BOOL fAllowRelative,
  1286. BOOL fQualifyDispName)
  1287. {
  1288. HRESULT hr;
  1289. IShellFolder * psfFolder;
  1290. hr = IEBindToObject(pidlParent, &psfFolder);
  1291. if (SUCCEEDED(hr))
  1292. {
  1293. ULONG ulEatten; // Not used.
  1294. SHSTRW strParseChunkThunked;
  1295. hr = strParseChunkThunked.SetStr(pszParseChunk);
  1296. if (SUCCEEDED(hr))
  1297. {
  1298. LPITEMIDLIST pidl = NULL;
  1299. // TODO: In the future, we may want to cycle through commonly used extensions in case the
  1300. // user doesn't add them.
  1301. hr = psfFolder->ParseDisplayName(_GetWindow(), NULL, strParseChunkThunked.GetInplaceStr(), &ulEatten, &pidl, NULL);
  1302. if (SUCCEEDED(hr))
  1303. {
  1304. // IShellFolder::ParseDisplayName() only generates PIDLs that are relative to the ISF. We need
  1305. // to make them Absolute.
  1306. LPITEMIDLIST pidlFull = ILCombine(pidlParent, pidl);
  1307. if (pidlFull)
  1308. {
  1309. // Parse the next segment or finish up if we reached the end.
  1310. hr = _ParseSeparator(pidlFull, pszNext, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
  1311. ILFree(pidlFull);
  1312. }
  1313. ILFree(pidl);
  1314. }
  1315. }
  1316. psfFolder->Release();
  1317. }
  1318. return hr;
  1319. }
  1320. /****************************************************\
  1321. FUNCTION: _CheckItem
  1322. DESCRIPTION:
  1323. This function will obtain the Display Name
  1324. of the ITEMID (pidlRelative) which is a child of
  1325. psfFolder. If it's Display Name matches the first
  1326. part of pcszStrToParse, we will return successful
  1327. and set ppszRemaining to the section of pcszStrToParse
  1328. after the segment just parsed.
  1329. This function will also see if the Display Name ends
  1330. in something that would indicate it's executable.
  1331. (.EXE, .BAT, .COM, ...). If so, we will match if
  1332. pcszStrToParse matches the Display Name without the
  1333. Extension.
  1334. \****************************************************/
  1335. HRESULT CShellUrl::_CheckItem(IShellFolder * psfFolder,
  1336. LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlRelative,
  1337. LPITEMIDLIST * ppidlChild, LPCTSTR pcszStrToParse,
  1338. LPTSTR * ppszRemaining, DWORD dwFlags)
  1339. {
  1340. HRESULT hr = E_FAIL;
  1341. *ppidlChild = NULL;
  1342. TCHAR szISFName[MAX_URL_STRING];
  1343. if (SUCCEEDED(DisplayNameOf(psfFolder, pidlRelative, dwFlags, szISFName, SIZECHARS(szISFName))))
  1344. {
  1345. DWORD cchISFLen = lstrlen(szISFName);
  1346. DWORD cchStrToParse = lstrlen(pcszStrToParse);
  1347. BOOL fEqual = FALSE;
  1348. // Either the item needs to match exactly, or it needs to do a partial match
  1349. // if the Shell Object is an executable file. For Example: "msdev" should match the
  1350. // "msdev.exe" file object.
  1351. if (cchISFLen > 0)
  1352. {
  1353. // We want to see if pcszStrToParse is a match to the first part of szISFName.
  1354. // First we will try to see if it's a direct match.
  1355. // Example: User="file.exe" Shell Item="file.exe"
  1356. // But we DON'T want to match if the StrToParse is longer than
  1357. // ISFName, unless the next char in StrToParse is a separator.
  1358. // If StrToParse is shorter than ISFName, then it can't be an exact match.
  1359. if (cchStrToParse >= cchISFLen &&
  1360. 0 == StrCmpNI(szISFName, pcszStrToParse, cchISFLen) &&
  1361. (cchStrToParse == cchISFLen || IS_SHELL_SEPARATOR(pcszStrToParse[cchISFLen])))
  1362. {
  1363. fEqual = TRUE;
  1364. }
  1365. else
  1366. {
  1367. int cchRoot = (int)((PathFindExtension(szISFName)-szISFName));
  1368. // If that failed, we try to see if the Shell Item is
  1369. // executable (.EXE, .COM, .BAT, .CMD, ...) and if so,
  1370. // we will see if pcszStrToParse matches Shell Item w/o the file
  1371. // extension.
  1372. // REARCHITECT this will match if there happens to be a space in the user's
  1373. // filename that doesn't denote commandline arguments.
  1374. // Example: User="foo file.doc" Shell Item="foo.exe"
  1375. if (PathIsExe(szISFName) && // shell object is executable
  1376. (!((dwFlags & SHGDN_INFOLDER) && !(dwFlags & SHGDN_FORPARSING))) && // we didn't strip extension
  1377. ((lstrlen(pcszStrToParse) >= cchRoot) && // and user entered at least root chars
  1378. ((pcszStrToParse[cchRoot] == TEXT('\0')) || // and user entered exact root
  1379. (pcszStrToParse[cchRoot] == TEXT(' ')))) && // or possible commandline args
  1380. (0 == StrCmpNI(szISFName, pcszStrToParse, cchRoot))) // and the root matches
  1381. {
  1382. // This wasn't a direct match, but we found that the segment entered
  1383. // by the user (pcszStrToParse) matched
  1384. // We found that the ISF item is an executable object and the
  1385. // string matched w/o the extension.
  1386. fEqual = TRUE;
  1387. cchISFLen = cchRoot; // So that we generate *ppszRemaining correctly
  1388. }
  1389. }
  1390. }
  1391. if (fEqual)
  1392. {
  1393. hr = S_OK; // We were able to navigate to this shell item token.
  1394. *ppszRemaining = (LPTSTR) &(pcszStrToParse[cchISFLen]); // We will only iterate over the string, so it's ok that we loose the const.
  1395. *ppidlChild = ILCombine(pidlParent, pidlRelative);
  1396. TraceMsg(TF_CHECKITEM, "ShellUrl: _CheckItem() PIDL=>%s< IS EQUAL TO StrIn=>%s<", pcszStrToParse, szISFName);
  1397. }
  1398. else
  1399. TraceMsg(TF_CHECKITEM, "ShellUrl: _CheckItem() PIDL=>%s< not equal to StrIn=>%s<", pcszStrToParse, szISFName);
  1400. }
  1401. return hr;
  1402. }
  1403. /****************************************************\
  1404. FUNCTION: _IsFilePidl
  1405. PARAMETERS:
  1406. pidl (IN) - Pidl to check if it is a File Pidl
  1407. DESCRIPTION:
  1408. The PIDL is a file pidl if:
  1409. 1. The pidl equals "Network Neighborhood" or descendent
  1410. 2. The pidl's grandparent or farther removed from "My Computer".
  1411. This algorithm only allows "Network Neighborhood" because
  1412. that ISF contains a huge number of PIDLs and takes for ever
  1413. to enumerate. The second clause will work in any part of the
  1414. file system except for the root drive (A:\, C:\). This is
  1415. because we need to allow other direct children of "My Computer"
  1416. to use the other parsing.
  1417. \****************************************************/
  1418. BOOL CShellUrl::_IsFilePidl(LPCITEMIDLIST pidl)
  1419. {
  1420. BOOL fResult = FALSE;
  1421. BOOL fNeedToSkip = FALSE;
  1422. if (!pidl || ILIsEmpty(pidl))
  1423. return fResult;
  1424. // Test for Network Neighborhood because it will take forever to enum.
  1425. fResult = IsSpecialFolderChild(pidl, CSIDL_NETWORK, FALSE);
  1426. if (!fResult)
  1427. {
  1428. // We only want to do this if we are not the immediate
  1429. // child.
  1430. if (IsSpecialFolderChild(pidl, CSIDL_DRIVES, FALSE))
  1431. {
  1432. TCHAR szActualPath[MAX_URL_STRING]; // IEGetDisplayName() needs the buffer to be this large.
  1433. IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szActualPath, SIZECHARS(szActualPath), NULL);
  1434. DWORD dwOutSize = MAX_URL_STRING;
  1435. if (SUCCEEDED(PathCreateFromUrl(szActualPath, szActualPath, &dwOutSize, 0)))
  1436. {
  1437. PathStripToRoot(szActualPath);
  1438. fResult = PathIsRoot(szActualPath);
  1439. }
  1440. }
  1441. }
  1442. return fResult;
  1443. }
  1444. /****************************************************\
  1445. FUNCTION: IsWebUrl
  1446. PARAMETERS
  1447. none.
  1448. DESCRIPTION:
  1449. Return TRUE if the URL is a Web Url (http,
  1450. ftp, other, ...). Return FALSE if it's a Shell Url
  1451. or File Url.
  1452. \****************************************************/
  1453. BOOL CShellUrl::IsWebUrl(void)
  1454. {
  1455. if (m_pidl)
  1456. {
  1457. if (!IsURLChild(m_pidl, TRUE))
  1458. return FALSE;
  1459. }
  1460. else
  1461. {
  1462. ASSERT(m_pszURL); // This CShellUrl hasn't been set.
  1463. if (m_pszURL && IsShellUrl(m_pszURL, TRUE))
  1464. return FALSE;
  1465. }
  1466. return TRUE;
  1467. }
  1468. /****************************************************\
  1469. FUNCTION: SetCurrentWorkingDir
  1470. PARAMETERS
  1471. pShellUrlNew - Pointer to a CShellUrl that will
  1472. be the "Current Working Directory"
  1473. DESCRIPTION:
  1474. This Shell Url will have a new current working
  1475. directory, which will be the CShellUrl passed in.
  1476. MEMORY ALLOCATION:
  1477. The caller needs to Allocate pShellUrlNew and
  1478. this object will take care of freeing it. WARNING:
  1479. this means it cannot be on the stack.
  1480. \****************************************************/
  1481. HRESULT CShellUrl::SetCurrentWorkingDir(LPCITEMIDLIST pidlCWD)
  1482. {
  1483. Pidl_Set(&m_pidlWorkingDir, pidlCWD);
  1484. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  1485. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: SetCurrentWorkingDir() pidl=>%s<", Dbg_PidlStr(m_pidlWorkingDir, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  1486. return S_OK;
  1487. }
  1488. /****************************************************\
  1489. PARAMETERS
  1490. pvPidl1 - First pidl to compare
  1491. pvPidl2 - Second pidl to compare
  1492. DESCRIPTION:
  1493. Return if the pidl matches. This doesn't work
  1494. for sorted lists (because we can't determine less
  1495. than or greater than).
  1496. \****************************************************/
  1497. int DPAPidlCompare(LPVOID pvPidl1, LPVOID pvPidl2, LPARAM lParam)
  1498. {
  1499. // return < 0 for pvPidl1 before pvPidl2.
  1500. // return == 0 for pvPidl1 equals pvPidl2.
  1501. // return > 0 for pvPidl1 after pvPidl2.
  1502. return (ILIsEqual((LPCITEMIDLIST)pvPidl1, (LPCITEMIDLIST)pvPidl2) ? 0 : 1);
  1503. }
  1504. /****************************************************\
  1505. PARAMETERS
  1506. pShellUrlNew - Pointer to a CShellUrl that will
  1507. be added to the "Shell Path"
  1508. DESCRIPTION:
  1509. This Shell Url will have the ShellUrl that's
  1510. passed in added to the "Shell Path", which will be
  1511. searched when trying to qualify the Shell Url during
  1512. parsing.
  1513. MEMORY ALLOCATION:
  1514. The caller needs to Allocate pShellUrlNew and
  1515. this object will take care of freeing it. WARNING:
  1516. this means it cannot be on the stack.
  1517. \****************************************************/
  1518. HRESULT CShellUrl::AddPath(LPCITEMIDLIST pidl)
  1519. {
  1520. ASSERT(IS_VALID_PIDL(pidl));
  1521. // we dont want to add any paths that arent derived from
  1522. // our root.
  1523. if (ILIsRooted(m_pidlWorkingDir) && !ILIsParent(m_pidlWorkingDir, pidl, FALSE))
  1524. return S_FALSE;
  1525. if (!m_hdpaPath)
  1526. {
  1527. m_hdpaPath = DPA_Create(CE_PATHGROW);
  1528. if (!m_hdpaPath)
  1529. return E_OUTOFMEMORY;
  1530. }
  1531. // Does the path already exist in our list?
  1532. if (-1 == DPA_Search(m_hdpaPath, (void *)pidl, 0, DPAPidlCompare, NULL, 0))
  1533. {
  1534. // No, so let's add it.
  1535. LPITEMIDLIST pidlNew = ILClone(pidl);
  1536. if (pidlNew)
  1537. DPA_AppendPtr(m_hdpaPath, pidlNew);
  1538. }
  1539. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  1540. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: AddPath() pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  1541. return S_OK;
  1542. }
  1543. /****************************************************\
  1544. FUNCTION: Reset
  1545. PARAMETERS:
  1546. none.
  1547. DESCRIPTION:
  1548. This function will "Clean" out the object and
  1549. reset it. Normally called when the caller is about
  1550. to set new values.
  1551. \****************************************************/
  1552. HRESULT CShellUrl::Reset(void)
  1553. {
  1554. Pidl_Set(&m_pidl, NULL);
  1555. Str_SetPtr(&m_pszURL, NULL);
  1556. Str_SetPtr(&m_pszArgs, NULL);
  1557. Str_SetPtr(&m_pszDisplayName, NULL);
  1558. m_dwGenType = 0;
  1559. return S_OK;
  1560. }
  1561. /****************************************************\
  1562. FUNCTION: _CanUseAdvParsing
  1563. PARAMETERS:
  1564. none.
  1565. DESCRIPTION:
  1566. This function will return TRUE if Advanced
  1567. Parsing (Shell URLs) should be supported. This
  1568. function will keep track of whether the user
  1569. has turn off Shell Parsing from the Control Panel.
  1570. \****************************************************/
  1571. #define REGSTR_USEADVPARSING_PATH TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Band\\Address")
  1572. #define REGSTR_USEADVPARSING_VALUE TEXT("UseShellParsing")
  1573. BOOL CShellUrl::_CanUseAdvParsing(void)
  1574. {
  1575. // WARNING: Since this is static, changes to the registry entry won't be
  1576. // read in until the time the process is launched. This is okay,
  1577. // because this feature will probably be removed from the released
  1578. // product and can be added back in as power toy.
  1579. static TRI_STATE fCanUseAdvParsing = TRI_UNKNOWN;
  1580. if (TRI_UNKNOWN == fCanUseAdvParsing)
  1581. fCanUseAdvParsing = (TRI_STATE) SHRegGetBoolUSValue(REGSTR_USEADVPARSING_PATH, REGSTR_USEADVPARSING_VALUE, FALSE, TRUE);
  1582. return fCanUseAdvParsing;
  1583. }
  1584. /****************************************************\
  1585. FUNCTION: _FixDriveDisplayName
  1586. PARAMETERS:
  1587. pszStart - Pointer to the beginning of the URL string.
  1588. pszCurrent - Pointer into current location in the URL string.
  1589. pidl - PIDL pointing to location of Shell Name space that
  1590. has been parsed so far.
  1591. DESCRIPTION:
  1592. This function exists to check if we are parsing
  1593. a drive letter. This is necessary because the Display
  1594. Name of drive letters end in '\', which will is needed
  1595. later to determine the start of the next segment.
  1596. \****************************************************/
  1597. #define DRIVE_STRENDING TEXT(":\\")
  1598. #define DRIVE_STRSIZE 3 // "C:\"
  1599. BOOL _FixDriveDisplayName(LPCTSTR pszStart, LPCTSTR pszCurrent, LPCITEMIDLIST pidl)
  1600. {
  1601. BOOL fResult = FALSE;
  1602. ASSERT(pszCurrent >= pszStart);
  1603. // The compiler will take care of adding '/ sizeof(TCHAR)'.
  1604. if (((pszCurrent - pszStart) == DRIVE_STRSIZE) &&
  1605. (0 == StrCmpN(&(pszStart[1]), DRIVE_STRENDING, SIZECHARS(DRIVE_STRENDING)-1)))
  1606. {
  1607. if (IsSpecialFolderChild(pidl, CSIDL_DRIVES, TRUE))
  1608. fResult = TRUE;
  1609. }
  1610. return fResult;
  1611. }
  1612. /****************************************************\
  1613. FUNCTION: _ParseRelativePidl
  1614. PARAMETERS:
  1615. pcszUrlIn - Pointer to URL to Parse.
  1616. dwFlags - Flags to modify the way the string is parsed.
  1617. pidl - This function will see if pcszUrlIn is a list of display names
  1618. relative to this pidl.
  1619. fAllowRelative - Do we allow relative parsing, which
  1620. means strings containing "..".
  1621. fQualifyDispName - If TRUE when we known that we need to force the
  1622. URL to be fully qualified if we bind to the destination.
  1623. This is needed because we are using state information to
  1624. find the destination URL and that state information won't
  1625. be available later.
  1626. DESCRIPTION:
  1627. Start the parsing by getting the pidl of ShellUrlRelative
  1628. and call _ParseNextSegment(). _ParseNextSegment() will
  1629. recursively parse each segment of the PIDL until either
  1630. it fails to fully parse of it finishes.
  1631. \****************************************************/
  1632. HRESULT CShellUrl::_ParseRelativePidl(LPCTSTR pcszUrlIn,
  1633. BOOL * pfPossibleWebUrl, DWORD dwFlags, LPCITEMIDLIST pidl,
  1634. BOOL fAllowRelative, BOOL fQualifyDispName)
  1635. {
  1636. HRESULT hr;
  1637. BOOL fFreePidl = FALSE;
  1638. if (!pcszUrlIn)
  1639. return E_INVALIDARG;
  1640. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseRelativePidl() Begin. pcszUrlIn=%s", pcszUrlIn);
  1641. hr = _ParseNextSegment(pidl, pcszUrlIn, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
  1642. if (pidl && fFreePidl)
  1643. ILFree((LPITEMIDLIST)pidl);
  1644. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  1645. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseRelativePidl() m_pidl=>%s<", Dbg_PidlStr(m_pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  1646. return hr;
  1647. }
  1648. /****************************************************\
  1649. FUNCTION: IsShellUrl
  1650. PARAMETERS:
  1651. LPCTSTR szUrl - URL from Outside Source.
  1652. return - Whether the URL is an Internet URL.
  1653. DESCRIPTION:
  1654. This function will determine if the URL is
  1655. a shell URL which includes the following:
  1656. 1. File Urls (E;\dir1\dir2)
  1657. 2. Shell Urls (shell:desktop)
  1658. \****************************************************/
  1659. BOOL IsShellUrl(LPCTSTR pcszUrl, BOOL fIncludeFileUrls)
  1660. {
  1661. int nSchemeBefore, nSchemeAfter;
  1662. TCHAR szParsedUrl[MAX_URL_STRING];
  1663. nSchemeBefore = GetUrlScheme(pcszUrl);
  1664. IURLQualifyT(pcszUrl, UQF_GUESS_PROTOCOL, szParsedUrl, NULL, NULL);
  1665. nSchemeAfter = GetUrlScheme(szParsedUrl);
  1666. // This is a "shell url" if it is a file: (and fIncludeFileUrls is
  1667. // set), or it is a shell:, or it is an invalid scheme (which
  1668. // occurs for things like "My Computer" and "Control Panel").
  1669. return ((fIncludeFileUrls && URL_SCHEME_FILE == nSchemeAfter) ||
  1670. URL_SCHEME_SHELL == nSchemeAfter ||
  1671. URL_SCHEME_INVALID == nSchemeBefore);
  1672. }
  1673. /****************************************************\
  1674. FUNCTION: IsSpecialFolderChild
  1675. PARAMETERS:
  1676. pidlToTest (In) - Is this PIDL to test and see if it's
  1677. a child of SpecialFolder(nFolder).
  1678. psfParent (In Optional)- The psf passed to
  1679. SHGetSpecialFolderLocation() if needed.
  1680. nFolder (In) - Special Folder Number (CSIDL_INTERNET, CSIDL_DRIVES, ...).
  1681. pdwLevels (In Optional) - Pointer to DWORD to receive levels between
  1682. pidlToTest and it's parent (nFolder) if S_OK is returned.
  1683. DESCRIPTION:
  1684. This function will see if pidlToTest is a child
  1685. of the Special Folder nFolder.
  1686. \****************************************************/
  1687. BOOL IsSpecialFolderChild(LPCITEMIDLIST pidlToTest, int nFolder, BOOL fImmediate)
  1688. {
  1689. LPITEMIDLIST pidlThePidl = NULL;
  1690. BOOL fResult = FALSE;
  1691. if (!pidlToTest)
  1692. return FALSE;
  1693. ASSERT(IS_VALID_PIDL(pidlToTest));
  1694. if (NOERROR == SHGetSpecialFolderLocation(NULL, nFolder, &pidlThePidl))
  1695. {
  1696. fResult = ILIsParent(pidlThePidl, pidlToTest, fImmediate);
  1697. ILFree(pidlThePidl);
  1698. }
  1699. return fResult; // Shell Items (My Computer, Control Panel)
  1700. }
  1701. /****************************************************\
  1702. FUNCTION: GetPidl
  1703. PARAMETERS
  1704. ppidl - Pointer that will receive the current PIDL.
  1705. DESCRIPTION:
  1706. This function will retrieve the pidl that the
  1707. Shell Url is set to.
  1708. MEMORY ALLOCATION:
  1709. This function will allocate the PIDL that ppidl
  1710. points to, and the caller needs to free the PIDL when
  1711. done with it.
  1712. \****************************************************/
  1713. HRESULT CShellUrl::GetPidl(LPITEMIDLIST * ppidl)
  1714. {
  1715. HRESULT hr = S_OK;
  1716. if (ppidl)
  1717. *ppidl = NULL;
  1718. if (!m_pidl)
  1719. hr = _GeneratePidl(m_pszURL, m_dwGenType);
  1720. if (ppidl)
  1721. {
  1722. if (m_pidl)
  1723. {
  1724. *ppidl = ILClone(m_pidl);
  1725. if (!*ppidl)
  1726. hr = E_FAIL;
  1727. }
  1728. else
  1729. hr = E_FAIL;
  1730. }
  1731. // Callers only free *ppidl if SUCCEDED(hr), so assert we act this way.
  1732. ASSERT((*ppidl && SUCCEEDED(hr)) || (!*ppidl && FAILED(hr)));
  1733. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  1734. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: GetPidl() *ppidl=>%s<", Dbg_PidlStr(*ppidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  1735. return hr;
  1736. }
  1737. //
  1738. // This is a wacky class! If GetPidl member of this class is called and
  1739. // a m_pidl is generated from and url and then Execute() assumes we have
  1740. // a valid location in our namespace and calls code that will not autoscan.
  1741. // This hacky function is used to return a pidl only if we have one to
  1742. // avoid the above problem.
  1743. //
  1744. HRESULT CShellUrl::GetPidlNoGenerate(LPITEMIDLIST * ppidl)
  1745. {
  1746. HRESULT hr = E_FAIL;
  1747. if (m_pidl && ppidl)
  1748. {
  1749. *ppidl = ILClone(m_pidl);
  1750. if (*ppidl)
  1751. {
  1752. hr = S_OK;
  1753. }
  1754. }
  1755. return hr;
  1756. }
  1757. /****************************************************\
  1758. FUNCTION: _GeneratePidl
  1759. PARAMETERS
  1760. pcszUrl - This URL will be used to generate the m_pidl.
  1761. dwGenType - This is needed to know how to parse pcszUrl
  1762. to generate the PIDL.
  1763. DESCRIPTION:
  1764. This CShellUrl maintains a pointer to the object
  1765. in the Shell Name Space by using either the string URL
  1766. or the PIDL. When this CShellUrl is set to one, we
  1767. delay generating the other one for PERF reasons.
  1768. This function generates the PIDL from the string URL
  1769. when we do need the string.
  1770. \****************************************************/
  1771. HRESULT CShellUrl::_GeneratePidl(LPCTSTR pcszUrl, DWORD dwGenType)
  1772. {
  1773. HRESULT hr;
  1774. if (!pcszUrl && m_pidl)
  1775. return S_OK; // The caller only wants the PIDL to be created if it doesn't exist.
  1776. if (pcszUrl && m_pidl)
  1777. {
  1778. ILFree(m_pidl);
  1779. m_pidl = NULL;
  1780. }
  1781. switch (dwGenType)
  1782. {
  1783. case GENTYPE_FROMURL:
  1784. if (ILIsRooted(m_pidlWorkingDir))
  1785. hr = E_FAIL; // MSN Displays error dialogs on IShellFolder::ParseDisplayName()
  1786. // fall through
  1787. case GENTYPE_FROMPATH:
  1788. hr = IECreateFromPath(pcszUrl, &m_pidl);
  1789. // This may fail if it's something like "ftp:/" and not yet valid".
  1790. break;
  1791. default:
  1792. hr = E_INVALIDARG;
  1793. break;
  1794. }
  1795. if (!m_pidl && SUCCEEDED(hr))
  1796. hr = E_FAIL;
  1797. return hr;
  1798. }
  1799. /****************************************************\
  1800. FUNCTION: SetPidl
  1801. PARAMETERS
  1802. pidl - New pidl to use.
  1803. DESCRIPTION:
  1804. The shell url will now consist of the new pidl
  1805. passed in.
  1806. MEMORY ALLOCATION:
  1807. The caller is responsible for Allocating and Freeing
  1808. the PIDL parameter.
  1809. \****************************************************/
  1810. HRESULT CShellUrl::SetPidl(LPCITEMIDLIST pidl)
  1811. {
  1812. HRESULT hr = S_OK;
  1813. ASSERT(!pidl || IS_VALID_PIDL(pidl));
  1814. Reset(); // External Calls to this will reset the entire CShellUrl.
  1815. return _SetPidl(pidl);
  1816. }
  1817. /****************************************************\
  1818. FUNCTION: _SetPidl
  1819. PARAMETERS
  1820. pidl - New pidl to use.
  1821. DESCRIPTION:
  1822. This function will reset the m_pidl member
  1823. variable without modifying m_szURL. This is only used
  1824. internally, and callers that want to reset the entire
  1825. CShellUrl to a PIDL should call the public method
  1826. SetPidl().
  1827. MEMORY ALLOCATION:
  1828. The caller is responsible for Allocating and Freeing
  1829. the PIDL parameter.
  1830. \****************************************************/
  1831. HRESULT CShellUrl::_SetPidl(LPCITEMIDLIST pidl)
  1832. {
  1833. HRESULT hr = S_OK;
  1834. DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
  1835. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _SetPidl() pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
  1836. Pidl_Set(&m_pidl, pidl);
  1837. if (!m_pidl)
  1838. hr = E_FAIL;
  1839. return hr;
  1840. }
  1841. /****************************************************\
  1842. FUNCTION: GetUrl
  1843. PARAMETERS
  1844. pszUrlOut (Out Optional) - If the caller wants the string.
  1845. cchUrlOutSize (In) - Size of String Buffer Passed in.
  1846. DESCRIPTION:
  1847. This function will retrieve the string value of
  1848. the shell url. This will not include the command line
  1849. arguments or other information needed for correct navigation
  1850. (AutoSearch=On/Off, ...). Note that this may be of the
  1851. form "Shell:/desktop/My Computer/...".
  1852. \****************************************************/
  1853. HRESULT CShellUrl::GetUrl(LPTSTR pszUrlOut, DWORD cchUrlOutSize)
  1854. {
  1855. HRESULT hr = S_OK;
  1856. if (!m_pszURL)
  1857. {
  1858. if (m_pidl)
  1859. hr = _GenerateUrl(m_pidl);
  1860. else
  1861. hr = E_FAIL; // User never set the CShellUrl.
  1862. }
  1863. if (SUCCEEDED(hr) && pszUrlOut)
  1864. StringCchCopy(pszUrlOut, cchUrlOutSize, m_pszURL);
  1865. return hr;
  1866. }
  1867. /****************************************************\
  1868. !!! WARNING - extremely specific to the ShellUrl/AddressBar - ZekeL - 18-NOV-98
  1869. !!! it depends on the bizarre pathology of the ShellUrl in order
  1870. !!! to be reparsed into a pidl later. cannot be used for anything else
  1871. PARAMETERS:
  1872. pidlIn - Pointer to PIDL to generate Display Names.
  1873. pszUrlOut - String Buffer to store list of Display Names for ITEMIDs
  1874. in pidlIn.
  1875. cchUrlOutSize - Size of Buffer in characters.
  1876. DESCRIPTION:
  1877. This function will take the PIDL passed in and
  1878. generate a string containing the ILGDN_ITEMONLY Display names
  1879. of each ITEMID in the pidl separated by '\'.
  1880. \****************************************************/
  1881. #define SZ_SEPARATOR TEXT("/")
  1882. HRESULT MutantGDNForShellUrl(LPCITEMIDLIST pidlIn, LPTSTR pszUrlOut, int cchUrlOutSize)
  1883. {
  1884. HRESULT hr = S_OK;
  1885. LPCITEMIDLIST pidlCur;
  1886. IShellFolder *psfCur = NULL;
  1887. if (ILIsRooted(pidlIn))
  1888. {
  1889. // need to start off with our virtual root
  1890. LPITEMIDLIST pidlFirst = ILCloneFirst(pidlIn);
  1891. if (pidlFirst)
  1892. {
  1893. IEBindToObject(pidlFirst, &psfCur);
  1894. ILFree(pidlFirst);
  1895. }
  1896. pidlCur = _ILNext(pidlIn);
  1897. }
  1898. else
  1899. {
  1900. SHGetDesktopFolder(&psfCur);
  1901. pidlCur = pidlIn;
  1902. }
  1903. ASSERT(pidlCur && IS_VALID_PIDL(pidlCur));
  1904. while (psfCur && SUCCEEDED(hr) && !ILIsEmpty(pidlCur) && (cchUrlOutSize > 0))
  1905. {
  1906. LPITEMIDLIST pidlCopy = ILCloneFirst(pidlCur);
  1907. if (pidlCopy)
  1908. {
  1909. StringCchCopy(pszUrlOut, cchUrlOutSize, SZ_SEPARATOR);
  1910. cchUrlOutSize -= SIZECHARS(SZ_SEPARATOR);
  1911. TCHAR szCurrDispName[MAX_PATH];
  1912. hr = DisplayNameOf(psfCur, pidlCopy, SHGDN_NORMAL, szCurrDispName, SIZECHARS(szCurrDispName));
  1913. if (SUCCEEDED(hr))
  1914. {
  1915. if (TBOOL((int)cchUrlOutSize > lstrlen(szCurrDispName)))
  1916. {
  1917. StringCchCat(pszUrlOut, cchUrlOutSize, szCurrDispName);
  1918. cchUrlOutSize -= lstrlen(szCurrDispName);
  1919. }
  1920. // may fail, in that case we terminate the loop
  1921. IShellFolder *psfCurNew = NULL; // for buggy BindToObject impls
  1922. hr = psfCur->BindToObject(pidlCopy, NULL, IID_IShellFolder, (void **)&psfCurNew);
  1923. psfCur->Release();
  1924. psfCur = psfCurNew;
  1925. }
  1926. pidlCur = _ILNext(pidlCur);
  1927. ILFree(pidlCopy);
  1928. }
  1929. else
  1930. hr = E_FAIL;
  1931. }
  1932. if (psfCur)
  1933. psfCur->Release();
  1934. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: MutantGDNForShellUrl() End. pszUrlOut=%s", pszUrlOut);
  1935. return hr;
  1936. }
  1937. /****************************************************\
  1938. FUNCTION: _GenerateUrl
  1939. PARAMETERS
  1940. pidl - This PIDL will be used to generate the m_pszURL, string URL.
  1941. DESCRIPTION:
  1942. This CShellUrl maintains a pointer to the object
  1943. in the Shell Name Space by using either the string URL
  1944. or the PIDL. When this CShellUrl is set to one, we
  1945. delay generating the other one for PERF reasons.
  1946. This function generates the string URL from the PIDL
  1947. when we do need the string.
  1948. \****************************************************/
  1949. #define SZ_THEINTERNET_PARSENAME TEXT("::{")
  1950. HRESULT CShellUrl::_GenerateUrl(LPCITEMIDLIST pidl)
  1951. {
  1952. HRESULT hr = S_OK;
  1953. TCHAR szUrl[MAX_URL_STRING];
  1954. ASSERT(IS_VALID_PIDL(pidl));
  1955. if (IsURLChild(pidl, TRUE) || _IsFilePidl(pidl))
  1956. {
  1957. hr = IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szUrl, SIZECHARS(szUrl), NULL);
  1958. if (SUCCEEDED(hr))
  1959. {
  1960. // Was the pidl pointing to "The Internet"?
  1961. if (0 == StrCmpN(szUrl, SZ_THEINTERNET_PARSENAME, (ARRAYSIZE(SZ_THEINTERNET_PARSENAME) - 1)))
  1962. {
  1963. // Yes, so we don't want the SHGDN_FORPARSING name
  1964. // because the user doesn't know what the heck it is. Since we
  1965. // navigate to the home page, let's display that.
  1966. hr = IEGetNameAndFlags(pidl, SHGDN_NORMAL, szUrl, SIZECHARS(szUrl), NULL);
  1967. }
  1968. }
  1969. }
  1970. else
  1971. {
  1972. // hr = MutantGDNForShellUrl(pidl, szUrl, SIZECHARS(szUrl));
  1973. hr = IEGetNameAndFlags(pidl, SHGDN_NORMAL, szUrl, SIZECHARS(szUrl), NULL);
  1974. }
  1975. if (SUCCEEDED(hr))
  1976. Str_SetPtr(&m_pszURL, szUrl);
  1977. if (!m_pszURL)
  1978. hr = E_OUTOFMEMORY;
  1979. if (FAILED(hr))
  1980. Str_SetPtr(&m_pszURL, NULL); // Clear it
  1981. return hr;
  1982. }
  1983. /****************************************************\
  1984. FUNCTION: SetUrl
  1985. PARAMETERS
  1986. szUrlOut (Out) - Url
  1987. DESCRIPTION:
  1988. Set the ShellUrl from a string that is parsible from
  1989. the root (desktop) ISF. This is normally used for
  1990. File Paths.
  1991. \****************************************************/
  1992. HRESULT CShellUrl::SetUrl(LPCTSTR pcszUrlIn, DWORD dwGenType)
  1993. {
  1994. Reset(); // External Calls to this will reset the entire CShellUrl.
  1995. return _SetUrl(pcszUrlIn, dwGenType);
  1996. }
  1997. /****************************************************\
  1998. FUNCTION: _SetUrl
  1999. PARAMETERS
  2000. pcszUrlIn (In) - The string URL for this CShellUrl
  2001. dwGenType (In) - Method to use when generating the PIDL
  2002. from pcszUrlIn.
  2003. DESCRIPTION:
  2004. This function will reset the m_pszURL member
  2005. variable without modifying m_pidl. This is only used
  2006. internally, and callers that want to reset the entire
  2007. CShellUrl to an URL should call the public method
  2008. SetUrl().
  2009. \****************************************************/
  2010. HRESULT CShellUrl::_SetUrl(LPCTSTR pcszUrlIn, DWORD dwGenType)
  2011. {
  2012. m_dwGenType = dwGenType;
  2013. return Str_SetPtr(&m_pszURL, pcszUrlIn) ? S_OK : E_OUTOFMEMORY;
  2014. }
  2015. /****************************************************\
  2016. FUNCTION: GetDisplayName
  2017. PARAMETERS
  2018. pszUrlOut (Out) - Get the Shell Url in String Form.
  2019. cchUrlOutSize (In) - Size of String Buffer Passed in.
  2020. DESCRIPTION:
  2021. This function will Fill in pszUrlOut with nice
  2022. versions of the Shell Url that can be displayed in
  2023. the AddressBar or in the Titles of windows.
  2024. \****************************************************/
  2025. HRESULT CShellUrl::GetDisplayName(LPTSTR pszUrlOut, DWORD cchUrlOutSize)
  2026. {
  2027. HRESULT hr = S_OK;
  2028. if (!m_pszDisplayName)
  2029. {
  2030. if (m_pidl)
  2031. {
  2032. LPITEMIDLIST pidl = NULL;
  2033. hr = GetPidl(&pidl);
  2034. if (SUCCEEDED(hr))
  2035. {
  2036. hr = _GenDispNameFromPidl(pidl, NULL);
  2037. ILFree(pidl);
  2038. }
  2039. }
  2040. else if (m_pszURL)
  2041. {
  2042. // In this case, we will just give back the URL.
  2043. Str_SetPtr(&m_pszDisplayName, m_pszURL);
  2044. if (NULL == m_pszDisplayName)
  2045. hr = E_OUTOFMEMORY;
  2046. }
  2047. else
  2048. {
  2049. hr = E_FAIL;
  2050. }
  2051. }
  2052. if (SUCCEEDED(hr) && pszUrlOut && m_pszDisplayName)
  2053. StringCchCopy(pszUrlOut, cchUrlOutSize, m_pszDisplayName);
  2054. return hr;
  2055. }
  2056. /****************************************************\
  2057. FUNCTION: _GenDispNameFromPidl
  2058. PARAMETERS
  2059. pidl (In) - This will be used to generate the Display Name.
  2060. pcszArgs (In) - These will be added to the end of the Display Name
  2061. DESCRIPTION:
  2062. This function will generate the Display Name
  2063. from the pidl and pcszArgs parameters. This is
  2064. normally not needed when this CShellUrl was parsed
  2065. from an outside source, because the Display Name
  2066. was generated at that time.
  2067. \****************************************************/
  2068. HRESULT CShellUrl::_GenDispNameFromPidl(LPCITEMIDLIST pidl, LPCTSTR pcszArgs)
  2069. {
  2070. HRESULT hr;
  2071. TCHAR szDispName[MAX_URL_STRING];
  2072. hr = GetUrl(szDispName, SIZECHARS(szDispName));
  2073. if (SUCCEEDED(hr))
  2074. {
  2075. if (pcszArgs)
  2076. StringCchCat(szDispName, ARRAYSIZE(szDispName), pcszArgs);
  2077. PathMakePretty(szDispName);
  2078. hr = Str_SetPtr(&m_pszDisplayName, szDispName) ? S_OK : E_OUTOFMEMORY;
  2079. }
  2080. return hr;
  2081. }
  2082. /****************************************************\
  2083. FUNCTION: GetArgs
  2084. PARAMETERS
  2085. pszArgsOut - The arguments to the Shell Url. (Only
  2086. for ShellExec().
  2087. cchArgsOutSize - Size of pszArgsOut in chars.
  2088. DESCRIPTION:
  2089. Get the arguments that will be passed to
  2090. ShellExec() if 1) the Pidl is navigated to, 2) it's
  2091. a File URL, and 3) it's not navigatable.
  2092. \****************************************************/
  2093. HRESULT CShellUrl::GetArgs(LPTSTR pszArgsOut, DWORD cchArgsOutSize)
  2094. {
  2095. ASSERT(pszArgsOut);
  2096. if (m_pszArgs)
  2097. StringCchCopy(pszArgsOut, cchArgsOutSize, m_pszArgs);
  2098. else
  2099. *pszArgsOut = 0;
  2100. TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: GetArgs() pszArgsOut=%s", pszArgsOut);
  2101. return S_OK;
  2102. }
  2103. /****************************************************\
  2104. FUNCTION: SetDefaultShellPath
  2105. PARAMETERS
  2106. psu - CShellUrl to set path.
  2107. DESCRIPTION:
  2108. "Desktop";"Desktop/My Computer" is the
  2109. most frequently used Shell Path for parsing. This
  2110. function will add those two items to the CShellUrl
  2111. passed in the paramter.
  2112. \****************************************************/
  2113. HRESULT SetDefaultShellPath(CShellUrl * psu)
  2114. {
  2115. ASSERT(psu);
  2116. LPITEMIDLIST pidl;
  2117. // We need to set the "Shell Path" which will allow
  2118. // the user to enter Display Names of items in Shell
  2119. // Folders that are frequently used. We add "Desktop"
  2120. // and "Desktop/My Computer" to the Shell Path because
  2121. // that is what users use most often.
  2122. // _pshuUrl will free pshuPath, so we can't.
  2123. psu->AddPath(&s_idlNULL);
  2124. SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidl); // Get Pidl for "My Computer"
  2125. if (pidl)
  2126. {
  2127. // psu will free pshuPath, so we can't.
  2128. psu->AddPath(pidl);
  2129. ILFree(pidl);
  2130. }
  2131. // Add favorites folder too
  2132. SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
  2133. if (pidl)
  2134. {
  2135. // psu will free pshuPath, so we can't.
  2136. psu->AddPath(pidl);
  2137. ILFree(pidl);
  2138. }
  2139. return S_OK;
  2140. }
  2141. void CShellUrl::SetMessageBoxParent(HWND hwnd)
  2142. {
  2143. // Find the topmost window so that the messagebox disables
  2144. // the entire frame
  2145. HWND hwndTopmost = NULL;
  2146. while (hwnd)
  2147. {
  2148. hwndTopmost = hwnd;
  2149. hwnd = GetParent(hwnd);
  2150. }
  2151. m_hwnd = hwndTopmost;
  2152. };