[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / vdm.c
index 3ff2c46..a3cb7cd 100644 (file)
 
 /* TYPES **********************************************************************/
 
+#define BINARY_UNKNOWN  (0)
+#define BINARY_PE_EXE32 (1)
+#define BINARY_PE_DLL32 (2)
+#define BINARY_PE_EXE64 (3)
+#define BINARY_PE_DLL64 (4)
+#define BINARY_WIN16    (5)
+#define BINARY_OS216    (6)
+#define BINARY_DOS      (7)
+#define BINARY_UNIX_EXE (8)
+#define BINARY_UNIX_LIB (9)
+
+
+typedef enum _ENV_NAME_TYPE
+{
+    EnvNameNotAPath = 1,
+    EnvNameSinglePath ,
+    EnvNameMultiplePath
+} ENV_NAME_TYPE;
+
 typedef struct _ENV_INFO
 {
-    ULONG NameType;
-    ULONG NameLength;
+    ENV_NAME_TYPE NameType;
+    ULONG  NameLength;
     PWCHAR Name;
 } ENV_INFO, *PENV_INFO;
 
 /* GLOBALS ********************************************************************/
 
-ENV_INFO BasepEnvNameType[] =
+#define ENV_NAME_ENTRY(type, name)  \
+    {(type), _ARRAYSIZE(name) - 1, (name)}
+
+static ENV_INFO BasepEnvNameType[] =
 {
-    {3, sizeof(L"PATH")      , L"PATH"      },
-    {2, sizeof(L"WINDIR")    , L"WINDIR"    },
-    {2, sizeof(L"SYSTEMROOT"), L"SYSTEMROOT"},
-    {3, sizeof(L"TEMP")      , L"TEMP"      },
-    {3, sizeof(L"TMP")       , L"TMP"       },
+    ENV_NAME_ENTRY(EnvNameMultiplePath, L"PATH"),
+    ENV_NAME_ENTRY(EnvNameSinglePath  , L"WINDIR"),
+    ENV_NAME_ENTRY(EnvNameSinglePath  , L"SYSTEMROOT"),
+    ENV_NAME_ENTRY(EnvNameMultiplePath, L"TEMP"),
+    ENV_NAME_ENTRY(EnvNameMultiplePath, L"TMP"),
 };
 
-UNICODE_STRING BaseDotComSuffixName = RTL_CONSTANT_STRING(L".com");
-UNICODE_STRING BaseDotPifSuffixName = RTL_CONSTANT_STRING(L".pif");
-UNICODE_STRING BaseDotExeSuffixName = RTL_CONSTANT_STRING(L".exe");
+static UNICODE_STRING BaseDotComSuffixName = RTL_CONSTANT_STRING(L".com");
+static UNICODE_STRING BaseDotPifSuffixName = RTL_CONSTANT_STRING(L".pif");
+static UNICODE_STRING BaseDotExeSuffixName = RTL_CONSTANT_STRING(L".exe");
 
 /* FUNCTIONS ******************************************************************/
 
@@ -64,7 +86,7 @@ BaseIsDosApplication(IN PUNICODE_STRING PathName,
     return 0;
 }
 
-BOOL
+NTSTATUS
 WINAPI
 BaseCheckVDM(IN ULONG BinaryType,
              IN PCWCH ApplicationName,
@@ -77,9 +99,434 @@ BaseCheckVDM(IN ULONG BinaryType,
              IN LPSTARTUPINFOW StartupInfo,
              IN HANDLE hUserToken OPTIONAL)
 {
-    /* This is not supported */
-    UNIMPLEMENTED;
-    return FALSE;
+    NTSTATUS Status;
+    PBASE_CHECK_VDM CheckVdm = &ApiMessage->Data.CheckVDMRequest;
+    PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
+    PWCHAR CurrentDir = NULL;
+    PWCHAR ShortAppName = NULL;
+    PWCHAR ShortCurrentDir = NULL;
+    ULONG Length;
+    PCHAR AnsiCmdLine = NULL;
+    PCHAR AnsiAppName = NULL;
+    PCHAR AnsiCurDirectory = NULL;
+    PCHAR AnsiDesktop = NULL;
+    PCHAR AnsiTitle = NULL;
+    PCHAR AnsiReserved = NULL;
+    STARTUPINFOA AnsiStartupInfo;
+    ULONG NumStrings = 5;
+
+    /* Parameters validation */
+    if (ApplicationName == NULL || CommandLine == NULL)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Trim leading whitespace from ApplicationName */
+    while (*ApplicationName == L' ' || *ApplicationName == L'\t')
+        ++ApplicationName;
+
+    /* Calculate the size of the short application name */
+    Length = GetShortPathNameW(ApplicationName, NULL, 0);
+    if (Length == 0)
+    {
+        Status = STATUS_OBJECT_PATH_INVALID;
+        goto Cleanup;
+    }
+
+    /* Allocate memory for the short application name */
+    ShortAppName = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
+                                           HEAP_ZERO_MEMORY,
+                                           Length * sizeof(WCHAR));
+    if (!ShortAppName)
+    {
+        Status = STATUS_NO_MEMORY;
+        goto Cleanup;
+    }
+
+    /* Get the short application name */
+    if (GetShortPathNameW(ApplicationName, ShortAppName, Length) == 0)
+    {
+        /* Try to determine which error occurred */
+        switch (GetLastError())
+        {
+            case ERROR_NOT_ENOUGH_MEMORY:
+            {
+                Status = STATUS_NO_MEMORY;
+                break;
+            }
+
+            case ERROR_INVALID_PARAMETER:
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                break;
+            }
+
+            default:
+            {
+                Status = STATUS_OBJECT_PATH_INVALID;
+            }
+        }
+
+        goto Cleanup;
+    }
+
+    /* Trim leading whitespace from CommandLine */
+    while (*CommandLine == L' ' || *CommandLine == L'\t')
+        ++CommandLine;
+
+    /*
+     * CommandLine is usually formatted as: 'ApplicationName param0 ...'.
+     * So we want to strip the first token (ApplicationName) from it.
+     * Two cases are in fact possible:
+     * - either the first token is indeed ApplicationName, so we just skip it;
+     * - or the first token is not exactly ApplicationName, because it happened
+     *   that somebody else already preprocessed CommandLine. Therefore we
+     *   suppose that the first token corresponds to an application name and
+     *   we skip it. Care should be taken when quotes are present in this token.
+     */
+     if (*CommandLine)
+     {
+        /* The first part of CommandLine should be the ApplicationName... */
+        Length = wcslen(ApplicationName);
+        if (Length <= wcslen(CommandLine) &&
+            _wcsnicmp(ApplicationName, CommandLine, Length) == 0)
+        {
+            /* Skip it */
+            CommandLine += Length;
+        }
+        /*
+         * ... but it is not, however we still have a token. We suppose that
+         * it corresponds to some sort of application name, so we skip it too.
+         */
+        else
+        {
+            /* Get rid of the first token. We stop when we see whitespace. */
+            while (*CommandLine && !(*CommandLine == L' ' || *CommandLine == L'\t'))
+            {
+                if (*CommandLine == L'\"')
+                {
+                    /* We enter a quoted part, skip it */
+                    ++CommandLine;
+                    while (*CommandLine && *CommandLine++ != L'\"') ;
+                }
+                else
+                {
+                    /* Go to the next character */
+                    ++CommandLine;
+                }
+            }
+        }
+    }
+
+    /*
+     * Trim remaining whitespace from CommandLine that may be
+     * present between the application name and the parameters.
+     */
+    while (*CommandLine == L' ' || *CommandLine == L'\t')
+        ++CommandLine;
+
+    /* Get the current directory */
+    if (CurrentDirectory == NULL)
+    {
+        /* Allocate memory for the current directory path */
+        Length = GetCurrentDirectoryW(0, NULL);
+        CurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
+                                             HEAP_ZERO_MEMORY,
+                                             Length * sizeof(WCHAR));
+        if (CurrentDir == NULL)
+        {
+            Status = STATUS_NO_MEMORY;
+            goto Cleanup;
+        }
+
+        /* Get the current directory */
+        GetCurrentDirectoryW(Length, CurrentDir);
+        CurrentDirectory = CurrentDir;
+    }
+
+    /* Calculate the size of the short current directory path */
+    Length = GetShortPathNameW(CurrentDirectory, NULL, 0);
+
+    /* Allocate memory for the short current directory path */
+    ShortCurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
+                                              HEAP_ZERO_MEMORY,
+                                              Length * sizeof(WCHAR));
+    if (!ShortCurrentDir)
+    {
+        Status = STATUS_NO_MEMORY;
+        goto Cleanup;
+    }
+
+    /* Get the short current directory path */
+    if (!GetShortPathNameW(CurrentDirectory, ShortCurrentDir, Length))
+    {
+        /* Try to determine which error occurred */
+        switch (GetLastError())
+        {
+            case ERROR_NOT_ENOUGH_MEMORY:
+            {
+                Status = STATUS_NO_MEMORY;
+                break;
+            }
+
+            case ERROR_INVALID_PARAMETER:
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                break;
+            }
+
+            default:
+            {
+                Status = STATUS_OBJECT_PATH_INVALID;
+            }
+        }
+        goto Cleanup;
+    }
+
+    /* Setup the input parameters */
+    CheckVdm->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
+    CheckVdm->BinaryType = BinaryType;
+    CheckVdm->CodePage = CP_ACP;
+    CheckVdm->dwCreationFlags = CreationFlags;
+    CheckVdm->CurDrive = CurrentDirectory[0] - L'A';
+    CheckVdm->CmdLen = wcslen(CommandLine) + 1;
+    CheckVdm->AppLen = wcslen(ShortAppName) + 1;
+    CheckVdm->PifLen = 0; // TODO: PIF file support!
+    CheckVdm->CurDirectoryLen = wcslen(ShortCurrentDir) + 1;
+    CheckVdm->EnvLen = AnsiEnvironment->Length;
+    CheckVdm->DesktopLen = (StartupInfo->lpDesktop != NULL) ? (wcslen(StartupInfo->lpDesktop) + 1) : 0;
+    CheckVdm->TitleLen = (StartupInfo->lpTitle != NULL) ? (wcslen(StartupInfo->lpTitle) + 1) : 0;
+    CheckVdm->ReservedLen = (StartupInfo->lpReserved != NULL) ? (wcslen(StartupInfo->lpReserved) + 1) : 0;
+
+    if (StartupInfo->dwFlags & STARTF_USESTDHANDLES)
+    {
+        /* Set the standard handles */
+        CheckVdm->StdIn  = StartupInfo->hStdInput;
+        CheckVdm->StdOut = StartupInfo->hStdOutput;
+        CheckVdm->StdErr = StartupInfo->hStdError;
+    }
+
+    /* Allocate memory for the ANSI strings */
+    // We need to add the newline characters '\r\n' to the command line
+    AnsiCmdLine = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CmdLen + 2);
+    AnsiAppName = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->AppLen);
+    AnsiCurDirectory = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CurDirectoryLen);
+    if (StartupInfo->lpDesktop)
+        AnsiDesktop = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
+                                             HEAP_ZERO_MEMORY,
+                                             CheckVdm->DesktopLen);
+    if (StartupInfo->lpTitle)
+        AnsiTitle = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
+                                           HEAP_ZERO_MEMORY,
+                                           CheckVdm->TitleLen);
+    if (StartupInfo->lpReserved)
+        AnsiReserved = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
+                                              HEAP_ZERO_MEMORY,
+                                              CheckVdm->ReservedLen);
+
+    if (!AnsiCmdLine
+        || !AnsiAppName
+        || !AnsiCurDirectory
+        || (StartupInfo->lpDesktop && !AnsiDesktop)
+        || (StartupInfo->lpTitle && !AnsiTitle)
+        || (StartupInfo->lpReserved && !AnsiReserved))
+    {
+        Status = STATUS_NO_MEMORY;
+        goto Cleanup;
+    }
+
+    /* Convert the command line into an ANSI string */
+    WideCharToMultiByte(CP_ACP,
+                        0,
+                        CommandLine,
+                        CheckVdm->CmdLen,
+                        AnsiCmdLine,
+                        CheckVdm->CmdLen,
+                        NULL,
+                        NULL);
+    /* Add the needed newline and NULL-terminate */
+    CheckVdm->CmdLen--; // Rewind back to the NULL character
+    AnsiCmdLine[CheckVdm->CmdLen++] = '\r';
+    AnsiCmdLine[CheckVdm->CmdLen++] = '\n';
+    AnsiCmdLine[CheckVdm->CmdLen++] = 0;
+
+    /* Convert the short application name into an ANSI string */
+    WideCharToMultiByte(CP_ACP,
+                        0,
+                        ShortAppName,
+                        CheckVdm->AppLen,
+                        AnsiAppName,
+                        CheckVdm->AppLen,
+                        NULL,
+                        NULL);
+
+    /* Convert the short current directory path into an ANSI string */
+    WideCharToMultiByte(CP_ACP,
+                        0,
+                        ShortCurrentDir,
+                        CheckVdm->CurDirectoryLen,
+                        AnsiCurDirectory,
+                        CheckVdm->CurDirectoryLen,
+                        NULL,
+                        NULL);
+
+    if (StartupInfo->lpDesktop)
+    {
+        /* Convert the desktop name into an ANSI string */
+        WideCharToMultiByte(CP_ACP,
+                            0,
+                            StartupInfo->lpDesktop,
+                            CheckVdm->DesktopLen,
+                            AnsiDesktop,
+                            CheckVdm->DesktopLen,
+                            NULL,
+                            NULL);
+        NumStrings++;
+    }
+
+    if (StartupInfo->lpTitle)
+    {
+        /* Convert the title into an ANSI string */
+        WideCharToMultiByte(CP_ACP,
+                            0,
+                            StartupInfo->lpTitle,
+                            CheckVdm->TitleLen,
+                            AnsiTitle,
+                            CheckVdm->TitleLen,
+                            NULL,
+                            NULL);
+        NumStrings++;
+    }
+
+    if (StartupInfo->lpReserved)
+    {
+        /* Convert the reserved value into an ANSI string */
+        WideCharToMultiByte(CP_ACP,
+                            0,
+                            StartupInfo->lpReserved,
+                            CheckVdm->ReservedLen,
+                            AnsiReserved,
+                            CheckVdm->ReservedLen,
+                            NULL,
+                            NULL);
+        NumStrings++;
+    }
+
+    /* Fill the ANSI startup info structure */
+    RtlCopyMemory(&AnsiStartupInfo, StartupInfo, sizeof(AnsiStartupInfo));
+    AnsiStartupInfo.lpReserved = AnsiReserved;
+    AnsiStartupInfo.lpDesktop = AnsiDesktop;
+    AnsiStartupInfo.lpTitle = AnsiTitle;
+
+    /* Allocate the capture buffer */
+    CaptureBuffer = CsrAllocateCaptureBuffer(NumStrings,
+                                             CheckVdm->CmdLen
+                                             + CheckVdm->AppLen
+                                             + CheckVdm->PifLen
+                                             + CheckVdm->CurDirectoryLen
+                                             + CheckVdm->DesktopLen
+                                             + CheckVdm->TitleLen
+                                             + CheckVdm->ReservedLen
+                                             + CheckVdm->EnvLen
+                                             + sizeof(*CheckVdm->StartupInfo));
+    if (CaptureBuffer == NULL)
+    {
+        Status = STATUS_NO_MEMORY;
+        goto Cleanup;
+    }
+
+    /* Capture the command line */
+    CsrCaptureMessageBuffer(CaptureBuffer,
+                            AnsiCmdLine,
+                            CheckVdm->CmdLen,
+                            (PVOID*)&CheckVdm->CmdLine);
+
+    /* Capture the application name */
+    CsrCaptureMessageBuffer(CaptureBuffer,
+                            AnsiAppName,
+                            CheckVdm->AppLen,
+                            (PVOID*)&CheckVdm->AppName);
+
+    CheckVdm->PifFile = NULL; // TODO: PIF file support!
+
+    /* Capture the current directory */
+    CsrCaptureMessageBuffer(CaptureBuffer,
+                            AnsiCurDirectory,
+                            CheckVdm->CurDirectoryLen,
+                            (PVOID*)&CheckVdm->CurDirectory);
+
+    /* Capture the environment */
+    CsrCaptureMessageBuffer(CaptureBuffer,
+                            AnsiEnvironment->Buffer,
+                            CheckVdm->EnvLen,
+                            (PVOID*)&CheckVdm->Env);
+
+    /* Capture the startup info structure */
+    CsrCaptureMessageBuffer(CaptureBuffer,
+                            &AnsiStartupInfo,
+                            sizeof(*CheckVdm->StartupInfo),
+                            (PVOID*)&CheckVdm->StartupInfo);
+
+    if (StartupInfo->lpDesktop)
+    {
+        /* Capture the desktop name */
+        CsrCaptureMessageBuffer(CaptureBuffer,
+                                AnsiDesktop,
+                                CheckVdm->DesktopLen,
+                                (PVOID*)&CheckVdm->Desktop);
+    }
+    else CheckVdm->Desktop = NULL;
+
+    if (StartupInfo->lpTitle)
+    {
+        /* Capture the title */
+        CsrCaptureMessageBuffer(CaptureBuffer,
+                                AnsiTitle,
+                                CheckVdm->TitleLen,
+                                (PVOID*)&CheckVdm->Title);
+    }
+    else CheckVdm->Title = NULL;
+
+    if (StartupInfo->lpReserved)
+    {
+        /* Capture the reserved parameter */
+        CsrCaptureMessageBuffer(CaptureBuffer,
+                                AnsiReserved,
+                                CheckVdm->ReservedLen,
+                                (PVOID*)&CheckVdm->Reserved);
+    }
+    else CheckVdm->Reserved = NULL;
+
+    /* Send the message to CSRSS */
+    Status = CsrClientCallServer((PCSR_API_MESSAGE)ApiMessage,
+                                 CaptureBuffer,
+                                 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCheckVDM),
+                                 sizeof(*CheckVdm));
+
+    /* Write back the task ID */
+    *iTask = CheckVdm->iTask;
+
+Cleanup:
+
+    /* Free the ANSI strings */
+    if (AnsiCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCmdLine);
+    if (AnsiAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiAppName);
+    if (AnsiCurDirectory) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCurDirectory);
+    if (AnsiDesktop) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiDesktop);
+    if (AnsiTitle) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiTitle);
+    if (AnsiReserved) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiReserved);
+
+    /* Free the capture buffer */
+    if (CaptureBuffer) CsrFreeCaptureBuffer(CaptureBuffer);
+
+    /* Free the current directory, if it was allocated here, and its short path */
+    if (ShortCurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortCurrentDir);
+    if (CurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDir);
+
+    /* Free the short app name */
+    if (ShortAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortAppName);
+
+    return Status;
 }
 
 BOOL
