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.

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