Source code of Windows XP (NT5)
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.

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