link.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 38k
Category:

Windows Kernel

Development Platform:

Visual C++

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