/*++ Module Name: forcedos.c Abstract: This program forces NT to treat and execute the given program as a DOS application. Author: William Hsieh - williamh 25-Jan-1993 Revision History: --*/ /* Some applications have Windows or OS/2 executable format while run these program under NT, users will get the following message: Please run this program under DOS. Since NT selects the subsystem for application based on application executable format. There is no way for NT to "run this program under DOS". This utility was provided for this purpose. We create a pif file for the application and then create a process for the pif. Since pif file always goes to NTVDM we got the chance to play game on the program. NTVDM will decode the pif file and dispatch the program to DOS. All the subsequent program exec from the first program will be forced to execute under DOS. */ #define UNICODE 1 #include #include #include #include #include #include #include #include #include "forcedos.h" WCHAR * Extention[MAX_EXTENTION]; WCHAR EXEExtention[] = L".EXE"; WCHAR COMExtention[] = L".COM"; WCHAR BATExtention[] = L".BAT"; WCHAR ProgramNameBuffer[MAX_PATH + 1]; WCHAR SearchPathName[MAX_PATH + 1]; WCHAR DefDirectory[MAX_PATH + 1]; char CommandLine[MAX_PATH + 1]; char ProgramName[MAX_PATH + 1]; WCHAR UnicodeMessage[MAX_MSG_LENGTH]; char OemMessage[MAX_MSG_LENGTH * 2]; #if DBG BOOL fOutputDebugInfo = FALSE; #endif void _cdecl main( int argc, char *argv[] ) { char * pCommandLine; char * pCurDirectory; char * pProgramName; char * p; BOOL fDisplayUsage; ULONG i, nChar, Length, CommandLineLength; PROCESS_INFORMATION ProcessInformation; DWORD ExitCode, dw; STARTUPINFO StartupInfo; PUNICODE_STRING pTebUnicodeString; NTSTATUS Status; OEM_STRING OemString, CmdLineString; UNICODE_STRING UnicodeString; WCHAR *pwch, *pFilePart; Extention[0] = COMExtention; Extention[1] = EXEExtention; Extention[2] = BATExtention; pCurDirectory = pProgramName = NULL; pCommandLine = CommandLine; CommandLineLength = 0; pTebUnicodeString = &NtCurrentTeb()->StaticUnicodeString; fDisplayUsage = TRUE; if ( argc > 1 ) { fDisplayUsage = FALSE; while (--argc != 0) { p = *++argv; if (pProgramName == NULL) { if (*p == '/' || *p == '-') { switch (*++p) { case '?': fDisplayUsage = TRUE; break; case 'D': case 'd': // if the directory follows the /D immediately // get it if (*++p != 0) { pCurDirectory = p; break; } else if (--argc > 1) // the next argument must be the curdirectory pCurDirectory = *++argv; else fDisplayUsage = TRUE; break; default: fDisplayUsage = TRUE; break; } } else { pProgramName = p; nChar = strlen(p); strncpy(CommandLine, pProgramName, nChar); pCommandLine = CommandLine + nChar; CommandLineLength = nChar + 1; } } else { // aggregate command line from all subsequent argvs nChar = strlen(p); if (CommandLineLength != 0) { strncpy(pCommandLine, " ", 1); pCommandLine++; } strncpy(pCommandLine, p, nChar); pCommandLine += nChar; CommandLineLength += nChar + 1; } if (fDisplayUsage) break; } if (pProgramName == NULL) fDisplayUsage = TRUE; } if ( fDisplayUsage) { OemString.Length = 0; OemString.MaximumLength = MAX_MSG_LENGTH << 1; OemString.Buffer = OemMessage; UnicodeString.Length = 0; UnicodeString.Buffer = UnicodeMessage; UnicodeString.MaximumLength = MAX_MSG_LENGTH << 1; for (i = ID_USAGE_BASE; i <= ID_USAGE_MAX; i++) { nChar = LoadString(NULL, i, UnicodeString.Buffer, UnicodeString.MaximumLength); UnicodeString.Length = (USHORT)(nChar << 1); Status = RtlUnicodeStringToOemString( &OemString, &UnicodeString, FALSE ); if (!NT_SUCCESS(Status)) break; if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), OemString.Buffer, OemString.Length, &Length, NULL) || Length != OemString.Length) break; } ExitProcess(0xFF); } if (pCurDirectory != NULL) { #if DBG if (fOutputDebugInfo) printf("Default directory = %s\n", pCurDirectory); #endif RtlInitString((PSTRING)&OemString, pCurDirectory); UnicodeString.MaximumLength = (MAX_PATH + 1) * sizeof(WCHAR); UnicodeString.Buffer = DefDirectory; UnicodeString.Length = 0; Status = RtlOemStringToUnicodeString(&UnicodeString, &OemString, FALSE); if (!NT_SUCCESS(Status)) YellAndExit(ID_BAD_DEFDIR, 0xFF); dw = GetFileAttributes(DefDirectory); if (dw == (DWORD)(-1) || !(dw & FILE_ATTRIBUTE_DIRECTORY)) YellAndExit(ID_BAD_DEFDIR, 0xFF); SetCurrentDirectory(DefDirectory); } else GetCurrentDirectory(MAX_PATH + 1, DefDirectory); // get a local copy of program name (for code conversion) strcpy(ProgramName, pProgramName); pProgramName = ProgramName; // when we feed SearchPath with an initial path name ".;%path%" // it will search the executable for use according to our requirement // Currentdir -> path SearchPathName[0] = L'.'; SearchPathName[1] = L';'; GetEnvironmentVariable(L"path", &SearchPathName[2], MAX_PATH + 1 - 2); RtlInitString((PSTRING)&OemString, pProgramName); Status = RtlOemStringToUnicodeString(pTebUnicodeString, &OemString, FALSE); if (!NT_SUCCESS(Status)) YellAndExit(ID_BAD_PATH, 0xFF); i = 0; nChar = 0; pwch = wcschr(pTebUnicodeString->Buffer, (WCHAR)'.'); Length = (pwch) ? 1 : MAX_EXTENTION; while (i < Length && (nChar = SearchPath( SearchPathName, pTebUnicodeString->Buffer, Extention[i], MAX_PATH + 1, ProgramNameBuffer, &pFilePart )) == 0) i++; if (nChar == 0) YellAndExit(ID_NO_FILE, 0xFF); nChar = GetFileAttributes(ProgramNameBuffer); if (nChar == (DWORD) (-1) || (nChar & FILE_ATTRIBUTE_DIRECTORY)) YellAndExit(ID_NO_FILE, 0xFF); if (OemString.Length + CommandLineLength > 128 - 2 - 1) YellAndExit(ID_BAD_CMDLINE, 0xFF); #if DBG if (fOutputDebugInfo) printf("Program path name is %s\n", ProgramNameBuffer); #endif RtlInitString((PSTRING)&CmdLineString, CommandLine); Status = RtlOemStringToUnicodeString(pTebUnicodeString, &CmdLineString, FALSE); if (!NT_SUCCESS(Status)) YellAndExit(ID_BAD_CMDLINE, 0xFF); ZeroMemory(&StartupInfo, sizeof(STARTUPINFO)); StartupInfo.cb = sizeof (STARTUPINFO); if (!CreateProcess( ProgramNameBuffer, // program name pTebUnicodeString->Buffer,// command line NULL, // process attr NULL, // thread attr TRUE, // inherithandle CREATE_FORCEDOS, // create flag NULL, // environment DefDirectory, // cur dir &StartupInfo, // startupinfo &ProcessInformation )) { YellAndExit(ID_BAD_PROCESS, 0xFF); #if DBG if(fOutputDebugInfo) printf("CreateProceess Failed, error code = %ld\n", GetLastError()); #endif } WaitForSingleObject(ProcessInformation.hProcess, INFINITE); GetExitCodeProcess(ProcessInformation.hProcess, &ExitCode); CloseHandle(ProcessInformation.hProcess); ExitProcess(ExitCode); } VOID YellAndExit ( UINT MsgID, // string table id from resource WORD ExitCode // exit code to be used ) { int MessageSize; ULONG SizeWritten; OEM_STRING OemString; UNICODE_STRING UnicodeString; MessageSize = LoadString(NULL, MsgID, UnicodeMessage, MAX_MSG_LENGTH << 1); OemString.Buffer = OemMessage; OemString.Length = 0; OemString.MaximumLength = MAX_MSG_LENGTH * 2; RtlInitUnicodeString(&UnicodeString, UnicodeMessage); RtlUnicodeStringToOemString(&OemString, &UnicodeString, FALSE); WriteFile(GetStdHandle(STD_ERROR_HANDLE), OemString.Buffer, OemString.Length, &SizeWritten, NULL ); ExitProcess(ExitCode); }