@@ -89,10 +536,8 @@ BaseUpdateVDMEntry(IN ULONG UpdateIndex,
                    IN ULONG IndexInfo,
                    IN ULONG BinaryType)
 {
-#if 0 // Unimplemented in BASESRV
-    NTSTATUS Status;
     BASE_API_MESSAGE ApiMessage;
-    PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry = &ApiMessage.Data.UpdateVdmEntry;
+    PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry = &ApiMessage.Data.UpdateVDMEntryRequest;
 
     /* Check what update is being sent */
     switch (UpdateIndex)
@@ -138,14 +583,14 @@ BaseUpdateVDMEntry(IN ULONG UpdateIndex,
     UpdateVdmEntry->BinaryType = BinaryType;
 
     /* Send the message to CSRSS */
-    Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
-                                 NULL,
-                                 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepUpdateVDMEntry),
-                                 sizeof(BASE_UPDATE_VDM_ENTRY));
-    if (!NT_SUCCESS(Status))
+    CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+                        NULL,
+                        CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepUpdateVDMEntry),
+                        sizeof(*UpdateVdmEntry));
+    if (!NT_SUCCESS(ApiMessage.Status))
     {
         /* Handle failure */
-        BaseSetLastNTError(Status);
+        BaseSetLastNTError(ApiMessage.Status);
         return FALSE;
     }
 
