1189 lines
36 KiB
C++
1189 lines
36 KiB
C++
|
#include "shellprv.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include <msi.h>
|
||
|
#include <msip.h>
|
||
|
#include "lnkcon.h"
|
||
|
#include "trayp.h" // for WMTRAY_ messages
|
||
|
#include "util.h" // for GetIconLocationFromExt
|
||
|
#include "ids.h"
|
||
|
|
||
|
LINKPROP_DATA* Create_LinkPropData()
|
||
|
{
|
||
|
LINKPROP_DATA *plpd = (LINKPROP_DATA*) LocalAlloc(LPTR, sizeof(*plpd));
|
||
|
if (plpd)
|
||
|
{
|
||
|
plpd->_cRef = 1;
|
||
|
plpd->hCheckNow = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||
|
|
||
|
}
|
||
|
return plpd;
|
||
|
}
|
||
|
|
||
|
LONG AddRef_LinkPropData(LINKPROP_DATA *plpd)
|
||
|
{
|
||
|
return plpd ? InterlockedIncrement(&plpd->_cRef) : 0;
|
||
|
}
|
||
|
|
||
|
LONG Release_LinkPropData(LINKPROP_DATA *plpd)
|
||
|
{
|
||
|
if (plpd)
|
||
|
{
|
||
|
if (InterlockedDecrement(&plpd->_cRef))
|
||
|
return plpd->_cRef;
|
||
|
|
||
|
if (plpd->psl)
|
||
|
plpd->psl->Release();
|
||
|
if (plpd->hCheckNow)
|
||
|
{
|
||
|
CloseHandle(plpd->hCheckNow);
|
||
|
plpd->hCheckNow = NULL;
|
||
|
}
|
||
|
LocalFree(plpd);
|
||
|
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// This string defined in shlink.c - hack to allow user to set working dir to $$
|
||
|
// and have it map to whatever "My Documents" is mapped to.
|
||
|
//
|
||
|
|
||
|
void _UpdateLinkIcon(LINKPROP_DATA *plpd, HICON hIcon)
|
||
|
{
|
||
|
if (!hIcon)
|
||
|
{
|
||
|
hIcon = SHGetFileIcon(NULL, plpd->szFile, 0, SHGFI_LARGEICON);
|
||
|
}
|
||
|
|
||
|
if (hIcon)
|
||
|
{
|
||
|
ReplaceDlgIcon(plpd->hDlg, IDD_ITEMICON, hIcon);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// put a path into an edit field, doing quoting as necessary
|
||
|
|
||
|
void SetDlgItemPath(HWND hdlg, int id, LPTSTR pszPath)
|
||
|
{
|
||
|
PathQuoteSpaces(pszPath);
|
||
|
SetDlgItemText(hdlg, id, pszPath);
|
||
|
}
|
||
|
|
||
|
// get a path from an edit field, unquoting as possible
|
||
|
|
||
|
void GetDlgItemPath(HWND hdlg, int id, LPTSTR pszPath)
|
||
|
{
|
||
|
GetDlgItemText(hdlg, id, pszPath, MAX_PATH);
|
||
|
PathRemoveBlanks(pszPath);
|
||
|
PathUnquoteSpaces(pszPath);
|
||
|
}
|
||
|
|
||
|
|
||
|
const int c_iShowCmds[] = {
|
||
|
SW_SHOWNORMAL,
|
||
|
SW_SHOWMINNOACTIVE,
|
||
|
SW_SHOWMAXIMIZED,
|
||
|
};
|
||
|
|
||
|
void _DisableAllChildren(HWND hwnd)
|
||
|
{
|
||
|
HWND hwndChild;
|
||
|
|
||
|
for (hwndChild = GetWindow(hwnd, GW_CHILD); hwndChild != NULL; hwndChild = GetWindow(hwndChild, GW_HWNDNEXT))
|
||
|
{
|
||
|
// we don't want to disable the static text controls (makes the dlg look bad)
|
||
|
if (!(SendMessage(hwndChild, WM_GETDLGCODE, 0, 0) & DLGC_STATIC))
|
||
|
{
|
||
|
EnableWindow(hwndChild, FALSE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void _GetPathAndArgs(LINKPROP_DATA *plpd, LPTSTR pszPath, LPTSTR pszArgs)
|
||
|
{
|
||
|
GetDlgItemText(plpd->hDlg, IDD_FILENAME, pszPath, MAX_PATH);
|
||
|
PathSeperateArgs(pszPath, pszArgs);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Returns fully qualified path to target of link, and # of characters
|
||
|
// in fully qualifed path as return value
|
||
|
//
|
||
|
INT _GetTargetOfLink(LINKPROP_DATA *plpd, LPTSTR pszTarget)
|
||
|
{
|
||
|
TCHAR szFile[MAX_PATH], szArgs[MAX_PATH];
|
||
|
INT cch = 0;
|
||
|
|
||
|
*pszTarget = 0;
|
||
|
|
||
|
_GetPathAndArgs(plpd, szFile, szArgs);
|
||
|
|
||
|
if (szFile[0])
|
||
|
{
|
||
|
LPTSTR psz;
|
||
|
TCHAR szExp[MAX_PATH];
|
||
|
|
||
|
if (SHExpandEnvironmentStrings(szFile, szExp, ARRAYSIZE(szExp)))
|
||
|
{
|
||
|
cch = SearchPath(NULL, szExp, TEXT(".EXE"), MAX_PATH, pszTarget, &psz);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return cch;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Do checking of the .exe type in the background so the UI doesn't
|
||
|
// get hung up while we scan. This is particularly important with
|
||
|
// the .exe is over the network or on a floppy.
|
||
|
//
|
||
|
STDAPI_(DWORD) _LinkCheckThreadProc(void *pv)
|
||
|
{
|
||
|
LINKPROP_DATA *plpd = (LINKPROP_DATA *)pv;
|
||
|
BOOL fCheck = TRUE, fEnable = FALSE;
|
||
|
|
||
|
DebugMsg(DM_TRACE, TEXT("_LinkCheckThreadProc created and running"));
|
||
|
|
||
|
while (plpd->bCheckRunInSep)
|
||
|
{
|
||
|
WaitForSingleObject(plpd->hCheckNow, INFINITE);
|
||
|
ResetEvent(plpd->hCheckNow);
|
||
|
|
||
|
if (plpd->bCheckRunInSep)
|
||
|
{
|
||
|
TCHAR szFullFile[MAX_PATH];
|
||
|
DWORD cch = _GetTargetOfLink(plpd, szFullFile);
|
||
|
|
||
|
if ((cch != 0) && (cch < ARRAYSIZE(szFullFile)))
|
||
|
{
|
||
|
DWORD dwBinaryType;
|
||
|
|
||
|
if (PathIsUNC(szFullFile) || IsRemoteDrive(DRIVEID(szFullFile)))
|
||
|
{
|
||
|
// Net Path, let the user decide...
|
||
|
fCheck = FALSE;
|
||
|
fEnable = TRUE;
|
||
|
}
|
||
|
else if (GetBinaryType(szFullFile, &dwBinaryType) && (dwBinaryType == SCS_WOW_BINARY))
|
||
|
{
|
||
|
// 16-bit binary, let the user decide, default to same VDM
|
||
|
fCheck = FALSE;
|
||
|
fEnable = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// 32-bit binary, or non-net path. don't enable the control
|
||
|
fCheck = TRUE;
|
||
|
fEnable = FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Error getting target of the link. don't enable the control
|
||
|
fCheck = TRUE;
|
||
|
fEnable = FALSE;
|
||
|
}
|
||
|
|
||
|
plpd->bEnableRunInSepVDM = fEnable;
|
||
|
plpd->bRunInSepVDM = fCheck;
|
||
|
|
||
|
if (plpd->hDlgAdvanced && IsWindow(plpd->hDlgAdvanced))
|
||
|
{
|
||
|
CheckDlgButton(plpd->hDlgAdvanced, IDD_RUNINSEPARATE, fCheck ? 1 : 0);
|
||
|
EnableWindow(GetDlgItem(plpd->hDlgAdvanced, IDD_RUNINSEPARATE), fEnable);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
plpd->bLinkThreadIsAlive = FALSE;
|
||
|
Release_LinkPropData(plpd);
|
||
|
DebugMsg(DM_TRACE, TEXT("_LinkCheckThreadProc exiting now..."));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// shut down the thread
|
||
|
|
||
|
void _StopThread(LINKPROP_DATA *plpd)
|
||
|
{
|
||
|
if (plpd->bLinkThreadIsAlive)
|
||
|
{
|
||
|
plpd->bCheckRunInSep = FALSE;
|
||
|
SetEvent(plpd->hCheckNow);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void * _GetLinkExtraData(IShellLink* psl, DWORD dwSig)
|
||
|
{
|
||
|
void * pDataBlock = NULL;
|
||
|
|
||
|
IShellLinkDataList *psld;
|
||
|
if (SUCCEEDED(psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psld))))
|
||
|
{
|
||
|
psld->CopyDataBlock(dwSig, &pDataBlock);
|
||
|
psld->Release();
|
||
|
}
|
||
|
|
||
|
return pDataBlock;
|
||
|
}
|
||
|
|
||
|
HRESULT _RemoveLinkExtraData(IShellLink* psl, DWORD dwSig)
|
||
|
{
|
||
|
IShellLinkDataList *psld;
|
||
|
HRESULT hr = psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psld));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = psld->RemoveDataBlock(dwSig);
|
||
|
psld->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT _SetLinkExtraData(IShellLink* psl, EXP_HEADER* peh)
|
||
|
{
|
||
|
IShellLinkDataList *psld;
|
||
|
HRESULT hr = psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psld));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// remove any existing datablock
|
||
|
psld->RemoveDataBlock(peh->dwSignature);
|
||
|
hr = psld->AddDataBlock((void*)peh);
|
||
|
psld->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Initializes the generic link dialog box.
|
||
|
void _UpdateLinkDlg(LINKPROP_DATA *plpd, BOOL bUpdatePath)
|
||
|
{
|
||
|
WORD wHotkey;
|
||
|
int i, iShowCmd;
|
||
|
TCHAR szBuffer[MAX_PATH];
|
||
|
TCHAR szCommand[MAX_PATH];
|
||
|
HRESULT hr;
|
||
|
SHFILEINFO sfi;
|
||
|
BOOL fIsDarwinLink;
|
||
|
|
||
|
|
||
|
// do this here so we don't slow down the loading
|
||
|
// of other pages
|
||
|
|
||
|
if (!bUpdatePath)
|
||
|
{
|
||
|
IPersistFile *ppf;
|
||
|
|
||
|
if (SUCCEEDED(plpd->psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf))))
|
||
|
{
|
||
|
WCHAR wszPath[MAX_PATH];
|
||
|
|
||
|
SHTCharToUnicode(plpd->szFile, wszPath, ARRAYSIZE(wszPath));
|
||
|
hr = ppf->Load(wszPath, 0);
|
||
|
ppf->Release();
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, IDS_LINKNOTLINK, szBuffer, ARRAYSIZE(szBuffer));
|
||
|
SetDlgItemText(plpd->hDlg, IDD_FILETYPE, szBuffer);
|
||
|
_DisableAllChildren(plpd->hDlg);
|
||
|
|
||
|
DebugMsg(DM_TRACE, TEXT("Shortcut IPersistFile::Load() failed %x"), hr);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fIsDarwinLink = SetLinkFlags(plpd->psl, 0, 0) & SLDF_HAS_DARWINID;
|
||
|
|
||
|
SHGetFileInfo(plpd->szFile, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES);
|
||
|
SetDlgItemText(plpd->hDlg, IDD_NAME, sfi.szDisplayName);
|
||
|
|
||
|
// we need to check for darwin links here so that we can gray out
|
||
|
// things that don't apply to darwin
|
||
|
if (fIsDarwinLink)
|
||
|
{
|
||
|
TCHAR szAppState[MAX_PATH];
|
||
|
DWORD cchAppState = ARRAYSIZE(szAppState);
|
||
|
HWND hwndTargetType = GetDlgItem(plpd->hDlg, IDD_FILETYPE);
|
||
|
|
||
|
// disable the children
|
||
|
_DisableAllChildren(plpd->hDlg);
|
||
|
|
||
|
// then special case the icon and the "Target type:" text
|
||
|
_UpdateLinkIcon(plpd, NULL);
|
||
|
|
||
|
LPEXP_DARWIN_LINK pDarwinData = (LPEXP_DARWIN_LINK)_GetLinkExtraData(plpd->psl, EXP_DARWIN_ID_SIG);
|
||
|
|
||
|
// The second clause will see if it is a Darwin Advertisement.
|
||
|
if (pDarwinData && (INSTALLSTATE_ADVERTISED == MsiQueryFeatureStateFromDescriptorW(pDarwinData->szwDarwinID)))
|
||
|
{
|
||
|
// the app is advertised (e.g. not installed), but will be faulted in on first use
|
||
|
LoadString(HINST_THISDLL, IDS_APP_NOT_FAULTED_IN, szAppState, ARRAYSIZE(szAppState));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// the darwin app is installed
|
||
|
LoadString(HINST_THISDLL, IDS_APP_FAULTED_IN, szAppState, ARRAYSIZE(szAppState));
|
||
|
}
|
||
|
|
||
|
SetWindowText(hwndTargetType, szAppState);
|
||
|
EnableWindow(hwndTargetType, TRUE);
|
||
|
|
||
|
// if we can ge the package name, put that in the Target field
|
||
|
if (pDarwinData &&
|
||
|
MsiGetProductInfo(pDarwinData->szwDarwinID,
|
||
|
INSTALLPROPERTY_PRODUCTNAME,
|
||
|
szAppState,
|
||
|
&cchAppState) == ERROR_SUCCESS)
|
||
|
{
|
||
|
SetWindowText(GetDlgItem(plpd->hDlg, IDD_FILENAME), szAppState);
|
||
|
}
|
||
|
|
||
|
if (pDarwinData)
|
||
|
{
|
||
|
LocalFree(pDarwinData);
|
||
|
}
|
||
|
|
||
|
// we disabled everything in _DisableAllChildren, so re-enable the ones we still apply for darwin
|
||
|
EnableWindow(GetDlgItem(plpd->hDlg, IDD_NAME), TRUE);
|
||
|
EnableWindow(GetDlgItem(plpd->hDlg, IDD_PATH), TRUE);
|
||
|
EnableWindow(GetDlgItem(plpd->hDlg, IDD_LINK_HOTKEY), TRUE);
|
||
|
EnableWindow(GetDlgItem(plpd->hDlg, IDD_LINK_SHOWCMD), TRUE);
|
||
|
EnableWindow(GetDlgItem(plpd->hDlg, IDD_LINK_DESCRIPTION), TRUE);
|
||
|
EnableWindow(GetDlgItem(plpd->hDlg, IDC_ADVANCED), TRUE);
|
||
|
|
||
|
// we skip all of the gook below if we are darwin since we only support the IDD_NAME, IDD_PATH, IDD_LINK_HOTKEY,
|
||
|
// IDD_LINK_SHOWCMD, and IDD_LINK_DESCRIPTION fields
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = plpd->psl->GetPath(szCommand, ARRAYSIZE(szCommand), NULL, SLGP_RAWPATH);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
hr = plpd->psl->GetPath(szCommand, ARRAYSIZE(szCommand), NULL, 0);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
plpd->bIsFile = TRUE;
|
||
|
|
||
|
// get type
|
||
|
if (!SHGetFileInfo(szCommand, 0, &sfi, sizeof(sfi), SHGFI_TYPENAME))
|
||
|
{
|
||
|
TCHAR szExp[MAX_PATH];
|
||
|
|
||
|
// Let's see if the string has expandable environment strings
|
||
|
if (SHExpandEnvironmentStrings(szCommand, szExp, ARRAYSIZE(szExp))
|
||
|
&& lstrcmp(szCommand, szExp)) // don't hit the disk a second time if the string hasn't changed
|
||
|
{
|
||
|
SHGetFileInfo(szExp, 0, &sfi, sizeof(sfi), SHGFI_TYPENAME);
|
||
|
}
|
||
|
}
|
||
|
SetDlgItemText(plpd->hDlg, IDD_FILETYPE, sfi.szTypeName);
|
||
|
|
||
|
// location
|
||
|
lstrcpy(szBuffer, szCommand);
|
||
|
PathRemoveFileSpec(szBuffer);
|
||
|
SetDlgItemText(plpd->hDlg, IDD_LOCATION, PathFindFileName(szBuffer));
|
||
|
|
||
|
// command
|
||
|
plpd->psl->GetArguments(szBuffer, ARRAYSIZE(szBuffer));
|
||
|
PathComposeWithArgs(szCommand, szBuffer);
|
||
|
GetDlgItemText(plpd->hDlg, IDD_FILENAME, szBuffer, ARRAYSIZE(szBuffer));
|
||
|
// Conditionally change to prevent "Apply" button from enabling
|
||
|
if (lstrcmp(szCommand, szBuffer) != 0)
|
||
|
SetDlgItemText(plpd->hDlg, IDD_FILENAME, szCommand);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LPITEMIDLIST pidl;
|
||
|
|
||
|
plpd->bIsFile = FALSE;
|
||
|
|
||
|
EnableWindow(GetDlgItem(plpd->hDlg, IDD_FILENAME), FALSE);
|
||
|
EnableWindow(GetDlgItem(plpd->hDlg, IDD_PATH), FALSE);
|
||
|
|
||
|
plpd->psl->GetIDList(&pidl);
|
||
|
|
||
|
if (pidl)
|
||
|
{
|
||
|
SHGetNameAndFlags(pidl, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szCommand, SIZECHARS(szCommand), NULL);
|
||
|
ILRemoveLastID(pidl);
|
||
|
SHGetNameAndFlags(pidl, SHGDN_NORMAL, szBuffer, SIZECHARS(szBuffer), NULL);
|
||
|
ILFree(pidl);
|
||
|
|
||
|
SetDlgItemText(plpd->hDlg, IDD_LOCATION, szBuffer);
|
||
|
SetDlgItemText(plpd->hDlg, IDD_FILETYPE, szCommand);
|
||
|
SetDlgItemText(plpd->hDlg, IDD_FILENAME, szCommand);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bUpdatePath)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
plpd->psl->GetWorkingDirectory(szBuffer, ARRAYSIZE(szBuffer));
|
||
|
SetDlgItemPath(plpd->hDlg, IDD_PATH, szBuffer);
|
||
|
|
||
|
plpd->psl->GetDescription(szBuffer, ARRAYSIZE(szBuffer));
|
||
|
SHLoadIndirectString(szBuffer, szBuffer, ARRAYSIZE(szBuffer), NULL); // will do nothing if the string isn't indirect
|
||
|
SetDlgItemText(plpd->hDlg, IDD_LINK_DESCRIPTION, szBuffer);
|
||
|
|
||
|
plpd->psl->GetHotkey(&wHotkey);
|
||
|
SendDlgItemMessage(plpd->hDlg, IDD_LINK_HOTKEY, HKM_SETHOTKEY, wHotkey, 0);
|
||
|
|
||
|
//
|
||
|
// Now initialize the Run SHOW Command combo box
|
||
|
//
|
||
|
for (iShowCmd = IDS_RUN_NORMAL; iShowCmd <= IDS_RUN_MAXIMIZED; iShowCmd++)
|
||
|
{
|
||
|
LoadString(HINST_THISDLL, iShowCmd, szBuffer, ARRAYSIZE(szBuffer));
|
||
|
SendDlgItemMessage(plpd->hDlg, IDD_LINK_SHOWCMD, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)szBuffer);
|
||
|
}
|
||
|
|
||
|
// Now setup the Show Command - Need to map to index numbers...
|
||
|
plpd->psl->GetShowCmd(&iShowCmd);
|
||
|
|
||
|
for (i = 0; i < ARRAYSIZE(c_iShowCmds); i++)
|
||
|
{
|
||
|
if (c_iShowCmds[i] == iShowCmd)
|
||
|
break;
|
||
|
}
|
||
|
if (i == ARRAYSIZE(c_iShowCmds))
|
||
|
{
|
||
|
ASSERT(0); // bogus link show cmd
|
||
|
i = 0; // SW_SHOWNORMAL
|
||
|
}
|
||
|
|
||
|
SendDlgItemMessage(plpd->hDlg, IDD_LINK_SHOWCMD, CB_SETCURSEL, i, 0);
|
||
|
|
||
|
// the icon
|
||
|
_UpdateLinkIcon(plpd, NULL);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Opens a folder window with the target of the link selected
|
||
|
//
|
||
|
void _FindTarget(LINKPROP_DATA *plpd)
|
||
|
{
|
||
|
if (plpd->psl->Resolve(plpd->hDlg, 0) == S_OK)
|
||
|
{
|
||
|
LPITEMIDLIST pidl;
|
||
|
|
||
|
_UpdateLinkDlg(plpd, TRUE);
|
||
|
|
||
|
plpd->psl->GetIDList(&pidl);
|
||
|
if (pidl)
|
||
|
{
|
||
|
SHOpenFolderAndSelectItems(pidl, 0, NULL, 0);
|
||
|
ILFree(pidl);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// let the user pick a new icon for a link...
|
||
|
|
||
|
BOOL _DoPickIcon(LINKPROP_DATA *plpd)
|
||
|
{
|
||
|
int iIconIndex;
|
||
|
SHFILEINFO sfi;
|
||
|
TCHAR *pszIconPath = sfi.szDisplayName;
|
||
|
IShellLinkDataList *psldl;
|
||
|
EXP_SZ_LINK *esli;
|
||
|
HRESULT hr;
|
||
|
|
||
|
*pszIconPath = 0;
|
||
|
|
||
|
//
|
||
|
// if the user has picked a icon before use it.
|
||
|
//
|
||
|
if (plpd->szIconPath[0] != 0 && plpd->iIconIndex >= 0)
|
||
|
{
|
||
|
lstrcpy(pszIconPath, plpd->szIconPath);
|
||
|
iIconIndex = plpd->iIconIndex;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// if this link has a icon use that.
|
||
|
//
|
||
|
plpd->psl->GetIconLocation(pszIconPath, MAX_PATH, &iIconIndex);
|
||
|
|
||
|
//
|
||
|
// check for an escaped version, if its there, use that
|
||
|
//
|
||
|
if (SUCCEEDED(hr = plpd->psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psldl))))
|
||
|
{
|
||
|
if (SUCCEEDED(hr = psldl->CopyDataBlock(EXP_SZ_ICON_SIG, (void **)&esli)))
|
||
|
{
|
||
|
ASSERT(esli);
|
||
|
#ifdef UNICODE
|
||
|
lstrcpyn(pszIconPath, esli->swzTarget, MAX_PATH);
|
||
|
#else
|
||
|
lstrcpyn(pszIconPath, esli->szTarget, MAX_PATH);
|
||
|
#endif
|
||
|
LocalFree(esli);
|
||
|
}
|
||
|
|
||
|
psldl->Release();
|
||
|
}
|
||
|
|
||
|
|
||
|
if (pszIconPath[0] == TEXT('.'))
|
||
|
{
|
||
|
TCHAR szFullIconPath[MAX_PATH];
|
||
|
|
||
|
// We now allow ".txt" for the icon path, but since the user is clicking
|
||
|
// on the "Change Icon..." button, we show the current icon that ".txt" is
|
||
|
// associated with
|
||
|
GetIconLocationFromExt(pszIconPath, szFullIconPath, ARRAYSIZE(szFullIconPath), &iIconIndex);
|
||
|
lstrcpyn(pszIconPath, szFullIconPath, ARRAYSIZE(sfi.szDisplayName));
|
||
|
}
|
||
|
else if (pszIconPath[0] == 0)
|
||
|
{
|
||
|
//
|
||
|
// link does not have a icon, if it is a link to a file
|
||
|
// use the file name
|
||
|
//
|
||
|
TCHAR szArgs[MAX_PATH];
|
||
|
_GetPathAndArgs(plpd, pszIconPath, szArgs);
|
||
|
|
||
|
iIconIndex = 0;
|
||
|
|
||
|
if (!plpd->bIsFile || !PathIsExe(pszIconPath))
|
||
|
{
|
||
|
//
|
||
|
// link is not to a file, go get the icon
|
||
|
//
|
||
|
SHGetFileInfo(plpd->szFile, 0, &sfi, sizeof(sfi), SHGFI_ICONLOCATION);
|
||
|
iIconIndex = sfi.iIcon;
|
||
|
ASSERT(pszIconPath == sfi.szDisplayName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (PickIconDlg(plpd->hDlg, pszIconPath, MAX_PATH, &iIconIndex))
|
||
|
{
|
||
|
HICON hIcon = ExtractIcon(HINST_THISDLL, pszIconPath, iIconIndex);
|
||
|
_UpdateLinkIcon(plpd, hIcon);
|
||
|
|
||
|
// don't save it out to the link yet, just store it in our instance data
|
||
|
plpd->iIconIndex = iIconIndex;
|
||
|
lstrcpy(plpd->szIconPath, pszIconPath);
|
||
|
|
||
|
PropSheet_Changed(GetParent(plpd->hDlg), plpd->hDlg);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDAPI SaveLink(LINKDATA *pld)
|
||
|
{
|
||
|
WORD wHotkey;
|
||
|
int iShowCmd;
|
||
|
IPersistFile *ppf;
|
||
|
HRESULT hr;
|
||
|
TCHAR szBuffer[MAX_PATH];
|
||
|
|
||
|
if (!(pld->plpd->bIsDirty || (pld->cpd.lpConsole && pld->cpd.bConDirty)))
|
||
|
return S_OK;
|
||
|
|
||
|
if (pld->plpd->bIsFile)
|
||
|
{
|
||
|
TCHAR szArgs[MAX_PATH];
|
||
|
|
||
|
_GetPathAndArgs(pld->plpd, szBuffer, szArgs);
|
||
|
|
||
|
// set the path (and pidl) of the link
|
||
|
pld->plpd->psl->SetPath(szBuffer);
|
||
|
|
||
|
// may be null
|
||
|
pld->plpd->psl->SetArguments(szArgs);
|
||
|
|
||
|
if (pld->plpd->bEnableRunInSepVDM && pld->plpd->bRunInSepVDM)
|
||
|
{
|
||
|
SetLinkFlags(pld->plpd->psl, SLDF_RUN_IN_SEPARATE, SLDF_RUN_IN_SEPARATE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetLinkFlags(pld->plpd->psl, 0, SLDF_RUN_IN_SEPARATE);
|
||
|
}
|
||
|
|
||
|
if (pld->plpd->bRunAsUser)
|
||
|
{
|
||
|
SetLinkFlags(pld->plpd->psl, SLDF_RUNAS_USER, SLDF_RUNAS_USER);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetLinkFlags(pld->plpd->psl, 0, SLDF_RUNAS_USER);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (pld->plpd->bIsFile || (SetLinkFlags(pld->plpd->psl, 0, 0) & SLDF_HAS_DARWINID))
|
||
|
{
|
||
|
// set the working directory of the link
|
||
|
GetDlgItemPath(pld->plpd->hDlg, IDD_PATH, szBuffer);
|
||
|
pld->plpd->psl->SetWorkingDirectory(szBuffer);
|
||
|
}
|
||
|
|
||
|
// set the description of the link if it changed.
|
||
|
TCHAR szOldComment[MAX_PATH];
|
||
|
pld->plpd->psl->GetDescription(szOldComment, ARRAYSIZE(szOldComment));
|
||
|
SHLoadIndirectString(szOldComment, szOldComment, ARRAYSIZE(szOldComment), NULL); // will do nothing if the string isn't indirect
|
||
|
GetDlgItemText(pld->plpd->hDlg, IDD_LINK_DESCRIPTION, szBuffer, ARRAYSIZE(szBuffer));
|
||
|
if (lstrcmp(szBuffer, szOldComment) != 0)
|
||
|
pld->plpd->psl->SetDescription(szBuffer);
|
||
|
|
||
|
// the hotkey
|
||
|
wHotkey = (WORD)SendDlgItemMessage(pld->plpd->hDlg, IDD_LINK_HOTKEY , HKM_GETHOTKEY, 0, 0);
|
||
|
pld->plpd->psl->SetHotkey(wHotkey);
|
||
|
|
||
|
// the show command combo box
|
||
|
iShowCmd = (int)SendDlgItemMessage(pld->plpd->hDlg, IDD_LINK_SHOWCMD, CB_GETCURSEL, 0, 0L);
|
||
|
if ((iShowCmd >= 0) && (iShowCmd < ARRAYSIZE(c_iShowCmds)))
|
||
|
{
|
||
|
pld->plpd->psl->SetShowCmd(c_iShowCmds[iShowCmd]);
|
||
|
}
|
||
|
|
||
|
// If the user explicitly selected a new icon, invalidate
|
||
|
// the icon cache entry for this link and then send around a file
|
||
|
// sys refresh message to all windows in case they are looking at
|
||
|
// this link.
|
||
|
if (pld->plpd->iIconIndex >= 0)
|
||
|
{
|
||
|
pld->plpd->psl->SetIconLocation(pld->plpd->szIconPath, pld->plpd->iIconIndex);
|
||
|
}
|
||
|
|
||
|
// Update/Save the console information in the pExtraData section of
|
||
|
// the shell link.
|
||
|
if (pld->cpd.lpConsole && pld->cpd.bConDirty)
|
||
|
{
|
||
|
LinkConsolePagesSave(pld);
|
||
|
}
|
||
|
|
||
|
hr = pld->plpd->psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
if (ppf->IsDirty() == S_OK)
|
||
|
{
|
||
|
// save using existing file name (pld->plpd->szFile)
|
||
|
hr = ppf->Save(NULL, TRUE);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
SHSysErrorMessageBox(pld->plpd->hDlg, NULL, IDS_LINKCANTSAVE,
|
||
|
hr & 0xFFF, PathFindFileName(pld->plpd->szFile),
|
||
|
MB_OK | MB_ICONEXCLAMATION);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pld->plpd->bIsDirty = FALSE;
|
||
|
}
|
||
|
}
|
||
|
ppf->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
void SetEditFocus(HWND hwnd)
|
||
|
{
|
||
|
SetFocus(hwnd);
|
||
|
Edit_SetSel(hwnd, 0, -1);
|
||
|
}
|
||
|
|
||
|
// returns:
|
||
|
// TRUE all link fields are valid
|
||
|
// FALSE some thing is wrong with what the user has entered
|
||
|
|
||
|
BOOL _ValidateLink(LINKPROP_DATA *plpd)
|
||
|
{
|
||
|
TCHAR szDir[MAX_PATH], szPath[MAX_PATH], szArgs[MAX_PATH];
|
||
|
TCHAR szExpPath[MAX_PATH];
|
||
|
BOOL bValidPath = FALSE;
|
||
|
|
||
|
if (!plpd->bIsFile)
|
||
|
return TRUE;
|
||
|
|
||
|
// validate the working directory field
|
||
|
|
||
|
GetDlgItemPath(plpd->hDlg, IDD_PATH, szDir);
|
||
|
|
||
|
if (*szDir &&
|
||
|
StrChr(szDir, TEXT('%')) == NULL && // has environement var %USER%
|
||
|
!IsRemovableDrive(DRIVEID(szDir)) &&
|
||
|
!PathIsDirectory(szDir))
|
||
|
{
|
||
|
ShellMessageBox(HINST_THISDLL, plpd->hDlg, MAKEINTRESOURCE(IDS_LINKBADWORKDIR),
|
||
|
MAKEINTRESOURCE(IDS_LINKERROR), MB_OK | MB_ICONEXCLAMATION, szDir);
|
||
|
|
||
|
SetEditFocus(GetDlgItem(plpd->hDlg, IDD_PATH));
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// validate the path (with arguments) field
|
||
|
|
||
|
_GetPathAndArgs(plpd, szPath, szArgs);
|
||
|
|
||
|
if (szPath[0] == 0)
|
||
|
return TRUE;
|
||
|
|
||
|
if (PathIsRoot(szPath) && IsRemovableDrive(DRIVEID(szPath)))
|
||
|
return TRUE;
|
||
|
|
||
|
if (PathIsLnk(szPath))
|
||
|
{
|
||
|
ShellMessageBox(HINST_THISDLL, plpd->hDlg, MAKEINTRESOURCE(IDS_LINKTOLINK),
|
||
|
MAKEINTRESOURCE(IDS_LINKERROR), MB_OK | MB_ICONEXCLAMATION);
|
||
|
SetEditFocus(GetDlgItem(plpd->hDlg, IDD_FILENAME));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
LPCTSTR dirs[2];
|
||
|
dirs[0] = szDir;
|
||
|
dirs[1] = NULL;
|
||
|
bValidPath = PathResolve(szPath, dirs, PRF_DONTFINDLNK | PRF_TRYPROGRAMEXTENSIONS);
|
||
|
if (!bValidPath)
|
||
|
{
|
||
|
// The path "as is" was invalid. See if it has environment variables
|
||
|
// which need to be expanded.
|
||
|
|
||
|
_GetPathAndArgs(plpd, szPath, szArgs);
|
||
|
|
||
|
if (SHExpandEnvironmentStrings(szPath, szExpPath, ARRAYSIZE(szExpPath)))
|
||
|
{
|
||
|
if (PathIsRoot(szExpPath) && IsRemovableDrive(DRIVEID(szDir)))
|
||
|
return TRUE;
|
||
|
|
||
|
bValidPath = PathResolve(szExpPath, dirs, PRF_DONTFINDLNK | PRF_TRYPROGRAMEXTENSIONS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bValidPath)
|
||
|
{
|
||
|
BOOL bSave;
|
||
|
|
||
|
if (plpd->bLinkThreadIsAlive)
|
||
|
{
|
||
|
bSave = plpd->bCheckRunInSep;
|
||
|
plpd->bCheckRunInSep = FALSE;
|
||
|
}
|
||
|
PathComposeWithArgs(szPath, szArgs);
|
||
|
GetDlgItemText(plpd->hDlg, IDD_FILENAME, szExpPath, ARRAYSIZE(szExpPath));
|
||
|
// only do this if something changed... that way we avoid having the PSM_CHANGED
|
||
|
// for nothing
|
||
|
if (lstrcmpi(szPath, szExpPath))
|
||
|
SetDlgItemText(plpd->hDlg, IDD_FILENAME, szPath);
|
||
|
|
||
|
if (plpd->bLinkThreadIsAlive)
|
||
|
{
|
||
|
plpd->bCheckRunInSep = bSave;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
ShellMessageBox(HINST_THISDLL, plpd->hDlg, MAKEINTRESOURCE(IDS_LINKBADPATH),
|
||
|
MAKEINTRESOURCE(IDS_LINKERROR), MB_OK | MB_ICONEXCLAMATION, szPath);
|
||
|
SetEditFocus(GetDlgItem(plpd->hDlg, IDD_FILENAME));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Array for context help:
|
||
|
const DWORD aLinkHelpIDs[] = {
|
||
|
IDD_LINE_1, NO_HELP,
|
||
|
IDD_LINE_2, NO_HELP,
|
||
|
IDD_ITEMICON, IDH_FCAB_LINK_ICON,
|
||
|
IDD_NAME, IDH_FCAB_LINK_NAME,
|
||
|
IDD_FILETYPE_TXT, IDH_FCAB_LINK_LINKTYPE,
|
||
|
IDD_FILETYPE, IDH_FCAB_LINK_LINKTYPE,
|
||
|
IDD_LOCATION_TXT, IDH_FCAB_LINK_LOCATION,
|
||
|
IDD_LOCATION, IDH_FCAB_LINK_LOCATION,
|
||
|
IDD_FILENAME, IDH_FCAB_LINK_LINKTO,
|
||
|
IDD_PATH, IDH_FCAB_LINK_WORKING,
|
||
|
IDD_LINK_HOTKEY, IDH_FCAB_LINK_HOTKEY,
|
||
|
IDD_LINK_SHOWCMD, IDH_FCAB_LINK_RUN,
|
||
|
IDD_LINK_DESCRIPTION, IDH_FCAB_LINK_DESCRIPTION,
|
||
|
IDD_FINDORIGINAL, IDH_FCAB_LINK_FIND,
|
||
|
IDD_LINKDETAILS, IDH_FCAB_LINK_CHANGEICON,
|
||
|
0, 0
|
||
|
};
|
||
|
|
||
|
// Array for context help (Advanced Dlg):
|
||
|
const DWORD aAdvancedLinkHelpIDs[] = {
|
||
|
IDD_RUNINSEPARATE, IDH_TRAY_RUN_SEPMEM,
|
||
|
IDD_LINK_RUNASUSER, IDH_FCAB_LINK_RUNASUSER,
|
||
|
0,0
|
||
|
};
|
||
|
|
||
|
UINT g_msgActivateDesktop = 0;
|
||
|
|
||
|
DWORD CALLBACK _LinkAddRefSyncCallBack(void *pv)
|
||
|
{
|
||
|
LINKPROP_DATA *plpd = (LINKPROP_DATA *)pv;
|
||
|
AddRef_LinkPropData(plpd);
|
||
|
plpd->bLinkThreadIsAlive = TRUE;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Dialog proc for the generic link property sheet
|
||
|
//
|
||
|
// uses DLG_LINKPROP template
|
||
|
|
||
|
BOOL_PTR CALLBACK _LinkAdvancedDlgProc(HWND hDlgAdvanced, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
LINKPROP_DATA *plpd = (LINKPROP_DATA *)GetWindowLongPtr(hDlgAdvanced, DWLP_USER);
|
||
|
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
{
|
||
|
TCHAR szFullFile[MAX_PATH];
|
||
|
DWORD cchVerb;
|
||
|
UINT cch;
|
||
|
|
||
|
plpd = (LINKPROP_DATA *)lParam;
|
||
|
SetWindowLongPtr(hDlgAdvanced, DWLP_USER, (LPARAM)plpd);
|
||
|
|
||
|
plpd->hDlgAdvanced = hDlgAdvanced;
|
||
|
|
||
|
cch = _GetTargetOfLink(plpd, szFullFile);
|
||
|
|
||
|
if ((cch != 0) && (cch < ARRAYSIZE(szFullFile)))
|
||
|
{
|
||
|
DWORD dwBinaryType;
|
||
|
|
||
|
// enable "run in seperate VDM" if this is a 16-bit image
|
||
|
if (GetBinaryType(szFullFile, &dwBinaryType) && (dwBinaryType == SCS_WOW_BINARY))
|
||
|
{
|
||
|
if (SetLinkFlags(plpd->psl, 0, 0) & SLDF_RUN_IN_SEPARATE)
|
||
|
{
|
||
|
EnableWindow(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE), TRUE);
|
||
|
CheckDlgButton(hDlgAdvanced, IDD_RUNINSEPARATE, BST_CHECKED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
EnableWindow(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE), TRUE);
|
||
|
CheckDlgButton(hDlgAdvanced, IDD_RUNINSEPARATE, BST_UNCHECKED);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// check it
|
||
|
CheckDlgButton(hDlgAdvanced, IDD_RUNINSEPARATE, BST_CHECKED);
|
||
|
EnableWindow(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE), FALSE);
|
||
|
}
|
||
|
|
||
|
// enable "runas" if the link target has that verb
|
||
|
if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_COMMAND, szFullFile, TEXT("runas"), NULL, &cchVerb)) &&
|
||
|
cchVerb)
|
||
|
{
|
||
|
EnableWindow(GetDlgItem(hDlgAdvanced, IDD_LINK_RUNASUSER), TRUE);
|
||
|
CheckDlgButton(hDlgAdvanced, IDD_LINK_RUNASUSER, (SetLinkFlags(plpd->psl, 0, 0) & SLDF_RUNAS_USER) ? BST_CHECKED : BST_UNCHECKED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
EnableWindow(GetDlgItem(hDlgAdvanced, IDD_LINK_RUNASUSER), FALSE);
|
||
|
CheckDlgButton(hDlgAdvanced, IDD_LINK_RUNASUSER, BST_UNCHECKED);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// fall back to disabling everything
|
||
|
CheckDlgButton(hDlgAdvanced, IDD_RUNINSEPARATE, BST_CHECKED);
|
||
|
EnableWindow(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE), FALSE);
|
||
|
EnableWindow(GetDlgItem(hDlgAdvanced, IDD_LINK_RUNASUSER), FALSE);
|
||
|
}
|
||
|
|
||
|
// get the initial state of the checkboxes
|
||
|
plpd->bEnableRunInSepVDM = IsWindowEnabled(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE));
|
||
|
plpd->bRunInSepVDM = IsDlgButtonChecked(hDlgAdvanced, IDD_RUNINSEPARATE);
|
||
|
plpd->bRunAsUser = IsDlgButtonChecked(hDlgAdvanced, IDD_LINK_RUNASUSER);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
{
|
||
|
UINT idControl = GET_WM_COMMAND_ID(wParam, lParam);
|
||
|
|
||
|
switch (idControl)
|
||
|
{
|
||
|
case IDD_RUNINSEPARATE:
|
||
|
case IDD_LINK_RUNASUSER:
|
||
|
plpd->bIsDirty = TRUE;
|
||
|
break;
|
||
|
|
||
|
case IDOK:
|
||
|
// get the final state of the checkboxes
|
||
|
plpd->bEnableRunInSepVDM = IsWindowEnabled(GetDlgItem(hDlgAdvanced, IDD_RUNINSEPARATE));
|
||
|
plpd->bRunInSepVDM = IsDlgButtonChecked(hDlgAdvanced, IDD_RUNINSEPARATE);
|
||
|
plpd->bRunAsUser = IsDlgButtonChecked(hDlgAdvanced, IDD_LINK_RUNASUSER);
|
||
|
// fall through
|
||
|
|
||
|
case IDCANCEL:
|
||
|
ReplaceDlgIcon(hDlgAdvanced, IDD_ITEMICON, NULL);
|
||
|
plpd->hDlgAdvanced = NULL;
|
||
|
EndDialog(hDlgAdvanced, (idControl == IDCANCEL) ? FALSE : TRUE);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_HELP:
|
||
|
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR)aAdvancedLinkHelpIDs);
|
||
|
break;
|
||
|
|
||
|
case WM_CONTEXTMENU:
|
||
|
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aAdvancedLinkHelpIDs);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL_PTR CALLBACK _LinkDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
LINKDATA *pld = (LINKDATA *)GetWindowLongPtr(hdlg, DWLP_USER);
|
||
|
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
|
||
|
pld = (LINKDATA *)((PROPSHEETPAGE *)lParam)->lParam;
|
||
|
SetWindowLongPtr(hdlg, DWLP_USER, (LPARAM)pld);
|
||
|
|
||
|
// setup dialog state variables
|
||
|
|
||
|
pld->plpd->hDlg = hdlg;
|
||
|
|
||
|
SendDlgItemMessage(hdlg, IDD_FILENAME, EM_LIMITTEXT, MAX_PATH-1, 0);
|
||
|
SetPathWordBreakProc(GetDlgItem(hdlg, IDD_FILENAME), TRUE);
|
||
|
SendDlgItemMessage(hdlg, IDD_PATH, EM_LIMITTEXT, MAX_PATH-1, 0);
|
||
|
SetPathWordBreakProc(GetDlgItem(hdlg, IDD_PATH), TRUE);
|
||
|
SendDlgItemMessage(hdlg, IDD_LINK_DESCRIPTION, EM_LIMITTEXT, MAX_PATH-1, 0);
|
||
|
|
||
|
// set valid combinations for the hotkey
|
||
|
SendDlgItemMessage(hdlg, IDD_LINK_HOTKEY, HKM_SETRULES,
|
||
|
HKCOMB_NONE | HKCOMB_A | HKCOMB_S | HKCOMB_C,
|
||
|
HOTKEYF_CONTROL | HOTKEYF_ALT);
|
||
|
|
||
|
SHAutoComplete(GetDlgItem(hdlg, IDD_FILENAME), 0);
|
||
|
SHAutoComplete(GetDlgItem(hdlg, IDD_PATH), 0);
|
||
|
|
||
|
ASSERT(pld->plpd->bLinkThreadIsAlive == FALSE);
|
||
|
|
||
|
_UpdateLinkDlg(pld->plpd, FALSE);
|
||
|
|
||
|
// Set up background thread to handle "Run In Separate Memory Space"
|
||
|
// check box.
|
||
|
pld->plpd->bCheckRunInSep = TRUE;
|
||
|
if (pld->plpd->hCheckNow)
|
||
|
{
|
||
|
SHCreateThread(_LinkCheckThreadProc, pld->plpd, 0, _LinkAddRefSyncCallBack);
|
||
|
}
|
||
|
|
||
|
// start off clean.
|
||
|
// do this here because we call some stuff above which generates
|
||
|
// wm_command/en_changes which we then think makes it dirty
|
||
|
pld->plpd->bIsDirty = FALSE;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case WM_DESTROY:
|
||
|
ReplaceDlgIcon(pld->plpd->hDlg, IDD_ITEMICON, NULL);
|
||
|
_StopThread(pld->plpd);
|
||
|
break;
|
||
|
|
||
|
case WM_NOTIFY:
|
||
|
switch (((NMHDR *)lParam)->code)
|
||
|
{
|
||
|
case PSN_RESET:
|
||
|
_StopThread(pld->plpd);
|
||
|
break;
|
||
|
case PSN_APPLY:
|
||
|
|
||
|
if ((((PSHNOTIFY *)lParam)->lParam))
|
||
|
_StopThread(pld->plpd);
|
||
|
|
||
|
if (FAILED(SaveLink(pld)))
|
||
|
SetWindowLongPtr(hdlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
|
||
|
break;
|
||
|
|
||
|
case PSN_KILLACTIVE:
|
||
|
// we implement the save on page change model, so
|
||
|
// validate and save changes here. this works for
|
||
|
// Apply Now, OK, and Page chagne.
|
||
|
|
||
|
SetWindowLongPtr(hdlg, DWLP_MSGRESULT, !_ValidateLink(pld->plpd)); // don't allow close
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
||
|
{
|
||
|
case IDD_FINDORIGINAL:
|
||
|
_FindTarget(pld->plpd);
|
||
|
break;
|
||
|
|
||
|
case IDD_LINKDETAILS:
|
||
|
if (_DoPickIcon(pld->plpd))
|
||
|
pld->plpd->bIsDirty = TRUE;
|
||
|
break;
|
||
|
|
||
|
case IDD_LINK_SHOWCMD:
|
||
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE)
|
||
|
{
|
||
|
PropSheet_Changed(GetParent(hdlg), hdlg);
|
||
|
pld->plpd->bIsDirty = TRUE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case IDD_LINK_HOTKEY:
|
||
|
case IDD_FILENAME:
|
||
|
case IDD_PATH:
|
||
|
case IDD_LINK_DESCRIPTION:
|
||
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
|
||
|
{
|
||
|
PropSheet_Changed(GetParent(hdlg), hdlg);
|
||
|
pld->plpd->bIsDirty = TRUE;
|
||
|
if (pld->plpd->bLinkThreadIsAlive && pld->plpd->bCheckRunInSep)
|
||
|
SetEvent(pld->plpd->hCheckNow);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case IDC_ADVANCED:
|
||
|
if ((DialogBoxParam(HINST_THISDLL,
|
||
|
MAKEINTRESOURCE(DLG_LINKPROP_ADVANCED),
|
||
|
hdlg,
|
||
|
_LinkAdvancedDlgProc,
|
||
|
(LPARAM)pld->plpd) == TRUE) &&
|
||
|
(pld->plpd->bIsDirty == TRUE))
|
||
|
{
|
||
|
// something on the advanced page changed
|
||
|
PropSheet_Changed(GetParent(hdlg), hdlg);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_HELP:
|
||
|
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aLinkHelpIDs);
|
||
|
break;
|
||
|
|
||
|
case WM_CONTEXTMENU:
|
||
|
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aLinkHelpIDs);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if (0 == g_msgActivateDesktop)
|
||
|
g_msgActivateDesktop = RegisterWindowMessage(TEXT("ActivateDesktop"));
|
||
|
|
||
|
if (msg == g_msgActivateDesktop)
|
||
|
{
|
||
|
HWND hwnd = FindWindow(TEXT(STR_DESKTOPCLASS), NULL);
|
||
|
SwitchToThisWindow(GetLastActivePopup(hwnd), TRUE);
|
||
|
SetForegroundWindow(hwnd);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Release the link object allocated during the initialize
|
||
|
//
|
||
|
UINT CALLBACK _LinkPrshtCallback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
|
||
|
{
|
||
|
LINKDATA *pld = (LINKDATA *)((PROPSHEETPAGE *)ppsp->lParam);
|
||
|
switch (uMsg)
|
||
|
{
|
||
|
case PSPCB_RELEASE:
|
||
|
if (pld->cpd.lpConsole)
|
||
|
{
|
||
|
LocalFree(pld->cpd.lpConsole);
|
||
|
}
|
||
|
if (pld->cpd.lpFEConsole)
|
||
|
{
|
||
|
LocalFree(pld->cpd.lpFEConsole);
|
||
|
}
|
||
|
DestroyFonts(&pld->cpd);
|
||
|
Release_LinkPropData(pld->plpd);
|
||
|
LocalFree(pld);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
STDAPI_(BOOL) AddLinkPage(LPCTSTR pszFile, LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
|
||
|
{
|
||
|
IShellLink *psl;
|
||
|
if (PathIsLnk(pszFile) && SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl))))
|
||
|
{
|
||
|
// alloc this data, since is it shared across several pages
|
||
|
// instead of putting it in as extra data in the page header
|
||
|
LINKDATA *pld = (LINKDATA *)LocalAlloc(LPTR, sizeof(*pld));
|
||
|
if (pld)
|
||
|
{
|
||
|
pld->plpd = Create_LinkPropData();
|
||
|
if (pld->plpd)
|
||
|
{
|
||
|
PROPSHEETPAGE psp;
|
||
|
|
||
|
psp.dwSize = sizeof(psp);
|
||
|
psp.dwFlags = PSP_DEFAULT | PSP_USECALLBACK;
|
||
|
psp.hInstance = HINST_THISDLL;
|
||
|
psp.pszTemplate = MAKEINTRESOURCE(DLG_LINKPROP);
|
||
|
psp.pfnDlgProc = _LinkDlgProc;
|
||
|
psp.pfnCallback = _LinkPrshtCallback;
|
||
|
psp.lParam = (LPARAM)pld; // pass to all dlg procs
|
||
|
|
||
|
lstrcpyn(pld->plpd->szFile, pszFile, ARRAYSIZE(pld->plpd->szFile));
|
||
|
pld->plpd->iIconIndex = -1;
|
||
|
pld->plpd->psl = psl;
|
||
|
ASSERT(!pld->plpd->szIconPath[0]);
|
||
|
|
||
|
HPROPSHEETPAGE hpage = CreatePropertySheetPage(&psp);
|
||
|
if (hpage)
|
||
|
{
|
||
|
if (pfnAddPage(hpage, lParam))
|
||
|
{
|
||
|
// Add console property pages if appropriate...
|
||
|
AddLinkConsolePages(pld, psl, pszFile, pfnAddPage, lParam);
|
||
|
return TRUE; // we added the link page
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DestroyPropertySheetPage(hpage);
|
||
|
}
|
||
|
}
|
||
|
Release_LinkPropData(pld->plpd);
|
||
|
|
||
|
}
|
||
|
LocalFree(pld);
|
||
|
}
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|