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