@@ -155,7 +600,7 @@ BaseUpdateVDMEntry(IN ULONG UpdateIndex,
         /* Return it to the caller */
         *WaitHandle = UpdateVdmEntry->WaitObjectForParent;
     }
-#endif
+
     /* We made it */
     return TRUE;
 }
@@ -165,11 +610,10 @@ WINAPI
 BaseCheckForVDM(IN HANDLE ProcessHandle,
                 OUT LPDWORD ExitCode)
 {
-#if 0 // Unimplemented in BASESRV
     NTSTATUS Status;
     EVENT_BASIC_INFORMATION EventBasicInfo;
     BASE_API_MESSAGE ApiMessage;
-    PBASE_GET_VDM_EXIT_CODE GetVdmExitCode = &ApiMessage.Data.GetVdmExitCode;
+    PBASE_GET_VDM_EXIT_CODE GetVdmExitCode = &ApiMessage.Data.GetVDMExitCodeRequest;
 
     /* It's VDM if the process is actually a wait handle (an event) */
     Status = NtQueryEvent(ProcessHandle,
@@ -187,12 +631,11 @@ BaseCheckForVDM(IN HANDLE ProcessHandle,
     Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
                                  NULL,
                                  CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetVDMExitCode),
-                                 sizeof(BASE_GET_VDM_EXIT_CODE));
+                                 sizeof(*GetVdmExitCode));
     if (!NT_SUCCESS(Status)) return FALSE;
 
     /* Get the exit code from the reply */
     *ExitCode = GetVdmExitCode->ExitCode;
-#endif
     return TRUE;
 }
 
