xref: /reactos/dll/win32/kernel32/client/vdm.c (revision 40462c92)
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 EnvironmentSize = 0;
750     SIZE_T RegionSize;
751     PWCHAR Environment, NewEnvironment = NULL;
752     ENV_NAME_TYPE NameType;
753     ULONG NameLength, NumChars, Remaining;
754     PWCHAR SourcePtr, DestPtr, StartPtr;
755 
756     /* Make sure we have both strings */
757     if (!AnsiEnv || !UnicodeEnv)
758     {
759         /* Fail */
760         SetLastError(ERROR_INVALID_PARAMETER);
761         return FALSE;
762     }
763 
764     /* Check if an environment was passed in */
765     if (!lpEnvironment)
766     {
767         /* Nope, create one */
768         Status = RtlCreateEnvironment(TRUE, &Environment);
769         if (!NT_SUCCESS(Status)) goto Cleanup;
770     }
771     else
772     {
773         /* Use the one we got */
774         Environment = lpEnvironment;
775     }
776 
777     /* Do we have something now ? */
778     if (!Environment)
779     {
780         /* Still not, bail out */
781         SetLastError(ERROR_BAD_ENVIRONMENT);
782         goto Cleanup;
783     }
784 
785     /*
786      * Count how much space the whole environment takes. The environment block is
787      * doubly NULL-terminated (NULL from last string and final NULL terminator).
788      */
789     SourcePtr = Environment;
790     while (!(*SourcePtr++ == UNICODE_NULL && *SourcePtr == UNICODE_NULL))
791         ++EnvironmentSize;
792     EnvironmentSize += 2; // Add the two terminating NULLs
793 
794     /*
795      * Allocate a new copy large enough to hold all the environment with paths
796      * in their short form. Since the short form of a path can be a bit longer
797      * than its long form, for example in the case where characters that are
798      * invalid in the 8.3 representation are present in the long path name:
799      *   'C:\\a+b' --> 'C:\\A_B~1', or:
800      *   'C:\\a b' --> 'C:\\AB2761~1' (with checksum inserted),
801      * we suppose that the possible total number of extra characters needed to
802      * convert the long paths into their short form is at most equal to MAX_PATH.
803      */
804     RegionSize = (EnvironmentSize + MAX_PATH) * sizeof(WCHAR);
805     Status = NtAllocateVirtualMemory(NtCurrentProcess(),
806                                      (PVOID*)&NewEnvironment,
807                                      0,
808                                      &RegionSize,
809                                      MEM_COMMIT,
810                                      PAGE_READWRITE);
811     if (!NT_SUCCESS(Status))
812     {
813         /* We failed, bail out */
814         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
815         NewEnvironment = NULL;
816         goto Cleanup;
817     }
818 
819     /* Parse the environment block */
820     Remaining = MAX_PATH - 2; // '-2': remove the last two NULLs. FIXME: is it really needed??
821     SourcePtr = Environment;
822     DestPtr   = NewEnvironment;
823 
824     /* Loop through all the environment strings */
825     while (*SourcePtr != UNICODE_NULL)
826     {
827         /*
828          * 1. Check the type of the environment variable and copy its name.
829          */
830 
831         /* Regular environment variable */
832         if (*SourcePtr != L'=')
833         {
834             StartPtr = SourcePtr;
835 
836             /* Copy the environment variable name, including the '=' */
837             while (*SourcePtr != UNICODE_NULL)
838             {
839                 *DestPtr++ = *SourcePtr;
840                 if (*SourcePtr++ == L'=') break;
841             }
842 
843             /* Guess the type of the environment variable */
844             NameType = BaseGetEnvNameType_U(StartPtr, SourcePtr - StartPtr - 1);
845         }
846         /* 'Current directory' environment variable (i.e. of '=X:=' form) */
847         else // if (*SourcePtr == L'=')
848         {
849             /* First assume we have a possibly malformed environment variable */
850             NameType = EnvNameNotAPath;
851 
852             /* Check for a valid 'Current directory' environment variable */
853             if (IS_ALPHA(SourcePtr[1]) && SourcePtr[2] == L':' && SourcePtr[3] == L'=')
854             {
855                 /*
856                  * Small optimization: convert the path to short form only if
857                  * the current directory is not the root directory (i.e. not
858                  * of the '=X:=Y:\' form), otherwise just do a simple copy.
859                  */
860                 if ( wcslen(SourcePtr) >= ARRAYSIZE("=X:=Y:\\")-1 &&
861                      !( IS_ALPHA(SourcePtr[4]) && SourcePtr[5] == L':' &&
862                         IS_PATH_SEPARATOR(SourcePtr[6]) && SourcePtr[7] == UNICODE_NULL ) )
863                 {
864                     NameType = EnvNameSinglePath;
865 
866                     /* Copy the '=X:=' prefix */
867                     *DestPtr++ = SourcePtr[0];
868                     *DestPtr++ = SourcePtr[1];
869                     *DestPtr++ = SourcePtr[2];
870                     *DestPtr++ = SourcePtr[3];
871                     SourcePtr += 4;
872                 }
873             }
874             else
875             {
876                 /*
877                  * Invalid stuff starting with '=', i.e.:
878                  * =? (with '?' not being a letter)
879                  * =X??? (with '?' not being ":=" and not followed by something longer than 3 characters)
880                  * =X:=??? (with '?' not being "X:\\")
881                  *
882                  * 'NameType' is already set to 'EnvNameNotAPath'.
883                  */
884             }
885         }
886 
887 
888         /*
889          * 2. Copy the environment value and perform conversions accordingly.
890          */
891 
892         if (NameType == EnvNameNotAPath)
893         {
894             /* Copy everything, including the NULL terminator */
895             do
896             {
897                 *DestPtr++ = *SourcePtr;
898             } while (*SourcePtr++ != UNICODE_NULL);
899         }
900         else if (NameType == EnvNameSinglePath)
901         {
902             /* Convert the path to its short form */
903             NameLength = wcslen(SourcePtr);
904             NumChars = GetShortPathNameW(SourcePtr, DestPtr, NameLength + 1 + Remaining);
905             if (NumChars == 0 || NumChars > NameLength + Remaining)
906             {
907                 /* If the conversion failed, just copy the original value */
908                 RtlCopyMemory(DestPtr, SourcePtr, NameLength * sizeof(WCHAR));
909                 NumChars = NameLength;
910             }
911             DestPtr += NumChars;
912             if (NumChars > NameLength)
913                 Remaining -= (NumChars - NameLength);
914 
915             SourcePtr += NameLength;
916 
917             /* Copy the NULL terminator */
918             *DestPtr++ = *SourcePtr++;
919         }
920         else // if (NameType == EnvNameMultiplePath)
921         {
922             WCHAR Delimiter;
923 
924             /* Loop through the list of paths (delimited by ';') and convert each path to its short form */
925             do
926             {
927                 /* Copy any trailing ';' before going to the next path */
928                 while (*SourcePtr == L';')
929                 {
930                     *DestPtr++ = *SourcePtr++;
931                 }
932 
933                 StartPtr = SourcePtr;
934 
935                 /* Find the next path list delimiter or the NULL terminator */
936                 while (*SourcePtr != UNICODE_NULL && *SourcePtr != L';')
937                 {
938                     ++SourcePtr;
939                 }
940                 Delimiter = *SourcePtr;
941 
942                 NameLength = SourcePtr - StartPtr;
943                 if (NameLength)
944                 {
945                     /*
946                      * Temporarily replace the possible path list delimiter by NULL.
947                      * 'lpEnvironment' must point to a read+write memory buffer!
948                      */
949                     *SourcePtr = UNICODE_NULL;
950 
951                     NumChars = GetShortPathNameW(StartPtr, DestPtr, NameLength + 1 + Remaining);
952                     if ( NumChars == 0 ||
953                         (Delimiter == L';' ? NumChars > NameLength + Remaining
954                                            : NumChars > NameLength /* + Remaining ?? */) )
955                     {
956                         /* If the conversion failed, just copy the original value */
957                         RtlCopyMemory(DestPtr, StartPtr, NameLength * sizeof(WCHAR));
958                         NumChars = NameLength;
959                     }
960                     DestPtr += NumChars;
961                     if (NumChars > NameLength)
962                         Remaining -= (NumChars - NameLength);
963 
964                     /* If removed, restore the path list delimiter in the source environment value and copy it */
965                     if (Delimiter != UNICODE_NULL)
966                     {
967                         *SourcePtr = Delimiter;
968                         *DestPtr++ = *SourcePtr++;
969                     }
970                 }
971             } while (*SourcePtr != UNICODE_NULL);
972 
973             /* Copy the NULL terminator */
974             *DestPtr++ = *SourcePtr++;
975         }
976     }
977 
978     /* NULL-terminate the environment block */
979     *DestPtr++ = UNICODE_NULL;
980 
981     /* Initialize the Unicode string to hold it */
982     RtlInitEmptyUnicodeString(UnicodeEnv, NewEnvironment,
983                               (DestPtr - NewEnvironment) * sizeof(WCHAR));
984     UnicodeEnv->Length = UnicodeEnv->MaximumLength;
985 
986     /* Create its ANSI version */
987     Status = RtlUnicodeStringToAnsiString(AnsiEnv, UnicodeEnv, TRUE);
988     if (!NT_SUCCESS(Status))
989     {
990         /* Set last error if conversion failure */
991         BaseSetLastNTError(Status);
992     }
993     else
994     {
995         /* Everything went okay, so return success */
996         Success = TRUE;
997         NewEnvironment = NULL;
998     }
999 
1000 Cleanup:
1001     /* Cleanup path starts here, start by destroying the environment copy */
1002     if (!lpEnvironment && Environment) RtlDestroyEnvironment(Environment);
1003 
1004     /* See if we are here due to failure */
1005     if (NewEnvironment)
1006     {
1007         /* Initialize the paths to be empty */
1008         RtlInitEmptyUnicodeString(UnicodeEnv, NULL, 0);
1009         RtlInitEmptyAnsiString(AnsiEnv, NULL, 0);
1010 
1011         /* Free the environment copy */
1012         RegionSize = 0;
1013         Status = NtFreeVirtualMemory(NtCurrentProcess(),
1014                                      (PVOID*)&NewEnvironment,
1015                                      &RegionSize,
1016                                      MEM_RELEASE);
1017         ASSERT(NT_SUCCESS(Status));
1018     }
1019 
1020     /* Return the result */
1021     return Success;
1022 }
1023 
1024 BOOL
1025 NTAPI
1026 BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv,
1027                           IN PUNICODE_STRING UnicodeEnv)
1028 {
1029     SIZE_T Dummy = 0;
1030 
1031     /* Clear the ANSI buffer since Rtl creates this for us */
1032     if (AnsiEnv->Buffer) RtlFreeAnsiString(AnsiEnv);
1033 
1034     /* The Unicode buffer is build by hand, though */
1035     if (UnicodeEnv->Buffer)
1036     {
1037         /* So clear it through the API */
1038         NtFreeVirtualMemory(NtCurrentProcess(),
1039                             (PVOID*)&UnicodeEnv->Buffer,
1040                             &Dummy,
1041                             MEM_RELEASE);
1042     }
1043 
1044     /* All done */
1045     return TRUE;
1046 }
1047 
1048 
1049 /* Check whether a file is an OS/2 or a very old Windows executable
1050  * by testing on import of KERNEL.
1051  *
1052  * FIXME: is reading the module imports the only way of discerning
1053  *        old Windows binaries from OS/2 ones ? At least it seems so...
1054  */
1055 static DWORD WINAPI
1056 InternalIsOS2OrOldWin(HANDLE hFile, IMAGE_DOS_HEADER *mz, IMAGE_OS2_HEADER *ne)
1057 {
1058   DWORD CurPos;
1059   LPWORD modtab = NULL;
1060   LPSTR nametab = NULL;
1061   DWORD Read, Ret;
1062   int i;
1063 
1064   Ret = BINARY_OS216;
1065   CurPos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
1066 
1067   /* read modref table */
1068   if((SetFilePointer(hFile, mz->e_lfanew + ne->ne_modtab, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
1069      (!(modtab = HeapAlloc(GetProcessHeap(), 0, ne->ne_cmod * sizeof(WORD)))) ||
1070      (!(ReadFile(hFile, modtab, ne->ne_cmod * sizeof(WORD), &Read, NULL))) ||
1071      (Read != (DWORD)ne->ne_cmod * sizeof(WORD)))
1072   {
1073     goto broken;
1074   }
1075 
1076   /* read imported names table */
1077   if((SetFilePointer(hFile, mz->e_lfanew + ne->ne_imptab, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
1078      (!(nametab = HeapAlloc(GetProcessHeap(), 0, ne->ne_enttab - ne->ne_imptab))) ||
1079      (!(ReadFile(hFile, nametab, ne->ne_enttab - ne->ne_imptab, &Read, NULL))) ||
1080      (Read != (DWORD)ne->ne_enttab - ne->ne_imptab))
1081   {
1082     goto broken;
1083   }
1084 
1085   for(i = 0; i < ne->ne_cmod; i++)
1086   {
1087     LPSTR module;
1088     module = &nametab[modtab[i]];
1089     if(!strncmp(&module[1], "KERNEL", module[0]))
1090     {
1091       /* very old windows file */
1092       Ret = BINARY_WIN16;
1093       goto done;
1094     }
1095   }
1096 
1097   broken:
1098   DPRINT1("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
1099 
1100   done:
1101   HeapFree(GetProcessHeap(), 0, modtab);
1102   HeapFree(GetProcessHeap(), 0, nametab);
1103   SetFilePointer(hFile, CurPos, NULL, FILE_BEGIN);
1104   return Ret;
1105 }
1106 
1107 static DWORD WINAPI
1108 InternalGetBinaryType(HANDLE hFile)
1109 {
1110   union
1111   {
1112     struct
1113     {
1114       unsigned char magic[4];
1115       unsigned char ignored[12];
1116       unsigned short type;
1117     } elf;
1118     struct
1119     {
1120       unsigned long magic;
1121       unsigned long cputype;
1122       unsigned long cpusubtype;
1123       unsigned long filetype;
1124     } macho;
1125     IMAGE_DOS_HEADER mz;
1126   } Header;
1127   char magic[4];
1128   DWORD Read;
1129 
1130   if((SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
1131      (!ReadFile(hFile, &Header, sizeof(Header), &Read, NULL) ||
1132       (Read != sizeof(Header))))
1133   {
1134     return BINARY_UNKNOWN;
1135   }
1136 
1137   if(!memcmp(Header.elf.magic, "\177ELF", sizeof(Header.elf.magic)))
1138   {
1139     /* FIXME: we don't bother to check byte order, architecture, etc. */
1140     switch(Header.elf.type)
1141     {
1142       case 2:
1143         return BINARY_UNIX_EXE;
1144       case 3:
1145         return BINARY_UNIX_LIB;
1146     }
1147     return BINARY_UNKNOWN;
1148   }
1149 
1150   /* Mach-o File with Endian set to Big Endian or Little Endian*/
1151   if(Header.macho.magic == 0xFEEDFACE ||
1152      Header.macho.magic == 0xCEFAEDFE)
1153   {
1154     switch(Header.macho.filetype)
1155     {
1156       case 0x8:
1157         /* MH_BUNDLE */
1158         return BINARY_UNIX_LIB;
1159     }
1160     return BINARY_UNKNOWN;
1161   }
1162 
1163   /* Not ELF, try DOS */
1164   if(Header.mz.e_magic == IMAGE_DOS_SIGNATURE)
1165   {
1166     /* We do have a DOS image so we will now try to seek into
1167      * the file by the amount indicated by the field
1168      * "Offset to extended header" and read in the
1169      * "magic" field information at that location.
1170      * This will tell us if there is more header information
1171      * to read or not.
1172      */
1173     if((SetFilePointer(hFile, Header.mz.e_lfanew, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
1174        (!ReadFile(hFile, magic, sizeof(magic), &Read, NULL) ||
1175         (Read != sizeof(magic))))
1176     {
1177       return BINARY_DOS;
1178     }
1179 
1180     /* Reading the magic field succeeded so
1181      * we will try to determine what type it is.
1182      */
1183     if(!memcmp(magic, "PE\0\0", sizeof(magic)))
1184     {
1185       IMAGE_FILE_HEADER FileHeader;
1186       if(!ReadFile(hFile, &FileHeader, sizeof(IMAGE_FILE_HEADER), &Read, NULL) ||
1187          (Read != sizeof(IMAGE_FILE_HEADER)))
1188       {
1189         return BINARY_DOS;
1190       }
1191 
1192       /* FIXME - detect 32/64 bit */
1193 
1194       if(FileHeader.Characteristics & IMAGE_FILE_DLL)
1195         return BINARY_PE_DLL32;
1196       return BINARY_PE_EXE32;
1197     }
1198 
1199     if(!memcmp(magic, "NE", 2))
1200     {
1201       /* This is a Windows executable (NE) header.  This can
1202        * mean either a 16-bit OS/2 or a 16-bit Windows or even a
1203        * DOS program (running under a DOS extender).  To decide
1204        * which, we'll have to read the NE header.
1205        */
1206       IMAGE_OS2_HEADER ne;
1207       if((SetFilePointer(hFile, Header.mz.e_lfanew, NULL, FILE_BEGIN) == 1) ||
1208          !ReadFile(hFile, &ne, sizeof(IMAGE_OS2_HEADER), &Read, NULL) ||
1209          (Read != sizeof(IMAGE_OS2_HEADER)))
1210       {
1211         /* Couldn't read header, so abort. */
1212         return BINARY_DOS;
1213       }
1214 
1215       switch(ne.ne_exetyp)
1216       {
1217         case 2:
1218           return BINARY_WIN16;
1219         case 5:
1220           return BINARY_DOS;
1221         default:
1222           return InternalIsOS2OrOldWin(hFile, &Header.mz, &ne);
1223       }
1224     }
1225     return BINARY_DOS;
1226   }
1227   return BINARY_UNKNOWN;
1228 }
1229 
1230 /*
1231  * @implemented
1232  */
1233 BOOL
1234 WINAPI
1235 GetBinaryTypeW (
1236     LPCWSTR lpApplicationName,
1237     LPDWORD lpBinaryType
1238     )
1239 {
1240   HANDLE hFile;
1241   DWORD BinType;
1242 
1243   if(!lpApplicationName || !lpBinaryType)
1244   {
1245     SetLastError(ERROR_INVALID_PARAMETER);
1246     return FALSE;
1247   }
1248 
1249   hFile = CreateFileW(lpApplicationName, GENERIC_READ, FILE_SHARE_READ, NULL,
1250                       OPEN_EXISTING, 0, 0);
1251   if(hFile == INVALID_HANDLE_VALUE)
1252   {
1253     return FALSE;
1254   }
1255 
1256   BinType = InternalGetBinaryType(hFile);
1257   CloseHandle(hFile);
1258 
1259   switch(BinType)
1260   {
1261     case BINARY_UNKNOWN:
1262     {
1263       WCHAR *dot;
1264 
1265       /*
1266        * guess from filename
1267        */
1268       if(!(dot = wcsrchr(lpApplicationName, L'.')))
1269       {
1270         return FALSE;
1271       }
1272       if(!lstrcmpiW(dot, L".COM"))
1273       {
1274         *lpBinaryType = SCS_DOS_BINARY;
1275         return TRUE;
1276       }
1277       if(!lstrcmpiW(dot, L".PIF"))
1278       {
1279         *lpBinaryType = SCS_PIF_BINARY;
1280         return TRUE;
1281       }
1282       return FALSE;
1283     }
1284     case BINARY_PE_EXE32:
1285     case BINARY_PE_DLL32:
1286     {
1287       *lpBinaryType = SCS_32BIT_BINARY;
1288       return TRUE;
1289     }
1290     case BINARY_PE_EXE64:
1291     case BINARY_PE_DLL64:
1292     {
1293       *lpBinaryType = SCS_64BIT_BINARY;
1294       return TRUE;
1295     }
1296     case BINARY_WIN16:
1297     {
1298       *lpBinaryType = SCS_WOW_BINARY;
1299       return TRUE;
1300     }
1301     case BINARY_OS216:
1302     {
1303       *lpBinaryType = SCS_OS216_BINARY;
1304       return TRUE;
1305     }
1306     case BINARY_DOS:
1307     {
1308       *lpBinaryType = SCS_DOS_BINARY;
1309       return TRUE;
1310     }
1311     case BINARY_UNIX_EXE:
1312     case BINARY_UNIX_LIB:
1313     {
1314       return FALSE;
1315     }
1316   }
1317 
1318   DPRINT1("Invalid binary type %lu returned!\n", BinType);
1319   return FALSE;
1320 }
1321 
1322 /*
1323  * @implemented
1324  */
1325 BOOL
1326 WINAPI
1327 GetBinaryTypeA(IN LPCSTR lpApplicationName,
1328                OUT LPDWORD lpBinaryType)
1329 {
1330     ANSI_STRING ApplicationNameString;
1331     UNICODE_STRING ApplicationNameW;
1332     BOOL StringAllocated = FALSE, Result;
1333     NTSTATUS Status;
1334 
1335     RtlInitAnsiString(&ApplicationNameString, lpApplicationName);
1336 
1337     if (ApplicationNameString.Length * sizeof(WCHAR) >= NtCurrentTeb()->StaticUnicodeString.MaximumLength)
1338     {
1339         StringAllocated = TRUE;
1340         Status = RtlAnsiStringToUnicodeString(&ApplicationNameW, &ApplicationNameString, TRUE);
1341     }
1342     else
1343     {
1344         Status = RtlAnsiStringToUnicodeString(&(NtCurrentTeb()->StaticUnicodeString), &ApplicationNameString, FALSE);
1345     }
1346 
1347     if (!NT_SUCCESS(Status))
1348     {
1349         BaseSetLastNTError(Status);
1350         return FALSE;
1351     }
1352 
1353     if (StringAllocated)
1354     {
1355         Result = GetBinaryTypeW(ApplicationNameW.Buffer, lpBinaryType);
1356         RtlFreeUnicodeString(&ApplicationNameW);
1357     }
1358     else
1359     {
1360         Result = GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString.Buffer, lpBinaryType);
1361     }
1362 
1363     return Result;
1364 }
1365 
1366 /*
1367  * @unimplemented
1368  */
1369 BOOL
1370 WINAPI
1371 CmdBatNotification (
1372     DWORD   Unknown
1373     )
1374 {
1375     STUB;
1376     return FALSE;
1377 }
1378 
1379 /*
1380  * @implemented
1381  */
1382 VOID
1383 WINAPI
1384 ExitVDM(BOOL IsWow, ULONG iWowTask)
1385 {
1386     BASE_API_MESSAGE ApiMessage;
1387     PBASE_EXIT_VDM ExitVdm = &ApiMessage.Data.ExitVDMRequest;
1388 
1389     /* Setup the input parameters */
1390     ExitVdm->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
1391     ExitVdm->iWowTask = IsWow ? iWowTask : 0; /* Always zero for DOS tasks */
1392     ExitVdm->WaitObjectForVDM = NULL;
1393 
1394     /* Call CSRSS */
1395     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1396                         NULL,
1397                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitVDM),
1398                         sizeof(*ExitVdm));
1399 
1400     /* Close the returned wait object handle, if any */
1401     if (NT_SUCCESS(ApiMessage.Status) && (ExitVdm->WaitObjectForVDM != NULL))
1402     {
1403         CloseHandle(ExitVdm->WaitObjectForVDM);
1404     }
1405 }
1406 
1407 /*
1408  * @implemented
1409  */
1410 BOOL
1411 WINAPI
1412 GetNextVDMCommand(PVDM_COMMAND_INFO CommandData)
1413 {
1414     BOOL Success = FALSE;
1415     NTSTATUS Status;
1416     BASE_API_MESSAGE ApiMessage;
1417     PBASE_GET_NEXT_VDM_COMMAND GetNextVdmCommand = &ApiMessage.Data.GetNextVDMCommandRequest;
1418     PBASE_IS_FIRST_VDM IsFirstVdm = &ApiMessage.Data.IsFirstVDMRequest;
1419     PBASE_SET_REENTER_COUNT SetReenterCount = &ApiMessage.Data.SetReenterCountRequest;
1420     PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
1421     ULONG NumStrings = 0;
1422 
1423     /*
1424      * Special case to test whether the VDM is the first one.
1425      */
1426     if (CommandData == NULL)
1427     {
1428         /* Call CSRSS */
1429         CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1430                             NULL,
1431                             CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepIsFirstVDM),
1432                             sizeof(*IsFirstVdm));
1433         if (!NT_SUCCESS(ApiMessage.Status))
1434         {
1435             BaseSetLastNTError(ApiMessage.Status);
1436             return FALSE;
1437         }
1438 
1439         /* Return TRUE if this is the first VDM */
1440         return IsFirstVdm->FirstVDM;
1441     }
1442 
1443     /* CommandData != NULL */
1444 
1445     /*
1446      * Special case to increment or decrement the reentrancy count.
1447      */
1448     if ((CommandData->VDMState == VDM_INC_REENTER_COUNT) ||
1449         (CommandData->VDMState == VDM_DEC_REENTER_COUNT))
1450     {
1451         /* Setup the input parameters */
1452         SetReenterCount->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
1453         SetReenterCount->fIncDec = CommandData->VDMState;
1454 
1455         /* Call CSRSS */
1456         CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1457                             NULL,
1458                             CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetReenterCount),
1459                             sizeof(*SetReenterCount));
1460         if (!NT_SUCCESS(ApiMessage.Status))
1461         {
1462             BaseSetLastNTError(ApiMessage.Status);
1463             return FALSE;
1464         }
1465 
1466         return TRUE;
1467     }
1468 
1469     /*
1470      * TODO!
1471      * Special case to retrieve or set WOW information.
1472      */
1473     // TODO: if CommandData->VDMState & (VDM_LIST_WOW_PROCESSES | VDM_LIST_WOW_TASKS | VDM_ADD_WOW_TASK)
1474     // then call BasepGetNextVDMCommand in a simpler way!
1475 
1476     /*
1477      * Regular case.
1478      */
1479 
1480     /* Clear the structure */
1481     RtlZeroMemory(GetNextVdmCommand, sizeof(*GetNextVdmCommand));
1482 
1483     /* Setup the input parameters */
1484     GetNextVdmCommand->iTask = CommandData->TaskId;
1485     GetNextVdmCommand->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
1486     GetNextVdmCommand->CmdLen = CommandData->CmdLen;
1487     GetNextVdmCommand->AppLen = CommandData->AppLen;
1488     GetNextVdmCommand->PifLen = CommandData->PifLen;
1489     GetNextVdmCommand->CurDirectoryLen = CommandData->CurDirectoryLen;
1490     GetNextVdmCommand->EnvLen = CommandData->EnvLen;
1491     GetNextVdmCommand->DesktopLen = CommandData->DesktopLen;
1492     GetNextVdmCommand->TitleLen = CommandData->TitleLen;
1493     GetNextVdmCommand->ReservedLen = CommandData->ReservedLen;
1494     GetNextVdmCommand->VDMState = CommandData->VDMState;
1495 
1496     /* Count the number of strings */
1497     if (CommandData->CmdLen) NumStrings++;
1498     if (CommandData->AppLen) NumStrings++;
1499     if (CommandData->PifLen) NumStrings++;
1500     if (CommandData->CurDirectoryLen) NumStrings++;
1501     if (CommandData->EnvLen) NumStrings++;
1502     if (CommandData->DesktopLen) NumStrings++;
1503     if (CommandData->TitleLen) NumStrings++;
1504     if (CommandData->ReservedLen) NumStrings++;
1505 
1506     /* Allocate the capture buffer */
1507     CaptureBuffer = CsrAllocateCaptureBuffer(NumStrings + 1,
1508                                              GetNextVdmCommand->CmdLen
1509                                              + GetNextVdmCommand->AppLen
1510                                              + GetNextVdmCommand->PifLen
1511                                              + GetNextVdmCommand->CurDirectoryLen
1512                                              + GetNextVdmCommand->EnvLen
1513                                              + GetNextVdmCommand->DesktopLen
1514                                              + GetNextVdmCommand->TitleLen
1515                                              + GetNextVdmCommand->ReservedLen
1516                                              + sizeof(*GetNextVdmCommand->StartupInfo));
1517     if (CaptureBuffer == NULL)
1518     {
1519         BaseSetLastNTError(STATUS_NO_MEMORY);
1520         goto Cleanup;
1521     }
1522 
1523     /* Capture the data */
1524 
1525     CsrAllocateMessagePointer(CaptureBuffer,
1526                               sizeof(*GetNextVdmCommand->StartupInfo),
1527                               (PVOID*)&GetNextVdmCommand->StartupInfo);
1528 
1529     if (CommandData->CmdLen)
1530     {
1531         CsrAllocateMessagePointer(CaptureBuffer,
1532                                   CommandData->CmdLen,
1533                                   (PVOID*)&GetNextVdmCommand->CmdLine);
1534     }
1535 
1536     if (CommandData->AppLen)
1537     {
1538         CsrAllocateMessagePointer(CaptureBuffer,
1539                                   CommandData->AppLen,
1540                                   (PVOID*)&GetNextVdmCommand->AppName);
1541     }
1542 
1543     if (CommandData->PifLen)
1544     {
1545         CsrAllocateMessagePointer(CaptureBuffer,
1546                                   CommandData->PifLen,
1547                                   (PVOID*)&GetNextVdmCommand->PifFile);
1548     }
1549 
1550     if (CommandData->CurDirectoryLen)
1551     {
1552         CsrAllocateMessagePointer(CaptureBuffer,
1553                                   CommandData->CurDirectoryLen,
1554                                   (PVOID*)&GetNextVdmCommand->CurDirectory);
1555     }
1556 
1557     if (CommandData->EnvLen)
1558     {
1559         CsrAllocateMessagePointer(CaptureBuffer,
1560                                   CommandData->EnvLen,
1561                                   (PVOID*)&GetNextVdmCommand->Env);
1562     }
1563 
1564     if (CommandData->DesktopLen)
1565     {
1566         CsrAllocateMessagePointer(CaptureBuffer,
1567                                   CommandData->DesktopLen,
1568                                   (PVOID*)&GetNextVdmCommand->Desktop);
1569     }
1570 
1571     if (CommandData->TitleLen)
1572     {
1573         CsrAllocateMessagePointer(CaptureBuffer,
1574                                   CommandData->TitleLen,
1575                                   (PVOID*)&GetNextVdmCommand->Title);
1576     }
1577 
1578     if (CommandData->ReservedLen)
1579     {
1580         CsrAllocateMessagePointer(CaptureBuffer,
1581                                   CommandData->ReservedLen,
1582                                   (PVOID*)&GetNextVdmCommand->Reserved);
1583     }
1584 
1585     while (TRUE)
1586     {
1587         /* Call CSRSS */
1588         Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1589                                      CaptureBuffer,
1590                                      CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetNextVDMCommand),
1591                                      sizeof(*GetNextVdmCommand));
1592 
1593         /* Exit the waiting loop if we did not receive any event handle */
1594         if (GetNextVdmCommand->WaitObjectForVDM == NULL)
1595             break;
1596 
1597         /* Wait for the event to become signaled and try again */
1598         Status = NtWaitForSingleObject(GetNextVdmCommand->WaitObjectForVDM,
1599                                        FALSE, NULL);
1600         if (Status != STATUS_SUCCESS)
1601         {
1602             /* Fail if we timed out, or if some other error happened */
1603             BaseSetLastNTError(Status);
1604             goto Cleanup;
1605         }
1606 
1607         /* Set the retry flag, clear the exit code, and retry a query */
1608         GetNextVdmCommand->VDMState |= VDM_FLAG_RETRY;
1609         GetNextVdmCommand->ExitCode = 0;
1610     }
1611 
1612     if (!NT_SUCCESS(Status))
1613     {
1614         if (Status == STATUS_INVALID_PARAMETER)
1615         {
1616             /*
1617              * One of the buffer lengths was less than required. Store the correct ones.
1618              * Note that the status code is not STATUS_BUFFER_TOO_SMALL as one would expect,
1619              * in order to keep compatibility with Windows 2003 BASESRV.DLL.
1620              */
1621             CommandData->CmdLen = GetNextVdmCommand->CmdLen;
1622             CommandData->AppLen = GetNextVdmCommand->AppLen;
1623             CommandData->PifLen = GetNextVdmCommand->PifLen;
1624             CommandData->CurDirectoryLen = GetNextVdmCommand->CurDirectoryLen;
1625             CommandData->EnvLen      = GetNextVdmCommand->EnvLen;
1626             CommandData->DesktopLen  = GetNextVdmCommand->DesktopLen;
1627             CommandData->TitleLen    = GetNextVdmCommand->TitleLen;
1628             CommandData->ReservedLen = GetNextVdmCommand->ReservedLen;
1629         }
1630         else
1631         {
1632             /* Any other failure */
1633             CommandData->CmdLen = 0;
1634             CommandData->AppLen = 0;
1635             CommandData->PifLen = 0;
1636             CommandData->CurDirectoryLen = 0;
1637             CommandData->EnvLen      = 0;
1638             CommandData->DesktopLen  = 0;
1639             CommandData->TitleLen    = 0;
1640             CommandData->ReservedLen = 0;
1641         }
1642 
1643         BaseSetLastNTError(Status);
1644         goto Cleanup;
1645     }
1646 
1647     /* Write back the standard handles */
1648     CommandData->StdIn  = GetNextVdmCommand->StdIn;
1649     CommandData->StdOut = GetNextVdmCommand->StdOut;
1650     CommandData->StdErr = GetNextVdmCommand->StdErr;
1651 
1652     /* Write back the startup info */
1653     RtlMoveMemory(&CommandData->StartupInfo,
1654                   GetNextVdmCommand->StartupInfo,
1655                   sizeof(*GetNextVdmCommand->StartupInfo));
1656 
1657     if (CommandData->CmdLen)
1658     {
1659         /* Write back the command line */
1660         RtlMoveMemory(CommandData->CmdLine,
1661                       GetNextVdmCommand->CmdLine,
1662                       GetNextVdmCommand->CmdLen);
1663 
1664         /* Set the actual length */
1665         CommandData->CmdLen = GetNextVdmCommand->CmdLen;
1666     }
1667 
1668     if (CommandData->AppLen)
1669     {
1670         /* Write back the application name */
1671         RtlMoveMemory(CommandData->AppName,
1672                       GetNextVdmCommand->AppName,
1673                       GetNextVdmCommand->AppLen);
1674 
1675         /* Set the actual length */
1676         CommandData->AppLen = GetNextVdmCommand->AppLen;
1677     }
1678 
1679     if (CommandData->PifLen)
1680     {
1681         /* Write back the PIF file name */
1682         RtlMoveMemory(CommandData->PifFile,
1683                       GetNextVdmCommand->PifFile,
1684                       GetNextVdmCommand->PifLen);
1685 
1686         /* Set the actual length */
1687         CommandData->PifLen = GetNextVdmCommand->PifLen;
1688     }
1689 
1690     if (CommandData->CurDirectoryLen)
1691     {
1692         /* Write back the current directory */
1693         RtlMoveMemory(CommandData->CurDirectory,
1694                       GetNextVdmCommand->CurDirectory,
1695                       GetNextVdmCommand->CurDirectoryLen);
1696 
1697         /* Set the actual length */
1698         CommandData->CurDirectoryLen = GetNextVdmCommand->CurDirectoryLen;
1699     }
1700 
1701     if (CommandData->EnvLen)
1702     {
1703         /* Write back the environment */
1704         RtlMoveMemory(CommandData->Env,
1705                       GetNextVdmCommand->Env,
1706                       GetNextVdmCommand->EnvLen);
1707 
1708         /* Set the actual length */
1709         CommandData->EnvLen = GetNextVdmCommand->EnvLen;
1710     }
1711 
1712     if (CommandData->DesktopLen)
1713     {
1714         /* Write back the desktop name */
1715         RtlMoveMemory(CommandData->Desktop,
1716                       GetNextVdmCommand->Desktop,
1717                       GetNextVdmCommand->DesktopLen);
1718 
1719         /* Set the actual length */
1720         CommandData->DesktopLen = GetNextVdmCommand->DesktopLen;
1721     }
1722 
1723     if (CommandData->TitleLen)
1724     {
1725         /* Write back the title */
1726         RtlMoveMemory(CommandData->Title,
1727                       GetNextVdmCommand->Title,
1728                       GetNextVdmCommand->TitleLen);
1729 
1730         /* Set the actual length */
1731         CommandData->TitleLen = GetNextVdmCommand->TitleLen;
1732     }
1733 
1734     if (CommandData->ReservedLen)
1735     {
1736         /* Write back the reserved parameter */
1737         RtlMoveMemory(CommandData->Reserved,
1738                       GetNextVdmCommand->Reserved,
1739                       GetNextVdmCommand->ReservedLen);
1740 
1741         /* Set the actual length */
1742         CommandData->ReservedLen = GetNextVdmCommand->ReservedLen;
1743     }
1744 
1745     /* Write the remaining output parameters */
1746     CommandData->TaskId        = GetNextVdmCommand->iTask;
1747     CommandData->CreationFlags = GetNextVdmCommand->dwCreationFlags;
1748     CommandData->CodePage      = GetNextVdmCommand->CodePage;
1749     CommandData->ExitCode      = GetNextVdmCommand->ExitCode;
1750     CommandData->CurrentDrive  = GetNextVdmCommand->CurrentDrive;
1751     CommandData->VDMState      = GetNextVdmCommand->VDMState;
1752     CommandData->ComingFromBat = GetNextVdmCommand->fComingFromBat;
1753 
1754     /* It was successful */
1755     Success = TRUE;
1756 
1757 Cleanup:
1758     if (CaptureBuffer != NULL) CsrFreeCaptureBuffer(CaptureBuffer);
1759     return Success;
1760 }
1761 
1762 
1763 /*
1764  * @implemented
1765  */
1766 DWORD
1767 WINAPI
1768 GetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs)
1769 {
1770     BASE_API_MESSAGE ApiMessage;
1771     PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest;
1772     PCSR_CAPTURE_BUFFER CaptureBuffer;
1773 
1774     /* Allocate the capture buffer */
1775     CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs);
1776     if (CaptureBuffer == NULL)
1777     {
1778         BaseSetLastNTError(STATUS_NO_MEMORY);
1779         return 0;
1780     }
1781 
1782     /* Setup the input parameters */
1783     VDMCurrentDirsRequest->cchCurDirs = cchCurDirs;
1784     CsrAllocateMessagePointer(CaptureBuffer,
1785                               cchCurDirs,
1786                               (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs);
1787 
1788     /* Call CSRSS */
1789     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1790                         CaptureBuffer,
1791                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetVDMCurDirs),
1792                         sizeof(*VDMCurrentDirsRequest));
1793 
1794     /* Set the last error */
1795     BaseSetLastNTError(ApiMessage.Status);
1796 
1797     if (NT_SUCCESS(ApiMessage.Status))
1798     {
1799         /* Copy the result */
1800         RtlMoveMemory(lpszzCurDirs, VDMCurrentDirsRequest->lpszzCurDirs, cchCurDirs);
1801     }
1802 
1803     /* Free the capture buffer */
1804     CsrFreeCaptureBuffer(CaptureBuffer);
1805 
1806     /* Return the size if it was successful, or if the buffer was too small */
1807     return (NT_SUCCESS(ApiMessage.Status) || (ApiMessage.Status == STATUS_BUFFER_TOO_SMALL))
1808            ? VDMCurrentDirsRequest->cchCurDirs : 0;
1809 }
1810 
1811 
1812 /*
1813  * @implemented (undocumented)
1814  */
1815 BOOL
1816 WINAPI
1817 RegisterConsoleVDM(IN DWORD dwRegisterFlags,
1818                    IN HANDLE hStartHardwareEvent,
1819                    IN HANDLE hEndHardwareEvent,
1820                    IN HANDLE hErrorHardwareEvent,
1821                    IN DWORD dwUnusedVar,
1822                    OUT LPDWORD lpVideoStateLength,
1823                    OUT PVOID* lpVideoState, // PVIDEO_HARDWARE_STATE_HEADER*
1824                    IN PVOID lpUnusedBuffer,
1825                    IN DWORD dwUnusedBufferLength,
1826                    IN COORD dwVDMBufferSize,
1827                    OUT PVOID* lpVDMBuffer)
1828 {
1829     BOOL Success;
1830     CONSOLE_API_MESSAGE ApiMessage;
1831     PCONSOLE_REGISTERVDM RegisterVDMRequest = &ApiMessage.Data.RegisterVDMRequest;
1832     PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
1833 
1834     /* Set up the data to send to the Console Server */
1835     RegisterVDMRequest->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
1836     RegisterVDMRequest->RegisterFlags = dwRegisterFlags;
1837 
1838     if (dwRegisterFlags != 0)
1839     {
1840         RegisterVDMRequest->StartHardwareEvent = hStartHardwareEvent;
1841         RegisterVDMRequest->EndHardwareEvent   = hEndHardwareEvent;
1842         RegisterVDMRequest->ErrorHardwareEvent = hErrorHardwareEvent;
1843 
1844         RegisterVDMRequest->VDMBufferSize = dwVDMBufferSize;
1845 
1846 #if 0
1847         RegisterVDMRequest->UnusedBufferLength = dwUnusedBufferLength;
1848 
1849         /* Allocate a Capture Buffer */
1850         CaptureBuffer = CsrAllocateCaptureBuffer(1, dwUnusedBufferLength);
1851         if (CaptureBuffer == NULL)
1852         {
1853             DPRINT1("CsrAllocateCaptureBuffer failed!\n");
1854             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1855             return FALSE;
1856         }
1857 
1858         /* Capture the buffer to write */
1859         CsrCaptureMessageBuffer(CaptureBuffer,
1860                                 (PVOID)lpUnusedBuffer,
1861                                 dwUnusedBufferLength,
1862                                 (PVOID*)&RegisterVDMRequest->UnusedBuffer);
1863 #endif
1864     }
1865     else
1866     {
1867         // CaptureBuffer = NULL;
1868     }
1869 
1870     /* Call the server */
1871     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1872                         CaptureBuffer,
1873                         CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepRegisterVDM),
1874                         sizeof(*RegisterVDMRequest));
1875 
1876     /* Check for success */
1877     Success = NT_SUCCESS(ApiMessage.Status);
1878 
1879     /* Release the capture buffer if needed */
1880     if (CaptureBuffer) CsrFreeCaptureBuffer(CaptureBuffer);
1881 
1882     /* Retrieve the results */
1883     if (Success)
1884     {
1885         if (dwRegisterFlags != 0)
1886         {
1887             _SEH2_TRY
1888             {
1889                 *lpVideoStateLength = RegisterVDMRequest->VideoStateLength;
1890                 *lpVideoState       = RegisterVDMRequest->VideoState;
1891                 *lpVDMBuffer        = RegisterVDMRequest->VDMBuffer;
1892             }
1893             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1894             {
1895                 SetLastError(ERROR_INVALID_ACCESS);
1896                 Success = FALSE;
1897             }
1898             _SEH2_END;
1899         }
1900     }
1901     else
1902     {
1903         BaseSetLastNTError(ApiMessage.Status);
1904     }
1905 
1906     /* Return success status */
1907     return Success;
1908 }
1909 
1910 
1911 /*
1912  * @unimplemented
1913  */
1914 BOOL
1915 WINAPI
1916 RegisterWowBaseHandlers (
1917     DWORD   Unknown0
1918     )
1919 {
1920     STUB;
1921     return FALSE;
1922 }
1923 
1924 
1925 /*
1926  * @unimplemented
1927  */
1928 BOOL
1929 WINAPI
1930 RegisterWowExec (
1931     DWORD   Unknown0
1932     )
1933 {
1934     STUB;
1935     return FALSE;
1936 }
1937 
1938 
1939 /*
1940  * @implemented
1941  */
1942 BOOL
1943 WINAPI
1944 SetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs)
1945 {
1946     BASE_API_MESSAGE ApiMessage;
1947     PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest;
1948     PCSR_CAPTURE_BUFFER CaptureBuffer;
1949 
1950     /* Allocate the capture buffer */
1951     CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs);
1952     if (CaptureBuffer == NULL)
1953     {
1954         BaseSetLastNTError(STATUS_NO_MEMORY);
1955         return FALSE;
1956     }
1957 
1958     /* Setup the input parameters */
1959     VDMCurrentDirsRequest->cchCurDirs = cchCurDirs;
1960     CsrCaptureMessageBuffer(CaptureBuffer,
1961                             lpszzCurDirs,
1962                             cchCurDirs,
1963                             (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs);
1964 
1965     /* Call CSRSS */
1966     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1967                         CaptureBuffer,
1968                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetVDMCurDirs),
1969                         sizeof(*VDMCurrentDirsRequest));
1970 
1971     /* Free the capture buffer */
1972     CsrFreeCaptureBuffer(CaptureBuffer);
1973 
1974     /* Set the last error */
1975     BaseSetLastNTError(ApiMessage.Status);
1976 
1977     return NT_SUCCESS(ApiMessage.Status) ? TRUE : FALSE;
1978 }
1979 
1980 /*
1981  * @unimplemented
1982  */
1983 DWORD
1984 WINAPI
1985 VDMConsoleOperation (
1986     DWORD   Unknown0,
1987     DWORD   Unknown1
1988     )
1989 {
1990     STUB;
1991     return 0;
1992 }
1993 
1994 
1995 /*
1996  * @unimplemented
1997  */
1998 BOOL
1999 WINAPI
2000 VDMOperationStarted(IN ULONG Unknown0)
2001 {
2002     DPRINT1("VDMOperationStarted(%d)\n", Unknown0);
2003 
2004     return BaseUpdateVDMEntry(VdmEntryUpdateControlCHandler,
2005                               NULL,
2006                               0,
2007                               Unknown0);
2008 }
2009