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

1166 lines
37 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include <msi.h>
  4. #include <msip.h>
  5. #include "lnkcon.h"
  6. #include "trayp.h" // for WMTRAY_ messages
  7. #include "util.h" // for GetIconLocationFromExt
  8. #include "ids.h"
  9. LINKPROP_DATA* Create_LinkPropData()
  10. {
  11. LINKPROP_DATA *plpd = (LINKPROP_DATA*) LocalAlloc(LPTR, sizeof(*plpd));
  12. if (plpd)
  13. {
  14. plpd->_cRef = 1;
  15. plpd->hCheckNow = CreateEvent(NULL, TRUE, FALSE, NULL);
  16. }
  17. return plpd;
  18. }
  19. LONG AddRef_LinkPropData(LINKPROP_DATA *plpd)
  20. {
  21. return plpd ? InterlockedIncrement(&plpd->_cRef) : 0;
  22. }
  23. LONG Release_LinkPropData(LINKPROP_DATA *plpd)
  24. {
  25. if (plpd)
  26. {
  27. ASSERT( 0 != plpd->_cRef );
  28. LONG cRef = InterlockedDecrement(&plpd->_cRef);
  29. if ( 0 == cRef )
  30. {
  31. if (plpd->psl)
  32. plpd->psl->Release();
  33. if (plpd->hCheckNow)
  34. {
  35. CloseHandle(plpd->hCheckNow);
  36. plpd->hCheckNow = NULL;
  37. }
  38. LocalFree(plpd);
  39. }
  40. return cRef;
  41. }
  42. return 0;
  43. }
  44. //
  45. // This string defined in shlink.c - hack to allow user to set working dir to $$
  46. // and have it map to whatever "My Documents" is mapped to.
  47. //
  48. void _UpdateLinkIcon(LINKPROP_DATA *plpd, HICON hIcon)
  49. {
  50. if (!hIcon)
  51. {
  52. hIcon = SHGetFileIcon(NULL, plpd->szFile, 0, SHGFI_LARGEICON);
  53. }
  54. if (hIcon)
  55. {
  56. ReplaceDlgIcon(plpd->hDlg, IDD_ITEMICON, hIcon);
  57. }
  58. }
  59. // put a path into an edit field, doing quoting as necessary
  60. void SetDlgItemPath(HWND hdlg, int id, LPTSTR pszPath)
  61. {
  62. PathQuoteSpaces(pszPath);
  63. SetDlgItemText(hdlg, id, pszPath);
  64. }
  65. // get a path from an edit field, unquoting as possible
  66. void GetDlgItemPath(HWND hdlg, int id, LPTSTR pszPath)
  67. {
  68. GetDlgItemText(hdlg, id, pszPath, MAX_PATH);
  69. PathRemoveBlanks(pszPath);
  70. PathUnquoteSpaces(pszPath);
  71. }
  72. const int c_iShowCmds[] = {
  73. SW_SHOWNORMAL,
  74. SW_SHOWMINNOACTIVE,
  75. SW_SHOWMAXIMIZED,
  76. };
  77. void _DisableAllChildren(HWND hwnd)
  78. {
  79. HWND hwndChild;
  80. for (hwndChild = GetWindow(hwnd, GW_CHILD); hwndChild != NULL; hwndChild = GetWindow(hwndChild, GW_HWNDNEXT))
  81. {
  82. // we don't want to disable the static text controls (makes the dlg look bad)
  83. if (!(SendMessage(hwndChild, WM_GETDLGCODE, 0, 0) & DLGC_STATIC))
  84. {
  85. EnableWindow(hwndChild, FALSE);
  86. }
  87. }
  88. }
  89. HRESULT _GetPathAndArgs(LINKPROP_DATA *plpd, LPTSTR pszPath, LPTSTR pszArgs, UINT cchArgs)
  90. {
  91. GetDlgItemText(plpd->hDlg, IDD_FILENAME, pszPath, MAX_PATH);
  92. return PathSeperateArgs(pszPath, pszArgs, cchArgs, NULL);
  93. }
  94. //
  95. // Returns fully qualified path to target of link, and # of characters
  96. // in fully qualifed path as return value
  97. //
  98. INT _GetTargetOfLink(LINKPROP_DATA *plpd, LPTSTR pszTarget)
  99. {
  100. TCHAR szFile[MAX_PATH];
  101. INT cch = 0;
  102. *pszTarget = 0;
  103. HRESULT hr = _GetPathAndArgs(plpd, szFile, NULL, 0);
  104. if (SUCCEEDED(hr))
  105. {
  106. if (szFile[0])
  107. {
  108. LPTSTR psz;
  109. TCHAR szExp[MAX_PATH];
  110. if (SHExpandEnvironmentStrings(szFile, szExp, ARRAYSIZE(szExp)))
  111. {
  112. cch = SearchPath(NULL, szExp, TEXT(".EXE"), MAX_PATH, pszTarget, &psz);
  113. }
  114. }
  115. }
  116. return cch;
  117. }
  118. //
  119. // Do checking of the .exe type in the background so the UI doesn't
  120. // get hung up while we scan. This is particularly important with
  121. // the .exe is over the network or on a floppy.
  122. //
  123. STDAPI_(DWORD) _LinkCheckThreadProc(void *pv)
  124. {
  125. LINKPROP_DATA *plpd = (LINKPROP_DATA *)pv;
  126. BOOL fCheck = TRUE, fEnable = FALSE;
  127. DebugMsg(DM_TRACE, TEXT("_LinkCheckThreadProc created and running"));
  128. while (plpd->bCheckRunInSep)
  129. {
  130. WaitForSingleObject(plpd->hCheckNow, INFINITE);
  131. ResetEvent(plpd->hCheckNow);
  132. if (plpd->bCheckRunInSep)
  133. {
  134. TCHAR szFullFile[MAX_PATH];
  135. DWORD cch = _GetTargetOfLink(plpd, szFullFile);
  136. if ((cch != 0) && (cch < ARRAYSIZE(szFullFile)))
  137. {
  138. DWORD dwBinaryType;
  139. if (PathIsUNC(szFullFile) || IsRemoteDrive(DRIVEID(szFullFile)))
  140. {
  141. // Net Path, let the user decide...
  142. fCheck = FALSE;
  143. fEnable = TRUE;
  144. }
  145. else if (GetBinaryType(szFullFile, &dwBinaryType) && (dwBinaryType == SCS_WOW_BINARY))
  146. {
  147. // 16-bit binary, let the user decide, default to same VDM
  148. fCheck = FALSE;
  149. fEnable = TRUE;
  150. }
  151. else
  152. {
  153. // 32-bit binary, or non-net path. don't enable the control
  154. fCheck = TRUE;
  155. fEnable = FALSE;
  156. }
  157. }
  158. else
  159. {
  160. // Error getting target of the link. don't enable the control
  161. fCheck = TRUE;
  162. fEnable = FALSE;
  163. }
  164. plpd->bEnableRunInSepVDM = fEnable;
  165. plpd->bRunInSepVDM = fCheck;
  166. if (plpd->hDlgAdvanced && IsWindow(plpd->hDlgAdvanced))
  167. {
  168. CheckDlgButton(plpd->hDlgAdvanced, IDD_RUNINSEPARATE, fCheck ? 1 : 0);
  169. EnableWindow(GetDlgItem(plpd->hDlgAdvanced, IDD_RUNINSEPARATE), fEnable);
  170. }
  171. }
  172. }
  173. plpd->bLinkThreadIsAlive = FALSE;
  174. Release_LinkPropData(plpd);
  175. DebugMsg(DM_TRACE, TEXT("_LinkCheckThreadProc exiting now..."));
  176. return 0;
  177. }
  178. // shut down the thread
  179. void _StopThread(LINKPROP_DATA *plpd)
  180. {
  181. if (plpd->bLinkThreadIsAlive)
  182. {
  183. plpd->bCheckRunInSep = FALSE;
  184. SetEvent(plpd->hCheckNow);
  185. }
  186. }
  187. void * _GetLinkExtraData(IShellLink* psl, DWORD dwSig)
  188. {
  189. void * pDataBlock = NULL;
  190. IShellLinkDataList *psld;
  191. if (SUCCEEDED(psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psld))))
  192. {
  193. psld->CopyDataBlock(dwSig, &pDataBlock);
  194. psld->Release();
  195. }
  196. return pDataBlock;
  197. }
  198. // Initializes the generic link dialog box.
  199. void _UpdateLinkDlg(LINKPROP_DATA *plpd, BOOL bUpdatePath)
  200. {
  201. WORD wHotkey;
  202. int i, iShowCmd;
  203. TCHAR szBuffer[MAX_PATH];
  204. TCHAR szCommand[MAX_PATH];
  205. HRESULT hr;
  206. SHFILEINFO sfi;
  207. BOOL fIsDarwinLink;
  208. // do this here so we don't slow down the loading
  209. // of other pages
  210. if (!bUpdatePath)
  211. {
  212. IPersistFile *ppf;
  213. if (SUCCEEDED(plpd->psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf))))
  214. {
  215. WCHAR wszPath[MAX_PATH];
  216. SHTCharToUnicode(plpd->szFile, wszPath, ARRAYSIZE(wszPath));
  217. hr = ppf->Load(wszPath, 0);
  218. ppf->Release();
  219. if (FAILED(hr))
  220. {
  221. LoadString(HINST_THISDLL, IDS_LINKNOTLINK, szBuffer, ARRAYSIZE(szBuffer));
  222. SetDlgItemText(plpd->hDlg, IDD_FILETYPE, szBuffer);
  223. _DisableAllChildren(plpd->hDlg);
  224. DebugMsg(DM_TRACE, TEXT("Shortcut IPersistFile::Load() failed %x"), hr);
  225. return;
  226. }
  227. }
  228. }
  229. fIsDarwinLink = SetLinkFlags(plpd->psl, 0, 0) & SLDF_HAS_DARWINID;
  230. SHGetFileInfo(plpd->szFile, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES);
  231. SetDlgItemText(plpd->hDlg, IDD_NAME, sfi.szDisplayName);
  232. // we need to check for darwin links here so that we can gray out
  233. // things that don't apply to darwin
  234. if (fIsDarwinLink)
  235. {
  236. TCHAR szAppState[MAX_PATH];
  237. DWORD cchAppState = ARRAYSIZE(szAppState);
  238. HWND hwndTargetType = GetDlgItem(plpd->hDlg, IDD_FILETYPE);
  239. // disable the children
  240. _DisableAllChildren(plpd->hDlg);
  241. // then special case the icon and the "Target type:" text
  242. _UpdateLinkIcon(plpd, NULL);
  243. LPEXP_DARWIN_LINK pDarwinData = (LPEXP_DARWIN_LINK)_GetLinkExtraData(plpd->psl, EXP_DARWIN_ID_SIG);
  244. // The second clause will see if it is a Darwin Advertisement.
  245. if (pDarwinData && (INSTALLSTATE_ADVERTISED == MsiQueryFeatureStateFromDescriptorW(pDarwinData->szwDarwinID)))
  246. {
  247. // the app is advertised (e.g. not installed), but will be faulted in on first use
  248. LoadString(HINST_THISDLL, IDS_APP_NOT_FAULTED_IN, szAppState, ARRAYSIZE(szAppState));
  249. }
  250. else
  251. {
  252. // the darwin app is installed
  253. LoadString(HINST_THISDLL, IDS_APP_FAULTED_IN, szAppState, ARRAYSIZE(szAppState));
  254. }
  255. SetWindowText(hwndTargetType, szAppState);
  256. EnableWindow(hwndTargetType, TRUE);
  257. // if we can ge the package name, put that in the Target field
  258. if (pDarwinData &&
  259. MsiGetProductInfo(pDarwinData->szwDarwinID,
  260. INSTALLPROPERTY_PRODUCTNAME,
  261. szAppState,
  262. &cchAppState) == ERROR_SUCCESS)
  263. {
  264. SetWindowText(GetDlgItem(plpd->hDlg, IDD_FILENAME), szAppState);
  265. }
  266. if (pDarwinData)
  267. {
  268. LocalFree(pDarwinData);
  269. }
  270. // we disabled everything in _DisableAllChildren, so re-enable the ones we still apply for darwin
  271. EnableWindow(GetDlgItem(plpd->hDlg, IDD_NAME), TRUE);
  272. EnableWindow(GetDlgItem(plpd->hDlg, IDD_PATH), TRUE);
  273. EnableWindow(GetDlgItem(plpd->hDlg, IDD_LINK_HOTKEY), TRUE);
  274. EnableWindow(GetDlgItem(plpd->hDlg, IDD_LINK_SHOWCMD), TRUE);
  275. EnableWindow(GetDlgItem(plpd->hDlg, IDD_LINK_DESCRIPTION), TRUE);
  276. EnableWindow(GetDlgItem(plpd->hDlg, IDC_ADVANCED), TRUE);
  277. // we skip all of the gook below if we are darwin since we only support the IDD_NAME, IDD_PATH, IDD_LINK_HOTKEY,
  278. // IDD_LINK_SHOWCMD, and IDD_LINK_DESCRIPTION fields
  279. }
  280. else
  281. {
  282. hr = plpd->psl->GetPath(szCommand, ARRAYSIZE(szCommand), NULL, SLGP_RAWPATH);
  283. if (FAILED(hr))
  284. hr = plpd->psl->GetPath(szCommand, ARRAYSIZE(szCommand), NULL, 0);
  285. if (S_OK == hr)
  286. {
  287. plpd->bIsFile = TRUE;
  288. // get type
  289. if (!SHGetFileInfo(szCommand, 0, &sfi, sizeof(sfi), SHGFI_TYPENAME))
  290. {
  291. TCHAR szExp[MAX_PATH];
  292. // Let's see if the string has expandable environment strings
  293. if (SHExpandEnvironmentStrings(szCommand, szExp, ARRAYSIZE(szExp))
  294. && lstrcmp(szCommand, szExp)) // don't hit the disk a second time if the string hasn't changed
  295. {
  296. SHGetFileInfo(szExp, 0, &sfi, sizeof(sfi), SHGFI_TYPENAME);
  297. }
  298. }
  299. SetDlgItemText(plpd->hDlg, IDD_FILETYPE, sfi.szTypeName);
  300. // location
  301. StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), szCommand);
  302. PathRemoveFileSpec(szBuffer);
  303. SetDlgItemText(plpd->hDlg, IDD_LOCATION, PathFindFileName(szBuffer));
  304. // command
  305. plpd->psl->GetArguments(szBuffer, ARRAYSIZE(szBuffer));
  306. PathComposeWithArgs(szCommand, szBuffer);
  307. GetDlgItemText(plpd->hDlg, IDD_FILENAME, szBuffer, ARRAYSIZE(szBuffer));
  308. // Conditionally change to prevent "Apply" button from enabling
  309. if (lstrcmp(szCommand, szBuffer) != 0)
  310. SetDlgItemText(plpd->hDlg, IDD_FILENAME, szCommand);
  311. }
  312. else
  313. {
  314. LPITEMIDLIST pidl;
  315. plpd->bIsFile = FALSE;
  316. EnableWindow(GetDlgItem(plpd->hDlg, IDD_FILENAME), FALSE);
  317. EnableWindow(GetDlgItem(plpd->hDlg, IDD_PATH), FALSE);
  318. plpd->psl->GetIDList(&pidl);
  319. if (pidl)
  320. {
  321. SHGetNameAndFlags(pidl, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szCommand, SIZECHARS(szCommand), NULL);
  322. ILRemoveLastID(pidl);
  323. SHGetNameAndFlags(pidl, SHGDN_NORMAL, szBuffer, SIZECHARS(szBuffer), NULL);
  324. ILFree(pidl);
  325. SetDlgItemText(plpd->hDlg, IDD_LOCATION, szBuffer);
  326. SetDlgItemText(plpd->hDlg, IDD_FILETYPE, szCommand);
  327. SetDlgItemText(plpd->hDlg, IDD_FILENAME, szCommand);
  328. }
  329. }
  330. }
  331. if (bUpdatePath)
  332. {
  333. return;
  334. }
  335. plpd->psl->GetWorkingDirectory(szBuffer, ARRAYSIZE(szBuffer));
  336. SetDlgItemPath(plpd->hDlg, IDD_PATH, szBuffer);
  337. plpd->psl->GetDescription(szBuffer, ARRAYSIZE(szBuffer));
  338. SHLoadIndirectString(szBuffer, szBuffer, ARRAYSIZE(szBuffer), NULL); // will do nothing if the string isn't indirect
  339. SetDlgItemText(plpd->hDlg, IDD_LINK_DESCRIPTION, szBuffer);
  340. plpd->psl->GetHotkey(&wHotkey);
  341. SendDlgItemMessage(plpd->hDlg, IDD_LINK_HOTKEY, HKM_SETHOTKEY, wHotkey, 0);
  342. //
  343. // Now initialize the Run SHOW Command combo box
  344. //
  345. for (iShowCmd = IDS_RUN_NORMAL; iShowCmd <= IDS_RUN_MAXIMIZED; iShowCmd++)
  346. {
  347. LoadString(HINST_THISDLL, iShowCmd, szBuffer, ARRAYSIZE(szBuffer));
  348. SendDlgItemMessage(plpd->hDlg, IDD_LINK_SHOWCMD, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)szBuffer);
  349. }
  350. // Now setup the Show Command - Need to map to index numbers...
  351. plpd->psl->GetShowCmd(&iShowCmd);
  352. for (i = 0; i < ARRAYSIZE(c_iShowCmds); i++)
  353. {
  354. if (c_iShowCmds[i] == iShowCmd)
  355. break;
  356. }
  357. if (i == ARRAYSIZE(c_iShowCmds))
  358. {
  359. ASSERT(0); // bogus link show cmd
  360. i = 0; // SW_SHOWNORMAL
  361. }
  362. SendDlgItemMessage(plpd->hDlg, IDD_LINK_SHOWCMD, CB_SETCURSEL, i, 0);
  363. // the icon
  364. _UpdateLinkIcon(plpd, NULL);
  365. }
  366. //
  367. // Opens a folder window with the target of the link selected
  368. //
  369. void _FindTarget(LINKPROP_DATA *plpd)
  370. {
  371. if (plpd->psl->Resolve(plpd->hDlg, 0) == S_OK)
  372. {
  373. LPITEMIDLIST pidl;
  374. _UpdateLinkDlg(plpd, TRUE);
  375. plpd->psl->GetIDList(&pidl);
  376. if (pidl)
  377. {
  378. SHOpenFolderAndSelectItems(pidl, 0, NULL, 0);
  379. ILFree(pidl);
  380. }
  381. }
  382. }
  383. // let the user pick a new icon for a link...
  384. BOOL _DoPickIcon(LINKPROP_DATA *plpd)
  385. {
  386. int iIconIndex;
  387. SHFILEINFO sfi;
  388. TCHAR * const pszIconPath = sfi.szDisplayName; // pszIconPath is just an alias for szDisplayName
  389. IShellLinkDataList *psldl;
  390. EXP_SZ_LINK *esli;
  391. HRESULT hr;
  392. *pszIconPath = 0;
  393. //
  394. // if the user has picked a icon before use it.
  395. //
  396. if (plpd->szIconPath[0] != 0 && plpd->iIconIndex >= 0)
  397. {
  398. StringCchCopy(pszIconPath, ARRAYSIZE(sfi.szDisplayName), plpd->szIconPath);
  399. iIconIndex = plpd->iIconIndex;
  400. }
  401. else
  402. {
  403. //
  404. // if this link has a icon use that.
  405. //
  406. plpd->psl->GetIconLocation(pszIconPath, MAX_PATH, &iIconIndex);
  407. //
  408. // check for an escaped version, if its there, use that
  409. //
  410. if (SUCCEEDED(hr = plpd->psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psldl))))
  411. {
  412. if (SUCCEEDED(hr = psldl->CopyDataBlock(EXP_SZ_ICON_SIG, (void **)&esli)))
  413. {
  414. ASSERT(esli);
  415. StringCchCopy(pszIconPath, MAX_PATH, esli->swzTarget);
  416. LocalFree(esli);
  417. }
  418. psldl->Release();
  419. }
  420. if (pszIconPath[0] == TEXT('.'))
  421. {
  422. TCHAR szFullIconPath[MAX_PATH];
  423. // We now allow ".txt" for the icon path, but since the user is clicking
  424. // on the "Change Icon..." button, we show the current icon that ".txt" is
  425. // associated with
  426. GetIconLocationFromExt(pszIconPath, szFullIconPath, ARRAYSIZE(szFullIconPath), &iIconIndex);
  427. StringCchCopy(pszIconPath, ARRAYSIZE(sfi.szDisplayName), szFullIconPath);
  428. }
  429. else if (pszIconPath[0] == 0)
  430. {
  431. //
  432. // link does not have a icon, if it is a link to a file
  433. // use the file name
  434. //
  435. iIconIndex = 0;
  436. HRESULT hr = _GetPathAndArgs(plpd, pszIconPath, NULL, 0);
  437. if (SUCCEEDED(hr))
  438. {
  439. if (!plpd->bIsFile || !PathIsExe(pszIconPath))
  440. {
  441. //
  442. // link is not to a file, go get the icon
  443. //
  444. SHGetFileInfo(plpd->szFile, 0, &sfi, sizeof(sfi), SHGFI_ICONLOCATION);
  445. iIconIndex = sfi.iIcon;
  446. ASSERT(pszIconPath == sfi.szDisplayName);
  447. }
  448. }
  449. }
  450. }
  451. if (PickIconDlg(plpd->hDlg, pszIconPath, MAX_PATH, &iIconIndex))
  452. {
  453. HICON hIcon = ExtractIcon(HINST_THISDLL, pszIconPath, iIconIndex);
  454. _UpdateLinkIcon(plpd, hIcon);
  455. // don't save it out to the link yet, just store it in our instance data
  456. plpd->iIconIndex = iIconIndex;
  457. StringCchCopy(plpd->szIconPath, ARRAYSIZE(plpd->szIconPath), pszIconPath);
  458. PropSheet_Changed(GetParent(plpd->hDlg), plpd->hDlg);
  459. return TRUE;
  460. }
  461. return FALSE;
  462. }
  463. STDAPI SaveLink(LINKDATA *pld)
  464. {
  465. WORD wHotkey;
  466. int iShowCmd;
  467. IPersistFile *ppf;
  468. HRESULT hr;
  469. TCHAR szBuffer[MAX_PATH];
  470. if (!(pld->plpd->bIsDirty || (pld->cpd.lpConsole && pld->cpd.bConDirty)))
  471. return S_OK;
  472. if (pld->plpd->bIsFile)
  473. {
  474. TCHAR szArgs[MAX_PATH];
  475. hr = _GetPathAndArgs(pld->plpd, szBuffer, szArgs, ARRAYSIZE(szArgs));
  476. if (SUCCEEDED(hr))
  477. {
  478. // set the path (and pidl) of the link
  479. pld->plpd->psl->SetPath(szBuffer);
  480. // may be null
  481. pld->plpd->psl->SetArguments(szArgs);
  482. }
  483. if (pld->plpd->bEnableRunInSepVDM && pld->plpd->bRunInSepVDM)
  484. {
  485. SetLinkFlags(pld->plpd->psl, SLDF_RUN_IN_SEPARATE, SLDF_RUN_IN_SEPARATE);
  486. }
  487. else
  488. {
  489. SetLinkFlags(pld->plpd->psl, 0, SLDF_RUN_IN_SEPARATE);
  490. }
  491. if (pld->plpd->bRunAsUser)
  492. {
  493. SetLinkFlags(pld->plpd->psl, SLDF_RUNAS_USER, SLDF_RUNAS_USER);
  494. }
  495. else
  496. {
  497. SetLinkFlags(pld->plpd->psl, 0, SLDF_RUNAS_USER);
  498. }
  499. }
  500. if (pld->plpd->bIsFile || (SetLinkFlags(pld->plpd->psl, 0, 0) & SLDF_HAS_DARWINID))
  501. {
  502. // set the working directory of the link
  503. GetDlgItemPath(pld->plpd->hDlg, IDD_PATH, szBuffer);
  504. pld->plpd->psl->SetWorkingDirectory(szBuffer);
  505. }
  506. // set the description of the link if it changed.
  507. TCHAR szOldComment[MAX_PATH];
  508. pld->plpd->psl->GetDescription(szOldComment, ARRAYSIZE(szOldComment));
  509. SHLoadIndirectString(szOldComment, szOldComment, ARRAYSIZE(szOldComment), NULL); // will do nothing if the string isn't indirect
  510. GetDlgItemText(pld->plpd->hDlg, IDD_LINK_DESCRIPTION, szBuffer, ARRAYSIZE(szBuffer));
  511. if (lstrcmp(szBuffer, szOldComment) != 0)
  512. pld->plpd->psl->SetDescription(szBuffer);
  513. // the hotkey
  514. wHotkey = (WORD)SendDlgItemMessage(pld->plpd->hDlg, IDD_LINK_HOTKEY , HKM_GETHOTKEY, 0, 0);
  515. pld->plpd->psl->SetHotkey(wHotkey);
  516. // the show command combo box
  517. iShowCmd = (int)SendDlgItemMessage(pld->plpd->hDlg, IDD_LINK_SHOWCMD, CB_GETCURSEL, 0, 0L);
  518. if ((iShowCmd >= 0) && (iShowCmd < ARRAYSIZE(c_iShowCmds)))
  519. {
  520. pld->plpd->psl->SetShowCmd(c_iShowCmds[iShowCmd]);
  521. }
  522. // If the user explicitly selected a new icon, invalidate
  523. // the icon cache entry for this link and then send around a file
  524. // sys refresh message to all windows in case they are looking at
  525. // this link.
  526. if (pld->plpd->iIconIndex >= 0)
  527. {
  528. pld->plpd->psl->SetIconLocation(pld->plpd->szIconPath, pld->plpd->iIconIndex);
  529. }
  530. // Update/Save the console information in the pExtraData section of
  531. // the shell link.
  532. if (pld->cpd.lpConsole && pld->cpd.bConDirty)
  533. {
  534. LinkConsolePagesSave(pld);
  535. }
  536. hr = pld->plpd->psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  537. if (SUCCEEDED(hr))
  538. {
  539. if (ppf->IsDirty() == S_OK)
  540. {
  541. // save using existing file name (pld->plpd->szFile)
  542. hr = ppf->Save(NULL, TRUE);
  543. if (FAILED(hr))
  544. {
  545. SHSysErrorMessageBox(pld->plpd->hDlg, NULL, IDS_LINKCANTSAVE,
  546. hr & 0xFFF, PathFindFileName(pld->plpd->szFile),
  547. MB_OK | MB_ICONEXCLAMATION);
  548. }
  549. else
  550. {
  551. pld->plpd->bIsDirty = FALSE;
  552. }
  553. }
  554. ppf->Release();
  555. }
  556. return hr;
  557. }
  558. void SetEditFocus(HWND hwnd)
  559. {
  560. SetFocus(hwnd);
  561. Edit_SetSel(hwnd, 0, -1);
  562. }
  563. // returns:
  564. // TRUE all link fields are valid
  565. // FALSE some thing is wrong with what the user has entered
  566. BOOL _ValidateLink(LINKPROP_DATA *plpd)
  567. {
  568. TCHAR szDir[MAX_PATH], szPath[MAX_PATH], szArgs[MAX_PATH];
  569. TCHAR szExpPath[MAX_PATH];
  570. BOOL bValidPath = FALSE;
  571. HRESULT hr;
  572. if (!plpd->bIsFile)
  573. return TRUE;
  574. // validate the working directory field
  575. GetDlgItemPath(plpd->hDlg, IDD_PATH, szDir);
  576. if (*szDir &&
  577. StrChr(szDir, TEXT('%')) == NULL && // has environement var %USER%
  578. !IsRemovableDrive(DRIVEID(szDir)) &&
  579. !PathIsDirectory(szDir))
  580. {
  581. ShellMessageBox(HINST_THISDLL, plpd->hDlg, MAKEINTRESOURCE(IDS_LINKBADWORKDIR),
  582. MAKEINTRESOURCE(IDS_LINKERROR), MB_OK | MB_ICONEXCLAMATION, szDir);
  583. SetEditFocus(GetDlgItem(plpd->hDlg, IDD_PATH));
  584. return FALSE;
  585. }
  586. // validate the path (with arguments) field
  587. hr = _GetPathAndArgs(plpd, szPath, szArgs, ARRAYSIZE(szArgs));
  588. if (SUCCEEDED(hr))
  589. {
  590. if (szPath[0] == 0)
  591. return TRUE;
  592. if (PathIsRoot(szPath) && IsRemovableDrive(DRIVEID(szPath)))
  593. return TRUE;
  594. if (PathIsLnk(szPath))
  595. {
  596. ShellMessageBox(HINST_THISDLL, plpd->hDlg, MAKEINTRESOURCE(IDS_LINKTOLINK),
  597. MAKEINTRESOURCE(IDS_LINKERROR), MB_OK | MB_ICONEXCLAMATION);
  598. SetEditFocus(GetDlgItem(plpd->hDlg, IDD_FILENAME));
  599. return FALSE;
  600. }
  601. LPCTSTR dirs[2];
  602. dirs[0] = szDir;
  603. dirs[1] = NULL;
  604. bValidPath = PathResolve(szPath, dirs, PRF_DONTFINDLNK | PRF_TRYPROGRAMEXTENSIONS);
  605. if (!bValidPath)
  606. {
  607. // The path "as is" was invalid. See if it has environment variables
  608. // which need to be expanded.
  609. hr = _GetPathAndArgs(plpd, szPath, szArgs, ARRAYSIZE(szArgs));
  610. if (SUCCEEDED(hr))
  611. {
  612. if (SHExpandEnvironmentStrings(szPath, szExpPath, ARRAYSIZE(szExpPath)))
  613. {
  614. if (PathIsRoot(szExpPath) && IsRemovableDrive(DRIVEID(szDir)))
  615. return TRUE;
  616. bValidPath = PathResolve(szExpPath, dirs, PRF_DONTFINDLNK | PRF_TRYPROGRAMEXTENSIONS);
  617. }
  618. }
  619. }
  620. if (bValidPath)
  621. {
  622. BOOL bSave;
  623. if (plpd->bLinkThreadIsAlive)
  624. {
  625. bSave = plpd->bCheckRunInSep;
  626. plpd->bCheckRunInSep = FALSE;
  627. }
  628. PathComposeWithArgs(szPath, szArgs);
  629. GetDlgItemText(plpd->hDlg, IDD_FILENAME, szExpPath, ARRAYSIZE(szExpPath));
  630. // only do this if something changed... that way we avoid having the PSM_CHANGED
  631. // for nothing
  632. if (lstrcmpi(szPath, szExpPath))
  633. SetDlgItemText(plpd->hDlg, IDD_FILENAME, szPath);
  634. if (plpd->bLinkThreadIsAlive)
  635. {
  636. plpd->bCheckRunInSep = bSave;
  637. }
  638. return TRUE;
  639. }
  640. }
  641. ShellMessageBox(HINST_THISDLL, plpd->hDlg, MAKEINTRESOURCE(IDS_LINKBADPATH),
  642. MAKEINTRESOURCE(IDS_LINKERROR), MB_OK | MB_ICONEXCLAMATION, szPath);
  643. SetEditFocus(GetDlgItem(plpd->hDlg, IDD_FILENAME));
  644. return FALSE;
  645. }
  646. // Array for context help:
  647. const DWORD aLinkHelpIDs[] = {
  648. IDD_LINE_1, NO_HELP,
  649. IDD_LINE_2, NO_HELP,
  650. IDD_ITEMICON, IDH_FCAB_LINK_ICON,
  651. IDD_NAME, IDH_FCAB_LINK_NAME,
  652. IDD_FILETYPE_TXT, IDH_FCAB_LINK_LINKTYPE,
  653. IDD_FILETYPE, IDH_FCAB_LINK_LINKTYPE,
  654. IDD_LOCATION_TXT, IDH_FCAB_LINK_LOCATION,
  655. IDD_LOCATION, IDH_FCAB_LINK_LOCATION,
  656. IDD_FILENAME, IDH_FCAB_LINK_LINKTO,
  657. IDD_PATH, IDH_FCAB_LINK_WORKING,
  658. IDD_LINK_HOTKEY, IDH_FCAB_LINK_HOTKEY,
  659. IDD_LINK_SHOWCMD, IDH_FCAB_LINK_RUN,
  660. IDD_LINK_DESCRIPTION, IDH_FCAB_LINK_DESCRIPTION,
  661. IDD_FINDORIGINAL, IDH_FCAB_LINK_FIND,
  662. IDD_LINKDETAILS, IDH_FCAB_LINK_CHANGEICON,
  663. 0, 0
  664. };
  665. // Array for context help (Advanced Dlg):
  666. const DWORD aAdvancedLinkHelpIDs[] = {
  667. IDD_RUNINSEPARATE, IDH_TRAY_RUN_SEPMEM,
  668. IDD_LINK_RUNASUSER, IDH_FCAB_LINK_RUNASUSER,
  669. 0,0
  670. };
  671. UINT g_msgActivateDesktop = 0;
  672. DWORD CALLBACK _LinkAddRefSyncCallBack(void *pv)
  673. {
  674. LINKPROP_DATA *plpd = (LINKPROP_DATA *)pv;
  675. AddRef_LinkPropData(plpd);
  676. plpd->bLinkThreadIsAlive = TRUE;
  677. return 0;
  678. }
  679. // Dialog proc for the generic link property sheet
  680. //
  681. // uses DLG_LINKPROP template
  682. BOOL_PTR CALLBACK _LinkAdvancedDlgProc(HWND hDlgAdvanced, UINT msg, WPARAM wParam, LPARAM lParam)
  683. {
  684. LINKPROP_DATA *plpd = (LINKPROP_DATA *)GetWindowLongPtr(hDlgAdvanced, DWLP_USER);
  685. switch (msg)
  686. {
  687. case WM_INITDIALOG:
  688. {
  689. TCHAR szFullFile[MAX_PATH];
  690. DWORD cchVerb;
  691. UINT cch;
  692. plpd = (LINKPROP_DATA *)lParam;
  693. SetWindowLongPtr(hDlgAdvanced, DWLP_USER, (LPARAM)plpd);
  694. plpd->hDlgAdvanced = hDlgAdvanced;
  695. cch = _GetTargetOfLink(plpd, szFullFile);
  696. if ((cch != 0) && (cch < ARRAYSIZE(szFullFile)))
  697. {
  698. DWORD dwBinaryType;
  699. // enable "run in seperate VDM" if this is a 16-bit image
  700. if (GetBinaryType(szFullFile, &dwBinaryType) && (dwBinaryType == SCS_WOW_BINARY))
  701. {
  702. if (SetLinkFlags(plpd->psl, 0, 0) & SLDF_RUN_IN_SEPARATE)
  703. {
  704. EnableWindow(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE), TRUE);
  705. CheckDlgButton(hDlgAdvanced, IDD_RUNINSEPARATE, BST_CHECKED);
  706. }
  707. else
  708. {
  709. EnableWindow(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE), TRUE);
  710. CheckDlgButton(hDlgAdvanced, IDD_RUNINSEPARATE, BST_UNCHECKED);
  711. }
  712. }
  713. else
  714. {
  715. // check it
  716. CheckDlgButton(hDlgAdvanced, IDD_RUNINSEPARATE, BST_CHECKED);
  717. EnableWindow(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE), FALSE);
  718. }
  719. // enable "runas" if the link target has that verb
  720. if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_COMMAND, szFullFile, TEXT("runas"), NULL, &cchVerb)) &&
  721. cchVerb)
  722. {
  723. EnableWindow(GetDlgItem(hDlgAdvanced, IDD_LINK_RUNASUSER), TRUE);
  724. CheckDlgButton(hDlgAdvanced, IDD_LINK_RUNASUSER, (SetLinkFlags(plpd->psl, 0, 0) & SLDF_RUNAS_USER) ? BST_CHECKED : BST_UNCHECKED);
  725. }
  726. else
  727. {
  728. EnableWindow(GetDlgItem(hDlgAdvanced, IDD_LINK_RUNASUSER), FALSE);
  729. CheckDlgButton(hDlgAdvanced, IDD_LINK_RUNASUSER, BST_UNCHECKED);
  730. }
  731. }
  732. else
  733. {
  734. // fall back to disabling everything
  735. CheckDlgButton(hDlgAdvanced, IDD_RUNINSEPARATE, BST_CHECKED);
  736. EnableWindow(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE), FALSE);
  737. EnableWindow(GetDlgItem(hDlgAdvanced, IDD_LINK_RUNASUSER), FALSE);
  738. }
  739. // get the initial state of the checkboxes
  740. plpd->bEnableRunInSepVDM = IsWindowEnabled(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE));
  741. plpd->bRunInSepVDM = IsDlgButtonChecked(hDlgAdvanced, IDD_RUNINSEPARATE);
  742. plpd->bRunAsUser = IsDlgButtonChecked(hDlgAdvanced, IDD_LINK_RUNASUSER);
  743. }
  744. break;
  745. case WM_COMMAND:
  746. {
  747. UINT idControl = GET_WM_COMMAND_ID(wParam, lParam);
  748. switch (idControl)
  749. {
  750. case IDD_RUNINSEPARATE:
  751. case IDD_LINK_RUNASUSER:
  752. plpd->bIsDirty = TRUE;
  753. break;
  754. case IDOK:
  755. // get the final state of the checkboxes
  756. plpd->bEnableRunInSepVDM = IsWindowEnabled(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE));
  757. plpd->bRunInSepVDM = IsDlgButtonChecked(hDlgAdvanced, IDD_RUNINSEPARATE);
  758. plpd->bRunAsUser = IsDlgButtonChecked(hDlgAdvanced, IDD_LINK_RUNASUSER);
  759. // fall through
  760. case IDCANCEL:
  761. ReplaceDlgIcon(hDlgAdvanced, IDD_ITEMICON, NULL);
  762. plpd->hDlgAdvanced = NULL;
  763. EndDialog(hDlgAdvanced, (idControl == IDCANCEL) ? FALSE : TRUE);
  764. break;
  765. }
  766. }
  767. break;
  768. case WM_HELP:
  769. WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR)aAdvancedLinkHelpIDs);
  770. break;
  771. case WM_CONTEXTMENU:
  772. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aAdvancedLinkHelpIDs);
  773. break;
  774. default:
  775. return FALSE;
  776. }
  777. return TRUE;
  778. }
  779. BOOL_PTR CALLBACK _LinkDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
  780. {
  781. LINKDATA *pld = (LINKDATA *)GetWindowLongPtr(hdlg, DWLP_USER);
  782. switch (msg)
  783. {
  784. case WM_INITDIALOG:
  785. pld = (LINKDATA *)((PROPSHEETPAGE *)lParam)->lParam;
  786. SetWindowLongPtr(hdlg, DWLP_USER, (LPARAM)pld);
  787. // setup dialog state variables
  788. pld->plpd->hDlg = hdlg;
  789. SendDlgItemMessage(hdlg, IDD_FILENAME, EM_LIMITTEXT, MAX_PATH-1, 0);
  790. SetPathWordBreakProc(GetDlgItem(hdlg, IDD_FILENAME), TRUE);
  791. SendDlgItemMessage(hdlg, IDD_PATH, EM_LIMITTEXT, MAX_PATH-1, 0);
  792. SetPathWordBreakProc(GetDlgItem(hdlg, IDD_PATH), TRUE);
  793. SendDlgItemMessage(hdlg, IDD_LINK_DESCRIPTION, EM_LIMITTEXT, MAX_PATH-1, 0);
  794. // set valid combinations for the hotkey
  795. SendDlgItemMessage(hdlg, IDD_LINK_HOTKEY, HKM_SETRULES,
  796. HKCOMB_NONE | HKCOMB_A | HKCOMB_S | HKCOMB_C,
  797. HOTKEYF_CONTROL | HOTKEYF_ALT);
  798. SHAutoComplete(GetDlgItem(hdlg, IDD_FILENAME), 0);
  799. SHAutoComplete(GetDlgItem(hdlg, IDD_PATH), 0);
  800. ASSERT(pld->plpd->bLinkThreadIsAlive == FALSE);
  801. _UpdateLinkDlg(pld->plpd, FALSE);
  802. // Set up background thread to handle "Run In Separate Memory Space"
  803. // check box.
  804. pld->plpd->bCheckRunInSep = TRUE;
  805. if (pld->plpd->hCheckNow)
  806. {
  807. SHCreateThread(_LinkCheckThreadProc, pld->plpd, 0, _LinkAddRefSyncCallBack);
  808. }
  809. // start off clean.
  810. // do this here because we call some stuff above which generates
  811. // wm_command/en_changes which we then think makes it dirty
  812. pld->plpd->bIsDirty = FALSE;
  813. break;
  814. case WM_DESTROY:
  815. ReplaceDlgIcon(pld->plpd->hDlg, IDD_ITEMICON, NULL);
  816. _StopThread(pld->plpd);
  817. break;
  818. case WM_NOTIFY:
  819. switch (((NMHDR *)lParam)->code)
  820. {
  821. case PSN_RESET:
  822. _StopThread(pld->plpd);
  823. break;
  824. case PSN_APPLY:
  825. if ((((PSHNOTIFY *)lParam)->lParam))
  826. _StopThread(pld->plpd);
  827. if (FAILED(SaveLink(pld)))
  828. SetWindowLongPtr(hdlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
  829. break;
  830. case PSN_KILLACTIVE:
  831. // we implement the save on page change model, so
  832. // validate and save changes here. this works for
  833. // Apply Now, OK, and Page chagne.
  834. SetWindowLongPtr(hdlg, DWLP_MSGRESULT, !_ValidateLink(pld->plpd)); // don't allow close
  835. break;
  836. }
  837. break;
  838. case WM_COMMAND:
  839. switch (GET_WM_COMMAND_ID(wParam, lParam))
  840. {
  841. case IDD_FINDORIGINAL:
  842. _FindTarget(pld->plpd);
  843. break;
  844. case IDD_LINKDETAILS:
  845. if (_DoPickIcon(pld->plpd))
  846. pld->plpd->bIsDirty = TRUE;
  847. break;
  848. case IDD_LINK_SHOWCMD:
  849. if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE)
  850. {
  851. PropSheet_Changed(GetParent(hdlg), hdlg);
  852. pld->plpd->bIsDirty = TRUE;
  853. }
  854. break;
  855. case IDD_LINK_HOTKEY:
  856. case IDD_FILENAME:
  857. case IDD_PATH:
  858. case IDD_LINK_DESCRIPTION:
  859. if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
  860. {
  861. PropSheet_Changed(GetParent(hdlg), hdlg);
  862. pld->plpd->bIsDirty = TRUE;
  863. if (pld->plpd->bLinkThreadIsAlive && pld->plpd->bCheckRunInSep)
  864. SetEvent(pld->plpd->hCheckNow);
  865. }
  866. break;
  867. case IDC_ADVANCED:
  868. if ((DialogBoxParam(HINST_THISDLL,
  869. MAKEINTRESOURCE(DLG_LINKPROP_ADVANCED),
  870. hdlg,
  871. _LinkAdvancedDlgProc,
  872. (LPARAM)pld->plpd) == TRUE) &&
  873. (pld->plpd->bIsDirty == TRUE))
  874. {
  875. // something on the advanced page changed
  876. PropSheet_Changed(GetParent(hdlg), hdlg);
  877. }
  878. break;
  879. default:
  880. return FALSE;
  881. }
  882. break;
  883. case WM_HELP:
  884. WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aLinkHelpIDs);
  885. break;
  886. case WM_CONTEXTMENU:
  887. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aLinkHelpIDs);
  888. break;
  889. default:
  890. if (0 == g_msgActivateDesktop)
  891. g_msgActivateDesktop = RegisterWindowMessage(TEXT("ActivateDesktop"));
  892. if (msg == g_msgActivateDesktop)
  893. {
  894. HWND hwnd = FindWindow(TEXT(STR_DESKTOPCLASS), NULL);
  895. SwitchToThisWindow(GetLastActivePopup(hwnd), TRUE);
  896. SetForegroundWindow(hwnd);
  897. }
  898. return FALSE;
  899. }
  900. return TRUE;
  901. }
  902. //
  903. // Release the link object allocated during the initialize
  904. //
  905. UINT CALLBACK _LinkPrshtCallback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
  906. {
  907. LINKDATA *pld = (LINKDATA *)((PROPSHEETPAGE *)ppsp->lParam);
  908. switch (uMsg)
  909. {
  910. case PSPCB_RELEASE:
  911. if (pld->cpd.lpConsole)
  912. {
  913. LocalFree(pld->cpd.lpConsole);
  914. }
  915. if (pld->cpd.lpFEConsole)
  916. {
  917. LocalFree(pld->cpd.lpFEConsole);
  918. }
  919. DestroyFonts(&pld->cpd);
  920. Release_LinkPropData(pld->plpd);
  921. LocalFree(pld);
  922. break;
  923. }
  924. return 1;
  925. }
  926. STDAPI_(BOOL) AddLinkPage(LPCTSTR pszFile, LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
  927. {
  928. IShellLink *psl;
  929. if (PathIsLnk(pszFile) && SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl))))
  930. {
  931. // alloc this data, since is it shared across several pages
  932. // instead of putting it in as extra data in the page header
  933. LINKDATA *pld = (LINKDATA *)LocalAlloc(LPTR, sizeof(*pld));
  934. if (pld)
  935. {
  936. pld->plpd = Create_LinkPropData();
  937. if (pld->plpd)
  938. {
  939. PROPSHEETPAGE psp;
  940. psp.dwSize = sizeof(psp);
  941. psp.dwFlags = PSP_DEFAULT | PSP_USECALLBACK;
  942. psp.hInstance = HINST_THISDLL;
  943. psp.pszTemplate = MAKEINTRESOURCE(DLG_LINKPROP);
  944. psp.pfnDlgProc = _LinkDlgProc;
  945. psp.pfnCallback = _LinkPrshtCallback;
  946. psp.lParam = (LPARAM)pld; // pass to all dlg procs
  947. StringCchCopy(pld->plpd->szFile, ARRAYSIZE(pld->plpd->szFile), pszFile);
  948. pld->plpd->iIconIndex = -1;
  949. pld->plpd->psl = psl;
  950. ASSERT(!pld->plpd->szIconPath[0]);
  951. HPROPSHEETPAGE hpage = CreatePropertySheetPage(&psp);
  952. if (hpage)
  953. {
  954. if (pfnAddPage(hpage, lParam))
  955. {
  956. // Add console property pages if appropriate...
  957. AddLinkConsolePages(pld, psl, pszFile, pfnAddPage, lParam);
  958. return TRUE; // we added the link page
  959. }
  960. else
  961. {
  962. DestroyPropertySheetPage(hpage);
  963. }
  964. }
  965. Release_LinkPropData(pld->plpd);
  966. }
  967. LocalFree(pld);
  968. }
  969. }
  970. return FALSE;
  971. }