@@ -232,7 +675,7 @@ BaseGetVdmConfigInfo(IN LPCWSTR CommandLineReserved,
          * %s%c  : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
          */
         _snwprintf(CommandLine,
-                   sizeof(CommandLine) / sizeof(CommandLine[0]),
+                   ARRAYSIZE(CommandLine),
                    L"\"%s\\ntvdm.exe\" -i%lx %s%c",
                    Buffer,
                    DosSeqId,
@@ -246,7 +689,7 @@ BaseGetVdmConfigInfo(IN LPCWSTR CommandLineReserved,
          * %s%c  : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
          */
         _snwprintf(CommandLine,
-                   sizeof(CommandLine) / sizeof(CommandLine[0]),
+                   ARRAYSIZE(CommandLine),
                    L"\"%s\\ntvdm.exe\" %s%c",
                    Buffer,
                    (BinaryType == BINARY_TYPE_DOS) ? L" " : L"-w",
@@ -257,26 +700,27 @@ BaseGetVdmConfigInfo(IN LPCWSTR CommandLineReserved,
     return RtlCreateUnicodeString(CmdLineString, CommandLine);
 }
 
-UINT
+ENV_NAME_TYPE
 WINAPI
 BaseGetEnvNameType_U(IN PWCHAR Name,
                      IN ULONG NameLength)
 {
     PENV_INFO EnvInfo;
-    ULONG NameType, i;
+    ENV_NAME_TYPE NameType;
+    ULONG i;
 
-    /* Start by assuming unknown type */
-    NameType = 1;
+    /* Start by assuming the environment variable doesn't describe paths */
+    NameType = EnvNameNotAPath;
 
     /* Loop all the environment names */
-    for (i = 0; i < (sizeof(BasepEnvNameType) / sizeof(ENV_INFO)); i++)
+    for (i = 0; i < ARRAYSIZE(BasepEnvNameType); i++)
     {
         /* Get this entry */
         EnvInfo = &BasepEnvNameType[i];
 
         /* Check if it matches the name */
         if ((EnvInfo->NameLength == NameLength) &&
-            !(_wcsnicmp(EnvInfo->Name, Name, NameLength)))
+            (_wcsnicmp(EnvInfo->Name, Name, NameLength) == 0))
         {
             /* It does, return the type */
             NameType = EnvInfo->NameType;
@@ -284,47 +728,32 @@ BaseGetEnvNameType_U(IN PWCHAR Name,
         }
     }
 
-    /* Return what we found, or unknown if nothing */
     return NameType;
 }
 
 BOOL
 NTAPI
-BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv,
-                          IN PUNICODE_STRING UnicodeEnv)
+BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
+                         OUT PANSI_STRING AnsiEnv,
+                         OUT PUNICODE_STRING UnicodeEnv)
 {
-    ULONG Dummy = 0;
-
-    /* Clear the ASCII buffer since Rtl creates this for us */
-    if (AnsiEnv->Buffer) RtlFreeAnsiString(AnsiEnv);
-
-    /* The Unicode buffer is build by hand, though */
-    if (UnicodeEnv->Buffer)
-    {
-        /* So clear it through the API */
-        NtFreeVirtualMemory(NtCurrentProcess(),
-                            (PVOID*)&UnicodeEnv->Buffer,
-                            &Dummy,
-                            MEM_RELEASE);
-    }
+#define IS_ALPHA(x)   \
+    ( ((x) >= L'A' && (x) <= L'Z') || ((x) >= L'a' && (x) <= L'z') )
 
-    /* All done */
-    return TRUE;
-}
+// From lib/rtl/path.c :
+// Can be put in some .h ??
+#define IS_PATH_SEPARATOR(x)    ((x) == L'\\' || (x) == L'/')
 
-BOOL
-NTAPI
-BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
-                         IN PANSI_STRING AnsiEnv,
-                         IN PUNICODE_STRING UnicodeEnv)
-{
-    BOOL Result;
-    ULONG RegionSize, EnvironmentSize = 0;
-    PWCHAR p, Environment, NewEnvironment = NULL;
+    BOOL Success = FALSE;
     NTSTATUS Status;
+    ULONG RegionSize, EnvironmentSize = 0;
+    PWCHAR Environment, NewEnvironment = NULL;
+    ENV_NAME_TYPE NameType;
+    ULONG NameLength, NumChars, Remaining;
+    PWCHAR SourcePtr, DestPtr, StartPtr;
 
     /* Make sure we have both strings */
-    if (!(AnsiEnv) || !(UnicodeEnv))
+    if (!AnsiEnv || !UnicodeEnv)
     {
         /* Fail */
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -335,8 +764,8 @@ BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
     if (!lpEnvironment)
     {
         /* Nope, create one */
-        Status = RtlCreateEnvironment(TRUE, (PWCHAR*)&Environment);
-        if (!NT_SUCCESS(Status)) goto Quickie;
+        Status = RtlCreateEnvironment(TRUE, &Environment);
+        if (!NT_SUCCESS(Status)) goto Cleanup;
     }
     else
     {
@@ -347,45 +776,213 @@ BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
     /* Do we have something now ? */
     if (!Environment)
     {
-        /* Still not, fail out */
+        /* Still not, bail out */
         SetLastError(ERROR_BAD_ENVIRONMENT);
-        goto Quickie;
+        goto Cleanup;
     }
 
-    /* Count how much space the whole environment takes */
-    p = Environment;
-    while ((*p++ != UNICODE_NULL) && (*p != UNICODE_NULL)) EnvironmentSize++;
-    EnvironmentSize += sizeof(UNICODE_NULL);
-
-    /* Allocate a new copy */
+    /*
+     * Count how much space the whole environment takes. The environment block is
+     * doubly NULL-terminated (NULL from last string and final NULL terminator).
+     */
+    SourcePtr = Environment;
+    while (!(*SourcePtr++ == UNICODE_NULL && *SourcePtr == UNICODE_NULL))
+        ++EnvironmentSize;
+    EnvironmentSize += 2; // Add the two terminating NULLs
+
+    /*
+     * Allocate a new copy large enough to hold all the environment with paths
+     * in their short form. Since the short form of a path can be a bit longer
+     * than its long form, for example in the case where characters that are
+     * invalid in the 8.3 representation are present in the long path name:
+     *   'C:\\a+b' --> 'C:\\A_B~1', or:
+     *   'C:\\a b' --> 'C:\\AB2761~1' (with checksum inserted),
+     * we suppose that the possible total number of extra characters needed to
+     * convert the long paths into their short form is at most equal to MAX_PATH.
+     */
     RegionSize = (EnvironmentSize + MAX_PATH) * sizeof(WCHAR);
-    if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),
-                                            (PVOID*)&NewEnvironment,
-                                            0,
-                                            &RegionSize,
-                                            MEM_COMMIT,
-                                            PAGE_READWRITE)))
+    Status = NtAllocateVirtualMemory(NtCurrentProcess(),
+                                     (PVOID*)&NewEnvironment,
+                                     0,
+                                     &RegionSize,
+                                     MEM_COMMIT,
+                                     PAGE_READWRITE);
+    if (!NT_SUCCESS(Status))
     {
         /* We failed, bail out */
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         NewEnvironment = NULL;
-        goto Quickie;
+        goto Cleanup;
     }
 
