[BASESRV][KERNEL32][NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Thu, 1 May 2014 23:52:36 +0000 (23:52 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Thu, 1 May 2014 23:52:36 +0000 (23:52 +0000)
Make our BaseSrvGetNextVDMCommand and GetNextVDMCommand a bit more Windows-compatible,
and modify NTVDM accordingly.

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

dll/win32/kernel32/client/vdm.c
include/reactos/subsys/win/vdm.h
subsystems/ntvdm/dos/dos32krnl/dos.c
subsystems/ntvdm/ntvdm.c
subsystems/win/basesrv/vdm.c

index 6c07b08..d3b827c 100644 (file)
@@ -1281,6 +1281,10 @@ GetNextVDMCommand(PVDM_COMMAND_INFO CommandData)
                         BaseSetLastNTError(Status);
                         goto Cleanup;
                     }
+
+                    /* Set the retry flag and clear the exit code */
+                    GetNextVdmCommand->VDMState |= VDM_FLAG_RETRY;
+                    GetNextVdmCommand->ExitCode = 0;
                 }
             }
             while (GetNextVdmCommand->WaitObjectForVDM != NULL);
index 5faa435..e493dcd 100644 (file)
@@ -48,11 +48,22 @@ typedef enum _VDM_ENTRY_CODE
 #define VDM_READY           0x04
 
 //
-// VDM Magic Values
+// VDM Flags
 //
+#define VDM_FLAG_FIRST_TASK     0x01
 #define VDM_FLAG_WOW            0x02
+#define VDM_FLAG_DOS            0x04
+#define VDM_FLAG_RETRY          0x08
 #define VDM_INC_REENTER_COUNT   0x10
 #define VDM_DEC_REENTER_COUNT   0x20
+#define VDM_FLAG_NESTED_TASK    0x40
+#define VDM_FLAG_DONT_WAIT      0x80
+#define VDM_GET_FIRST_COMMAND   0x100
+#define VDM_GET_ENVIRONMENT     0x400
+#define VDM_FLAG_SEPARATE_WOW   0x800
+#define VDM_LIST_WOW_PROCESSES  0x1000
+#define VDM_LIST_WOW_TASKS      0x4000
+#define VDM_ADD_WOW_TASK        0x8000
 
 typedef struct
 {
index f8f214a..d0bf064 100644 (file)
@@ -1336,7 +1336,7 @@ WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
             ZeroMemory(&CommandInfo, sizeof(CommandInfo));
 
             /* Initialize the structure members */
-            CommandInfo.VDMState = VDM_NOT_READY;
+            CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
             CommandInfo.CmdLine = CmdLine;
             CommandInfo.CmdLen = sizeof(CmdLine);
             CommandInfo.AppName = AppName;
@@ -1399,6 +1399,7 @@ VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
     PDOS_MCB CurrentMcb;
     LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
     PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
+    VDM_COMMAND_INFO CommandInfo;
 
     DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
            Psp,
@@ -1445,6 +1446,22 @@ Done:
         if (CurrentPsp == SYSTEM_PSP) VdmRunning = FALSE;
     }
 
+    // FIXME: This is probably not the best way to do it
+    /* Check if this was a nested DOS task */
+    if (VdmRunning)
+    {
+        /* Decrement the re-entry count */
+        CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
+        GetNextVDMCommand(&CommandInfo);
+
+        /* Clear the structure */
+        ZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+        /* Update the VDM state of the task */
+        CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
+        GetNextVDMCommand(&CommandInfo);
+    }
+
     /* Save the return code - Normal termination */
     DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
 
index be32afe..c6cc701 100644 (file)
@@ -390,6 +390,7 @@ VOID ConsoleCleanup(VOID)
 
 DWORD WINAPI CommandThreadProc(LPVOID Parameter)
 {
+    BOOLEAN First = TRUE;
     DWORD Result;
     VDM_COMMAND_INFO CommandInfo;
     CHAR CmdLine[MAX_PATH];
@@ -407,7 +408,7 @@ DWORD WINAPI CommandThreadProc(LPVOID Parameter)
         ZeroMemory(&CommandInfo, sizeof(CommandInfo));
 
         /* Initialize the structure members */
-        CommandInfo.VDMState = VDM_NOT_LOADED;
+        CommandInfo.VDMState = VDM_FLAG_DOS;
         CommandInfo.CmdLine = CmdLine;
         CommandInfo.CmdLen = sizeof(CmdLine);
         CommandInfo.AppName = AppName;
@@ -421,6 +422,12 @@ DWORD WINAPI CommandThreadProc(LPVOID Parameter)
         CommandInfo.Env = Env;
         CommandInfo.EnvLen = sizeof(Env);
 
+        if (First)
+        {
+            CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK;
+            First = FALSE;
+        }
+
         /* Wait for the next available VDM */
         if (!GetNextVDMCommand(&CommandInfo)) break;
 
index e809495..8d3af6f 100644 (file)
@@ -905,21 +905,58 @@ CSR_API(BaseSrvGetNextVDMCommand)
         GetNextVdmCommandRequest->iTask = ConsoleRecord->SessionId;
         GetNextVdmCommandRequest->WaitObjectForVDM = NULL;
 
-        if (!(GetNextVdmCommandRequest->VDMState & VDM_NOT_READY))
+        if (GetNextVdmCommandRequest->VDMState & VDM_GET_FIRST_COMMAND)
         {
+            /* Check if the DOS record list is empty */
+            if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                goto Cleanup;
+            }
+
+            /* Get the first DOS record */
+            DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink, VDM_DOS_RECORD, Entry);
+
+            /* Make sure its command information is still there */
+            if (DosRecord->CommandInfo == NULL)
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                goto Cleanup;
+            }
+
+            /* Fill the command information */
+            Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
+            goto Cleanup;
+        }
+
+        /* Check if we should set the state of a running DOS record to ready */
+        if (!(GetNextVdmCommandRequest->VDMState
+            & (VDM_FLAG_FIRST_TASK | VDM_FLAG_RETRY | VDM_FLAG_NESTED_TASK)))
+        {
+            /* Search for a DOS record that is currently running */
             for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
             {
                 DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
-                if (DosRecord->State == VDM_NOT_READY)
-                {
-                    /* If NTVDM is asking for a new command, it means these are done */
-                    DosRecord->State = VDM_READY; 
+                if (DosRecord->State == VDM_NOT_READY) break;
+            }
 
-                    NtSetEvent(DosRecord->ServerEvent, NULL);
-                    NtClose(DosRecord->ServerEvent);
-                    DosRecord->ServerEvent = NULL;
-                }
+            /* Check if we found any */
+            if (i == &ConsoleRecord->DosListHead)
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                goto Cleanup;
             }
