xref: /reactos/dll/win32/kernel32/client/vdm.c (revision c2c66aff)
1 /*
2  * PROJECT:         ReactOS Win32 Base API
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            dll/win32/kernel32/client/vdm.c
5  * PURPOSE:         Virtual DOS Machines (VDM) Support
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <k32.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* TYPES **********************************************************************/
17 
18 #define BINARY_UNKNOWN  (0)
19 #define BINARY_PE_EXE32 (1)
20 #define BINARY_PE_DLL32 (2)
21 #define BINARY_PE_EXE64 (3)
22 #define BINARY_PE_DLL64 (4)
23 #define BINARY_WIN16    (5)
24 #define BINARY_OS216    (6)
25 #define BINARY_DOS      (7)
26 #define BINARY_UNIX_EXE (8)
27 #define BINARY_UNIX_LIB (9)
28 
29 
30 typedef enum _ENV_NAME_TYPE
31 {
32     EnvNameNotAPath = 1,
33     EnvNameSinglePath ,
34     EnvNameMultiplePath
35 } ENV_NAME_TYPE;
36 
37 typedef struct _ENV_INFO
38 {
39     ENV_NAME_TYPE NameType;
40     ULONG  NameLength;
41     PWCHAR Name;
42 } ENV_INFO, *PENV_INFO;
43 
44 /* GLOBALS ********************************************************************/
45 
46 #define ENV_NAME_ENTRY(type, name)  \
47     {(type), _ARRAYSIZE(name) - 1, (name)}
48 
49 static ENV_INFO BasepEnvNameType[] =
50 {
51     ENV_NAME_ENTRY(EnvNameMultiplePath, L"PATH"),
52     ENV_NAME_ENTRY(EnvNameSinglePath  , L"WINDIR"),
53     ENV_NAME_ENTRY(EnvNameSinglePath  , L"SYSTEMROOT"),
54     ENV_NAME_ENTRY(EnvNameMultiplePath, L"TEMP"),
55     ENV_NAME_ENTRY(EnvNameMultiplePath, L"TMP"),
56 };
57 
58 static UNICODE_STRING BaseDotComSuffixName = RTL_CONSTANT_STRING(L".com");
59 static UNICODE_STRING BaseDotPifSuffixName = RTL_CONSTANT_STRING(L".pif");
60 static UNICODE_STRING BaseDotExeSuffixName = RTL_CONSTANT_STRING(L".exe");
61 
62 /* FUNCTIONS ******************************************************************/
63 
64 ULONG
65 WINAPI
66 BaseIsDosApplication(IN PUNICODE_STRING PathName,
67                      IN NTSTATUS Status)
68 {
69     UNICODE_STRING String;
70 
71     /* Is it a .com? */
72     String.Length = BaseDotComSuffixName.Length;
73     String.Buffer = &PathName->Buffer[(PathName->Length - String.Length) / sizeof(WCHAR)];
74     if (RtlEqualUnicodeString(&String, &BaseDotComSuffixName, TRUE)) return BINARY_TYPE_COM;
75 
76     /* Is it a .pif? */
77     String.Length = BaseDotPifSuffixName.Length;
78     String.Buffer = &PathName->Buffer[(PathName->Length - String.Length) / sizeof(WCHAR)];
79     if (RtlEqualUnicodeString(&String, &BaseDotPifSuffixName, TRUE)) return BINARY_TYPE_PIF;
80 
81     /* Is it an exe? */
82     String.Length = BaseDotExeSuffixName.Length;
83     String.Buffer = &PathName->Buffer[(PathName->Length - String.Length) / sizeof(WCHAR)];
84     if (RtlEqualUnicodeString(&String, &BaseDotExeSuffixName, TRUE)) return BINARY_TYPE_EXE;
85 
86     return 0;
87 }
88 
89 NTSTATUS
90 WINAPI
91 BaseCheckVDM(IN ULONG BinaryType,
92              IN PCWCH ApplicationName,
93              IN PCWCH CommandLine,
94              IN PCWCH CurrentDirectory,
95              IN PANSI_STRING AnsiEnvironment,
96              IN PBASE_API_MESSAGE ApiMessage,
97              IN OUT PULONG iTask,
98              IN DWORD CreationFlags,
99              IN LPSTARTUPINFOW StartupInfo,
100              IN HANDLE hUserToken OPTIONAL)
101 {
102     NTSTATUS Status;
103     PBASE_CHECK_VDM CheckVdm = &ApiMessage->Data.CheckVDMRequest;
104     PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
105     PWCHAR CurrentDir = NULL;
106     PWCHAR ShortAppName = NULL;
107     PWCHAR ShortCurrentDir = NULL;
108     ULONG Length;
109     PCHAR AnsiCmdLine = NULL;
110     PCHAR AnsiAppName = NULL;
111     PCHAR AnsiCurDirectory = NULL;
112     PCHAR AnsiDesktop = NULL;
113     PCHAR AnsiTitle = NULL;
114     PCHAR AnsiReserved = NULL;
115     STARTUPINFOA AnsiStartupInfo;
116     ULONG NumStrings = 5;
117 
118     /* Parameters validation */
119     if (ApplicationName == NULL || CommandLine == NULL)
120     {
121         return STATUS_INVALID_PARAMETER;
122     }
123 
124     /* Trim leading whitespace from ApplicationName */
125     while (*ApplicationName == L' ' || *ApplicationName == L'\t')
126         ++ApplicationName;
127 
128     /* Calculate the size of the short application name */
129     Length = GetShortPathNameW(ApplicationName, NULL, 0);
130     if (Length == 0)
131     {
132         Status = STATUS_OBJECT_PATH_INVALID;
133         goto Cleanup;
134     }
135 
136     /* Allocate memory for the short application name */
137     ShortAppName = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
138                                            HEAP_ZERO_MEMORY,
139                                            Length * sizeof(WCHAR));
140     if (!ShortAppName)
141     {
142         Status = STATUS_NO_MEMORY;
143         goto Cleanup;
144     }
145 
146     /* Get the short application name */
147     if (GetShortPathNameW(ApplicationName, ShortAppName, Length) == 0)
148     {
149         /* Try to determine which error occurred */
150         switch (GetLastError())
151         {
152             case ERROR_NOT_ENOUGH_MEMORY:
153             {
154                 Status = STATUS_NO_MEMORY;
155                 break;
156             }
157 
158             case ERROR_INVALID_PARAMETER:
159             {
160                 Status = STATUS_INVALID_PARAMETER;
161                 break;
162             }
163 
164             default:
165             {
166                 Status = STATUS_OBJECT_PATH_INVALID;
167             }
168         }
169 
170         goto Cleanup;
171     }
172 
173     /* Trim leading whitespace from CommandLine */
174     while (*CommandLine == L' ' || *CommandLine == L'\t')
175         ++CommandLine;
176 
177     /*
178      * CommandLine is usually formatted as: 'ApplicationName param0 ...'.
179      * So we want to strip the first token (ApplicationName) from it.
180      * Two cases are in fact possible:
181      * - either the first token is indeed ApplicationName, so we just skip it;
182      * - or the first token is not exactly ApplicationName, because it happened
183      *   that somebody else already preprocessed CommandLine. Therefore we
184      *   suppose that the first token corresponds to an application name and
185      *   we skip it. Care should be taken when quotes are present in this token.
186      */
187      if (*CommandLine)
188      {
189         /* The first part of CommandLine should be the ApplicationName... */
190         Length = wcslen(ApplicationName);
191         if (Length <= wcslen(CommandLine) &&
192             _wcsnicmp(ApplicationName, CommandLine, Length) == 0)
193         {
194             /* Skip it */
195             CommandLine += Length;
196         }
197         /*
198          * ... but it is not, however we still have a token. We suppose that
199          * it corresponds to some sort of application name, so we skip it too.
200          */
201         else
202         {
203             /* Get rid of the first token. We stop when we see whitespace. */
204             while (*CommandLine && !(*CommandLine == L' ' || *CommandLine == L'\t'))
205             {
206                 if (*CommandLine == L'\"')
207                 {
208                     /* We enter a quoted part, skip it */
209                     ++CommandLine;
210                     while (*CommandLine && *CommandLine++ != L'\"') ;
211                 }
212                 else
213                 {
214                     /* Go to the next character */
215                     ++CommandLine;
216                 }
217             }
218         }
219     }
220 
221     /*
222      * Trim remaining whitespace from CommandLine that may be
223      * present between the application name and the parameters.
224      */
225     while (*CommandLine == L' ' || *CommandLine == L'\t')
226         ++CommandLine;
227 
228     /* Get the current directory */
229     if (CurrentDirectory == NULL)
230     {
231         /* Allocate memory for the current directory path */
232         Length = GetCurrentDirectoryW(0, NULL);
233         CurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
234                                              HEAP_ZERO_MEMORY,
235                                              Length * sizeof(WCHAR));
236         if (CurrentDir == NULL)
237         {
238             Status = STATUS_NO_MEMORY;
239             goto Cleanup;
240         }
241 
242         /* Get the current directory */
243         GetCurrentDirectoryW(Length, CurrentDir);
244         CurrentDirectory = CurrentDir;
245     }
246 
247     /* Calculate the size of the short current directory path */
248     Length = GetShortPathNameW(CurrentDirectory, NULL, 0);
249 
250     /* Allocate memory for the short current directory path */
251     ShortCurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
252                                               HEAP_ZERO_MEMORY,
253                                               Length * sizeof(WCHAR));
254     if (!ShortCurrentDir)
255     {
256         Status = STATUS_NO_MEMORY;
257         goto Cleanup;
258     }
259 
260     /* Get the short current directory path */
261     if (!GetShortPathNameW(CurrentDirectory, ShortCurrentDir, Length))
262     {
263         /* Try to determine which error occurred */
264         switch (GetLastError())
265         {
266             case ERROR_NOT_ENOUGH_MEMORY:
267             {
268                 Status = STATUS_NO_MEMORY;
269                 break;
270             }
271 
272             case ERROR_INVALID_PARAMETER:
273             {
274                 Status = STATUS_INVALID_PARAMETER;
275                 break;
276             }
277 
278             default:
279             {
280                 Status = STATUS_OBJECT_PATH_INVALID;
281             }
282         }
283         goto Cleanup;
284     }
285 
286     /* Setup the input parameters */
287     CheckVdm->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
288     CheckVdm->BinaryType = BinaryType;
289     CheckVdm->CodePage = CP_ACP;
290     CheckVdm->dwCreationFlags = CreationFlags;
291     CheckVdm->CurDrive = CurrentDirectory[0] - L'A';
292     CheckVdm->CmdLen = wcslen(CommandLine) + 1;
293     CheckVdm->AppLen = wcslen(ShortAppName) + 1;
294     CheckVdm->PifLen = 0; // TODO: PIF file support!
295     CheckVdm->CurDirectoryLen = wcslen(ShortCurrentDir) + 1;
296     CheckVdm->EnvLen = AnsiEnvironment->Length;
297     CheckVdm->DesktopLen = (StartupInfo->lpDesktop != NULL) ? (wcslen(StartupInfo->lpDesktop) + 1) : 0;
298     CheckVdm->TitleLen = (StartupInfo->lpTitle != NULL) ? (wcslen(StartupInfo->lpTitle) + 1) : 0;
299     CheckVdm->ReservedLen = (StartupInfo->lpReserved != NULL) ? (wcslen(StartupInfo->lpReserved) + 1) : 0;
300 
301     if (StartupInfo->dwFlags & STARTF_USESTDHANDLES)
302     {
303         /* Set the standard handles */
304         CheckVdm->StdIn  = StartupInfo->hStdInput;
305         CheckVdm->StdOut = StartupInfo->hStdOutput;
306         CheckVdm->StdErr = StartupInfo->hStdError;
307     }
308 
309     /* Allocate memory for the ANSI strings */
310     // We need to add the newline characters '\r\n' to the command line
311     AnsiCmdLine = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CmdLen + 2);
312     AnsiAppName = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->AppLen);
313     AnsiCurDirectory = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CurDirectoryLen);
314     if (StartupInfo->lpDesktop)
315         AnsiDesktop = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
316                                              HEAP_ZERO_MEMORY,
317                                              CheckVdm->DesktopLen);
318     if (StartupInfo->lpTitle)
319         AnsiTitle = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
320                                            HEAP_ZERO_MEMORY,
321                                            CheckVdm->TitleLen);
322     if (StartupInfo->lpReserved)
323         AnsiReserved = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
324                                               HEAP_ZERO_MEMORY,
325                                               CheckVdm->ReservedLen);
326 
327     if (!AnsiCmdLine
328         || !AnsiAppName
329         || !AnsiCurDirectory
330         || (StartupInfo->lpDesktop && !AnsiDesktop)
331         || (StartupInfo->lpTitle && !AnsiTitle)
332         || (StartupInfo->lpReserved && !AnsiReserved))
333     {
334         Status = STATUS_NO_MEMORY;
335         goto Cleanup;
336     }
337 
338     /* Convert the command line into an ANSI string */
339     WideCharToMultiByte(CP_ACP,
340                         0,
341                         CommandLine,
342                         CheckVdm->CmdLen,
343                         AnsiCmdLine,
344                         CheckVdm->CmdLen,
345                         NULL,
346                         NULL);
347     /* Add the needed newline and NULL-terminate */
348     CheckVdm->CmdLen--; // Rewind back to the NULL character
349     AnsiCmdLine[CheckVdm->CmdLen++] = '\r';
350     AnsiCmdLine[CheckVdm->CmdLen++] = '\n';
351     AnsiCmdLine[CheckVdm->CmdLen++] = 0;
352 
353     /* Convert the short application name into an ANSI string */
354     WideCharToMultiByte(CP_ACP,
355                         0,
356                         ShortAppName,
357                         CheckVdm->AppLen,
358                         AnsiAppName,
359                         CheckVdm->AppLen,
360                         NULL,
361                         NULL);
362 
363     /* Convert the short current directory path into an ANSI string */
364     WideCharToMultiByte(CP_ACP,
365                         0,
366                         ShortCurrentDir,
367                         CheckVdm->CurDirectoryLen,
368                         AnsiCurDirectory,
369                         CheckVdm->CurDirectoryLen,
370                         NULL,
371                         NULL);
372 
373     if (StartupInfo->lpDesktop)
374     {
375         /* Convert the desktop name into an ANSI string */
376         WideCharToMultiByte(CP_ACP,
377                             0,
378                             StartupInfo->lpDesktop,
379                             CheckVdm->DesktopLen,
380                             AnsiDesktop,
381                             CheckVdm->DesktopLen,
382                             NULL,
383                             NULL);
384         NumStrings++;
385     }
386 
387     if (StartupInfo->lpTitle)
388     {
389         /* Convert the title into an ANSI string */
390         WideCharToMultiByte(CP_ACP,
391                             0,
392                             StartupInfo->lpTitle,
393                             CheckVdm->TitleLen,
394                             AnsiTitle,
395                             CheckVdm->TitleLen,
396                             NULL,
397                             NULL);
398         NumStrings++;
399     }
400 
401     if (StartupInfo->lpReserved)
402     {
403         /* Convert the reserved value into an ANSI string */
404         WideCharToMultiByte(CP_ACP,
405                             0,
406                             StartupInfo->lpReserved,
407                             CheckVdm->ReservedLen,
408                             AnsiReserved,
409                             CheckVdm->ReservedLen,
410                             NULL,
411                             NULL);
412         NumStrings++;
413     }
414 
415     /* Fill the ANSI startup info structure */
416     RtlCopyMemory(&AnsiStartupInfo, StartupInfo, sizeof(AnsiStartupInfo));
417     AnsiStartupInfo.lpReserved = AnsiReserved;
418     AnsiStartupInfo.lpDesktop = AnsiDesktop;
419     AnsiStartupInfo.lpTitle = AnsiTitle;
420 
421     /* Allocate the capture buffer */
422     CaptureBuffer = CsrAllocateCaptureBuffer(NumStrings,
423                                              CheckVdm->CmdLen
424                                              + CheckVdm->AppLen
425                                              + CheckVdm->PifLen
426                                              + CheckVdm->CurDirectoryLen
427                                              + CheckVdm->DesktopLen
428                                              + CheckVdm->TitleLen
429                                              + CheckVdm->ReservedLen
430                                              + CheckVdm->EnvLen
431                                              + sizeof(*CheckVdm->StartupInfo));
432     if (CaptureBuffer == NULL)
433     {
434         Status = STATUS_NO_MEMORY;
435         goto Cleanup;
436     }
437 
438     /* Capture the command line */
439     CsrCaptureMessageBuffer(CaptureBuffer,
440                             AnsiCmdLine,
441                             CheckVdm->CmdLen,
442                             (PVOID*)&CheckVdm->CmdLine);
443 
444     /* Capture the application name */
445     CsrCaptureMessageBuffer(CaptureBuffer,
446                             AnsiAppName,
447                             CheckVdm->AppLen,
448                             (PVOID*)&CheckVdm->AppName);
449 
450     CheckVdm->PifFile = NULL; // TODO: PIF file support!
451 
452     /* Capture the current directory */
453     CsrCaptureMessageBuffer(CaptureBuffer,
454                             AnsiCurDirectory,
455                             CheckVdm->CurDirectoryLen,
456                             (PVOID*)&CheckVdm->CurDirectory);
457 
458     /* Capture the environment */
459     CsrCaptureMessageBuffer(CaptureBuffer,
460                             AnsiEnvironment->Buffer,
461                             CheckVdm->EnvLen,
462                             (PVOID*)&CheckVdm->Env);
463 
464     /* Capture the startup info structure */
465     CsrCaptureMessageBuffer(CaptureBuffer,
466                             &AnsiStartupInfo,
467                             sizeof(*CheckVdm->StartupInfo),
468                             (PVOID*)&CheckVdm->StartupInfo);
469 
470     if (StartupInfo->lpDesktop)
471     {
472         /* Capture the desktop name */
473         CsrCaptureMessageBuffer(CaptureBuffer,
474                                 AnsiDesktop,
475                                 CheckVdm->DesktopLen,
476                                 (PVOID*)&CheckVdm->Desktop);
477     }
478     else CheckVdm->Desktop = NULL;
479 
480     if (StartupInfo->lpTitle)
481     {
482         /* Capture the title */
483         CsrCaptureMessageBuffer(CaptureBuffer,
484                                 AnsiTitle,
485                                 CheckVdm->TitleLen,
486                                 (PVOID*)&CheckVdm->Title);
487     }
488     else CheckVdm->Title = NULL;
489 
490     if (StartupInfo->lpReserved)
491     {
492         /* Capture the reserved parameter */
493         CsrCaptureMessageBuffer(CaptureBuffer,
494                                 AnsiReserved,
495                                 CheckVdm->ReservedLen,
496                                 (PVOID*)&CheckVdm->Reserved);
497     }
498     else CheckVdm->Reserved = NULL;
499 
500     /* Send the message to CSRSS */
501     Status = CsrClientCallServer((PCSR_API_MESSAGE)ApiMessage,
502                                  CaptureBuffer,
503                                  CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCheckVDM),
504                                  sizeof(*CheckVdm));
505 
506     /* Write back the task ID */
507     *iTask = CheckVdm->iTask;
508 
509 Cleanup:
510 
511     /* Free the ANSI strings */
512     if (AnsiCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCmdLine);
513     if (AnsiAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiAppName);
514     if (AnsiCurDirectory) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCurDirectory);
515     if (AnsiDesktop) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiDesktop);
516     if (AnsiTitle) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiTitle);
517     if (AnsiReserved) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiReserved);
518 
519     /* Free the capture buffer */
520     if (CaptureBuffer) CsrFreeCaptureBuffer(CaptureBuffer);
521 
522     /* Free the current directory, if it was allocated here, and its short path */
523     if (ShortCurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortCurrentDir);
524     if (CurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDir);
525 
526     /* Free the short app name */
527     if (ShortAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortAppName);
528 
529     return Status;
530 }
531 
532 BOOL
533 WINAPI
534 BaseUpdateVDMEntry(IN ULONG UpdateIndex,
535                    IN OUT PHANDLE WaitHandle,
536                    IN ULONG IndexInfo,
537                    IN ULONG BinaryType)
538 {
539     BASE_API_MESSAGE ApiMessage;
540     PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry = &ApiMessage.Data.UpdateVDMEntryRequest;
541 
542     /* Check what update is being sent */
543     switch (UpdateIndex)
544     {
545         /* VDM is being undone */
546         case VdmEntryUndo:
547         {
548             /* Tell the server how far we had gotten along */
549             UpdateVdmEntry->iTask = HandleToUlong(*WaitHandle);
550             UpdateVdmEntry->VDMCreationState = IndexInfo;
551             break;
552         }
553 
554         /* VDM is ready with a new process handle */
555         case VdmEntryUpdateProcess:
556         {
557             /* Send it the process handle */
558             UpdateVdmEntry->VDMProcessHandle = *WaitHandle;
559             UpdateVdmEntry->iTask = IndexInfo;
560             break;
561         }
562     }
563 
564     /* Also check what kind of binary this is for the console handle */
565     if (BinaryType == BINARY_TYPE_WOW)
566     {
567         /* Magic value for 16-bit apps */
568         UpdateVdmEntry->ConsoleHandle = (HANDLE)-1;
569     }
570     else if (UpdateVdmEntry->iTask)
571     {
572         /* No handle for true VDM */
573         UpdateVdmEntry->ConsoleHandle = NULL;
574     }
575     else
576     {
577         /* Otherwise, use the regular console handle */
578         UpdateVdmEntry->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
579     }
580 
581     /* Finally write the index and binary type */
582     UpdateVdmEntry->EntryIndex = UpdateIndex;
583     UpdateVdmEntry->BinaryType = BinaryType;
584 
585     /* Send the message to CSRSS */
586     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
587                         NULL,
588                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepUpdateVDMEntry),
589                         sizeof(*UpdateVdmEntry));
590     if (!NT_SUCCESS(ApiMessage.Status))
591     {
592         /* Handle failure */
593         BaseSetLastNTError(ApiMessage.Status);
594         return FALSE;
595     }
596 
597     /* If this was an update, CSRSS returns a new wait handle */
598     if (UpdateIndex == VdmEntryUpdateProcess)
599     {
600         /* Return it to the caller */
601         *WaitHandle = UpdateVdmEntry->WaitObjectForParent;
602     }
603 
604     /* We made it */
605     return TRUE;
606 }
607 
608 BOOL
609 WINAPI
610 BaseCheckForVDM(IN HANDLE ProcessHandle,
611                 OUT LPDWORD ExitCode)
612 {
613     NTSTATUS Status;
614     EVENT_BASIC_INFORMATION EventBasicInfo;
615     BASE_API_MESSAGE ApiMessage;
616     PBASE_GET_VDM_EXIT_CODE GetVdmExitCode = &ApiMessage.Data.GetVDMExitCodeRequest;
617 
618     /* It's VDM if the process is actually a wait handle (an event) */
619     Status = NtQueryEvent(ProcessHandle,
620                           EventBasicInformation,
621                           &EventBasicInfo,
622                           sizeof(EventBasicInfo),
623                           NULL);
624     if (!NT_SUCCESS(Status)) return FALSE;
625 
626     /* Setup the input parameters */
627     GetVdmExitCode->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
628     GetVdmExitCode->hParent = ProcessHandle;
629 
630     /* Call CSRSS */
631     Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
632                                  NULL,
633                                  CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetVDMExitCode),
634                                  sizeof(*GetVdmExitCode));
635     if (!NT_SUCCESS(Status)) return FALSE;
636 
637     /* Get the exit code from the reply */
638     *ExitCode = GetVdmExitCode->ExitCode;
639     return TRUE;
640 }
641 
642 BOOL
643 WINAPI
644 BaseGetVdmConfigInfo(IN LPCWSTR CommandLineReserved,
645                      IN ULONG DosSeqId,
646                      IN ULONG BinaryType,
647                      IN PUNICODE_STRING CmdLineString,
648                      OUT PULONG VdmSize)
649 {
650     WCHAR Buffer[MAX_PATH];
651     WCHAR CommandLine[MAX_PATH * 2];
652     ULONG Length;
653 
654     /* Clear the buffer in case we fail */
655     CmdLineString->Buffer = 0;
656 
657     /* Always return the same size: 16 Mb */
658     *VdmSize = 0x1000000;
659 
660     /* Get the system directory */
661     Length = GetSystemDirectoryW(Buffer, MAX_PATH);
662     if (!(Length) || (Length >= MAX_PATH))
663     {
664         /* Eliminate no path or path too big */
665         SetLastError(ERROR_INVALID_NAME);
666         return FALSE;
667     }
668 
669     /* Check if this is VDM with a DOS Sequence ID */
670     if (DosSeqId)
671     {
672         /*
673          * Build the VDM string for it:
674          * -i%lx : Gives the DOS Sequence ID;
675          * %s%c  : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
676          */
677         _snwprintf(CommandLine,
678                    ARRAYSIZE(CommandLine),
679                    L"\"%s\\ntvdm.exe\" -i%lx %s%c",
680                    Buffer,
681                    DosSeqId,
682                    (BinaryType == BINARY_TYPE_DOS) ? L" " : L"-w",
683                    (BinaryType == BINARY_TYPE_SEPARATE_WOW) ? L's' : L' ');
684     }
685     else
686     {
687         /*
688          * Build the string for it without the DOS Sequence ID:
689          * %s%c  : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
690          */
691         _snwprintf(CommandLine,
692                    ARRAYSIZE(CommandLine),
693                    L"\"%s\\ntvdm.exe\" %s%c",
694                    Buffer,
695                    (BinaryType == BINARY_TYPE_DOS) ? L" " : L"-w",
696                    (BinaryType == BINARY_TYPE_SEPARATE_WOW) ? L's' : L' ');
697     }
698 
699     /* Create the actual string */
700     return RtlCreateUnicodeString(CmdLineString, CommandLine);
701 }
702 
703 ENV_NAME_TYPE
704 WINAPI
705 BaseGetEnvNameType_U(IN PWCHAR Name,
706                      IN ULONG NameLength)
707 {
708     PENV_INFO EnvInfo;
709     ENV_NAME_TYPE NameType;
710     ULONG i;
711 
712     /* Start by assuming the environment variable doesn't describe paths */
713     NameType = EnvNameNotAPath;
714 
715     /* Loop all the environment names */
716     for (i = 0; i < ARRAYSIZE(BasepEnvNameType); i++)
717     {
718         /* Get this entry */
719         EnvInfo = &BasepEnvNameType[i];
720 
721         /* Check if it matches the name */
722         if ((EnvInfo->NameLength == NameLength) &&
723             (_wcsnicmp(EnvInfo->Name, Name, NameLength) == 0))
724         {
725             /* It does, return the type */
726             NameType = EnvInfo->NameType;
727             break;
728         }
729     }
730 
731     return NameType;
732 }
733 
734 BOOL
735 NTAPI
736 BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
737                          OUT PANSI_STRING AnsiEnv,
738                          OUT PUNICODE_STRING UnicodeEnv)
739 {
740 #define IS_ALPHA(x)   \
741     ( ((x) >= L'A' && (x) <= L'Z') || ((x) >= L'a' && (x) <= L'z') )
742 
743 // From lib/rtl/path.c :
744 // Can be put in some .h ??
745 #define IS_PATH_SEPARATOR(x)    ((x) == L'\\' || (x) == L'/')
746 
747     BOOL Success = FALSE;
748     NTSTATUS Status;
749     ULONG RegionSize, EnvironmentSize = 0;
750     PWCHAR Environment, NewEnvironment = NULL;
751     ENV_NAME_TYPE NameType;
752     ULONG NameLength, NumChars, Remaining;
753     PWCHAR SourcePtr, DestPtr, StartPtr;
754 
755     /* Make sure we have both strings */
756     if (!AnsiEnv || !UnicodeEnv)
757     {
758         /* Fail */
759         SetLastError(ERROR_INVALID_PARAMETER);
760         return FALSE;
761     }
762 
763     /* Check if an environment was passed in */
764     if (!lpEnvironment)
765     {
766         /* Nope, create one */
767         Status = RtlCreateEnvironment(TRUE, &Environment);
768         if (!NT_SUCCESS(Status)) goto Cleanup;
769     }
770     else
771     {
772         /* Use the one we got */
773         Environment = lpEnvironment;
774     }
775 
776     /* Do we have something now ? */
777     if (!Environment)
778     {
779         /* Still not, bail out */
780         SetLastError(ERROR_BAD_ENVIRONMENT);
781         goto Cleanup;
782     }
783 
784     /*
785      * Count how much space the whole environment takes. The environment block is
786      * doubly NULL-terminated (NULL from last string and final NULL terminator).
787      */
788     SourcePtr = Environment;
789     while (!(*SourcePtr++ == UNICODE_NULL && *SourcePtr == UNICODE_NULL))
790         ++EnvironmentSize;
791     EnvironmentSize += 2; // Add the two terminating NULLs
792 
793     /*
794      * Allocate a new copy large enough to hold all the environment with paths
795      * in their short form. Since the short form of a path can be a bit longer
796      * than its long form, for example in the case where characters that are
797      * invalid in the 8.3 representation are present in the long path name:
798      *   'C:\\a+b' --> 'C:\\A_B~1', or:
799      *   'C:\\a b' --> 'C:\\AB2761~1' (with checksum inserted),
800      * we suppose that the possible total number of extra characters needed to
801      * convert the long paths into their short form is at most equal to MAX_PATH.
802      */
803     RegionSize = (EnvironmentSize + MAX_PATH) * sizeof(WCHAR);
804     Status = NtAllocateVirtualMemory(NtCurrentProcess(),
805                                      (PVOID*)&NewEnvironment,
806                                      0,
807                                      &RegionSize,
808                                      MEM_COMMIT,
809                                      PAGE_READWRITE);
810     if (!NT_SUCCESS(Status))
811     {
812         /* We failed, bail out */
813         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
814         NewEnvironment = NULL;
815         goto Cleanup;
816     }
817 
818     /* Parse the environment block */
819     Remaining = MAX_PATH - 2; // '-2': remove the last two NULLs. FIXME: is it really needed??
820     SourcePtr = Environment;
821     DestPtr   = NewEnvironment;
822 
823     /* Loop through all the environment strings */
824     while (*SourcePtr != UNICODE_NULL)
825     {
826         /*
827          * 1. Check the type of the environment variable and copy its name.
828          */
829 
830         /* Regular environment variable */
831         if (*SourcePtr != L'=')
832         {
833             StartPtr = SourcePtr;
834 
835             /* Copy the environment variable name, including the '=' */
836             while (*SourcePtr != UNICODE_NULL)
837             {
838                 *DestPtr++ = *SourcePtr;
839                 if (*SourcePtr++ == L'=') break;
840             }
841 
842             /* Guess the type of the environment variable */
843             NameType = BaseGetEnvNameType_U(StartPtr, SourcePtr - StartPtr - 1);
844         }
845         /* 'Current directory' environment variable (i.e. of '=X:=' form) */
846         else // if (*SourcePtr == L'=')
847         {
848             /* First assume we have a possibly malformed environment variable */
849             NameType = EnvNameNotAPath;
850 
851             /* Check for a valid 'Current directory' environment variable */
852             if (IS_ALPHA(SourcePtr[1]) && SourcePtr[2] == L':' && SourcePtr[3] == L'=')
853             {
854                 /*
855                  * Small optimization: convert the path to short form only if
856                  * the current directory is not the root directory (i.e. not
857                  * of the '=X:=Y:\' form), otherwise just do a simple copy.
858                  */
859                 if ( wcslen(SourcePtr) >= ARRAYSIZE("=X:=Y:\\")-1 &&
860                      !( IS_ALPHA(SourcePtr[4]) && SourcePtr[5] == L':' &&
861                         IS_PATH_SEPARATOR(SourcePtr[6]) && SourcePtr[7] == UNICODE_NULL ) )
862                 {
863                     NameType = EnvNameSinglePath;
864 
865                     /* Copy the '=X:=' prefix */
866                     *DestPtr++ = SourcePtr[0];
867                     *DestPtr++ = SourcePtr[1];
868                     *DestPtr++ = SourcePtr[2];
869                     *DestPtr++ = SourcePtr[3];
870                     SourcePtr += 4;
871                 }
872             }
873             else
874             {
875                 /*
876                  * Invalid stuff starting with '=', i.e.:
877                  * =? (with '?' not being a letter)
878                  * =X??? (with '?' not being ":=" and not followed by something longer than 3 characters)
879                  * =X:=??? (with '?' not being "X:\\")
880                  *
881                  * 'NameType' is already set to 'EnvNameNotAPath'.
882                  */
883             }
884         }
885 
886 
887         /*
888          * 2. Copy the environment value and perform conversions accordingly.
889          */
890 
891         if (NameType == EnvNameNotAPath)
892         {
893             /* Copy everything, including the NULL terminator */
894             do
895             {
896                 *DestPtr++ = *SourcePtr;
897             } while (*SourcePtr++ != UNICODE_NULL);
898         }
899         else if (NameType == EnvNameSinglePath)
900         {
901             /* Convert the path to its short form */
902             NameLength = wcslen(SourcePtr);
903             NumChars = GetShortPathNameW(SourcePtr, DestPtr, NameLength + 1 + Remaining);
904             if (NumChars == 0 || NumChars > NameLength + Remaining)
905             {
906                 /* If the conversion failed, just copy the original value */
907                 RtlCopyMemory(DestPtr, SourcePtr, NameLength * sizeof(WCHAR));
908                 NumChars = NameLength;
909             }
910             DestPtr += NumChars;
911             if (NumChars > NameLength)
912                 Remaining -= (NumChars - NameLength);
913 
914             SourcePtr += NameLength;
915 
916             /* Copy the NULL terminator */
917             *DestPtr++ = *SourcePtr++;
918         }
919         else // if (NameType == EnvNameMultiplePath)
920         {
921             WCHAR Delimiter;
922 
923             /* Loop through the list of paths (delimited by ';') and convert each path to its short form */
924             do
925             {
926                 /* Copy any trailing ';' before going to the next path */
927                 while (*SourcePtr == L';')
928                 {
929                     *DestPtr++ = *SourcePtr++;
930                 }
931 
932                 StartPtr = SourcePtr;
933 
934                 /* Find the next path list delimiter or the NULL terminator */
935                 while (*SourcePtr != UNICODE_NULL && *SourcePtr != L';')
936                 {
937                     ++SourcePtr;
938                 }
939                 Delimiter = *SourcePtr;
940 
941                 NameLength = SourcePtr - StartPtr;
942                 if (NameLength)
943                 {
944                     /*
945                      * Temporarily replace the possible path list delimiter by NULL.
946                      * 'lpEnvironment' must point to a read+write memory buffer!
947                      */
948                     *SourcePtr = UNICODE_NULL;
949 
950                     NumChars = GetShortPathNameW(StartPtr, DestPtr, NameLength + 1 + Remaining);
951                     if ( NumChars == 0 ||
952                         (Delimiter == L';' ? NumChars > NameLength + Remaining
953                                            : NumChars > NameLength /* + Remaining ?? */) )
954                     {
955                         /* If the conversion failed, just copy the original value */
956                         RtlCopyMemory(DestPtr, StartPtr, NameLength * sizeof(WCHAR));
957                         NumChars = NameLength;
958                     }
959                     DestPtr += NumChars;
960                     if (NumChars > NameLength)
961                         Remaining -= (NumChars - NameLength);
962 
963                     /* If removed, restore the path list delimiter in the source environment value and copy it */
964                     if (Delimiter != UNICODE_NULL)
965                     {
966                         *SourcePtr = Delimiter;
967                         *DestPtr++ = *SourcePtr++;
968                     }
969                 }
970             } while (*SourcePtr != UNICODE_NULL);
971 
972             /* Copy the NULL terminator */
973             *DestPtr++ = *SourcePtr++;
974         }
975     }
976 
977     /* NULL-terminate the environment block */
978     *DestPtr++ = UNICODE_NULL;
979 
980     /* Initialize the Unicode string to hold it */
981     RtlInitEmptyUnicodeString(UnicodeEnv, NewEnvironment,
982                               (DestPtr - NewEnvironment) * sizeof(WCHAR));
983     UnicodeEnv->Length = UnicodeEnv->MaximumLength;
984 
985     /* Create its ANSI version */
986     Status = RtlUnicodeStringToAnsiString(AnsiEnv, UnicodeEnv, TRUE);
987     if (!NT_SUCCESS(Status))
988     {
989         /* Set last error if conversion failure */
990         BaseSetLastNTError(Status);
991     }
992     else
993     {
994         /* Everything went okay, so return success */
995         Success = TRUE;
996         NewEnvironment = NULL;
997     }
998 
999 Cleanup:
1000     /* Cleanup path starts here, start by destroying the environment copy */
1001     if (!lpEnvironment && Environment) RtlDestroyEnvironment(Environment);
1002 
1003     /* See if we are here due to failure */
1004     if (NewEnvironment)
1005     {
1006         /* Initialize the paths to be empty */
1007         RtlInitEmptyUnicodeString(UnicodeEnv, NULL, 0);
1008         RtlInitEmptyAnsiString(AnsiEnv, NULL, 0);
1009 
1010         /* Free the environment copy */
1011         RegionSize = 0;
1012         Status = NtFreeVirtualMemory(NtCurrentProcess(),
1013                                      (PVOID*)&NewEnvironment,
1014                                      &RegionSize,
1015                                      MEM_RELEASE);
1016         ASSERT(NT_SUCCESS(Status));
1017     }
1018 
1019     /* Return the result */
1020     return Success;
1021 }
1022 
1023 BOOL
1024 NTAPI
1025 BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv,
1026                           IN PUNICODE_STRING UnicodeEnv)
1027 {
1028     ULONG Dummy = 0;
1029 
1030     /* Clear the ANSI buffer since Rtl creates this for us */
1031     if (AnsiEnv->Buffer) RtlFreeAnsiString(AnsiEnv);
1032 
1033     /* The Unicode buffer is build by hand, though */
1034     if (UnicodeEnv->Buffer)
1035     {
1036         /* So clear it through the API */
1037         NtFreeVirtualMemory(NtCurrentProcess(),
1038                             (PVOID*)&UnicodeEnv->Buffer,
1039                             &Dummy,
1040                             MEM_RELEASE);
1041     }
1042 
1043     /* All done */
1044     return TRUE;
1045 }
1046 
1047 
1048 /* Check whether a file is an OS/2 or a very old Windows executable
1049  * by testing on import of KERNEL.
1050  *
1051  * FIXME: is reading the module imports the only way of discerning
1052  *        old Windows binaries from OS/2 ones ? At least it seems so...
1053  */
1054 static DWORD WINAPI
1055 InternalIsOS2OrOldWin(HANDLE hFile, IMAGE_DOS_HEADER *mz, IMAGE_OS2_HEADER *ne)
1056 {
1057   DWORD CurPos;
1058   LPWORD modtab = NULL;
1059   LPSTR nametab = NULL;
1060   DWORD Read, Ret;
1061   int i;
1062 
1063   Ret = BINARY_OS216;
1064   CurPos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
1065 
1066   /* read modref table */
1067   if((SetFilePointer(hFile, mz->e_lfanew + ne->ne_modtab, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
1068      (!(modtab = HeapAlloc(GetProcessHeap(), 0, ne->ne_cmod * sizeof(WORD)))) ||
1069      (!(ReadFile(hFile, modtab, ne->ne_cmod * sizeof(WORD), &Read, NULL))) ||
1070      (Read != (DWORD)ne->ne_cmod * sizeof(WORD)))
1071   {
1072     goto broken;
1073   }
1074 
1075   /* read imported names table */
1076   if((SetFilePointer(hFile, mz->e_lfanew + ne->ne_imptab, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
1077      (!(nametab = HeapAlloc(GetProcessHeap(), 0, ne->ne_enttab - ne->ne_imptab))) ||
1078      (!(ReadFile(hFile, nametab, ne->ne_enttab - ne->ne_imptab, &Read, NULL))) ||
1079      (Read != (DWORD)ne->ne_enttab - ne->ne_imptab))
1080   {
1081     goto broken;
1082   }
1083 
1084   for(i = 0; i < ne->ne_cmod; i++)
1085   {
1086     LPSTR module;
1087     module = &nametab[modtab[i]];
1088     if(!strncmp(&module[1], "KERNEL", module[0]))
1089     {
1090       /* very old windows file */
1091       Ret = BINARY_WIN16;
1092       goto done;
1093     }
1094   }
1095 
1096   broken:
1097   DPRINT1("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
1098 
1099   done:
1100   HeapFree(GetProcessHeap(), 0, modtab);
1101   HeapFree(GetProcessHeap(), 0, nametab);
1102   SetFilePointer(hFile, CurPos, NULL, FILE_BEGIN);
1103   return Ret;
1104 }
1105 
1106 static DWORD WINAPI
1107 InternalGetBinaryType(HANDLE hFile)
1108 {
1109   union
1110   {
1111     struct
1112     {
1113       unsigned char magic[4];
1114       unsigned char ignored[12];
1115       unsigned short type;
1116     } elf;
1117     struct
1118     {
1119       unsigned long magic;
1120       unsigned long cputype;
1121       unsigned long cpusubtype;
1122       unsigned long filetype;
1123     } macho;
1124     IMAGE_DOS_HEADER mz;
1125   } Header;
1126   char magic[4];
1127   DWORD Read;
1128 
1129   if((SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
1130      (!ReadFile(hFile, &Header, sizeof(Header), &Read, NULL) ||
1131       (Read != sizeof(Header))))
1132   {
1133     return BINARY_UNKNOWN;
1134   }
1135 
1136   if(!memcmp(Header.elf.magic, "\177ELF", sizeof(Header.elf.magic)))
1137   {
1138     /* FIXME: we don't bother to check byte order, architecture, etc. */
1139     switch(Header.elf.type)
1140     {
1141       case 2:
1142         return BINARY_UNIX_EXE;
1143       case 3:
1144         return BINARY_UNIX_LIB;
1145     }
1146     return BINARY_UNKNOWN;
1147   }
1148 
1149   /* Mach-o File with Endian set to Big Endian or Little Endian*/
1150   if(Header.macho.magic == 0xFEEDFACE ||
1151      Header.macho.magic == 0xCEFAEDFE)
1152   {
1153     switch(Header.macho.filetype)
1154     {
1155       case 0x8:
1156         /* MH_BUNDLE */
1157         return BINARY_UNIX_LIB;
1158     }
1159     return BINARY_UNKNOWN;
1160   }
1161 
1162   /* Not ELF, try DOS */
1163   if(Header.mz.e_magic == IMAGE_DOS_SIGNATURE)
1164   {
1165     /* We do have a DOS image so we will now try to seek into
1166      * the file by the amount indicated by the field
1167      * "Offset to extended header" and read in the
1168      * "magic" field information at that location.
1169      * This will tell us if there is more header information
1170      * to read or not.
1171      */
1172     if((SetFilePointer(hFile, Header.mz.e_lfanew, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
1173        (!ReadFile(hFile, magic, sizeof(magic), &Read, NULL) ||
1174         (Read != sizeof(magic))))
1175     {
1176       return BINARY_DOS;
1177     }
1178 
1179     /* Reading the magic field succeeded so
1180      * we will try to determine what type it is.
1181      */
1182     if(!memcmp(magic, "PE\0\0", sizeof(magic)))
1183     {
1184       IMAGE_FILE_HEADER FileHeader;
1185       if(!ReadFile(hFile, &FileHeader, sizeof(IMAGE_FILE_HEADER), &Read, NULL) ||
1186          (Read != sizeof(IMAGE_FILE_HEADER)))
1187       {
1188         return BINARY_DOS;
1189       }
1190 
1191       /* FIXME - detect 32/64 bit */
1192 
1193       if(FileHeader.Characteristics & IMAGE_FILE_DLL)
1194         return BINARY_PE_DLL32;
1195       return BINARY_PE_EXE32;
1196     }
1197 
1198     if(!memcmp(magic, "NE", 2))
1199     {
1200       /* This is a Windows executable (NE) header.  This can
1201        * mean either a 16-bit OS/2 or a 16-bit Windows or even a
1202        * DOS program (running under a DOS extender).  To decide
1203        * which, we'll have to read the NE header.
1204        */
1205       IMAGE_OS2_HEADER ne;
1206       if((SetFilePointer(hFile, Header.mz.e_lfanew, NULL, FILE_BEGIN) == 1) ||
1207          !ReadFile(hFile, &ne, sizeof(IMAGE_OS2_HEADER), &Read, NULL) ||
1208          (Read != sizeof(IMAGE_OS2_HEADER)))
1209       {
1210         /* Couldn't read header, so abort. */
1211         return BINARY_DOS;
1212       }
1213 
1214       switch(ne.ne_exetyp)
1215       {
1216         case 2:
1217           return BINARY_WIN16;
1218         case 5:
1219           return BINARY_DOS;
1220         default:
1221           return InternalIsOS2OrOldWin(hFile, &Header.mz, &ne);
1222       }
1223     }
1224     return BINARY_DOS;
1225   }
1226   return BINARY_UNKNOWN;
1227 }
1228 
1229 /*
1230  * @implemented
1231  */
1232 BOOL
1233 WINAPI
1234 GetBinaryTypeW (
1235     LPCWSTR lpApplicationName,
1236     LPDWORD lpBinaryType
1237     )
1238 {
1239   HANDLE hFile;
1240   DWORD BinType;
1241 
1242   if(!lpApplicationName || !lpBinaryType)
1243   {
1244     SetLastError(ERROR_INVALID_PARAMETER);
1245     return FALSE;
1246   }
1247 
1248   hFile = CreateFileW(lpApplicationName, GENERIC_READ, FILE_SHARE_READ, NULL,
1249                       OPEN_EXISTING, 0, 0);
1250   if(hFile == INVALID_HANDLE_VALUE)
1251   {
1252     return FALSE;
1253   }
1254 
1255   BinType = InternalGetBinaryType(hFile);
1256   CloseHandle(hFile);
1257 
1258   switch(BinType)
1259   {
1260     case BINARY_UNKNOWN:
1261     {
1262       WCHAR *dot;
1263 
1264       /*
1265        * guess from filename
1266        */
1267       if(!(dot = wcsrchr(lpApplicationName, L'.')))
1268       {
1269         return FALSE;
1270       }
1271       if(!lstrcmpiW(dot, L".COM"))
1272       {
1273         *lpBinaryType = SCS_DOS_BINARY;
1274         return TRUE;
1275       }
1276       if(!lstrcmpiW(dot, L".PIF"))
1277       {
1278         *lpBinaryType = SCS_PIF_BINARY;
1279         return TRUE;
1280       }
1281       return FALSE;
1282     }
1283     case BINARY_PE_EXE32:
1284     case BINARY_PE_DLL32:
1285     {
1286       *lpBinaryType = SCS_32BIT_BINARY;
1287       return TRUE;
1288     }
1289     case BINARY_PE_EXE64:
1290     case BINARY_PE_DLL64:
1291     {
1292       *lpBinaryType = SCS_64BIT_BINARY;
1293       return TRUE;
1294     }
1295     case BINARY_WIN16:
1296     {
1297       *lpBinaryType = SCS_WOW_BINARY;
1298       return TRUE;
1299     }
1300     case BINARY_OS216:
1301     {
1302       *lpBinaryType = SCS_OS216_BINARY;
1303       return TRUE;
1304     }
1305     case BINARY_DOS:
1306     {
1307       *lpBinaryType = SCS_DOS_BINARY;
1308       return TRUE;
1309     }
1310     case BINARY_UNIX_EXE:
1311     case BINARY_UNIX_LIB:
1312     {
1313       return FALSE;
1314     }
1315   }
1316 
1317   DPRINT1("Invalid binary type %lu returned!\n", BinType);
1318   return FALSE;
1319 }
1320 
1321 /*
1322  * @implemented
1323  */
1324 BOOL
1325 WINAPI
1326 GetBinaryTypeA(IN LPCSTR lpApplicationName,
1327                OUT LPDWORD lpBinaryType)
1328 {
1329     ANSI_STRING ApplicationNameString;
1330     UNICODE_STRING ApplicationNameW;
1331     BOOL StringAllocated = FALSE, Result;
1332     NTSTATUS Status;
1333 
1334     RtlInitAnsiString(&ApplicationNameString, lpApplicationName);
1335 
1336     if (ApplicationNameString.Length * sizeof(WCHAR) >= NtCurrentTeb()->StaticUnicodeString.MaximumLength)
1337     {
1338         StringAllocated = TRUE;
1339         Status = RtlAnsiStringToUnicodeString(&ApplicationNameW, &ApplicationNameString, TRUE);
1340     }
1341     else
1342     {
1343         Status = RtlAnsiStringToUnicodeString(&(NtCurrentTeb()->StaticUnicodeString), &ApplicationNameString, FALSE);
1344     }
1345 
1346     if (!NT_SUCCESS(Status))
1347     {
1348         BaseSetLastNTError(Status);
1349         return FALSE;
1350     }
1351 
1352     if (StringAllocated)
1353     {
1354         Result = GetBinaryTypeW(ApplicationNameW.Buffer, lpBinaryType);
1355         RtlFreeUnicodeString(&ApplicationNameW);
1356     }
1357     else
1358     {
1359         Result = GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString.Buffer, lpBinaryType);
1360     }
1361 
1362     return Result;
1363 }
1364 
1365 /*
1366  * @unimplemented
1367  */
1368 BOOL
1369 WINAPI
1370 CmdBatNotification (
1371     DWORD   Unknown
1372     )
1373 {
1374     STUB;
1375     return FALSE;
1376 }
1377 
1378 /*
1379  * @implemented
1380  */
1381 VOID
1382 WINAPI
1383 ExitVDM(BOOL IsWow, ULONG iWowTask)
1384 {
1385     BASE_API_MESSAGE ApiMessage;
1386     PBASE_EXIT_VDM ExitVdm = &ApiMessage.Data.ExitVDMRequest;
1387 
1388     /* Setup the input parameters */
1389     ExitVdm->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
1390     ExitVdm->iWowTask = IsWow ? iWowTask : 0; /* Always zero for DOS tasks */
1391     ExitVdm->WaitObjectForVDM = NULL;
1392 
1393     /* Call CSRSS */
1394     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1395                         NULL,
1396                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitVDM),
1397                         sizeof(*ExitVdm));
1398 
1399     /* Close the returned wait object handle, if any */
1400     if (NT_SUCCESS(ApiMessage.Status) && (ExitVdm->WaitObjectForVDM != NULL))
1401     {
1402         CloseHandle(ExitVdm->WaitObjectForVDM);
1403     }
1404 }
1405 
1406 /*
1407  * @implemented
1408  */
1409 BOOL
1410 WINAPI
1411 GetNextVDMCommand(PVDM_COMMAND_INFO CommandData)
1412 {
1413     BOOL Success = FALSE;
1414     NTSTATUS Status;
1415     BASE_API_MESSAGE ApiMessage;
1416     PBASE_GET_NEXT_VDM_COMMAND GetNextVdmCommand = &ApiMessage.Data.GetNextVDMCommandRequest;
1417     PBASE_IS_FIRST_VDM IsFirstVdm = &ApiMessage.Data.IsFirstVDMRequest;
1418     PBASE_SET_REENTER_COUNT SetReenterCount = &ApiMessage.Data.SetReenterCountRequest;
1419     PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
1420     ULONG NumStrings = 0;
1421 
1422     /*
1423      * Special case to test whether the VDM is the first one.
1424      */
1425     if (CommandData == NULL)
1426     {
1427         /* Call CSRSS */
1428         CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1429                             NULL,
1430                             CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepIsFirstVDM),
1431                             sizeof(*IsFirstVdm));
1432         if (!NT_SUCCESS(ApiMessage.Status))
1433         {
1434             BaseSetLastNTError(ApiMessage.Status);
1435             return FALSE;
1436         }
1437 
1438         /* Return TRUE if this is the first VDM */
1439         return IsFirstVdm->FirstVDM;
1440     }
1441 
1442     /* CommandData != NULL */
1443 
1444     /*
1445      * Special case to increment or decrement the reentrancy count.
1446      */
1447     if ((CommandData->VDMState == VDM_INC_REENTER_COUNT) ||
1448         (CommandData->VDMState == VDM_DEC_REENTER_COUNT))
1449     {
1450         /* Setup the input parameters */
1451         SetReenterCount->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
1452         SetReenterCount->fIncDec = CommandData->VDMState;
1453 
1454         /* Call CSRSS */
1455         CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1456                             NULL,
1457                             CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetReenterCount),
1458                             sizeof(*SetReenterCount));
1459         if (!NT_SUCCESS(ApiMessage.Status))
1460         {
1461             BaseSetLastNTError(ApiMessage.Status);
1462             return FALSE;
1463         }
1464 
1465         return TRUE;
1466     }
1467 
1468     /*
1469      * TODO!
1470      * Special case to retrieve or set WOW information.
1471      */
1472     // TODO: if CommandData->VDMState & (VDM_LIST_WOW_PROCESSES | VDM_LIST_WOW_TASKS | VDM_ADD_WOW_TASK)
1473     // then call BasepGetNextVDMCommand in a simpler way!
1474 
1475     /*
1476      * Regular case.
1477      */
1478 
1479     /* Clear the structure */
1480     RtlZeroMemory(GetNextVdmCommand, sizeof(*GetNextVdmCommand));
1481 
1482     /* Setup the input parameters */
1483     GetNextVdmCommand->iTask = CommandData->TaskId;
1484     GetNextVdmCommand->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
1485     GetNextVdmCommand->CmdLen = CommandData->CmdLen;
1486     GetNextVdmCommand->AppLen = CommandData->AppLen;
1487     GetNextVdmCommand->PifLen = CommandData->PifLen;
1488     GetNextVdmCommand->CurDirectoryLen = CommandData->CurDirectoryLen;
1489     GetNextVdmCommand->EnvLen = CommandData->EnvLen;
1490     GetNextVdmCommand->DesktopLen = CommandData->DesktopLen;
1491     GetNextVdmCommand->TitleLen = CommandData->TitleLen;
1492     GetNextVdmCommand->ReservedLen = CommandData->ReservedLen;
1493     GetNextVdmCommand->VDMState = CommandData->VDMState;
1494 
1495     /* Count the number of strings */
1496     if (CommandData->CmdLen) NumStrings++;
1497     if (CommandData->AppLen) NumStrings++;
1498     if (CommandData->PifLen) NumStrings++;
1499     if (CommandData->CurDirectoryLen) NumStrings++;
1500     if (CommandData->EnvLen) NumStrings++;
1501     if (CommandData->DesktopLen) NumStrings++;
1502     if (CommandData->TitleLen) NumStrings++;
1503     if (CommandData->ReservedLen) NumStrings++;
1504 
1505     /* Allocate the capture buffer */
1506     CaptureBuffer = CsrAllocateCaptureBuffer(NumStrings + 1,
1507                                              GetNextVdmCommand->CmdLen
1508                                              + GetNextVdmCommand->AppLen
1509                                              + GetNextVdmCommand->PifLen
1510                                              + GetNextVdmCommand->CurDirectoryLen
1511                                              + GetNextVdmCommand->EnvLen
1512                                              + GetNextVdmCommand->DesktopLen
1513                                              + GetNextVdmCommand->TitleLen
1514                                              + GetNextVdmCommand->ReservedLen
1515                                              + sizeof(*GetNextVdmCommand->StartupInfo));
1516     if (CaptureBuffer == NULL)
1517     {
1518         BaseSetLastNTError(STATUS_NO_MEMORY);
1519         goto Cleanup;
1520     }
1521 
1522     /* Capture the data */
1523 
1524     CsrAllocateMessagePointer(CaptureBuffer,
1525                               sizeof(*GetNextVdmCommand->StartupInfo),
1526                               (PVOID*)&GetNextVdmCommand->StartupInfo);
1527 
1528     if (CommandData->CmdLen)
1529     {
1530         CsrAllocateMessagePointer(CaptureBuffer,
1531                                   CommandData->CmdLen,
1532                                   (PVOID*)&GetNextVdmCommand->CmdLine);
1533     }
1534 
1535     if (CommandData->AppLen)
1536     {
1537         CsrAllocateMessagePointer(CaptureBuffer,
1538                                   CommandData->AppLen,
1539                                   (PVOID*)&GetNextVdmCommand->AppName);
1540     }
1541 
1542     if (CommandData->PifLen)
1543     {
1544         CsrAllocateMessagePointer(CaptureBuffer,
1545                                   CommandData->PifLen,
1546                                   (PVOID*)&GetNextVdmCommand->PifFile);
1547     }
1548 
1549     if (CommandData->CurDirectoryLen)
1550     {
1551         CsrAllocateMessagePointer(CaptureBuffer,
1552                                   CommandData->CurDirectoryLen,
1553                                   (PVOID*)&GetNextVdmCommand->CurDirectory);
1554     }
1555 
1556     if (CommandData->EnvLen)
1557     {
1558         CsrAllocateMessagePointer(CaptureBuffer,
1559                                   CommandData->EnvLen,
1560                                   (PVOID*)&GetNextVdmCommand->Env);
1561     }
1562 
1563     if (CommandData->DesktopLen)
1564     {
1565         CsrAllocateMessagePointer(CaptureBuffer,
1566                                   CommandData->DesktopLen,
1567                                   (PVOID*)&GetNextVdmCommand->Desktop);
1568     }
1569 
1570     if (CommandData->TitleLen)
1571     {
1572         CsrAllocateMessagePointer(CaptureBuffer,
1573                                   CommandData->TitleLen,
1574                                   (PVOID*)&GetNextVdmCommand->Title);
1575     }
1576 
1577     if (CommandData->ReservedLen)
1578     {
1579         CsrAllocateMessagePointer(CaptureBuffer,
1580                                   CommandData->ReservedLen,
1581                                   (PVOID*)&GetNextVdmCommand->Reserved);
1582     }
1583 
1584     while (TRUE)
1585     {
1586         /* Call CSRSS */
1587         Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1588                                      CaptureBuffer,
1589                                      CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetNextVDMCommand),
1590                                      sizeof(*GetNextVdmCommand));
1591 
1592         /* Exit the waiting loop if we did not receive any event handle */
1593         if (GetNextVdmCommand->WaitObjectForVDM == NULL)
1594             break;
1595 
1596         /* Wait for the event to become signaled and try again */
1597         Status = NtWaitForSingleObject(GetNextVdmCommand->WaitObjectForVDM,
1598                                        FALSE, NULL);
1599         if (Status != STATUS_SUCCESS)
1600         {
1601             /* Fail if we timed out, or if some other error happened */
1602             BaseSetLastNTError(Status);
1603             goto Cleanup;
1604         }
1605 
1606         /* Set the retry flag, clear the exit code, and retry a query */
1607         GetNextVdmCommand->VDMState |= VDM_FLAG_RETRY;
1608         GetNextVdmCommand->ExitCode = 0;
1609     }
1610 
1611     if (!NT_SUCCESS(Status))
1612     {
1613         if (Status == STATUS_INVALID_PARAMETER)
1614         {
1615             /*
1616              * One of the buffer lengths was less than required. Store the correct ones.
1617              * Note that the status code is not STATUS_BUFFER_TOO_SMALL as one would expect,
1618              * in order to keep compatibility with Windows 2003 BASESRV.DLL.
1619              */
1620             CommandData->CmdLen = GetNextVdmCommand->CmdLen;
1621             CommandData->AppLen = GetNextVdmCommand->AppLen;
1622             CommandData->PifLen = GetNextVdmCommand->PifLen;
1623             CommandData->CurDirectoryLen = GetNextVdmCommand->CurDirectoryLen;
1624             CommandData->EnvLen      = GetNextVdmCommand->EnvLen;
1625             CommandData->DesktopLen  = GetNextVdmCommand->DesktopLen;
1626             CommandData->TitleLen    = GetNextVdmCommand->TitleLen;
1627             CommandData->ReservedLen = GetNextVdmCommand->ReservedLen;
1628         }
1629         else
1630         {
1631             /* Any other failure */
1632             CommandData->CmdLen = 0;
1633             CommandData->AppLen = 0;
1634             CommandData->PifLen = 0;
1635             CommandData->CurDirectoryLen = 0;
1636             CommandData->EnvLen      = 0;
1637             CommandData->DesktopLen  = 0;
1638             CommandData->TitleLen    = 0;
1639             CommandData->ReservedLen = 0;
1640         }
1641 
1642         BaseSetLastNTError(Status);
1643         goto Cleanup;
1644     }
1645 
1646     /* Write back the standard handles */
1647     CommandData->StdIn  = GetNextVdmCommand->StdIn;
1648     CommandData->StdOut = GetNextVdmCommand->StdOut;
1649     CommandData->StdErr = GetNextVdmCommand->StdErr;
1650 
1651     /* Write back the startup info */
1652     RtlMoveMemory(&CommandData->StartupInfo,
1653                   GetNextVdmCommand->StartupInfo,
1654                   sizeof(*GetNextVdmCommand->StartupInfo));
1655 
1656     if (CommandData->CmdLen)
1657     {
1658         /* Write back the command line */
1659         RtlMoveMemory(CommandData->CmdLine,
1660                       GetNextVdmCommand->CmdLine,
1661                       GetNextVdmCommand->CmdLen);
1662 
1663         /* Set the actual length */
1664         CommandData->CmdLen = GetNextVdmCommand->CmdLen;
1665     }
1666 
1667     if (CommandData->AppLen)
1668     {
1669         /* Write back the application name */
1670         RtlMoveMemory(CommandData->AppName,
1671                       GetNextVdmCommand->AppName,
1672                       GetNextVdmCommand->AppLen);
1673 
1674         /* Set the actual length */
1675         CommandData->AppLen = GetNextVdmCommand->AppLen;
1676     }
1677 
1678     if (CommandData->PifLen)
1679     {
1680         /* Write back the PIF file name */
1681         RtlMoveMemory(CommandData->PifFile,
1682                       GetNextVdmCommand->PifFile,
1683                       GetNextVdmCommand->PifLen);
1684 
1685         /* Set the actual length */
1686         CommandData->PifLen = GetNextVdmCommand->PifLen;
1687     }
1688 
1689     if (CommandData->CurDirectoryLen)
1690     {
1691         /* Write back the current directory */
1692         RtlMoveMemory(CommandData->CurDirectory,
1693                       GetNextVdmCommand->CurDirectory,
1694                       GetNextVdmCommand->CurDirectoryLen);
1695 
1696         /* Set the actual length */
1697         CommandData->CurDirectoryLen = GetNextVdmCommand->CurDirectoryLen;
1698     }
1699 
1700     if (CommandData->EnvLen)
1701     {
1702         /* Write back the environment */
1703         RtlMoveMemory(CommandData->Env,
1704                       GetNextVdmCommand->Env,
1705                       GetNextVdmCommand->EnvLen);
1706 
1707         /* Set the actual length */
1708         CommandData->EnvLen = GetNextVdmCommand->EnvLen;
1709     }
1710 
1711     if (CommandData->DesktopLen)
1712     {
1713         /* Write back the desktop name */
1714         RtlMoveMemory(CommandData->Desktop,
1715                       GetNextVdmCommand->Desktop,
1716                       GetNextVdmCommand->DesktopLen);
1717 
1718         /* Set the actual length */
1719         CommandData->DesktopLen = GetNextVdmCommand->DesktopLen;
1720     }
1721 
1722     if (CommandData->TitleLen)
1723     {
1724         /* Write back the title */
1725         RtlMoveMemory(CommandData->Title,
1726                       GetNextVdmCommand->Title,
1727                       GetNextVdmCommand->TitleLen);
1728 
1729         /* Set the actual length */
1730         CommandData->TitleLen = GetNextVdmCommand->TitleLen;
1731     }
1732 
1733     if (CommandData->ReservedLen)
1734     {
1735         /* Write back the reserved parameter */
1736         RtlMoveMemory(CommandData->Reserved,
1737                       GetNextVdmCommand->Reserved,
1738                       GetNextVdmCommand->ReservedLen);
1739 
1740         /* Set the actual length */
1741         CommandData->ReservedLen = GetNextVdmCommand->ReservedLen;
1742     }
1743 
1744     /* Write the remaining output parameters */
1745     CommandData->TaskId        = GetNextVdmCommand->iTask;
1746     CommandData->CreationFlags = GetNextVdmCommand->dwCreationFlags;
1747     CommandData->CodePage      = GetNextVdmCommand->CodePage;
1748     CommandData->ExitCode      = GetNextVdmCommand->ExitCode;
1749     CommandData->CurrentDrive  = GetNextVdmCommand->CurrentDrive;
1750     CommandData->VDMState      = GetNextVdmCommand->VDMState;
1751     CommandData->ComingFromBat = GetNextVdmCommand->fComingFromBat;
1752 
1753     /* It was successful */
1754     Success = TRUE;
1755 
1756 Cleanup:
1757     if (CaptureBuffer != NULL) CsrFreeCaptureBuffer(CaptureBuffer);
1758     return Success;
1759 }
1760 
1761 
1762 /*
1763  * @implemented
1764  */
1765 DWORD
1766 WINAPI
1767 GetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs)
1768 {
1769     BASE_API_MESSAGE ApiMessage;
1770     PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest;
1771     PCSR_CAPTURE_BUFFER CaptureBuffer;
1772 
1773     /* Allocate the capture buffer */
1774     CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs);
1775     if (CaptureBuffer == NULL)
1776     {
1777         BaseSetLastNTError(STATUS_NO_MEMORY);
1778         return 0;
1779     }
1780 
1781     /* Setup the input parameters */
1782     VDMCurrentDirsRequest->cchCurDirs = cchCurDirs;
1783     CsrAllocateMessagePointer(CaptureBuffer,
1784                               cchCurDirs,
1785                               (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs);
1786 
1787     /* Call CSRSS */
1788     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1789                         CaptureBuffer,
1790                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetVDMCurDirs),
1791                         sizeof(*VDMCurrentDirsRequest));
1792 
1793     /* Set the last error */
1794     BaseSetLastNTError(ApiMessage.Status);
1795 
1796     if (NT_SUCCESS(ApiMessage.Status))
1797     {
1798         /* Copy the result */
1799         RtlMoveMemory(lpszzCurDirs, VDMCurrentDirsRequest->lpszzCurDirs, cchCurDirs);
1800     }
1801 
1802     /* Free the capture buffer */
1803     CsrFreeCaptureBuffer(CaptureBuffer);
1804 
1805     /* Return the size if it was successful, or if the buffer was too small */
1806     return (NT_SUCCESS(ApiMessage.Status) || (ApiMessage.Status == STATUS_BUFFER_TOO_SMALL))
1807            ? VDMCurrentDirsRequest->cchCurDirs : 0;
1808 }
1809 
1810 
1811 /*
1812  * @implemented (undocumented)
1813  */
1814 BOOL
1815 WINAPI
1816 RegisterConsoleVDM(IN DWORD dwRegisterFlags,
1817                    IN HANDLE hStartHardwareEvent,
1818                    IN HANDLE hEndHardwareEvent,
1819                    IN HANDLE hErrorHardwareEvent,
1820                    IN DWORD dwUnusedVar,
1821                    OUT LPDWORD lpVideoStateLength,
1822                    OUT PVOID* lpVideoState, // PVIDEO_HARDWARE_STATE_HEADER*
1823                    IN PVOID lpUnusedBuffer,
1824                    IN DWORD dwUnusedBufferLength,
1825                    IN COORD dwVDMBufferSize,
1826                    OUT PVOID* lpVDMBuffer)
1827 {
1828     BOOL Success;
1829     CONSOLE_API_MESSAGE ApiMessage;
1830     PCONSOLE_REGISTERVDM RegisterVDMRequest = &ApiMessage.Data.RegisterVDMRequest;
1831     PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
1832 
1833     /* Set up the data to send to the Console Server */
1834     RegisterVDMRequest->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
1835     RegisterVDMRequest->RegisterFlags = dwRegisterFlags;
1836 
1837     if (dwRegisterFlags != 0)
1838     {
1839         RegisterVDMRequest->StartHardwareEvent = hStartHardwareEvent;
1840         RegisterVDMRequest->EndHardwareEvent   = hEndHardwareEvent;
1841         RegisterVDMRequest->ErrorHardwareEvent = hErrorHardwareEvent;
1842 
1843         RegisterVDMRequest->VDMBufferSize = dwVDMBufferSize;
1844 
1845 #if 0
1846         RegisterVDMRequest->UnusedBufferLength = dwUnusedBufferLength;
1847 
1848         /* Allocate a Capture Buffer */
1849         CaptureBuffer = CsrAllocateCaptureBuffer(1, dwUnusedBufferLength);
1850         if (CaptureBuffer == NULL)
1851         {
1852             DPRINT1("CsrAllocateCaptureBuffer failed!\n");
1853             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1854             return FALSE;
1855         }
1856 
1857         /* Capture the buffer to write */
1858         CsrCaptureMessageBuffer(CaptureBuffer,
1859                                 (PVOID)lpUnusedBuffer,
1860                                 dwUnusedBufferLength,
1861                                 (PVOID*)&RegisterVDMRequest->UnusedBuffer);
1862 #endif
1863     }
1864     else
1865     {
1866         // CaptureBuffer = NULL;
1867     }
1868 
1869     /* Call the server */
1870     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1871                         CaptureBuffer,
1872                         CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepRegisterVDM),
1873                         sizeof(*RegisterVDMRequest));
1874 
1875     /* Check for success */
1876     Success = NT_SUCCESS(ApiMessage.Status);
1877 
1878     /* Release the capture buffer if needed */
1879     if (CaptureBuffer) CsrFreeCaptureBuffer(CaptureBuffer);
1880 
1881     /* Retrieve the results */
1882     if (Success)
1883     {
1884         if (dwRegisterFlags != 0)
1885         {
1886             _SEH2_TRY
1887             {
1888                 *lpVideoStateLength = RegisterVDMRequest->VideoStateLength;
1889                 *lpVideoState       = RegisterVDMRequest->VideoState;
1890                 *lpVDMBuffer        = RegisterVDMRequest->VDMBuffer;
1891             }
1892             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1893             {
1894                 SetLastError(ERROR_INVALID_ACCESS);
1895                 Success = FALSE;
1896             }
1897             _SEH2_END;
1898         }
1899     }
1900     else
1901     {
1902         BaseSetLastNTError(ApiMessage.Status);
1903     }
1904 
1905     /* Return success status */
1906     return Success;
1907 }
1908 
1909 
1910 /*
1911  * @unimplemented
1912  */
1913 BOOL
1914 WINAPI
1915 RegisterWowBaseHandlers (
1916     DWORD   Unknown0
1917     )
1918 {
1919     STUB;
1920     return FALSE;
1921 }
1922 
1923 
1924 /*
1925  * @unimplemented
1926  */
1927 BOOL
1928 WINAPI
1929 RegisterWowExec (
1930     DWORD   Unknown0
1931     )
1932 {
1933     STUB;
1934     return FALSE;
1935 }
1936 
1937 
1938 /*
1939  * @implemented
1940  */
1941 BOOL
1942 WINAPI
1943 SetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs)
1944 {
1945     BASE_API_MESSAGE ApiMessage;
1946     PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest;
1947     PCSR_CAPTURE_BUFFER CaptureBuffer;
1948 
1949     /* Allocate the capture buffer */
1950     CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs);
1951     if (CaptureBuffer == NULL)
1952     {
1953         BaseSetLastNTError(STATUS_NO_MEMORY);
1954         return FALSE;
1955     }
1956 
1957     /* Setup the input parameters */
1958     VDMCurrentDirsRequest->cchCurDirs = cchCurDirs;
1959     CsrCaptureMessageBuffer(CaptureBuffer,
1960                             lpszzCurDirs,
1961                             cchCurDirs,
1962                             (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs);
1963 
1964     /* Call CSRSS */
1965     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1966                         CaptureBuffer,
1967                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetVDMCurDirs),
1968                         sizeof(*VDMCurrentDirsRequest));
1969 
1970     /* Free the capture buffer */
1971     CsrFreeCaptureBuffer(CaptureBuffer);
1972 
1973     /* Set the last error */
1974     BaseSetLastNTError(ApiMessage.Status);
1975 
1976     return NT_SUCCESS(ApiMessage.Status) ? TRUE : FALSE;
1977 }
1978 
1979 /*
1980  * @unimplemented
1981  */
1982 DWORD
1983 WINAPI
1984 VDMConsoleOperation (
1985     DWORD   Unknown0,
1986     DWORD   Unknown1
1987     )
1988 {
1989     STUB;
1990     return 0;
1991 }
1992 
1993 
1994 /*
1995  * @unimplemented
1996  */
1997 BOOL
1998 WINAPI
1999 VDMOperationStarted(IN ULONG Unknown0)
2000 {
2001     DPRINT1("VDMOperationStarted(%d)\n", Unknown0);
2002 
2003     return BaseUpdateVDMEntry(VdmEntryUpdateControlCHandler,
2004                               NULL,
2005                               0,
2006                               Unknown0);
2007 }
2008