-    /* Begin parsing the new environment */
-    p = NewEnvironment;
+    /* Parse the environment block */
+    Remaining = MAX_PATH - 2; // '-2': remove the last two NULLs. FIXME: is it really needed??
+    SourcePtr = Environment;
+    DestPtr   = NewEnvironment;
+
+    /* Loop through all the environment strings */
+    while (*SourcePtr != UNICODE_NULL)
+    {
+        /*
+         * 1. Check the type of the environment variable and copy its name.
+         */
+
+        /* Regular environment variable */
+        if (*SourcePtr != L'=')
+        {
+            StartPtr = SourcePtr;
+
+            /* Copy the environment variable name, including the '=' */
+            while (*SourcePtr != UNICODE_NULL)
+            {
+                *DestPtr++ = *SourcePtr;
+                if (*SourcePtr++ == L'=') break;
+            }
 
-    /* FIXME: Code here */
+            /* Guess the type of the environment variable */
+            NameType = BaseGetEnvNameType_U(StartPtr, SourcePtr - StartPtr - 1);
+        }
+        /* 'Current directory' environment variable (i.e. of '=X:=' form) */
+        else // if (*SourcePtr == L'=')
+        {
+            /* First assume we have a possibly malformed environment variable */
+            NameType = EnvNameNotAPath;
+
+            /* Check for a valid 'Current directory' environment variable */
+            if (IS_ALPHA(SourcePtr[1]) && SourcePtr[2] == L':' && SourcePtr[3] == L'=')
+            {
+                /*
+                 * Small optimization: convert the path to short form only if
+                 * the current directory is not the root directory (i.e. not
+                 * of the '=X:=Y:\' form), otherwise just do a simple copy.
+                 */
+                if ( wcslen(SourcePtr) >= ARRAYSIZE("=X:=Y:\\")-1 &&
+                     !( IS_ALPHA(SourcePtr[4]) && SourcePtr[5] == L':' &&
+                        IS_PATH_SEPARATOR(SourcePtr[6]) && SourcePtr[7] == UNICODE_NULL ) )
+                {
+                    NameType = EnvNameSinglePath;
+
+                    /* Copy the '=X:=' prefix */
+                    *DestPtr++ = SourcePtr[0];
+                    *DestPtr++ = SourcePtr[1];
+                    *DestPtr++ = SourcePtr[2];
+                    *DestPtr++ = SourcePtr[3];
+                    SourcePtr += 4;
+                }
+            }
+            else
+            {
+                /*
+                 * Invalid stuff starting with '=', i.e.:
+                 * =? (with '?' not being a letter)
+                 * =X??? (with '?' not being ":=" and not followed by something longer than 3 characters)
+                 * =X:=??? (with '?' not being "X:\\")
+                 *
+                 * 'NameType' is already set to 'EnvNameNotAPath'.
+                 */
+            }
+        }
+
+
+        /*
+         * 2. Copy the environment value and perform conversions accordingly.
+         */
+
+        if (NameType == EnvNameNotAPath)
+        {
+            /* Copy everything, including the NULL terminator */
+            do
+            {
+                *DestPtr++ = *SourcePtr;
+            } while (*SourcePtr++ != UNICODE_NULL);
+        }
+        else if (NameType == EnvNameSinglePath)
+        {
+            /* Convert the path to its short form */
+            NameLength = wcslen(SourcePtr);
+            NumChars = GetShortPathNameW(SourcePtr, DestPtr, NameLength + 1 + Remaining);
+            if (NumChars == 0 || NumChars > NameLength + Remaining)
+            {
+                /* If the conversion failed, just copy the original value */
+                RtlCopyMemory(DestPtr, SourcePtr, NameLength * sizeof(WCHAR));
+                NumChars = NameLength;
+            }
+            DestPtr += NumChars;
+            if (NumChars > NameLength)
+                Remaining -= (NumChars - NameLength);
+
+            SourcePtr += NameLength;
+
+            /* Copy the NULL terminator */
+            *DestPtr++ = *SourcePtr++;
+        }
+        else // if (NameType == EnvNameMultiplePath)
+        {
+            WCHAR Delimiter;
+
+            /* Loop through the list of paths (delimited by ';') and convert each path to its short form */
+            do
+            {
+                /* Copy any trailing ';' before going to the next path */
+                while (*SourcePtr == L';')
+                {
+                    *DestPtr++ = *SourcePtr++;
+                }
+
+                StartPtr = SourcePtr;
+
+                /* Find the next path list delimiter or the NULL terminator */
+                while (*SourcePtr != UNICODE_NULL && *SourcePtr != L';')
+                {
+                    ++SourcePtr;
+                }
+                Delimiter = *SourcePtr;
+
+                NameLength = SourcePtr - StartPtr;
+                if (NameLength)
+                {
+                    /*
+                     * Temporarily replace the possible path list delimiter by NULL.
+                     * 'lpEnvironment' must point to a read+write memory buffer!
+                     */
+                    *SourcePtr = UNICODE_NULL;
+
+                    NumChars = GetShortPathNameW(StartPtr, DestPtr, NameLength + 1 + Remaining);
+                    if ( NumChars == 0 ||
+                        (Delimiter == L';' ? NumChars > NameLength + Remaining
+                                           : NumChars > NameLength /* + Remaining ?? */) )
+                    {
+                        /* If the conversion failed, just copy the original value */
+                        RtlCopyMemory(DestPtr, StartPtr, NameLength * sizeof(WCHAR));
+                        NumChars = NameLength;
+                    }
+                    DestPtr += NumChars;
+                    if (NumChars > NameLength)
+                        Remaining -= (NumChars - NameLength);
+
+                    /* If removed, restore the path list delimiter in the source environment value and copy it */
+                    if (Delimiter != UNICODE_NULL)
+                    {
+                        *SourcePtr = Delimiter;
+                        *DestPtr++ = *SourcePtr++;
+                    }
+                }
+            } while (*SourcePtr != UNICODE_NULL);
+
+            /* Copy the NULL terminator */
+            *DestPtr++ = *SourcePtr++;
+        }
+    }
 
-    /* Terminate it */
-    *p++ = UNICODE_NULL;
+    /* NULL-terminate the environment block */
+    *DestPtr++ = UNICODE_NULL;
 
-    /* Initialize the unicode string to hold it */
-    EnvironmentSize = (p - NewEnvironment) * sizeof(WCHAR);
-    RtlInitEmptyUnicodeString(UnicodeEnv, NewEnvironment, (USHORT)EnvironmentSize);
-    UnicodeEnv->Length = (USHORT)EnvironmentSize;
+    /* Initialize the Unicode string to hold it */
+    RtlInitEmptyUnicodeString(UnicodeEnv, NewEnvironment,
+                              (DestPtr - NewEnvironment) * sizeof(WCHAR));
+    UnicodeEnv->Length = UnicodeEnv->MaximumLength;
 
-    /* Create the ASCII version of it */
+    /* Create its ANSI version */
     Status = RtlUnicodeStringToAnsiString(AnsiEnv, UnicodeEnv, TRUE);
     if (!NT_SUCCESS(Status))
     {
@@ -395,13 +992,13 @@ BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
     else
     {
         /* Everything went okay, so return success */
-        Result = TRUE;
+        Success = TRUE;
         NewEnvironment = NULL;
     }
 
-Quickie:
-    /* Cleanup path starts here, start by destroying the envrionment copy */
-    if (!(lpEnvironment) && (Environment)) RtlDestroyEnvironment(Environment);
+Cleanup:
+    /* Cleanup path starts here, start by destroying the environment copy */
+    if (!lpEnvironment && Environment) RtlDestroyEnvironment(Environment);
 
     /* See if we are here due to failure */
     if (NewEnvironment)
@@ -420,7 +1017,31 @@ Quickie:
     }
 
     /* Return the result */
-    return Result;
+    return Success;
+}
+
+BOOL
+NTAPI
+BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv,
+                          IN PUNICODE_STRING UnicodeEnv)
+{
+    ULONG Dummy = 0;
+
+    /* Clear the ANSI buffer since Rtl creates this for us */
+    if (AnsiEnv->Buffer) RtlFreeAnsiString(AnsiEnv);
+
+    /* The Unicode buffer is build by hand, though */
+    if (UnicodeEnv->Buffer)
+    {
+        /* So clear it through the API */
+        NtFreeVirtualMemory(NtCurrentProcess(),
+                            (PVOID*)&UnicodeEnv->Buffer,
+                            &Dummy,
+                            MEM_RELEASE);
+    }
+
+    /* All done */
+    return TRUE;
 }
 
 
@@ -525,7 +1146,7 @@ InternalGetBinaryType(HANDLE hFile)
     return BINARY_UNKNOWN;
   }
 
-  /* Mach-o File with Endian set to Big Endian  or Little Endian*/
+  /* Mach-o File with Endian set to Big Endian or Little Endian*/
   if(Header.macho.magic == 0xFEEDFACE ||
      Header.macho.magic == 0xCEFAEDFE)
   {
@@ -574,7 +1195,7 @@ InternalGetBinaryType(HANDLE hFile)
       return BINARY_PE_EXE32;
     }
 
