[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Sat, 26 Apr 2014 08:57:17 +0000 (08:57 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Sat, 26 Apr 2014 08:57:17 +0000 (08:57 +0000)
Rewrite DosCreateProcess, separating the loading code into DosLoadExecutable.
Implement INT 21h function AH = 0x4B (Create Process).

svn path=/branches/ntvdm/; revision=62972

subsystems/ntvdm/dos/dos32krnl/dos.c
subsystems/ntvdm/dos/dos32krnl/dos.h
subsystems/ntvdm/ntvdm.c

index a9d10a0..5b7ad70 100644 (file)
@@ -383,15 +383,13 @@ static VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner)
     Mcb->OwnerPsp = NewOwner;
 }
 
-
-
-static WORD DosCopyEnvironmentBlock(WORD SourceSegment, LPCSTR ProgramName)
+static WORD DosCopyEnvironmentBlock(LPCVOID Environment, LPCSTR ProgramName)
 {
-    PCHAR Ptr, SourceBuffer, DestBuffer = NULL;
+    PCHAR Ptr, DestBuffer = NULL;
     ULONG TotalSize = 0;
     WORD DestSegment;
 
-    Ptr = SourceBuffer = (PCHAR)SEG_OFF_TO_PTR(SourceSegment, 0);
+    Ptr = (PCHAR)Environment;
 
     /* Calculate the size of the environment block */
     while (*Ptr)
@@ -408,7 +406,7 @@ static WORD DosCopyEnvironmentBlock(WORD SourceSegment, LPCSTR ProgramName)
     DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
     if (!DestSegment) return 0;
 
-    Ptr = SourceBuffer;
+    Ptr = (PCHAR)Environment;
 
     DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
     while (*Ptr)
@@ -1042,57 +1040,51 @@ VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WOR
     PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
 }
 
-BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
+DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
+                        IN LPCSTR ExecutablePath,
+                        IN LPCSTR CommandLine,
+                        IN PVOID Environment,
+                        OUT PDWORD StackLocation OPTIONAL,
+                        OUT PDWORD EntryPoint OPTIONAL)
 {
-    BOOLEAN Success = FALSE, AllocatedEnvBlock = FALSE;
+    DWORD Result = ERROR_SUCCESS;
     HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
     LPBYTE Address = NULL;
-    LPSTR ProgramFilePath, Parameters[256];
-    CHAR CommandLineCopy[DOS_CMDLINE_LENGTH];
-    CHAR ParamString[DOS_CMDLINE_LENGTH];
-    DWORD ParamCount = 0;
     WORD Segment = 0;
+    WORD EnvBlock = 0;
     WORD MaxAllocSize;
     DWORD i, FileSize, ExeSize;
     PIMAGE_DOS_HEADER Header;
     PDWORD RelocationTable;
     PWORD RelocWord;
 
-    DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
-           CommandLine,
-           EnvBlock);
-
-    /* Save a copy of the command line */
-    strcpy(CommandLineCopy, CommandLine);
-
-    /* Get the file name of the executable */
-    ProgramFilePath = strtok(CommandLineCopy, " \t");
-
-    /* Load the parameters in the local array */
-    while ((ParamCount < sizeof(Parameters)/sizeof(Parameters[0]))
-           && ((Parameters[ParamCount] = strtok(NULL, " \t")) != NULL))
-    {
-        ParamCount++;
-    }
-
-    ZeroMemory(ParamString, sizeof(ParamString));
+    DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
+            LoadType,
+            ExecutablePath,
+            CommandLine,
+            Environment,
+            StackLocation,
+            EntryPoint);
 
-    /* Store the parameters in a string */
-    for (i = 0; i < ParamCount; i++)
+    if (LoadType == DOS_LOAD_OVERLAY)
     {
-        strncat(ParamString, Parameters[i], DOS_CMDLINE_LENGTH - strlen(ParamString) - 1);
-        strncat(ParamString, " ", DOS_CMDLINE_LENGTH - strlen(ParamString) - 1);
+        DPRINT1("Overlay loading is not supported yet.\n");
+        return ERROR_NOT_SUPPORTED;
     }
 
     /* Open a handle to the executable */
-    FileHandle = CreateFileA(ProgramFilePath,
+    FileHandle = CreateFileA(ExecutablePath,
                              GENERIC_READ,
                              0,
                              NULL,
                              OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL,
                              NULL);
-    if (FileHandle == INVALID_HANDLE_VALUE) goto Cleanup;
+    if (FileHandle == INVALID_HANDLE_VALUE)
+    {
+        Result = GetLastError();
+        goto Cleanup;
+    }
 
     /* Get the file size */
     FileSize = GetFileSize(FileHandle, NULL);
@@ -1104,23 +1096,26 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
                                     0,
                                     0,
                                     NULL);
