1965 lines
63 KiB
C++
1965 lines
63 KiB
C++
#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);
|
|
}
|