-    if(!memcmp(magic, "NE", 1))
+    if(!memcmp(magic, "NE", 2))
     {
       /* This is a Windows executable (NE) header.  This can
        * mean either a 16-bit OS/2 or a 16-bit Windows or even a
@@ -755,70 +1376,534 @@ CmdBatNotification (
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
-DWORD
+VOID
 WINAPI
-ExitVDM (
-    DWORD   Unknown0,
-    DWORD   Unknown1
-    )
+ExitVDM(BOOL IsWow, ULONG iWowTask)
 {
-    STUB;
-    return 0;
-}
+    BASE_API_MESSAGE ApiMessage;
+    PBASE_EXIT_VDM ExitVdm = &ApiMessage.Data.ExitVDMRequest;
+
+    /* Setup the input parameters */
+    ExitVdm->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
+    ExitVdm->iWowTask = IsWow ? iWowTask : 0; /* Always zero for DOS tasks */
+    ExitVdm->WaitObjectForVDM = NULL;
 
+    /* Call CSRSS */
+    CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+                        NULL,
+                        CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitVDM),
+                        sizeof(*ExitVdm));
+
+    /* Close the returned wait object handle, if any */
+    if (NT_SUCCESS(ApiMessage.Status) && (ExitVdm->WaitObjectForVDM != NULL))
+    {
+        CloseHandle(ExitVdm->WaitObjectForVDM);
+    }
+}
 
 /*
- * @unimplemented
+ * @implemented
  */
-DWORD
+BOOL
 WINAPI
