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.

2024 lines
62 KiB

  1. /*
  2. * isurl.cpp - IUniformResourceLocator implementation for Intshcut class.
  3. */
  4. #include "priv.h"
  5. #include "ishcut.h"
  6. #include "urlprop.h"
  7. #include "assocurl.h"
  8. #include "shlwapi.h"
  9. #include "infotip.h"
  10. #include "resource.h"
  11. #include <intshctp.h>
  12. #include <mluisupp.h>
  13. #define DM_PLUGGABLE DM_TRACE
  14. #define DM_SHELLEXECOBJECT 0x80000000
  15. extern HRESULT CreateTargetFrame(LPCOLESTR pszTargetName, LPUNKNOWN /*IN,OUT*/ *ppunk);
  16. const TCHAR c_szDefaultVerbSubKeyFmt[] = TEXT("%s\\Shell");
  17. const TCHAR c_szAppCmdLineFmt[] = TEXT(" %s");
  18. const TCHAR c_szQuotesAppCmdLineFmt[] = TEXT(" \"%s\"");
  19. /***************************** Private Functions *****************************/
  20. /* input flags to MyExecute() */
  21. typedef enum myexecute_in_flags
  22. {
  23. /*
  24. * Adds double quotes around the given argument string on the generated
  25. * command line if the argument string contains any white space.
  26. */
  27. ME_IFL_QUOTE_ARGS = 0x0001,
  28. /* flag combinations */
  29. ALL_ME_IN_FLAGS = ME_IFL_QUOTE_ARGS
  30. }
  31. MYEXECUTE_IN_FLAGS;
  32. /*----------------------------------------------------------
  33. Purpose: Calls CreateProcess() politely
  34. Returns:
  35. Cond: --
  36. */
  37. HRESULT
  38. MyExecute(
  39. LPCTSTR pcszApp,
  40. LPCTSTR pcszArgs,
  41. DWORD dwInFlags)
  42. {
  43. HRESULT hr;
  44. TCHAR szFullApp[MAX_PATH];
  45. ASSERT(IS_VALID_STRING_PTR(pcszApp, -1));
  46. ASSERT(IS_VALID_STRING_PTR(pcszArgs, -1));
  47. ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_ME_IN_FLAGS));
  48. hr = PathSearchAndQualify(pcszApp, szFullApp, SIZECHARS(szFullApp));
  49. if (hr == S_OK)
  50. {
  51. DWORD cbSize;
  52. LPTSTR pszCmdLine;
  53. // (+ 1) for null terminator.
  54. cbSize = max(SIZEOF(c_szAppCmdLineFmt),
  55. SIZEOF(c_szQuotesAppCmdLineFmt)) +
  56. + CbFromCch(lstrlen(szFullApp) + lstrlen(pcszArgs) + 1);
  57. pszCmdLine = (LPTSTR)LocalAlloc(LPTR, cbSize);
  58. if (pszCmdLine)
  59. {
  60. LPCTSTR pcszFmt;
  61. STARTUPINFO si;
  62. PROCESS_INFORMATION pi;
  63. // Execute URL via one-shot app.
  64. pcszFmt = (IsFlagSet(dwInFlags, ME_IFL_QUOTE_ARGS) &&
  65. StrPBrk(pcszArgs, TEXT(" \t")) != NULL)
  66. ? c_szQuotesAppCmdLineFmt : c_szAppCmdLineFmt;
  67. wnsprintf(pszCmdLine, cbSize / sizeof(TCHAR), pcszFmt, pcszArgs);
  68. ZeroMemory(&si, SIZEOF(si));
  69. si.cb = SIZEOF(si);
  70. // Specify command line exactly as given to app.
  71. if (CreateProcess(szFullApp, pszCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
  72. {
  73. CloseHandle(pi.hProcess);
  74. CloseHandle(pi.hThread);
  75. hr = S_OK;
  76. TraceMsg(TF_INTSHCUT, "MyExecute(): CreateProcess() \"%s\" succeeded.", pszCmdLine);
  77. }
  78. else
  79. {
  80. hr = E_FAIL;
  81. TraceMsg(TF_WARNING, "MyExecute(): CreateProcess() \"%s\" failed.", pszCmdLine);
  82. }
  83. LocalFree(pszCmdLine);
  84. pszCmdLine = NULL;
  85. }
  86. else
  87. {
  88. hr = E_OUTOFMEMORY;
  89. }
  90. }
  91. else
  92. {
  93. TraceMsg(TF_WARNING, "MyExecute(): Unable to find app %s.", pcszApp);
  94. }
  95. return(hr);
  96. }
  97. /*----------------------------------------------------------
  98. Purpose: Returns TRUE if the given internet shortcut points
  99. to a website (as opposed to an ftp site, etc).
  100. Returns: see above
  101. */
  102. BOOL IsWebsite(IN Intshcut * pintshcut)
  103. {
  104. ASSERT(pintshcut);
  105. // (scotth): we are assuming that file: schemes are
  106. // generally web pages. This is not true. For file: schemes,
  107. // we should first verify that it is an htm filetype.)
  108. return (URL_SCHEME_HTTP == pintshcut->GetScheme() ||
  109. URL_SCHEME_FILE == pintshcut->GetScheme());
  110. }
  111. BOOL
  112. GetClassDefaultVerb(
  113. LPCTSTR pcszClass,
  114. LPTSTR pszDefaultVerbBuf,
  115. UINT cchBufLen)
  116. {
  117. // No; get the default verb
  118. TCHAR szKey[MAX_PATH];
  119. StrCpyN(szKey, pcszClass, SIZECHARS(szKey));
  120. StrCatBuff(szKey, TEXT("\\"), SIZECHARS(szKey));
  121. StrCatBuff(szKey, TEXT("shell"), SIZECHARS(szKey));
  122. DWORD cbSize = CbFromCch(cchBufLen);
  123. if (NO_ERROR != SHGetValue(HKEY_CLASSES_ROOT, szKey, NULL, NULL, pszDefaultVerbBuf, &cbSize)
  124. || !*pszDefaultVerbBuf)
  125. {
  126. // Default to "open" if the registry doesn't specify one
  127. StrCpyN(pszDefaultVerbBuf, TEXT("open"), cchBufLen);
  128. }
  129. return TRUE;
  130. }
  131. #ifdef DEBUG
  132. BOOL
  133. IsValidPCPARSEDURL(
  134. LPCTSTR pcszURL,
  135. PCPARSEDURL pcpu)
  136. {
  137. return(IS_VALID_READ_PTR(pcpu, CPARSEDURL) &&
  138. (IS_VALID_STRING_PTR(pcpu->pszProtocol, -1) &&
  139. EVAL(IsStringContained(pcszURL, pcpu->pszProtocol)) &&
  140. EVAL(pcpu->cchProtocol < (UINT)lstrlen(pcpu->pszProtocol))) &&
  141. (IS_VALID_STRING_PTR(pcpu->pszSuffix, -1) &&
  142. EVAL(IsStringContained(pcszURL, pcpu->pszSuffix)) &&
  143. EVAL(pcpu->cchSuffix <= (UINT)lstrlen(pcpu->pszSuffix))) &&
  144. EVAL(pcpu->cchProtocol + pcpu->cchSuffix < (UINT)lstrlen(pcszURL)));
  145. }
  146. BOOL
  147. IsValidPCURLINVOKECOMMANDINFO(
  148. PCURLINVOKECOMMANDINFO pcurlici)
  149. {
  150. return(IS_VALID_READ_PTR(pcurlici, CURLINVOKECOMMANDINFO) &&
  151. EVAL(pcurlici->dwcbSize >= SIZEOF(*pcurlici)) &&
  152. FLAGS_ARE_VALID(pcurlici->dwFlags, ALL_IURL_INVOKECOMMAND_FLAGS) &&
  153. (IsFlagClear(pcurlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI) ||
  154. NULL == pcurlici->hwndParent ||
  155. IS_VALID_HANDLE(pcurlici->hwndParent, WND)) &&
  156. (IsFlagSet(pcurlici->dwFlags, IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB) ||
  157. IS_VALID_STRING_PTR(pcurlici->pcszVerb, -1)));
  158. }
  159. #endif
  160. BOOL IsValidProtocolChar(TCHAR ch)
  161. {
  162. if ((ch>=TEXT('a') && ch<=TEXT('z')) ||
  163. (ch>=TEXT('A') && ch<=TEXT('Z')) ||
  164. (ch>=TEXT('0') && ch<=TEXT('9')) ||
  165. (ch == TEXT('+')) ||
  166. (ch == TEXT('-')) ||
  167. (ch == TEXT('.')) )
  168. {
  169. return TRUE;
  170. }
  171. return FALSE;
  172. }
  173. /********************************** Methods **********************************/
  174. typedef struct
  175. {
  176. UINT idsVerb;
  177. UINT idsMenuHelp;
  178. LPCTSTR pszVerb;
  179. } ISCM;
  180. const static ISCM g_rgiscm[] =
  181. {
  182. { IDS_MENUOPEN, IDS_MH_OPEN, TEXT("open") }, // IDCMD_ISCM_OPEN
  183. { IDS_SYNCHRONIZE, IDS_MH_SYNCHRONIZE, TEXT("update now")}, // IDCMD_ISCM_SYNC
  184. { IDS_MAKE_OFFLINE, IDS_MH_MAKE_OFFLINE, TEXT("subscribe")}, // IDCMD_ISCM_SUB
  185. };
  186. // WARNING - these must match their index into g_rgiscm
  187. #define IDCMD_ISCM_OPEN 0
  188. #define IDCMD_ISCM_SYNC 1
  189. #define IDCMD_ISCM_SUB 2
  190. BOOL _IsSubscribed(LPCWSTR pszUrl, BOOL *pfSubscribable)
  191. {
  192. BOOL fRet = FALSE;
  193. ISubscriptionMgr * pMgr;
  194. *pfSubscribable = FALSE;
  195. if (SUCCEEDED(CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ISubscriptionMgr, &pMgr))))
  196. {
  197. pMgr->IsSubscribed(pszUrl, &fRet);
  198. pMgr->Release();
  199. }
  200. if (!fRet)
  201. {
  202. //test if we CAN subscribe to this thing
  203. if (!SHRestricted2W(REST_NoAddingSubscriptions, pszUrl, 0) &&
  204. IsFeaturePotentiallyAvailable(CLSID_SubscriptionMgr))
  205. {
  206. *pfSubscribable = IsSubscribableW(pszUrl);
  207. }
  208. }
  209. else
  210. *pfSubscribable = TRUE;
  211. return fRet;
  212. }
  213. void _InsertISCM(UINT indexISCM, HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT uFlags)
  214. {
  215. TCHAR szMenu[CCH_MENUMAX];
  216. uFlags |= MF_BYPOSITION | MF_STRING;
  217. MLLoadShellLangString(g_rgiscm[indexISCM].idsVerb, szMenu, SIZECHARS(szMenu));
  218. InsertMenu_PrivateNoMungeW(hmenu, indexMenu, uFlags, idCmdFirst + indexISCM, szMenu);
  219. }
  220. // IContextMenu::QueryContextMenu handler for Intshcut
  221. // The context menu handler adds the open verb for .url
  222. // files. This is because we remove the shell\open\command
  223. // key in Nashville for this file type.
  224. STDMETHODIMP Intshcut::QueryContextMenu(
  225. IN HMENU hmenu,
  226. IN UINT indexMenu,
  227. IN UINT idCmdFirst,
  228. IN UINT idCmdLast,
  229. IN UINT uFlags)
  230. {
  231. //
  232. // LEGACY - .URL files have to maintain an open verb in the registry - ZekeL - 14-APR-99
  233. // we would like to just use the "open" verb here in the context menu extension,
  234. // but we need to not duplicate the open verb that is added by DefCM
  235. // on NT5+ shell32 we disable that verb so we can add it here.
  236. // on earlier shell32 we want to add "open" any time we arent
  237. // initialized by DefCM. if we think that DefCM added us,
  238. // then we go ahead and allow the DefCM's open from the registry.
  239. //
  240. if (!m_fProbablyDefCM || GetUIVersion() >= 5)
  241. {
  242. _InsertISCM(IDCMD_ISCM_OPEN, hmenu, indexMenu, idCmdFirst, 0);
  243. if (-1 == GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0))
  244. SetMenuDefaultItem(hmenu, indexMenu, MF_BYPOSITION);
  245. indexMenu++;
  246. }
  247. #ifndef UNIX
  248. /* v-sriran: 12/8/97
  249. * disabling the context menu item for subscribe, separators etc.
  250. * because we are not supporting subscriptions right now
  251. */
  252. // skip this if we only want default or if there is no room for more.
  253. if (!(uFlags & CMF_DEFAULTONLY) && (idCmdLast - idCmdFirst >= ARRAYSIZE(g_rgiscm)))
  254. {
  255. WCHAR *pwszURL;
  256. if (SUCCEEDED(GetURLW(&pwszURL)))
  257. {
  258. BOOL bSubscribable = FALSE; //can be subscribed to
  259. BOOL bSub = _IsSubscribed(pwszURL, &bSubscribable);
  260. m_bCheckForDelete = bSub && m_pszFile;
  261. if (bSubscribable || bSub)
  262. {
  263. // add a separator for our subscription stuff
  264. InsertMenu(hmenu, indexMenu++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  265. UINT uMenuFlags = 0;
  266. if (bSub)
  267. {
  268. uMenuFlags |= MF_CHECKED;
  269. if (SHRestricted2W(REST_NoRemovingSubscriptions, pwszURL, 0))
  270. {
  271. uMenuFlags |= MF_GRAYED;
  272. }
  273. }
  274. _InsertISCM(IDCMD_ISCM_SUB, hmenu, indexMenu++, idCmdFirst, uMenuFlags);
  275. if (bSub)
  276. {
  277. uMenuFlags = 0;
  278. if (SHRestricted2W(REST_NoManualUpdates, NULL, 0))
  279. {
  280. uMenuFlags |= MF_GRAYED;
  281. }
  282. _InsertISCM(IDCMD_ISCM_SYNC, hmenu, indexMenu++, idCmdFirst, uMenuFlags);
  283. }
  284. }
  285. SHFree(pwszURL);
  286. }
  287. }
  288. #endif /* UNIX */
  289. return ResultFromShort(ARRAYSIZE(g_rgiscm));
  290. }
  291. STDMETHODIMP Intshcut::InvokeCommand(IN LPCMINVOKECOMMANDINFO pici)
  292. {
  293. HRESULT hres = E_INVALIDARG;
  294. ASSERT(pici);
  295. if (pici && SIZEOF(*pici) <= pici->cbSize)
  296. {
  297. UINT idCmd;
  298. if (0 == HIWORD(pici->lpVerb)) // Is the ID cmd given?
  299. {
  300. idCmd = LOWORD(pici->lpVerb); // Yes
  301. // Old versions of ShellExec() didnt get the right default command - Zekel - 15-MAR-99
  302. // since our QCM implementation doesnt add anything to the menu
  303. // if we fix the QCM to work correctly, then this problem will go away.
  304. // it sent 0xfffe instead. so just adjust here.
  305. if (idCmd == 0xfffe && GetUIVersion() <= 4)
  306. idCmd = IDCMD_ISCM_OPEN;
  307. }
  308. else
  309. {
  310. // No; a language-independent verb was supplied
  311. int i;
  312. LPCTSTR pszVerb;
  313. LPCMINVOKECOMMANDINFOEX piciex = (LPCMINVOKECOMMANDINFOEX)pici;
  314. ASSERT(SIZEOF(*piciex) <= piciex->cbSize);
  315. WCHAR szVerb[40];
  316. if (piciex->lpVerbW)
  317. {
  318. pszVerb = piciex->lpVerbW;
  319. }
  320. else
  321. {
  322. if (piciex->lpVerb)
  323. {
  324. ASSERT(lstrlenA(piciex->lpVerb) < ARRAYSIZE(szVerb));
  325. SHAnsiToUnicode(piciex->lpVerb, szVerb, ARRAYSIZE(szVerb));
  326. }
  327. else
  328. {
  329. szVerb[0] = L'\0';
  330. }
  331. pszVerb = szVerb;
  332. }
  333. idCmd = (UINT)-1;
  334. for (i = 0; i < ARRAYSIZE(g_rgiscm); i++)
  335. {
  336. if (0 == StrCmpI(g_rgiscm[i].pszVerb, pszVerb))
  337. {
  338. idCmd = i;
  339. break;
  340. }
  341. }
  342. }
  343. switch (idCmd)
  344. {
  345. case IDCMD_ISCM_OPEN:
  346. {
  347. URLINVOKECOMMANDINFO urlici;
  348. urlici.dwcbSize = SIZEOF(urlici);
  349. urlici.hwndParent = pici->hwnd;
  350. urlici.pcszVerb = NULL;
  351. urlici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB;
  352. if (IsFlagClear(pici->fMask, CMIC_MASK_FLAG_NO_UI))
  353. {
  354. SetFlag(urlici.dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI);
  355. }
  356. if (IsFlagSet(pici->fMask, SEE_MASK_FLAG_DDEWAIT))
  357. {
  358. SetFlag(urlici.dwFlags, IURL_INVOKECOMMAND_FL_DDEWAIT);
  359. }
  360. hres = InvokeCommand(&urlici);
  361. m_bCheckForDelete = FALSE;
  362. }
  363. break;
  364. case IDCMD_ISCM_SUB:
  365. case IDCMD_ISCM_SYNC:
  366. {
  367. hres = S_OK;
  368. WCHAR *pwszURL;
  369. if (SUCCEEDED(GetURLW(&pwszURL)))
  370. {
  371. ISubscriptionMgr * pMgr;
  372. if (SUCCEEDED(JITCoCreateInstance(CLSID_SubscriptionMgr,
  373. NULL,
  374. CLSCTX_INPROC_SERVER,
  375. IID_PPV_ARG(ISubscriptionMgr, &pMgr),
  376. pici->hwnd,
  377. FIEF_FLAG_FORCE_JITUI)))
  378. {
  379. if (idCmd == IDCMD_ISCM_SUB)
  380. {
  381. BOOL bSubscribed;
  382. pMgr->IsSubscribed(pwszURL, &bSubscribed);
  383. if (!bSubscribed)
  384. {
  385. SHFILEINFO sfi = {0};
  386. WCHAR wszName[MAX_PATH];
  387. wszName[0] = 0;
  388. if (SHGetFileInfo(m_pszFile, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME))
  389. {
  390. SHTCharToUnicode(sfi.szDisplayName, wszName, ARRAYSIZE(wszName));
  391. }
  392. if (!wszName[0])
  393. StrCpyNW(wszName, pwszURL, ARRAYSIZE(wszName));
  394. //all subscriptions to local .urls are treated as subscribing something
  395. //that's already in Favorites, so user isn't forced to add it to their
  396. //favorites as they subscribe.
  397. if (SUCCEEDED(pMgr->CreateSubscription(pici->hwnd, pwszURL, wszName,
  398. CREATESUBS_FROMFAVORITES,
  399. SUBSTYPE_URL,
  400. NULL)))
  401. {
  402. pMgr->UpdateSubscription(pwszURL);
  403. }
  404. }
  405. else
  406. {
  407. pMgr->DeleteSubscription(pwszURL, pici->hwnd);
  408. }
  409. }
  410. else if (idCmd == IDCMD_ISCM_SYNC)
  411. {
  412. pMgr->UpdateSubscription(pwszURL);
  413. }
  414. pMgr->Release();
  415. }
  416. SHFree(pwszURL);
  417. m_bCheckForDelete = FALSE;
  418. }
  419. break;
  420. }
  421. default:
  422. hres = E_INVALIDARG;
  423. break;
  424. }
  425. }
  426. return hres;
  427. }
  428. /*----------------------------------------------------------
  429. Purpose: IContextMenu::GetCommandString handler for Intshcut
  430. */
  431. STDMETHODIMP Intshcut::GetCommandString(
  432. IN UINT_PTR idCmd,
  433. IN UINT uType,
  434. IN OUT UINT* puReserved,
  435. IN OUT LPSTR pszName,
  436. IN UINT cchMax)
  437. {
  438. HRESULT hres;
  439. TCHAR szMenu[CCH_MENUMAX];
  440. ASSERT(NULL == puReserved);
  441. ASSERT(IS_VALID_WRITE_BUFFER(pszName, char, cchMax));
  442. switch (uType)
  443. {
  444. case GCS_HELPTEXTA:
  445. case GCS_HELPTEXTW:
  446. if (idCmd < ARRAYSIZE(g_rgiscm))
  447. {
  448. MLLoadString(g_rgiscm[idCmd].idsMenuHelp, szMenu, SIZECHARS(szMenu));
  449. if (GCS_HELPTEXTA == uType)
  450. {
  451. UnicodeToAnsi(szMenu, pszName, cchMax);
  452. }
  453. else
  454. {
  455. StrCpyN((LPWSTR)pszName, szMenu, cchMax);
  456. }
  457. hres = NOERROR;
  458. }
  459. else
  460. {
  461. ASSERT(0);
  462. hres = E_INVALIDARG;
  463. }
  464. break;
  465. case GCS_VALIDATEA:
  466. case GCS_VALIDATEW:
  467. hres = idCmd < ARRAYSIZE(g_rgiscm) ? S_OK : S_FALSE;
  468. break;
  469. case GCS_VERBA:
  470. case GCS_VERBW:
  471. if (idCmd < ARRAYSIZE(g_rgiscm))
  472. {
  473. LPCTSTR pszVerb = g_rgiscm[idCmd].pszVerb;
  474. if (GCS_VERBA == uType)
  475. {
  476. UnicodeToAnsi(pszVerb, pszName, cchMax);
  477. }
  478. else
  479. {
  480. StrCpyN((LPWSTR)pszName, pszVerb, cchMax);
  481. }
  482. hres = NOERROR;
  483. }
  484. else
  485. {
  486. ASSERT(0);
  487. hres = E_INVALIDARG;
  488. }
  489. break;
  490. default:
  491. hres = E_NOTIMPL;
  492. break;
  493. }
  494. return hres;
  495. }
  496. // IContextMenu2::HandleMenuMsg handler for Intshcut
  497. STDMETHODIMP Intshcut::HandleMenuMsg(IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
  498. {
  499. return S_OK;
  500. }
  501. /*----------------------------------------------------------
  502. Purpose: Bring up UI to ask the user what to associate this
  503. URL protocol to.
  504. */
  505. STDMETHODIMP
  506. Intshcut::RegisterProtocolHandler(
  507. HWND hwndParent,
  508. LPTSTR pszAppBuf,
  509. UINT cchBuf)
  510. {
  511. HRESULT hr;
  512. DWORD dwFlags = 0;
  513. TCHAR szURL[MAX_URL_STRING];
  514. ASSERT(! hwndParent ||
  515. IS_VALID_HANDLE(hwndParent, WND));
  516. ASSERT(IS_VALID_WRITE_BUFFER(pszAppBuf, TCHAR, cchBuf));
  517. ASSERT(m_pprop);
  518. hr = InitProp();
  519. if (SUCCEEDED(hr))
  520. {
  521. hr = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
  522. ASSERT(S_OK == hr);
  523. SetFlag(dwFlags, URLASSOCDLG_FL_REGISTER_ASSOC);
  524. if (! m_pszFile)
  525. SetFlag(dwFlags, URLASSOCDLG_FL_USE_DEFAULT_NAME);
  526. hr = AssociateURL(hwndParent, dwFlags, m_pszFile, szURL, pszAppBuf, cchBuf);
  527. switch (hr)
  528. {
  529. case S_FALSE:
  530. TraceMsg(TF_INTSHCUT, "Intshcut::RegisterProtocolHandler(): One time execution of %s via %s requested.",
  531. szURL, pszAppBuf);
  532. break;
  533. case S_OK:
  534. TraceMsg(TF_INTSHCUT, "Intshcut::RegisterProtocolHandler(): Protocol handler registered for %s.",
  535. szURL);
  536. break;
  537. default:
  538. ASSERT(FAILED(hr));
  539. break;
  540. }
  541. ASSERT(! cchBuf ||
  542. (IS_VALID_STRING_PTR(pszAppBuf, -1) &&
  543. (UINT)lstrlen(pszAppBuf) < cchBuf));
  544. }
  545. return(hr);
  546. }
  547. // Returns the protocol scheme value (URL_SCHEME_*).
  548. STDMETHODIMP_(DWORD)
  549. Intshcut::GetScheme(void)
  550. {
  551. DWORD dwScheme = URL_SCHEME_UNKNOWN;
  552. if (SUCCEEDED(InitProp()))
  553. {
  554. m_pprop->GetProp(PID_IS_SCHEME, &dwScheme);
  555. }
  556. return dwScheme;
  557. }
  558. // IUniformResourceLocator::SetURL handler for Intshcut
  559. //
  560. // Note:
  561. // 1. SetURL clears the IDList, so that when we launch this shortcut,
  562. // we will use the URL.
  563. STDMETHODIMP
  564. Intshcut::SetURL(
  565. IN LPCTSTR pszURL, OPTIONAL
  566. IN DWORD dwFlags)
  567. {
  568. HRESULT hres = E_FAIL;
  569. ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
  570. ASSERT(! pszURL ||
  571. IS_VALID_STRING_PTR(pszURL, -1));
  572. ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_IURL_SETURL_FLAGS));
  573. hres = InitProp();
  574. if (SUCCEEDED(hres))
  575. {
  576. hres = m_pprop->SetURLProp(pszURL, dwFlags);
  577. if (SUCCEEDED(hres))
  578. {
  579. // if the path was set successfully, clear the pidl.
  580. m_pprop->SetIDListProp(NULL);
  581. }
  582. }
  583. return hres;
  584. }
  585. /*----------------------------------------------------------
  586. Purpose: IUniformResourceLocatorA::SetURL handler for Intshcut
  587. Ansi version
  588. */
  589. STDMETHODIMP
  590. Intshcut::SetURL(
  591. IN LPCSTR pcszURL, OPTIONAL
  592. IN DWORD dwInFlags)
  593. {
  594. if ( !pcszURL )
  595. {
  596. return SetURL((LPCTSTR)NULL, dwInFlags);
  597. }
  598. else
  599. {
  600. WCHAR wszURL[MAX_URL_STRING];
  601. ASSERT(IS_VALID_STRING_PTRA(pcszURL, -1));
  602. AnsiToUnicode(pcszURL, wszURL, SIZECHARS(wszURL));
  603. return SetURL(wszURL, dwInFlags);
  604. }
  605. }
  606. STDMETHODIMP Intshcut::GetURLW(WCHAR **ppwsz)
  607. {
  608. LPTSTR pszURL;
  609. HRESULT hres = GetURL(&pszURL);
  610. if (S_OK == hres)
  611. {
  612. hres = SHStrDup(pszURL, ppwsz);
  613. SHFree(pszURL);
  614. }
  615. else
  616. hres = E_FAIL; // map S_FALSE to FAILED()
  617. return hres;
  618. }
  619. // IUniformResourceLocator::GetURL handler for Intshcut
  620. STDMETHODIMP Intshcut::GetURL(LPTSTR * ppszURL)
  621. {
  622. HRESULT hres;
  623. TCHAR szURL[MAX_URL_STRING];
  624. ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
  625. ASSERT(IS_VALID_WRITE_PTR(ppszURL, PTSTR));
  626. *ppszURL = NULL;
  627. hres = InitProp();
  628. if (SUCCEEDED(hres))
  629. {
  630. hres = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
  631. if (S_OK == hres)
  632. {
  633. // (+ 1) for null terminator.
  634. int cch = lstrlen(szURL) + 1;
  635. *ppszURL = (PTSTR)SHAlloc(CbFromCch(cch));
  636. if (*ppszURL)
  637. StrCpyN(*ppszURL, szURL, cch);
  638. else
  639. hres = E_OUTOFMEMORY;
  640. }
  641. }
  642. ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
  643. ASSERT((hres == S_OK &&
  644. IS_VALID_STRING_PTR(*ppszURL, -1)) ||
  645. ((hres == S_FALSE ||
  646. hres == E_OUTOFMEMORY) &&
  647. ! *ppszURL));
  648. return hres;
  649. }
  650. /*----------------------------------------------------------
  651. Purpose: IUniformResourceLocatorA::GetURL handler for Intshcut
  652. Ansi version
  653. */
  654. STDMETHODIMP Intshcut::GetURL(LPSTR * ppszURL)
  655. {
  656. HRESULT hres;
  657. TCHAR szURL[MAX_URL_STRING];
  658. ASSERT(IS_VALID_WRITE_PTR(ppszURL, PSTR));
  659. *ppszURL = NULL;
  660. hres = InitProp();
  661. if (SUCCEEDED(hres))
  662. {
  663. hres = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
  664. if (S_OK == hres)
  665. {
  666. DWORD cch = WideCharToMultiByte(CP_ACP, 0, szURL, -1, NULL, 0, NULL, NULL);
  667. *ppszURL = (LPSTR)SHAlloc(CbFromCchA(cch + 1));
  668. if (*ppszURL)
  669. UnicodeToAnsi(szURL, *ppszURL, cch);
  670. else
  671. hres = E_OUTOFMEMORY;
  672. }
  673. }
  674. return hres;
  675. }
  676. HRESULT HandlePluggableProtocol(LPCTSTR pszURL, LPCTSTR pszProtocol)
  677. {
  678. HRESULT hres = E_UNEXPECTED;
  679. HKEY hkey;
  680. TraceMsg(DM_PLUGGABLE, "HandlePluggableProtocol called");
  681. if (RegOpenKey(HKEY_CLASSES_ROOT, TEXT("PROTOCOLS\\Handler"), &hkey) == ERROR_SUCCESS) {
  682. HKEY hkeyProtocol;
  683. if (RegOpenKey(hkey, pszProtocol, &hkeyProtocol) == ERROR_SUCCESS) {
  684. TraceMsg(DM_PLUGGABLE, "HandlePluggableProtocol found %s", pszProtocol);
  685. IUnknown* punk = NULL; // CreateTargetFrame's ppunk is [IN][OUT]
  686. hres = CreateTargetFrame(NULL, &punk);
  687. if (SUCCEEDED(hres)) {
  688. IWebBrowser2* pauto;
  689. hres = punk->QueryInterface(IID_IWebBrowser2, (LPVOID*)&pauto);
  690. if (SUCCEEDED(hres))
  691. {
  692. TraceMsg(DM_PLUGGABLE, "HandlePluggableProtocol calling navigate with %s", pszURL);
  693. LBSTR::CString strUrl;
  694. LPTSTR pstrUrl = strUrl.GetBuffer( MAX_URL_STRING );
  695. if ( strUrl.GetAllocLength() < MAX_URL_STRING )
  696. {
  697. TraceMsg( TF_WARNING, "HandlePluggableProtocol() - strUrl Allocation Failed!" );
  698. strUrl.Empty();
  699. }
  700. else
  701. {
  702. SHTCharToUnicode( pszURL, pstrUrl, MAX_URL_STRING );
  703. // Let CString class own the buffer again.
  704. strUrl.ReleaseBuffer();
  705. }
  706. pauto->Navigate( strUrl, PVAREMPTY, PVAREMPTY, PVAREMPTY, PVAREMPTY );
  707. pauto->put_Visible(TRUE);
  708. pauto->Release();
  709. }
  710. punk->Release();
  711. }
  712. RegCloseKey(hkeyProtocol);
  713. } else {
  714. TraceMsg(DM_WARNING, "HandlePluggableProtocol can't find %s", pszProtocol);
  715. }
  716. RegCloseKey(hkey);
  717. } else {
  718. ASSERT(0);
  719. }
  720. return hres;
  721. }
  722. HRESULT _IEExecFile_TryRunningWindow(VARIANT *pvarIn, DWORD cid)
  723. {
  724. HRESULT hr = E_FAIL;
  725. ASSERT(pvarIn);
  726. IShellWindows *psw = WinList_GetShellWindows(TRUE);
  727. if (psw)
  728. {
  729. IUnknown *punk;
  730. if (SUCCEEDED(psw->_NewEnum(&punk)))
  731. {
  732. VARIANT var = {0};
  733. IEnumVARIANT *penum;
  734. //
  735. // its too bad _NewEnum doesnt return an penum....
  736. // this should never fail.
  737. //
  738. punk->QueryInterface(IID_PPV_ARG(IEnumVARIANT, &penum));
  739. ASSERT(penum);
  740. //
  741. // this can be super spendy since every one of these
  742. // items is marshalled.
  743. //
  744. // should we clone the stream here??
  745. //
  746. while (FAILED(hr) && S_OK == penum->Next(1, &var, NULL))
  747. {
  748. ASSERT(var.vt == VT_DISPATCH);
  749. ASSERT(var.pdispVal);
  750. IOleCommandTarget *poct;
  751. if (SUCCEEDED(var.pdispVal->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &poct))))
  752. {
  753. CoAllowSetForegroundWindow(poct, NULL);
  754. hr = poct->Exec(&CGID_Explorer, cid, 0, pvarIn, NULL);
  755. poct->Release();
  756. }
  757. // this should release the pdisp
  758. VariantClear(&var);
  759. }
  760. punk->Release();
  761. penum->Release();
  762. }
  763. psw->Release();
  764. }
  765. TraceMsgW(DM_SHELLEXECOBJECT, "IEExecFile_Running returns 0x%X", hr);
  766. return hr;
  767. }
  768. BOOL IsIESchemeHandler(LPTSTR pszVerb, LPTSTR pszScheme)
  769. {
  770. // if we fail to get any value at all, the we must assume that it
  771. // is some protocol like about: or res: that is not in the registry
  772. // so we default to success.
  773. BOOL fRet = FALSE;
  774. TCHAR szExe[MAX_PATH];
  775. if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_EXECUTABLE, pszScheme, pszVerb, szExe, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szExe)))))
  776. {
  777. // if we find something and it aint us, then fail.
  778. if ((StrStrI(szExe, TEXT("iexplore.exe")) || StrStrI(szExe, TEXT("explorer.exe"))))
  779. {
  780. fRet = TRUE;
  781. TraceMsg(DM_SHELLEXECOBJECT, "IsIEScheme() found %s", szExe);
  782. }
  783. }
  784. else
  785. {
  786. // these are unregistered schemes, we are the only ones that
  787. // should ever even use the unregistered schemes like
  788. // res: or shell: so return TRUE here too.
  789. fRet = *pszScheme && *pszScheme != TEXT('.');
  790. }
  791. TraceMsg(DM_SHELLEXECOBJECT, "IsIEScheme() returns %d for %s", fRet, pszScheme);
  792. return fRet;
  793. }
  794. HRESULT IEExecFile(LPTSTR pszVerb, LPTSTR pszScheme, DWORD cid, LPTSTR pszPath)
  795. {
  796. HRESULT hr = E_FAIL;
  797. ASSERT(pszVerb);
  798. ASSERT(pszScheme);
  799. ASSERT(pszPath);
  800. if (IsIESchemeHandler(pszVerb, pszScheme))
  801. {
  802. VARIANT varIn = {0};
  803. varIn.vt = VT_BSTR;
  804. SHSTRW str;
  805. str.SetStr(pszPath);
  806. varIn.bstrVal = SysAllocString(str.GetStr());
  807. if (varIn.bstrVal)
  808. {
  809. if (!SHRegGetBoolUSValue(REGSTR_PATH_MAIN, TEXT("AllowWindowReuse"), FALSE, TRUE)
  810. || FAILED(hr = _IEExecFile_TryRunningWindow(&varIn, cid)))
  811. {
  812. IOleCommandTarget *poct;
  813. if (SUCCEEDED(CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_ALL,
  814. IID_PPV_ARG(IOleCommandTarget, &poct))))
  815. {
  816. hr = poct->Exec(&CGID_Explorer, cid, 0, &varIn, NULL);
  817. poct->Release();
  818. }
  819. }
  820. SysFreeString(varIn.bstrVal);
  821. }
  822. }
  823. TraceMsg(DM_SHELLEXECOBJECT, "IEExecFile returns 0x%X for %s", hr, pszPath);
  824. return hr;
  825. }
  826. /*----------------------------------------------------------
  827. Purpose: IUniformResourceLocator::InvokeCommand for Intshcut
  828. Note:
  829. 1. If the internet shortcut comes with a pidl, use it to ShellExec,
  830. otherwise use the URL.
  831. */
  832. STDMETHODIMP Intshcut::InvokeCommand(PURLINVOKECOMMANDINFO purlici)
  833. {
  834. HRESULT hr = E_INVALIDARG;
  835. BOOL bExecFailedWhine = FALSE;
  836. ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
  837. ASSERT(IS_VALID_STRUCT_PTR(purlici, CURLINVOKECOMMANDINFO));
  838. if (purlici && EVAL(SIZEOF(*purlici) == purlici->dwcbSize))
  839. {
  840. //
  841. // App compat. Don't use stack space for the URL. We use up 16-bit app
  842. // stack space when we they shell exec urls.
  843. //
  844. LPWSTR pszURL = (LPWSTR)LocalAlloc(LPTR, MAX_URL_STRING * sizeof(WCHAR));
  845. if (pszURL)
  846. {
  847. hr = InitProp();
  848. if (SUCCEEDED(hr))
  849. {
  850. //
  851. // App Compat: Don't use up stack space.
  852. //
  853. LPWSTR pszT = (LPWSTR)LocalAlloc(LPTR, MAX_PATH * sizeof(WCHAR));
  854. if (pszT)
  855. {
  856. SHELLEXECUTEINFO sei = {0};
  857. LPITEMIDLIST pidl = NULL;
  858. LPTSTR pszProtocol = NULL;
  859. PARSEDURL pu;
  860. pu.nScheme = 0; // init to avoid bogus C4701 warning
  861. sei.fMask = SEE_MASK_NO_HOOKS;
  862. // check if we have a pidl for the target.
  863. hr = GetIDListInternal(&pidl);
  864. if ((hr == S_OK) && pidl)
  865. {
  866. // yse, use the pidl to ShellExec.
  867. sei.fMask |= SEE_MASK_INVOKEIDLIST;
  868. sei.lpIDList = pidl;
  869. }
  870. else
  871. {
  872. // no, get the URL and invoke class handler.
  873. hr = InitProp();
  874. if (SUCCEEDED(hr))
  875. {
  876. hr = m_pprop->GetProp(PID_IS_URL, pszURL, MAX_URL_STRING);
  877. }
  878. if (S_OK == hr)
  879. {
  880. hr = CopyURLProtocol(pszURL, &pszProtocol, &pu);
  881. if (hr == S_OK)
  882. {
  883. hr = IsProtocolRegistered(pszProtocol);
  884. if (hr == URL_E_UNREGISTERED_PROTOCOL &&
  885. IsFlagSet(purlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI))
  886. {
  887. TraceMsg(TF_INTSHCUT, "Intshcut::InvokeCommand(): Unregistered URL protocol %s. Invoking URL protocol handler association dialog.",
  888. pszProtocol);
  889. hr = RegisterProtocolHandler(purlici->hwndParent, pszT,
  890. MAX_PATH);
  891. if (FAILED(hr))
  892. hr = URL_E_UNREGISTERED_PROTOCOL;
  893. }
  894. //
  895. // I have no idea what this RegisterProtocolHandler
  896. // does (it looks too complicated). I, however, know
  897. // that we come here if the user type one of pluggable
  898. // protocol. And RegisterProtocolHandler returns E_FAIL.
  899. // (SatoNa)
  900. //
  901. if (FAILED(hr)) {
  902. if (SUCCEEDED(HandlePluggableProtocol(pszURL, pszProtocol))) {
  903. hr = S_OK;
  904. goto done;
  905. }
  906. }
  907. if (SUCCEEDED(hr))
  908. {
  909. hr = ResultFromWin32(RegOpenKeyExW(HKEY_CLASSES_ROOT, pszProtocol, 0, KEY_READ, &sei.hkeyClass));
  910. sei.fMask |= SEE_MASK_CLASSKEY;
  911. }
  912. }
  913. }
  914. }
  915. switch (hr)
  916. {
  917. case S_OK:
  918. {
  919. //
  920. // App Compat: Don't use up stack space.
  921. //
  922. LPWSTR pszVerb = (LPWSTR)LocalAlloc(LPTR, MAX_PATH * sizeof(WCHAR));
  923. if (pszVerb)
  924. {
  925. int nShowCmd;
  926. // Execute URL via registered protocol handler.
  927. if (IsFlagClear(purlici->dwFlags,
  928. IURL_INVOKECOMMAND_FL_ALLOW_UI))
  929. SetFlag(sei.fMask, SEE_MASK_FLAG_NO_UI);
  930. if (purlici->dwFlags & IURL_INVOKECOMMAND_FL_DDEWAIT)
  931. SetFlag(sei.fMask, SEE_MASK_FLAG_DDEWAIT);
  932. if (IsFlagClear(purlici->dwFlags,
  933. IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB))
  934. {
  935. sei.lpVerb = purlici->pcszVerb;
  936. }
  937. else
  938. {
  939. if (pszProtocol &&
  940. GetClassDefaultVerb(pszProtocol, pszVerb,
  941. MAX_PATH))
  942. sei.lpVerb = pszVerb;
  943. else
  944. ASSERT(! sei.lpVerb);
  945. }
  946. ASSERT(m_pprop);
  947. hr = InitProp();
  948. if (SUCCEEDED(hr))
  949. {
  950. m_pprop->GetProp(PID_IS_WORKINGDIR, pszT, MAX_PATH);
  951. m_pprop->GetProp(PID_IS_SHOWCMD, &nShowCmd); // inits to zero if not found
  952. // if we have a file try using a direct connection
  953. // to the shell to give the whole shortcut
  954. if (m_pszFile && ((IsIEDefaultBrowser()) || (_IsInFavoritesFolder())))
  955. {
  956. LPTSTR pszType = pszProtocol;
  957. if (pu.nScheme == URL_SCHEME_FILE)
  958. pszType = PathFindExtension(pszURL);
  959. hr = IEExecFile(pszVerb, pszType, SBCMDID_IESHORTCUT, m_pszFile);
  960. }
  961. else
  962. hr = E_FAIL;
  963. // if we failed to pass it to IE, then we should just default
  964. // to the old behavior
  965. if (FAILED(hr))
  966. {
  967. sei.cbSize = SIZEOF(sei);
  968. sei.hwnd = purlici->hwndParent;
  969. sei.lpFile = pszURL;
  970. sei.lpDirectory = pszT;
  971. sei.nShow = nShowCmd ? nShowCmd : SW_NORMAL;
  972. // We have to special case "file:" URLs,
  973. // because Nashville's Explorer typically handles
  974. // file: URLs via DDE, which fails for executables
  975. // (eg, "file://c:\windows\notepad.exe") and
  976. // non-hostable docs (like text files).
  977. //
  978. // So in this case, we remove the protocol class
  979. // and execute the suffix.
  980. // App Compat: Don't use up stack space.
  981. DWORD cchPath = MAX_PATH;
  982. LPWSTR pszPath = (LPWSTR)LocalAlloc(LPTR, cchPath * sizeof(WCHAR));
  983. if (pszPath)
  984. {
  985. if (IsFlagSet(sei.fMask, SEE_MASK_CLASSKEY) &&
  986. (URL_SCHEME_FILE == pu.nScheme) &&
  987. SUCCEEDED(PathCreateFromUrl(pszURL, pszPath, &cchPath, 0)))
  988. {
  989. sei.hkeyClass = NULL;
  990. ClearFlag(sei.fMask, SEE_MASK_CLASSKEY);
  991. sei.lpFile = pszPath;
  992. }
  993. if (m_pszFile && IsOS(OS_WHISTLERORGREATER))
  994. {
  995. // this is the security context
  996. // so that shellexec() can do zone checks
  997. sei.lpClass = m_pszFile;
  998. sei.fMask |= SEE_MASK_HASTITLE | SEE_MASK_HASLINKNAME;
  999. }
  1000. TraceMsg(TF_INTSHCUT, "Intshcut::InvokeCommand(): Invoking %s verb on URL %s.",
  1001. sei.lpVerb ? sei.lpVerb : TEXT("open"),
  1002. sei.lpFile);
  1003. hr = ShellExecuteEx(&sei) ? S_OK : IS_E_EXEC_FAILED;
  1004. LocalFree(pszPath);
  1005. pszPath = NULL;
  1006. }
  1007. else
  1008. {
  1009. hr = E_OUTOFMEMORY;
  1010. }
  1011. }
  1012. }
  1013. if (hr != S_OK)
  1014. TraceMsg(TF_WARNING, "Intshcut::InvokeCommand(): ShellExecuteEx() via registered protcol handler failed for %s.",
  1015. pszURL);
  1016. LocalFree(pszVerb);
  1017. pszVerb = NULL;
  1018. }
  1019. else
  1020. {
  1021. hr = E_OUTOFMEMORY;
  1022. }
  1023. break;
  1024. }
  1025. case S_FALSE:
  1026. hr = MyExecute(pszT, pszURL, 0);
  1027. switch (hr)
  1028. {
  1029. case E_FAIL:
  1030. bExecFailedWhine = TRUE;
  1031. hr = IS_E_EXEC_FAILED;
  1032. break;
  1033. default:
  1034. break;
  1035. }
  1036. break;
  1037. default:
  1038. ASSERT(FAILED(hr));
  1039. break;
  1040. }
  1041. done:
  1042. if (pszProtocol)
  1043. {
  1044. LocalFree(pszProtocol);
  1045. pszProtocol = NULL;
  1046. }
  1047. if (pidl)
  1048. ILFree(pidl);
  1049. if (sei.hkeyClass)
  1050. RegCloseKey(sei.hkeyClass);
  1051. if (FAILED(hr) &&
  1052. IsFlagSet(purlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI))
  1053. {
  1054. switch (hr)
  1055. {
  1056. case IS_E_EXEC_FAILED:
  1057. if (bExecFailedWhine)
  1058. {
  1059. ASSERT(IS_VALID_STRING_PTR(pszT, -1));
  1060. MLShellMessageBox(
  1061. purlici->hwndParent,
  1062. MAKEINTRESOURCE(IDS_IS_EXEC_FAILED),
  1063. MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
  1064. (MB_OK | MB_ICONEXCLAMATION),
  1065. pszT);
  1066. }
  1067. break;
  1068. case URL_E_INVALID_SYNTAX:
  1069. MLShellMessageBox(
  1070. purlici->hwndParent,
  1071. MAKEINTRESOURCE(IDS_IS_EXEC_INVALID_SYNTAX),
  1072. MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
  1073. (MB_OK | MB_ICONEXCLAMATION),
  1074. pszURL);
  1075. break;
  1076. case URL_E_UNREGISTERED_PROTOCOL:
  1077. {
  1078. LPTSTR pszProtocol;
  1079. if (CopyURLProtocol(pszURL, &pszProtocol, NULL) == S_OK)
  1080. {
  1081. MLShellMessageBox(
  1082. purlici->hwndParent,
  1083. MAKEINTRESOURCE(IDS_IS_EXEC_UNREGISTERED_PROTOCOL),
  1084. MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
  1085. (MB_OK | MB_ICONEXCLAMATION),
  1086. pszProtocol);
  1087. LocalFree(pszProtocol);
  1088. pszProtocol = NULL;
  1089. }
  1090. break;
  1091. }
  1092. case E_OUTOFMEMORY:
  1093. MLShellMessageBox(
  1094. purlici->hwndParent,
  1095. MAKEINTRESOURCE(IDS_IS_EXEC_OUT_OF_MEMORY),
  1096. MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
  1097. (MB_OK | MB_ICONEXCLAMATION));
  1098. break;
  1099. default:
  1100. ASSERT(hr == E_ABORT);
  1101. break;
  1102. }
  1103. }
  1104. LocalFree(pszT);
  1105. pszT = NULL;
  1106. }
  1107. else
  1108. {
  1109. hr = E_OUTOFMEMORY;
  1110. }
  1111. }
  1112. LocalFree(pszURL);
  1113. pszURL = NULL;
  1114. }
  1115. else
  1116. {
  1117. hr = E_OUTOFMEMORY;
  1118. }
  1119. }
  1120. ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
  1121. ASSERT(hr == S_OK ||
  1122. hr == E_ABORT ||
  1123. hr == E_OUTOFMEMORY ||
  1124. hr == URL_E_INVALID_SYNTAX ||
  1125. hr == URL_E_UNREGISTERED_PROTOCOL ||
  1126. hr == IS_E_EXEC_FAILED ||
  1127. hr == E_INVALIDARG);
  1128. return(hr);
  1129. }
  1130. /*----------------------------------------------------------
  1131. Purpose: IUniformResourceLocatorA::InvokeCommand for Intshcut
  1132. Ansi version
  1133. */
  1134. STDMETHODIMP
  1135. Intshcut::InvokeCommand(
  1136. IN PURLINVOKECOMMANDINFOA purlici)
  1137. {
  1138. HRESULT hres = E_INVALIDARG;
  1139. ASSERT(purlici);
  1140. ASSERT(SIZEOF(*purlici) == purlici->dwcbSize);
  1141. if (SIZEOF(*purlici) == purlici->dwcbSize)
  1142. {
  1143. URLINVOKECOMMANDINFOW ici;
  1144. ici.dwcbSize = SIZEOF(ici);
  1145. ici.dwFlags = purlici->dwFlags;
  1146. ici.hwndParent = purlici->hwndParent;
  1147. ici.pcszVerb = NULL;
  1148. if (purlici->pcszVerb)
  1149. {
  1150. //
  1151. // App compat hack.
  1152. //
  1153. // Note: use local alloc here instead of the stack since 16-bit code
  1154. // can shell exec urls and we don't want to use up their stack.
  1155. //
  1156. int cch = lstrlenA(purlici->pcszVerb) + 1;
  1157. ici.pcszVerb = (LPWSTR)LocalAlloc(LPTR, cch * sizeof(WCHAR));
  1158. if (ici.pcszVerb)
  1159. {
  1160. AnsiToUnicode(purlici->pcszVerb, (LPWSTR)ici.pcszVerb, cch);
  1161. }
  1162. }
  1163. hres = InvokeCommand(&ici);
  1164. if (ici.pcszVerb)
  1165. {
  1166. LocalFree((void*)ici.pcszVerb);
  1167. ici.pcszVerb = NULL;
  1168. }
  1169. }
  1170. return hres;
  1171. }
  1172. STDMETHODIMP Intshcut::Create(REFFMTID fmtid, const CLSID *pclsid,
  1173. DWORD grfFlags, DWORD grfMode, IPropertyStorage **pppropstg)
  1174. {
  1175. *pppropstg = NULL;
  1176. return E_NOTIMPL;
  1177. }
  1178. STDMETHODIMP Intshcut::Open(REFFMTID fmtid, DWORD grfMode, IPropertyStorage **pppropstg)
  1179. {
  1180. HRESULT hres = E_FAIL; // assume failure
  1181. *pppropstg = NULL;
  1182. if (IsEqualGUID(fmtid, FMTID_Intshcut))
  1183. {
  1184. // Create a URLProp object for this format ID
  1185. hres = CIntshcutProp_CreateInstance(NULL, IID_PPV_ARG(IPropertyStorage, pppropstg));
  1186. if (SUCCEEDED(hres))
  1187. {
  1188. // Initialize this object
  1189. IntshcutProp * pisprop = (IntshcutProp *)*pppropstg;
  1190. hres = pisprop->InitFromFile(m_pszFile);
  1191. }
  1192. }
  1193. else if (IsEqualGUID(fmtid, FMTID_InternetSite))
  1194. {
  1195. // Create a URLProp object for this format ID
  1196. hres = CIntsiteProp_CreateInstance(NULL, IID_PPV_ARG(IPropertyStorage, pppropstg));
  1197. if (SUCCEEDED(hres))
  1198. {
  1199. hres = InitProp();
  1200. if (SUCCEEDED(hres))
  1201. {
  1202. TCHAR szURL[MAX_URL_STRING];
  1203. hres = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
  1204. if (SUCCEEDED(hres))
  1205. {
  1206. IntsiteProp * pisprop = (IntsiteProp *)*pppropstg;
  1207. hres = pisprop->InitFromDB(szURL, this, FALSE);
  1208. }
  1209. }
  1210. if (FAILED(hres))
  1211. {
  1212. (*pppropstg)->Release();
  1213. *pppropstg = NULL;
  1214. }
  1215. }
  1216. }
  1217. return hres;
  1218. }
  1219. STDMETHODIMP Intshcut::Delete(REFFMTID fmtid)
  1220. {
  1221. return STG_E_ACCESSDENIED;
  1222. }
  1223. STDMETHODIMP Intshcut::Enum(OUT IEnumSTATPROPSETSTG ** ppenum)
  1224. {
  1225. *ppenum = NULL;
  1226. return E_NOTIMPL;
  1227. }
  1228. // get the required propery that indicates the item has been changed
  1229. STDAPI GetRecentlyChanged(IPropertyStorage *ppropstg, PROPID propid, LPTSTR pszBuf, DWORD cchBuf)
  1230. {
  1231. PROPVARIANT propvar;
  1232. HRESULT hres = E_FAIL; // assume not there
  1233. PROPSPEC prspec = { PRSPEC_PROPID, propid };
  1234. if (S_OK == ppropstg->ReadMultiple(1, &prspec, &propvar))
  1235. {
  1236. if ((VT_UI4 == propvar.vt) && (PIDISF_RECENTLYCHANGED & propvar.lVal))
  1237. hres = S_FALSE; // we've got it, skip this property
  1238. PropVariantClear(&propvar);
  1239. }
  1240. return hres;
  1241. }
  1242. STDAPI GetStringPropURL(IPropertyStorage *ppropstg, PROPID propid, LPTSTR pszBuf, DWORD cchBuf)
  1243. {
  1244. HRESULT hres = GetStringProp(ppropstg, propid, pszBuf, cchBuf);
  1245. if (SUCCEEDED(hres))
  1246. {
  1247. // get rid of the query string for display
  1248. if (UrlIs(pszBuf, URLIS_HASQUERY))
  1249. UrlCombine(pszBuf, TEXT("?..."), pszBuf, &cchBuf, 0);
  1250. }
  1251. return hres;
  1252. }
  1253. BOOL Intshcut::_TryLink(REFIID riid, void **ppvOut)
  1254. {
  1255. HRESULT hr = InitProp();
  1256. if (SUCCEEDED(hr) && URL_SCHEME_FILE == GetScheme())
  1257. {
  1258. // This shortcut is not in the favorites folder as far as we know
  1259. TCHAR szURL[INTERNET_MAX_URL_LENGTH];
  1260. DWORD cch = SIZECHARS(szURL);
  1261. *szURL = 0;
  1262. m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
  1263. if (*szURL && SUCCEEDED(PathCreateFromUrl(szURL, szURL, &cch, 0)))
  1264. {
  1265. if (!_punkLink)
  1266. {
  1267. hr = _CreateShellLink(szURL, &_punkLink);
  1268. }
  1269. if (_punkLink)
  1270. {
  1271. if (SUCCEEDED(_punkLink->QueryInterface(riid, ppvOut)))
  1272. return TRUE;
  1273. }
  1274. }
  1275. if (FAILED(hr))
  1276. ATOMICRELEASE(_punkLink);
  1277. }
  1278. return FALSE;
  1279. }
  1280. STDMETHODIMP Intshcut::GetInfoTip(DWORD dwFlags, WCHAR **ppwszTip)
  1281. {
  1282. HRESULT hr = E_FAIL;
  1283. IQueryInfo *pqi;
  1284. if (_TryLink(IID_PPV_ARG(IQueryInfo, &pqi)))
  1285. {
  1286. hr = pqi->GetInfoTip(dwFlags, ppwszTip);
  1287. pqi->Release();
  1288. }
  1289. if (FAILED(hr))
  1290. {
  1291. static const ITEM_PROP c_rgTitleAndURL[] = {
  1292. { &FMTID_InternetSite, PID_INTSITE_TITLE, GetStringProp, IDS_FAV_STRING },
  1293. { &FMTID_Intshcut, PID_IS_URL, GetStringPropURL, IDS_FAV_STRING },
  1294. { NULL, 0, 0, 0 },
  1295. };
  1296. hr = GetInfoTipFromStorage(SAFECAST(this, IPropertySetStorage *), c_rgTitleAndURL, ppwszTip);
  1297. }
  1298. return hr;
  1299. }
  1300. STDMETHODIMP Intshcut::GetInfoFlags(DWORD *pdwFlags)
  1301. {
  1302. *pdwFlags = 0;
  1303. #if 0
  1304. // This Function is commented out since it has not been tested.
  1305. // It can be uncommented if we provide support for providing offline cursor
  1306. // for shortucts. I think this needs updates to listview in comctl -- BharatS
  1307. LPSTR pszURL;
  1308. if (S_OK == GetURL(&pszURL))
  1309. {
  1310. BOOL fCached = UrlIsCached(pszUrl);
  1311. if (!fCached)
  1312. {
  1313. CHAR szCanonicalizedUrlA[MAX_URL_STRING];
  1314. DWORD dwLen = ARRAYSIZE(szCanonicalizedUrlA);
  1315. InternetCanonicalizeUrlA(pszURL, szCanonicalizedUrlA, &dwLen, 0);
  1316. fCached = UrlIsMappedOrInCache(szCanonicalizedUrlA);
  1317. }
  1318. if (fCached)
  1319. *pdwFlags |= QIF_CACHED;
  1320. SHFree(pszURL);
  1321. }
  1322. return S_OK;
  1323. #else
  1324. return E_NOTIMPL;
  1325. #endif
  1326. }
  1327. /*----------------------------------------------------------
  1328. IQueryCodePage:
  1329. */
  1330. STDMETHODIMP Intshcut::GetCodePage(UINT * puiCodePage)
  1331. {
  1332. HRESULT hres = E_FAIL;
  1333. *puiCodePage = 0; // NULL out the code page.
  1334. if (IsFlagSet(m_dwFlags, ISF_CODEPAGE))
  1335. {
  1336. *puiCodePage = m_uiCodePage;
  1337. hres = S_OK;
  1338. }
  1339. return hres;
  1340. }
  1341. STDMETHODIMP Intshcut::SetCodePage(UINT uiCodePage)
  1342. {
  1343. SetFlag(m_dwFlags, ISF_CODEPAGE);
  1344. m_uiCodePage = uiCodePage;
  1345. return S_OK;
  1346. }
  1347. /***************************** Exported Functions ****************************/
  1348. // This function was ported from URL.DLL. Normally, since our
  1349. // internet shortcut object has a context menu handler, we don't
  1350. // call this function.
  1351. //
  1352. // Only one thing needs this entry point: Exchange. Sigh.
  1353. //
  1354. // Instead of simply calling ShellExecuteEx to handle opening file
  1355. // attachments, they grovel thru the registry themselves. Of course,
  1356. // their code is incomplete and thinks a file-association needs to
  1357. // have an explicit \shell\open\command that works before it executes
  1358. // it. Hmm, it brings to mind a phrase, like:
  1359. //
  1360. //
  1361. //
  1362. // So, we export this API so they will work. But really the invoke
  1363. // occurs in the context menu handler for normal cases.
  1364. //
  1365. STDAPI_(void) OpenURL(HWND hwndParent, HINSTANCE hinst, LPSTR pszCmdLine, int nShowCmd)
  1366. {
  1367. HRESULT hr;
  1368. HRESULT hrCoInit;
  1369. Intshcut * pIntshcut = new Intshcut; // This must be a 0 INITed memory allocation
  1370. WCHAR wszPath[MAX_PATH];
  1371. if (!pIntshcut)
  1372. return;
  1373. hrCoInit = SHCoInitialize(); // gets called from rundll32 in browser only mode - hence we need to
  1374. // make sure that OLE has been init'ed
  1375. ASSERT(IS_VALID_HANDLE(hwndParent, WND));
  1376. ASSERT(IS_VALID_HANDLE(hinst, INSTANCE));
  1377. ASSERT(IS_VALID_STRING_PTRA(pszCmdLine, -1));
  1378. ASSERT(IsValidShowCmd(nShowCmd));
  1379. // Assume the entire command line is an Internet Shortcut file path.
  1380. TrimWhiteSpaceA(pszCmdLine);
  1381. TraceMsgA(TF_INTSHCUT, "OpenURL(): Trying to open Internet Shortcut %s.",
  1382. pszCmdLine);
  1383. #ifndef UNIX
  1384. AnsiToUnicode(pszCmdLine, wszPath, SIZECHARS(wszPath));
  1385. hr = pIntshcut->LoadFromFile(wszPath);
  1386. #else /* UNIX */
  1387. #ifndef ANSI_SHELL32_ON_UNIX
  1388. // IEUNIX : Our Shell32 calls this function with unicode command line
  1389. hr = pIntshcut->LoadFromFile((LPWSTR)pszCmdLine);
  1390. #else
  1391. hr = pIntshcut->LoadFromFile(pszCmdLine);
  1392. #endif
  1393. #endif /* !UNIX */
  1394. if (hr == S_OK)
  1395. {
  1396. URLINVOKECOMMANDINFO urlici;
  1397. urlici.dwcbSize = SIZEOF(urlici);
  1398. urlici.hwndParent = hwndParent;
  1399. urlici.pcszVerb = NULL;
  1400. urlici.dwFlags = (IURL_INVOKECOMMAND_FL_ALLOW_UI |
  1401. IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB);
  1402. hr = pIntshcut->InvokeCommand(&urlici);
  1403. }
  1404. if (hr != S_OK)
  1405. {
  1406. MLShellMessageBox(
  1407. hwndParent,
  1408. MAKEINTRESOURCE(IDS_IS_LOADFROMFILE_FAILED),
  1409. MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
  1410. (MB_OK | MB_ICONEXCLAMATION),
  1411. wszPath);
  1412. }
  1413. pIntshcut->Release();
  1414. SHCoUninitialize(hrCoInit);
  1415. }
  1416. // INamedPropertyBag Methods
  1417. //
  1418. // Reads & writes properties from a section in the shortcut ini file
  1419. const TCHAR c_szSizeSuffix[] = TEXT("__Size");
  1420. STDMETHODIMP Intshcut::WritePropertyNPB(
  1421. LPCOLESTR pszSectionNameW,
  1422. /* [in] */ LPCOLESTR pszPropNameW,
  1423. /* [out][in] */ PROPVARIANT *pVar)
  1424. {
  1425. const TCHAR *pszSectionName;
  1426. const TCHAR *pszPropName;
  1427. HRESULT hr;
  1428. if((NULL == pszSectionNameW) || (NULL == pszPropNameW) || (NULL == pVar))
  1429. {
  1430. return E_FAIL;
  1431. }
  1432. if(S_OK != _CreateTemporaryBackingFile())
  1433. {
  1434. ASSERT(NULL == m_pszTempFileName);
  1435. return E_FAIL;
  1436. }
  1437. ASSERT(m_pszTempFileName);
  1438. pszSectionName = pszSectionNameW;
  1439. pszPropName = pszPropNameW;
  1440. // Write the appropriate value in depending on the type
  1441. switch(pVar->vt)
  1442. {
  1443. // NOTE: (andrewgu) these types we also can round-trip using the same code pass as for
  1444. // unsigned types, except bharats in a codereview recommended we comment these out because
  1445. // they'll look goofy in the *.ini file (you wrote -5 but see 4294967290 junk instead).
  1446. // VT_UINT is not listed as "may appear in an OLE property set" in <wtypes.h>.
  1447. /* case VT_I1:
  1448. case VT_I2:
  1449. case VT_I4:
  1450. case VT_INT:
  1451. case VT_UINT: */
  1452. case VT_UI1:
  1453. case VT_UI2:
  1454. case VT_UI4:
  1455. hr = WriteUnsignedToFile(m_pszTempFileName, pszSectionName, pszPropName, pVar->ulVal);
  1456. break;
  1457. case VT_BSTR:
  1458. hr = WriteGenericString(m_pszTempFileName, pszSectionName, pszPropName, pVar->bstrVal);
  1459. break;
  1460. case VT_BLOB:
  1461. {
  1462. TCHAR *pszSizePropName = NULL;
  1463. int cchPropName = lstrlen(pszPropName) + ARRAYSIZE(c_szSizeSuffix) + 1;
  1464. DWORD dwAllocSize = cchPropName * sizeof(TCHAR);
  1465. pszSizePropName = (TCHAR *)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, dwAllocSize);
  1466. if(pszSizePropName)
  1467. {
  1468. DWORD dwBufferSize;
  1469. StrCpyN(pszSizePropName, pszPropName, cchPropName);
  1470. StrCatBuff(pszSizePropName, c_szSizeSuffix, cchPropName);
  1471. // OK Now - we have the name for the size
  1472. // we write it out
  1473. dwBufferSize = pVar->blob.cbSize;
  1474. hr = WriteBinaryToFile(m_pszTempFileName, pszSectionName, pszSizePropName,
  1475. (LPVOID)(&dwBufferSize), sizeof(DWORD));
  1476. if(S_OK == hr)
  1477. {
  1478. // Write out the buffer
  1479. hr = WriteBinaryToFile(m_pszTempFileName, pszSectionName, pszPropName,
  1480. (LPVOID)(pVar->blob.pBlobData), dwBufferSize);
  1481. }
  1482. LocalFree((LPVOID)pszSizePropName);
  1483. pszSizePropName = NULL;
  1484. }
  1485. else
  1486. {
  1487. hr = E_OUTOFMEMORY;
  1488. }
  1489. break;
  1490. }
  1491. default:
  1492. hr = WriteBinaryToFile(m_pszTempFileName, pszSectionName, pszPropName, (LPVOID)pVar, sizeof(PROPVARIANT));
  1493. break;
  1494. }
  1495. return hr;
  1496. }
  1497. STDMETHODIMP Intshcut::ReadPropertyNPB(
  1498. /* [in] */ LPCOLESTR pszSectionNameW,
  1499. /* [in] */ LPCOLESTR pszPropNameW,
  1500. /* [out][in] */ PROPVARIANT *pVar)
  1501. {
  1502. const TCHAR *pszSectionName;
  1503. const TCHAR *pszPropName;
  1504. TCHAR *pszFileToReadFrom;
  1505. HRESULT hr;
  1506. if((NULL == pszSectionNameW) || (NULL == pszPropNameW) || (NULL == pVar))
  1507. {
  1508. if (NULL != pVar)
  1509. pVar->vt = VT_ERROR;
  1510. return E_FAIL;
  1511. }
  1512. if(m_pszTempFileName)
  1513. {
  1514. pszFileToReadFrom = m_pszTempFileName;
  1515. }
  1516. else if(m_pszFile)
  1517. {
  1518. pszFileToReadFrom = m_pszFile;
  1519. }
  1520. else
  1521. {
  1522. pVar->vt = VT_EMPTY;
  1523. return S_FALSE;
  1524. }
  1525. pszSectionName = pszSectionNameW;
  1526. pszPropName = pszPropNameW;
  1527. switch(pVar->vt)
  1528. {
  1529. // NOTE: (andrewgu) these types we also can round-trip using the same code pass as for
  1530. // unsigned types, except bharats in a codereview recommended we comment these out because
  1531. // they'll look goofy in the *.ini file (you wrote -5 but see 4294967290 junk instead).
  1532. // VT_UINT is not listed as "may appear in an OLE property set" in <wtypes.h>.
  1533. /* case VT_I1:
  1534. case VT_I2:
  1535. case VT_I4:
  1536. case VT_INT:
  1537. case VT_UINT: */
  1538. case VT_UI1:
  1539. case VT_UI2:
  1540. case VT_UI4:
  1541. pVar->ulVal = 0;
  1542. hr = ReadUnsignedFromFile(pszFileToReadFrom, pszSectionName, pszPropName, &(pVar->ulVal));
  1543. break;
  1544. case VT_BSTR:
  1545. // It is a string
  1546. pVar->vt = VT_BSTR;
  1547. pVar->bstrVal = NULL;
  1548. hr = ReadBStrFromFile(pszFileToReadFrom, pszSectionName, pszPropName, &(pVar->bstrVal));
  1549. break;
  1550. case VT_BLOB:
  1551. {
  1552. TCHAR *pszSizePropName = NULL;
  1553. int cchPropName = lstrlen(pszPropName) + ARRAYSIZE(c_szSizeSuffix) + 1;
  1554. DWORD dwAllocSize = cchPropName * sizeof(TCHAR);
  1555. pszSizePropName = (TCHAR *)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, dwAllocSize);
  1556. if(pszSizePropName)
  1557. {
  1558. DWORD dwBufferSize;
  1559. StrCpyN(pszSizePropName, pszPropName, cchPropName);
  1560. StrCatBuff(pszSizePropName, c_szSizeSuffix, cchPropName);
  1561. // Read the Size first
  1562. hr = ReadBinaryFromFile(pszFileToReadFrom, pszSectionName, pszSizePropName,
  1563. &dwBufferSize, sizeof(DWORD));
  1564. if(S_OK == hr)
  1565. {
  1566. pVar->blob.pBlobData = (unsigned char *)CoTaskMemAlloc(dwBufferSize);
  1567. if(pVar->blob.pBlobData)
  1568. {
  1569. hr = ReadBinaryFromFile(pszFileToReadFrom, pszSectionName, pszPropName,
  1570. pVar->blob.pBlobData, dwBufferSize);
  1571. if(S_OK == hr)
  1572. {
  1573. pVar->blob.cbSize = dwBufferSize;
  1574. }
  1575. else
  1576. {
  1577. CoTaskMemFree(pVar->blob.pBlobData);
  1578. }
  1579. }
  1580. }
  1581. LocalFree(pszSizePropName);
  1582. pszSizePropName = NULL;
  1583. }
  1584. else
  1585. {
  1586. hr = E_OUTOFMEMORY;
  1587. }
  1588. break;
  1589. }
  1590. default:
  1591. {
  1592. // all else
  1593. PROPVARIANT tmpPropvar = {0};
  1594. hr = ReadBinaryFromFile(pszFileToReadFrom, pszSectionName, pszPropName, &tmpPropvar, sizeof(PROPVARIANT));
  1595. if((S_OK == hr) && (tmpPropvar.vt == pVar->vt))
  1596. {
  1597. memcpy(pVar, &tmpPropvar, sizeof(PROPVARIANT));
  1598. }
  1599. else
  1600. {
  1601. pVar->vt = VT_ERROR;
  1602. }
  1603. break;
  1604. }
  1605. }
  1606. if(hr != S_OK)
  1607. {
  1608. memset(pVar, 0, sizeof(PROPVARIANT));
  1609. pVar->vt = VT_EMPTY;
  1610. }
  1611. return hr;
  1612. }
  1613. STDMETHODIMP Intshcut::RemovePropertyNPB (
  1614. /* [in] */ LPCOLESTR pszSectionNameW,
  1615. /* [in] */ LPCOLESTR pszPropNameW)
  1616. {
  1617. const TCHAR *pszSectionName;
  1618. const TCHAR *pszPropName;
  1619. HRESULT hr;
  1620. TCHAR *pszFileToDeleteFrom;
  1621. // Return if there is no file name
  1622. if((NULL == pszSectionNameW) || (NULL == pszPropNameW))
  1623. {
  1624. return E_FAIL;
  1625. }
  1626. if(m_pszTempFileName)
  1627. {
  1628. pszFileToDeleteFrom = m_pszTempFileName;
  1629. }
  1630. else if(m_pszFile)
  1631. {
  1632. pszFileToDeleteFrom = m_pszFile;
  1633. }
  1634. else
  1635. {
  1636. return E_FAIL;
  1637. }
  1638. // Just delete the key corresponding to this property name
  1639. pszSectionName = pszSectionNameW;
  1640. pszPropName = pszPropNameW;
  1641. hr = SHDeleteIniString(pszSectionName, pszPropName, pszFileToDeleteFrom)? S_OK : E_FAIL;
  1642. return hr;
  1643. }