|
|
#include "shellprv.h"
#include "shlexec.h"
#include "netview.h"
extern "C" { #include <badapps.h>
} #include <htmlhelp.h>
#include "ole2dup.h"
#include <newexe.h>
#include "ids.h"
#define SAFE_DEBUGSTR(str) ((str) ? (str) : "<NULL>")
HINSTANCE Window_GetInstance(HWND hwnd) { DWORD idProcess;
GetWindowThreadProcessId(hwnd, &idProcess); // HINSTANCEs are pointers valid only within
// a single process, so 33 is returned to indicate success
// as 0-32 are reserved for error. (Actually 32 is supposed
// to be a valid success return but some apps get it wrong.)
return (HINSTANCE)(DWORD_PTR)(idProcess ? 33 : 0); }
// 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) { // 32-bit window
return LOWORD(GetWindowLongPtr(hwnd,GWLP_HINSTANCE)) == 0; }
#define COPYTODST(_szdst, _szend, _szsrc, _ulen, _ret) \
{ \ UINT _utemp = _ulen; \ if ((UINT)(_szend-_szdst) < _utemp + 1) { \ return(_ret); \ } \ StrCpyN(_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?
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, (UINT)(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, (UINT) (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))
// Returns TRUE is app is LFN aware.
// This assumes all Win32 apps are LFN aware.
BOOL App_IsLFNAware(LPCTSTR pszFile) { BOOL fRet = FALSE; // Assume Win 4.0 apps and Win32 apps are LFN aware.
DWORD dw = GetExeType(pszFile); // TraceMsg(TF_SHELLEXEC, "s.aila: %s %s %x", pszFile, szFile, dw);
if ((LOWORD(dw) == PEMAGIC) || ((LOWORD(dw) == NEMAGIC) && (HIWORD(dw) >= 0x0400))) { TCHAR sz[MAX_PATH]; PathToAppPathKey(pszFile, sz, ARRAYSIZE(sz)); fRet = (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE, sz, TEXT("UseShortName"), NULL, NULL, NULL)); } return fRet; }
// apps can tag themselves in a way so we know we can pass an URL on the cmd
// line. this uses the existance of a value called "UseURL" under the
// App Paths key in the registry associated with the app that is passed in.
// pszPath is the path to the exe
BOOL DoesAppWantUrl(LPCTSTR pszPath) { TCHAR sz[MAX_PATH]; PathToAppPathKey(pszPath, sz, ARRAYSIZE(sz)); return (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, sz, TEXT("UseURL"), NULL, NULL, NULL)); }
BOOL _AppIsLFNAware(LPCTSTR pszFile) { TCHAR szFile[MAX_PATH];
// Does it look like a DDE command?
if (pszFile && *pszFile && (*pszFile != TEXT('['))) { // Nope - Hopefully just a regular old command %1 thing.
lstrcpyn(szFile, pszFile, ARRAYSIZE(szFile)); LPTSTR 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
//
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) { TraceMsg(TF_SHELLEXEC, "ShellExecuteEx: Getting short version of path."); COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED); } else { TraceMsg(TF_SHELLEXEC, "ShellExecuteEx: 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 { sz[0] = '\0'; // ensure a valid string, regardless of what happens within _GetNextParm
_GetNextParm(lpT, sz, ARRAYSIZE(sz)); COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED); break; } } break; case TEXT('s'): case TEXT('S'): wnsprintf(sz, ARRAYSIZE(sz), TEXT("%ld"), nShow); COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED); break; case TEXT('h'): case TEXT('H'): wnsprintf(sz, ARRAYSIZE(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; } } wnsprintf(sz, ARRAYSIZE(sz), TEXT(":%ld:%ld"), *ppidlGlobal, GetCurrentProcessId()); } else { StrCpyN(sz,TEXT(":0"), ARRAYSIZE(sz)); } 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 stuff as soon as this
// is up and running.
TraceMsg(TF_SHELLEXEC, "ShellExecuteEx: Using long version of path."); COPYTODST(lpTo, lpEnd, lpFile, lstrlen(lpFile), SE_ERR_ACCESSDENIED); break; case TEXT('D'): case TEXT('d'): { // %D gives the display name of an object.
if (lpID && SUCCEEDED(SHGetNameAndFlags(lpID, SHGDN_FORPARSING, sz, ARRAYSIZE(sz), NULL))) { COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED); } else return SE_ERR_ACCESSDENIED; break; } default: goto NormalChar; } // TraceMsg(TF_SHELLEXEC, "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('"')) { // TraceMsg(TF_SHELLEXEC, "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 ThreadID_GetVisibleWindow(DWORD dwID) { HWND hwnd; for (hwnd = GetWindow(GetDesktopWindow(), GW_CHILD); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT)) { DWORD dwIDTmp = GetWindowThreadProcessId(hwnd, NULL); TraceMsg(TF_SHELLEXEC, "s.ti_gvw: Hwnd %x Thread ID %x.", hwnd, dwIDTmp); if (IsWindowVisible(hwnd) && (dwIDTmp == dwID)) { TraceMsg(TF_SHELLEXEC, "s.ti_gvw: Found match %x.", hwnd); return hwnd; } } return NULL; }
void ActivateHandler(HWND hwnd, DWORD_PTR dwHotKey) { ASSERT(hwnd); hwnd = GetTopParentWindow(hwnd); // returns non-NULL for any non-NULL input
HWND hwndT = GetLastActivePopup(hwnd); // returns non-NULL for any non-NULL input
if (!IsWindowVisible(hwndT)) { DWORD dwID = GetWindowThreadProcessId(hwnd, NULL); TraceMsg(TF_SHELLEXEC, "ActivateHandler: Hwnd %x Thread 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 = ThreadID_GetVisibleWindow(dwID); if (hwnd) { hwndT = GetLastActivePopup(hwnd);
if (IsIconic(hwnd)) { TraceMsg(TF_SHELLEXEC, "ActivateHandler: Window is iconic, restoring."); ShowWindow(hwnd,SW_RESTORE); } else { TraceMsg(TF_SHELLEXEC, "ActivateHandler: 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); } } } }
BOOL FindExistingDrv(LPCTSTR pszUNCRoot, LPTSTR pszLocalName, DWORD cchLocalName) { 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; SHWNetGetConnection(szDriveName, pszLocalName, &cb); if (lstrcmpi(pszUNCRoot, pszLocalName) == 0) { StrCpyN(pszLocalName, szDriveName, cchLocalName); return(TRUE); } } } return(FALSE); }
// Returns whether the given net path exists. This fails for NON net paths.
//
BOOL NetPathExists(LPCTSTR lpszPath, DWORD *lpdwType) { BOOL fResult = FALSE; NETRESOURCE nr; LPTSTR lpSystem; DWORD dwRes, dwSize = 1024; void * lpv;
if (!lpszPath || !*lpszPath) return FALSE;
lpv = (void *)LocalAlloc(LPTR, dwSize); if (!lpv) return FALSE;
TryWNetAgain: nr.dwScope = RESOURCE_GLOBALNET; nr.dwType = RESOURCETYPE_ANY; nr.dwDisplayType = 0; nr.lpLocalName = NULL; nr.lpRemoteName = (LPTSTR)lpszPath; nr.lpProvider = NULL; nr.lpComment = NULL; dwRes = WNetGetResourceInformation(&nr, lpv, &dwSize, &lpSystem);
// If our buffer wasn't big enough, try a bigger buffer...
if (dwRes == WN_MORE_DATA) { void * tmp = LocalReAlloc(lpv, dwSize, LMEM_MOVEABLE); if (!tmp) { LocalFree(lpv); SetLastError(ERROR_OUTOFMEMORY); return FALSE; }
lpv = tmp; goto TryWNetAgain; }
fResult = (dwRes == WN_SUCCESS);
if (fResult && lpdwType) *lpdwType = ((LPNETRESOURCE)lpv)->dwType;
LocalFree(lpv);
return fResult; }
HRESULT _CheckExistingNet(LPCTSTR pszFile, LPCTSTR pszRoot, BOOL fPrint) { //
// 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.
//
HRESULT hr = S_FALSE;
if (!PathIsRoot(pszFile)) { // if we are checking for a printshare, then it must be a Root
if (fPrint) hr = E_FAIL; else if (PathFileExists(pszFile)) hr = S_OK; }
if (S_FALSE == hr) { DWORD dwType; if (NetPathExists(pszRoot, &dwType)) { if (fPrint ? dwType != RESOURCETYPE_PRINT : dwType == RESOURCETYPE_PRINT) hr = E_FAIL; else hr = S_OK; } else if (-1 != GetFileAttributes(pszRoot)) { //
// IE 4.01 SP1 QFE #104. GetFileAttributes now called
// as a last resort become some clients often fail when using
// WNetGetResourceInformation. For example, many NFS clients were
// broken because of this.
//
hr = S_OK; } }
if (hr == E_FAIL) SetLastError(ERROR_NOT_SUPPORTED); return hr; }
HRESULT _CheckNetUse(HWND hwnd, LPTSTR pszShare, UINT fConnect, LPTSTR pszOut, DWORD cchOut) { NETRESOURCE rc; DWORD dw, err; DWORD dwRedir = CONNECT_TEMPORARY;
if (!(fConnect & VALIDATEUNC_NOUI)) dwRedir |= CONNECT_INTERACTIVE;
if (fConnect & VALIDATEUNC_CONNECT) dwRedir |= CONNECT_REDIRECT;
// VALIDATE_PRINT happens only after a failed attempt to validate for
// a file. That previous attempt will have given the option to
// connect to other media -- don't do it here or the user will be
// presented with the same dialog twice when the first one is cancelled.
if (fConnect & VALIDATEUNC_PRINT) dwRedir |= CONNECT_CURRENT_MEDIA;
rc.lpRemoteName = pszShare; rc.lpLocalName = NULL; rc.lpProvider = NULL; rc.dwType = (fConnect & VALIDATEUNC_PRINT) ? RESOURCETYPE_PRINT : RESOURCETYPE_DISK;
err = WNetUseConnection(hwnd, &rc, NULL, NULL, dwRedir, pszOut, &cchOut, &dw);
TraceMsg(TF_SHELLEXEC, "SHValidateUNC WNetUseConnection(%s) returned %x", pszShare, err);
if (err) { SetLastError(err); return E_FAIL; } else if (fConnect & VALIDATEUNC_PRINT) { // just because WNetUse succeeded, doesnt mean
// NetPathExists will. if it fails then
// we shouldnt succeed this call regardless
// because we are only interested in print shares.
if (!NetPathExists(pszShare, &dw) || (dw != RESOURCETYPE_PRINT)) { SetLastError(ERROR_NOT_SUPPORTED); return E_FAIL; } }
return S_OK; }
//
// 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) { HRESULT hr; TCHAR szShare[MAX_PATH]; BOOL fPrint = (fConnect & VALIDATEUNC_PRINT); UINT cchOrig = lstrlen(pszFile) + 1;
ASSERT(PathIsUNC(pszFile)); ASSERT((fConnect & ~VALIDATEUNC_VALID) == 0); ASSERT((fConnect & VALIDATEUNC_CONNECT) ? !fPrint : TRUE);
lstrcpyn(szShare, pszFile, ARRAYSIZE(szShare));
if (!PathStripToRoot(szShare)) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; }
if (fConnect & VALIDATEUNC_CONNECT) hr = S_FALSE; else hr = _CheckExistingNet(pszFile, szShare, fPrint);
if (S_FALSE == hr) { TCHAR szAccessName[MAX_PATH];
if (!fPrint && FindExistingDrv(szShare, szAccessName, ARRAYSIZE(szAccessName))) { hr = S_OK; } else hr = _CheckNetUse(hwndOwner, szShare, fConnect, szAccessName, SIZECHARS(szAccessName));
if (S_OK == hr && !fPrint) { StrCatBuff(szAccessName, pszFile + lstrlen(szShare), ARRAYSIZE(szAccessName)); // The name should only get shorter, so no need to check length
lstrcpyn(pszFile, szAccessName, cchOrig);
// Handle the root case
if (cchOrig >= 4 && pszFile[2] == TEXT('\0')) { pszFile[2] = TEXT('\\'); pszFile[3] = TEXT('\0'); }
hr = _CheckExistingNet(pszFile, szShare, FALSE); } }
return (hr == S_OK); }
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};
TraceMsg(TF_SHELLEXEC, "RealShellExecuteExA(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX, %d)", hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle, lpReserved, nShowCmd, lphProcess, dwFlags);
// 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; }
// 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};
TraceMsg(TF_SHELLEXEC, "RealShellExecuteExW(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX, %d)", hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle, lpReserved, nShowCmd, lphProcess, dwFlags);
if (lpReserved) { sei.fMask |= SEE_MASK_RESERVED; sei.hInstApp = (HINSTANCE)lpReserved; }
if (lpTitle) { sei.fMask |= SEE_MASK_HASTITLE; sei.lpClass = lpTitle; }
if (dwFlags & EXEC_SEPARATE_VDM) { sei.fMask |= SEE_MASK_FLAG_SEPVDM; }
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) { TraceMsg(TF_SHELLEXEC, "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) { TraceMsg(TF_SHELLEXEC, "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).
// DDEWAIT makes us synchronous, and gets around threads without
// msg pumps and ones that are killed immediately after shellexec()
SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO), 0, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL}; ULONG fMask = SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST; if(!(SHGetAppCompatFlags(ACF_WIN95SHLEXEC) & ACF_WIN95SHLEXEC)) fMask |= SEE_MASK_FLAG_DDEWAIT; sei.fMask = fMask;
TraceMsg(TF_SHELLEXEC, "ShellExecute(%04X, %s, %s, %s, %s, %d)", hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd);
ShellExecuteEx(&sei); return sei.hInstApp; }
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).
// DDEWAIT makes us synchronous, and gets around threads without
// msg pumps and ones that are killed immediately after shellexec()
SHELLEXECUTEINFOA sei = { sizeof(SHELLEXECUTEINFOA), 0, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL}; ULONG fMask = SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST; if (!(SHGetAppCompatFlags(ACF_WIN95SHLEXEC) & ACF_WIN95SHLEXEC)) fMask |= SEE_MASK_FLAG_DDEWAIT; sei.fMask = fMask;
TraceMsg(TF_SHELLEXEC, "ShellExecuteA(%04X, %S, %S, %S, %S, %d)", hwnd, SAFE_DEBUGSTR(lpOp), SAFE_DEBUGSTR(lpFile), SAFE_DEBUGSTR(lpArgs), SAFE_DEBUGSTR(lpDir), nShowCmd);
ShellExecuteExA(&sei); return sei.hInstApp; }
// Returns TRUE if the specified app is listed under the specified key
STDAPI_(BOOL) IsNameListedUnderKey(LPCTSTR pszFileName, LPCTSTR pszKey) { HKEY hkey; // Enum through the list of apps.
if (RegOpenKeyEx(HKEY_CURRENT_USER, pszKey, 0, KEY_READ, &hkey) == ERROR_SUCCESS) { TCHAR szValue[MAX_PATH], szData[MAX_PATH]; DWORD dwType, cbData = sizeof(szData); DWORD cchValue = ARRAYSIZE(szValue); int iValue = 0; while (RegEnumValue(hkey, iValue, szValue, &cchValue, NULL, &dwType, (LPBYTE)szData, &cbData) == ERROR_SUCCESS) { if (lstrcmpi(szData, pszFileName) == 0) { RegCloseKey(hkey); return TRUE; } cbData = sizeof(szData); cchValue = ARRAYSIZE(szValue); iValue++; } RegCloseKey(hkey); } return FALSE; }
#define REGSTR_PATH_POLICIES_EXPLORER REGSTR_PATH_POLICIES TEXT("\\Explorer\\RestrictRun")
#define REGSTR_PATH_POLICIES_EXPLORER_DISALLOW REGSTR_PATH_POLICIES TEXT("\\Explorer\\DisallowRun")
//----------------------------------------------------------------------------
// Returns TRUE if the specified app is not on the list of unrestricted apps.
BOOL RestrictedApp(LPCTSTR pszApp) { LPTSTR pszFileName = PathFindFileName(pszApp);
TraceMsg(TF_SHELLEXEC, "RestrictedApp: %s ", pszFileName);
// Special cases:
// Apps you can always run.
if (lstrcmpi(pszFileName, c_szRunDll) == 0) return FALSE;
if (lstrcmpi(pszFileName, TEXT("systray.exe")) == 0) return FALSE;
return !IsNameListedUnderKey(pszFileName, REGSTR_PATH_POLICIES_EXPLORER); }
//----------------------------------------------------------------------------
// Returns TRUE if the specified app is on the list of disallowed apps.
// not much safety gained from filename checking.
BOOL DisallowedApp(LPCTSTR pszApp) { LPTSTR pszFileName = PathFindFileName(pszApp);
TraceMsg(TF_SHELLEXEC, "DisallowedApp: %s ", pszFileName);
return IsNameListedUnderKey(pszFileName, REGSTR_PATH_POLICIES_EXPLORER_DISALLOW); }
/*
* Returns: * S_OK or error. * *phrHook is hook result if S_OK is returned, otherwise it is S_FALSE. */ HRESULT InvokeShellExecuteHook(REFGUID clsidHook, LPSHELLEXECUTEINFO pei, HRESULT *phrHook) { *phrHook = S_FALSE; IUnknown *punk; HRESULT hr = SHExtCoCreateInstance(NULL, &clsidHook, NULL, IID_PPV_ARG(IUnknown, &punk)); if (hr == S_OK) { IShellExecuteHook *pshexhk; hr = punk->QueryInterface(IID_PPV_ARG(IShellExecuteHook, &pshexhk)); if (hr == S_OK) { *phrHook = pshexhk->Execute(pei); pshexhk->Release(); } else { IShellExecuteHookA *pshexhkA; hr = punk->QueryInterface(IID_PPV_ARG(IShellExecuteHookA, &pshexhkA)); if (SUCCEEDED(hr)) { SHELLEXECUTEINFOA seia; UINT cchVerb = 0; UINT cchFile = 0; UINT cchParameters = 0; UINT cchDirectory = 0; UINT cchClass = 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; if (_UseClassName(pei->fMask) && pei->lpClass) cchClass = WideCharToMultiByte(CP_ACP,0, pei->lpClass, -1, NULL, 0, NULL, NULL)+1;
lpszBuffer = (LPSTR) alloca(cchVerb+cchFile+cchParameters+cchDirectory+cchClass);
seia.lpVerb = NULL; seia.lpFile = NULL; seia.lpParameters = NULL; seia.lpDirectory = NULL; seia.lpClass = 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; lpszBuffer += cchDirectory; } if (_UseClassName(pei->fMask) && pei->lpClass) { WideCharToMultiByte(CP_ACP, 0, pei->lpClass, -1, lpszBuffer, cchClass, NULL, NULL); seia.lpClass = lpszBuffer; }
*phrHook = pshexhkA->Execute(&seia);
pei->hInstApp = seia.hInstApp; // hook may set hProcess (e.g. CURLExec creates dummy process
// to signal IEAK that IE setup failed -- in browser only mode)
pei->hProcess = seia.hProcess;
pshexhkA->Release(); } } punk->Release(); }
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 (RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szShellExecuteHooks, 0, KEY_READ, &hkeyHooks) == ERROR_SUCCESS) { DWORD dwiValue; TCHAR szCLSID[GUIDSTR_MAX]; DWORD cchCLSID;
// 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 (cchCLSID = ARRAYSIZE(szCLSID), dwiValue = 0; RegEnumValue(hkeyHooks, dwiValue, szCLSID, &cchCLSID, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; cchCLSID = ARRAYSIZE(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); }
BOOL InRunDllProcess(void) { static BOOL s_fInRunDll = -1;
if (-1 == s_fInRunDll) { TCHAR sz[MAX_PATH]; s_fInRunDll = FALSE; if (GetModuleFileName(NULL, sz, SIZECHARS(sz))) { //
// WARNING - rundll often seems to fail to add the DDEWAIT flag, and
// it often needs to since it is common to use rundll as a fire
// and forget process, and it exits too early.
//
// note: this changes DDE flags for any app with "rundll" in its name
// shouldnt be a big deal from a security point of view.
if (StrStrI(sz, TEXT("rundll"))) s_fInRunDll = TRUE; } }
return s_fInRunDll; }
#ifdef DEBUG
/*----------------------------------------------------------
Purpose: Validation function for SHELLEXECUTEINFO
*/ BOOL IsValidPSHELLEXECUTEINFO(LPSHELLEXECUTEINFO pei) { //
// Note that we do *NOT* validate hInstApp, for several reasons.
//
// 1. It is an OUT parameter, not an IN parameter.
// 2. It often contains an error code (see documentation).
// 3. Even when it contains an HINSTANCE, it's an HINSTANCE
// in another process, so we can't validate it anyway.
//
return (IS_VALID_WRITE_PTR(pei, SHELLEXECUTEINFO) && IS_VALID_SIZE(pei->cbSize, sizeof(*pei)) && (IsFlagSet(pei->fMask, SEE_MASK_FLAG_NO_UI) || NULL == pei->hwnd || IS_VALID_HANDLE(pei->hwnd, WND)) && (NULL == pei->lpVerb || IS_VALID_STRING_PTR(pei->lpVerb, -1)) && (NULL == pei->lpFile || IS_VALID_STRING_PTR(pei->lpFile, -1)) && (NULL == pei->lpParameters || IS_VALID_STRING_PTR(pei->lpParameters, -1)) && (NULL == pei->lpDirectory || IS_VALID_STRING_PTR(pei->lpDirectory, -1)) && (IsFlagClear(pei->fMask, SEE_MASK_IDLIST) || IsFlagSet(pei->fMask, SEE_MASK_INVOKEIDLIST) || // because SEE_MASK_IDLIST is part of SEE_MASK_INVOKEIDLIST this line will
IS_VALID_PIDL((LPCITEMIDLIST)(pei->lpIDList))) && // defer to the next clause if the superset is true
(IsFlagClear(pei->fMask, SEE_MASK_INVOKEIDLIST) || NULL == pei->lpIDList || IS_VALID_PIDL((LPCITEMIDLIST)(pei->lpIDList))) && (!_UseClassName(pei->fMask) || IS_VALID_STRING_PTR(pei->lpClass, -1)) && (!_UseTitleName(pei->fMask) || NULL == pei->lpClass || IS_VALID_STRING_PTR(pei->lpClass, -1)) && (!_UseClassKey(pei->fMask) || IS_VALID_HANDLE(pei->hkeyClass, KEY)) && (IsFlagClear(pei->fMask, SEE_MASK_ICON) || IS_VALID_HANDLE(pei->hIcon, ICON))); }
#endif // DEBUG
//
// 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.
//
BOOL WINAPI ShellExecuteEx(LPSHELLEXECUTEINFO pei) { DWORD err = NOERROR;
// Don't overreact if CoInitializeEx fails; it just means we
// can't do our shell hooks.
HRESULT hrInit = SHCoInitialize();
if (IS_VALID_STRUCT_PTR(pei, SHELLEXECUTEINFO) && sizeof(*pei) == pei->cbSize) { // This internal bit prevents error message box reporting
// when we recurse back into ShellExecuteEx
ULONG ulOriginalMask = pei->fMask; pei->fMask |= SEE_MASK_FLAG_SHELLEXEC; if (SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), TEXT("MaximizeApps"), FALSE, FALSE)) // && (GetSystemMetrics(SM_CYSCREEN)<=600))
{ switch (pei->nShow) { case SW_NORMAL: case SW_SHOW: case SW_RESTORE: case SW_SHOWDEFAULT: pei->nShow = SW_MAXIMIZE; } }
if (!(pei->fMask & SEE_MASK_FLAG_DDEWAIT) && InRunDllProcess()) { //
// WARNING - rundll often seems to fail to add the DDEWAIT flag, and
// it often needs to since it is common to use rundll as a fire
// and forget process, and it exits too early.
//
pei->fMask |= (SEE_MASK_FLAG_DDEWAIT | SEE_MASK_WAITFORINPUTIDLE); }
// ShellExecuteNormal does its own SetLastError
err = ShellExecuteNormal(pei);
// Mike's attempt to be consistent in error reporting:
if (err != ERROR_SUCCESS) { // 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
// LEGACY - ERROR_RESTRICTED_APP was never mapped to a valid error - ZekeL 2001-FEB-14
// because we always called _ShellExecuteError() before
// resetting the mask to ulOriginalMask, we never mapped
// ERROR_RESTRICTED_APP (which is -1) to a valid code
if (err != ERROR_DLL_NOT_FOUND && err != ERROR_CANCELLED) { _ShellExecuteError(pei, NULL, err); } }
pei->fMask = ulOriginalMask; } else { // Failed parameter validation
pei->hInstApp = (HINSTANCE)SE_ERR_ACCESSDENIED; err = ERROR_ACCESS_DENIED; }
SHCoUninitialize(hrInit);
if (err != ERROR_SUCCESS) SetLastError(err); return err == ERROR_SUCCESS; }
//+-------------------------------------------------------------------------
//
// 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:
//
//--------------------------------------------------------------------------
inline BOOL _ThunkClass(ULONG fMask) { return (fMask & SEE_MASK_HASLINKNAME) || (fMask & SEE_MASK_HASTITLE) || _UseClassName(fMask); }
BOOL WINAPI ShellExecuteExA(LPSHELLEXECUTEINFOA pei) { if (pei->cbSize != sizeof(SHELLEXECUTEINFOA)) { pei->hInstApp = (HINSTANCE)SE_ERR_ACCESSDENIED; SetLastError(ERROR_ACCESS_DENIED); return FALSE; }
SHELLEXECUTEINFOW seiw = {0}; seiw.cbSize = sizeof(SHELLEXECUTEINFOW); seiw.fMask = pei->fMask; seiw.hwnd = pei->hwnd; seiw.nShow = pei->nShow;
if (_UseClassKey(pei->fMask)) seiw.hkeyClass = pei->hkeyClass;
if (pei->fMask & SEE_MASK_IDLIST) seiw.lpIDList = pei->lpIDList;
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
ThunkText *pThunkText = ConvertStrings(6, pei->lpVerb, pei->lpFile, pei->lpParameters, pei->lpDirectory, _ThunkClass(pei->fMask) ? pei->lpClass : NULL, (pei->fMask & SEE_MASK_RESERVED) ? pei->hInstApp : NULL);
if (NULL == pThunkText) { pei->hInstApp = (HINSTANCE)SE_ERR_OOM; 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];
// If we are passed the SEE_MASK_FILEANDURL flag, this means that
// we have a lpFile parameter that has both the CacheFilename and the URL
// (seperated by a single NULL, eg. "CacheFileName\0UrlName). We therefore
// need to special case the thunking of pei->lpFile.
LPWSTR pwszFileAndUrl = NULL; if (pei->fMask & SEE_MASK_FILEANDURL) { int iUrlLength; int iCacheFileLength = lstrlenW(pThunkText->m_pStr[1]); WCHAR wszURL[INTERNET_MAX_URL_LENGTH]; LPSTR pszUrlPart = (LPSTR)&pei->lpFile[iCacheFileLength + 1];
if (IsBadStringPtrA(pszUrlPart, INTERNET_MAX_URL_LENGTH) || !PathIsURLA(pszUrlPart)) { ASSERT(FALSE); } else { // we have a vaild URL, so thunk it
iUrlLength = lstrlenA(pszUrlPart);
DWORD cchFileAndUrl = iUrlLength + iCacheFileLength + 2; pwszFileAndUrl = (LPWSTR)LocalAlloc(LPTR, cchFileAndUrl * sizeof(WCHAR)); if (!pwszFileAndUrl) { pei->hInstApp = (HINSTANCE)SE_ERR_OOM; return FALSE; }
SHAnsiToUnicode(pszUrlPart, wszURL, ARRAYSIZE(wszURL));
// construct the wide multi-string
StrCpyNW(pwszFileAndUrl, pThunkText->m_pStr[1], cchFileAndUrl); StrCpyNW(&pwszFileAndUrl[iCacheFileLength + 1], wszURL, cchFileAndUrl - (iCacheFileLength + 1)); seiw.lpFile = pwszFileAndUrl; } }
// Call the real UNICODE ShellExecuteEx
BOOL fRet = ShellExecuteEx(&seiw);
pei->hInstApp = seiw.hInstApp;
if (pei->fMask & SEE_MASK_NOCLOSEPROCESS) { pei->hProcess = seiw.hProcess; }
LocalFree(pThunkText); if (pwszFileAndUrl) LocalFree(pwszFileAndUrl);
return fRet; }
// To display an error message appropriately, call this if ShellExecuteEx fails.
void _DisplayShellExecError(ULONG fMask, HWND hwnd, LPCTSTR pszFile, LPCTSTR pszTitle, DWORD dwErr) {
if (!(fMask & SEE_MASK_FLAG_NO_UI)) { if (dwErr != ERROR_CANCELLED) { LPCTSTR pszHeader; UINT ids;
// don't display "user cancelled", the user knows that already
// make sure parent window is the foreground window
if (hwnd) SetForegroundWindow(hwnd);
if (pszTitle) pszHeader = pszTitle; else pszHeader = pszFile;
// 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;
case ERROR_BAD_NET_NAME: case ERROR_SEM_TIMEOUT: ids = IDS_REASONS_BADNETNAME; break; // LEGACY - ERROR_RESTRICTED_APP was never mapped to a valid error - ZekeL 2001-FEB-14
// because we always called _ShellExecuteError() before
// resetting the mask to ulOriginalMask, we never mapped
// ERROR_RESTRICTED_APP (which is -1) to a valid code
case ERROR_RESTRICTED_APP: ids = IDS_RESTRICTIONS; // restrictions like to use IDS_RESTRICTIONSTITLE
if (!pszTitle) pszHeader = MAKEINTRESOURCE(IDS_RESTRICTIONSTITLE); break;
// If we don't get a match, let the system handle it for us
default: ids = 0; SHSysErrorMessageBox( hwnd, pszHeader, IDS_SHLEXEC_ERROR, dwErr, pszFile, MB_OK | MB_ICONSTOP); break; }
if (ids) { ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(ids), pszHeader, (ids == IDS_LowMemError)? (MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL):(MB_OK | MB_ICONSTOP), pszFile); } } }
SetLastError(dwErr); // The message box may have clobbered.
}
void _ShellExecuteError(LPSHELLEXECUTEINFO pei, LPCTSTR lpTitle, DWORD dwErr) { ASSERT(!ISSHELLEXECSUCCEEDED(pei->hInstApp));
// if dwErr not passed in, get it
if (dwErr == 0) dwErr = GetLastError();
_DisplayShellExecError(pei->fMask, pei->hwnd, pei->lpFile, lpTitle, dwErr); }
//----------------------------------------------------------------------------
// 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]; LPCTSTR 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; }
TraceMsg(TF_SHELLEXEC, "FindExecutable: PathResolve -> %s", (LPCSTR)szFile);
if (PathIsExe(szFile)) { // public API, can't change to have cch.
StrCpyN(lpResult, szFile, MAX_PATH); // assumed length!
goto Exit; }
if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_EXECUTABLE, szFile, NULL, szFile, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szFile))))) { StrCpyN(lpResult, szFile, MAX_PATH); // assumed length!
} else { hInstance = (HINSTANCE)SE_ERR_NOASSOC; }
Exit: TraceMsg(TF_SHELLEXEC, "FindExec(%s) ==> %s", (LPTSTR)lpFile, (LPTSTR)lpResult); SetCurrentDirectory(szOldDir); return hInstance; }
HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult) { HINSTANCE hResult; WCHAR wszResult[MAX_PATH]; ThunkText * pThunkText = ConvertStrings(2, lpFile, lpDirectory);
*lpResult = '\0'; if (NULL == pThunkText) { return (HINSTANCE)SE_ERR_OOM; }
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.
// public API, assume MAX_PATH
if (0 == WideCharToMultiByte(CP_ACP, 0, wszResult, -1, lpResult, MAX_PATH, NULL, NULL)) { SetLastError((DWORD)E_FAIL); return (HINSTANCE) SE_ERR_FNF; }
return hResult;
}
//----------------------------------------------------------------------------
// 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;
//
// This is the form of the structure that is shoved into the shared memory
// block. It must be the 32-bit version for interoperability reasons.
//
typedef struct _WaitForItem32 { DWORD dwSize; DWORD fOperation; // Operation to perform
DWORD NotUsed1; LONG hEvent; // Truncated event handle
UINT NotUsed2; ITEMIDLIST idlItem; // pidl to wait for
} WAITFORITEM32, *PWAITFORITEM32;
//
// These macros enforce type safety so people are forced to use the
// WAITFORITEM32 structure when accessing the shared memory block.
//
#define SHLockWaitForItem(h, pid) ((PWAITFORITEM32)SHLockShared(h, pid))
__inline void SHUnlockWaitForItem(PWAITFORITEM32 pwfi) { SHUnlockShared(pwfi); }
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);
memcpy(&(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) { PWAITFORITEM32 pwfiFind = SHLockWaitForItem(hWait, dwProcId); if (pwfiFind) { pwfiFind->hEvent = HandleToLong(SHWaitOp_OperateInternal(pwfiFind->fOperation, &(pwfiFind->idlItem))); SHUnlockWaitForItem(pwfiFind); } }
HANDLE SHWaitOp_Create(DWORD fOperation, LPCITEMIDLIST pidlItem, DWORD dwProcId) { UINT uSizeIDList = pidlItem ? ILGetSize(pidlItem) : 0; UINT uSize = sizeof(WAITFORITEM32) + uSizeIDList; HANDLE hWaitOp = SHAllocShared(NULL, uSize, dwProcId); if (hWaitOp) { PWAITFORITEM32 pwfi = SHLockWaitForItem(hWaitOp,dwProcId); if (pwfi) { pwfi->dwSize = uSize; pwfi->fOperation = fOperation; pwfi->NotUsed1 = 0; pwfi->hEvent = HandleToLong((HANDLE)NULL); pwfi->NotUsed2 = 0;
if (pidlItem) memcpy(&(pwfi->idlItem), pidlItem, uSizeIDList);
SHUnlockWaitForItem(pwfi); } else { // clean up
SHFreeShared(hWaitOp, dwProcId); hWaitOp = NULL; } }
return hWaitOp; }
// 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
//
STDAPI_(DWORD) SHWaitForFileToOpen(LPCITEMIDLIST pidl, UINT uOptions, DWORD dwTimeout) { HWND hwndShell; HANDLE hWaitOp; HANDLE hEvent = NULL; DWORD dwProcIdSrc = GetCurrentProcessId(); DWORD dwReturn = WAIT_OBJECT_0; // we need a default
hwndShell = GetShellWindow();
if ((uOptions & (WFFO_WAIT | WFFO_ADD)) != 0) { if (hwndShell) { DWORD dwProcIdDst; GetWindowThreadProcessId(hwndShell, &dwProcIdDst);
// Do just the add and/or wait portions
hWaitOp = SHWaitOp_Create(uOptions & (WFFO_WAIT | WFFO_ADD), pidl, dwProcIdSrc); if (hWaitOp) { SendMessage(hwndShell, CWM_WAITOP, (WPARAM)hWaitOp, (LPARAM)dwProcIdSrc);
// Now get the hEvent and convert to a local handle
PWAITFORITEM32 pwfi = SHLockWaitForItem(hWaitOp, dwProcIdSrc); if (pwfi) { hEvent = SHMapHandle(LongToHandle(pwfi->hEvent),dwProcIdDst, dwProcIdSrc, EVENT_ALL_ACCESS, 0); SHUnlockWaitForItem(pwfi); } SHFreeShared(hWaitOp,dwProcIdSrc); } } else { // Do just the add and/or wait portions
hEvent = SHWaitOp_OperateInternal(uOptions & (WFFO_WAIT | WFFO_ADD), pidl); }
if (hEvent) { if (uOptions & WFFO_WAIT) dwReturn = SHProcessMessagesUntilEvent(NULL, hEvent, dwTimeout);
if (hwndShell) // Close the duplicated handle.
CloseHandle(hEvent); } }
if (uOptions & WFFO_REMOVE) { if (hwndShell) { hWaitOp = SHWaitOp_Create(WFFO_REMOVE, pidl, dwProcIdSrc); if (hWaitOp) { SendMessage(hwndShell, CWM_WAITOP, (WPARAM)hWaitOp, (LPARAM)dwProcIdSrc); SHFreeShared(hWaitOp,dwProcIdSrc); } } else { SHWaitOp_OperateInternal(WFFO_REMOVE, pidl); } } return dwReturn; }
// Signals that the file is open
//
STDAPI_(BOOL) SignalFileOpen(LPCITEMIDLIST pidl) { BOOL fResult = FALSE; HWND hwndShell = GetShellWindow(); if (hwndShell) { PWAITFORITEM32 pwfi; DWORD dwProcId = GetCurrentProcessId(); HANDLE hWaitOp = SHWaitOp_Create(WFFO_SIGNAL, pidl, dwProcId); if (hWaitOp) { SendMessage(hwndShell, CWM_WAITOP, (WPARAM)hWaitOp, (LPARAM)dwProcId);
// Now get the hEvent to determine return value...
pwfi = SHLockWaitForItem(hWaitOp, dwProcId); if (pwfi) { fResult = (LongToHandle(pwfi->hEvent) != (HANDLE)NULL); SHUnlockWaitForItem(pwfi); } SHFreeShared(hWaitOp,dwProcId); } } else { fResult = (SHWaitOp_OperateInternal(WFFO_SIGNAL, pidl) == (HANDLE)NULL); }
// Let everyone know that we opened something
UINT uMsg = RegisterWindowMessage(SH_FILEOPENED); BroadcastSystemMessage(BSF_POSTMESSAGE, BSM_ALLCOMPONENTS, uMsg, NULL, NULL);
return fResult; }
//
// Checks to see if darwin is enabled.
//
BOOL IsDarwinEnabled() { static BOOL s_fDarwinEnabled = TRUE; static BOOL s_fInit = FALSE; if (!s_fInit) { HKEY hkeyPolicy = 0; if (RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_POLICIES_EXPLORER, 0, KEY_READ, &hkeyPolicy) == ERROR_SUCCESS) { if (SHQueryValueEx(hkeyPolicy, TEXT("DisableMSI"), NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { s_fDarwinEnabled = FALSE; // policy turns MSI off
} RegCloseKey(hkeyPolicy); } s_fInit = TRUE; } return s_fDarwinEnabled; }
// takes the darwin ID string from the registry, and calls darwin to get the
// full path to the application.
//
// IN: pszDarwinDescriptor - this has the contents of the darwin key read out of the registry.
// it should contain a string like "[Darwin-ID-for-App] /switches".
//
// OUT: pszDarwinComand - the full path to the application to this buffer w/ switches.
//
STDAPI ParseDarwinID(LPTSTR pszDarwinDescriptor, LPTSTR pszDarwinCommand, DWORD cchDarwinCommand) { DWORD dwError = CommandLineFromMsiDescriptor(pszDarwinDescriptor, pszDarwinCommand, &cchDarwinCommand);
return HRESULT_FROM_WIN32(dwError); }
|