-GetNextVDMCommand (
-    DWORD   Unknown0
-    )
+GetNextVDMCommand(PVDM_COMMAND_INFO CommandData)
 {
-    STUB;
-    return 0;
+    BOOL Success = FALSE;
+    NTSTATUS Status;
+    BASE_API_MESSAGE ApiMessage;
+    PBASE_GET_NEXT_VDM_COMMAND GetNextVdmCommand = &ApiMessage.Data.GetNextVDMCommandRequest;
+    PBASE_IS_FIRST_VDM IsFirstVdm = &ApiMessage.Data.IsFirstVDMRequest;
+    PBASE_SET_REENTER_COUNT SetReenterCount = &ApiMessage.Data.SetReenterCountRequest;
+    PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
+    ULONG NumStrings = 0;
+
+    /*
+     * Special case to test whether the VDM is the first one.
+     */
+    if (CommandData == NULL)
+    {
+        /* Call CSRSS */
+        CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+                            NULL,
+                            CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepIsFirstVDM),
+                            sizeof(*IsFirstVdm));
+        if (!NT_SUCCESS(ApiMessage.Status))
+        {
+            BaseSetLastNTError(ApiMessage.Status);
+            return FALSE;
+        }
+
+        /* Return TRUE if this is the first VDM */
+        return IsFirstVdm->FirstVDM;
+    }
+
+    /* CommandData != NULL */
+
+    /*
+     * Special case to increment or decrement the reentrancy count.
+     */
+    if ((CommandData->VDMState == VDM_INC_REENTER_COUNT) ||
+        (CommandData->VDMState == VDM_DEC_REENTER_COUNT))
+    {
+        /* Setup the input parameters */
+        SetReenterCount->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
+        SetReenterCount->fIncDec = CommandData->VDMState;
+
+        /* Call CSRSS */
+        CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+                            NULL,
+                            CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetReenterCount),
+                            sizeof(*SetReenterCount));
+        if (!NT_SUCCESS(ApiMessage.Status))
+        {
+            BaseSetLastNTError(ApiMessage.Status);
+            return FALSE;
+        }
+
+        return TRUE;
+    }
+
+    /*
+     * TODO!
+     * Special case to retrieve or set WOW information.
+     */
+    // TODO: if CommandData->VDMState & (VDM_LIST_WOW_PROCESSES | VDM_LIST_WOW_TASKS | VDM_ADD_WOW_TASK)
+    // then call BasepGetNextVDMCommand in a simpler way!
+
+    /*
+     * Regular case.
+     */
+
+    /* Clear the structure */
+    RtlZeroMemory(GetNextVdmCommand, sizeof(*GetNextVdmCommand));
+
+    /* Setup the input parameters */
+    GetNextVdmCommand->iTask = CommandData->TaskId;
+    GetNextVdmCommand->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
+    GetNextVdmCommand->CmdLen = CommandData->CmdLen;
+    GetNextVdmCommand->AppLen = CommandData->AppLen;
+    GetNextVdmCommand->PifLen = CommandData->PifLen;
+    GetNextVdmCommand->CurDirectoryLen = CommandData->CurDirectoryLen;
+    GetNextVdmCommand->EnvLen = CommandData->EnvLen;
+    GetNextVdmCommand->DesktopLen = CommandData->DesktopLen;
+    GetNextVdmCommand->TitleLen = CommandData->TitleLen;
+    GetNextVdmCommand->ReservedLen = CommandData->ReservedLen;
+    GetNextVdmCommand->VDMState = CommandData->VDMState;
+
+    /* Count the number of strings */
+    if (CommandData->CmdLen) NumStrings++;
+    if (CommandData->AppLen) NumStrings++;
+    if (CommandData->PifLen) NumStrings++;
+    if (CommandData->CurDirectoryLen) NumStrings++;
+    if (CommandData->EnvLen) NumStrings++;
+    if (CommandData->DesktopLen) NumStrings++;
+    if (CommandData->TitleLen) NumStrings++;
+    if (CommandData->ReservedLen) NumStrings++;
+
+    /* Allocate the capture buffer */
+    CaptureBuffer = CsrAllocateCaptureBuffer(NumStrings + 1,
+                                             GetNextVdmCommand->CmdLen
+                                             + GetNextVdmCommand->AppLen
+                                             + GetNextVdmCommand->PifLen
+                                             + GetNextVdmCommand->CurDirectoryLen
+                                             + GetNextVdmCommand->EnvLen
+                                             + GetNextVdmCommand->DesktopLen
+                                             + GetNextVdmCommand->TitleLen
+                                             + GetNextVdmCommand->ReservedLen
+                                             + sizeof(*GetNextVdmCommand->StartupInfo));
+    if (CaptureBuffer == NULL)
+    {
+        BaseSetLastNTError(STATUS_NO_MEMORY);
+        goto Cleanup;
+    }
+
+    /* Capture the data */
+
+    CsrAllocateMessagePointer(CaptureBuffer,
+                              sizeof(*GetNextVdmCommand->StartupInfo),
+                              (PVOID*)&GetNextVdmCommand->StartupInfo);
+
+    if (CommandData->CmdLen)
+    {
+        CsrAllocateMessagePointer(CaptureBuffer,
+                                  CommandData->CmdLen,
+                                  (PVOID*)&GetNextVdmCommand->CmdLine);
+    }
+
+    if (CommandData->AppLen)
+    {
+        CsrAllocateMessagePointer(CaptureBuffer,
+                                  CommandData->AppLen,
+                                  (PVOID*)&GetNextVdmCommand->AppName);
+    }
+
+    if (CommandData->PifLen)
+    {
+        CsrAllocateMessagePointer(CaptureBuffer,
+                                  CommandData->PifLen,
+                                  (PVOID*)&GetNextVdmCommand->PifFile);
+    }
+
+    if (CommandData->CurDirectoryLen)
+    {
+        CsrAllocateMessagePointer(CaptureBuffer,
+                                  CommandData->CurDirectoryLen,
+                                  (PVOID*)&GetNextVdmCommand->CurDirectory);
+    }
+
+    if (CommandData->EnvLen)
+    {
+        CsrAllocateMessagePointer(CaptureBuffer,
+                                  CommandData->EnvLen,
+                                  (PVOID*)&GetNextVdmCommand->Env);
+    }
+
+    if (CommandData->DesktopLen)
+    {
+        CsrAllocateMessagePointer(CaptureBuffer,
+                                  CommandData->DesktopLen,
+                                  (PVOID*)&GetNextVdmCommand->Desktop);
+    }
+
+    if (CommandData->TitleLen)
+    {
+        CsrAllocateMessagePointer(CaptureBuffer,
+                                  CommandData->TitleLen,
+                                  (PVOID*)&GetNextVdmCommand->Title);
+    }
+
+    if (CommandData->ReservedLen)
+    {
+        CsrAllocateMessagePointer(CaptureBuffer,
+                                  CommandData->ReservedLen,
+                                  (PVOID*)&GetNextVdmCommand->Reserved);
+    }
+
+    while (TRUE)
+    {
+        /* Call CSRSS */
+        Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+                                     CaptureBuffer,
+                                     CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetNextVDMCommand),
+                                     sizeof(*GetNextVdmCommand));
+
+        /* Exit the waiting loop if we did not receive any event handle */
+        if (GetNextVdmCommand->WaitObjectForVDM == NULL)
+            break;
+
+        /* Wait for the event to become signaled and try again */
+        Status = NtWaitForSingleObject(GetNextVdmCommand->WaitObjectForVDM,
+                                       FALSE, NULL);
+        if (Status != STATUS_SUCCESS)
+        {
+            /* Fail if we timed out, or if some other error happened */
+            BaseSetLastNTError(Status);
+            goto Cleanup;
+        }
+
+        /* Set the retry flag, clear the exit code, and retry a query */
+        GetNextVdmCommand->VDMState |= VDM_FLAG_RETRY;
+        GetNextVdmCommand->ExitCode = 0;
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        if (Status == STATUS_INVALID_PARAMETER)
+        {
+            /*
+             * One of the buffer lengths was less than required. Store the correct ones.
+             * Note that the status code is not STATUS_BUFFER_TOO_SMALL as one would expect,
+             * in order to keep compatibility with Windows 2003 BASESRV.DLL.
+             */
+            CommandData->CmdLen = GetNextVdmCommand->CmdLen;
+            CommandData->AppLen = GetNextVdmCommand->AppLen;
+            CommandData->PifLen = GetNextVdmCommand->PifLen;
+            CommandData->CurDirectoryLen = GetNextVdmCommand->CurDirectoryLen;
+            CommandData->EnvLen      = GetNextVdmCommand->EnvLen;
+            CommandData->DesktopLen  = GetNextVdmCommand->DesktopLen;
+            CommandData->TitleLen    = GetNextVdmCommand->TitleLen;
+            CommandData->ReservedLen = GetNextVdmCommand->ReservedLen;
+        }
+        else
+        {
+            /* Any other failure */
+            CommandData->CmdLen = 0;
+            CommandData->AppLen = 0;
+            CommandData->PifLen = 0;
+            CommandData->CurDirectoryLen = 0;
+            CommandData->EnvLen      = 0;
+            CommandData->DesktopLen  = 0;
+            CommandData->TitleLen    = 0;
+            CommandData->ReservedLen = 0;
+        }
+
+        BaseSetLastNTError(Status);
+        goto Cleanup;
+    }
+
+    /* Write back the standard handles */
+    CommandData->StdIn  = GetNextVdmCommand->StdIn;
+    CommandData->StdOut = GetNextVdmCommand->StdOut;
+    CommandData->StdErr = GetNextVdmCommand->StdErr;
+
+    /* Write back the startup info */
+    RtlMoveMemory(&CommandData->StartupInfo,
+                  GetNextVdmCommand->StartupInfo,
+                  sizeof(*GetNextVdmCommand->StartupInfo));
+
+    if (CommandData->CmdLen)
+    {
+        /* Write back the command line */
+        RtlMoveMemory(CommandData->CmdLine,
+                      GetNextVdmCommand->CmdLine,
+                      GetNextVdmCommand->CmdLen);
+
+        /* Set the actual length */
+        CommandData->CmdLen = GetNextVdmCommand->CmdLen;
+    }
+
+    if (CommandData->AppLen)
+    {
+        /* Write back the application name */
+        RtlMoveMemory(CommandData->AppName,
+                      GetNextVdmCommand->AppName,
+                      GetNextVdmCommand->AppLen);
+
+        /* Set the actual length */
+        CommandData->AppLen = GetNextVdmCommand->AppLen;
+    }
+
+    if (CommandData->PifLen)
+    {
+        /* Write back the PIF file name */
+        RtlMoveMemory(CommandData->PifFile,
+                      GetNextVdmCommand->PifFile,
+                      GetNextVdmCommand->PifLen);
+
+        /* Set the actual length */
+        CommandData->PifLen = GetNextVdmCommand->PifLen;
+    }
+
+    if (CommandData->CurDirectoryLen)
+    {
+        /* Write back the current directory */
+        RtlMoveMemory(CommandData->CurDirectory,
+                      GetNextVdmCommand->CurDirectory,
+                      GetNextVdmCommand->CurDirectoryLen);
+
+        /* Set the actual length */
+        CommandData->CurDirectoryLen = GetNextVdmCommand->CurDirectoryLen;
+    }
+
+    if (CommandData->EnvLen)
+    {
+        /* Write back the environment */
+        RtlMoveMemory(CommandData->Env,
+                      GetNextVdmCommand->Env,
+                      GetNextVdmCommand->EnvLen);
+
+        /* Set the actual length */
+        CommandData->EnvLen = GetNextVdmCommand->EnvLen;
+    }
+
+    if (CommandData->DesktopLen)
+    {
+        /* Write back the desktop name */
+        RtlMoveMemory(CommandData->Desktop,
+                      GetNextVdmCommand->Desktop,
+                      GetNextVdmCommand->DesktopLen);
+
+        /* Set the actual length */
+        CommandData->DesktopLen = GetNextVdmCommand->DesktopLen;
+    }
+
+    if (CommandData->TitleLen)
+    {
+        /* Write back the title */
+        RtlMoveMemory(CommandData->Title,
+                      GetNextVdmCommand->Title,
+                      GetNextVdmCommand->TitleLen);
+
+        /* Set the actual length */
+        CommandData->TitleLen = GetNextVdmCommand->TitleLen;
+    }
+
+    if (CommandData->ReservedLen)
+    {
+        /* Write back the reserved parameter */
+        RtlMoveMemory(CommandData->Reserved,
+                      GetNextVdmCommand->Reserved,
+                      GetNextVdmCommand->ReservedLen);
+
+        /* Set the actual length */
+        CommandData->ReservedLen = GetNextVdmCommand->ReservedLen;
+    }
+
+    /* Write the remaining output parameters */
+    CommandData->TaskId        = GetNextVdmCommand->iTask;
+    CommandData->CreationFlags = GetNextVdmCommand->dwCreationFlags;
+    CommandData->CodePage      = GetNextVdmCommand->CodePage;
+    CommandData->ExitCode      = GetNextVdmCommand->ExitCode;
+    CommandData->CurrentDrive  = GetNextVdmCommand->CurrentDrive;
+    CommandData->VDMState      = GetNextVdmCommand->VDMState;
+    CommandData->ComingFromBat = GetNextVdmCommand->fComingFromBat;
+
+    /* It was successful */
+    Success = TRUE;
+
+Cleanup:
+    if (CaptureBuffer != NULL) CsrFreeCaptureBuffer(CaptureBuffer); 
+    return Success;
 }
 
 
 /*
- * @unimplemented
+ * @implemented
  */
 DWORD
 WINAPI