-    if (FileMapping == NULL) goto Cleanup;
+    if (FileMapping == NULL)
+    {
+        Result = GetLastError();
+        goto Cleanup;
+    }
 
     /* Map the file into memory */
     Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
-    if (Address == NULL) goto Cleanup;
-
-    /* Did we get an environment segment? */
-    if (!EnvBlock)
+    if (Address == NULL)
     {
-        /* Set a flag to know if the environment block was allocated here */
-        AllocatedEnvBlock = TRUE;
+        Result = GetLastError();
+        goto Cleanup;
+    }
 
-        /* No, copy the one from the parent */
-        EnvBlock = DosCopyEnvironmentBlock((CurrentPsp != SYSTEM_PSP)
-                                           ? SEGMENT_TO_PSP(CurrentPsp)->EnvBlock
-                                           : SYSTEM_ENV_BLOCK,
-                                           ProgramFilePath);
+    /* Copy the environment block to DOS memory */
+    EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
+    if (EnvBlock == 0)
+    {
+        Result = ERROR_NOT_ENOUGH_MEMORY;
+        goto Cleanup;
     }
 
     /* Check if this is an EXE file or a COM file */
@@ -1152,11 +1147,15 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         }
 
         /* Check if at least the lowest allocation was successful */
-        if (Segment == 0) goto Cleanup;
+        if (Segment == 0)
+        {
+            Result = ERROR_NOT_ENOUGH_MEMORY;
+            goto Cleanup;
+        }
 
         /* Initialize the PSP */
         DosInitializePsp(Segment,
-                         ParamString,
+                         CommandLine,
                          (WORD)ExeSize,
                          EnvBlock);
 
@@ -1184,21 +1183,22 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
             *RelocWord += Segment + (sizeof(DOS_PSP) >> 4);
         }
 
-        /* Set the initial segment registers */
-        setDS(Segment);
-        setES(Segment);
-
-        /* Set the stack to the location from the header */
-        EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss,
-                         Header->e_sp);
+        if (LoadType == DOS_LOAD_AND_EXECUTE)
+        {
+            /* Set the initial segment registers */
+            setDS(Segment);
+            setES(Segment);
 
-        /* Execute */
-        CurrentPsp = Segment;
-        DiskTransferArea = MAKELONG(0x80, Segment);
-        EmulatorExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
-                        Header->e_ip);
+            /* Set the stack to the location from the header */
+            EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss,
+                             Header->e_sp);
 
-        Success = TRUE;
+            /* Execute */
+            CurrentPsp = Segment;
+            DiskTransferArea = MAKELONG(0x80, Segment);
+            EmulatorExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
+                            Header->e_ip);
+        }
     }
     else
     {
@@ -1208,11 +1208,19 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         DosAllocateMemory(0xFFFF, &MaxAllocSize);
 
         /* Make sure it's enough for the whole program and the PSP */
-        if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP))) goto Cleanup;
+        if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
+        {
+            Result = ERROR_NOT_ENOUGH_MEMORY;
+            goto Cleanup;
+        }
 
         /* Allocate all of it */
         Segment = DosAllocateMemory(MaxAllocSize, NULL);
-        if (Segment == 0) goto Cleanup;
+        if (Segment == 0)
+        {
+            Result = ERROR_ARENA_TRASHED;
+            goto Cleanup;
+        }
 
         /* The process owns its own memory */
         DosChangeMemoryOwner(Segment, Segment);
@@ -1225,36 +1233,37 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
 
         /* Initialize the PSP */
         DosInitializePsp(Segment,
-                         ParamString,
+                         CommandLine,
                          MaxAllocSize,
                          EnvBlock);
 
-        /* Set the initial segment registers */
-        setDS(Segment);
-        setES(Segment);
-
-        /* Set the stack to the last word of the segment */
-        EmulatorSetStack(Segment, 0xFFFE);
+        if (LoadType == DOS_LOAD_AND_EXECUTE)
+        {
+            /* Set the initial segment registers */
+            setDS(Segment);
+            setES(Segment);
 
-        /*
-         * Set the value on the stack to 0, so that a near return
-         * jumps to PSP:0000 which has the exit code.
-         */
-        *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
+            /* Set the stack to the last word of the segment */
+            EmulatorSetStack(Segment, 0xFFFE);
 
-        /* Execute */
-        CurrentPsp = Segment;
-        DiskTransferArea = MAKELONG(0x80, Segment);
-        EmulatorExecute(Segment, 0x100);
+            /*
+             * Set the value on the stack to 0, so that a near return
+             * jumps to PSP:0000 which has the exit code.
+             */
+            *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
 
-        Success = TRUE;
+            /* Execute */
+            CurrentPsp = Segment;
+            DiskTransferArea = MAKELONG(0x80, Segment);
+            EmulatorExecute(Segment, 0x100);
+        }
     }
 
 Cleanup:
-    if (!Success)
+    if (Result != ERROR_SUCCESS)
     {
         /* It was not successful, cleanup the DOS memory */
-        if (AllocatedEnvBlock) DosFreeMemory(EnvBlock);
+        if (EnvBlock) DosFreeMemory(EnvBlock);
         if (Segment) DosFreeMemory(Segment);
     }
 
@@ -1267,7 +1276,118 @@ Cleanup:
     /* Close the file handle */
     if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
 
-    return Success;
+    return Result;
+}
+
+WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
+                      LPCSTR ProgramName,
+                      PDOS_EXEC_PARAM_BLOCK Parameters)
+{
+    DWORD BinaryType;
+    LPVOID Environment = NULL;
+    VDM_COMMAND_INFO CommandInfo;
+    CHAR CmdLine[MAX_PATH];
+    CHAR AppName[MAX_PATH];
+    CHAR PifFile[MAX_PATH];
+    CHAR Desktop[MAX_PATH];
+    CHAR Title[MAX_PATH];
+    CHAR Env[MAX_PATH];
+    STARTUPINFOA StartupInfo;
+    PROCESS_INFORMATION ProcessInfo;
+
+    /* Get the binary type */
+    if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
+
+    /* Did the caller specify an environment segment? */
+    if (Parameters->Environment)
+    {
+        /* Yes, use it instead of the parent one */
+        Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
+    }
+
+    /* Set up the startup info structure */
+    ZeroMemory(&StartupInfo, sizeof(STARTUPINFOA));
+    StartupInfo.cb = sizeof(STARTUPINFOA);
+
+    /* Create the process */
+    if (!CreateProcessA(ProgramName,
+                        FAR_POINTER(Parameters->CommandLine),
+                        NULL,
+                        NULL,
+                        FALSE,
+                        0,
+                        Environment,
+                        NULL,
+                        &StartupInfo,
+                        &ProcessInfo))
+    {
+        return GetLastError();
+    }
+
+    /* Check the type of the program */
+    switch (BinaryType)
+    {
+        /* These are handled by NTVDM */
+        case SCS_DOS_BINARY:
+        case SCS_WOW_BINARY:
+        {
+            /* Clear the structure */
+            ZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+            /* Initialize the structure members */
+            CommandInfo.VDMState = VDM_NOT_READY;
+            CommandInfo.CmdLine = CmdLine;
+            CommandInfo.CmdLen = sizeof(CmdLine);
+            CommandInfo.AppName = AppName;
+            CommandInfo.AppLen = sizeof(AppName);
+            CommandInfo.PifFile = PifFile;
+            CommandInfo.PifLen = sizeof(PifFile);
+            CommandInfo.Desktop = Desktop;
+            CommandInfo.DesktopLen = sizeof(Desktop);
+            CommandInfo.Title = Title;
+            CommandInfo.TitleLen = sizeof(Title);
+            CommandInfo.Env = Env;
+            CommandInfo.EnvLen = sizeof(Env);
+
+            /* Get the VDM command information */
+            if (!GetNextVDMCommand(&CommandInfo))
+            {
+                /* Shouldn't happen */
+                ASSERT(FALSE);
+            }
+
+            /* Increment the re-entry count */
+            CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
+            GetNextVDMCommand(&CommandInfo);
+
+            /* Load the executable */
+            if (DosLoadExecutable(LoadType,
+                                  AppName,
+                                  CmdLine,
+                                  Env,
+                                  &Parameters->StackLocation,
+                                  &Parameters->EntryPoint) != ERROR_SUCCESS)
+            {
+                DisplayMessage(L"Could not load '%S'", AppName);
+                break;
+            }
+
+            break;
+        }
+
+        /* Not handled by NTVDM */
+        default:
+        {
+            /* Wait for the process to finish executing */
+            WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
+        }
+    }
+
+    /* Close the handles */
+    CloseHandle(ProcessInfo.hProcess);
+    CloseHandle(ProcessInfo.hThread);
+
+    return ERROR_SUCCESS;
 }
 
 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
@@ -2345,6 +2465,27 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Execute */
+        case 0x4B:
+        {
+            DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)getAL();
+            LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
+            PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
+            WORD ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock);
+
+            if (ErrorCode == ERROR_SUCCESS)
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            }
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ErrorCode);
+            }
+
+            break;
+        }
+
         /* Terminate With Return Code */
         case 0x4C:
         {
index d82bb1a..18e0b14 100644 (file)
@@ -53,6 +53,13 @@ enum DOS_ALLOC_STRATEGY
     DOS_ALLOC_LAST_FIT
 };
 
