Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

4327 lines
131 KiB

/*
* shlexec.c -
*
* Implements the ShellExecuteEx() function
*/
#include "shellprv.h"
#pragma hdrstop
// Recent MRU variables
HANDLE g_hMRURecent = NULL;
LONG g_uMRURecentRef = 0;
// this is used to make sure we're loaded at the same address.
// see OpenRecentdocsMru
MRUCMPPROC g_pfnRecentDocsCompare = NULL;
#define CH_GUIDFIRST TEXT('{') // '}'
// These fake ERROR_ values are used to display non-winerror.h available error
// messages. They are mapped to valid winerror.h values in _ShellExecuteError.
#define ERROR_RESTRICTED_APP ((UINT)-1)
#define SEE_MASK_CLASS (SEE_MASK_CLASSNAME|SEE_MASK_CLASSKEY)
#define _UseClassName(_mask) (((_mask)&SEE_MASK_CLASS) == SEE_MASK_CLASSNAME)
#define _UseClassKey(_mask) (((_mask)&SEE_MASK_CLASS) == SEE_MASK_CLASSKEY)
#define _UseTitleName(_mask) (((_mask)&SEE_MASK_HASTITLE) || ((_mask)&SEE_MASK_HASLINKNAME))
#define SEE_MASK_PIDL (SEE_MASK_IDLIST|SEE_MASK_INVOKEIDLIST)
#define _UseIDList(_mask) (((_mask)&SEE_MASK_PIDL) == SEE_MASK_IDLIST)
#define _InvokeIDList(_mask) (((_mask)&SEE_MASK_PIDL) == SEE_MASK_INVOKEIDLIST)
// secret kernel api to get name of missing 16 bit component
extern int WINAPI PK16FNF(TCHAR *szBuffer);
HINSTANCE _DDEExecute(
HWND hwndParent,
ATOM aApplication,
ATOM aTopic,
LPCTSTR lpCommand,
LPCTSTR lpFile,
LPCTSTR lpParms,
int nShowCmd,
DWORD dwHotKey,
BOOL fLFNAware,
BOOL fWaitForDDE,
BOOL fActivateHandler,
LPCITEMIDLIST lpID,
LPITEMIDLIST *ppidlGlobal);
BOOL _ShellExecPidl(LPSHELLEXECUTEINFO pei, LPITEMIDLIST pidlExec);
// in exec2.c
int FindAssociatedExe(HWND hwnd, LPTSTR lpCommand, LPCTSTR pszDocument, HKEY hkeyProgID);
// BUGBUG: There's an lstrcatn in inc16\windows.h, why don't we use it?
void lstrcatN(LPTSTR pszDest, LPCTSTR pszSrc, UINT cchMax)
{
UINT cch = lstrlen(pszDest);
VDATEINPUTBUF(pszDest, TCHAR, cchMax);
if (cch < cchMax)
lstrcpyn(pszDest+cch, pszSrc, cchMax-cch);
}
//----------------------------------------------------------------------------
HINSTANCE Window_GetInstance(HWND hwnd)
{
DWORD idProcess;
GetWindowThreadProcessId(hwnd, &idProcess);
#ifdef WINNT
return (HINSTANCE)idProcess;
#else
return (HINSTANCE)GetProcessDword(idProcess,GPD_HINST);
#endif
}
/* _RoundRobinWindows:
* A silly little enumproc to find any window (EnumWindows) which has a
* matching EXE file path. The desired match EXE pathname is pointed to
* by the lParam. The found window's handle is stored in the
* first word of this buffer.
*/
BOOL CALLBACK _RoundRobinWindows(HWND hWnd, LPARAM lParam)
{
TCHAR szT[MAX_PATH];
HINSTANCE hInstance;
#ifdef DEBUG
// char szWnd[MAX_PATH];
#endif
// Filter out invisible and disabled windows
if (!IsWindowEnabled(hWnd) || !IsWindowVisible(hWnd))
return TRUE;
hInstance = Window_GetInstance(hWnd);
// NB We are trying to get the filename from a 16bit hinst so
// we need to thunk down.
GetModuleFileName16(hInstance, szT, ARRAYSIZE(szT) - 1);
#ifdef DEBUG
// GetWindowText(hWnd, szWnd, ARRAYSIZE(szWnd));
// DebugMsg(DM_TRACE, "s.rrw: %x %x %s %s", hWnd, hInstance, szWnd, szT);
#endif
if (lstrcmpi(PathFindFileName(szT), PathFindFileName((LPTSTR)lParam)) == 0)
{
*(HWND *)lParam = hWnd;
return FALSE;
}
return TRUE;
}
/* Find a "main" window that is the ancestor of a given window
*/
HWND _GetAncestorWindow(HWND hwnd)
{
HWND hwndT;
/* First go up the parent chain to find the popup window. Then go
* up the owner chain to find the main window
*/
while (NULL != (hwndT = GetWindow(hwnd, GW_OWNER)))
hwnd = hwndT;
return(hwnd);
}
/*
* A helper for RealShellExecute which finds the top-level window of a
* program which came from a particular EXE file. Returns NULL if none
* was found.
* Assumes: finding ultimate parent/owner of any window which matches EXEs
* is the top-level window desired. If we had an EnumTopLevelWindows...
*/
HWND _FindPopupFromExe(LPTSTR lpExe)
{
HWND hwnd;
BOOL b;
TCHAR szExe[MAX_PATH];
lstrcpyn(szExe, lpExe, ARRAYSIZE(szExe));
PathUnquoteSpaces(szExe);
b = EnumWindows(_RoundRobinWindows, (LPARAM)szExe);
if (b)
return NULL;
hwnd = *(HWND *)szExe;
if (hwnd == NULL)
return NULL;
// Climbing up parents/owners not strictly necessary for two reasons:
// * EnumWindows is usually going to find the "main" window first for
// each app anyway,
// * Our usage here passes it on to ActivateHandler, which climbs for
// us.
return _GetAncestorWindow(hwnd);
}
#define COPYTODST(_szdst, _szend, _szsrc, _ulen, _ret) \
{ \
UINT _utemp = _ulen; \
if ((UINT)(_szend-_szdst) <= _utemp) { \
return(_ret); \
} \
lstrcpyn(_szdst, _szsrc, _utemp+1); \
_szdst += _utemp; \
}
/* Returns NULL if this is the last parm, pointer to next space otherwise
*/
LPTSTR _GetNextParm(LPCTSTR lpSrc, LPTSTR lpDst, UINT cchDst)
{
LPCTSTR lpNextQuote, lpNextSpace;
LPTSTR lpEnd = lpDst+cchDst-1; // dec to account for trailing NULL
BOOL fQuote; // quoted string?
BOOL fDoubleQuote; // is this quote a double quote?
VDATEINPUTBUF(lpDst, TCHAR, cchDst);
while (*lpSrc == TEXT(' '))
++lpSrc;
if (!*lpSrc)
return(NULL);
fQuote = (*lpSrc == TEXT('"'));
if (fQuote)
lpSrc++; // skip leading quote
for (;;)
{
lpNextQuote = StrChr(lpSrc, TEXT('"'));
if (!fQuote)
{
// for an un-quoted string, copy all chars to first space/null
lpNextSpace = StrChr(lpSrc, TEXT(' '));
if (!lpNextSpace) // null before space! (end of string)
{
if (!lpNextQuote)
{
// copy all chars to the null
if (lpDst)
{
COPYTODST(lpDst, lpEnd, lpSrc, lstrlen(lpSrc), NULL);
}
return NULL;
}
else
{
// we have a quote to convert. Fall through.
}
}
else if (!lpNextQuote || lpNextSpace < lpNextQuote)
{
// copy all chars to the space
if (lpDst)
{
COPYTODST(lpDst, lpEnd, lpSrc, lpNextSpace-lpSrc, NULL);
}
return (LPTSTR)lpNextSpace;
}
else
{
// quote before space. Fall through to convert quote.
}
}
else if (!lpNextQuote)
{
// a quoted string without a terminating quote? Illegal!
Assert(0);
return NULL;
}
// we have a potential quote to convert
Assert(lpNextQuote);
fDoubleQuote = *(lpNextQuote+1) == TEXT('"');
if (fDoubleQuote)
lpNextQuote++; // so the quote is copied
if (lpDst)
{
COPYTODST(lpDst, lpEnd, lpSrc, lpNextQuote-lpSrc, NULL);
}
lpSrc = lpNextQuote+1;
if (!fDoubleQuote)
{
// we just copied the rest of this quoted string. if this wasn't
// quoted, it's an illegal string... treat the quote as a space.
Assert(fQuote);
return (LPTSTR)lpSrc;
}
}
}
//----------------------------------------------------------------------------
#define PEMAGIC ((WORD)'P'+((WORD)'E'<<8))
// #define NEMAGIC ((WORD)'N'+((WORD)'E'<<8)) // defined in newexe.h
//----------------------------------------------------------------------------
// Returns TRUE is app is LFN aware.
// NB This simply assumes all Win4.0 and all Win32 apps are LFN aware.
BOOL App_IsLFNAware(LPCTSTR pszFile)
{
DWORD dw;
Assert(pszFile);
Assert(*pszFile);
// Assume Win 4.0 apps and Win32 apps are LFN aware.
dw = GetExeType(pszFile);
// DebugMsg(DM_TRACE, "s.aila: %s %s %x", lpszFile, szFile, dw);
if ((LOWORD(dw) == PEMAGIC) || ((LOWORD(dw) == NEMAGIC) && (HIWORD(dw) >= 0x0400)))
{
return TRUE;
}
else
{
return FALSE;
}
}
//----------------------------------------------------------------------------
BOOL _AppIsLFNAware(LPCTSTR lpszFile)
{
TCHAR szFile[MAX_PATH];
LPTSTR pszArgs;
// Does it look like a DDE command?
if (lpszFile && *lpszFile && (*lpszFile != TEXT('[')))
{
// Nope - Hopefully just a regular old command %1 thing.
lstrcpyn(szFile, lpszFile, ARRAYSIZE(szFile));
pszArgs = PathGetArgs(szFile);
if (*pszArgs)
*(pszArgs - 1) = TEXT('\0');
PathRemoveBlanks(szFile); // remove any blanks that may be after the command
PathUnquoteSpaces(szFile);
return App_IsLFNAware(szFile);
}
return FALSE;
}
// in:
// lpFile exe name (used for %0 or %1 in replacement string)
// lpFrom string template to sub params and file into "excel.exe %1 %2 /n %3"
// lpParams parameter list "foo.txt bar.txt"
// out:
// lpTo output string with all parameters replaced
//
// supports:
// %* replace with all parameters
// %0, %1 replace with file
// %n use nth parameter
//
// replace parameter placeholders (%1 %2 ... %n) with parameters
// BUGBUG, we need to make sure we don't do more than MAX_PATH bytes into lpTo
UINT ReplaceParameters(LPTSTR lpTo, UINT cchTo, LPCTSTR lpFile,
LPCTSTR lpFrom, LPCTSTR lpParms, int nShow, DWORD * pdwHotKey, BOOL fLFNAware,
LPCITEMIDLIST lpID, LPITEMIDLIST *ppidlGlobal)
{
int i;
TCHAR c;
LPCTSTR lpT;
TCHAR sz[MAX_PATH];
BOOL fFirstParam = TRUE;
LPTSTR lpEnd = lpTo + cchTo - 1; // dec to allow trailing NULL
LPTSTR pToOrig = lpTo;
for ( ; *lpFrom; lpFrom++)
{
if (*lpFrom == TEXT('%'))
{
switch (*(++lpFrom))
{
case TEXT('~'): // Copy all parms starting with nth (n >= 2 and <= 9)
c = *(++lpFrom);
if (c >= TEXT('2') && c <= TEXT('9'))
{
for (i = 2, lpT = lpParms; i < c-TEXT('0') && lpT; i++)
{
lpT = _GetNextParm(lpT, NULL, 0);
}
if (lpT)
{
COPYTODST(lpTo, lpEnd, lpT, lstrlen(lpT), SE_ERR_ACCESSDENIED);
}
}
else
{
lpFrom -= 2; // Backup over %~ and pass through
goto NormalChar;
}
break;
case TEXT('*'): // Copy all parms
if (lpParms)
{
COPYTODST(lpTo, lpEnd, lpParms, lstrlen(lpParms), SE_ERR_ACCESSDENIED);
}
break;
case TEXT('0'):
case TEXT('1'):
// %0, %1, copy the file name
// If the filename comes first then we don't need to convert it to
// a shortname. If it appears anywhere else and the app is not LFN
// aware then we must.
if (!(fFirstParam || fLFNAware || _AppIsLFNAware(pToOrig)) &&
GetShortPathName(lpFile, sz, ARRAYSIZE(sz)) > 0)
{
DebugMsg(DM_TRACE, TEXT("s.see: Getting short version of path."));
COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
}
else
{
DebugMsg(DM_TRACE, TEXT("s.see: Using long version of path."));
COPYTODST(lpTo, lpEnd, lpFile, lstrlen(lpFile), SE_ERR_ACCESSDENIED);
}
break;
case TEXT('2'):
case TEXT('3'):
case TEXT('4'):
case TEXT('5'):
case TEXT('6'):
case TEXT('7'):
case TEXT('8'):
case TEXT('9'):
for (i = *lpFrom-TEXT('2'), lpT = lpParms; lpT; --i)
{
if (i)
lpT = _GetNextParm(lpT, NULL, 0);
else
{
_GetNextParm(lpT, sz, ARRAYSIZE(sz));
COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
break;
}
}
break;
case TEXT('s'):
case TEXT('S'):
wsprintf(sz, TEXT("%ld"), nShow);
COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
break;
case TEXT('h'):
case TEXT('H'):
wsprintf(sz, TEXT("%X"), pdwHotKey ? *pdwHotKey : 0);
COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
if (pdwHotKey)
*pdwHotKey = 0;
break;
// Note that a new global IDList is created for each
case TEXT('i'):
case TEXT('I'):
// Note that a single global ID list is created and used over
// again, so that it may be easily destroyed if anything
// goes wrong
if (ppidlGlobal)
{
if (lpID && !*ppidlGlobal)
{
*ppidlGlobal = (LPITEMIDLIST)SHAllocShared(lpID,ILGetSize(lpID),GetCurrentProcessId());
if (!*ppidlGlobal)
{
return(SE_ERR_OOM);
}
}
wsprintf(sz, TEXT(":%ld:%ld"), *ppidlGlobal,GetCurrentProcessId());
}
else
{
lstrcpy(sz,TEXT(":0"));
}
COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
break;
case TEXT('l'):
case TEXT('L'):
// Like %1 only using the long name.
// REVIEW UNDONE IANEL Remove the fFirstParam and fLFNAware crap as soon as this
// is up and running.
DebugMsg(DM_TRACE, TEXT("s.see: Using long version of path."));
COPYTODST(lpTo, lpEnd, lpFile, lstrlen(lpFile), SE_ERR_ACCESSDENIED);
break;
default:
goto NormalChar;
}
// DebugMsg(DM_TRACE, "s.rp: Past first param (1).");
fFirstParam = FALSE;
}
else
{
NormalChar:
// not a "%?" thing, just copy this to the destination
if (lpEnd-lpTo < 2)
{
// Always check for room for DBCS char
return(SE_ERR_ACCESSDENIED);
}
*lpTo++ = *lpFrom;
// Special case for things like "%1" ie don't clear the first param flag
// if we hit a dbl-quote.
if (*lpFrom != TEXT('"'))
{
// DebugMsg(DM_TRACE, "s.rp: Past first param (2).");
fFirstParam = FALSE;
}
else if (IsDBCSLeadByte(*lpFrom))
{
*lpTo++ = *(++lpFrom);
}
}
}
// We should always have enough room since we dec'ed cchTo when determining
// lpEnd
*lpTo = 0;
// This means success
return(0);
}
HWND ProcID_GetVisibleWindow(DWORD dwID)
{
HWND hwnd;
DWORD dwIDTmp;
for (hwnd = GetWindow(GetDesktopWindow(), GW_CHILD); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT))
{
GetWindowThreadProcessId(hwnd, &dwIDTmp);
// DebugMsg(DM_TRACE, "s.pi_gvw: Hwnd %x Proc ID %x.", hwnd, dwIDTmp);
if (IsWindowVisible(hwnd) && (dwIDTmp == dwID))
{
// DebugMsg(DM_TRACE, "s.pi_gvw: Found match %x.", hwnd);
return hwnd;
}
}
return NULL;
}
void ActivateHandler(HWND hwnd, DWORD dwHotKey)
{
HWND hwndT;
DWORD dwID;
hwnd = _GetAncestorWindow(hwnd);
hwndT = GetLastActivePopup(hwnd);
if (!IsWindowVisible(hwndT))
{
GetWindowThreadProcessId(hwnd, &dwID);
// DebugMsg(DM_TRACE, "sdll.ah: Hwnd %x Proc ID %x.", hwnd, dwID);
Assert(dwID);
// Find the first visible top level window owned by the
// same guy that's handling the DDE conversation.
hwnd = ProcID_GetVisibleWindow(dwID);
if (hwnd)
{
hwndT = GetLastActivePopup(hwnd);
if (IsIconic(hwnd))
{
// DebugMsg(DM_TRACE, "sdll.ah: Window is iconic, restoring.");
ShowWindow(hwnd,SW_RESTORE);
}
else
{
// DebugMsg(DM_TRACE, "sdll.ah: Window is normal, bringing to top.");
BringWindowToTop(hwnd);
if (hwndT && hwnd != hwndT)
BringWindowToTop(hwndT);
}
// set the hotkey
if (dwHotKey) {
SendMessage(hwnd, WM_SETHOTKEY, dwHotKey, 0);
}
}
}
}
// Some apps when run no-active steal the focus anyway so we
// we set it back to the previously active window.
void FixActivationStealingApps(HWND hwndOldActive, int nShow)
{
HWND hwndNew;
if (nShow == SW_SHOWMINNOACTIVE && (hwndNew = GetForegroundWindow()) != hwndOldActive && IsIconic(hwndNew))
SetForegroundWindow(hwndOldActive);
}
// reg call for simpeltons
void RegGetValue(HKEY hkRoot, LPCTSTR lpKey, LPTSTR lpValue)
{
LONG l = MAX_PATH;
*lpValue = 0;
RegQueryValue(hkRoot, lpKey, lpValue, &l);
}
BOOL FindExistingDrv(LPCTSTR pszUNCRoot, LPTSTR pszLocalName)
{
int iDrive;
for (iDrive = 0; iDrive < 26; iDrive++) {
if (IsRemoteDrive(iDrive)) {
TCHAR szDriveName[3];
DWORD cb = MAX_PATH;
szDriveName[0] = (TCHAR)iDrive + (TCHAR)TEXT('A');
szDriveName[1] = TEXT(':');
szDriveName[2] = 0;
WNetGetConnection(szDriveName, pszLocalName, &cb);
if (lstrcmpi(pszUNCRoot, pszLocalName) == 0) {
lstrcpy(pszLocalName, szDriveName);
return(TRUE);
}
}
}
return(FALSE);
}
//
// SHValidateUNC
//
// This function validates a UNC path by calling WNetAddConnection3.
// It will make it possible for the user to type a remote (RNA) UNC
// app/document name from Start->Run dialog.
//
// fConnect - flags controling what to do
//
// VALIDATEUNC_NOUI // dont bring up stinking UI!
// VALIDATEUNC_CONNECT // connect a drive letter
// VALIDATEUNC_PRINT // validate as print share instead of disk share
//
BOOL WINAPI SHValidateUNC(HWND hwndOwner, LPTSTR pszFile, UINT fConnect)
{
TCHAR szFile[MAX_PATH];
DWORD dwType;
Assert(PathIsUNC(pszFile));
Assert((fConnect & ~VALIDATEUNC_VALID) == 0);
Assert((fConnect & VALIDATEUNC_CONNECT) ? !(fConnect & VALIDATEUNC_PRINT) : TRUE);
lstrcpyn(szFile, pszFile, ARRAYSIZE(szFile));
if (!PathStripToRoot(szFile))
{
SetLastError(ERROR_PATH_NOT_FOUND);
return FALSE;
}
//
// Don't call extra WNetAddConnection3, if the root is already valid.
//
if ( (fConnect & VALIDATEUNC_CONNECT) ||
(!( NetPathExists(szFile,&dwType) && dwType!=RESOURCETYPE_PRINT ))
)
{
DWORD err;
TCHAR szAccessName[MAX_PATH];
DWORD dwRedir = CONNECT_TEMPORARY;
if (fConnect & VALIDATEUNC_CONNECT)
dwRedir |= CONNECT_REDIRECT;
if (!(fConnect & VALIDATEUNC_NOUI))
dwRedir |= CONNECT_INTERACTIVE;
if (FindExistingDrv(szFile, szAccessName))
{
err = 0;
}
else
{
NETRESOURCE rc;
DWORD dwResult;
UINT cchAccessName = ARRAYSIZE(szAccessName);
// Postponed bug: for VALIDATEUNC_PRINT case we should validate as
// both RESOURCETYPE_PRINT and RESOURCETYPE_DISK. Then, when we
// call WNetGetResourceInformation we can check the type for a print
// share. If it's a print share, return 2 instead of 1.
rc.lpRemoteName = szFile;
rc.lpLocalName = NULL;
rc.lpProvider = NULL;
rc.dwType = (fConnect & VALIDATEUNC_PRINT) ? RESOURCETYPE_PRINT : RESOURCETYPE_DISK;
err = WNetUseConnection(hwndOwner, &rc, NULL, NULL, dwRedir,
szAccessName, &cchAccessName, &dwResult);
DebugMsg(DM_TRACE, TEXT("sh TR - SHValidateUNC WNetUseConnection(%s) returned %x"), szFile, err);
if (!err && (fConnect & VALIDATEUNC_PRINT))
{
// Double check to make sure that this is really a printer...
// if we asked for a printer...
BYTE bBuf[512]; // BUGBUG - What if 512 isn't big enough?
DWORD cb = SIZEOF(bBuf);
LPTSTR pszSystemPart;
rc.dwType = 0; // Don't know yet what it is...
if ((WNetGetResourceInformation(&rc, bBuf, &cb, &pszSystemPart)
!= WN_SUCCESS) || (((LPNETRESOURCE)bBuf)->dwType != RESOURCETYPE_PRINT))
return FALSE;
}
}
if (err)
{
SetLastError(err);
return FALSE;
}
if (!(fConnect & VALIDATEUNC_PRINT))
{
lstrcatN(szAccessName, pszFile + lstrlen(szFile), ARRAYSIZE(szAccessName));
// The name should only get shorter, so no need to check length
lstrcpy(pszFile, szAccessName);
// Handle the root case
if (pszFile[2] == TEXT('\0'))
{
pszFile[2] = TEXT('\\');
pszFile[3] = TEXT('\0');
}
//
// This used to be a call to GetFileAttributes(), but
// GetFileAttributes() doesn't handle net paths very well.
// However, we need to be careful, because other shell code
// expects SHValidateUNC to return false for paths that point
// to print shares.
//
if (NetPathExists(szFile,&dwType) && dwType!=RESOURCETYPE_PRINT)
return TRUE;
else
return FALSE;
}
}
return TRUE;
}
HINSTANCE WINAPI RealShellExecuteExA(HWND hwnd, LPCSTR lpOp, LPCSTR lpFile,
LPCSTR lpArgs, LPCSTR lpDir, LPSTR lpResult,
LPCSTR lpTitle, LPSTR lpReserved,
WORD nShowCmd, LPHANDLE lphProcess,
DWORD dwFlags )
{
SHELLEXECUTEINFOA sei = { SIZEOF(SHELLEXECUTEINFOA), SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL};
DebugMsg(DM_TRACE, TEXT("RealShellExecuteExA(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX, %d)"),
hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle,
lpReserved, nShowCmd, lphProcess, dwFlags );
#ifdef WINNT
//
// Pass along the lpReserved parameter to the new process
//
if ( lpReserved )
{
sei.fMask |= SEE_MASK_RESERVED;
sei.hInstApp = (HINSTANCE)lpReserved;
}
//
// Pass along the lpTitle parameter to the new process
//
if ( lpTitle )
{
sei.fMask |= SEE_MASK_HASTITLE;
sei.lpClass = lpTitle;
}
//
// Pass along the SEPARATE_VDM flag
//
if ( dwFlags & EXEC_SEPARATE_VDM )
{
sei.fMask |= SEE_MASK_FLAG_SEPVDM;
}
#endif
//
// Pass along the NO_CONSOLE flag
//
if ( dwFlags & EXEC_NO_CONSOLE )
{
sei.fMask |= SEE_MASK_NO_CONSOLE;
}
if ( lphProcess )
{
//
// Return the process handle
//
sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
ShellExecuteExA(&sei);
*lphProcess = sei.hProcess;
}
else
{
ShellExecuteExA(&sei);
}
return sei.hInstApp;
}
HINSTANCE WINAPI RealShellExecuteExW(HWND hwnd, LPCWSTR lpOp, LPCWSTR lpFile,
LPCWSTR lpArgs, LPCWSTR lpDir, LPWSTR lpResult,
LPCWSTR lpTitle, LPWSTR lpReserved,
WORD nShowCmd, LPHANDLE lphProcess,
DWORD dwFlags )
{
SHELLEXECUTEINFOW sei = { SIZEOF(SHELLEXECUTEINFOW), SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL};
DebugMsg(DM_TRACE, TEXT("RealShellExecuteExW(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX, %d)"),
hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle,
lpReserved, nShowCmd, lphProcess, dwFlags );
#ifdef WINNT
//
// Pass along the lpReserved parameter to the new process
//
if ( lpReserved )
{
sei.fMask |= SEE_MASK_RESERVED;
sei.hInstApp = (HINSTANCE)lpReserved;
}
//
// Pass along the lpTitle parameter to the new process
//
if ( lpTitle )
{
sei.fMask |= SEE_MASK_HASTITLE;
sei.lpClass = lpTitle;
}
//
// Pass along the SEPARATE_VDM flag
//
if ( dwFlags & EXEC_SEPARATE_VDM )
{
sei.fMask |= SEE_MASK_FLAG_SEPVDM;
}
#endif
//
// Pass along the NO_CONSOLE flag
//
if ( dwFlags & EXEC_NO_CONSOLE )
{
sei.fMask |= SEE_MASK_NO_CONSOLE;
}
if ( lphProcess )
{
//
// Return the process handle
//
sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
ShellExecuteExW(&sei);
*lphProcess = sei.hProcess;
}
else
{
ShellExecuteExW(&sei);
}
return sei.hInstApp;
}
HINSTANCE WINAPI RealShellExecuteA(HWND hwnd, LPCSTR lpOp, LPCSTR lpFile,
LPCSTR lpArgs, LPCSTR lpDir, LPSTR lpResult,
LPCSTR lpTitle, LPSTR lpReserved,
WORD nShowCmd, LPHANDLE lphProcess )
{
DebugMsg(DM_TRACE, TEXT("RealShellExecuteA(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX)"),
hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle,
lpReserved, nShowCmd, lphProcess );
return RealShellExecuteExA(hwnd,lpOp,lpFile,lpArgs,lpDir,lpResult,lpTitle,lpReserved,nShowCmd,lphProcess,0);
}
HINSTANCE RealShellExecuteW(HWND hwnd, LPCWSTR lpOp, LPCWSTR lpFile,
LPCWSTR lpArgs, LPCWSTR lpDir, LPWSTR lpResult,
LPCWSTR lpTitle, LPWSTR lpReserved,
WORD nShowCmd, LPHANDLE lphProcess)
{
DebugMsg(DM_TRACE, TEXT("RealShellExecuteW(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX)"),
hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle,
lpReserved, nShowCmd, lphProcess );
return RealShellExecuteExW(hwnd,lpOp,lpFile,lpArgs,lpDir,lpResult,lpTitle,lpReserved,nShowCmd,lphProcess,0);
}
HINSTANCE WINAPI ShellExecute(HWND hwnd, LPCTSTR lpOp, LPCTSTR lpFile, LPCTSTR lpArgs,
LPCTSTR lpDir, int nShowCmd)
{
// NB The FORCENOIDLIST flag stops us from going through the ShellExecPidl()
// code (for backwards compatability with progman).
SHELLEXECUTEINFO sei = { SIZEOF(SHELLEXECUTEINFO), SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL};
DebugMsg(DM_TRACE, TEXT("ShellExecute(%04X, %s, %s, %s, %s, %d)"), hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd);
ShellExecuteEx(&sei);
return sei.hInstApp;
}
#ifdef UNICODE
HINSTANCE WINAPI ShellExecuteA(HWND hwnd, LPCSTR lpOp, LPCSTR lpFile, LPCSTR lpArgs,
LPCSTR lpDir, int nShowCmd)
{
// NB The FORCENOIDLIST flag stops us from going through the ShellExecPidl()
// code (for backwards compatability with progman).
SHELLEXECUTEINFOA sei = { SIZEOF(SHELLEXECUTEINFOA), SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL};
DebugMsg(DM_TRACE, TEXT("ShellExecuteA(%04X, %S, %S, %S, %S, %d)"), hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd);
ShellExecuteExA(&sei);
return sei.hInstApp;
}
#else
HINSTANCE APIENTRY ShellExecuteW(
HWND hwnd,
LPCWSTR lpOp,
LPCWSTR lpFile,
LPCWSTR lpArgs,
LPCWSTR lpDir,
INT nShowCmd)
{
// NB The FORCENOIDLIST flag stops us from going through the ShellExecPidl()
// code (for backwards compatability with progman).
SHELLEXECUTEINFOW sei = { SIZEOF(SHELLEXECUTEINFOW), SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL};
DebugMsg(DM_TRACE, TEXT("ShellExecuteA(%04X, %S, %S, %S, %S, %d)"), hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd);
ShellExecuteExW(&sei);
return sei.hInstApp;
}
#endif
//----------------------------------------------------------------------------
// ShellExecuteEx: Extended Shell execute.
/* like ShellExecute() but you can specify the class to use. */
// dwNull entries are to keep the sz[] strings in front of them null terminated
// in cases where lstrcatN fills up sz[] -- lstrcatN does not null terminate...
// Put them after everything just to be safe (it's definitely needed for
// szFile, szNewDir, szClassName, & szTemp, I haven't researched the others)
// I'm using DWORDS to keep everything dword aligned... Is that needed?
typedef struct
{
TCHAR szFile[MAX_PATH];
DWORD dwNull0;
TCHAR szNewDir[MAX_PATH];
DWORD dwNull1;
TCHAR szValue[MAX_PATH];
DWORD dwNull2;
TCHAR szCommand[MAX_PATH*2];
DWORD dwNull3;
TCHAR szDDECmd[MAX_PATH];
DWORD dwNull4;
TCHAR szClassName[MAX_PATH];
DWORD dwNull5;
TCHAR szImageName[MAX_PATH];
DWORD dwNull6;
// char szLinkParams[MAX_PATH];
// DWORD dwNull7;
TCHAR szExt[PATH_CCH_EXT+4]; // need 1 for null termination, 3 to dword align
TCHAR szTemp[MAX_PATH];
DWORD dwNull8;
STARTUPINFO startup;
PROCESS_INFORMATION pi;
} SEEM, *PSEEM;
// Helper function to Create and Initialize an Environment block for the
// new application.
//
TCHAR const c_szAppPaths[] = REGSTR_PATH_APPPATHS;
TCHAR const c_szEquals[] = TEXT("=");
extern const TCHAR c_szSlash[];
LPTSTR _BuildEnvironmentForNewProcess(PSEEM pseem)
{
LPTSTR psz;
LPTSTR pszEnv;
LPTSTR pszPath = NULL;
LPTSTR pszNewEnv;
int cchT;
int cchOldEnv;
HKEY hkeyProgram;
DWORD dwType;
DWORD cbData;
BOOL fExeHasPath = FALSE;
// Use the szTemp variable of pseem to build key to the programs specific
// key in the registry as well as other things...
lstrcpy(pseem->szTemp, c_szAppPaths);
lstrcat(pseem->szTemp, c_szSlash);
lstrcatN(pseem->szTemp, PathFindFileName(pseem->szImageName), ARRAYSIZE(pseem->szTemp));
if (RegOpenKey(HKEY_LOCAL_MACHINE, pseem->szTemp, &hkeyProgram) == ERROR_SUCCESS)
{
cbData = SIZEOF(pseem->szTemp);
if (RegQueryValueEx(hkeyProgram, (LPTSTR)c_szPATH, NULL, &dwType, (LPBYTE)pseem->szTemp,
&cbData) == ERROR_SUCCESS)
fExeHasPath= TRUE;
RegCloseKey(hkeyProgram);
}
pszEnv = GetEnvironmentStrings();
// Currently only clone environment if we have path.
if (!fExeHasPath && pszEnv)
return(NULL);
// We need to figure out how big an environment we have.
// While we are at it find the path environment var...
cchT = lstrlen(c_szPATH);
for (psz = pszEnv; *psz; psz += lstrlen(psz)+1)
{
// Well lets try to use the NLS CompareString function to find
// out if we found the Path... Note return of 2 is equal...
if ((CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, psz,
cchT, c_szPATH, cchT) == 2) && (*(psz+cchT) == TEXT('=')))
{
// We found path
pszPath = psz;
}
}
// Now lets allocate some memory to create a new environment from.
cchOldEnv = (int)(psz - pszEnv) + 1;
// BUGBUG (DavePl) Why 10 and not 11? Or 9?
// Comment from BobDay: 2 of the 10 come from nul terminators of the
// pseem->szTemp and cchT strings added on. The additional space might
// come from the fact that 16-bit Windows used to pass around an
// environment block that had some extra stuff on the end. The extra
// stuff had things like the path name (argv[0]) and a nCmdShow value.
pszNewEnv = (LPTSTR)LocalAlloc(LPTR, (cchOldEnv + lstrlen(pseem->szTemp) + cchT + 10) * SIZEOF(TCHAR));
if (pszNewEnv == NULL)
return(NULL);
if (pszPath)
{
// We found a path from before, calc how many bytes to copy
// to start off with. This should be up till the end of the
// current path= var
cchT = (int)(pszPath-pszEnv) + lstrlen(c_szPATH) + 1;
hmemcpy(pszNewEnv, pszEnv, cchT * SIZEOF(TCHAR));
psz = pszNewEnv + cchT;
lstrcpy(psz, pseem->szTemp);
psz += lstrlen(pseem->szTemp);
*psz++ = TEXT(';'); // add a ; between old path and new things
// and copy in the rest of the stuff.
hmemcpy(psz, pszEnv+cchT, (cchOldEnv-cchT) * SIZEOF(TCHAR));
}
else
{
//
// Path not found so copy entire old environment down
// And add PATH= and the end.
//
hmemcpy(pszNewEnv, pszEnv, cchOldEnv * SIZEOF(TCHAR));
psz = pszNewEnv + cchOldEnv -1; // Before last trailing NULL
lstrcpy(psz, c_szPATH);
lstrcat(psz, c_szEquals);
lstrcat(psz, pseem->szTemp);
// Add the Final Null for the end of the environment.
*(psz + lstrlen(psz) + 1) = TEXT('\0');
}
return(pszNewEnv);
}
BOOL _CheckForRegisteredProgram(PSEEM pseem)
{
DWORD cbData;
// Only supporte for files with no paths specified
if (PathFindFileName(pseem->szFile) != pseem->szFile)
return(FALSE);
// Use the szTemp variable of pseem to build key to the programs specific
// key in the registry as well as other things...
lstrcpy(pseem->szTemp, c_szAppPaths);
lstrcat(pseem->szTemp, c_szSlash);
lstrcatN(pseem->szTemp, pseem->szFile, ARRAYSIZE(pseem->szTemp));
// Currently we will only look up .EXE if an extension is not
// specified
if (*PathFindExtension(pseem->szTemp)==TEXT('\0'))
{
lstrcatN(pseem->szTemp, c_szDotExe, ARRAYSIZE(pseem->szTemp));
}
cbData = SIZEOF(pseem->szTemp); // Yes, sizeof() not ARRAYSIZE()
if (RegQueryValue(HKEY_LOCAL_MACHINE, pseem->szTemp,
pseem->szTemp, &cbData) == ERROR_SUCCESS)
{
lstrcpy(pseem->szFile, pseem->szTemp);
return(TRUE);
}
return(FALSE);
}
//----------------------------------------------------------------------------
#define REGSTR_PATH_POLICIES_EXPLORER REGSTR_PATH_POLICIES TEXT("\\Explorer\\RestrictRun")
TCHAR const c_szSysTray[] = TEXT("systray.exe");
//----------------------------------------------------------------------------
// Returns TRUE if the specified app is not on the list of unrestricted apps.
BOOL RestrictedApp(LPCTSTR pszApp)
{
LPTSTR pszFileName;
HKEY hkey;
int iValue = 0;
TCHAR szValue[MAX_PATH];
TCHAR szData[MAX_PATH];
DWORD dwType;
DWORD cbData;
DWORD cchValue;
pszFileName = PathFindFileName(pszApp);
DebugMsg(DM_TRACE, TEXT("s.ra: %s "), pszFileName);
// Special cases:
// Apps you can always run.
if (lstrcmpi(pszFileName, c_szRunDll) == 0)
return FALSE;
if (lstrcmpi(pszFileName, c_szSysTray) == 0)
return FALSE;
// Enum through the list of apps.
if (RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_POLICIES_EXPLORER, &hkey) == ERROR_SUCCESS)
{
cbData = SIZEOF(szData);
cchValue = ARRAYSIZE(szValue);
while (RegEnumValue(hkey, iValue, szValue, &cchValue, NULL, &dwType,
(LPBYTE)szData, &cbData) == ERROR_SUCCESS)
{
if (lstrcmpi(szData, pszFileName) == 0)
return FALSE;
cbData = SIZEOF(szData);
cchValue = ARRAYSIZE(szValue);
iValue++;
}
RegCloseKey(hkey);
}
// End of the list...
return TRUE;
}
//----------------------------------------------------------------------------
// Returns TRUE if the specified app is not on the list of unrestricted apps.
typedef struct {
// local data
HWND hDlg;
// parameters
DWORD dwHelpId;
LPTSTR lpszTitle;
BOOL fDone;
} APPCOMPATDLG_DATA, *PAPPCOMPATDLG_DATA;
BOOL CALLBACK AppCompat_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAPPCOMPATDLG_DATA lpdata;
int returncode;
DWORD aHelpIDs[4];
lpdata = (PAPPCOMPATDLG_DATA)GetWindowLong(hDlg, DWL_USER);
switch (uMsg)
{
case WM_INITDIALOG:
/* The title will be in the lParam. */
lpdata = (PAPPCOMPATDLG_DATA)lParam;
lpdata->hDlg = hDlg;
SetWindowLong(hDlg, DWL_USER, (LONG)lpdata);
SetWindowText(hDlg, lpdata->lpszTitle);
return TRUE;
case WM_DESTROY:
break;
case WM_HELP:
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, TEXT("apps.hlp>Proc4"), HELP_CONTEXT, 0);
break;
case WM_CONTEXTMENU: // right mouse click
// WinHelp((HWND) wParam, NULL, HELP_WM_HELP,
// (DWORD) (LPSTR) aRunHelpIds);
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDHELP:
aHelpIDs[0]=IDHELP;
aHelpIDs[1]=lpdata->dwHelpId;
aHelpIDs[2]=0;
aHelpIDs[3]=0;
WinHelp(hDlg, TEXT("apps.hlp>Proc4"), HELP_CONTEXT, (DWORD)lpdata->dwHelpId);
break;
case IDD_COMMAND:
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
}
break;
case IDOK:
if (IsDlgButtonChecked(hDlg, IDD_STATE))
returncode = 0x8000 | IDOK;
else
returncode = IDOK;
EndDialog(hDlg, returncode);
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
default:
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}
BOOL CheckAppCompatibility(LPCTSTR pszApp)
{
LPTSTR pszFileName;
HKEY hkey;
HKEY hkeyApp;
int iValue = 0;
int cchValue= 0;
int cbName = 0;
int cbData = 0;
DWORD dwType;
pszFileName = PathFindFileName(pszApp);
DebugMsg(DM_TRACE, TEXT("s.ra: %s "), pszFileName);
if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_CHECKBADAPPS, &hkey) != ERROR_SUCCESS)
return (TRUE);
// Check for the app name
if (RegOpenKey(hkey, pszFileName, &hkeyApp) != ERROR_SUCCESS)
{
// Didn't find it so a good app.
RegCloseKey(hkey);
return (TRUE);
}
{
DWORD dwHelpId;
TCHAR szHelpId[10];
TCHAR szDir[MAX_PATH];
TCHAR szName[MAX_PATH];
APPCOMPATDLG_DATA data;
BOOL fLowSeverity = FALSE;
// App is in the incompatible list.
// Check if 4.0 or greater app and blow off
dwHelpId = GetExeType(pszApp);
if (HIWORD(dwHelpId) >= 0x0400)
{
cbName = IDOK;
goto Exit;
}
// Get directory of this app so that we can check for dependant
// files.
lstrcpyn(szDir, pszApp, pszFileName-pszApp+1);
// Enum keys under this app name and check for dependant files.
iValue = 0;
cchValue = ARRAYSIZE(szName);
cbData = ARRAYSIZE(szHelpId);
while (RegEnumValue(hkeyApp, iValue, szName, &cchValue, NULL, &dwType,
(LPBYTE)&szHelpId, &cbData) == ERROR_SUCCESS)
{
dwHelpId = StrToInt(szHelpId);
// Fully qualified path to dependant file
// * means match any file.
lstrcat(szDir, szName);
if (szName[0] != TEXT('*') && !PathFileExists(szDir))
{
// File doesn't exist. Continue with enumeration.
lstrcpyn(szDir, pszApp, pszFileName-pszApp+1);
cbData = SIZEOF(szHelpId);
cchValue = ARRAYSIZE(szName);
iValue++;
continue;
}
else
{
// Get the flags...
lstrcpy(szDir, TEXT("Flags"));
lstrcat(szDir, szName);
cbData = SIZEOF(szDir);
if (RegQueryValueEx(hkeyApp, szDir, NULL, &dwType, (LPBYTE)szDir, &cbData) == ERROR_SUCCESS && cbData >= 1)
{
if (StrChr(szDir, TEXT('L')))
fLowSeverity = TRUE;
if (StrChr(szDir, TEXT('N')) && !GetSystemMetrics(SM_NETWORK))
{
// Message should only be displayed on a networked
// system.
cbName = IDOK;
goto Exit;
}
}
// Check the version if any...
lstrcpy(szDir, TEXT("Version"));
lstrcat(szDir, szName);
cbData = SIZEOF(szDir);
if (RegQueryValueEx(hkeyApp, szDir, NULL, &dwType, (LPBYTE)szDir, &cbData) == ERROR_SUCCESS &&
(cbData == 8) &&
VersionDLL_Init())
{
DWORD dwVerLen, dwVerHandle;
DWORD dwMajorVer, dwMinorVer;
DWORD dwBadMajorVer, dwBadMinorVer;
LPTSTR lpVerBuffer;
BOOL fBadApp = FALSE;
// What is a bad version according to the registry key?
dwBadMajorVer = *((DWORD *)szDir);
dwBadMinorVer = *((DWORD *)szDir+1);
// If no version resource can be found, assume 0.
dwMajorVer = 0;
dwMinorVer = 0;
// Version data in inf file should be of the form 8 bytes
// Major Minor
// 3.10 10.10
// 40 30 20 10 is 10 20 30 40 in registry
if (0 != (dwVerLen = g_pfnGetFileVersionInfoSize(pszApp, &dwVerHandle)))
{
if (NULL != (lpVerBuffer = (LPTSTR)GlobalAlloc(GPTR, dwVerLen)))
{
if (g_pfnGetFileVersionInfo(pszApp, dwVerHandle, dwVerLen, lpVerBuffer))
{
// Step of header, "VS_VERINFO"+NULL to get at struct
dwMajorVer = ((VS_FIXEDFILEINFO UNALIGNED *)(lpVerBuffer+lstrlen((LPTSTR)lpVerBuffer+4)+1+4))->dwProductVersionMS;
dwMinorVer = ((VS_FIXEDFILEINFO UNALIGNED *)(lpVerBuffer+lstrlen((LPTSTR)lpVerBuffer+4)+1+4))->dwProductVersionLS;
}
GlobalFree((HANDLE)lpVerBuffer);
}
}
if (dwMajorVer < dwBadMajorVer)
fBadApp = TRUE;
else if ((dwMajorVer == dwBadMajorVer) && (dwMinorVer <= dwBadMinorVer))
fBadApp = TRUE;
if (!fBadApp)
{
// This dude is ok
cbName = IDOK;
goto Exit;
}
}
data.dwHelpId = dwHelpId;
data.lpszTitle = pszFileName;
cbName = DialogBoxParam(HINST_THISDLL,
(fLowSeverity ? MAKEINTRESOURCE(DLG_APPCOMPATWARN) : MAKEINTRESOURCE(DLG_APPCOMPAT)),
NULL, AppCompat_DlgProc, (LPARAM)&data);
if (cbName & 0x8000)
{
// Delete so we don't warn again.
RegDeleteValue(hkeyApp, szName);
}
goto Exit;
}
}
// No more items left to enumerate so return success.
cbName = IDOK;
}
Exit:
RegCloseKey(hkeyApp);
RegCloseKey(hkey);
if ((cbName & 0x0FFF) == IDOK)
return(TRUE);
return (FALSE);
}
//----------------------------------------------------------------------------
HINSTANCE MapWin32ErrToHINST(UINT errWin32)
{
HINSTANCE hinst;
switch (errWin32) {
case ERROR_SHARING_VIOLATION:
hinst = (HINSTANCE)SE_ERR_SHARE;
break;
case ERROR_OUTOFMEMORY: // 14
hinst = (HINSTANCE)SE_ERR_OOM; // 8
break;
case ERROR_BAD_PATHNAME:
case ERROR_BAD_NETPATH:
case ERROR_PATH_BUSY:
case ERROR_NO_NET_OR_BAD_PATH:
hinst = (HINSTANCE)SE_ERR_PNF;
break;
case ERROR_OLD_WIN_VERSION:
hinst = (HINSTANCE)10;
break;
case ERROR_APP_WRONG_OS:
hinst = (HINSTANCE)12;
break;
case ERROR_RMODE_APP:
hinst = (HINSTANCE)15;
break;
case ERROR_SINGLE_INSTANCE_APP:
hinst = (HINSTANCE)16;
break;
case ERROR_INVALID_DLL:
hinst = (HINSTANCE)20;
break;
case ERROR_NO_ASSOCIATION:
hinst = (HINSTANCE)SE_ERR_NOASSOC;
break;
case ERROR_DDE_FAIL:
hinst = (HINSTANCE)SE_ERR_DDEFAIL;
break;
case ERROR_DLL_NOT_FOUND:
hinst = (HINSTANCE)SE_ERR_DLLNOTFOUND;
break;
default:
hinst = (HINSTANCE)errWin32;
if (errWin32 >= SE_ERR_SHARE)
hinst = (HINSTANCE)ERROR_ACCESS_DENIED;
break;
}
return hinst;
}
UINT MapHINSTToWin32Err(HINSTANCE hinst)
{
UINT errWin32;
switch ((UINT)hinst) {
case SE_ERR_SHARE:
errWin32 = ERROR_SHARING_VIOLATION;
break;
case 10:
errWin32 = ERROR_OLD_WIN_VERSION;
break;
case 12:
errWin32 = ERROR_APP_WRONG_OS;
break;
case 15:
errWin32 = ERROR_RMODE_APP;
break;
case 16:
errWin32 = ERROR_SINGLE_INSTANCE_APP;
break;
case 20:
errWin32 = ERROR_INVALID_DLL;
break;
case SE_ERR_NOASSOC:
errWin32 = ERROR_NO_ASSOCIATION;
break;
case SE_ERR_DDEFAIL:
errWin32 = ERROR_DDE_FAIL;
break;
case SE_ERR_DLLNOTFOUND:
errWin32 = ERROR_DLL_NOT_FOUND;
break;
default:
errWin32 = (UINT)hinst;
break;
}
return errWin32;
}
#ifndef NO_SHELLEXECUTE_HOOK
/*
* Returns:
* S_OK or error.
* *phrHook is hook result if S_OK is returned, otherwise it is S_FALSE.
*/
HRESULT InvokeShellExecuteHook(LPCLSID pclsidHook, LPSHELLEXECUTEINFO pei,
HRESULT *phrHook)
{
HRESULT hr;
IUnknown *punk;
*phrHook = S_FALSE;
hr = SHCoCreateInstance(NULL, pclsidHook, NULL, &IID_IUnknown, &punk);
if (hr == S_OK)
{
IShellExecuteHook *pshexhk;
hr = punk->lpVtbl->QueryInterface(punk, &IID_IShellExecuteHook, &pshexhk);
if (hr == S_OK)
{
*phrHook = pshexhk->lpVtbl->Execute(pshexhk, pei);
pshexhk->lpVtbl->Release(pshexhk);
}
#ifdef UNICODE
else
{
IShellExecuteHookA *pshexhkA;
hr = punk->lpVtbl->QueryInterface(punk, &IID_IShellExecuteHookA,
&pshexhkA);
if (SUCCEEDED(hr))
{
SHELLEXECUTEINFOA seia;
UINT cchVerb = 0;
UINT cchFile = 0;
UINT cchParameters = 0;
UINT cchDirectory = 0;
LPSTR lpszBuffer;
seia = *(SHELLEXECUTEINFOA*)pei; // Copy all of the binary data
if (pei->lpVerb)
{
cchVerb = WideCharToMultiByte(CP_ACP,0,
pei->lpVerb, -1,
NULL, 0,
NULL, NULL)+1;
}
if (pei->lpFile)
cchFile = WideCharToMultiByte(CP_ACP,0,
pei->lpFile, -1,
NULL, 0,
NULL, NULL)+1;
if (pei->lpParameters)
cchParameters = WideCharToMultiByte(CP_ACP,0,
pei->lpParameters, -1,
NULL, 0,
NULL, NULL)+1;
if (pei->lpDirectory)
cchDirectory = WideCharToMultiByte(CP_ACP,0,
pei->lpDirectory, -1,
NULL, 0,
NULL, NULL)+1;
lpszBuffer = alloca(cchVerb+cchFile+cchParameters+cchDirectory);
seia.lpVerb = NULL;
seia.lpFile = NULL;
seia.lpParameters = NULL;
seia.lpDirectory = NULL;
//
// Convert all of the strings to ANSI
//
if (pei->lpVerb)
{
WideCharToMultiByte(CP_ACP, 0,
pei->lpVerb, -1,
lpszBuffer, cchVerb,
NULL, NULL);
seia.lpVerb = lpszBuffer;
lpszBuffer += cchVerb;
}
if (pei->lpFile)
{
WideCharToMultiByte(CP_ACP, 0,
pei->lpFile, -1,
lpszBuffer, cchFile,
NULL, NULL);
seia.lpFile = lpszBuffer;
lpszBuffer += cchFile;
}
if (pei->lpParameters)
{
WideCharToMultiByte(CP_ACP, 0,
pei->lpParameters, -1,
lpszBuffer, cchParameters,
NULL, NULL);
seia.lpParameters = lpszBuffer;
lpszBuffer += cchParameters;
}
if (pei->lpDirectory)
{
WideCharToMultiByte(CP_ACP, 0,
pei->lpDirectory, -1,
lpszBuffer, cchDirectory,
NULL, NULL);
seia.lpDirectory = lpszBuffer;
}
*phrHook = pshexhkA->lpVtbl->Execute(pshexhkA, &seia );
pei->hInstApp = seia.hInstApp;
pshexhkA->lpVtbl->Release(pshexhkA);
}
}
#endif
punk->lpVtbl->Release(punk);
}
return(hr);
}
const TCHAR c_szShellExecuteHooks[] = REGSTR_PATH_EXPLORER TEXT("\\ShellExecuteHooks");
/*
* Returns:
* S_OK Execution handled by hook. pei->hInstApp filled in.
* S_FALSE Execution not handled by hook. pei->hInstApp not filled in.
* E_... Error during execution by hook. pei->hInstApp filled in.
*/
HRESULT TryShellExecuteHooks(LPSHELLEXECUTEINFO pei)
{
HRESULT hr = S_FALSE;
HKEY hkeyHooks;
// Enumerate the list of hooks. A hook is registered as a GUID value of the
// c_szShellExecuteHooks key.
if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szShellExecuteHooks, &hkeyHooks)
== ERROR_SUCCESS)
{
DWORD dwiValue;
TCHAR szCLSID[GUIDSTR_MAX];
DWORD dwcbCLSIDLen;
// Invoke each hook. A hook returns S_FALSE if it does not handle the
// exec. Stop when a hook returns S_OK (handled) or an error.
for (dwcbCLSIDLen = SIZEOF(szCLSID), dwiValue = 0;
RegEnumValue(hkeyHooks, dwiValue, szCLSID, &dwcbCLSIDLen, NULL,
NULL, NULL, NULL) == ERROR_SUCCESS;
dwcbCLSIDLen = SIZEOF(szCLSID), dwiValue++)
{
CLSID clsidHook;
if (SUCCEEDED(SHCLSIDFromString(szCLSID, &clsidHook)))
{
HRESULT hrHook;
if (InvokeShellExecuteHook(&clsidHook, pei, &hrHook) == S_OK &&
hrHook != S_FALSE)
{
hr = hrHook;
break;
}
}
}
RegCloseKey(hkeyHooks);
}
Assert(hr == S_FALSE ||
(hr == S_OK && ISSHELLEXECSUCCEEDED(pei->hInstApp)) ||
(FAILED(hr) && ! ISSHELLEXECSUCCEEDED(pei->hInstApp)));
return(hr);
}
#endif // ! NO_SHELLEXECUTE_HOOK
//----------------------------------------------------------------------------
const TCHAR c_szSlashCommand[] = TEXT("\\command");
const TCHAR c_szSlashDDEExec[] = TEXT("\\ddeexec");
const TCHAR c_szSlashShellSlash[] = TEXT("\\shell\\");
const TCHAR c_szConv[] = TEXT("ddeconv");
const TCHAR c_szDot[] = TEXT(".");
const TCHAR c_szSlashApplication[] = TEXT("\\application");
const TCHAR c_szSlashTopic[] = TEXT("\\topic");
const TCHAR c_szSystem[] = TEXT("System");
const TCHAR c_szSlashIfExec[] = TEXT("\\ifexec");
const TCHAR c_szDDEEvent[] = TEXT("ddeevent");
BOOL ShellExecuteNormal(LPSHELLEXECUTEINFO pei)
{
LPCTSTR lpParameters;
LPCTSTR lpClass;
LPCTSTR lpVerb;
LPCTSTR lpTitle;
LPCITEMIDLIST lpID;
HINSTANCE hInstance;
ATOM aApplication = 0;
ATOM aTopic = 0;
BOOL bUnderExt = FALSE;
HWND hwndOld = NULL;
BOOL fLFNAware = FALSE;
BOOL fActivateHandler = TRUE;
LPITEMIDLIST pidlGlobal = NULL;
LPTSTR pszEnv = NULL;
HKEY hkClass, hkBase = NULL;
HKEY hkToBeClosed = NULL;
BOOL fCreateProcessFailed = FALSE;
PSEEM pseem;
UINT errWin32 = ERROR_SUCCESS; // for SetLastError
lpVerb = pei->lpVerb ? pei->lpVerb : c_szOpen;
lpParameters= pei->lpParameters;
lpID = _UseIDList(pei->fMask) ? pei->lpIDList : 0;
lpClass = _UseClassName(pei->fMask) ? pei->lpClass : NULL;
hkClass = _UseClassKey(pei->fMask) ? pei->hkeyClass : NULL;
lpTitle = _UseTitleName(pei->fMask) ? pei->lpClass : NULL;
pei->hProcess = 0;
pseem = (PSEEM)LocalAlloc(LPTR, SIZEOF(SEEM));
if (!pseem)
{
pei->hInstApp = (HINSTANCE)SE_ERR_OOM;
goto error_exit;
}
if (pei->fMask & SEE_MASK_HOTKEY) {
pseem->startup.hStdInput = (HANDLE)(pei->dwHotKey);
pseem->startup.dwFlags |= STARTF_USEHOTKEY;
}
SetAppStartingCursor(pei->hwnd, TRUE);
Assert(pseem->szDDECmd[0] == 0);
//
// Copy the specified directory in pseem->szNewDir if the working
// directory is specified; otherwise, get the current directory threre.
//
if (pei->lpDirectory && *pei->lpDirectory)
{
lstrcpyn(pseem->szNewDir, pei->lpDirectory, ARRAYSIZE(pseem->szNewDir));
if (pei->fMask & SEE_MASK_DOENVSUBST)
DoEnvironmentSubst(pseem->szNewDir, ARRAYSIZE(pseem->szNewDir));
//
// if the passed directory is not valid (after env subst) dont
// fail, act just like Win31 and use whatever the current dir is.
//
// Win31 is stranger than I could imagine, if you pass ShellExecute
// a invalid directory, it will change the current drive.
//
if (!PathIsDirectory(pseem->szNewDir))
{
if (PathGetDriveNumber(pseem->szNewDir) >= 0)
{
DebugMsg(DM_TRACE, TEXT("ShellExecute: bad directory %s, using %c:"), pseem->szNewDir, pseem->szNewDir[0]);
PathStripToRoot(pseem->szNewDir);
}
else
{
DebugMsg(DM_TRACE, TEXT("ShellExecute: bad directory %s, using current dir"), pseem->szNewDir);
GetCurrentDirectory(ARRAYSIZE(pseem->szNewDir), pseem->szNewDir);
}
}
}
else
{
GetCurrentDirectory(ARRAYSIZE(pseem->szNewDir), pseem->szNewDir);
}
//
// Copy the file name to pseem->szFile, if it is specified. Then,
// perform environment sbstitution.
//
if (pei->lpFile)
{
DebugMsg(DM_TRACE, TEXT("ShellExecute: lpFile = %s"), pei->lpFile);
lstrcpyn(pseem->szFile, pei->lpFile, ARRAYSIZE(pseem->szFile));
if (pei->fMask & SEE_MASK_DOENVSUBST)
DoEnvironmentSubst(pseem->szFile, ARRAYSIZE(pseem->szFile));
}
else
{
Assert(!pseem->szFile[0]);
}
//
// If the specified filename is a UNC path, validate it now.
//
if (PathIsUNC(pseem->szFile))
{
// Notes:
// SHValidateUNC() returns FALSE if it failed. In such a case,
// GetLastError will gives us the right error code.
//
if (!SHValidateUNC(pei->hwnd, pseem->szFile,
(pei->fMask & SEE_MASK_CONNECTNETDRV) ? VALIDATEUNC_CONNECT : 0))
{
// Note that SHValidateUNC calls SetLastError
errWin32 = GetLastError();
if (ERROR_CANCELLED != errWin32)
{
// Postponed bug: combine these two SHValidateUNC calls into
// one SHValidateUNC call. Modify SHValidateUNC to check
// for the type of the resource and return 2 for print resource.
// Now check to see if it's a print share, if it is, we need to exec as pidl
if (SHValidateUNC(pei->hwnd, pseem->szFile, VALIDATEUNC_PRINT))
goto TryExecPidl;
}
// Not a print share, use the error returned from the first call
goto GotLastErrorAndReturn;
}
}
#ifndef NO_SHELLEXECUTE_HOOK
if (! (pei->fMask & SEE_MASK_NO_HOOKS))
{
HRESULT hr;
hr = TryShellExecuteHooks(pei);
switch (hr)
{
case S_FALSE: /* not handled */
break;
case S_OK: /* handled successfully */
Assert(ISSHELLEXECSUCCEEDED(pei->hInstApp));
hInstance = pei->hInstApp;
goto Exit;
break;
default: /* handled unsuccessfully */
Assert(FAILED(hr));
Assert(! ISSHELLEXECSUCCEEDED(pei->hInstApp));
hInstance = pei->hInstApp;
goto Exit;
break;
}
}
#endif
//
// If we're explicitly given a class then we don't care if the file exits.
// Just let the handler for the class worry about it.
//
if ( *pseem->szFile &&
((!lpClass && !hkClass) || _InvokeIDList(pei->fMask)) )
{
//
// Neither class name nor class key is given.
// Get fully qualified path and add .exe extension if needed
//
LPCTSTR dirs[2] = { pseem->szNewDir, NULL };
if (!PathResolve(pseem->szFile, dirs,
PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS | PRF_FIRSTDIRDEF))
{
// Then check to see if there is a registered program in the
// registry and see if it exists.
if (!_CheckForRegisteredProgram(pseem) ||
!PathResolve(pseem->szFile, dirs,
PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS | PRF_FIRSTDIRDEF))
{
hInstance = (HINSTANCE)SE_ERR_FNF;
goto Exit;
}
}
//
// check for default verb and exec the pidl instead, it is smarter than
// all this path code, ie calls context menu handlers etc....
//
if ((!(pei->fMask & (SEE_MASK_NOCLOSEPROCESS|SEE_MASK_FLAG_DDEWAIT|SEE_MASK_FORCENOIDLIST)) &&
(pei->lpVerb == NULL || *pei->lpVerb == 0)) ||
PathIsLink(pseem->szFile) ||
_InvokeIDList(pei->fMask))
{
//BUGBUG simple PIDL?
LPITEMIDLIST pidl;
TryExecPidl:
pidl = ILCreateFromPath(pseem->szFile);
if (pidl)
{
#ifdef DEBUG
static int panic=0;
Assert(panic==0);
panic++;
#endif
if (!_ShellExecPidl(pei, pidl))
errWin32 = GetLastError();
hInstance = pei->hInstApp;
ILFree(pidl);
#ifdef DEBUG
panic--;
Assert(panic==0);
#endif
goto Exit;
}
}
}
//
// If the class key is not specified from the caller, RegOpen it here.
//
if (!hkClass)
{
if (lpClass)
{
//
// If the class name is a GUID, combine it after "CLSID" and use it as the key.
//
lstrcpyn(pseem->szClassName, lpClass, ARRAYSIZE(pseem->szClassName));
if (pseem->szClassName[0]==CH_GUIDFIRST)
{
PathCombine(pseem->szClassName, c_szCLSID, pseem->szClassName);
}
RegOpenKey(HKEY_CLASSES_ROOT, pseem->szClassName, &hkClass);
}
else
{
SHGetFileClassKey(pseem->szFile, &hkClass, &hkBase);
}
if (hkClass == NULL)
{
if (PathIsExe(pseem->szFile)) {
goto OpenExe;
} else {
//
// COMPATIBILITY WARNING
//
// ShellExecute should return 0 instead of ERROR_OUTOFMEMORY.
//
hInstance = (HINSTANCE)SE_ERR_NOASSOC;
goto Exit;
}
}
hkToBeClosed = hkClass; // this hkey must be closed later.
}
// build up class\shell\verb string
lstrcpy(pseem->szClassName, c_szSlashShellSlash + 1);
// Anyone can specify a bogusly long lpVerb, be careful
lstrcatN(pseem->szClassName, lpVerb, ARRAYSIZE(pseem->szClassName));
if (RegQueryValue(hkClass, pseem->szClassName, NULL, NULL) != ERROR_SUCCESS)
{
if (hkBase)
{
hkClass = hkBase;
}
else
{
if (PathIsExe(pseem->szFile)) {
goto OpenExe;
} else {
hInstance = (HINSTANCE)SE_ERR_NOASSOC;
goto Exit;
}
}
}
// If "normal" and we are exec-ing (not just finding the EXE)
if (!bUnderExt)
{
LPTSTR lpVerbEnd;
int tmp;
// if there isn't enough room at the end of szClassName for all the
// DDE stuff (c_szSlashDDEExec+c_szSlashApplication is longest),
// then we should bail
// BUGBUG (DavePl) Just in case you're wondering, apparently 21 is the number
// of characters in "\ddeexec" and "\application" combined.
// Comment from BobDay: We could replace this with SIZEOF(...) can't we?
tmp = lstrlen(pseem->szClassName);
if (tmp > ARRAYSIZE(pseem->szClassName) - 21)
goto skipDDE;
lpVerbEnd = pseem->szClassName + tmp;
lstrcpy(lpVerbEnd, c_szSlashDDEExec);
// first check for class\shell\verb\ddeexec
RegGetValue(hkClass, pseem->szClassName, pseem->szValue);
if (pseem->szValue[0])
{
LPTSTR lpDDEEnd;
HKEY hkeyDDE;
// safe because max value length is MAX_PATH
lstrcpy(pseem->szDDECmd, pseem->szValue); // the DDE cmd string
// See if we were told to not activate the window or not.
if (RegOpenKey(hkClass, pseem->szClassName, &hkeyDDE) == ERROR_SUCCESS)
{
DWORD dwType;
DWORD cbData = MAX_PATH*SIZEOF(TCHAR);
// Any activation info?
if (RegQueryValueEx(hkeyDDE, TEXT("NoActivateHandler"), NULL, &dwType,
(LPBYTE)pseem->szValue, &cbData) == ERROR_SUCCESS)
fActivateHandler = FALSE;
RegCloseKey(hkeyDDE);
}
// look for the "application" and "topic" subkeys
lpDDEEnd = pseem->szClassName + lstrlen(pseem->szClassName);
lstrcpy(lpDDEEnd, c_szSlashApplication);
RegGetValue(hkClass, pseem->szClassName, pseem->szValue);
if (!pseem->szValue[0]) {
// if "application" key not found fake it from the
// command string (class\shell\verb\command)
lstrcpy(lpVerbEnd, c_szSlashCommand);
RegGetValue(hkClass, pseem->szClassName, pseem->szValue);
lstrcpy(lpVerbEnd, c_szSlashDDEExec); // restore ddeexec
// trim off the extension and path parts
PathRemoveArgs(pseem->szValue);
PathRemoveExtension(pseem->szValue);
PathStripPath(pseem->szValue);
}
aApplication = GlobalAddAtom(pseem->szValue);
lstrcpy(lpDDEEnd, c_szSlashTopic);
RegGetValue(hkClass, pseem->szClassName, pseem->szValue);
if (!pseem->szValue[0])
lstrcpy(pseem->szValue, c_szSystem); // default to this
aTopic = GlobalAddAtom(pseem->szValue);
hwndOld = GetForegroundWindow();
hInstance = _DDEExecute(pei->hwnd, aApplication, aTopic, pseem->szDDECmd,
pseem->szFile, lpParameters, pei->nShow, (DWORD)(pseem->startup.hStdInput),
fLFNAware, (pei->fMask&SEE_MASK_FLAG_DDEWAIT), fActivateHandler, lpID, &pidlGlobal);
// if the error is file not found, that indicates that
// no one answered the initiate (ie, run the app) else
// either it worked or the guy died
//
if ((UINT)hInstance != SE_ERR_FNF)
goto Exit;
// if it wasn't found, determine the correct command
lstrcpy(lpDDEEnd, c_szSlashIfExec); // allow ifexec to override default
RegGetValue(hkClass, pseem->szClassName, pseem->szValue);
if (pseem->szValue[0])
lstrcpy(pseem->szDDECmd, pseem->szValue);
}
*lpVerbEnd = 0; // remove DDE stuff
skipDDE:;
}
// find ClassName\shell\verb\command in reg db (the path to the exe)
lstrcatN(pseem->szClassName, c_szSlashCommand, ARRAYSIZE(pseem->szClassName));
RegGetValue(hkClass, pseem->szClassName, pseem->szValue);
// do we have the necessary RegDB info to do an exec?
if (!pseem->szValue[0])
{
OpenExe:
hInstance = (HINSTANCE)SE_ERR_NOASSOC;
// even with no association, we know how to open an executable
if (PathIsExe(pseem->szFile) && !lstrcmpi(lpVerb, c_szOpen))
{
// NB WinExec can handle long names so there's no need to convert it.
lstrcpy(pseem->szCommand, pseem->szFile);
//
// We need to append the parameter
//
if (lpParameters && *lpParameters) {
// pseem->szFile could have been at MAX_PATH, both of these need to be lstrcatN:
lstrcatN(pseem->szCommand, c_szSpace, ARRAYSIZE(pseem->szCommand));
lstrcatN(pseem->szCommand, lpParameters, ARRAYSIZE(pseem->szCommand));
}
goto TryAgain;
}
}
else
{
LPTSTR pszIn;
LPTSTR pszOut;
DWORD dwFlags;
// parse arguments into command line
hInstance = (HINSTANCE)ReplaceParameters(pseem->szCommand,
ARRAYSIZE(pseem->szCommand), pseem->szFile,
pseem->szValue, lpParameters, pei->nShow, NULL, fLFNAware, lpID, &pidlGlobal);
if (hInstance)
{
goto Exit;
}
TryAgain:
hwndOld = GetForegroundWindow();
// DebugMsg(DM_TRACE, "WinExec(%s) (was %s)", (LPSTR)pseem->szCommand, pseem->szFile);
pseem->startup.cb = SIZEOF(pseem->startup);
// Was zero filled by Alloc...
//pseem->startup.lpReserved = NULL;
//pseem->startup.lpDesktop = NULL;
//pseem->startup.dwFlags = 0L;
//pseem->startup.cbReserved2 = 0;
//pseem->startup.lpReserved2 = NULL;
pseem->startup.dwFlags |= STARTF_USESHOWWINDOW;
pseem->startup.wShowWindow = pei->nShow;
pseem->startup.lpTitle = (LPTSTR)lpTitle;
#ifdef WINNT
if ( pei->fMask & SEE_MASK_RESERVED )
{
pseem->startup.lpReserved = (LPTSTR)pei->hInstApp;
}
if (pei->fMask & SEE_MASK_HASLINKNAME)
{
pseem->startup.dwFlags |= STARTF_TITLEISLINKNAME;
}
#endif
if (pei->fMask & SEE_MASK_ICON) {
pseem->startup.hStdOutput = (HANDLE)pei->hIcon;
pseem->startup.dwFlags |= STARTF_HASSHELLDATA;
}
pszOut = pseem->szImageName;
if (pseem->szCommand[0] == TEXT('"'))
{
pszIn = StrChr(&pseem->szCommand[1],TEXT('"'));
if (pszIn)
{
lstrcpyn(pseem->szImageName, &pseem->szCommand[1],
pszIn - &pseem->szCommand[1] + 1);
}
else
lstrcpy(pseem->szImageName, &pseem->szCommand[1]);
}
else
{
pszIn = StrChr(pseem->szCommand, TEXT(' '));
if (pszIn)
{
lstrcpyn(pseem->szImageName, pseem->szCommand,
pszIn - pseem->szCommand + 1);
}
else
lstrcpy(pseem->szImageName, pseem->szCommand);
}
// See if we need to pass a new environment to the new process
pszEnv = _BuildEnvironmentForNewProcess(pseem);
// Check exec restrictions.
if (SHRestricted(REST_RESTRICTRUN) && RestrictedApp(pseem->szImageName))
{
// Restrictions are in effect and this app is restricted.
hInstance = (HINSTANCE)ERROR_ACCESS_DENIED;
errWin32 = ERROR_RESTRICTED_APP;
goto Exit;
}
// Check if app is incompatible in some fashion...
if (!CheckAppCompatibility(pseem->szImageName))
{
hInstance = (HINSTANCE)ERROR_ACCESS_DENIED;
errWin32 = ERROR_CANCELLED;
goto Exit;
}
// Last but not least if the image itself is on a UNC path like cases
// where you install the application off of a server, make sure
// we have access to that server now...
if (PathIsUNC(pseem->szImageName))
{
// Notes:
// SHValidateUNC() returns FALSE if it failed. In such a case,
// GetLastError will gives us the right error code.
//
if (!SHValidateUNC(pei->hwnd, pseem->szImageName,
(pei->fMask & SEE_MASK_CONNECTNETDRV) ? VALIDATEUNC_CONNECT : 0))
{
// Note that SHValidateUNC calls SetLastError
goto GetLastErrorAndReturn;
}
}
#ifdef WINNT
{
//
// WOWShellExecute sets this global variable
// The cb is only valid when we are being called from wow
// If valid use it
//
extern LPFNWOWSHELLEXECCB lpfnWowShellExecCB;
if (lpfnWowShellExecCB) {
#ifdef UNICODE
LPSTR lpCmdLineA;
DWORD cch = lstrlen(pseem->szCommand)+1;
hInstance = (HANDLE)SE_ERR_OOM;
if (NULL != (lpCmdLineA = (LPSTR)LocalAlloc(LPTR, cch))) {
// This shouldn't fail but if it does, we should return a better
// error code
if (WideCharToMultiByte(CP_ACP, 0, pseem->szCommand, -1, lpCmdLineA,
cch, NULL, NULL)) {
hInstance = (HANDLE)(*lpfnWowShellExecCB)(lpCmdLineA, pseem->startup.wShowWindow);
}
LocalFree((HLOCAL)lpCmdLineA);
}
#else
hInstance = (HANDLE)(*lpfnWowShellExecCB)(pseem->szCommand, pseem->startup.wShowWindow);
#endif
//
// If we were doing DDE, then retry now that the app has been
// exec'd
//
if (pseem->szDDECmd[0])
{
hInstance = _DDEExecute(pei->hwnd, aApplication, aTopic,
pseem->szDDECmd, pseem->szFile, lpParameters,
pei->nShow, (DWORD)(pseem->startup.hStdInput), fLFNAware,
(pei->fMask&SEE_MASK_FLAG_DDEWAIT), fActivateHandler,
lpID, &pidlGlobal);
}
goto Exit;
}
}
dwFlags = CREATE_DEFAULT_ERROR_MODE;
#else
dwFlags = CREATE_NEW_PROCESS_GROUP | CREATE_DEFAULT_ERROR_MODE;
#endif
if ( (pei->fMask & SEE_MASK_NO_CONSOLE) == 0 )
{
dwFlags |= CREATE_NEW_CONSOLE;
}
#ifdef WINNT
if ( pei->fMask & SEE_MASK_FLAG_SEPVDM )
{
dwFlags |= CREATE_SEPARATE_WOW_VDM;
}
#endif
#ifdef UNICODE
dwFlags |= CREATE_UNICODE_ENVIRONMENT;
#endif
DebugMsg(DM_TRACE, TEXT("CreateProcess(NULL,%s,...)"), pseem->szCommand);
if (CreateProcess(NULL, pseem->szCommand, NULL, NULL, FALSE,
dwFlags,
pszEnv, pseem->szNewDir, &pseem->startup,
&pseem->pi))
{
// If we're doing DDE we'd better wait for the app to be up and running
// before we try to talk to them.
if (pseem->szDDECmd[0])
{
// Yep, How long to wait? For now, try 60 seconds to handle
// pig-slow OLE apps.
WaitForInputIdle(pseem->pi.hProcess, 60*1000);
}
#ifndef WINNT
// For 16-bit apps, we need to wait until they've started. 32-bit
// apps never have to wait.
// On NT, the 16-bit app path doesn't get this far so we can avoid
// it altogether.
else if (GetProcessDword(GetCurrentProcessId(), GPD_FLAGS) & GPF_WIN16_PROCESS)
{
// NT and win3.1 16 bit callers all wait, even if the target is
// a 32 bit guy
WaitForInputIdle(pseem->pi.hProcess, 10*1000);
}
#endif
// Find the "hinstance" of whatever we just created.
hInstance = (HINSTANCE)GetProcessDword(pseem->pi.dwProcessId, GPD_HINST);
if (!hInstance)
{
// App probably exited. Pretend that this is a success.
hInstance = (HINSTANCE)42;
}
if (!(pei->fMask & SEE_MASK_NOCLOSEPROCESS))
{
CloseHandle(pseem->pi.hProcess);
}
CloseHandle(pseem->pi.hThread);
pei->hProcess = pseem->pi.hProcess;
// Now fix the focus and do any dde stuff that we need to do
FixActivationStealingApps(hwndOld, pei->nShow);
if (pseem->szDDECmd[0])
{
hInstance = _DDEExecute(pei->hwnd, aApplication, aTopic,
pseem->szDDECmd, pseem->szFile, lpParameters,
pei->nShow, (DWORD)(pseem->startup.hStdInput), fLFNAware,
(pei->fMask&SEE_MASK_FLAG_DDEWAIT), fActivateHandler,
lpID, &pidlGlobal);
}
}
else
{
fCreateProcessFailed = TRUE;
GetLastErrorAndReturn:
errWin32 = GetLastError();
GotLastErrorAndReturn:
hInstance = MapWin32ErrToHINST(errWin32);
// special case some error returns
switch (errWin32)
{
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_BAD_PATHNAME:
case ERROR_INVALID_NAME:
if ((pseem->szValue[0] != TEXT('%')) && fCreateProcessFailed)
{
UINT uAppType = LOWORD(GetExeType(pseem->szImageName));
//
// PK16FNF only applies to 16bit modules, and only when it was an
// implicit DLL load failure (ie, the szImageName exists)
//
if ((uAppType == NEMAGIC) &&
(PK16FNF(pseem->szImageName), pseem->szImageName[0]))
{
// do the message here so that callers of us won't need
// to deal with 32bit and 16bit apps separately
ShellMessageBox(HINST_THISDLL, pei->hwnd, MAKEINTRESOURCE(IDS_CANTFINDCOMPONENT), NULL,
MB_OK|MB_ICONEXCLAMATION | MB_SETFOREGROUND, pseem->szImageName, pseem->szFile);
hInstance = (HINSTANCE)SE_ERR_DLLNOTFOUND;
errWin32 = ERROR_DLL_NOT_FOUND;
}
else if (uAppType != PEMAGIC) // ie, it was not found
{
//
// have user help us find missing exe
//
hInstance = (HINSTANCE)FindAssociatedExe(pei->hwnd,
pseem->szCommand,
pseem->szFile,
hkClass);
//
// We infinitely retry until either the user cancel it
// or we find it.
//
if ((int)hInstance == -1)
goto TryAgain;
//
// Canclled by the user.
//
if ((int)hInstance == ERROR_INVALID_FUNCTION)
errWin32 = ERROR_CANCELLED;
}
}
break;
case ERROR_SINGLE_INSTANCE_APP:
// REVIEW: first we should search for windows with pseem->szFile in
// their title (maybe sans the extension). if that fails then
// we should look for the exe that we tried to run (as we do now)
// try to activate it. it would be nice if we could pass it params too...
PathRemoveArgs(pseem->szCommand); // strip off the params
hwndOld = _FindPopupFromExe(pseem->szCommand); // find the exe
DebugMsg(DM_TRACE, TEXT("Single instance exe (%s), activating hwnd (%x)"), (LPTSTR)pseem->szCommand, hwndOld);
if (hwndOld) {
SwitchToThisWindow(hwndOld, TRUE);
// Success - try to get it's hinstance.
hInstance = Window_GetInstance(hwndOld);
if (!hInstance)
{
DebugMsg(DM_ERROR, TEXT("s.sen: Can't get instance for single instance app."));
hInstance = (HINSTANCE)42;
}
}
break;
} // switch (errWin32)
} // GetLastErrorAndReturn
}
Exit:
// Map FNF for drives to something slightly more sensible.
if (hInstance == (HINSTANCE)SE_ERR_FNF && PathIsRoot(pseem->szFile) &&
!PathIsUNC(pseem->szFile))
{
// NB CD-Rom drives with disk missing will hit this.
if ((DriveType(DRIVEID(pseem->szFile)) == DRIVE_CDROM) ||
(DriveType(DRIVEID(pseem->szFile)) == DRIVE_REMOVABLE))
errWin32 = ERROR_NOT_READY;
else
errWin32 = ERROR_BAD_UNIT;
}
// This checks for NULL
SHCloseClassKey(hkToBeClosed);
SHCloseClassKey(hkBase);
if (aTopic)
GlobalDeleteAtom(aTopic);
if (aApplication)
GlobalDeleteAtom(aApplication);
if (pszEnv)
LocalFree((HLOCAL)pszEnv);
LocalFree((HLOCAL)pseem);
if (!ISSHELLEXECSUCCEEDED(hInstance) && pidlGlobal)
{
// Clean this up if the exec failed
SHFreeShared((HANDLE)pidlGlobal,GetCurrentProcessId());
}
SetAppStartingCursor(pei->hwnd, FALSE);
pei->hInstApp = hInstance;
if (ISSHELLEXECSUCCEEDED(hInstance))
{
return TRUE;
}
else
{
error_exit:
if (errWin32 != ERROR_SUCCESS)
{
SetLastError(errWin32);
// REVIEW: if errWin32 == ERROR_CANCELLED, we may want to
// set hInstApp to 42 so silly people who don't check the return
// code properly won't put up bogus messages. We should still
// return FALSE. But this won't help everything and we should
// really evangelize the proper use of ShellExecuteEx. In fact,
// if we do want to do this, we should do it in ShellExecute
// only. (This will force new people to do it right.)
}
else
{
// these hInstance error codes exactly match the win32 error codes
SetLastError(MapHINSTToWin32Err(hInstance));
}
return FALSE;
}
}
HRESULT SHBindToIDListParent(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv, LPCITEMIDLIST *ppidlLast)
{
HRESULT hres;
LPITEMIDLIST pidlLast = ILFindLastID(pidl);
if (pidlLast)
{
LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE);
// Special case for the object in the root
if (pidlLast == pidl)
{
// REVIEW: should this be CreateViewObject?
hres = psfDesktop->lpVtbl->QueryInterface(psfDesktop, riid, ppv);
}
else
{
USHORT uSave = pidlLast->mkid.cb;
pidlLast->mkid.cb = 0;
hres = psfDesktop->lpVtbl->BindToObject(psfDesktop, pidl, NULL, riid, ppv);
pidlLast->mkid.cb = uSave;
}
}
else
{
hres = E_INVALIDARG;
}
if (ppidlLast)
*ppidlLast = pidlLast;
return hres;
}
// ShellExec for a pidl
BOOL _ShellExecPidl(LPSHELLEXECUTEINFO pei, LPITEMIDLIST pidlExec)
{
HRESULT hres = E_OUTOFMEMORY;
UINT errWin32 = ERROR_SUCCESS;
// I need a copy so that the bind can modify the IDList
LPITEMIDLIST pidl = ILClone(pidlExec);
if (pidl)
{
LPITEMIDLIST pidlLast;
IShellFolder *psf;
hres = SHBindToIDListParent(pidl, &IID_IShellFolder, &psf, &pidlLast);
if (SUCCEEDED(hres))
{
IContextMenu *pcm;
hres = psf->lpVtbl->GetUIObjectOf(psf, pei->hwnd, 1, &pidlLast, &IID_IContextMenu, NULL, &pcm);
if (SUCCEEDED(hres))
{
HMENU hmenu = CreatePopupMenu();
if (hmenu)
{
CMINVOKECOMMANDINFOEX ici = {
SIZEOF(CMINVOKECOMMANDINFOEX),
(pei->fMask & SEE_VALID_CMIC_FLAGS) | CMIC_MASK_FLAG_NO_UI,
pei->hwnd,
NULL,
NULL,
NULL,
pei->nShow,
pei->dwHotKey,
pei->hIcon,
};
#ifdef UNICODE
CHAR szVerbAnsi[MAX_PATH];
CHAR szParametersAnsi[MAX_PATH];
CHAR szDirectoryAnsi[MAX_PATH];
ici.lpVerbW = pei->lpVerb;
ici.lpParametersW = pei->lpParameters;
ici.lpDirectoryW = pei->lpDirectory;
ici.fMask |= CMIC_MASK_UNICODE;
if (ici.lpVerbW)
{
WideCharToMultiByte(CP_ACP, 0,
ici.lpVerbW, -1,
szVerbAnsi, ARRAYSIZE(szVerbAnsi),
NULL, NULL);
ici.lpVerb = szVerbAnsi;
}
if (ici.lpParametersW)
{
WideCharToMultiByte(CP_ACP, 0,
ici.lpParametersW, -1,
szParametersAnsi, ARRAYSIZE(szParametersAnsi),
NULL, NULL);
ici.lpParameters = szParametersAnsi;
}
if (ici.lpDirectoryW)
{
WideCharToMultiByte(CP_ACP, 0,
ici.lpDirectoryW, -1,
szDirectoryAnsi, ARRAYSIZE(szDirectoryAnsi),
NULL, NULL);
ici.lpDirectory = szDirectoryAnsi;
}
#else
ici.lpVerb = pei->lpVerb;
ici.lpParameters = pei->lpParameters;
ici.lpDirectory = pei->lpDirectory;
#endif
#define CMD_ID_FIRST 1
#define CMD_ID_LAST 0x7fff
pcm->lpVtbl->QueryContextMenu(pcm, hmenu,
0, CMD_ID_FIRST, CMD_ID_LAST, CMF_DEFAULTONLY);
if (ici.lpVerb == NULL || *ici.lpVerb == 0)
{
UINT idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - CMD_ID_FIRST);
}
SetLastError(ERROR_SUCCESS);
hres = pcm->lpVtbl->InvokeCommand(pcm,
(LPCMINVOKECOMMANDINFO)&ici);
// Assume success
pei->hInstApp = (HINSTANCE)42;
if (FAILED(hres))
{
errWin32 = GetLastError();
if (errWin32 != ERROR_SUCCESS)
{
// Assume that the InvokeCommand set the last
// error properly. (Such as when we wind up
// calling back into ShellExecuteEx.)
pei->hInstApp = MapWin32ErrToHINST(errWin32);
}
}
DestroyMenu(hmenu);
}
else
hres = E_OUTOFMEMORY;
pcm->lpVtbl->Release(pcm);
}
psf->lpVtbl->Release(psf);
}
ILFree(pidl);
}
if (FAILED(hres))
{
// map hres to hInstApp/errWin32 if we havn't done so already
if (errWin32 == ERROR_SUCCESS)
{
switch (hres) {
case E_OUTOFMEMORY:
pei->hInstApp = (HINSTANCE)SE_ERR_OOM;
errWin32 = ERROR_NOT_ENOUGH_MEMORY;
break;
default:
pei->hInstApp = (HINSTANCE)SE_ERR_ACCESSDENIED;
errWin32 = ERROR_ACCESS_DENIED;
break;
}
}
SetLastError(errWin32);
}
return(SUCCEEDED(hres));
}
//
// ShellExecuteEx
//
// returns TRUE if the execute succeeded, in which case
// hInstApp should be the hinstance of the app executed (>32)
// NOTE: in some cases the HINSTANCE cannot (currently) be determined.
// In these cases, hInstApp is set to 42.
//
// returns FALSE if the execute did not succeed, in which case
// GetLastError will contain error information
// For backwards compatibility, hInstApp will contain the
// best SE_ERR_ error information (<=32) possible.
//
extern BOOL CheckResourcesBeforeExec(void);
BOOL WINAPI ShellExecuteEx(LPSHELLEXECUTEINFO pei)
{
BOOL fRet;
ULONG ulOriginalMask;
// BUGBUG: We need many robustness checks here
if (pei->cbSize != SIZEOF(SHELLEXECUTEINFO))
{
pei->hInstApp = (HINSTANCE)SE_ERR_ACCESSDENIED;
SetLastError(ERROR_ACCESS_DENIED);
return FALSE;
}
// This internal bit prevents error message box reporting
// when we recurse back into ShellExecuteEx
ulOriginalMask = pei->fMask;
pei->fMask |= SEE_MASK_FLAG_SHELLEXEC;
// This is semi-bogus, but before we exec something we should make sure that the
// user heap has memory left.
if (!CheckResourcesBeforeExec())
{
DebugMsg(DM_TRACE, TEXT("ShellExecuteEx - User said Low memory so return out of memory"));
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
fRet= FALSE;
}
else if (_InvokeIDList(pei->fMask) && pei->lpIDList)
{
// _ShellExecPidl does its own SetLastError
fRet = _ShellExecPidl(pei, pei->lpIDList);
}
else
{
// if _InvokeIDList, ShellExecuteNormal will create a pidl
// and call _ShellExecPidl on that.
// ShellExecuteNormal does its own SetLastError
fRet = ShellExecuteNormal(pei);
}
// Mike's attempt to be consistent in error reporting:
if (!fRet)
{
// we shouldn't put up errors on dll's not found.
// this is handled WITHIN shellexecuteNormal because
// sometimes kernel will put up the message for us, and sometimes
// we need to. we've put the curtion at ShellExecuteNormal
if (GetLastError() != ERROR_DLL_NOT_FOUND)
_ShellExecuteError(pei, NULL, 0);
}
pei->fMask = ulOriginalMask;
return fRet;
}
#ifdef UNICODE
//+-------------------------------------------------------------------------
//
// Function: ShellExecuteExA
//
// Synopsis: Thunks ANSI call to ShellExecuteA to ShellExecuteW
//
// Arguments: [pei] -- pointer to an ANSI SHELLEXECUTINFO struct
//
// Returns: BOOL success value
//
// History: 2-04-95 bobday Created
// 2-06-95 davepl Changed to ConvertStrings
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL WINAPI ShellExecuteExA(LPSHELLEXECUTEINFOA pei)
{
BOOL b;
SHELLEXECUTEINFOW seiw;
ThunkText * pThunkText;
memset( &seiw, 0, SIZEOF(SHELLEXECUTEINFOW) );
// BUGBUG: We need many robustness checks here
if (pei->cbSize != SIZEOF(SHELLEXECUTEINFOA))
{
pei->hInstApp = (HINSTANCE)SE_ERR_ACCESSDENIED;
SetLastError(ERROR_ACCESS_DENIED);
return FALSE;
}
seiw.cbSize = SIZEOF(SHELLEXECUTEINFOW);
seiw.fMask = pei->fMask;
seiw.hwnd = pei->hwnd;
seiw.nShow = pei->nShow;
if ( pei->fMask & SEE_MASK_IDLIST )
seiw.lpIDList = pei->lpIDList;
//
// CLASSNAME has a boolean value of (3) and CLASSKEY has a value of (1). Since
// name thus includes key, but does not imply it, we only copy when the classname
// is not set.
//
if ( pei->fMask & SEE_MASK_CLASSKEY & !(pei->fMask & SEE_MASK_CLASSNAME))
seiw.hkeyClass = pei->hkeyClass;
if ( pei->fMask & SEE_MASK_HOTKEY )
seiw.dwHotKey = pei->dwHotKey;
if ( pei->fMask & SEE_MASK_ICON )
seiw.hIcon = pei->hIcon;
//
// Thunk the text fields as appropriate
//
pThunkText =
ConvertStrings( 6,
pei->lpVerb,
pei->lpFile,
pei->lpParameters,
pei->lpDirectory,
((pei->fMask & SEE_MASK_HASLINKNAME) ||
(pei->fMask & SEE_MASK_HASTITLE) ||
(pei->fMask & SEE_MASK_CLASSNAME)) ? pei->lpClass : NULL,
(pei->fMask & SEE_MASK_RESERVED) ? pei->hInstApp : NULL);
if (NULL == pThunkText)
{
pei->hInstApp = (HINSTANCE)SE_ERR_OOM; // BUGBUG (DavePl) More appropriate error code
return FALSE;
}
//
// Set our UNICODE text fields to point to the thunked strings
//
seiw.lpVerb = pThunkText->m_pStr[0];
seiw.lpFile = pThunkText->m_pStr[1];
seiw.lpParameters = pThunkText->m_pStr[2];
seiw.lpDirectory = pThunkText->m_pStr[3];
seiw.lpClass = pThunkText->m_pStr[4];
seiw.hInstApp = (HINSTANCE)pThunkText->m_pStr[5];
//
// Call the real UNICODE ShellExecuteEx
b = ShellExecuteEx(&seiw);
pei->hInstApp = seiw.hInstApp;
if (pei->fMask & SEE_MASK_NOCLOSEPROCESS)
pei->hProcess = seiw.hProcess;
LocalFree(pThunkText);
return b;
}
#else
BOOL WINAPI ShellExecuteExW(LPSHELLEXECUTEINFOW pei)
{
return FALSE; // BUGBUG - BobDay - We should move this into SHUNIMP.C
}
#endif
// To display an error message appropriately, call this if ShellExecuteEx fails.
void _ShellExecuteError(LPSHELLEXECUTEINFO pei, LPCTSTR lpTitle, DWORD dwErr)
{
Assert(!ISSHELLEXECSUCCEEDED(pei->hInstApp));
// if dwErr not passed in, get it
if (dwErr == 0)
dwErr = GetLastError();
if (!(pei->fMask & SEE_MASK_FLAG_NO_UI))
{
if (dwErr != ERROR_CANCELLED)
{
LPCTSTR lpHeader;
UINT ids;
// don't display "user cancelled", the user knows that already
// make sure parent window is the foreground window
if (pei->hwnd)
SetForegroundWindow(pei->hwnd);
lpHeader = lpTitle;
if (!lpTitle)
lpHeader = pei->lpFile;
// Use our messages when we can -- they're more descriptive
switch (dwErr)
{
case 0:
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_OUTOFMEMORY:
ids = IDS_LowMemError;
break;
case ERROR_FILE_NOT_FOUND:
ids = IDS_RunFileNotFound;
break;
case ERROR_PATH_NOT_FOUND:
case ERROR_BAD_PATHNAME:
ids = IDS_PathNotFound;
break;
case ERROR_TOO_MANY_OPEN_FILES:
ids = IDS_TooManyOpenFiles;
break;
case ERROR_ACCESS_DENIED:
ids = IDS_RunAccessDenied;
break;
case ERROR_BAD_FORMAT:
// NB CreateProcess, when execing a Win16 apps maps just about all of
// these errors to BadFormat. Not very useful but there it is.
ids = IDS_BadFormat;
break;
case ERROR_SHARING_VIOLATION:
ids = IDS_ShareError;
break;
case ERROR_OLD_WIN_VERSION:
ids = IDS_OldWindowsVer;
break;
case ERROR_APP_WRONG_OS:
ids = IDS_OS2AppError;
break;
case ERROR_SINGLE_INSTANCE_APP:
ids = IDS_MultipleDS;
break;
case ERROR_RMODE_APP:
ids = IDS_RModeApp;
break;
case ERROR_INVALID_DLL:
ids = IDS_InvalidDLL;
break;
case ERROR_NO_ASSOCIATION:
ids = IDS_NoAssocError;
break;
case ERROR_DDE_FAIL:
ids = IDS_DDEFailError;
break;
// THESE ARE FAKE ERROR_ VALUES DEFINED AT TOP OF THIS FILE.
// THEY ARE FOR ERROR MESSAGE PURPOSES ONLY AND ARE MAPPED
// TO VALID WINERROR.H ERROR MESSAGES BELOW.
case ERROR_RESTRICTED_APP:
ids = IDS_RESTRICTIONS;
// restrictions like to use IDS_RESTRICTIONSTITLE
if (!lpTitle)
lpHeader = MAKEINTRESOURCE(IDS_RESTRICTIONSTITLE);
break;
// If we don't get a match, let the system handle it for us
default:
ids = 0;
SHSysErrorMessageBox(
pei->hwnd,
lpHeader,
IDS_SHLEXEC_ERROR,
dwErr,
pei->lpFile,
MB_OK | MB_ICONSTOP);
break;
}
if (ids)
{
DWORD dwErrSave = GetLastError(); // The message box may clobber.
ShellMessageBox(HINST_THISDLL, pei->hwnd, MAKEINTRESOURCE(ids),
lpHeader, (ids == IDS_LowMemError)?
(MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL):(MB_OK | MB_ICONSTOP),
pei->lpFile);
SetLastError(dwErrSave);
}
}
}
if (!(pei->fMask & SEE_MASK_FLAG_SHELLEXEC))
{
UINT err = 0;
switch (dwErr)
{
case ERROR_RESTRICTED_APP:
err = ERROR_ACCESS_DENIED;
break;
}
if (err)
SetLastError(err);
}
}
// ---------------- DDE Exec stuff --------------------------------
VOID WaitForThisDDEMsg(HWND hMe, UINT wMsg)
{
DWORD lEndTime;
MSG msg;
/* Wait 10 seconds at most */
lEndTime = GetTickCount() + 10000;
do
{
while (PeekMessage(&msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
{
DispatchMessage(&msg);
/* Return if the target window got this DDE message
*/
if (msg.hwnd==hMe && msg.message==wMsg)
return;
}
} while (GetTickCount() < lEndTime) ;
}
LONG CALLBACK DDESubClassWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
HWND hwndConv;
UINT nLow;
UINT nHigh;
HANDLE hEvent;
switch (wMsg)
{
case WM_DDE_ACK:
if (!(hwndConv = GetProp(hWnd, c_szConv)))
{
// this is the first ACK for our INITIATE message
return SetProp(hWnd, c_szConv, (HANDLE)wParam);
}
else if ((UINT)hwndConv == 1)
{
// this is the ACK for our EXECUTE message
// but we are in the destroy code, so don't destroy again
UnpackDDElParam(wMsg, lParam, &nLow, &nHigh);
GlobalFree((HGLOBAL)nHigh);
FreeDDElParam(wMsg, lParam);
}
else if ((HWND)wParam == hwndConv)
{
// this is the ACK for our EXECUTE message
UnpackDDElParam(wMsg, lParam, &nLow, &nHigh);
GlobalFree((HGLOBAL)nHigh);
FreeDDElParam(wMsg, lParam);
/* The TERMINATE message will be sent in the DESTROY code
*/
DestroyWindow(hWnd);
}
// This is the ACK for our INITIATE message for all servers
// besides the first. We return FALSE, so the conversation
// should terminate.
break;
case WM_DDE_TERMINATE:
if (GetProp(hWnd, c_szConv) == (HANDLE)wParam)
{
// this TERMINATE was originated by another application
// (otherwise, hwndConv would be 1)
// they should have freed the memory for the exec message
PostMessage((HWND)wParam, WM_DDE_TERMINATE, (WPARAM)hWnd, 0L);
RemoveProp(hWnd, c_szConv);
DestroyWindow(hWnd);
}
// This is the TERMINATE response for our TERMINATE message
// or a random terminate (which we don't really care about)
break;
case WM_DESTROY:
if (NULL != (hwndConv = GetProp(hWnd, c_szConv)))
{
/* Make sure the window is not destroyed twice
*/
SetProp(hWnd, c_szConv, (HANDLE)1);
/* Post the TERMINATE message and then
* Wait for the acknowledging TERMINATE message or a timeout
*/
PostMessage(hwndConv, WM_DDE_TERMINATE, (WPARAM)hWnd, 0L);
WaitForThisDDEMsg(hWnd, WM_DDE_TERMINATE);
RemoveProp(hWnd, c_szConv);
}
// the DDE conversation is officially over, let ShellExec know
if (NULL != (hEvent = GetProp(hWnd, c_szDDEEvent)))
{
RemoveProp(hWnd, c_szDDEEvent);
SetEvent(hEvent);
CloseHandle(hEvent);
}
/* Fall through */
default:
return DefWindowProc(hWnd, wMsg, wParam, lParam);
}
return 0L;
}
// Short cut all DDE commands with a WM_NOTIFY
HINSTANCE _DDEExecuteShortCut(HWND hwnd, HGLOBAL hMem, int nShow, DWORD dwHotKey)
{
LPNMVIEWFOLDER lpnm;
LPTSTR lpCmd;
HINSTANCE hret = (HINSTANCE)SE_ERR_FNF;
lpCmd = GlobalLock(hMem);
lpnm = Alloc(SIZEOF(NMVIEWFOLDER));
if (lpnm)
{
HWND hwndOwner;
// get the top most owner.
while (NULL != (hwndOwner = GetWindow(hwnd, GW_OWNER)))
hwnd = hwndOwner;
lpnm->hdr.hwndFrom = NULL;
lpnm->hdr.idFrom = 0;
lpnm->hdr.code = SEN_DDEEXECUTE;
lpnm->dwHotKey = dwHotKey;
lstrcpyn(lpnm->szCmd, lpCmd, ARRAYSIZE(lpnm->szCmd));
if (SendMessage(hwnd, WM_NOTIFY, 0, (LPARAM)lpnm)) {
hret = Window_GetInstance(hwnd);
}
Free(lpnm);
}
else
{
DebugMsg(DM_TRACE, TEXT("DDEExecuteShortCut - Error Could not allocate lpnm"));
SetLastError(ERROR_OUTOFMEMORY);
hret = (HINSTANCE)SE_ERR_OOM;
}
GlobalUnlock(hMem);
return hret;
}
//----------------------------------------------------------------------------
// Return TRUE if the window belongs to a 32bit or a Win4.0 app.
// NB We can't just check if it's a 32bit window
// since many apps use 16bit ddeml windows to communicate with the shell
// On NT we can.
BOOL Window_IsLFNAware(HWND hwnd)
{
#ifdef WINNT
if (LOWORD(GetWindowLong(hwnd,GWL_HINSTANCE)) == 0) {
// 32-bit window
return TRUE;
}
// BUGBUG - BobDay - Don't know about whether Win31 or Win40 yet?
return FALSE;
#else
DWORD idProcess;
GetWindowThreadProcessId(hwnd, &idProcess);
if (!(GetProcessDword(idProcess, GPD_FLAGS) & GPF_WIN16_PROCESS) ||
(GetProcessDword(idProcess, GPD_EXP_WINVER) >= 0x0400))
{
// DebugMsg(DM_TRACE, "s.fdapila: Win32 app (%x) handling DDE cmd.", hwnd);
return TRUE;
}
// DebugMsg(DM_TRACE, "s.fdapila: Win16 app (%x) handle DDE cmd.", hwnd);
return FALSE;
#endif
}
//----------------------------------------------------------------------------
// SHProcessMessagesUntilEvent:
// this does a message loop until an event or a timeout occurs
//
DWORD SHProcessMessagesUntilEvent(HWND hwnd, HANDLE hEvent, DWORD dwTimeout)
{
MSG msg;
DWORD dwEndTime = GetTickCount() + dwTimeout;
LONG lWait = (LONG)dwTimeout;
DWORD dwReturn;
for (;;)
{
dwReturn = MsgWaitForMultipleObjects(1, &hEvent,
FALSE, lWait, QS_ALLINPUT);
// were we signalled or did we time out?
if (dwReturn != (WAIT_OBJECT_0 + 1))
{
break;
}
// we woke up because of messages.
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
Assert(msg.message != WM_QUIT);
TranslateMessage(&msg);
if (msg.message == WM_SETCURSOR) {
SetCursor(LoadCursor(NULL, IDC_WAIT));
} else {
DispatchMessage(&msg);
}
}
// calculate new timeout value
if (dwTimeout != INFINITE)
{
lWait = (LONG)dwEndTime - GetTickCount();
}
}
return dwReturn;
}
//----------------------------------------------------------------------------
HINSTANCE _DDEExecute(
HWND hwndParent,
ATOM aApplication,
ATOM aTopic,
LPCTSTR lpCommand,
LPCTSTR lpFile,
LPCTSTR lpParms,
int nShowCmd,
DWORD dwHotKey,
BOOL fLFNAware,
BOOL fWaitForDDE,
BOOL fActivateHandler,
LPCITEMIDLIST lpID,
LPITEMIDLIST *ppidlGlobal)
{
// Make this bigger than MAX_PATH as we may pass in a path that is
// near MAX_PATH in length plus we need room fir the other gunk in
// the command.
TCHAR szCommand[MAX_PATH+64];
HGLOBAL hMem;
HINSTANCE result;
LPTSTR lpT;
HWND hwndDDE, hwndConv;
HANDLE hConversationDone;
DWORD dwResult;
// BOOL fActivate;
// fActivate = (nShowCmd == SW_NORMAL) || (nShowCmd == SW_MAXIMIZE);
// Get the actual command string.
// NB We'll assume the guy we're going to talk to is LFN aware. If we're wrong
// we'll rebuild the command string a bit later on.
result = (HINSTANCE)ReplaceParameters(szCommand, ARRAYSIZE(szCommand), lpFile,
lpCommand, lpParms, nShowCmd, &dwHotKey, TRUE, lpID, ppidlGlobal);
if (result)
return(result);
// Get dde memory for the command and copy the command line.
hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (lstrlen(szCommand) + 1) * SIZEOF(TCHAR));
if (!hMem)
{
SetLastError(ERROR_OUTOFMEMORY);
return (HINSTANCE)SE_ERR_OOM;
}
lpT = GlobalLock(hMem);
lstrcpy(lpT,szCommand);
GlobalUnlock(hMem);
if (hwndParent)
{
result = _DDEExecuteShortCut(hwndParent, hMem, nShowCmd, dwHotKey);
if ((UINT)result != SE_ERR_FNF)
{
// success! punt grimy old dde stuff
goto DDEExit1;
}
}
result = (HINSTANCE)SE_ERR_OOM;
// Create a hidden window for the conversation
// lets be lazy and not create a class for it
hwndDDE = CreateWindow(c_szStatic, NULL, WS_DISABLED, 0, 0, 0, 0,
_GetAncestorWindow(hwndParent), NULL, HINST_THISDLL, 0L);
if (!hwndDDE)
{
goto DDEExit1;
}
hConversationDone = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!hConversationDone)
{
goto DDEExit2;
}
SetProp(hwndDDE, c_szDDEEvent, hConversationDone);
// Set the wndproc i really want
SetWindowLong(hwndDDE, GWL_WNDPROC, (LONG)(FARPROC)DDESubClassWndProc);
// Send the initiate message.
// NB This doesn't need packing.
#define DDE_TIMEOUT 20000 // 20 seconds.
SendMessageTimeout((HWND)-1, WM_DDE_INITIATE, (WPARAM)hwndDDE, MAKELONG(aApplication,aTopic), SMTO_ABORTIFHUNG, DDE_TIMEOUT, &dwResult);
// no one responded
if (!(hwndConv = GetProp(hwndDDE, c_szConv)))
{
result = (HINSTANCE)SE_ERR_FNF; // so RealShellExec() will try to run this guy
goto DDEExit3;
}
// This doesn't work if the other guy is using ddeml.
if (fActivateHandler)
ActivateHandler(hwndConv, dwHotKey);
// Can the guy we're talking to handle LFNs?
if (!Window_IsLFNAware(hwndConv))
{
// Nope - App isn't LFN aware - redo the command string.
Assert(hMem);
GlobalFree(hMem);
hMem = NULL;
if (ppidlGlobal && *ppidlGlobal)
{
SHFree((HANDLE)*ppidlGlobal);
*ppidlGlobal = NULL;
}
result = (HINSTANCE)ReplaceParameters(szCommand, ARRAYSIZE(szCommand), lpFile,
lpCommand, lpParms, nShowCmd, &dwHotKey, FALSE, lpID, ppidlGlobal);
if (result)
goto DDEExit3;
// Get dde memory for the command and copy the command line.
hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (lstrlen(szCommand) + 1) * SIZEOF(TCHAR));
if (!hMem)
{
result = (HINSTANCE)SE_ERR_OOM;
SetLastError(ERROR_OUTOFMEMORY);
goto DDEExit3;
}
lpT = GlobalLock(hMem);
lstrcpy(lpT,szCommand);
GlobalUnlock(hMem);
}
#ifdef UNICODE
// are we talking to an ANSI app? If so, need to convert EXECUTE string to ANSI
if (!IsWindowUnicode(hwndConv))
{
DWORD dwSize = GlobalSize(hMem);
lpT = GlobalLock(hMem);
WideCharToMultiByte( CP_ACP, 0,
(LPWSTR)szCommand, -1,
(LPSTR)lpT, dwSize,
NULL, NULL
);
GlobalUnlock(hMem);
}
#endif
// Send the execute message to the application.
result = (HINSTANCE)SE_ERR_DDEFAIL;
SetLastError(ERROR_OUTOFMEMORY);
if (!PostMessage(hwndConv, WM_DDE_EXECUTE, (WPARAM)hwndDDE, (LPARAM)PackDDElParam(WM_DDE_EXECUTE, 0,(UINT)hMem)))
goto DDEExit3;
// everything's going fine so far, so return to the application
// with the instance handle of the guy, and hope he can execute our string
result = Window_GetInstance(hwndConv);
if (fWaitForDDE)
{
// We can't return from this call until the DDE conversation terminates.
// Otherwise the thread may go away, nuking our hwndConv window,
// messing up the DDE conversation, and Word drops funky error messages
// on us.
SHProcessMessagesUntilEvent(NULL, hConversationDone, INFINITE);
}
return result;
DDEExit3:
RemoveProp(hwndDDE, c_szDDEEvent);
CloseHandle(hConversationDone);
DDEExit2:
// The conversation will be terminated in the destroy code
DestroyWindow(hwndDDE);
DDEExit1:
if (hMem)
GlobalFree(hMem);
return result;
}
int cdecl RecentDocsCompare(const void * p1, const void *p2, size_t cb)
{
return lstrcmpi((LPCTSTR)p1, (LPCTSTR)p2);
}
//-----------------------------------------------------------------
//
// OpenRececentMRU -
// MRU list, that is used by the shell to display the recent menu of
// the tray.
#define MAXRECENTDOCS 15
TCHAR const c_szRecentDocs[] = TEXT("RecentDocs");
HANDLE OpenRecentDocMRU(void)
{
// We will do like the run dialog MRU stuff and basically keep an open
// count of the number of people who have it open and only close it
// when the count goes to zero.
ENTERCRITICAL;
// make sure we were loaded at the same address in all processes
if (g_pfnRecentDocsCompare) {
// if this function isn't the same as what we stashed away
// then this process is loaded at a different address.
if (g_pfnRecentDocsCompare != (MRUCMPPROC)RecentDocsCompare) {
LEAVECRITICAL;
return NULL;
}
} else {
// stash away our function..
g_pfnRecentDocsCompare = (MRUCMPPROC)RecentDocsCompare;
}
if (g_hMRURecent == NULL)
{
#pragma warning (disable: 4113)
// We are initializing a MRUINFO struct with a MRUCMPDATAPROC rather than
// a MRUCMPPROC function pointer (the MRU_BINARY flag indicates this to
// whoever winds up using it). Since the compiler doesn't like this, we
// disable the warning temporarily.
MRUINFO mi = {
SIZEOF(MRUINFO),
MAXRECENTDOCS,
MRU_BINARY | MRU_CACHEWRITE,
NULL,
c_szRecentDocs,
(MRUCMPDATAPROC)RecentDocsCompare // BUGBUG: MRUINFO should be a union
};
#pragma warning (default: 4113)
mi.hKey = GetExplorerUserHkey(TRUE);
if (mi.hKey)
{
g_hMRURecent = CreateMRUList(&mi);
}
if (g_hMRURecent)
{
g_uMRURecentRef = 1;
}
}
else
InterlockedIncrement(&g_uMRURecentRef);
LEAVECRITICAL;
return g_hMRURecent;
}
void CloseRecentDocMRU(void)
{
InterlockedDecrement(&g_uMRURecentRef);
FlushRecentDocMRU();
}
void FlushRecentDocMRU(void)
{
DebugMsg(DM_TRACE, TEXT("sh TR - FlushRecentDocMRU called (cRef=%d)"), g_uMRURecentRef);
ENTERCRITICAL;
if (g_uMRURecentRef==0 && g_hMRURecent)
{
FreeMRUList(g_hMRURecent);
g_pfnRecentDocsCompare = NULL;
g_hMRURecent = NULL;
}
LEAVECRITICAL;
}
BOOL GetExtensionClassDescription(LPCTSTR lpszFile)
{
LPTSTR lpszExt = PathFindExtension(lpszFile);
if (*lpszExt) {
TCHAR szClass[128];
TCHAR szDescription[MAX_PATH];
DWORD dwClass = SIZEOF(szClass);
if (RegQueryValue(HKEY_CLASSES_ROOT, lpszExt, szClass, &dwClass) != ERROR_SUCCESS) {
// if this fails, use the extension cause it might be a pseudoclass
lstrcpyn(szClass, lpszExt, ARRAYSIZE(szClass));
}
return GetClassDescription(HKEY_CLASSES_ROOT, szClass, szDescription, ARRAYSIZE(szDescription),
GCD_MUSTHAVEOPENCMD | GCD_ALLOWPSUDEOCLASSES);
}
return FALSE;
}
//-----------------------------------------------------------------
//
// Add the named file to the Recently opened MRU list, that is used
// by the shell to display the recent menu of the tray.
// this registry will hold two pidls: the target pointing to followed by
// the pidl of the link created pointing it. In both cases,
// only the last item id is stored. (we may want to change this... but
// then again, we may not)
void InternalAddToRecentDocs(LPCITEMIDLIST pidlAbs, LPCTSTR pszPath)
{
TCHAR szDir[MAX_PATH+1]; // for double null
LPITEMIDLIST pidlRecent;
LPBYTE pitem = NULL;
HRESULT hres = E_FAIL;
HANDLE hmru;
int iItem, cbItem;
LPCTSTR pszFileName;
BOOL fDeleteOldOne = FALSE;
#define BUF_SIZE 2048
// use szDir just as a random temporary
// allow only classes with default commands
if (pidlAbs == NULL && pszPath == NULL)
{
LPITEMIDLIST pidlRecent = SHCloneSpecialIDList(NULL, CSIDL_RECENT, TRUE);
if (pidlRecent)
{
HKEY hkey;
// first, delete all the files
SHFILEOPSTRUCT sFileOp =
{
NULL,
FO_DELETE,
szDir,
NULL,
FOF_NOCONFIRMATION | FOF_SILENT,
};
SHGetPathFromIDList(pidlRecent, szDir);
PathAppend(szDir, c_szStarDotStar);
szDir[lstrlen(szDir) +1] = 0; // double null terminate
SHFileOperation(&sFileOp);
// now delete the registry stuff
hkey = GetExplorerHkey(TRUE);
if (hkey)
{
RegDeleteKey(hkey, c_szRecentDocs);
}
ILFree(pidlRecent);
FlushRecentDocMRU();
}
return;
}
if (!GetExtensionClassDescription(pszPath))
return;
pidlRecent = SHCloneSpecialIDList(NULL, CSIDL_RECENT, TRUE);
if (!pidlRecent) // really hosed
return;
pitem = (void*)LocalAlloc(LPTR, BUF_SIZE);
if (!pitem)
goto Exit1;
if (pszPath==NULL || *pszPath==TEXT('\0'))
goto Exit1;
pszFileName = PathFindFileName(pszPath);
if (pszFileName==0 || *pszFileName==TEXT('\0'))
goto Exit1;
hmru = OpenRecentDocMRU();
if (!hmru)
goto Exit1;
cbItem = (lstrlen(pszFileName) + 1) * SIZEOF(TCHAR);
hres = NOERROR; // assume success
iItem = FindMRUData(hmru, pszFileName, cbItem, NULL);
if (iItem != -1)
{
// We found the one with the file name -- replace it.
// Get the item data in pitem
int cbRetrieved = EnumMRUList(hmru, iItem, pitem, BUF_SIZE);
if (cbRetrieved == -1)
{
// Failed to retrieve. Skip everything.
hres = E_FAIL;
goto Endit;
}
// The item data is in pitem.
fDeleteOldOne = TRUE;
}
else
{
// Find the one to be nuked next -- replace it.
if (EnumMRUList(hmru, MAXRECENTDOCS - 1 , pitem, BUF_SIZE) != -1)
{
// Found one. The item data is in pitem.
fDeleteOldOne = TRUE;
}
}
//
// fDeleteOldOne indicates, pitem contains the data for item to be deleted.
//
if (fDeleteOldOne)
{
LPCITEMIDLIST pidlLinkLast;
LPITEMIDLIST pidlFullLink;
pidlLinkLast = (LPCITEMIDLIST)(pitem + (lstrlen((LPTSTR)pitem) + 1) * SIZEOF(TCHAR));
pidlFullLink = ILCombine(pidlRecent, pidlLinkLast);
if (pidlFullLink)
{
// This is semi-gross, but some link types like calling cards are the
// actual data. If we delete and recreate they lose their info for the
// run. We will detect this by knowing that their pidl will be the
// same as the one we are deleting...
if (ILIsEqual(pidlFullLink, pidlAbs))
{
ILFree(pidlFullLink);
goto Exit2;
}
// now remove out link to it
SHGetPathFromIDList(pidlFullLink, szDir);
Win32DeleteFile(szDir);
ILFree(pidlFullLink);
Assert(hres == NOERROR);
}
else
{
hres = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hres))
{
IShellLink *psl;
// create the new one
if (SHGetPathFromIDList(pidlRecent, szDir)) {
hres = CShellLink_CreateInstance(NULL, &IID_IShellLink, &psl);
if (SUCCEEDED(hres))
{
LPITEMIDLIST pidlFullLink;
hres = CreateLinkToPidl(pidlAbs, psl, szDir, &pidlFullLink, FALSE);
psl->lpVtbl->Release(psl);
if (SUCCEEDED(hres) && pidlFullLink)
{
LPCITEMIDLIST pidlLinkLast = ILFindLastID(pidlFullLink);
int cbLinkLast = ILGetSize(pidlLinkLast);
hmemcpy( pitem, pszFileName, cbItem );
hmemcpy( pitem + cbItem, pidlLinkLast, cbLinkLast);
cbItem += cbLinkLast;
ILFree(pidlFullLink);
}
SHChangeNotifyHandleEvents();
}
}
}
Endit:
if (SUCCEEDED(hres))
AddMRUData(hmru, pitem, cbItem);
Exit2:
CloseRecentDocMRU();
Exit1:
if (pitem)
LocalFree((HLOCAL)pitem);
ILFree(pidlRecent);
}
typedef struct _ARD {
DWORD dwOffsetPath;
DWORD dwOffsetPidl;
} XMITARD, *PXMITARD;
void AddToRecentDocs( LPCITEMIDLIST pidl, LPCTSTR lpszPath )
{
HANDLE hARD;
HWND hwnd;
DWORD dwSizePidl = 0;
DWORD dwSizePath = 0;
if (lpszPath)
dwSizePath = (lstrlen(lpszPath)+1)*SIZEOF(TCHAR);
if (pidl)
dwSizePidl = ILGetSize(pidl);
hwnd = GetShellWindow();
if (hwnd)
{
PXMITARD px;
DWORD dwProcId;
DWORD dwOffset;
GetWindowThreadProcessId(hwnd, &dwProcId);
hARD = SHAllocShared(NULL, SIZEOF(XMITARD)+dwSizePath+dwSizePidl, dwProcId);
if (!hARD)
return; // Well, we are going to miss one, sorry.
px = (PXMITARD)SHLockShared(hARD,dwProcId);
if (!px)
{
SHFreeShared(hARD,dwProcId);
return; // Well, we are going to miss one, sorry.
}
px->dwOffsetPidl = 0;
px->dwOffsetPath = 0;
dwOffset = SIZEOF(XMITARD);
if (lpszPath)
{
px->dwOffsetPath = dwOffset;
hmemcpy((LPBYTE)px+dwOffset,lpszPath,dwSizePath);
dwOffset += dwSizePath;
}
if (pidl)
{
px->dwOffsetPidl = dwOffset;
hmemcpy((LPBYTE)px+dwOffset,pidl,dwSizePidl);
}
SHUnlockShared(px);
PostMessage(hwnd, CWM_ADDTORECENT, (WPARAM)hARD, (LPARAM)dwProcId);
}
else
{
InternalAddToRecentDocs(pidl, lpszPath);
}
}
void WINAPI ReceiveAddToRecentDocs(HANDLE hARD, DWORD dwProcId)
{
PXMITARD px;
LPITEMIDLIST pidl = NULL;
LPTSTR pszPath = NULL;
px = SHLockShared(hARD, dwProcId);
if (!px)
return;
if (px->dwOffsetPath)
pszPath = (LPTSTR)((LPBYTE)px+px->dwOffsetPath);
if (px->dwOffsetPidl)
pidl = (LPITEMIDLIST)((LPBYTE)px+px->dwOffsetPidl);
InternalAddToRecentDocs(pidl,pszPath);
SHUnlockShared(px);
SHFreeShared(hARD, dwProcId);
}
//
// put things in the shells recent docs list for the start menu
//
// in:
// uFlags SHARD_ (shell add recent docs) flags
// pv LPCSTR or LPCITEMIDLIST (path or pidl indicated by uFlags)
// may be NULL, meaning clear the recent list
//
void WINAPI SHAddToRecentDocs(UINT uFlags, LPCVOID pv)
{
TCHAR szTemp[MAX_PATH + 1]; // for double null
if (pv == NULL) // we should nuke all recent docs.
{
AddToRecentDocs(NULL, NULL);
}
else if (uFlags == SHARD_PIDL)
{
// pv is a LPCITEMIDLIST (pidl)
if (SHGetPathFromIDList((LPCITEMIDLIST)pv, szTemp))
{
AddToRecentDocs((LPCITEMIDLIST)pv, szTemp);
}
}
else if (uFlags == SHARD_PATH)
{
// pv is a LPTCSTR (path)
LPITEMIDLIST pidl = ILCreateFromPath((LPCTSTR)pv);
if (!pidl)
pidl = SHSimpleIDListFromPath((LPCTSTR)pv);
if (pidl)
{
AddToRecentDocs(pidl, (LPCTSTR)pv);
ILFree(pidl);
}
}
#ifdef UNICODE
else if (uFlags == SHARD_PATHA)
{
MultiByteToWideChar(CP_ACP, 0,
(LPCSTR)pv, -1,
szTemp, ARRAYSIZE(szTemp));
SHAddToRecentDocs(SHARD_PATH,szTemp);
}
#else
else if (uFlags == SHARD_PATHW)
{
WideCharToMultiByte(CP_ACP, 0,
(LPCWSTR)pv, -1,
szTemp, ARRAYSIZE(szTemp),
NULL, NULL);
SHAddToRecentDocs(SHARD_PATH,szTemp);
}
#endif
}
//----------------------------------------------------------------------------
// Given a file name and directory, get the path to the execuatable that
// would be exec'd if you tried to ShellExecute this thing.
HINSTANCE WINAPI FindExecutable(LPCTSTR lpFile, LPCTSTR lpDirectory, LPTSTR lpResult)
{
HINSTANCE hInstance = (HINSTANCE)42; // assume success must be > 32
TCHAR szOldDir[MAX_PATH];
TCHAR szFile[MAX_PATH];
TCHAR szOpen[MAX_PATH];
HKEY hkey, hkBase;
LPTSTR dirs[2];
// Progman relies on lpResult being a ptr to an null string on error.
*lpResult = TEXT('\0');
GetCurrentDirectory(ARRAYSIZE(szOldDir), szOldDir);
if (lpDirectory && *lpDirectory)
SetCurrentDirectory(lpDirectory);
else
lpDirectory = szOldDir; // needed for PathResolve()
if (!GetShortPathName(lpFile, szFile, ARRAYSIZE(szFile))) {
// if the lpFile is unqualified or bogus, let's use it down
// in PathResolve.
lstrcpyn(szFile, lpFile, ARRAYSIZE(szFile));
}
// get fully qualified path and add .exe extension if needed
dirs[0] = (LPTSTR)lpDirectory;
dirs[1] = NULL;
if (!PathResolve(szFile, dirs, PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS | PRF_FIRSTDIRDEF))
{
// File doesn't exist, return file not found.
hInstance = (HINSTANCE)SE_ERR_FNF;
goto Exit;
}
// DebugMsg(DM_TRACE, "PathResolve -> %s", (LPCSTR)szFile);
if (PathIsExe(szFile))
{
lstrcpy(lpResult, szFile);
goto Exit;
}
// Set to zero for error case
szOpen[0] = 0;
if (SHGetFileClassKey(szFile, &hkey, &hkBase))
{
LONG lLen = SIZEOF(szOpen);
if (RegQueryValue(hkey, c_szShellOpenCmd, szOpen, &lLen) != ERROR_SUCCESS)
{
RegGetValue(hkBase, c_szShellOpenCmd, szOpen);
}
SHCloseClassKey(hkey);
SHCloseClassKey(hkBase);
}
// Do we have the necessary RegDB info to do an exec?
if (szOpen[0] == 0)
{
hInstance = (HINSTANCE)SE_ERR_NOASSOC;
}
else
{
ReplaceParameters(lpResult, 80, szFile,
szOpen, c_szNULL, 0, NULL, FALSE, NULL, NULL);
PathRemoveArgs(lpResult);
PathRemoveBlanks(lpResult);
PathUnquoteSpaces(lpResult);
}
Exit:
DebugMsg(DM_TRACE, TEXT("FindExec(%s) ==> %s"), (LPTSTR)lpFile, (LPTSTR)lpResult);
SetCurrentDirectory(szOldDir);
return hInstance;
}
#ifdef UNICODE
HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
{
HINSTANCE hResult;
WCHAR wszResult[MAX_PATH];
ThunkText * pThunkText = ConvertStrings(2, lpFile, lpDirectory);
BOOL fDefUsed;
*lpResult = '\0';
if (NULL == pThunkText)
{
return (HINSTANCE)SE_ERR_OOM; // BUGBUG (DavePl) More appropriate error code
}
hResult = FindExecutableW(pThunkText->m_pStr[0], pThunkText->m_pStr[1], wszResult);
LocalFree(pThunkText);
// FindExecutableW terminates wszResult for us, so this is safe
// even if the above call fails
// Thunk the output result string back to ANSI. If the conversion fails,
// or if the default char is used, we fail the API call.
// BUGBUG (DavePl) Mapped chars could still make the path useless.
if (0 == WideCharToMultiByte(CP_ACP,
WC_COMPOSITECHECK | WC_DEFAULTCHAR,
wszResult,
-1,
lpResult,
MAX_PATH,
"_",
&fDefUsed) || fDefUsed)
{
SetLastError((DWORD)E_FAIL); // BUGBUG Need better error value
return (HINSTANCE) SE_ERR_FNF; // BUGBUG (DavePl) More appropriate error code
}
return hResult;
}
#else
HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpResult)
{
return 0; // BUGBUG - BobDay - We should move this into SHUNIMP.C
}
#endif
//----------------------------------------------------------------------------
// Data structures for our wait for file open functions
//
typedef struct _WaitForItem * PWAITFORITEM;
typedef struct _WaitForItem
{
DWORD dwSize;
DWORD fOperation; // Operation to perform
PWAITFORITEM pwfiNext;
HANDLE hEvent; // Handle to event that was registered.
UINT iWaiting; // Number of clients that are waiting.
ITEMIDLIST idlItem; // pidl to wait for
} WAITFORITEM;
PWAITFORITEM g_pwfiHead = NULL;
HANDLE SHWaitOp_OperateInternal( DWORD fOperation, LPCITEMIDLIST pidlItem)
{
PWAITFORITEM pwfi;
HANDLE hEvent = (HANDLE)NULL;
for (pwfi = g_pwfiHead; pwfi != NULL; pwfi = pwfi->pwfiNext)
{
if (ILIsEqual(&(pwfi->idlItem), pidlItem))
{
hEvent = pwfi->hEvent;
break;
}
}
if (fOperation & WFFO_ADD)
{
if (!pwfi)
{
UINT uSize;
UINT uSizeIDList = 0;
if (pidlItem)
uSizeIDList = ILGetSize(pidlItem);
uSize = SIZEOF(WAITFORITEM) + uSizeIDList;
// Create an event to wait for
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent)
pwfi = (PWAITFORITEM)SHAlloc(uSize);
if (pwfi)
{
pwfi->dwSize = uSize;
// pwfi->fOperation = 0; // Meaningless
pwfi->hEvent = hEvent;
pwfi->iWaiting = ((fOperation & WFFO_WAIT) != 0);
hmemcpy( (LPVOID)(&(pwfi->idlItem)), pidlItem, uSizeIDList);
// now link it in
pwfi->pwfiNext = g_pwfiHead;
g_pwfiHead = pwfi;
}
}
}
if (pwfi)
{
if (fOperation & WFFO_WAIT)
pwfi->iWaiting++;
if (fOperation & WFFO_SIGNAL)
SetEvent(hEvent);
if (fOperation & WFFO_REMOVE)
pwfi->iWaiting--; // decrement in use count;
// Only check removal case if not adding
if ((fOperation & WFFO_ADD) == 0)
{
// Remove it if nobody waiting on it
if (pwfi->iWaiting == 0)
{
if (g_pwfiHead == pwfi)
g_pwfiHead = pwfi->pwfiNext;
else
{
PWAITFORITEM pwfiT = g_pwfiHead;
while ((pwfiT != NULL) && (pwfiT->pwfiNext != pwfi))
pwfiT = pwfiT->pwfiNext;
Assert (pwfiT != NULL);
if (pwfiT != NULL)
pwfiT->pwfiNext = pwfi->pwfiNext;
}
// Close the handle
CloseHandle(pwfi->hEvent);
// Free the memory
SHFree(pwfi);
hEvent = NULL; // NULL indicates nobody waiting... (for remove case)
}
}
}
return hEvent;
}
void SHWaitOp_Operate( HANDLE hWait, DWORD dwProcId)
{
PWAITFORITEM pwfiFind;
pwfiFind = (PWAITFORITEM)SHLockShared(hWait, dwProcId);
if (!pwfiFind)
return;
pwfiFind->hEvent = SHWaitOp_OperateInternal(pwfiFind->fOperation, &(pwfiFind->idlItem));
SHUnlockShared(pwfiFind);
}
HANDLE SHWaitOp_Create( DWORD fOperation, LPCITEMIDLIST pidlItem, DWORD dwProcId)
{
UINT uSizeIDList = 0;
UINT uSize;
HANDLE hWaitOp;
PWAITFORITEM pwfi;
if (pidlItem)
uSizeIDList = ILGetSize(pidlItem);
uSize = SIZEOF(WAITFORITEM) + uSizeIDList;
hWaitOp = SHAllocShared(NULL, uSize, dwProcId);
if (!hWaitOp)
goto Punt;
pwfi = (PWAITFORITEM)SHLockShared(hWaitOp,dwProcId);
if (!pwfi)
goto Punt;
pwfi->dwSize = uSize;
pwfi->fOperation = fOperation;
pwfi->pwfiNext = NULL;
pwfi->hEvent = (HANDLE)NULL;
pwfi->iWaiting = 0;
if (pidlItem)
hmemcpy( (LPVOID)(&(pwfi->idlItem)), pidlItem, uSizeIDList);
SHUnlockShared(pwfi);
return hWaitOp;
Punt:
if (hWaitOp)
SHFreeShared(hWaitOp, dwProcId);
return (HANDLE)NULL;
}
//----------------------------------------------------------------------------
// SHWaitForFileToOpen - This function allows the cabinet to wait for a
// file (in particular folders) to signal us that they are in an open state.
// This should take care of several synchronazation problems with the shell
// not knowing when a folder is in the process of being opened or not
//
DWORD WINAPI SHWaitForFileToOpen(LPCITEMIDLIST pidl, UINT uOptions, DWORD dwTimeout)
{
HWND hwndShell;
HANDLE hWaitOp;
HANDLE hEvent;
DWORD dwProcIdSrc;
DWORD dwReturn = WAIT_OBJECT_0; // we need a default
hwndShell = GetShellWindow();
if ( (uOptions & (WFFO_WAIT | WFFO_ADD)) != 0)
{
if (hwndShell)
{
PWAITFORITEM pwfi;
DWORD dwProcIdDst;
dwProcIdSrc = GetCurrentProcessId();
GetWindowThreadProcessId(hwndShell, &dwProcIdDst);
// Do just the add and/or wait portions
hWaitOp = SHWaitOp_Create( uOptions & (WFFO_WAIT | WFFO_ADD), pidl, dwProcIdSrc);
SendMessage(hwndShell, CWM_WAITOP, (WPARAM)hWaitOp, (LPARAM)dwProcIdSrc);
//
// Now get the hEvent and convert to a local handle
//
pwfi = (PWAITFORITEM)SHLockShared(hWaitOp, dwProcIdSrc);
if (pwfi)
{
hEvent = MapHandle(pwfi->hEvent,dwProcIdDst, dwProcIdSrc, EVENT_ALL_ACCESS, 0);
}
SHUnlockShared(pwfi);
SHFreeShared(hWaitOp,dwProcIdSrc);
}
else
{
// Do just the add and/or wait portions
hEvent = SHWaitOp_OperateInternal(uOptions & (WFFO_WAIT | WFFO_ADD), pidl);
}
if ((uOptions & WFFO_WAIT) && hEvent != (HANDLE)NULL)
{
dwReturn = SHProcessMessagesUntilEvent(NULL, hEvent, dwTimeout);
}
// BUGBUGBC hEvent can be NULL at this point, closing is bad
if (hwndShell) // Close the duplicated handle.
CloseHandle(hEvent);
}
if (uOptions & WFFO_REMOVE)
{
if (hwndShell)
{
dwProcIdSrc = GetCurrentProcessId();
hWaitOp = SHWaitOp_Create( WFFO_REMOVE, pidl, dwProcIdSrc);
SendMessage(hwndShell, CWM_WAITOP, (WPARAM)hWaitOp, (LPARAM)dwProcIdSrc);
SHFreeShared(hWaitOp,dwProcIdSrc);
}
else
{
SHWaitOp_OperateInternal(WFFO_REMOVE, pidl);
}
}
return dwReturn;
}
//----------------------------------------------------------------------------
// SignalFileOpen - Signals that the file is open
//
BOOL WINAPI SignalFileOpen(LPCITEMIDLIST pidl)
{
HWND hwndShell;
BOOL fResult;
PWAITFORITEM pwfi;
hwndShell = GetShellWindow();
if (hwndShell)
{
HANDLE hWaitOp;
DWORD dwProcId;
dwProcId = GetCurrentProcessId();
hWaitOp = SHWaitOp_Create( WFFO_SIGNAL, pidl, dwProcId);
SendMessage(hwndShell, CWM_WAITOP, (WPARAM)hWaitOp, (LPARAM)dwProcId);
//
// Now get the hEvent to determine return value...
//
pwfi = (PWAITFORITEM)SHLockShared(hWaitOp, dwProcId);
if (pwfi)
{
fResult = (pwfi->hEvent != (HANDLE)NULL);
}
SHUnlockShared(pwfi);
SHFreeShared(hWaitOp,dwProcId);
}
else
{
fResult = (SHWaitOp_OperateInternal(WFFO_SIGNAL, pidl) == (HANDLE)NULL);
}
return fResult;
}