-GetVDMCurrentDirectories (
-    DWORD   Unknown0,
-    DWORD   Unknown1
-    )
+GetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs)
 {
-    STUB;
-    return 0;
+    BASE_API_MESSAGE ApiMessage;
+    PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest;
+    PCSR_CAPTURE_BUFFER CaptureBuffer;
+
+    /* Allocate the capture buffer */
+    CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs);
+    if (CaptureBuffer == NULL)
+    {
+        BaseSetLastNTError(STATUS_NO_MEMORY);
+        return 0;
+    }
+
+    /* Setup the input parameters */
+    VDMCurrentDirsRequest->cchCurDirs = cchCurDirs;
+    CsrAllocateMessagePointer(CaptureBuffer,
+                              cchCurDirs,
+                              (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs);
+
+    /* Call CSRSS */
+    CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+                        CaptureBuffer,
+                        CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetVDMCurDirs),
+                        sizeof(*VDMCurrentDirsRequest));
+
+    /* Set the last error */
+    BaseSetLastNTError(ApiMessage.Status);
+
+    if (NT_SUCCESS(ApiMessage.Status))
+    {
+        /* Copy the result */
+        RtlMoveMemory(lpszzCurDirs, VDMCurrentDirsRequest->lpszzCurDirs, cchCurDirs);
+    }
+
+    /* Free the capture buffer */
+    CsrFreeCaptureBuffer(CaptureBuffer);
+
+    /* Return the size if it was successful, or if the buffer was too small */
+    return (NT_SUCCESS(ApiMessage.Status) || (ApiMessage.Status == STATUS_BUFFER_TOO_SMALL))
+           ? VDMCurrentDirsRequest->cchCurDirs : 0;
 }
 
 
 /*
- * @unimplemented
+ * @implemented (undocumented)
  */
 BOOL
 WINAPI
-RegisterConsoleVDM (
-    DWORD   Unknown0,
-    DWORD   Unknown1,
-    DWORD   Unknown2,
-    DWORD   Unknown3,
-    DWORD   Unknown4,
-    DWORD   Unknown5,
-    DWORD   Unknown6,
-    DWORD   Unknown7,
-    DWORD   Unknown8,
-    DWORD   Unknown9,
-    DWORD   Unknown10
-    )
+RegisterConsoleVDM(IN DWORD dwRegisterFlags,
+                   IN HANDLE hStartHardwareEvent,
+                   IN HANDLE hEndHardwareEvent,
+                   IN HANDLE hErrorHardwareEvent,
+                   IN DWORD dwUnusedVar,
+                   OUT LPDWORD lpVideoStateLength,
+                   OUT PVOID* lpVideoState, // PVIDEO_HARDWARE_STATE_HEADER*
+                   IN PVOID lpUnusedBuffer,
+                   IN DWORD dwUnusedBufferLength,
+                   IN COORD dwVDMBufferSize,
+                   OUT PVOID* lpVDMBuffer)
 {
-    STUB;
-    return FALSE;
+    BOOL Success;
+    CONSOLE_API_MESSAGE ApiMessage;
+    PCONSOLE_REGISTERVDM RegisterVDMRequest = &ApiMessage.Data.RegisterVDMRequest;
+    PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
+
+    /* Set up the data to send to the Console Server */
+    RegisterVDMRequest->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
+    RegisterVDMRequest->RegisterFlags = dwRegisterFlags;
+
+    if (dwRegisterFlags != 0)
+    {
+        RegisterVDMRequest->StartHardwareEvent = hStartHardwareEvent;
+        RegisterVDMRequest->EndHardwareEvent   = hEndHardwareEvent;
+        RegisterVDMRequest->ErrorHardwareEvent = hErrorHardwareEvent;
+
+        RegisterVDMRequest->VDMBufferSize = dwVDMBufferSize;
+
+#if 0
+        RegisterVDMRequest->UnusedBufferLength = dwUnusedBufferLength;
+
+        /* Allocate a Capture Buffer */
+        CaptureBuffer = CsrAllocateCaptureBuffer(1, dwUnusedBufferLength);
+        if (CaptureBuffer == NULL)
+        {
+            DPRINT1("CsrAllocateCaptureBuffer failed!\n");
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            return FALSE;
+        }
+
+        /* Capture the buffer to write */
+        CsrCaptureMessageBuffer(CaptureBuffer,
+                                (PVOID)lpUnusedBuffer,
+                                dwUnusedBufferLength,
+                                (PVOID*)&RegisterVDMRequest->UnusedBuffer);
+#endif
+    }
+    else
+    {
+        // CaptureBuffer = NULL;
+    }
+
+    /* Call the server */
+    CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+                        CaptureBuffer,
+                        CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepRegisterVDM),
+                        sizeof(*RegisterVDMRequest));
+
+    /* Check for success */
+    Success = NT_SUCCESS(ApiMessage.Status);
+
+    /* Release the capture buffer if needed */
+    if (CaptureBuffer) CsrFreeCaptureBuffer(CaptureBuffer);
+
+    /* Retrieve the results */
+    if (Success)
+    {
+        if (dwRegisterFlags != 0)
+        {
+            _SEH2_TRY
+            {
+                *lpVideoStateLength = RegisterVDMRequest->VideoStateLength;
+                *lpVideoState       = RegisterVDMRequest->VideoState;
+                *lpVDMBuffer        = RegisterVDMRequest->VDMBuffer;
+            }
+            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+            {
+                SetLastError(ERROR_INVALID_ACCESS);
+                Success = FALSE;
+            }
+            _SEH2_END;
+        }
+    }
+    else
+    {
+        BaseSetLastNTError(ApiMessage.Status);
+    }
+
+    /* Return success status */
+    return Success;
 }
 
 
@@ -851,17 +1936,44 @@ RegisterWowExec (
 
 
 /*
- * @unimplemented
+ * @implemented
  */
 BOOL
 WINAPI
-SetVDMCurrentDirectories (
-    DWORD   Unknown0,
-    DWORD   Unknown1
-    )
+SetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs)
 {
-    STUB;
-    return FALSE;
+    BASE_API_MESSAGE ApiMessage;
+    PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest;
+    PCSR_CAPTURE_BUFFER CaptureBuffer;
+
+    /* Allocate the capture buffer */
+    CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs);
+    if (CaptureBuffer == NULL)
+    {
+        BaseSetLastNTError(STATUS_NO_MEMORY);
+        return FALSE;
+    }
+
+    /* Setup the input parameters */
+    VDMCurrentDirsRequest->cchCurDirs = cchCurDirs;
+    CsrCaptureMessageBuffer(CaptureBuffer,
+                            lpszzCurDirs,
+                            cchCurDirs,
+                            (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs);
+
+    /* Call CSRSS */
+    CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+                        CaptureBuffer,
+                        CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetVDMCurDirs),
+                        sizeof(*VDMCurrentDirsRequest));
+
+    /* Free the capture buffer */
+    CsrFreeCaptureBuffer(CaptureBuffer);
+
+    /* Set the last error */
+    BaseSetLastNTError(ApiMessage.Status);
+
+    return NT_SUCCESS(ApiMessage.Status) ? TRUE : FALSE;
 }
 
 /*
@@ -882,12 +1994,14 @@ VDMConsoleOperation (
 /*
  * @unimplemented
  */
-DWORD
+BOOL
 WINAPI
-VDMOperationStarted (
-    DWORD   Unknown0
-    )
+VDMOperationStarted(IN ULONG Unknown0)
 {
-    STUB;
-    return 0;
+    DPRINT1("VDMOperationStarted(%d)\n", Unknown0);
+
+    return BaseUpdateVDMEntry(VdmEntryUpdateControlCHandler,
+                              NULL,
+                              0,
+                              Unknown0);
 }