+typedef enum
+{
+    DOS_LOAD_AND_EXECUTE = 0x00,
+    DOS_LOAD_ONLY = 0x01,
+    DOS_LOAD_OVERLAY = 0x03
+} DOS_EXEC_TYPE;
+
 #pragma pack(push, 1)
 
 typedef struct _DOS_MCB
@@ -136,6 +143,19 @@ typedef struct _DOS_FIND_FILE_BLOCK
     CHAR FileName[13];
 } DOS_FIND_FILE_BLOCK, *PDOS_FIND_FILE_BLOCK;
 
+typedef struct _DOS_EXEC_PARAM_BLOCK
+{
+    /* Input variables */
+    WORD Environment;
+    DWORD CommandLine;
+    DWORD FirstFcb;
+    DWORD SecondFcb;
+
+    /* Output variables */
+    DWORD StackLocation;
+    DWORD EntryPoint;
+} DOS_EXEC_PARAM_BLOCK, *PDOS_EXEC_PARAM_BLOCK;
+
 #pragma pack(pop)
 
 /* FUNCTIONS ******************************************************************/
@@ -169,7 +189,19 @@ WORD DosReadFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesRead);
 WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritten);
 
 VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment);
-BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock);
+DWORD DosLoadExecutable(
+    IN DOS_EXEC_TYPE LoadType,
+    IN LPCSTR ExecutablePath,
+    IN LPCSTR CommandLine,
+    IN PVOID Environment,
+    OUT PDWORD StackLocation OPTIONAL,
+    OUT PDWORD EntryPoint OPTIONAL
+);
+WORD DosCreateProcess(
+    DOS_EXEC_TYPE LoadType,
+    LPCSTR ProgramName,
+    PDOS_EXEC_PARAM_BLOCK Parameters
+);
 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode);
 BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle);
 
index a079e2d..6c7099d 100644 (file)
@@ -385,6 +385,7 @@ DWORD WINAPI CommandThreadProc(LPVOID Parameter)
     CHAR PifFile[MAX_PATH];
     CHAR Desktop[MAX_PATH];
     CHAR Title[MAX_PATH];
+    CHAR Env[MAX_PATH];
 
     UNREFERENCED_PARAMETER(Parameter);
 
@@ -405,13 +406,20 @@ DWORD WINAPI CommandThreadProc(LPVOID Parameter)
         CommandInfo.DesktopLen = sizeof(Desktop);
         CommandInfo.Title = Title;
         CommandInfo.TitleLen = sizeof(Title);
+        CommandInfo.Env = Env;
+        CommandInfo.EnvLen = sizeof(Env);
 
         /* Wait for the next available VDM */
         if (!GetNextVDMCommand(&CommandInfo)) break;
 
         /* Start the process from the command line */
         DPRINT1("Starting '%s'...\n", AppName);
-        if (!DosCreateProcess(AppName, 0))
+        if (DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
+                              AppName,
+                              CmdLine,
+                              Env,
+                              NULL,
+                              NULL) != ERROR_SUCCESS)
         {
             DisplayMessage(L"Could not start '%S'", AppName);
             break;
@@ -430,17 +438,20 @@ DWORD WINAPI CommandThreadProc(LPVOID Parameter)
 INT wmain(INT argc, WCHAR *argv[])
 {
 #ifdef STANDALONE
-
+    CHAR ApplicationName[MAX_PATH];
     CHAR CommandLine[DOS_CMDLINE_LENGTH];
 
-    if (argc == 2 && argv[1] != NULL)
+    if (argc >= 2)
     {
-        WideCharToMultiByte(CP_ACP, 0, argv[1], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
+        WideCharToMultiByte(CP_ACP, 0, argv[1], -1, ApplicationName, sizeof(ApplicationName), NULL, NULL);
+
+        if (argc >= 3) WideCharToMultiByte(CP_ACP, 0, argv[2], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
+        else strcpy(CommandLine, "");
     }
     else
     {
         wprintf(L"\nReactOS Virtual DOS Machine\n\n"
-                L"Usage: NTVDM <executable>\n");
+                L"Usage: NTVDM <executable> [<parameters>]\n");
         return 0;
     }
 
@@ -496,9 +507,14 @@ INT wmain(INT argc, WCHAR *argv[])
 
     /* Start the process from the command line */
     DPRINT1("Starting '%s'...\n", CommandLine);
-    if (!DosCreateProcess(CommandLine, 0))
+    if (DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
+                          ApplicationName,
+                          CommandLine,
+                          GetEnvironmentStrings(),
+                          NULL,
+                          NULL) != ERROR_SUCCESS)
     {
-        DisplayMessage(L"Could not start '%S'", CommandLine);
+        DisplayMessage(L"Could not start '%S'", ApplicationName);
         goto Cleanup;
     }