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.
1800 lines
56 KiB
1800 lines
56 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: wowexec.c
|
|
*
|
|
* Copyright (c) 1991, Microsoft Corporation
|
|
*
|
|
* WOWEXEC - 16 Bit Server Task - Does Exec Calls on Behalf of 32 bit CreateProcess
|
|
*
|
|
*
|
|
* History:
|
|
* 05-21-91 MattFe Ported to Windows
|
|
* mar-20-92 MattFe Added Error Message Boxes (from win 3.1 progman)
|
|
* apr-1-92 mattfe added commandline exec and switch to path (from win 3.1 progman)
|
|
* jun-1-92 mattfe changed wowgetnextvdmcommand
|
|
* 12-Nov-93 DaveHart Multiple WOW support and remove captive
|
|
* GetNextVDMCommand thread from WOW32.
|
|
* 16-Nov-93 DaveHart Reduce data segment size.
|
|
\***************************************************************************/
|
|
#include "wowexec.h"
|
|
#include "wowinfo.h"
|
|
#include "shellapi.h"
|
|
#ifndef PULONG
|
|
#define PULONG
|
|
#endif
|
|
#include "vint.h"
|
|
#include "dde.h"
|
|
|
|
|
|
/*
|
|
* External Prototypes
|
|
*/
|
|
extern WORD FAR PASCAL WOWQueryDebug( void );
|
|
extern WORD FAR PASCAL WowWaitForMsgAndEvent( HWND);
|
|
extern void FAR PASCAL WowMsgBox(LPSTR szMsg, LPSTR szTitle, DWORD dwOptionalStyle);
|
|
extern DWORD FAR PASCAL WowPartyByNumber(DWORD dw, LPSTR psz);
|
|
extern DWORD FAR PASCAL WowKillTask(WORD htask);
|
|
extern void FAR PASCAL WowShutdownTimer(WORD fEnable);
|
|
HWND FaxInit(HINSTANCE hInst);
|
|
|
|
/*
|
|
* Global Variables
|
|
*/
|
|
HANDLE hAppInstance;
|
|
HWND ghwndMain = NULL;
|
|
HWND ghwndEdit = NULL;
|
|
char szOOMExitTitle[32+1];
|
|
char szOOMExitMsg[64+1];
|
|
char szAppTitleBuffer[32];
|
|
LPSTR lpszAppTitle = szAppTitleBuffer;
|
|
char szWindowsDirectory[MAXITEMPATHLEN+1];
|
|
char szOriginalDirectory[MAXITEMPATHLEN+1];
|
|
BOOL gfSharedWOW = FALSE;
|
|
BOOL gfInjectedWOW = FALSE;
|
|
WORD gwFirstCmdShow;
|
|
|
|
|
|
|
|
/*
|
|
* Forward declarations.
|
|
*/
|
|
BOOL InitializeApp(LPSTR lpszCommandLine);
|
|
LONG FAR PASCAL WndProc(HWND hwnd, WORD message, WORD wParam, LONG lParam);
|
|
WORD NEAR PASCAL ExecProgram(PWOWINFO pWowInfo);
|
|
BOOL NEAR PASCAL ExecApplication(PWOWINFO pWowInfo);
|
|
void NEAR PASCAL MyMessageBox(WORD idTitle, WORD idMessage, LPSTR psz);
|
|
PSTR FAR PASCAL GetFilenameFromPath( PSTR szPath );
|
|
void NEAR PASCAL GetPathInfo ( PSTR szPath, PSTR *pszFileName, PSTR *pszExt, WORD *pich, BOOL *pfUnc);
|
|
BOOL NEAR PASCAL StartRequestedApp(VOID);
|
|
#ifdef DEBUG
|
|
BOOL FAR PASCAL PartyDialogProc(HWND hDlg, WORD msg, WORD wParam, LONG lParam);
|
|
#endif
|
|
|
|
#define AnsiNext(x) ((x)+1)
|
|
|
|
typedef struct PARAMETERBLOCK {
|
|
WORD wEnvSeg;
|
|
LPSTR lpCmdLine;
|
|
LPVOID lpCmdShow;
|
|
DWORD dwReserved;
|
|
} PARAMETERBLOCK, *PPARAMETERBLOCK;
|
|
|
|
typedef struct CMDSHOW {
|
|
WORD two;
|
|
WORD nCmdShow;
|
|
} CMDSHOW, *PCMDSHOW;
|
|
|
|
#define CCHMAX 260+13 // MAX_PATH plus 8.3 plus NULL
|
|
|
|
#define ERROR_ERROR 0
|
|
#define ERROR_FILENOTFOUND 2
|
|
#define ERROR_PATHNOTFOUND 3
|
|
#define ERROR_MANYOPEN 4
|
|
#define ERROR_DYNLINKSHARE 5
|
|
#define ERROR_LIBTASKDATA 6
|
|
#define ERROR_MEMORY 8
|
|
#define ERROR_VERSION 10
|
|
#define ERROR_BADEXE 11
|
|
#define ERROR_OTHEREXE 12
|
|
#define ERROR_DOS4EXE 13
|
|
#define ERROR_UNKNOWNEXE 14
|
|
#define ERROR_RMEXE 15
|
|
#define ERROR_MULTDATAINST 16
|
|
#define ERROR_PMODEONLY 18
|
|
#define ERROR_COMPRESSED 19
|
|
#define ERROR_DYNLINKBAD 20
|
|
#define ERROR_WIN32 21
|
|
|
|
/* FindPrevInstanceProc -
|
|
* A little enumproc to find any window (EnumWindows) which has a
|
|
* matching EXE file path. The desired match EXE pathname is pointed to
|
|
* by the lParam. The found window's handle is stored in the
|
|
* first word of this buffer.
|
|
*/
|
|
|
|
BOOL CALLBACK FindPrevInstanceProc(HWND hWnd, LPSTR lpszParam)
|
|
{
|
|
char szT[CCHMAX];
|
|
HANDLE hInstance;
|
|
|
|
// Filter out invisible and disabled windows
|
|
//
|
|
|
|
if (!IsWindowEnabled(hWnd) || !IsWindowVisible(hWnd))
|
|
return TRUE;
|
|
|
|
hInstance = GetWindowWord(hWnd, GWW_HINSTANCE);
|
|
GetModuleFileName(hInstance, szT, sizeof (szT)-1);
|
|
|
|
// Make sure that the hWnd belongs to the current VDM process
|
|
//
|
|
// GetWindowTask returns the wowexec htask16 if the window belongs
|
|
// to a different process - thus we filter out windows in
|
|
// 'separate VDM' processes.
|
|
// - nanduri
|
|
|
|
if (lstrcmpi(szT, lpszParam) == 0 &&
|
|
GetWindowTask(hWnd) != GetWindowTask(ghwndMain)) {
|
|
*(LPHANDLE)lpszParam = hWnd;
|
|
return FALSE;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
HWND near pascal FindPopupFromExe(LPSTR lpExe)
|
|
{
|
|
HWND hwnd = (HWND)0;
|
|
BOOL b;
|
|
|
|
b = EnumWindows(FindPrevInstanceProc, (LONG)(LPSTR)lpExe);
|
|
if (!b && (hwnd = *(LPHANDLE)(LPSTR)lpExe)) {
|
|
// Find a "main" window that is the ancestor of a given window
|
|
//
|
|
|
|
HWND hwndT;
|
|
|
|
// First go up the parent chain to find the popup window. Then go
|
|
// up the owner chain to find the main window
|
|
//
|
|
|
|
while (hwndT = GetParent(hwnd))
|
|
hwnd = hwndT;
|
|
|
|
while (hwndT = GetWindow(hwnd, GW_OWNER))
|
|
hwnd = hwndT;
|
|
}
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
WORD ActivatePrevInstance(LPSTR lpszPath)
|
|
{
|
|
HWND hwnd;
|
|
HINSTANCE ret = IDS_MULTIPLEDSMSG;
|
|
|
|
if (hwnd = FindPopupFromExe(lpszPath)) {
|
|
if (IsIconic(hwnd)) {
|
|
ShowWindow(hwnd,SW_SHOWNORMAL);
|
|
}
|
|
else {
|
|
HWND hwndT = GetLastActivePopup(hwnd);
|
|
BringWindowToTop(hwnd);
|
|
if (hwndT && hwnd != hwndT)
|
|
BringWindowToTop(hwndT);
|
|
}
|
|
ret = 0;
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* */
|
|
/* ExecProgram() - */
|
|
/* */
|
|
/* Taken from Win 3.1 Progman -maf */
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/* Returns 0 for success. Otherwise returns a IDS_ string code. */
|
|
|
|
WORD NEAR PASCAL ExecProgram(PWOWINFO pWowInfo)
|
|
{
|
|
WORD ret;
|
|
PARAMETERBLOCK ParmBlock;
|
|
CMDSHOW CmdShow;
|
|
char CmdLine[CCHMAX];
|
|
|
|
ret = 0;
|
|
|
|
// Don't mess with the mouse state; unless we're on a mouseless system.
|
|
if (!GetSystemMetrics(SM_MOUSEPRESENT))
|
|
ShowCursor(TRUE);
|
|
|
|
//
|
|
// prepare the dos style cmd line (counted pascal string)
|
|
// pWowInfo->lpCmdLine contains the command tail (excluding argv[0])
|
|
//
|
|
CmdLine[0] = lstrlen(pWowInfo->lpCmdLine) - 2;
|
|
lstrcpy( &CmdLine[1], pWowInfo->lpCmdLine);
|
|
|
|
// We have a WOWINFO structure, then use it to pass the correct environment
|
|
|
|
ParmBlock.wEnvSeg = HIWORD(pWowInfo->lpEnv);
|
|
ParmBlock.lpCmdLine = CmdLine;
|
|
ParmBlock.lpCmdShow = &CmdShow;
|
|
CmdShow.two = 2;
|
|
CmdShow.nCmdShow = pWowInfo->wShowWindow;
|
|
|
|
ParmBlock.dwReserved = NULL;
|
|
|
|
ret = LoadModule(pWowInfo->lpAppName,(LPVOID)&ParmBlock) ;
|
|
|
|
switch (ret)
|
|
{
|
|
case ERROR_ERROR:
|
|
case ERROR_MEMORY:
|
|
ret = IDS_NOMEMORYMSG;
|
|
break;
|
|
|
|
case ERROR_FILENOTFOUND:
|
|
ret = IDS_FILENOTFOUNDMSG;
|
|
break;
|
|
|
|
case ERROR_PATHNOTFOUND:
|
|
ret = IDS_BADPATHMSG;
|
|
break;
|
|
|
|
case ERROR_MANYOPEN:
|
|
ret = IDS_MANYOPENFILESMSG;
|
|
break;
|
|
|
|
case ERROR_DYNLINKSHARE:
|
|
ret = IDS_ACCESSDENIED;
|
|
break;
|
|
|
|
case ERROR_VERSION:
|
|
ret = IDS_NEWWINDOWSMSG;
|
|
break;
|
|
|
|
case ERROR_RMEXE:
|
|
/* KERNEL has already put up a messagebox for this one. */
|
|
ret = 0;
|
|
break;
|
|
|
|
case ERROR_MULTDATAINST:
|
|
ret = ActivatePrevInstance(pWowInfo->lpAppName);
|
|
break;
|
|
|
|
case ERROR_COMPRESSED:
|
|
ret = IDS_COMPRESSEDEXE;
|
|
break;
|
|
|
|
case ERROR_DYNLINKBAD:
|
|
ret = IDS_INVALIDDLL;
|
|
break;
|
|
|
|
case SE_ERR_SHARE:
|
|
ret = IDS_SHAREERROR;
|
|
break;
|
|
|
|
case ERROR_WIN32:
|
|
ret = IDS_CANTLOADWIN32DLL;
|
|
break;
|
|
|
|
//
|
|
// We shouldn't get any of the following errors,
|
|
// so the strings have been removed from the resource
|
|
// file. That's why there's the OutputDebugString
|
|
// on checked builds only.
|
|
//
|
|
|
|
#ifdef DEBUG
|
|
case ERROR_OTHEREXE:
|
|
case ERROR_PMODEONLY:
|
|
case SE_ERR_ASSOCINCOMPLETE:
|
|
case SE_ERR_DDETIMEOUT:
|
|
case SE_ERR_DDEFAIL:
|
|
case SE_ERR_DDEBUSY:
|
|
case SE_ERR_NOASSOC:
|
|
{
|
|
char szTmp[64];
|
|
wsprintf(szTmp, "WOWEXEC: Unexpected error %d executing app, fix that code!\n", (int)ret);
|
|
OutputDebugString(szTmp);
|
|
}
|
|
//
|
|
// fall through to default case, so the execution
|
|
// is the same as on the free build.
|
|
//
|
|
#endif
|
|
|
|
default:
|
|
if (ret < 32)
|
|
goto EPExit;
|
|
ret = 0;
|
|
}
|
|
|
|
EPExit:
|
|
|
|
if (!GetSystemMetrics(SM_MOUSEPRESENT)) {
|
|
/*
|
|
* We want to turn the mouse off here on mouseless systems, but
|
|
* the mouse will already have been turned off by USER if the
|
|
* app has GP'd so make sure everything's kosher.
|
|
*/
|
|
if (ShowCursor(FALSE) != -1)
|
|
ShowCursor(TRUE);
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ExecApplication
|
|
*
|
|
* Code Taken From Win 3.1 ExecItem()
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#define TDB_PDB_OFFSET 0x60
|
|
#define PDB_ENV_OFFSET 0x2C
|
|
|
|
BOOL NEAR PASCAL ExecApplication(PWOWINFO pWowInfo)
|
|
{
|
|
|
|
WORD ret;
|
|
LPSTR szEnv;
|
|
LPSTR szEnd;
|
|
BYTE bDrive;
|
|
WORD wSegEnvSave;
|
|
HANDLE hTask;
|
|
LPSTR lpTask;
|
|
HANDLE hPDB;
|
|
LPSTR lpPDB;
|
|
HANDLE hNewEnv;
|
|
|
|
int nLength;
|
|
int nNewEnvLength;
|
|
LPSTR lpstrEnv;
|
|
LPSTR lpstr;
|
|
LPSTR lpOriginalEnv;
|
|
BOOL bBlanks;
|
|
LPSTR szEnvTmp;
|
|
|
|
|
|
if (!pWowInfo) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Seup the environment from WOWINFO record from getvdmcommand
|
|
//
|
|
|
|
|
|
// Figure out who we are (so we can edit our PDB/PSP)
|
|
|
|
hTask = GetCurrentTask();
|
|
lpTask = GlobalLock( hTask );
|
|
if ( lpTask == NULL ) {
|
|
ret = IDS_NOMEMORYMSG;
|
|
goto punt;
|
|
}
|
|
|
|
hPDB = *((LPWORD)(lpTask + TDB_PDB_OFFSET));
|
|
lpPDB = GlobalLock( hPDB );
|
|
|
|
// Save our environment block
|
|
wSegEnvSave = *((LPWORD)(lpPDB + PDB_ENV_OFFSET));
|
|
|
|
|
|
// Now determine the length of the original env
|
|
|
|
lpOriginalEnv = (LPSTR)MAKELONG(0,wSegEnvSave);
|
|
|
|
do {
|
|
nLength = lstrlen(lpOriginalEnv);
|
|
lpOriginalEnv += nLength + 1;
|
|
} while ( nLength != 0 );
|
|
|
|
lpOriginalEnv += 2; // Skip over magic word, see comment below
|
|
|
|
nNewEnvLength = 4 + lstrlen(lpOriginalEnv); // See magic comments below!
|
|
|
|
// WOW Apps cannot deal with an invalid TEMP=c:\bugusdir directory
|
|
// Usually on Win 3.1 the TEMP= is checked in ldboot.asm check_temp
|
|
// routine. However on NT since we get a new environment with each
|
|
// WOW app it means that we have to check it here. If it is not
|
|
// valid then it is edited in the environment.
|
|
// - mattfe june 11 93
|
|
|
|
szEnv = pWowInfo->lpEnv;
|
|
szEnd = szEnv + pWowInfo->EnvSize;
|
|
szEnd--;
|
|
|
|
while ( szEnv < szEnd ) {
|
|
|
|
nLength = lstrlen(szEnv) + 1;
|
|
|
|
if ( (*szEnv == 'T') &&
|
|
(*(szEnv+1) == 'E') &&
|
|
(*(szEnv+2) == 'M') &&
|
|
(*(szEnv+3) == 'P') &&
|
|
(*(szEnv+4) == '=') ) {
|
|
|
|
// Try to set the current directory to the TEMP= dir
|
|
// If it fails then nuke the TEMP= part of the environment
|
|
// in the same way check_TEMP does in ldboot.asm
|
|
// We also scan for blanks, just like check_TEMP
|
|
|
|
bBlanks = FALSE;
|
|
szEnvTmp = szEnv+5;
|
|
while (*szEnvTmp != 0) {
|
|
if (*szEnvTmp == ' ') {
|
|
bBlanks = TRUE;
|
|
break;
|
|
}
|
|
szEnvTmp++;
|
|
}
|
|
|
|
if (bBlanks || (SetCurrentDirectory(szEnv+5) )) {
|
|
while (*szEnv != 0) {
|
|
*szEnv = 'x';
|
|
szEnv++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
szEnv += nLength;
|
|
}
|
|
|
|
// WOW Apps only have a Single Current Directory
|
|
// Find =d:=D:\path where d is the active drive letter
|
|
// Note that the drive info doesn have to be at the start
|
|
// of the environment.
|
|
|
|
bDrive = pWowInfo->CurDrive + 'A';
|
|
szEnv = pWowInfo->lpEnv;
|
|
szEnd = szEnv + pWowInfo->EnvSize;
|
|
szEnd--;
|
|
|
|
while ( szEnv < szEnd ) {
|
|
|
|
nLength = lstrlen(szEnv) + 1;
|
|
if ( *szEnv == '=' ) {
|
|
if ( (bDrive == (*(szEnv+1) & 0xdf)) &&
|
|
(*(szEnv+2) == ':') && (*(szEnv+3) == '=') ) {
|
|
SetCurrentDirectory(szEnv+4);
|
|
}
|
|
} else {
|
|
nNewEnvLength += nLength;
|
|
}
|
|
szEnv += nLength;
|
|
}
|
|
|
|
// Now allocate and make a personal copy of the Env
|
|
|
|
hNewEnv = GlobalAlloc( GMEM_MOVEABLE, (DWORD)nNewEnvLength );
|
|
if ( hNewEnv == NULL ) {
|
|
ret = IDS_NOMEMORYMSG;
|
|
goto punt;
|
|
}
|
|
lpstrEnv = GlobalLock( hNewEnv );
|
|
if ( lpstrEnv == NULL ) {
|
|
GlobalFree( hNewEnv );
|
|
ret = IDS_NOMEMORYMSG;
|
|
goto punt;
|
|
}
|
|
|
|
// Copy only the non-current directory env variables
|
|
|
|
szEnv = pWowInfo->lpEnv;
|
|
lpstr = lpstrEnv;
|
|
|
|
while ( szEnv < szEnd ) {
|
|
nLength = lstrlen(szEnv) + 1;
|
|
|
|
// Copy everything except the drive letters
|
|
|
|
if ( *szEnv != '=' ) {
|
|
lstrcpy( lpstr, szEnv );
|
|
lpstr += nLength;
|
|
}
|
|
szEnv += nLength;
|
|
}
|
|
*lpstr++ = '\0'; // Extra '\0' on the end
|
|
|
|
// Magic environment goop!
|
|
//
|
|
// Windows only supports the passing of environment information
|
|
// using the LoadModule API. The WinExec API just causes
|
|
// the application to inherit the current DOS PDB's environment.
|
|
//
|
|
// Also, the environment block has a little gotcha at the end. Just
|
|
// after the double-nuls there is a magic WORD value 0x0001 (DOS 3.0
|
|
// and later). After the value is a nul terminated string indicating
|
|
// the applications executable file name (including PATH).
|
|
//
|
|
// We copy the value from WOWEXEC's original environment because
|
|
// that is what WinExec appears to do.
|
|
//
|
|
// -BobDay
|
|
|
|
*lpstr++ = '\1';
|
|
*lpstr++ = '\0'; // Magic 0x0001 from DOS
|
|
|
|
lstrcpy( lpstr, lpOriginalEnv ); // More Magic (see comment above)
|
|
|
|
// Temporarily update our environment block
|
|
|
|
*((LPWORD)(lpPDB + PDB_ENV_OFFSET)) = (WORD)hNewEnv | 1;
|
|
|
|
pWowInfo->lpEnv = lpstrEnv;
|
|
|
|
|
|
//
|
|
// Set our current drive & directory as requested.
|
|
//
|
|
|
|
SetCurrentDirectory(pWowInfo->lpCurDir);
|
|
|
|
ret = ExecProgram(pWowInfo);
|
|
|
|
// Restore our environment block
|
|
|
|
*((LPWORD)(lpPDB + PDB_ENV_OFFSET)) = wSegEnvSave;
|
|
|
|
GlobalUnlock( hPDB );
|
|
GlobalUnlock( hTask );
|
|
GlobalUnlock( hNewEnv );
|
|
GlobalFree( hNewEnv );
|
|
|
|
|
|
punt:
|
|
|
|
// Change back to the Windows Directory
|
|
// So that if we are execing from a NET Drive its
|
|
// Not kept Active
|
|
|
|
SetCurrentDirectory(szWindowsDirectory);
|
|
|
|
// Always call this when we are done try to start an app.
|
|
// It will do nothing if we were successful in starting an
|
|
// app, otherwise if we were unsucessful it will signal that
|
|
// the app has completed.
|
|
WowFailedExec();
|
|
|
|
// Check for errors.
|
|
if (ret) {
|
|
MyMessageBox(IDS_EXECERRTITLE, ret, pWowInfo->lpAppName);
|
|
|
|
if ( ! gfSharedWOW) {
|
|
|
|
//
|
|
// We have just failed to exec the only app we are going to
|
|
// try to exec in this separate WOW VDM. We need to end WOW
|
|
// here explicitly, otherwise we'll hang around forever because
|
|
// the normal path is for kernel to exit the VDM when a task
|
|
// exit causes the number of tasks to transition from 2 to 1 --
|
|
// in this case the number of tasks never exceeds 1.
|
|
//
|
|
|
|
ExitKernelThunk(0);
|
|
|
|
}
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* */
|
|
/* MyMessageBox() - */
|
|
/* Taken From Win 3.1 Progman - maf */
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
void NEAR PASCAL MyMessageBox(WORD idTitle, WORD idMessage, LPSTR psz)
|
|
{
|
|
char szTitle[MAXTITLELEN+1];
|
|
char szMessage[MAXMESSAGELEN+1];
|
|
char szTempField[MAXMESSAGELEN+1];
|
|
|
|
|
|
if (!LoadString(hAppInstance, idTitle, szTitle, sizeof(szTitle)))
|
|
goto MessageBoxOOM;
|
|
|
|
if (idMessage > IDS_LAST)
|
|
{
|
|
if (!LoadString(hAppInstance, IDS_UNKNOWNMSG, szTempField, sizeof(szTempField)))
|
|
goto MessageBoxOOM;
|
|
wsprintf(szMessage, szTempField, idMessage);
|
|
}
|
|
else
|
|
{
|
|
if (!LoadString(hAppInstance, idMessage, szTempField, sizeof(szTempField)))
|
|
goto MessageBoxOOM;
|
|
|
|
if (psz)
|
|
wsprintf(szMessage, szTempField, (LPSTR)psz);
|
|
else
|
|
lstrcpy(szMessage, szTempField);
|
|
}
|
|
|
|
WowMsgBox(szMessage, szTitle, MB_ICONEXCLAMATION);
|
|
return;
|
|
|
|
|
|
MessageBoxOOM:
|
|
WowMsgBox(szOOMExitMsg, szOOMExitTitle, MB_ICONHAND);
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* main
|
|
*
|
|
*
|
|
* History:
|
|
* 04-13-91 ScottLu Created - from 32 bit exec app
|
|
* 21-mar-92 mattfe significant alterations for WOW
|
|
\***************************************************************************/
|
|
|
|
int PASCAL WinMain(HANDLE hInstance,
|
|
HANDLE hPrevInstance, LPSTR lpszCmdLine, int iCmd)
|
|
|
|
{
|
|
int i;
|
|
MSG msg;
|
|
LPSTR pch,pch1;
|
|
WORD ret;
|
|
WOWINFO wowinfo;
|
|
char aszWOWDEB[CCHMAX];
|
|
LPSTR pchWOWDEB;
|
|
HANDLE hMMDriver;
|
|
char szLoad[CCHMAX];
|
|
int iPastSystem32Pos;
|
|
char szBuffer[150];
|
|
BOOL bFinished;
|
|
int iStart;
|
|
int iEnd;
|
|
|
|
#define PATH_32_WHACK "32\\"
|
|
|
|
hAppInstance = hInstance ;
|
|
|
|
// Only Want One WOWExec
|
|
if (hPrevInstance != NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
if (!InitializeApp(lpszCmdLine)) {
|
|
OutputDebugString("WOWEXEC: InitializeApp failure!\n");
|
|
return 0;
|
|
}
|
|
|
|
iPastSystem32Pos = GetSystemDirectory((LPSTR)&szLoad, sizeof(szLoad));
|
|
|
|
//
|
|
// Make sure to have room for 32\filename.ext\0
|
|
//
|
|
if (iPastSystem32Pos == 0 || iPastSystem32Pos >= CCHMAX-1-3-1-8-1-3-1) {
|
|
OutputDebugString("WOWEXEC: Bad system32 directory!\n");
|
|
return 0;
|
|
}
|
|
|
|
lstrcpyn( &(szLoad[iPastSystem32Pos]), PATH_32_WHACK, sizeof(PATH_32_WHACK));
|
|
iPastSystem32Pos += sizeof(PATH_32_WHACK)-1;
|
|
|
|
/*
|
|
* Look for a drivers= line in the [boot] section of SYSTEM.INI
|
|
* If present it is the 16 bit MultiMedia interface, so load it
|
|
*/
|
|
|
|
/* Load DDL's from DRIVERS section in system.ini
|
|
*/
|
|
GetPrivateProfileString( (LPSTR)"boot", /* [Boot] section */
|
|
(LPSTR)"drivers", /* Drivers= */
|
|
(LPSTR)"", /* Default if no match */
|
|
szBuffer, /* Return buffer */
|
|
sizeof(szBuffer),
|
|
(LPSTR)"system.ini" );
|
|
|
|
if (!*szBuffer) {
|
|
goto Done;
|
|
}
|
|
|
|
bFinished = FALSE;
|
|
iStart = 0;
|
|
|
|
while (!bFinished) {
|
|
iEnd = iStart;
|
|
|
|
while (szBuffer[iEnd] && (szBuffer[iEnd] != ' ') &&
|
|
(szBuffer[iEnd] != ',')) {
|
|
iEnd++;
|
|
}
|
|
|
|
if (szBuffer[iEnd] == NULL) {
|
|
bFinished = TRUE;
|
|
}
|
|
else {
|
|
szBuffer[iEnd] = NULL;
|
|
}
|
|
|
|
/* Load and enable the driver.
|
|
*/
|
|
OpenDriver( &(szBuffer[iStart]), NULL, NULL );
|
|
iStart = iEnd + 1;
|
|
}
|
|
|
|
Done:
|
|
|
|
/*
|
|
* Look for a debug= line in the [boot] section of SYSTEM.INI
|
|
* If present it is the 16 bit MultiMedia interface, so load it
|
|
*/
|
|
|
|
if ( !gfInjectedWOW && (WOWQueryDebug() & 0x0001)!=0 ) {
|
|
pchWOWDEB = "WOWDEB.EXE";
|
|
} else {
|
|
pchWOWDEB = "";
|
|
}
|
|
|
|
GetPrivateProfileString((LPSTR)"boot", (LPSTR)"debug",pchWOWDEB, aszWOWDEB, sizeof(aszWOWDEB), (LPSTR)"SYSTEM.INI");
|
|
aszWOWDEB[sizeof(aszWOWDEB)-1] = '\0';
|
|
|
|
if ( lstrlen(pchWOWDEB) != 0 ) {
|
|
if (lstrcmp(pchWOWDEB, aszWOWDEB) == 0) {
|
|
lstrcpyn(&(szLoad[iPastSystem32Pos]), pchWOWDEB, sizeof("WOWDEB.EXE"));
|
|
WinExec((LPSTR)szLoad,SW_SHOW);
|
|
} else {
|
|
WinExec((LPSTR)aszWOWDEB,SW_SHOW);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/* Preload winspool.exe. Apps will keep loading and freeing it
|
|
* which is slow. We might as well load it now so the reference
|
|
* count is 1 so it will never be loaded or freed
|
|
*/
|
|
//
|
|
// Disabled load of winspool.exe to save 8k. Size vs. speed,
|
|
// which one do we care about? Right now, size!
|
|
//
|
|
LoadLibrary("WINSPOOL.EXE");
|
|
#endif
|
|
|
|
// Always load SHELL.DLL, FileMaker Pro and Lotus Install require it.
|
|
|
|
lstrcpyn(&(szLoad[iPastSystem32Pos]), "SHELL.DLL", sizeof("SHELL.DLL"));
|
|
LoadLibrary(szLoad);
|
|
|
|
//
|
|
// Start any apps pending in basesrv queue
|
|
//
|
|
|
|
while (StartRequestedApp() && gfSharedWOW) {
|
|
/* null stmt */ ;
|
|
}
|
|
|
|
|
|
while (1) {
|
|
|
|
WowWaitForMsgAndEvent(ghwndMain);
|
|
|
|
//
|
|
// Always check for messages
|
|
//
|
|
|
|
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) &&
|
|
msg.message != WM_WOWEXECHEARTBEAT )
|
|
{
|
|
if (msg.message != WM_QUIT) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* InitializeApp
|
|
*
|
|
* History:
|
|
* 04-13-91 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
BOOL InitializeApp(LPSTR lpszCommandLine)
|
|
{
|
|
WNDCLASS wc;
|
|
int cyExecStart, cxExecStart;
|
|
USHORT TitleLen, cbCopy;
|
|
HWND hwndFax;
|
|
int lResult;
|
|
|
|
|
|
// Remove Real Mode Segment Address
|
|
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = WndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = hAppInstance;
|
|
wc.hIcon = LoadIcon(hAppInstance, MAKEINTRESOURCE(ID_WOWEXEC_ICON));
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
|
|
wc.lpszClassName = "WOWExecClass";
|
|
#ifdef DEBUG
|
|
wc.lpszMenuName = "MainMenu";
|
|
#else
|
|
wc.lpszMenuName = NULL;
|
|
#endif
|
|
|
|
if (!RegisterClass(&wc)) {
|
|
OutputDebugString("WOWEXEC: RegisterClass failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Load these strings now. If we need them later, we won't be able to load
|
|
* them at that time.
|
|
*/
|
|
LoadString(hAppInstance, IDS_OOMEXITTITLE, szOOMExitTitle, sizeof(szOOMExitTitle));
|
|
LoadString(hAppInstance, IDS_OOMEXITMSG, szOOMExitMsg, sizeof(szOOMExitMsg));
|
|
LoadString(hAppInstance, IDS_APPTITLE, szAppTitleBuffer, sizeof(szAppTitleBuffer));
|
|
|
|
ghwndMain = CreateWindow("WOWExecClass", lpszAppTitle,
|
|
WS_OVERLAPPED | WS_CAPTION | WS_BORDER | WS_THICKFRAME |
|
|
WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN |
|
|
WS_SYSMENU,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
NULL, NULL, hAppInstance, NULL);
|
|
|
|
if (ghwndMain == NULL ) {
|
|
#ifdef DEBUG
|
|
OutputDebugString("WOWEXEC: ghwndMain Null\n");
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
hwndFax = FaxInit(hAppInstance);
|
|
|
|
//
|
|
// Give our window handle to BaseSrv, which will post WM_WOWEXECSTARTAPP
|
|
// messages when we have commands to pick up. The return value tells
|
|
// us if we are the shared WOW VDM or not (a seperate WOW VDM).
|
|
// We also pick up the ShowWindow parameter (SW_SHOW, SW_MINIMIZED, etc)
|
|
// for the first WOW app here. Subsequent ones we get from BaseSrv.
|
|
//
|
|
|
|
//
|
|
// gwFirstCmdShow is no longer used, and is available.
|
|
//
|
|
|
|
lResult = WOWRegisterShellWindowHandle(ghwndMain,
|
|
&gwFirstCmdShow,
|
|
hwndFax
|
|
);
|
|
|
|
if (lResult < 0) {
|
|
gfInjectedWOW=TRUE;
|
|
} else if (lResult > 0) {
|
|
gfSharedWOW=TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If this isn't the shared WOW, tell the kernel to exit when the
|
|
// last app (except WowExec) exits.
|
|
//
|
|
|
|
if (!gfSharedWOW) {
|
|
WowSetExitOnLastApp(TRUE);
|
|
}
|
|
|
|
/* Remember the original directory. */
|
|
GetCurrentDirectory(NULL, szOriginalDirectory);
|
|
GetWindowsDirectory(szWindowsDirectory, MAXITEMPATHLEN+1);
|
|
|
|
#ifdef DEBUG
|
|
|
|
ShowWindow(ghwndMain, SW_MINIMIZE);
|
|
|
|
//
|
|
// If this is the shared WOW, change the app title string to
|
|
// reflect this and change the window title.
|
|
//
|
|
|
|
if (gfSharedWOW) {
|
|
|
|
LoadString(hAppInstance, IDS_SHAREDAPPTITLE, szAppTitleBuffer, sizeof(szAppTitleBuffer));
|
|
|
|
}
|
|
|
|
SetWindowText(ghwndMain, lpszAppTitle);
|
|
UpdateWindow(ghwndMain);
|
|
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* WndProc
|
|
*
|
|
* History:
|
|
* 04-07-91 DarrinM Created.
|
|
\***************************************************************************/
|
|
|
|
LONG FAR PASCAL WndProc(
|
|
HWND hwnd,
|
|
WORD message,
|
|
WORD wParam,
|
|
LONG lParam)
|
|
{
|
|
char chbuf[50];
|
|
HICON hIcon;
|
|
|
|
switch (message) {
|
|
case WM_CREATE:
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
// ignore since wowexec must stay around
|
|
return 0;
|
|
|
|
#ifdef DEBUG
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case MM_ABOUT:
|
|
LoadString(hAppInstance, errTitle, (LPSTR)chbuf, sizeof(chbuf));
|
|
hIcon = LoadIcon(hAppInstance, MAKEINTRESOURCE(ID_WOWEXEC_ICON));
|
|
ShellAbout(ghwndMain, (LPSTR)chbuf, (LPSTR)lpszAppTitle, hIcon);
|
|
break;
|
|
|
|
case MM_BREAK:
|
|
_asm int 3
|
|
break;
|
|
|
|
case MM_FAULT:
|
|
_asm mov cs:0,ax
|
|
break;
|
|
|
|
case MM_EXIT:
|
|
ExitKernelThunk(0);
|
|
break;
|
|
|
|
case MM_WATSON:
|
|
WinExec("drwatson", SW_MINIMIZE );
|
|
break;
|
|
|
|
case MM_PARTY:
|
|
{
|
|
FARPROC lpPartyDialogProc;
|
|
|
|
lpPartyDialogProc = MakeProcInstance(PartyDialogProc, hAppInstance);
|
|
|
|
DialogBox(hAppInstance, MAKEINTRESOURCE(ID_PARTY_DIALOG),
|
|
hwnd, lpPartyDialogProc);
|
|
|
|
FreeProcInstance(lpPartyDialogProc);
|
|
}
|
|
break;
|
|
|
|
case MM_GENTHUNK:
|
|
{
|
|
DWORD FAR PASCAL CallProc32W(DWORD, DWORD, DWORD, DWORD,
|
|
DWORD, DWORD, DWORD, DWORD,
|
|
DWORD, DWORD, DWORD, DWORD,
|
|
DWORD, DWORD, DWORD, DWORD,
|
|
DWORD, DWORD, DWORD, DWORD,
|
|
DWORD, DWORD, DWORD, DWORD,
|
|
DWORD, DWORD, DWORD, DWORD,
|
|
DWORD, DWORD, DWORD, DWORD,
|
|
DWORD, DWORD, DWORD
|
|
);
|
|
|
|
#define BIT(bitpos) ((DWORD)1 << bitpos)
|
|
|
|
DWORD hmodKernel32, hmodUser32, hmodWow32;
|
|
DWORD pfn32;
|
|
DWORD dw16, dw32;
|
|
DWORD p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
|
|
p11, p12, p13, p14, p15, p16, p17, p18, p19, p20,
|
|
p21, p22, p23, p24, p25, p26, p27, p28, p29, p30,
|
|
p31, p32;
|
|
char szBuf16[1024], szBuf32[1024];
|
|
char *pszErr;
|
|
|
|
hmodKernel32 = LoadLibraryEx32W("kernel32", 0, 0);
|
|
hmodUser32 = LoadLibraryEx32W("user32", 0, 0);
|
|
hmodWow32 = LoadLibraryEx32W("wow32", 0, 0);
|
|
|
|
|
|
//
|
|
// Simple printf test.
|
|
//
|
|
|
|
pfn32 = GetProcAddress32W(hmodUser32, "wsprintfA");
|
|
|
|
dw16 = wsprintf(szBuf16, "simple printf %ld", 12345678);
|
|
|
|
dw32 = CallProc32W( (DWORD)(LPSTR) szBuf32,
|
|
(DWORD)(LPSTR) "simple printf %ld",
|
|
12345678,
|
|
0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
pfn32,
|
|
BIT(30) | BIT(31),
|
|
CPEX_DEST_CDECL | 32
|
|
);
|
|
|
|
if (dw16 != dw32 || lstrcmp(szBuf16, szBuf32)) {
|
|
pszErr = "simple printf comparison failed";
|
|
goto ErrorMsg;
|
|
}
|
|
|
|
MessageBox(hwnd, "s1 success", "Genthunk Sanity Test", MB_OK);
|
|
|
|
|
|
dw32 = CallProcEx32W( CPEX_DEST_CDECL | 3,
|
|
BIT(0) | BIT(1),
|
|
pfn32,
|
|
(DWORD)(LPSTR) szBuf32,
|
|
(DWORD)(LPSTR) "simple printf %ld",
|
|
12345678 );
|
|
|
|
if (dw16 != dw32 || lstrcmp(szBuf16, szBuf32)) {
|
|
pszErr = "simple printf comparison failed (CallProcEx)";
|
|
goto ErrorMsg;
|
|
}
|
|
|
|
MessageBox(hwnd, "s2 success", "Genthunk Sanity Test", MB_OK);
|
|
|
|
//
|
|
// Complex printf test.
|
|
//
|
|
|
|
// pfn32 still points to wsprintfA
|
|
// pfn32 = GetProcAddress32W(hmodUser32, "wsprintfA");
|
|
|
|
|
|
#if 0 // this blows out Win16 wsprintf!
|
|
dw16 = wsprintf(szBuf16,
|
|
"complex printf "
|
|
"%ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s "
|
|
"%ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s ",
|
|
12345678,
|
|
0x87654321,
|
|
"str",
|
|
12345678,
|
|
0x87654321,
|
|
"str",
|
|
12345678,
|
|
0x87654321,
|
|
"str",
|
|
12345678,
|
|
0x87654321,
|
|
"str",
|
|
12345678,
|
|
0x87654321,
|
|
"str",
|
|
12345678,
|
|
0x87654321,
|
|
"str",
|
|
12345678,
|
|
0x87654321,
|
|
"str",
|
|
12345678,
|
|
0x87654321,
|
|
"str",
|
|
12345678,
|
|
0x87654321,
|
|
"str",
|
|
12345678,
|
|
0x87654321,
|
|
"str"
|
|
);
|
|
#else
|
|
lstrcpy(szBuf16, "complex printf "
|
|
"12345678 87654321 str "
|
|
"12345678 87654321 str "
|
|
"12345678 87654321 str "
|
|
"12345678 87654321 str "
|
|
"12345678 87654321 str "
|
|
"12345678 87654321 str "
|
|
"12345678 87654321 str "
|
|
"12345678 87654321 str "
|
|
"12345678 87654321 str "
|
|
"12345678 87654321 str "
|
|
);
|
|
dw16 = lstrlen(szBuf16);
|
|
#endif
|
|
|
|
dw32 = CallProc32W(
|
|
(DWORD)(LPSTR) szBuf32,
|
|
(DWORD)(LPSTR) "complex printf "
|
|
"%ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s "
|
|
"%ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s ",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
pfn32,
|
|
BIT(0) | BIT(3) | BIT(6) | BIT(9) | BIT(12) | BIT(15) |
|
|
BIT(18) | BIT(21) | BIT(24) | BIT(27) | BIT(30) | BIT(31),
|
|
CPEX_DEST_CDECL | 32
|
|
);
|
|
|
|
if (dw16 != dw32 || lstrcmp(szBuf16, szBuf32)) {
|
|
pszErr = "complex printf comparison failed";
|
|
goto ErrorMsg;
|
|
}
|
|
|
|
MessageBox(hwnd, "c1 success", "Genthunk Sanity Test", MB_OK);
|
|
|
|
dw32 = CallProcEx32W( CPEX_DEST_CDECL | 32,
|
|
BIT(0) | BIT(1) | BIT(4) | BIT(7) | BIT(10) | BIT(13) |
|
|
BIT(16) | BIT(19) | BIT(22) | BIT(25) | BIT(28) | BIT(31),
|
|
pfn32,
|
|
(DWORD)(LPSTR) szBuf32,
|
|
(DWORD)(LPSTR) "complex printf "
|
|
"%ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s "
|
|
"%ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s %ld %lx %s ",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str",
|
|
12345678,
|
|
0x87654321,
|
|
(DWORD)(LPSTR) "str"
|
|
);
|
|
|
|
|
|
if (dw16 != dw32 || lstrcmp(szBuf16, szBuf32)) {
|
|
pszErr = "complex printf comparison failed (CallProcEx)";
|
|
goto ErrorMsg;
|
|
}
|
|
|
|
MessageBox(hwnd, "c2 success", "Genthunk Sanity Test", MB_OK);
|
|
|
|
//
|
|
// Simple WINAPI test (GetProcAddress of LoadModule)
|
|
//
|
|
|
|
pfn32 = GetProcAddress32W(hmodKernel32, "GetProcAddress");
|
|
|
|
dw16 = GetProcAddress32W(hmodKernel32, "LoadModule");
|
|
|
|
dw32 = CallProc32W( hmodKernel32,
|
|
(DWORD)(LPSTR) "LoadModule",
|
|
0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
pfn32,
|
|
BIT(30),
|
|
CPEX_DEST_STDCALL | 32
|
|
);
|
|
|
|
if (dw16 != dw32) {
|
|
pszErr = "GetProcAddress comparison failed";
|
|
goto ErrorMsg;
|
|
}
|
|
|
|
MessageBox(hwnd, "w1 success", "Genthunk Sanity Test", MB_OK);
|
|
|
|
dw32 = CallProcEx32W( CPEX_DEST_STDCALL | 2,
|
|
BIT(1),
|
|
pfn32,
|
|
hmodKernel32,
|
|
(DWORD)(LPSTR) "LoadModule" );
|
|
|
|
wsprintf(szBuf16, "GPA via CP32Ex(LoadModule) == %lx\n", dw32);
|
|
OutputDebugString(szBuf16);
|
|
if (dw16 != dw32) {
|
|
pszErr = "GetProcAddress comparison failed (CallProcEx)";
|
|
goto ErrorMsg;
|
|
}
|
|
|
|
MessageBox(hwnd, "w2 success", "Genthunk Sanity Test", MB_OK);
|
|
|
|
//
|
|
// Complex WINAPI test WOWStdCall32ArgsTestTarget exists only on
|
|
// checked WOW32.dll
|
|
//
|
|
|
|
pfn32 = GetProcAddress32W(hmodWow32, "WOWStdCall32ArgsTestTarget");
|
|
|
|
if (!pfn32) {
|
|
MessageBox(hwnd,
|
|
"WOWStdCall32ArgsTestTarget not found, use checked wow32.dll for this test.",
|
|
"Genthunk Quicktest",
|
|
MB_OK
|
|
);
|
|
goto Done;
|
|
}
|
|
|
|
p1 = 1;
|
|
p2 = 2;
|
|
p3 = 3;
|
|
p4 = 4;
|
|
p5 = 5;
|
|
p6 = 6;
|
|
p7 = 7;
|
|
p8 = 8;
|
|
p9 = 9;
|
|
p10 = 10;
|
|
p11 = 11;
|
|
p12 = 12;
|
|
p13 = 13;
|
|
p14 = 14;
|
|
p15 = 15;
|
|
p16 = 16;
|
|
p17 = 17;
|
|
p18 = 18;
|
|
p19 = 19;
|
|
p20 = 10;
|
|
p21 = 21;
|
|
p22 = 22;
|
|
p23 = 23;
|
|
p24 = 24;
|
|
p25 = 25;
|
|
p26 = 26;
|
|
p27 = 27;
|
|
p28 = 28;
|
|
p29 = 29;
|
|
p30 = 30;
|
|
p31 = 31;
|
|
p32 = 32;
|
|
|
|
dw16 = ((((p1+p2+p3+p4+p5+p6+p7+p8+p9+p10) -
|
|
(p11+p12+p13+p14+p15+p16+p17+p18+p19+p20)) << p21) +
|
|
((p22+p23+p24+p25+p26) - (p27+p28+p29+p30+p31+p32)));
|
|
|
|
dw32 = CallProc32W( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
|
|
p11, p12, p13, p14, p15, p16, p17, p18, p19, p20,
|
|
p21, p22,
|
|
(DWORD)(LPDWORD) &p23,
|
|
p24, p25, p26, p27, p28, p29, p30,
|
|
p31,
|
|
(DWORD)(LPDWORD) &p32,
|
|
pfn32,
|
|
BIT(9) | BIT(0),
|
|
CPEX_DEST_STDCALL | 32
|
|
);
|
|
|
|
if (dw16 != dw32) {
|
|
pszErr = "WOWStdCall32ArgsTestTarget comparison failed";
|
|
goto ErrorMsg;
|
|
}
|
|
|
|
MessageBox(hwnd, "cw1 success", "Genthunk Sanity Test", MB_OK);
|
|
|
|
dw32 = CallProcEx32W( CPEX_DEST_STDCALL | 32,
|
|
BIT(22) | BIT(31),
|
|
pfn32,
|
|
p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
|
|
p11, p12, p13, p14, p15, p16, p17, p18, p19, p20,
|
|
p21, p22,
|
|
(DWORD)(LPDWORD) &p23,
|
|
p24, p25, p26, p27, p28, p29, p30,
|
|
p31,
|
|
(DWORD)(LPDWORD) &p32
|
|
);
|
|
|
|
if (dw16 != dw32) {
|
|
pszErr = "WOWStdCall32ArgsTestTarget comparison failed (CallProcEx)";
|
|
goto ErrorMsg;
|
|
|
|
ErrorMsg:
|
|
MessageBox(hwnd, pszErr, "Genthunk Sanity Test Failure", MB_OK);
|
|
goto Done;
|
|
}
|
|
|
|
wsprintf(szBuf16, "result of odd calc is %lx\n", dw32);
|
|
OutputDebugString(szBuf16);
|
|
|
|
MessageBox(hwnd, "Test successful!", "Genthunk Quicktest", MB_OK);
|
|
|
|
Done:
|
|
FreeLibrary32W(hmodKernel32);
|
|
FreeLibrary32W(hmodUser32);
|
|
FreeLibrary32W(hmodWow32);
|
|
}
|
|
break;
|
|
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case WM_WOWEXECSTARTAPP: // WM_USER+0
|
|
|
|
#ifdef DEBUG
|
|
OutputDebugString("WOWEXEC - got WM_WOWEXECSTARTAPP\n");
|
|
#endif
|
|
|
|
//
|
|
// Either BaseSrv or Wow32 asked us to go looking for
|
|
// commands to run.
|
|
//
|
|
|
|
if (!gfSharedWOW) {
|
|
|
|
//
|
|
// We shouldn't get this message unless we are the shared
|
|
// WOW VDM!
|
|
//
|
|
|
|
#ifdef DEBUG
|
|
OutputDebugString("WOWEXEC - separate WOW VDM got WM_WOWEXECSTARTAPP!\n");
|
|
_asm int 3;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Start requested apps until there are no more to start.
|
|
// This handles the case where several Win16 apps are launched
|
|
// in a row, before BaseSrv has the window handle for WowExec.
|
|
//
|
|
|
|
while (StartRequestedApp()) {
|
|
/* Null stmt */ ;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_WOWEXEC_START_TASK:
|
|
{
|
|
char sz[512];
|
|
|
|
sz[ GlobalGetAtomName(wParam, sz, sizeof sz) ] = 0;
|
|
GlobalDeleteAtom(wParam);
|
|
WinExec(sz, (WORD)lParam);
|
|
}
|
|
|
|
case WM_WOWEXECHEARTBEAT:
|
|
// Probably will never get here...
|
|
break;
|
|
|
|
case WM_WOWEXECSTARTTIMER:
|
|
WowShutdownTimer(1);
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
if (wParam == 1) { // timer ID
|
|
|
|
KillTimer(ghwndMain, 1);
|
|
|
|
//
|
|
// The shutdown timer has expired, meaning it's time to kill this
|
|
// shared WOW VDM. First we need to let basesrv know not to queue
|
|
// any more apps for us to start.
|
|
//
|
|
|
|
if (WOWRegisterShellWindowHandle(NULL,
|
|
&gwFirstCmdShow,
|
|
NULL
|
|
)) {
|
|
|
|
//
|
|
// BaseSrv deregistered us successfully after confirming
|
|
// there are no pending commands for us.
|
|
//
|
|
|
|
ExitKernelThunk(0);
|
|
} else {
|
|
|
|
//
|
|
// There must be pending commands for us. Might as well
|
|
// start them.
|
|
//
|
|
|
|
PostMessage(ghwndMain, WM_WOWEXECSTARTAPP, 0, 0);
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_TIMECHANGE:
|
|
*((DWORD *)(((DWORD)40 << 16) | FIXED_NTVDMSTATE_REL40))
|
|
|= VDM_TIMECHANGE;
|
|
break;
|
|
|
|
case WM_DDE_INITIATE:
|
|
{
|
|
// In win31, the Program Manager WindowProc calls peekmessage to filterout
|
|
// otherwindowcreated and otherwindowdestroyed messages (which are atoms in win31)
|
|
// whenever it receives WM_DDE_INITIATE message.
|
|
//
|
|
// This has a side effect - basically when peekmessage is called the app ie program
|
|
// manager effectively yields allowing scheduling of other apps.
|
|
//
|
|
// So we do the side effect thing (simulate win31 behaviour) -
|
|
//
|
|
// The bug: 20221, rumba as/400 can't connect the firsttime to sna server.
|
|
// Scenario: Rumbawsf execs snasrv and blocks on yield, snasrv execs wnap and blocks
|
|
// on yield. Eventually wnap yields and rumbawsf is scheduled which
|
|
// broadcasts a ddeinitiate message. When WOWEXEC receives this message
|
|
// it will peek for nonexistent msg, which allows snasrv to get scheduled
|
|
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL, 0xFFFF, 0xFFFF, PM_REMOVE))
|
|
DispatchMessage(&msg);
|
|
}
|
|
break;
|
|
|
|
|
|
|
|
case WM_CLOSE:
|
|
#ifdef DEBUG
|
|
ExitKernelThunk(0);
|
|
#else
|
|
// ignore since wowexec must stay around
|
|
return 0;
|
|
#endif // ! DEBUG
|
|
|
|
|
|
default:
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
BOOL FAR PASCAL PartyDialogProc(HWND hDlg, WORD msg, WORD wParam, LONG lParam)
|
|
{
|
|
#ifdef DEBUG
|
|
BOOL f;
|
|
DWORD dw;
|
|
char szBuf[255];
|
|
|
|
switch (msg) {
|
|
|
|
case WM_INITDIALOG:
|
|
SendDlgItemMessage(hDlg, IDD_PARTY_NUMBER, EM_LIMITTEXT, 5, 0L);
|
|
SendDlgItemMessage(hDlg, IDD_PARTY_STRING, EM_LIMITTEXT, sizeof(szBuf)-1, 0L);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (wParam) {
|
|
|
|
case 0xdab /* IDCANCEL */:
|
|
EndDialog(hDlg, FALSE);
|
|
break;
|
|
|
|
case 0xdad /* IDOK */:
|
|
dw = GetDlgItemInt(hDlg, IDD_PARTY_NUMBER, &f, FALSE);
|
|
GetDlgItemText(hDlg, IDD_PARTY_STRING, szBuf, sizeof(szBuf));
|
|
WowPartyByNumber(dw, szBuf);
|
|
EndDialog(hDlg, TRUE);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Misc File Routines - taken from progman (pmdlg.c) mattfe apr-1 92
|
|
|
|
//-------------------------------------------------------------------------
|
|
PSTR FAR PASCAL GetFilenameFromPath
|
|
// Given a full path returns a ptr to the filename bit. Unless it's a UNC style
|
|
// path in which case
|
|
(
|
|
PSTR szPath
|
|
)
|
|
{
|
|
DWORD dummy;
|
|
PSTR pFileName;
|
|
BOOL fUNC;
|
|
|
|
|
|
GetPathInfo(szPath, &pFileName, (PSTR*) &dummy, (WORD*) &dummy,
|
|
&fUNC);
|
|
|
|
// If it's a UNC then the 'filename' part is the whole thing.
|
|
if (fUNC)
|
|
pFileName = szPath;
|
|
|
|
return pFileName;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
void NEAR PASCAL GetPathInfo
|
|
// Get pointers and an index to specific bits of a path.
|
|
// Stops scaning at first space.
|
|
(
|
|
// Uses:
|
|
PSTR szPath, // The path.
|
|
|
|
// Returns:
|
|
PSTR *pszFileName, // The start of the filename in the path.
|
|
PSTR *pszExt, // Extension part of path (starting with the dot).
|
|
WORD *pich, // Index (in DBCS characters) of filename part starting at 0.
|
|
BOOL *pfUnc // Contents set to true if it's a UNC style path.
|
|
)
|
|
{
|
|
char *pch; // Temp variable.
|
|
WORD ich = 0; // Temp.
|
|
|
|
*pszExt = NULL; // If no extension, return NULL.
|
|
*pszFileName = szPath; // If no seperate filename component, return path.
|
|
*pich = 0;
|
|
*pfUnc = FALSE; // Default to not UNC style.
|
|
|
|
// Check for UNC style paths.
|
|
if (*szPath == '\\' && *(szPath+1) == '\\')
|
|
*pfUnc = TRUE;
|
|
|
|
// Search forward to find the last backslash or colon in the path.
|
|
// While we're at it, look for the last dot.
|
|
for (pch = szPath; *pch; pch = AnsiNext(pch))
|
|
{
|
|
if (*pch == ' ')
|
|
{
|
|
// Found a space - stop here.
|
|
break;
|
|
}
|
|
if (*pch == '\\' || *pch == ':')
|
|
{
|
|
// Found it, record ptr to it and it's index.
|
|
*pszFileName = pch+1;
|
|
*pich = ich+1;
|
|
}
|
|
if (*pch == '.')
|
|
{
|
|
// Found a dot.
|
|
*pszExt = pch;
|
|
}
|
|
ich++;
|
|
}
|
|
|
|
// Check that the last dot is part of the last filename.
|
|
if (*pszExt < *pszFileName)
|
|
*pszExt = NULL;
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// StartRequestedApp
|
|
// Calls WIN32 Base GetNextVDMCommand
|
|
// and then starts the application
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#define LargeEnvSize() 0x1000 // Size of a large env
|
|
|
|
BOOL NEAR PASCAL StartRequestedApp(VOID)
|
|
{
|
|
char achCmdLine[CCHMAX];
|
|
char achAppName[CCHMAX];
|
|
#ifdef DEBUG
|
|
char achAppNamePlusCmdLine[sizeof(achCmdLine) + sizeof(achAppName)];
|
|
int iGetNextVdmCmdLoops = 0;
|
|
#endif
|
|
char achCurDir[CCHMAX];
|
|
WOWINFO wowinfo;
|
|
BOOL b;
|
|
HANDLE hmemEnvironment;
|
|
USHORT usEnvSize;
|
|
|
|
achCmdLine[0] = '\0';
|
|
achAppName[0] = '\0';
|
|
|
|
// We start by assuming that the app will have an environment that will
|
|
// be less than a large environment size. If not then
|
|
// WowGetNextVdmCommand will fail and we will try again with the
|
|
// enviroment size needed for the app as returned from the failed
|
|
// WowGetNextVdmCommand call. If we detect that WowGetNextVdmCommand fails
|
|
// but that we have plenty of environment space then we know another
|
|
// problem has occured and we give up.
|
|
|
|
// We don't worry about wasting memory since the environment will be
|
|
// merged into another buffer and this buffer will be freed below.
|
|
|
|
wowinfo.EnvSize = LargeEnvSize();
|
|
hmemEnvironment = NULL;
|
|
|
|
do {
|
|
if (hmemEnvironment != NULL) {
|
|
GlobalUnlock(hmemEnvironment);
|
|
GlobalFree(hmemEnvironment);
|
|
}
|
|
|
|
// We need to allocate twice the space specified so that international
|
|
// character set conversions can be successful.
|
|
hmemEnvironment = GlobalAlloc(GMEM_MOVEABLE, 2*wowinfo.EnvSize);
|
|
if (hmemEnvironment == NULL) {
|
|
#ifdef DEBUG
|
|
OutputDebugString("WOWEXEC - failed to allocate Environment Memory\n");
|
|
#endif
|
|
MyMessageBox(IDS_EXECERRTITLE, IDS_NOMEMORYMSG, NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
wowinfo.lpEnv = GlobalLock(hmemEnvironment);
|
|
#ifdef DEBUG
|
|
if (wowinfo.lpEnv == NULL) {
|
|
OutputDebugString("WOWEXEC ASSERT - GlobalLock failed, fix this!\n");
|
|
_asm { int 3 };
|
|
}
|
|
|
|
if (2*wowinfo.EnvSize > GlobalSize(hmemEnvironment)) {
|
|
OutputDebugString("WOWEXEC ASSERT - alloced memory too small, fix this!\n");
|
|
_asm { int 3 };
|
|
}
|
|
#endif
|
|
wowinfo.lpCmdLine = achCmdLine;
|
|
wowinfo.CmdLineSize = CCHMAX;
|
|
wowinfo.lpAppName = achAppName;
|
|
wowinfo.AppNameSize = CCHMAX;
|
|
wowinfo.CurDrive = 0;
|
|
wowinfo.lpCurDir = achCurDir;
|
|
wowinfo.CurDirSize = sizeof(achCurDir);
|
|
wowinfo.iTask = 0;
|
|
|
|
usEnvSize = wowinfo.EnvSize;
|
|
|
|
#ifdef DEBUG
|
|
if (++iGetNextVdmCmdLoops == 4) {
|
|
OutputDebugString("WOWEXEC ASSERT - Too many calls to GetNextVdmCommand\n");
|
|
_asm { int 3 };
|
|
}
|
|
#endif
|
|
} while (! (b = WowGetNextVdmCommand(&wowinfo)) &&
|
|
(wowinfo.EnvSize > usEnvSize));
|
|
|
|
if ( ! b ) {
|
|
#ifdef DEBUG
|
|
OutputDebugString("WOWEXEC - GetNextVdmCommand failed.\n");
|
|
#endif
|
|
MyMessageBox(IDS_EXECERRTITLE, IDS_NOMEMORYMSG, achCmdLine);
|
|
GlobalUnlock( hmemEnvironment );
|
|
GlobalFree( hmemEnvironment );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If CmdLineSize == 0, no more commands (wowexec was the command)
|
|
// see WK32WowGetNextVdm
|
|
//
|
|
if (! wowinfo.CmdLineSize) {
|
|
GlobalUnlock( hmemEnvironment );
|
|
GlobalFree( hmemEnvironment );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
lstrcpy(achAppNamePlusCmdLine, achAppName);
|
|
lstrcat(achAppNamePlusCmdLine, ":");
|
|
lstrcat(achAppNamePlusCmdLine, achCmdLine);
|
|
// Chop off trailing CRLF from command tail.
|
|
achAppNamePlusCmdLine[ lstrlen(achAppNamePlusCmdLine) - 2 ] = '\0';
|
|
|
|
OutputDebugString("WOWEXEC: CommandLine = <");
|
|
OutputDebugString(achAppNamePlusCmdLine);
|
|
OutputDebugString(">\n");
|
|
|
|
SetWindowText(ghwndMain, achAppNamePlusCmdLine);
|
|
UpdateWindow(ghwndMain);
|
|
#endif
|
|
|
|
ExecApplication(&wowinfo);
|
|
|
|
#ifdef DEBUG
|
|
|
|
if ( ! gfSharedWOW ) {
|
|
|
|
//
|
|
// If this is a separate WOW, we have just executed the one and only
|
|
// app we're going to spawn. Change our window title to
|
|
// Command Line - WOWExec so that it's easy to see which WOW this
|
|
// window is associated with. No need to worry about freeing
|
|
// this memory, since when we go away the VDM goes away and
|
|
// vice-versa.
|
|
//
|
|
|
|
lpszAppTitle = GlobalLock(
|
|
GlobalAlloc(GMEM_FIXED,
|
|
lstrlen(achAppNamePlusCmdLine) +
|
|
3 + // for " - "
|
|
lstrlen(szAppTitleBuffer) +
|
|
1 // for null terminator
|
|
));
|
|
|
|
lstrcpy(lpszAppTitle, achAppNamePlusCmdLine);
|
|
lstrcat(lpszAppTitle, " - ");
|
|
lstrcat(lpszAppTitle, szAppTitleBuffer);
|
|
}
|
|
|
|
|
|
SetWindowText(ghwndMain, lpszAppTitle);
|
|
UpdateWindow(ghwndMain);
|
|
#endif
|
|
|
|
GlobalUnlock(hmemEnvironment);
|
|
GlobalFree(hmemEnvironment);
|
|
|
|
return TRUE; // We ran an app.
|
|
}
|
|
|
|
|