mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1467 lines
36 KiB
1467 lines
36 KiB
/*
|
|
* exec.c -
|
|
*
|
|
* Implements the ShellExecute() function
|
|
*/
|
|
#define UNICODE 1
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
WCHAR szOpen[] = L"open";
|
|
WCHAR szSection[] = L"windows";
|
|
WCHAR szProgramsW[] = L"programs";
|
|
WCHAR szExtensions[] = L"extensions";
|
|
WCHAR szSlashCommand[] = L"\\command";
|
|
WCHAR szSlashDDEExec[] = L"\\ddeexec";
|
|
WCHAR szUserExtKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Extensions\\";
|
|
|
|
WCHAR szConv[] = L"ddeconv";
|
|
|
|
// in exec2nt.c
|
|
HANDLE APIENTRY
|
|
FindAssociatedExeW(
|
|
HWND hwnd,
|
|
LPWSTR lpCommand,
|
|
LPWSTR lpName);
|
|
|
|
VOID WaitForThisDDEMsg(HWND hMe, UINT wMsg);
|
|
|
|
|
|
// in util.c
|
|
LPWSTR APIENTRY SheRemoveQuotesW(LPWSTR sz);
|
|
VOID APIENTRY CheckEscapes(LPWSTR szFile, DWORD cch);
|
|
|
|
LPFNWOWSHELLEXECCB lpfnWowShellExecCB = NULL;
|
|
|
|
/*
|
|
* Reads the list of program strings from win.ini
|
|
*/
|
|
|
|
LPWSTR GetPrograms()
|
|
{
|
|
static LPWSTR lpPrograms = WCHAR_NULL;
|
|
LPWSTR lpT,lpS;
|
|
|
|
if (lpPrograms) {
|
|
return lpPrograms;
|
|
}
|
|
|
|
if (!(lpPrograms = (LPWSTR)LocalAlloc(LPTR, (CBFILENAME+1) * sizeof(WCHAR)))) {
|
|
return(NULL);
|
|
} else {
|
|
|
|
GetProfileString(szSection,szProgramsW,WSTR_BLANK,lpPrograms,CBFILENAME);
|
|
|
|
for (lpS = lpT = lpPrograms; *lpT; lpT++) {
|
|
if (*lpT == WCHAR_SPACE) {
|
|
while (*lpT == WCHAR_SPACE) {
|
|
lpT++;
|
|
}
|
|
lpT--;
|
|
*lpS++ = 0;
|
|
} else {
|
|
*lpS++ = *lpT;
|
|
}
|
|
}
|
|
|
|
*lpS++ = WCHAR_NULL;
|
|
*lpS++ = WCHAR_NULL;
|
|
return(lpPrograms);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Determines if an extension is a program
|
|
*/
|
|
|
|
BOOL IsProgram(
|
|
LPWSTR lpExt)
|
|
{
|
|
LPWSTR lpPrograms = GetPrograms();
|
|
return(IsStringInList(lpExt,lpPrograms));
|
|
}
|
|
|
|
|
|
/* finds a file along the path. Returns the error code or 0 if success.
|
|
*/
|
|
|
|
WORD
|
|
SearchForFile(
|
|
LPCWSTR lpDir,
|
|
LPWSTR lpFile,
|
|
LPWSTR lpFullPath,
|
|
DWORD cchFullPath,
|
|
LPWSTR lpExt)
|
|
{
|
|
LPWSTR lpT;
|
|
LPWSTR lpD;
|
|
LPWSTR lpExts;
|
|
WCHAR szFile[CBFILENAME+1];
|
|
DWORD cchPath;
|
|
|
|
if (*lpFile == WCHAR_QUOTE) {
|
|
lpFile = SheRemoveQuotes(lpFile);
|
|
}
|
|
if (NULL != (lpT=StrRChrW(lpFile, NULL, WCHAR_BSLASH))) {
|
|
++lpT;
|
|
} else if (NULL != (lpT=StrRChrW(lpFile, NULL, WCHAR_COLON))) {
|
|
++lpT;
|
|
} else {
|
|
lpT = lpFile;
|
|
}
|
|
|
|
if (NULL != (lpT=StrRChrW(lpT, NULL, WCHAR_DOT))) {
|
|
int n;
|
|
|
|
n = wcslen(lpT + 1);
|
|
StrCpyN(lpExt, lpT+1, n < 64 ? n+1 : 65); // max extension
|
|
} else {
|
|
*lpExt = WCHAR_NULL;
|
|
}
|
|
|
|
// If there's no extension then just use programs list don't
|
|
// try searc hing for the app sans extension. This fixes the bogus
|
|
// file.run stuff.
|
|
if (!*lpExt) {
|
|
goto UseDefExts;
|
|
}
|
|
|
|
//
|
|
// NOTE: Do NOT call CharUpper for any of the strings in this routine.
|
|
// It will cause problems for the Turkish locale.
|
|
//
|
|
|
|
cchPath = SearchPath(lpDir, lpFile, NULL, cchFullPath, lpFullPath, &lpT);
|
|
|
|
if (!cchPath) {
|
|
cchPath = SearchPath(NULL, lpFile, NULL, cchFullPath, lpFullPath, &lpT);
|
|
}
|
|
|
|
if (cchPath >= cchFullPath) {
|
|
return(SE_ERR_OOM);
|
|
}
|
|
|
|
if (cchPath == 0) {
|
|
return(SE_ERR_FNF);
|
|
}
|
|
|
|
CheckEscapes(lpFullPath, cchFullPath);
|
|
return 0;
|
|
|
|
UseDefExts:
|
|
wcscpy(szFile,lpFile);
|
|
lpFile = szFile;
|
|
|
|
wcscat(lpFile,WSTR_DOT);
|
|
lpD = lpFile + wcslen(lpFile);
|
|
|
|
if (NULL != (lpExts = GetPrograms())) {
|
|
// We want to pass through the loop twice checking whether the
|
|
// file is in lpDir first, and then if it's in the sysdirs, via SearchPath(NULL, ...)
|
|
// Add some state and extend the while loop
|
|
LPCWSTR lpTempDir = lpDir;
|
|
LPWSTR lpTempExts = lpExts;
|
|
BOOL bCheckedSysDirs = FALSE;
|
|
|
|
while (*lpTempExts || !bCheckedSysDirs) {
|
|
|
|
// After the first pass, lpTempExts will be NULL
|
|
// Reset it and loop through again with lpTempDir = NULL so that
|
|
// SearchPath looks at the system dirs
|
|
|
|
if (!*lpTempExts) {
|
|
bCheckedSysDirs = TRUE;
|
|
lpTempExts = lpExts;
|
|
lpTempDir = NULL;
|
|
}
|
|
|
|
wcscpy(lpD,lpTempExts);
|
|
wcscpy(lpExt,lpTempExts);
|
|
|
|
cchPath = SearchPath(lpTempDir, lpFile, NULL, cchFullPath, lpFullPath, &lpT);
|
|
if (cchPath >= cchFullPath) {
|
|
return(SE_ERR_OOM);
|
|
}
|
|
|
|
if (cchPath != 0) {
|
|
CheckEscapes(lpFullPath, cchFullPath);
|
|
return 0;
|
|
}
|
|
|
|
lpTempExts += wcslen(lpTempExts)+1;
|
|
}
|
|
}
|
|
return(SE_ERR_FNF);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: QualifyAppName
|
|
//
|
|
// Synopsis: Creates a fully qualified path to the app in a commandline
|
|
//
|
|
// INC lpCmdLine Command line to qualify
|
|
// (Must have DQuotes if has spaces)
|
|
// OUT lpImage Fully qualified result
|
|
// OUT ppArgs Pointer to args in lpCmdLine, _incl_ leading space
|
|
// OPTIONAL
|
|
//
|
|
// Return: DWORD length of path, 0 = fail
|
|
//
|
|
//
|
|
// Assumes: len of executable in lpCmdLine is < CBCOMMAND
|
|
// len of exts are < 64
|
|
//
|
|
// Effects:
|
|
//
|
|
//
|
|
// Notes:
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
QualifyAppName(
|
|
IN LPCWSTR lpCmdLine,
|
|
OUT LPWSTR lpImage,
|
|
OPTIONAL OUT LPCWSTR* ppArgs)
|
|
{
|
|
LPWSTR lpAppName;
|
|
BOOL bAppNameInQuotes = FALSE;
|
|
DWORD cch = 0;
|
|
|
|
lpAppName = lpImage;
|
|
|
|
// sanity check
|
|
if (!lpCmdLine) {
|
|
return(0);
|
|
}
|
|
|
|
while (*lpCmdLine &&
|
|
(*lpCmdLine != WCHAR_SPACE || bAppNameInQuotes)) {
|
|
|
|
if (*lpCmdLine == WCHAR_QUOTE) {
|
|
bAppNameInQuotes = !bAppNameInQuotes;
|
|
lpCmdLine++;
|
|
continue;
|
|
}
|
|
|
|
*lpAppName++ = *lpCmdLine++;
|
|
cch++;
|
|
}
|
|
|
|
*lpAppName = WCHAR_NULL;
|
|
|
|
//
|
|
// Save the pointer to the argument list
|
|
//
|
|
if (ppArgs) {
|
|
*ppArgs = lpCmdLine;
|
|
}
|
|
|
|
if (SheGetPathOffsetW(lpImage) == -1) {
|
|
WCHAR szTemp[CBCOMMAND];
|
|
|
|
lstrcpy((LPWSTR)szTemp, lpImage);
|
|
|
|
if (StrChrW(lpImage, WCHAR_DOT)) {
|
|
LPWSTR lpFileName;
|
|
|
|
return(SearchPath(NULL, szTemp, NULL, CBCOMMAND, lpImage, &lpFileName));
|
|
}
|
|
else {
|
|
WCHAR szExt[65];
|
|
|
|
*lpImage = WCHAR_NULL;
|
|
if (SearchForFile(NULL, (LPWSTR)szTemp, lpImage, CBCOMMAND, szExt)) {
|
|
return(0);
|
|
}
|
|
|
|
return(lstrlen(lpImage));
|
|
}
|
|
}
|
|
|
|
return(cch);
|
|
}
|
|
|
|
VOID
|
|
ReplaceParametersW(
|
|
LPWSTR lpFrom,
|
|
LPWSTR lpFile,
|
|
LPCWSTR lpParms,
|
|
LPWSTR lpTo,
|
|
DWORD cchTo)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies parameters into slots of lpFrom template. lpFile is automatically
|
|
copied into %0 (??) and/or %1 slot. Other parameters may not be in sequential
|
|
order.
|
|
|
|
Arguments:
|
|
|
|
LPWSTR lpFrom - this is the template to replace parms into (appname %0 %1 %3 %2 ...)
|
|
LPWSTR lpFile - this is the file that we want the app to load
|
|
LPCWSTR lpParms - parms to insert into template slots
|
|
LPWSTR lpTo - receiving buffer for cmdline
|
|
DWORD cchTo - length of receiving buffers (assumed equal in length)
|
|
|
|
--*/
|
|
{
|
|
INT i;
|
|
LPWSTR lpT;
|
|
BOOL bFileInQuotes = FALSE;
|
|
|
|
for (; *lpFrom && --cchTo; lpFrom++) {
|
|
if (*lpFrom == L'%') {
|
|
++lpFrom;
|
|
if (*lpFrom == L'0') {
|
|
wcscpy(lpTo,lpFile);
|
|
lpTo += wcslen(lpTo);
|
|
continue;
|
|
}
|
|
if (*lpFrom < L'1' || *lpFrom > L'9') {
|
|
*lpTo++ = *lpFrom;
|
|
continue;
|
|
}
|
|
if (*lpFrom == L'1') {
|
|
lpT = lpFile;
|
|
if (*lpFile == WCHAR_QUOTE) {
|
|
bFileInQuotes = TRUE;
|
|
lpT++;
|
|
}
|
|
}
|
|
else {
|
|
i = (*lpFrom - L'2');
|
|
lpT = (LPWSTR)lpParms;
|
|
for (;;) {
|
|
while (*lpT == WCHAR_SPACE)
|
|
lpT++;
|
|
if (!*lpT || !i)
|
|
break;
|
|
i--;
|
|
|
|
if (!(lpT=StrChrW(lpT, WCHAR_SPACE)))
|
|
continue;
|
|
}
|
|
}
|
|
while (*lpT) {
|
|
if (*lpT == WCHAR_SPACE && !bFileInQuotes)
|
|
break;
|
|
*lpTo++ = *lpT++;
|
|
if (*lpT == WCHAR_QUOTE && bFileInQuotes)
|
|
break;
|
|
}
|
|
bFileInQuotes = FALSE;
|
|
}
|
|
else {
|
|
*lpTo++ = *lpFrom;
|
|
}
|
|
}
|
|
*lpTo = WCHAR_NULL;
|
|
}
|
|
|
|
LONG APIENTRY
|
|
HereTharBeTygars(
|
|
HWND hWnd,
|
|
WORD wMsg,
|
|
WPARAM wParam,
|
|
LONG lParam)
|
|
{
|
|
HWND hwndConv;
|
|
|
|
switch (wMsg) {
|
|
case WM_DDE_ACK:
|
|
if (!(hwndConv=GetProp(hWnd, szConv)))
|
|
{
|
|
// this is the first ACK for our INITIATE message
|
|
|
|
return(SetProp(hWnd, (LPCWSTR)szConv, (HANDLE)wParam));
|
|
}
|
|
else if (hwndConv == (HWND)1)
|
|
{
|
|
// this is the ACK for our EXECUTE message
|
|
// but we are in the destroy code, so don't destroy again
|
|
|
|
FreeDDElParam(WM_DDE_ACK, lParam);
|
|
}
|
|
else if ((HWND)wParam == hwndConv)
|
|
{
|
|
// this is the ACK for our EXECUTE message
|
|
|
|
FreeDDElParam(WM_DDE_ACK, lParam);
|
|
|
|
/* The TERMINATE message will be sent in the DESTROY code
|
|
*/
|
|
|
|
DestroyWindow(hWnd);
|
|
}
|
|
|
|
// This is the ACK for our INITIATE message for all servers
|
|
// besides the first. We return FALSE, so the conversation
|
|
// should terminate.
|
|
break;
|
|
|
|
case WM_DDE_TERMINATE:
|
|
if (GetProp(hWnd, szConv) == (HWND)wParam)
|
|
{
|
|
// this TERMINATE was originated by another application
|
|
// (otherwise, hwndConv would be 1)
|
|
// they should have freed the memory for the exec message
|
|
|
|
PostMessage((HWND)wParam, WM_DDE_TERMINATE, (WPARAM)hWnd, 0L);
|
|
|
|
RemoveProp(hWnd, szConv);
|
|
DestroyWindow(hWnd);
|
|
}
|
|
|
|
// This is the TERMINATE response for our TERMINATE message
|
|
// or a random terminate (which we don't really care about)
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (NULL != (hwndConv=GetProp(hWnd, szConv)))
|
|
{
|
|
/* Make sure the window is not destroyed twice
|
|
*/
|
|
SetProp(hWnd, (LPCWSTR)szConv, (HANDLE)1);
|
|
|
|
/* Post the TERMINATE message and then
|
|
* Wait for the acknowledging TERMINATE message or a timeout
|
|
*/
|
|
PostMessage(hwndConv, WM_DDE_TERMINATE, (WPARAM)hWnd, 0L);
|
|
WaitForThisDDEMsg(hWnd, WM_DDE_TERMINATE);
|
|
|
|
RemoveProp(hWnd, szConv);
|
|
}
|
|
/* Fall through */
|
|
default:
|
|
return DefWindowProc(hWnd,wMsg,wParam,lParam);
|
|
}
|
|
|
|
return 0L;
|
|
}
|
|
|
|
VOID
|
|
LocalActivateHandler(
|
|
HWND hwnd)
|
|
{
|
|
HWND hwndT;
|
|
|
|
while (NULL != (hwndT = GetParent(hwnd)))
|
|
hwnd = hwndT;
|
|
|
|
while (NULL != (hwndT = GetWindow(hwnd,GW_OWNER)))
|
|
hwnd = hwndT;
|
|
|
|
hwndT = GetLastActivePopup(hwnd);
|
|
|
|
if (IsIconic(hwnd))
|
|
ShowWindow(hwnd,SW_SHOWNORMAL);
|
|
else {
|
|
BringWindowToTop(hwnd);
|
|
if (hwndT && hwnd != hwndT)
|
|
BringWindowToTop(hwndT);
|
|
}
|
|
}
|
|
|
|
HANDLE APIENTRY
|
|
DDEExecuteW(
|
|
HWND hwndParent,
|
|
ATOM aApplication,
|
|
ATOM aTopic,
|
|
LPWSTR lpCommand,
|
|
LPWSTR lpFile,
|
|
LPCWSTR lpParms,
|
|
WORD nShowCmd)
|
|
{
|
|
WCHAR szDDECmdW[CBCOMMAND];
|
|
HANDLE hMem;
|
|
HANDLE hRet;
|
|
LPVOID lpDDECmdAlloc;
|
|
HWND hwndDDE;
|
|
HWND hwndConv;
|
|
DWORD cbDDECmd;
|
|
|
|
/* get the actual command string
|
|
*/
|
|
ReplaceParametersW(lpCommand, lpFile, lpParms, szDDECmdW, CBCOMMAND);
|
|
|
|
/* create a hidden window for the conversation
|
|
* lets be lazy and not create a class for it
|
|
*/
|
|
hwndDDE = CreateWindow( TEXT("static"), WSTR_BLANK, WS_DISABLED, 0, 0, 0, 0,
|
|
hwndParent, NULL, hInstance, 0L);
|
|
if (!hwndDDE)
|
|
return (HANDLE)SE_ERR_OOM;
|
|
|
|
/* set the wndproc i really want
|
|
*/
|
|
SetWindowLong(hwndDDE,GWL_WNDPROC,(LONG)(FARPROC)HereTharBeTygars);
|
|
|
|
/* send the initiate message
|
|
*/
|
|
SendMessage((HWND)-1,WM_DDE_INITIATE,(WPARAM)hwndDDE,MAKELONG(aApplication,aTopic));
|
|
|
|
/* no one responded
|
|
*/
|
|
if (!(hwndConv = GetProp(hwndDDE, szConv))) {
|
|
hRet = (HANDLE)SE_ERR_FNF;
|
|
goto DDEExit0;
|
|
}
|
|
|
|
/* get dde memory for the command and copy the command line
|
|
*/
|
|
hRet = (HANDLE)SE_ERR_OOM;
|
|
|
|
//
|
|
// Allocate space for Global Memory. In the ANSI case we also
|
|
// need 2* strlen since the worst case scenario is a DBCS string
|
|
// that's totally double'd.
|
|
//
|
|
cbDDECmd = (wcslen(szDDECmdW)+1)*sizeof(WCHAR);
|
|
|
|
hMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, cbDDECmd);
|
|
if (!hMem)
|
|
goto DDEExit;
|
|
|
|
lpDDECmdAlloc = GlobalLock(hMem);
|
|
|
|
if ((nShowCmd == SW_NORMAL) || (nShowCmd == SW_MAXIMIZE)) {
|
|
LocalActivateHandler(hwndConv);
|
|
}
|
|
|
|
if (!IsWindowUnicode(hwndConv)) {
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, szDDECmdW, -1, (LPSTR)lpDDECmdAlloc, cbDDECmd,
|
|
NULL, NULL);
|
|
|
|
GlobalUnlock(hMem);
|
|
|
|
// send the execute message to the application
|
|
//
|
|
if (!PostMessageA(hwndConv, WM_DDE_EXECUTE, (WPARAM)hwndDDE, (LPARAM)hMem))
|
|
goto DDEExit2;
|
|
|
|
} else {
|
|
|
|
wcscpy((LPWSTR)lpDDECmdAlloc, szDDECmdW);
|
|
|
|
GlobalUnlock(hMem);
|
|
|
|
// send the execute message to the application
|
|
//
|
|
if (!PostMessageW(hwndConv, WM_DDE_EXECUTE, (WPARAM)hwndDDE, (LPARAM)hMem))
|
|
goto DDEExit2;
|
|
}
|
|
|
|
|
|
/* everything's going fine so FAR, so return to the application
|
|
* with the instance handle of the guy
|
|
*/
|
|
|
|
return (HANDLE)GetWindowLong(hwndConv,GWL_HINSTANCE);
|
|
|
|
DDEExit2:
|
|
GlobalFree(hMem);
|
|
|
|
DDEExit:
|
|
PostMessage(hwndConv, WM_DDE_TERMINATE, (WPARAM)hwndDDE, 0L);
|
|
hwndConv = NULL;
|
|
|
|
DDEExit0:
|
|
DestroyWindow(hwndDDE);
|
|
return hRet;
|
|
}
|
|
|
|
|
|
INT
|
|
GetExtRegData(
|
|
IN HWND hwndParent, // used in DDEExecute
|
|
IN HKEY hKey, // handle to registry base key
|
|
OUT LPWSTR lpKey, // buffer for constructing key path
|
|
OUT LPWSTR lpValue, // buffer to write value data to
|
|
IN LPWSTR lpExt, // ptr to 3 char extension str
|
|
IN LPCWSTR lpOperation, // ptr to operation str
|
|
OUT LPWSTR lpDDECommand, // ptr to dde command str (DDEExecute)
|
|
IN LPWSTR lpPathName, // ptr to pathname (DDEExecute)
|
|
IN LPCWSTR lpParameters, // ptr to paramters to pass to ddeexecute
|
|
IN LPWSTR lpResult, // used to store exe str on findexecutable call
|
|
OUT ATOM *paApplication,
|
|
OUT ATOM *paTopic,
|
|
IN WORD nShow,
|
|
OUT PHANDLE phRet,
|
|
OUT PBOOL pbDosWowApp)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the execution data in the registry. This logic was separated from
|
|
RealShellExecute in order to provide per-user file association.
|
|
|
|
Return Value:
|
|
|
|
2 DDE success, already launched, so don't launch again
|
|
1 success, try to launch it
|
|
0 failure (no DB association found) Exit
|
|
-1 failure (no DB association found) Check Extensions
|
|
-2 failure (DB association found) CheckExtensions
|
|
|
|
--*/
|
|
|
|
{
|
|
INT nRet = 1;
|
|
BOOL bCrossRef = FALSE;
|
|
LONG lError;
|
|
LPWSTR lpUserKey, lpCurKey;
|
|
|
|
WCHAR szVal[CBCOMMAND];
|
|
WCHAR szTmp[CBCOMMAND];
|
|
|
|
LPWSTR lpS;
|
|
LPWSTR lpT;
|
|
|
|
szVal[0] = WCHAR_NULL;
|
|
|
|
lpCurKey = lpUserKey = lpKey;
|
|
wcscpy(lpUserKey, (LPWSTR)szUserExtKey);
|
|
lpKey += wcslen(lpKey);
|
|
|
|
if (hKey == HKEY_CLASSES_ROOT) {
|
|
lpCurKey = lpKey;
|
|
}
|
|
|
|
*lpKey = WCHAR_DOT;
|
|
wcscpy(lpKey+1, lpExt);
|
|
|
|
lError = CBCOMMAND;
|
|
lError = RegQueryValue(hKey, lpCurKey, szTmp, &lError);
|
|
|
|
if (lError == ERROR_OUTOFMEMORY) {
|
|
*phRet = (HANDLE)SE_ERR_OOM;
|
|
return(0);
|
|
}
|
|
else if (lError != ERROR_SUCCESS) {
|
|
return(-1);
|
|
}
|
|
|
|
// First pass: Always look for the command association in the user key
|
|
|
|
lpCurKey = lpUserKey;
|
|
hKey = HKEY_CURRENT_USER;
|
|
|
|
FindCmd:
|
|
|
|
if (*szTmp) {
|
|
wcscpy(lpKey, szTmp);
|
|
|
|
// cache value for second pass
|
|
if (!bCrossRef) {
|
|
wcscpy((LPWSTR)szVal, szTmp);
|
|
}
|
|
}
|
|
|
|
wcscat(lpKey,L"\\shell\\");
|
|
wcscat(lpKey,lpOperation);
|
|
|
|
//
|
|
// Preserve this endpoint of the string
|
|
//
|
|
lpT = lpKey + wcslen(lpKey);
|
|
|
|
wcscat(lpKey, szSlashCommand);
|
|
|
|
lError = CBCOMMAND;
|
|
lError = RegQueryValue(hKey, lpCurKey, lpValue, &lError);
|
|
|
|
if (lError == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Only convert if we don't want the result
|
|
// (FindExecutable case)
|
|
//
|
|
if (!lpResult)
|
|
*pbDosWowApp = SheConvertPathW(lpValue, lpPathName, CBCOMMAND);
|
|
|
|
} else {
|
|
|
|
*lpValue = 0;
|
|
}
|
|
|
|
if (*szTmp && !lpResult) {
|
|
|
|
wcscpy(lpT, szSlashDDEExec);
|
|
lpS = lpKey + wcslen(lpKey);
|
|
|
|
// first check for DDEEXEC
|
|
//
|
|
|
|
lError = CBCOMMAND;
|
|
lError = RegQueryValue(hKey, lpCurKey, szTmp, &lError);
|
|
|
|
if ((lError == ERROR_SUCCESS) && *szTmp) {
|
|
|
|
LPWSTR lpApp;
|
|
|
|
wcscpy(lpDDECommand, szTmp);
|
|
|
|
lError = CBFILENAME;
|
|
wcscpy(lpS,L"\\application");
|
|
*szTmp = 0;
|
|
RegQueryValue(hKey, lpCurKey, szTmp, &lError);
|
|
|
|
// if "application" key not found fake it from the
|
|
// command string
|
|
|
|
lpApp = szTmp;
|
|
|
|
if (!*szTmp) {
|
|
LPWSTR lp;
|
|
|
|
if (!*lpValue) {
|
|
*phRet = (HANDLE)SE_ERR_ASSOCINCOMPLETE;
|
|
nRet = 0;
|
|
goto QueryDone;
|
|
}
|
|
|
|
wcscpy(szTmp, lpValue);
|
|
|
|
/* In the first word, look for the last '\\', or last ':'
|
|
* if no '\\' and then copy up to the extension
|
|
*/
|
|
if (NULL != (lp=StrChrW(szTmp, WCHAR_SPACE)))
|
|
*lp = WCHAR_NULL;
|
|
|
|
if (NULL != (lp=StrRChrW(szTmp, NULL, WCHAR_BSLASH)))
|
|
lpApp = lp + 1;
|
|
else if (NULL != (lp=StrRChrW(szTmp, NULL, WCHAR_COLON)))
|
|
lpApp = lp + 1;
|
|
|
|
if (NULL != (lp=StrChrW(lpApp, WCHAR_DOT))) // remove extension part
|
|
*lp = 0;
|
|
}
|
|
|
|
*paApplication = GlobalAddAtom(lpApp);
|
|
|
|
*szTmp = 0;
|
|
lError = CBFILENAME;
|
|
wcscpy(lpS,L"\\topic");
|
|
RegQueryValue(hKey, lpCurKey, szTmp, &lError);
|
|
if (!*szTmp) {
|
|
wcscpy(szTmp, L"System"); // default to this
|
|
}
|
|
*paTopic = GlobalAddAtom(szTmp);
|
|
|
|
*phRet = DDEExecuteW(hwndParent,*paApplication,*paTopic,lpDDECommand,
|
|
lpPathName, lpParameters, nShow);
|
|
|
|
// if the error is file not found, that indicates that
|
|
// no one answered the initiate (ie, run the app) else
|
|
// either it worked or the guy died
|
|
//
|
|
if (*phRet != (HANDLE)SE_ERR_FNF) {
|
|
|
|
//
|
|
// We are returning 2 to indicate that we have successfully
|
|
// used DDE to load the file.
|
|
//
|
|
// Also, goto Done; instead of QueryDone bacause we
|
|
// don't want to retry with the per-machine file association.
|
|
//
|
|
// [AlbertT]
|
|
//
|
|
nRet = 2;
|
|
goto Done;
|
|
}
|
|
|
|
// if it wasn't found, determine the correct command
|
|
lError = CBCOMMAND;
|
|
wcscpy(lpS, L"\\ifexec");
|
|
*szTmp = 0;
|
|
lError = RegQueryValue(hKey, lpCurKey, szTmp, &lError);
|
|
if (lError == ERROR_SUCCESS) {
|
|
if (*szTmp)
|
|
wcscpy(lpDDECommand, szTmp);
|
|
else {
|
|
*lpDDECommand = WCHAR_NULL;
|
|
}
|
|
}
|
|
else if (lError != ERROR_BADKEY && lError != ERROR_FILE_NOT_FOUND) {
|
|
*phRet = (HANDLE)SE_ERR_ASSOCINCOMPLETE;
|
|
nRet = 0;
|
|
goto QueryDone;
|
|
}
|
|
}
|
|
|
|
*lpT = 0;
|
|
}
|
|
|
|
if (!*lpValue) {
|
|
nRet = -2;
|
|
}
|
|
|
|
QueryDone:
|
|
|
|
// Run through the cmd search part again for HKEY_CLASSES_ROOT
|
|
// if it couldn't find it in HKEY_CURRENT_USERS
|
|
|
|
if ((nRet != 1) && !bCrossRef) {
|
|
|
|
wcscpy(lpValue, (LPWSTR)szVal);
|
|
|
|
// the second pass is always with key set to HKEY_CLASSES_ROOT
|
|
lpCurKey = lpKey;
|
|
hKey = HKEY_CLASSES_ROOT;
|
|
|
|
*lpKey = WCHAR_DOT;
|
|
wcscpy(lpKey+1, lpExt);
|
|
|
|
bCrossRef = TRUE;
|
|
|
|
// reset return value
|
|
nRet = 1;
|
|
goto FindCmd;
|
|
}
|
|
|
|
Done:
|
|
|
|
return(nRet);
|
|
}
|
|
|
|
|
|
#ifdef OLDUNICODE
|
|
|
|
// main bulk of execute handler
|
|
|
|
HINSTANCE RealShellExecuteExW(
|
|
HWND hwndParent,
|
|
LPCTSTR lpOperation,
|
|
LPCTSTR lpFile,
|
|
LPCTSTR lpParameters,
|
|
LPCTSTR lpDirectory,
|
|
LPTSTR lpResult,
|
|
LPCTSTR lpTitle,
|
|
LPTSTR lpReserved,
|
|
WORD nShow,
|
|
LPHANDLE lphProcess,
|
|
DWORD dwFlags)
|
|
{
|
|
BOOL bDosWowApp = FALSE;
|
|
LPWSTR lpszDosWowDir;
|
|
|
|
BOOL status;
|
|
BOOL fOpen;
|
|
HANDLE hRet;
|
|
INT nRet;
|
|
|
|
// assumes extension is max 63 ?
|
|
WCHAR szExt[64];
|
|
|
|
WCHAR szOldDir[CBPATHMAX];
|
|
WCHAR szOldEnvDir[CBPATHMAX];
|
|
WCHAR szTempEnvDir[CBPATHMAX];
|
|
|
|
LPWSTR lpUpperFile = NULL;
|
|
|
|
LPWSTR lpUpperDir = NULL;
|
|
LPWSTR lpDDECommand = NULL;
|
|
LPWSTR lpCmdLine = NULL;
|
|
|
|
LPWSTR lpMem;
|
|
LPWSTR lpImage = NULL;
|
|
LPWSTR lpKey = NULL;
|
|
LPWSTR lpValue = NULL;
|
|
LPWSTR lpPathName = NULL;
|
|
|
|
LPWSTR lpTemp;
|
|
|
|
ATOM aApplication = 0;
|
|
ATOM aTopic = 0;
|
|
BOOL bAlreadyFound = FALSE;
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
DWORD dwError = 0;
|
|
DWORD cch;
|
|
DWORD OldErrorMode;
|
|
|
|
|
|
//
|
|
// Make uppercase versions of the file and the directory so that
|
|
// DOS won't barf on uppercasing oem characters.
|
|
//
|
|
// Warning: be careful if you want to take these out... we are
|
|
// modifying lpUpperFile, so things are no longer const...
|
|
//
|
|
if (lpFile) {
|
|
if (!(lpUpperFile = (LPWSTR)LocalAlloc(LPTR, (wcslen(lpFile)+1) * sizeof(TCHAR)))) {
|
|
hRet = (HANDLE)SE_ERR_OOM;
|
|
goto Exit;
|
|
}
|
|
wcscpy(lpUpperFile, lpFile);
|
|
}
|
|
if (lpDirectory) {
|
|
if (!(lpUpperDir = (LPWSTR)LocalAlloc(LPTR, (wcslen(lpDirectory)+1) * sizeof(TCHAR)))) {
|
|
hRet = (HANDLE)SE_ERR_OOM;
|
|
goto Exit;
|
|
}
|
|
wcscpy(lpUpperDir, lpDirectory);
|
|
}
|
|
|
|
if (!lpOperation) {
|
|
fOpen = TRUE;
|
|
lpOperation = szOpen;
|
|
}
|
|
else
|
|
fOpen = (_wcsicmp(szOpen,lpOperation) == 0);
|
|
|
|
GetCurrentDirectory(CBPATHMAX, szOldDir);
|
|
|
|
//
|
|
// get the env var. for the current drive
|
|
//
|
|
cch = CBPATHMAX;
|
|
SheGetDirExW(szOldDir, &cch, szOldEnvDir);
|
|
|
|
//
|
|
// If we did not pass in a directory, then we only need to
|
|
// set the environment variable for the current drive
|
|
// (this is done since Wow reads this instead of GetCurrentDirectory()).
|
|
// We use lpszDosWowDir as the directory that we need to set (the
|
|
// call is deferred until we know if we are dos/wow).
|
|
//
|
|
// If we did pass in a directory, we need to change lpszDosWowDir below.
|
|
//
|
|
szTempEnvDir[0] = 0;
|
|
lpszDosWowDir = szOldDir;
|
|
|
|
if (lpUpperDir) {
|
|
|
|
lpTemp = lpUpperDir;
|
|
|
|
//
|
|
// skip leading spaces
|
|
//
|
|
while (*lpTemp == WCHAR_SPACE)
|
|
lpTemp++;
|
|
|
|
if (*lpTemp) {
|
|
|
|
if (WCHAR_QUOTE == *lpTemp) {
|
|
lpTemp = SheRemoveQuotesW(lpTemp);
|
|
}
|
|
|
|
//
|
|
// a drive is specified, get the current
|
|
// directory for that drive, and reset it later
|
|
//
|
|
cch = CBPATHMAX;
|
|
SheGetDirExW( lpTemp, &cch, szTempEnvDir);
|
|
|
|
//
|
|
// To maintain backward compatibility with NT 1.0,
|
|
// we change the directory here too. This has the net
|
|
// effect of making the following work:
|
|
//
|
|
// Current dir = C:\curdir
|
|
//
|
|
// RealShellExecute() with:
|
|
//
|
|
// file = ".\foo.txt"
|
|
// dir = "d:\otherdir"
|
|
//
|
|
// The old code would try to launch:
|
|
//
|
|
// d:\otherdir\foo.txt
|
|
//
|
|
// The reason this worked is because we changed the current
|
|
// directory early... i.e., here.
|
|
//
|
|
SheChangeDirExW(lpTemp);
|
|
|
|
lpszDosWowDir = lpTemp;
|
|
}
|
|
}
|
|
|
|
|
|
hRet = (HANDLE)SE_ERR_OOM;
|
|
|
|
{
|
|
UINT cchCommandLen;
|
|
UINT cchParameters;
|
|
|
|
// Need to explicitly calculate how much we need for the lpImage buff that is
|
|
// passed to CreateProcessW in case lpParameters is mondo huge and the name of the
|
|
// fully qualified lpPathName is bigger than the original
|
|
if (lpParameters) {
|
|
cchParameters = wcslen(lpParameters);
|
|
} else {
|
|
cchParameters = 0;
|
|
}
|
|
|
|
cchCommandLen = CBCOMMAND + cchParameters + 2; // 2 for space + null
|
|
|
|
if (!(lpMem = (LPWSTR)LocalAlloc(LPTR, (CBFILENAME + (CBCOMMAND * 4) + cchCommandLen)
|
|
* sizeof(TCHAR)))) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
lpPathName = lpMem;
|
|
lpKey = lpPathName + CBFILENAME;
|
|
lpValue = lpKey + CBCOMMAND;
|
|
lpImage = lpValue + CBCOMMAND;
|
|
lpCmdLine = lpImage + CBCOMMAND;
|
|
lpDDECommand = lpCmdLine + CBCOMMAND;
|
|
|
|
if (hRet = (HANDLE)SearchForFile(lpDirectory, lpUpperFile, lpPathName, CBFILENAME, szExt))
|
|
goto Exit;
|
|
|
|
if (IsProgram(szExt)) {
|
|
if (!fOpen) {
|
|
hRet = (HANDLE)SE_ERR_NOASSOC;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// if its in programs=, then run it
|
|
//
|
|
// Convert only if we aren't doing a FindExecutable
|
|
//
|
|
if (!lpResult)
|
|
bDosWowApp = SheConvertPathW(lpPathName, NULL, CBFILENAME);
|
|
|
|
wcscpy(lpCmdLine,lpPathName);
|
|
if (lpParameters && *lpParameters) {
|
|
wcscat(lpCmdLine,WSTR_SPACE);
|
|
wcscat(lpCmdLine,lpParameters);
|
|
}
|
|
|
|
goto FoundCommandLine;
|
|
}
|
|
|
|
//
|
|
// Allow *.dll when we are doing a find executable
|
|
//
|
|
if (!_wcsicmp(szExt, L"dll") && lpResult){
|
|
|
|
goto FoundCommandLine;
|
|
}
|
|
|
|
nRet = GetExtRegData(hwndParent, HKEY_CURRENT_USER, lpKey, lpValue,
|
|
(LPWSTR)szExt, lpOperation, lpDDECommand, lpPathName, lpParameters,
|
|
lpResult, &aApplication, &aTopic, nShow, &hRet, &bDosWowApp);
|
|
|
|
if (nRet <= 0) {
|
|
nRet = GetExtRegData(hwndParent, HKEY_CLASSES_ROOT, lpKey, lpValue,
|
|
(LPWSTR)szExt, lpOperation, lpDDECommand, lpPathName, lpParameters,
|
|
lpResult, &aApplication, &aTopic, nShow, &hRet, &bDosWowApp);
|
|
}
|
|
|
|
/*
|
|
* 0 = total fail, while 2 = DDE success, so just exit.
|
|
*
|
|
* [AlbertT]
|
|
*/
|
|
|
|
if (nRet == 0 || nRet == 2) {
|
|
|
|
if (nRet == 2) {
|
|
|
|
//
|
|
// Set return code to success.
|
|
//
|
|
|
|
hRet = (HANDLE) 33;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* do we have the necessary RegDB info to do an exec?
|
|
*/
|
|
if (nRet < 0) {
|
|
|
|
/*
|
|
* No. so look in win.ini [extensions] section
|
|
* for ext=file.exe ^.ext thing
|
|
*/
|
|
|
|
LPWSTR lpD;
|
|
|
|
hRet = (HANDLE)(nRet == -2 ? SE_ERR_ASSOCINCOMPLETE : SE_ERR_NOASSOC);
|
|
if (!fOpen) {
|
|
/*
|
|
* if its not in the reg database, it can only be opened.
|
|
*/
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* check extensions section
|
|
*/
|
|
if (!GetProfileString(szExtensions,szExt,WSTR_BLANK,lpValue,CBCOMMAND)) {
|
|
/*
|
|
* not in extensions
|
|
*/
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// We need to do a shorten path on the app name, in case it is an lfn.
|
|
// Must do conversion here also!
|
|
//
|
|
bDosWowApp = SheConvertPathW(lpValue, lpPathName, CBCOMMAND);
|
|
|
|
lpTemp = lpValue; // save
|
|
for (lpD = lpCmdLine; *lpValue; lpValue++) {
|
|
LPWSTR lpE = NULL;
|
|
LPWSTR lpT;
|
|
|
|
/* when we find ^, insert the path without the extension
|
|
*/
|
|
if (*lpValue == L'^') {
|
|
for (lpT = lpPathName; *lpT; lpT=CharNext(lpT)) {
|
|
if (*lpT == WCHAR_DOT)
|
|
lpE = lpT;
|
|
else if (*lpT == WCHAR_BSLASH || *lpT == WCHAR_COLON)
|
|
lpE = NULL;
|
|
}
|
|
if (lpE) {
|
|
*lpE = WCHAR_NULL;
|
|
}
|
|
wcscpy(lpD,lpPathName);
|
|
lpD += wcslen(lpD);
|
|
}
|
|
else {
|
|
*lpD++ = *lpValue;
|
|
}
|
|
}
|
|
lpValue = lpTemp; // restore
|
|
*lpD = 0;
|
|
}
|
|
else {
|
|
TryAgain:
|
|
|
|
//
|
|
// parse arguments into command line
|
|
//
|
|
ReplaceParametersW(lpValue, lpPathName, lpParameters, lpCmdLine, CBCOMMAND);
|
|
}
|
|
|
|
FoundCommandLine:
|
|
|
|
if (lpResult) {
|
|
//
|
|
// just want the command
|
|
//
|
|
wcscpy(lpResult, lpCmdLine);
|
|
hRet = (HANDLE)1000;
|
|
}
|
|
else {
|
|
|
|
HWND hwndGAW;
|
|
|
|
//
|
|
// run!
|
|
//
|
|
|
|
si.cb = sizeof(STARTUPINFO);
|
|
si.lpReserved = lpReserved;
|
|
si.lpDesktop = NULL;
|
|
si.lpTitle = (LPWSTR)lpTitle;
|
|
si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L;
|
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = nShow;
|
|
si.lpReserved2 = NULL;
|
|
si.cbReserved2 = 0;
|
|
|
|
//
|
|
// Now we must set the cur dir environ var if we are
|
|
// a wow app--it must be shortened too!
|
|
//
|
|
if (bDosWowApp) {
|
|
|
|
SheShortenPath(lpszDosWowDir, TRUE);
|
|
|
|
//
|
|
// Set the current directory
|
|
//
|
|
// This is necessary because CreateProcessW calls
|
|
// BaseCheckVDM which requires that the current directory
|
|
// is < MAXIMUM_VDM_CURRENT_DIR.
|
|
//
|
|
SetCurrentDirectory(lpszDosWowDir);
|
|
|
|
//
|
|
// set the environmnet variable for WOW apps.
|
|
//
|
|
SheChangeDirExW(lpszDosWowDir);
|
|
}
|
|
|
|
//
|
|
// The cb is only valid when we are being called from wow
|
|
// If valid use it
|
|
//
|
|
if (lpfnWowShellExecCB) {
|
|
|
|
LPSTR lpCmdLineA;
|
|
DWORD cch = lstrlen(lpCmdLine)+1;
|
|
|
|
if ((lpCmdLineA = (LPSTR)LocalAlloc(LPTR, cch))) {
|
|
|
|
// This shouldn't fail but if it does, we should return a better
|
|
// error code
|
|
|
|
if (!WideCharToMultiByte(CP_ACP, 0, lpCmdLine, -1, lpCmdLineA,
|
|
cch, NULL, NULL)) {
|
|
|
|
hRet = (HANDLE)SE_ERR_OOM;
|
|
|
|
} else {
|
|
|
|
dwError = (*lpfnWowShellExecCB)(lpCmdLineA, nShow);
|
|
hRet = (HANDLE)dwError;
|
|
|
|
}
|
|
|
|
LocalFree((HLOCAL)lpCmdLineA);
|
|
}
|
|
else {
|
|
|
|
hRet = (HANDLE)SE_ERR_OOM;
|
|
}
|
|
|
|
} else {
|
|
|
|
// it's ok to not check the return code here
|
|
// because createprocess will presumably fail and generate
|
|
// one for us
|
|
|
|
QualifyAppName(lpCmdLine, lpImage, NULL);
|
|
|
|
status = CreateProcess(lpImage,
|
|
lpCmdLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
dwFlags & EXEC_SEPARATE_VDM ?
|
|
CREATE_SEPARATE_WOW_VDM | CREATE_NEW_CONSOLE :
|
|
CREATE_NEW_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi);
|
|
|
|
if ( status ) {
|
|
|
|
//
|
|
// If we are doing dde, synchronize by waiting
|
|
// to avoid sending dde msgs to a proc that's creating but
|
|
// not ready yet.
|
|
//
|
|
if ( *lpDDECommand ) {
|
|
WaitForInputIdle( pi.hProcess, 120000 );
|
|
}
|
|
if (lphProcess) {
|
|
//
|
|
// If the calling app wants the Process handle
|
|
// return it and do not close it.
|
|
//
|
|
*lphProcess = pi.hProcess;
|
|
} else {
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
hRet = (HANDLE) 33;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Some apps when run no-active steal the focus anyway so we
|
|
* we steal it back.
|
|
*/
|
|
|
|
hwndGAW = GetActiveWindow();
|
|
if (nShow == SW_SHOWMINNOACTIVE && hwndGAW != hwndParent && IsIconic(hwndGAW))
|
|
SetActiveWindow(hwndParent);
|
|
|
|
/*
|
|
* These values that hRet are being set to are the same values
|
|
* that WinExec currently returns.
|
|
*/
|
|
if (hRet < (HANDLE)32) {
|
|
/*
|
|
* If CreateProcess failed, then look at GetLastError to determine
|
|
* appropriate return code.
|
|
*/
|
|
|
|
if (!dwError) {
|
|
dwError = GetLastError();
|
|
}
|
|
|
|
switch (dwError) {
|
|
case ERROR_FILE_NOT_FOUND:
|
|
hRet = (HANDLE)2;
|
|
break;
|
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
|
|
//
|
|
// This was changed from 3->2. Is that correct?
|
|
//
|
|
hRet = (HANDLE)2;
|
|
break;
|
|
|
|
case ERROR_BAD_EXE_FORMAT:
|
|
hRet = (HANDLE)11;
|
|
break;
|
|
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
|
|
//
|
|
// This is an ugly hack. We must return a value < 32
|
|
// in the error case or else the callee thinks they
|
|
// we're successful. For now, return memory error.
|
|
//
|
|
default:
|
|
|
|
hRet = (HANDLE)8;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nRet == 1 && hRet == (HANDLE)SE_ERR_FNF) {
|
|
if (!bAlreadyFound) {
|
|
bAlreadyFound = TRUE;
|
|
//
|
|
// BUGBUG: Fourth parameter is new. Use NULL to make it link
|
|
//
|
|
if ((hRet = (HANDLE)FindAssociatedExeW(hwndParent, lpValue, szExt))
|
|
== (HANDLE)-1) {
|
|
|
|
bDosWowApp = SheConvertPathW(lpValue, lpPathName, CBCOMMAND);
|
|
goto TryAgain;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hRet >= (HANDLE)33 && *lpDDECommand) {
|
|
DDEExecuteW(hwndParent,aApplication,aTopic,lpDDECommand,
|
|
lpPathName,lpParameters,nShow);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
//
|
|
// reset environment variable for drives
|
|
//
|
|
if (szTempEnvDir[0]) {
|
|
OldErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
|
|
SheChangeDirExW(szTempEnvDir);
|
|
SetErrorMode(OldErrorMode);
|
|
}
|
|
|
|
SheChangeDirExW(szOldEnvDir);
|
|
SetCurrentDirectory(szOldDir);
|
|
|
|
if (lpUpperFile)
|
|
LocalFree(lpUpperFile);
|
|
|
|
if (lpUpperDir)
|
|
LocalFree(lpUpperDir);
|
|
|
|
LocalFree(lpMem);
|
|
|
|
if (aTopic)
|
|
GlobalDeleteAtom(aTopic);
|
|
if (aApplication)
|
|
GlobalDeleteAtom(aApplication);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
HINSTANCE RealShellExecuteW(
|
|
HWND hwndParent,
|
|
LPCTSTR lpOperation,
|
|
LPCTSTR lpFile,
|
|
LPCTSTR lpParameters,
|
|
LPCTSTR lpDirectory,
|
|
LPTSTR lpResult,
|
|
LPCTSTR lpTitle,
|
|
LPTSTR lpReserved,
|
|
WORD nShow,
|
|
LPHANDLE lphProcess)
|
|
{
|
|
|
|
return RealShellExecuteExW(hwndParent, lpOperation, lpFile, lpParameters,
|
|
lpDirectory, lpResult, lpTitle, lpReserved, nShow,
|
|
lphProcess, 0);
|
|
}
|
|
|
|
#endif
|
|
|
|
#define STR_BLANK ""
|
|
|
|
HINSTANCE APIENTRY WOWShellExecute(
|
|
HWND hwnd,
|
|
LPCSTR lpOperation,
|
|
LPCSTR lpFile,
|
|
LPSTR lpParameters,
|
|
LPCSTR lpDirectory,
|
|
INT nShowCmd,
|
|
LPFNWOWSHELLEXECCB lpfnCBWinExec)
|
|
{
|
|
HINSTANCE hinstRet;
|
|
|
|
lpfnWowShellExecCB = lpfnCBWinExec;
|
|
|
|
if (!lpParameters)
|
|
lpParameters = STR_BLANK;
|
|
|
|
hinstRet = RealShellExecuteExA(hwnd,lpOperation,lpFile,lpParameters,
|
|
lpDirectory,NULL, (LPCSTR)STR_BLANK,NULL,(WORD)nShowCmd, NULL, 0);
|
|
|
|
lpfnWowShellExecCB = NULL;
|
|
|
|
return(hinstRet);
|
|
}
|
|
|
|
#ifdef OLDUNICODE
|
|
|
|
HINSTANCE APIENTRY FindExecutableW(
|
|
LPCTSTR lpFile,
|
|
LPCTSTR lpDirectory,
|
|
LPTSTR lpResult)
|
|
{
|
|
HINSTANCE h;
|
|
|
|
*lpResult = 0;
|
|
h = RealShellExecuteExW(NULL,NULL,lpFile,WSTR_BLANK,lpDirectory,lpResult,
|
|
NULL,NULL,FALSE, NULL, 0);
|
|
|
|
if (h == (HINSTANCE)SE_ERR_SHARE) {
|
|
wcscpy(lpResult, lpFile); // well, this is close
|
|
} else if (h > (HINSTANCE)32) {
|
|
if (*lpResult) {
|
|
if (*lpResult == WCHAR_QUOTE) {
|
|
lpResult++;
|
|
if (lpResult = StrChrW(lpResult, WCHAR_QUOTE))
|
|
lpResult++;
|
|
}
|
|
if (lpResult = StrChrW(lpResult, WCHAR_SPACE))
|
|
*lpResult = WCHAR_NULL;
|
|
}
|
|
else {
|
|
h = (HINSTANCE)SE_ERR_FNF; // h = 2
|
|
}
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
#endif
|