+
+            /* Set the exit code */
+            DosRecord->ExitCode = GetNextVdmCommandRequest->ExitCode;
+
+            /* Update the VDM state */
+            DosRecord->State = VDM_READY; 
+
+            /* Notify all waiting threads that the task is finished */
+            NtSetEvent(DosRecord->ServerEvent, NULL);
+            NtClose(DosRecord->ServerEvent);
+            DosRecord->ServerEvent = NULL;
         }
 
         /* Search for a DOS record that isn't loaded yet */
@@ -931,16 +968,40 @@ CSR_API(BaseSrvGetNextVDMCommand)
 
         if (i != &ConsoleRecord->DosListHead)
         {
-            /* Fill the command information */
-            Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
-            if (!NT_SUCCESS(Status)) goto Cleanup;
+            /* DOS tasks which haven't been loaded yet should have a command info structure */
+            ASSERT(DosRecord->CommandInfo != NULL);
+
+            /* Check if the caller only wants environment data */
+            if (GetNextVdmCommandRequest->VDMState & VDM_GET_ENVIRONMENT)
+            {
+                if (GetNextVdmCommandRequest->EnvLen < DosRecord->CommandInfo->EnvLen)
+                {
+                    /* Not enough space was reserved */
+                    GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
+                    Status = STATUS_BUFFER_OVERFLOW;
+                    goto Cleanup;
+                }
 
-            /* Free the command information, it's no longer needed */
-            BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
-            DosRecord->CommandInfo = NULL;
+                /* Copy the environment data */
+                RtlMoveMemory(GetNextVdmCommandRequest->Env,
+                              DosRecord->CommandInfo->Env,
+                              DosRecord->CommandInfo->EnvLen);
 
-            /* Update the VDM state */
-            DosRecord->State = VDM_NOT_READY;
+                /* Return the actual size to the caller */
+                GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
+            }
+            else
+            {
+                /* Fill the command information */
+                Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
+
+                /* Free the command information, it's no longer needed */
+                BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
+                DosRecord->CommandInfo = NULL;
+
+                /* Update the VDM state */
+                GetNextVdmCommandRequest->VDMState = DosRecord->State = VDM_NOT_READY;
+            }
 
             Status = STATUS_SUCCESS;
             goto Cleanup;
@@ -954,21 +1015,25 @@ CSR_API(BaseSrvGetNextVDMCommand)
     }
 
     /* There is no command yet */
-    if (ConsoleRecord->ServerEvent)
+    if ((GetNextVdmCommandRequest->VDMState & (VDM_FLAG_DONT_WAIT | VDM_FLAG_RETRY))
+        != (VDM_FLAG_DONT_WAIT | VDM_FLAG_RETRY))
     {
-        /* Reset the event */
-        NtResetEvent(ConsoleRecord->ServerEvent, NULL);
-    }
-    else
-    {
-        /* Create a pair of wait handles */
-        Status = BaseSrvCreatePairWaitHandles(&ConsoleRecord->ServerEvent,
-                                              &ConsoleRecord->ClientEvent);
-        if (!NT_SUCCESS(Status)) goto Cleanup;
-    }
+        if (ConsoleRecord->ServerEvent)
+        {
+            /* Reset the event */
+            NtResetEvent(ConsoleRecord->ServerEvent, NULL);
+        }
+        else
+        {
+            /* Create a pair of wait handles */
+            Status = BaseSrvCreatePairWaitHandles(&ConsoleRecord->ServerEvent,
+                                                  &ConsoleRecord->ClientEvent);
+            if (!NT_SUCCESS(Status)) goto Cleanup;
+        }
 
-    /* Return the client event handle */
-    GetNextVdmCommandRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent;
+        /* Return the client event handle */
+        GetNextVdmCommandRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent;
+    }
 
 Cleanup:
     /* Leave the critical section */