/****************************** 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); 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; 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 #ifndef DBCS #define AnsiNext(x) ((x)+1) #define AnsiPrev(y,x) ((x)-1) #define IsDBCSLeadByte(x) (FALSE) #endif typedef struct PARAMETERBLOCK { WORD wEnvSeg; LPSTR lpCmdLine; LPVOID lpCmdShow; DWORD dwReserved; } PARAMETERBLOCK, *PPARAMETERBLOCK; typedef struct CMDSHOW { WORD two; WORD nCmdShow; } CMDSHOW, *PCMDSHOW; #define CCHMAX 256+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 silly little enumproc to find any window (EnumWindows) which has a * matching EXE file path. The desired match EXE pathname is pointed to * by the lParam. The found window's handle is stored in the * first word of this buffer. */ BOOL CALLBACK FindPrevInstanceProc(HWND hWnd, LPSTR lpszParam) { char szT[260]; 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); // Check for errors. if (ret) { MyMessageBox(IDS_EXECERRTITLE, ret, pWowInfo->lpAppName); } // 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(); 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 szBuffer[150]; BOOL bFinished; int iStart; int iEnd; hAppInstance = hInstance ; // Only Want One WOWExec if (hPrevInstance != NULL) { return(FALSE); } if (!InitializeApp(lpszCmdLine)) { OutputDebugString("WOWEXEC: InitializeApp failure!\n"); return 0; } /* * Look for a drivers= line in the [boot] section of SYSTEM.INI * If present it is the 16 bit MultiMedia interface, so load it */ #ifdef OLD_MMSYSTEM_LOAD if (GetPrivateProfileString((LPSTR)"boot", (LPSTR)"drivers",(LPSTR)"", aszMMDriver, sizeof(aszMMDriver), (LPSTR)"SYSTEM.INI")) { /* * We have now discovered an app that rewrites the "drivers" entry with * multiple drivers - so the existing load method fails. As a temporary fix * while we decide what the "proper" fix is I will always load MMSYSTEM and * ONLY MMSYSTEM. * * aszMMDriver[sizeof(aszMMDriver)-1] = '\0'; * hMMDriver = LoadLibrary((LPSTR)aszMMDriver); * #ifdef DEBUG * if (hMMDriver < 32) { * OutputDebugString("WOWEXEC: Load of MultiMedia driver failed\n"); * } * #endif */ LoadLibrary("MMSYSTEM.DLL"); } #else /* 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: #endif /* * 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 ( WOWQueryDebug() & 0x0001 ) { 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 ) { 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. LoadLibrary("SHELL.DLL"); // // Start any apps pending in basesrv queue // while (StartRequestedApp() && gfSharedWOW) { /* null stmt */ ; } while (1) { if (!WowWaitForMsgAndEvent(ghwndMain) && 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; // 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; } /* * Guess size for now. */ cyExecStart = GetSystemMetrics(SM_CYMENU) * 6; cxExecStart = GetSystemMetrics(SM_CXSCREEN) / 2; /* 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, 30, 30, cxExecStart, cyExecStart, 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. // gfSharedWOW = WOWRegisterShellWindowHandle(ghwndMain, &gwFirstCmdShow, hwndFax ); // // 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; } 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; #define WM_WOWEXEC_START_TASK (WM_USER+2) 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_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. }