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)
9 /* INCLUDES *******************************************************************/
16 /* TYPES **********************************************************************/
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)
30 typedef enum _ENV_NAME_TYPE
37 typedef struct _ENV_INFO
39 ENV_NAME_TYPE NameType
;
42 } ENV_INFO
, *PENV_INFO
;
44 /* GLOBALS ********************************************************************/
46 // NOTE: We cannot use ARRAYSIZE in this macro. GCC would complain otherwise.
47 #define ENV_NAME_ENTRY(type, name) \
48 {(type), (sizeof(name)/sizeof(*((ENV_INFO*)0)->Name)) - 1, (name)}
50 static ENV_INFO BasepEnvNameType
[] =
52 ENV_NAME_ENTRY(EnvNameMultiplePath
, L
"PATH"),
53 ENV_NAME_ENTRY(EnvNameSinglePath
, L
"WINDIR"),
54 ENV_NAME_ENTRY(EnvNameSinglePath
, L
"SYSTEMROOT"),
55 ENV_NAME_ENTRY(EnvNameMultiplePath
, L
"TEMP"),
56 ENV_NAME_ENTRY(EnvNameMultiplePath
, L
"TMP"),
59 static UNICODE_STRING BaseDotComSuffixName
= RTL_CONSTANT_STRING(L
".com");
60 static UNICODE_STRING BaseDotPifSuffixName
= RTL_CONSTANT_STRING(L
".pif");
61 static UNICODE_STRING BaseDotExeSuffixName
= RTL_CONSTANT_STRING(L
".exe");
63 /* FUNCTIONS ******************************************************************/
67 BaseIsDosApplication(IN PUNICODE_STRING PathName
,
70 UNICODE_STRING String
;
73 String
.Length
= BaseDotComSuffixName
.Length
;
74 String
.Buffer
= &PathName
->Buffer
[(PathName
->Length
- String
.Length
) / sizeof(WCHAR
)];
75 if (RtlEqualUnicodeString(&String
, &BaseDotComSuffixName
, TRUE
)) return BINARY_TYPE_COM
;
78 String
.Length
= BaseDotPifSuffixName
.Length
;
79 String
.Buffer
= &PathName
->Buffer
[(PathName
->Length
- String
.Length
) / sizeof(WCHAR
)];
80 if (RtlEqualUnicodeString(&String
, &BaseDotPifSuffixName
, TRUE
)) return BINARY_TYPE_PIF
;
83 String
.Length
= BaseDotExeSuffixName
.Length
;
84 String
.Buffer
= &PathName
->Buffer
[(PathName
->Length
- String
.Length
) / sizeof(WCHAR
)];
85 if (RtlEqualUnicodeString(&String
, &BaseDotExeSuffixName
, TRUE
)) return BINARY_TYPE_EXE
;
92 BaseCheckVDM(IN ULONG BinaryType
,
93 IN PCWCH ApplicationName
,
95 IN PCWCH CurrentDirectory
,
96 IN PANSI_STRING AnsiEnvironment
,
97 IN PBASE_API_MESSAGE ApiMessage
,
99 IN DWORD CreationFlags
,
100 IN LPSTARTUPINFOW StartupInfo
,
101 IN HANDLE hUserToken OPTIONAL
)
104 PBASE_CHECK_VDM CheckVdm
= &ApiMessage
->Data
.CheckVDMRequest
;
105 PCSR_CAPTURE_BUFFER CaptureBuffer
;
106 PWCHAR CurrentDir
= NULL
;
107 PWCHAR ShortAppName
= NULL
;
108 PWCHAR ShortCurrentDir
= NULL
;
110 PCHAR AnsiCmdLine
= NULL
;
111 PCHAR AnsiAppName
= NULL
;
112 PCHAR AnsiCurDirectory
= NULL
;
113 PCHAR AnsiDesktop
= NULL
;
114 PCHAR AnsiTitle
= NULL
;
115 PCHAR AnsiReserved
= NULL
;
116 STARTUPINFOA AnsiStartupInfo
;
117 ULONG NumStrings
= 5;
119 /* Parameters validation */
120 if (ApplicationName
== NULL
|| CommandLine
== NULL
)
122 return STATUS_INVALID_PARAMETER
;
125 /* Trim leading whitespace from ApplicationName */
126 while (*ApplicationName
== L
' ' || *ApplicationName
== L
'\t')
129 /* Calculate the size of the short application name */
130 Length
= GetShortPathNameW(ApplicationName
, NULL
, 0);
133 Status
= STATUS_OBJECT_PATH_INVALID
;
137 /* Allocate memory for the short application name */
138 ShortAppName
= (PWCHAR
)RtlAllocateHeap(RtlGetProcessHeap(),
140 Length
* sizeof(WCHAR
));
143 Status
= STATUS_NO_MEMORY
;
147 /* Get the short application name */
148 if (GetShortPathNameW(ApplicationName
, ShortAppName
, Length
) == 0)
150 /* Try to determine which error occurred */
151 switch (GetLastError())
153 case ERROR_NOT_ENOUGH_MEMORY
:
155 Status
= STATUS_NO_MEMORY
;
159 case ERROR_INVALID_PARAMETER
:
161 Status
= STATUS_INVALID_PARAMETER
;
167 Status
= STATUS_OBJECT_PATH_INVALID
;
174 /* Trim leading whitespace from CommandLine */
175 while (*CommandLine
== L
' ' || *CommandLine
== L
'\t')
179 * CommandLine is usually formatted as: 'ApplicationName param0 ...'.
180 * So we want to strip the first token (ApplicationName) from it.
181 * Two cases are in fact possible:
182 * - either the first token is indeed ApplicationName, so we just skip it;
183 * - or the first token is not exactly ApplicationName, because it happened
184 * that somebody else already preprocessed CommandLine. Therefore we
185 * suppose that the first token corresponds to an application name and
186 * we skip it. Care should be taken when quotes are present in this token.
190 /* The first part of CommandLine should be the ApplicationName... */
191 Length
= wcslen(ApplicationName
);
192 if (Length
<= wcslen(CommandLine
) &&
193 _wcsnicmp(ApplicationName
, CommandLine
, Length
) == 0)
196 CommandLine
+= Length
;
199 * ... but it is not, however we still have a token. We suppose that
200 * it corresponds to some sort of application name, so we skip it too.
204 /* Get rid of the first token. We stop when we see whitespace. */
205 while (*CommandLine
&& !(*CommandLine
== L
' ' || *CommandLine
== L
'\t'))
207 if (*CommandLine
== L
'\"')
209 /* We enter a quoted part, skip it */
211 while (*CommandLine
&& *CommandLine
++ != L
'\"') ;
215 /* Go to the next character */
223 * Trim remaining whitespace from CommandLine that may be
224 * present between the application name and the parameters.
226 while (*CommandLine
== L
' ' || *CommandLine
== L
'\t')
229 /* Get the current directory */
230 if (CurrentDirectory
== NULL
)
232 /* Allocate memory for the current directory path */
233 Length
= GetCurrentDirectoryW(0, NULL
);
234 CurrentDir
= (PWCHAR
)RtlAllocateHeap(RtlGetProcessHeap(),
236 Length
* sizeof(WCHAR
));
237 if (CurrentDir
== NULL
)
239 Status
= STATUS_NO_MEMORY
;
243 /* Get the current directory */
244 GetCurrentDirectoryW(Length
, CurrentDir
);
245 CurrentDirectory
= CurrentDir
;
248 /* Calculate the size of the short current directory path */
249 Length
= GetShortPathNameW(CurrentDirectory
, NULL
, 0);
251 /* Allocate memory for the short current directory path */
252 ShortCurrentDir
= (PWCHAR
)RtlAllocateHeap(RtlGetProcessHeap(),
254 Length
* sizeof(WCHAR
));
255 if (!ShortCurrentDir
)
257 Status
= STATUS_NO_MEMORY
;
261 /* Get the short current directory path */
262 if (!GetShortPathNameW(CurrentDirectory
, ShortCurrentDir
, Length
))
264 /* Try to determine which error occurred */
265 switch (GetLastError())
267 case ERROR_NOT_ENOUGH_MEMORY
:
269 Status
= STATUS_NO_MEMORY
;
273 case ERROR_INVALID_PARAMETER
:
275 Status
= STATUS_INVALID_PARAMETER
;
281 Status
= STATUS_OBJECT_PATH_INVALID
;
287 /* Setup the input parameters */
288 CheckVdm
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
289 CheckVdm
->BinaryType
= BinaryType
;
290 CheckVdm
->CodePage
= CP_ACP
;
291 CheckVdm
->dwCreationFlags
= CreationFlags
;
292 CheckVdm
->CurDrive
= CurrentDirectory
[0] - L
'A';
293 CheckVdm
->CmdLen
= wcslen(CommandLine
) + 1;
294 CheckVdm
->AppLen
= wcslen(ShortAppName
) + 1;
295 CheckVdm
->PifLen
= 0; // TODO: PIF file support!
296 CheckVdm
->CurDirectoryLen
= wcslen(ShortCurrentDir
) + 1;
297 CheckVdm
->EnvLen
= AnsiEnvironment
->Length
;
298 CheckVdm
->DesktopLen
= (StartupInfo
->lpDesktop
!= NULL
) ? (wcslen(StartupInfo
->lpDesktop
) + 1) : 0;
299 CheckVdm
->TitleLen
= (StartupInfo
->lpTitle
!= NULL
) ? (wcslen(StartupInfo
->lpTitle
) + 1) : 0;
300 CheckVdm
->ReservedLen
= (StartupInfo
->lpReserved
!= NULL
) ? (wcslen(StartupInfo
->lpReserved
) + 1) : 0;
302 if (StartupInfo
->dwFlags
& STARTF_USESTDHANDLES
)
304 /* Set the standard handles */
305 CheckVdm
->StdIn
= StartupInfo
->hStdInput
;
306 CheckVdm
->StdOut
= StartupInfo
->hStdOutput
;
307 CheckVdm
->StdErr
= StartupInfo
->hStdError
;
310 /* Allocate memory for the ANSI strings */
311 // We need to add the newline characters '\r\n' to the command line
312 AnsiCmdLine
= (PCHAR
)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, CheckVdm
->CmdLen
+ 2);
313 AnsiAppName
= (PCHAR
)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, CheckVdm
->AppLen
);
314 AnsiCurDirectory
= (PCHAR
)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, CheckVdm
->CurDirectoryLen
);
315 if (StartupInfo
->lpDesktop
)
316 AnsiDesktop
= (PCHAR
)RtlAllocateHeap(RtlGetProcessHeap(),
318 CheckVdm
->DesktopLen
);
319 if (StartupInfo
->lpTitle
)
320 AnsiTitle
= (PCHAR
)RtlAllocateHeap(RtlGetProcessHeap(),
323 if (StartupInfo
->lpReserved
)
324 AnsiReserved
= (PCHAR
)RtlAllocateHeap(RtlGetProcessHeap(),
326 CheckVdm
->ReservedLen
);
331 || (StartupInfo
->lpDesktop
&& !AnsiDesktop
)
332 || (StartupInfo
->lpTitle
&& !AnsiTitle
)
333 || (StartupInfo
->lpReserved
&& !AnsiReserved
))
335 Status
= STATUS_NO_MEMORY
;
339 /* Convert the command line into an ANSI string */
340 WideCharToMultiByte(CP_ACP
,
348 /* Add the needed newline and NULL-terminate */
349 CheckVdm
->CmdLen
--; // Rewind back to the NULL character
350 AnsiCmdLine
[CheckVdm
->CmdLen
++] = '\r';
351 AnsiCmdLine
[CheckVdm
->CmdLen
++] = '\n';
352 AnsiCmdLine
[CheckVdm
->CmdLen
++] = 0;
354 /* Convert the short application name into an ANSI string */
355 WideCharToMultiByte(CP_ACP
,
364 /* Convert the short current directory path into an ANSI string */
365 WideCharToMultiByte(CP_ACP
,
368 CheckVdm
->CurDirectoryLen
,
370 CheckVdm
->CurDirectoryLen
,
374 if (StartupInfo
->lpDesktop
)
376 /* Convert the desktop name into an ANSI string */
377 WideCharToMultiByte(CP_ACP
,
379 StartupInfo
->lpDesktop
,
380 CheckVdm
->DesktopLen
,
382 CheckVdm
->DesktopLen
,
388 if (StartupInfo
->lpTitle
)
390 /* Convert the title into an ANSI string */
391 WideCharToMultiByte(CP_ACP
,
393 StartupInfo
->lpTitle
,
402 if (StartupInfo
->lpReserved
)
404 /* Convert the reserved value into an ANSI string */
405 WideCharToMultiByte(CP_ACP
,
407 StartupInfo
->lpReserved
,
408 CheckVdm
->ReservedLen
,
410 CheckVdm
->ReservedLen
,
416 /* Fill the ANSI startup info structure */
417 RtlCopyMemory(&AnsiStartupInfo
, StartupInfo
, sizeof(AnsiStartupInfo
));
418 AnsiStartupInfo
.lpReserved
= AnsiReserved
;
419 AnsiStartupInfo
.lpDesktop
= AnsiDesktop
;
420 AnsiStartupInfo
.lpTitle
= AnsiTitle
;
422 /* Allocate the capture buffer */
423 CaptureBuffer
= CsrAllocateCaptureBuffer(NumStrings
,
427 + CheckVdm
->CurDirectoryLen
428 + CheckVdm
->DesktopLen
430 + CheckVdm
->ReservedLen
432 + sizeof(*CheckVdm
->StartupInfo
));
433 if (CaptureBuffer
== NULL
)
435 Status
= STATUS_NO_MEMORY
;
439 /* Capture the command line */
440 CsrCaptureMessageBuffer(CaptureBuffer
,
443 (PVOID
*)&CheckVdm
->CmdLine
);
445 /* Capture the application name */
446 CsrCaptureMessageBuffer(CaptureBuffer
,
449 (PVOID
*)&CheckVdm
->AppName
);
451 CheckVdm
->PifFile
= NULL
; // TODO: PIF file support!
453 /* Capture the current directory */
454 CsrCaptureMessageBuffer(CaptureBuffer
,
456 CheckVdm
->CurDirectoryLen
,
457 (PVOID
*)&CheckVdm
->CurDirectory
);
459 /* Capture the environment */
460 CsrCaptureMessageBuffer(CaptureBuffer
,
461 AnsiEnvironment
->Buffer
,
463 (PVOID
*)&CheckVdm
->Env
);
465 /* Capture the startup info structure */
466 CsrCaptureMessageBuffer(CaptureBuffer
,
468 sizeof(*CheckVdm
->StartupInfo
),
469 (PVOID
*)&CheckVdm
->StartupInfo
);
471 if (StartupInfo
->lpDesktop
)
473 /* Capture the desktop name */
474 CsrCaptureMessageBuffer(CaptureBuffer
,
476 CheckVdm
->DesktopLen
,
477 (PVOID
*)&CheckVdm
->Desktop
);
479 else CheckVdm
->Desktop
= NULL
;
481 if (StartupInfo
->lpTitle
)
483 /* Capture the title */
484 CsrCaptureMessageBuffer(CaptureBuffer
,
487 (PVOID
*)&CheckVdm
->Title
);
489 else CheckVdm
->Title
= NULL
;
491 if (StartupInfo
->lpReserved
)
493 /* Capture the reserved parameter */
494 CsrCaptureMessageBuffer(CaptureBuffer
,
496 CheckVdm
->ReservedLen
,
497 (PVOID
*)&CheckVdm
->Reserved
);
499 else CheckVdm
->Reserved
= NULL
;
501 /* Send the message to CSRSS */
502 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)ApiMessage
,
504 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepCheckVDM
),
507 /* Write back the task ID */
508 *iTask
= CheckVdm
->iTask
;
512 /* Free the ANSI strings */
513 if (AnsiCmdLine
) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCmdLine
);
514 if (AnsiAppName
) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiAppName
);
515 if (AnsiCurDirectory
) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCurDirectory
);
516 if (AnsiDesktop
) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiDesktop
);
517 if (AnsiTitle
) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiTitle
);
518 if (AnsiReserved
) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiReserved
);
520 /* Free the capture buffer */
521 CsrFreeCaptureBuffer(CaptureBuffer
);
523 /* Free the current directory, if it was allocated here, and its short path */
524 if (ShortCurrentDir
) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortCurrentDir
);
525 if (CurrentDir
) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDir
);
527 /* Free the short app name */
528 if (ShortAppName
) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortAppName
);
535 BaseUpdateVDMEntry(IN ULONG UpdateIndex
,
536 IN OUT PHANDLE WaitHandle
,
540 BASE_API_MESSAGE ApiMessage
;
541 PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry
= &ApiMessage
.Data
.UpdateVDMEntryRequest
;
543 /* Check what update is being sent */
546 /* VDM is being undone */
549 /* Tell the server how far we had gotten along */
550 UpdateVdmEntry
->iTask
= HandleToUlong(*WaitHandle
);
551 UpdateVdmEntry
->VDMCreationState
= IndexInfo
;
555 /* VDM is ready with a new process handle */
556 case VdmEntryUpdateProcess
:
558 /* Send it the process handle */
559 UpdateVdmEntry
->VDMProcessHandle
= *WaitHandle
;
560 UpdateVdmEntry
->iTask
= IndexInfo
;
565 /* Also check what kind of binary this is for the console handle */
566 if (BinaryType
== BINARY_TYPE_WOW
)
568 /* Magic value for 16-bit apps */
569 UpdateVdmEntry
->ConsoleHandle
= (HANDLE
)-1;
571 else if (UpdateVdmEntry
->iTask
)
573 /* No handle for true VDM */
574 UpdateVdmEntry
->ConsoleHandle
= NULL
;
578 /* Otherwise, use the regular console handle */
579 UpdateVdmEntry
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
582 /* Finally write the index and binary type */
583 UpdateVdmEntry
->EntryIndex
= UpdateIndex
;
584 UpdateVdmEntry
->BinaryType
= BinaryType
;
586 /* Send the message to CSRSS */
587 CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
589 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepUpdateVDMEntry
),
590 sizeof(*UpdateVdmEntry
));
591 if (!NT_SUCCESS(ApiMessage
.Status
))
594 BaseSetLastNTError(ApiMessage
.Status
);
598 /* If this was an update, CSRSS returns a new wait handle */
599 if (UpdateIndex
== VdmEntryUpdateProcess
)
601 /* Return it to the caller */
602 *WaitHandle
= UpdateVdmEntry
->WaitObjectForParent
;
611 BaseCheckForVDM(IN HANDLE ProcessHandle
,
612 OUT LPDWORD ExitCode
)
615 EVENT_BASIC_INFORMATION EventBasicInfo
;
616 BASE_API_MESSAGE ApiMessage
;
617 PBASE_GET_VDM_EXIT_CODE GetVdmExitCode
= &ApiMessage
.Data
.GetVDMExitCodeRequest
;
619 /* It's VDM if the process is actually a wait handle (an event) */
620 Status
= NtQueryEvent(ProcessHandle
,
621 EventBasicInformation
,
623 sizeof(EventBasicInfo
),
625 if (!NT_SUCCESS(Status
)) return FALSE
;
627 /* Setup the input parameters */
628 GetVdmExitCode
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
629 GetVdmExitCode
->hParent
= ProcessHandle
;
632 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
634 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepGetVDMExitCode
),
635 sizeof(*GetVdmExitCode
));
636 if (!NT_SUCCESS(Status
)) return FALSE
;
638 /* Get the exit code from the reply */
639 *ExitCode
= GetVdmExitCode
->ExitCode
;
645 BaseGetVdmConfigInfo(IN LPCWSTR CommandLineReserved
,
648 IN PUNICODE_STRING CmdLineString
,
651 WCHAR Buffer
[MAX_PATH
];
652 WCHAR CommandLine
[MAX_PATH
* 2];
655 /* Clear the buffer in case we fail */
656 CmdLineString
->Buffer
= 0;
658 /* Always return the same size: 16 Mb */
659 *VdmSize
= 0x1000000;
661 /* Get the system directory */
662 Length
= GetSystemDirectoryW(Buffer
, MAX_PATH
);
663 if (!(Length
) || (Length
>= MAX_PATH
))
665 /* Eliminate no path or path too big */
666 SetLastError(ERROR_INVALID_NAME
);
670 /* Check if this is VDM with a DOS Sequence ID */
674 * Build the VDM string for it:
675 * -i%lx : Gives the DOS Sequence ID;
676 * %s%c : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
678 _snwprintf(CommandLine
,
679 ARRAYSIZE(CommandLine
),
680 L
"\"%s\\ntvdm.exe\" -i%lx %s%c",
683 (BinaryType
== BINARY_TYPE_DOS
) ? L
" " : L
"-w",
684 (BinaryType
== BINARY_TYPE_SEPARATE_WOW
) ? L
's' : L
' ');
689 * Build the string for it without the DOS Sequence ID:
690 * %s%c : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
692 _snwprintf(CommandLine
,
693 ARRAYSIZE(CommandLine
),
694 L
"\"%s\\ntvdm.exe\" %s%c",
696 (BinaryType
== BINARY_TYPE_DOS
) ? L
" " : L
"-w",
697 (BinaryType
== BINARY_TYPE_SEPARATE_WOW
) ? L
's' : L
' ');
700 /* Create the actual string */
701 return RtlCreateUnicodeString(CmdLineString
, CommandLine
);
706 BaseGetEnvNameType_U(IN PWCHAR Name
,
710 ENV_NAME_TYPE NameType
;
713 /* Start by assuming the environment variable doesn't describe paths */
714 NameType
= EnvNameNotAPath
;
716 /* Loop all the environment names */
717 for (i
= 0; i
< ARRAYSIZE(BasepEnvNameType
); i
++)
720 EnvInfo
= &BasepEnvNameType
[i
];
722 /* Check if it matches the name */
723 if ((EnvInfo
->NameLength
== NameLength
) &&
724 (_wcsnicmp(EnvInfo
->Name
, Name
, NameLength
) == 0))
726 /* It does, return the type */
727 NameType
= EnvInfo
->NameType
;
737 BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment
,
738 OUT PANSI_STRING AnsiEnv
,
739 OUT PUNICODE_STRING UnicodeEnv
)
741 #define IS_ALPHA(x) \
742 ( ((x) >= L'A' && (x) <= L'Z') || ((x) >= L'a' && (x) <= L'z') )
744 // From lib/rtl/path.c :
745 // Can be put in some .h ??
746 #define IS_PATH_SEPARATOR(x) ((x) == L'\\' || (x) == L'/')
748 BOOL Success
= FALSE
;
750 ULONG RegionSize
, EnvironmentSize
= 0;
751 PWCHAR Environment
, NewEnvironment
= NULL
;
752 ENV_NAME_TYPE NameType
;
753 ULONG NameLength
, NumChars
, Remaining
;
754 PWCHAR SourcePtr
, DestPtr
, StartPtr
;
756 /* Make sure we have both strings */
757 if (!AnsiEnv
|| !UnicodeEnv
)
760 SetLastError(ERROR_INVALID_PARAMETER
);
764 /* Check if an environment was passed in */
767 /* Nope, create one */
768 Status
= RtlCreateEnvironment(TRUE
, &Environment
);
769 if (!NT_SUCCESS(Status
)) goto Cleanup
;
773 /* Use the one we got */
774 Environment
= lpEnvironment
;
777 /* Do we have something now ? */
780 /* Still not, bail out */
781 SetLastError(ERROR_BAD_ENVIRONMENT
);
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).
789 SourcePtr
= Environment
;
790 while (!(*SourcePtr
++ == UNICODE_NULL
&& *SourcePtr
== UNICODE_NULL
))
792 EnvironmentSize
+= 2; // Add the two terminating NULLs
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.
804 RegionSize
= (EnvironmentSize
+ MAX_PATH
) * sizeof(WCHAR
);
805 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
806 (PVOID
*)&NewEnvironment
,
811 if (!NT_SUCCESS(Status
))
813 /* We failed, bail out */
814 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
815 NewEnvironment
= NULL
;
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
;
824 /* Loop through all the environment strings */
825 while (*SourcePtr
!= UNICODE_NULL
)
828 * 1. Check the type of the environment variable and copy its name.
831 /* Regular environment variable */
832 if (*SourcePtr
!= L
'=')
834 StartPtr
= SourcePtr
;
836 /* Copy the environment variable name, including the '=' */
837 while (*SourcePtr
!= UNICODE_NULL
)
839 *DestPtr
++ = *SourcePtr
;
840 if (*SourcePtr
++ == L
'=') break;
843 /* Guess the type of the environment variable */
844 NameType
= BaseGetEnvNameType_U(StartPtr
, SourcePtr
- StartPtr
- 1);
846 /* 'Current directory' environment variable (i.e. of '=X:=' form) */
847 else // if (*SourcePtr == L'=')
849 /* First assume we have a possibly malformed environment variable */
850 NameType
= EnvNameNotAPath
;
852 /* Check for a valid 'Current directory' environment variable */
853 if (IS_ALPHA(SourcePtr
[1]) && SourcePtr
[2] == L
':' && SourcePtr
[3] == L
'=')
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.
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
) )
864 NameType
= EnvNameSinglePath
;
866 /* Copy the '=X:=' prefix */
867 *DestPtr
++ = SourcePtr
[0];
868 *DestPtr
++ = SourcePtr
[1];
869 *DestPtr
++ = SourcePtr
[2];
870 *DestPtr
++ = SourcePtr
[3];
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:\\")
882 * 'NameType' is already set to 'EnvNameNotAPath'.
889 * 2. Copy the environment value and perform conversions accordingly.
892 if (NameType
== EnvNameNotAPath
)
894 /* Copy everything, including the NULL terminator */
897 *DestPtr
++ = *SourcePtr
;
898 } while (*SourcePtr
++ != UNICODE_NULL
);
900 else if (NameType
== EnvNameSinglePath
)
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
)
907 /* If the conversion failed, just copy the original value */
908 RtlCopyMemory(DestPtr
, SourcePtr
, NameLength
* sizeof(WCHAR
));
909 NumChars
= NameLength
;
912 if (NumChars
> NameLength
)
913 Remaining
-= (NumChars
- NameLength
);
915 SourcePtr
+= NameLength
;
917 /* Copy the NULL terminator */
918 *DestPtr
++ = *SourcePtr
++;
920 else // if (NameType == EnvNameMultiplePath)
924 /* Loop through the list of paths (delimited by ';') and convert each path to its short form */
927 /* Copy any trailing ';' before going to the next path */
928 while (*SourcePtr
== L
';')
930 *DestPtr
++ = *SourcePtr
++;
933 StartPtr
= SourcePtr
;
935 /* Find the next path list delimiter or the NULL terminator */
936 while (*SourcePtr
!= UNICODE_NULL
&& *SourcePtr
!= L
';')
940 Delimiter
= *SourcePtr
;
942 NameLength
= SourcePtr
- StartPtr
;
946 * Temporarily replace the possible path list delimiter by NULL.
947 * 'lpEnvironment' must point to a read+write memory buffer!
949 *SourcePtr
= UNICODE_NULL
;
951 NumChars
= GetShortPathNameW(StartPtr
, DestPtr
, NameLength
+ 1 + Remaining
);
952 if ( NumChars
== 0 ||
953 (Delimiter
== L
';' ? NumChars
> NameLength
+ Remaining
954 : NumChars
> NameLength
/* + Remaining ?? */) )
956 /* If the conversion failed, just copy the original value */
957 RtlCopyMemory(DestPtr
, StartPtr
, NameLength
* sizeof(WCHAR
));
958 NumChars
= NameLength
;
961 if (NumChars
> NameLength
)
962 Remaining
-= (NumChars
- NameLength
);
964 /* If removed, restore the path list delimiter in the source environment value and copy it */
965 if (Delimiter
!= UNICODE_NULL
)
967 *SourcePtr
= Delimiter
;
968 *DestPtr
++ = *SourcePtr
++;
971 } while (*SourcePtr
!= UNICODE_NULL
);
973 /* Copy the NULL terminator */
974 *DestPtr
++ = *SourcePtr
++;
978 /* NULL-terminate the environment block */
979 *DestPtr
++ = UNICODE_NULL
;
981 /* Initialize the Unicode string to hold it */
982 RtlInitEmptyUnicodeString(UnicodeEnv
, NewEnvironment
,
983 (DestPtr
- NewEnvironment
) * sizeof(WCHAR
));
984 UnicodeEnv
->Length
= UnicodeEnv
->MaximumLength
;
986 /* Create its ANSI version */
987 Status
= RtlUnicodeStringToAnsiString(AnsiEnv
, UnicodeEnv
, TRUE
);
988 if (!NT_SUCCESS(Status
))
990 /* Set last error if conversion failure */
991 BaseSetLastNTError(Status
);
995 /* Everything went okay, so return success */
997 NewEnvironment
= NULL
;
1001 /* Cleanup path starts here, start by destroying the environment copy */
1002 if (!lpEnvironment
&& Environment
) RtlDestroyEnvironment(Environment
);
1004 /* See if we are here due to failure */
1007 /* Initialize the paths to be empty */
1008 RtlInitEmptyUnicodeString(UnicodeEnv
, NULL
, 0);
1009 RtlInitEmptyAnsiString(AnsiEnv
, NULL
, 0);
1011 /* Free the environment copy */
1013 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
1014 (PVOID
*)&NewEnvironment
,
1017 ASSERT(NT_SUCCESS(Status
));
1020 /* Return the result */
1026 BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv
,
1027 IN PUNICODE_STRING UnicodeEnv
)
1031 /* Clear the ANSI buffer since Rtl creates this for us */
1032 if (AnsiEnv
->Buffer
) RtlFreeAnsiString(AnsiEnv
);
1034 /* The Unicode buffer is build by hand, though */
1035 if (UnicodeEnv
->Buffer
)
1037 /* So clear it through the API */
1038 NtFreeVirtualMemory(NtCurrentProcess(),
1039 (PVOID
*)&UnicodeEnv
->Buffer
,
1049 /* Check whether a file is an OS/2 or a very old Windows executable
1050 * by testing on import of KERNEL.
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...
1056 InternalIsOS2OrOldWin(HANDLE hFile
, IMAGE_DOS_HEADER
*mz
, IMAGE_OS2_HEADER
*ne
)
1059 LPWORD modtab
= NULL
;
1060 LPSTR nametab
= NULL
;
1065 CurPos
= SetFilePointer(hFile
, 0, NULL
, FILE_CURRENT
);
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
)))
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
))
1085 for(i
= 0; i
< ne
->ne_cmod
; i
++)
1088 module
= &nametab
[modtab
[i
]];
1089 if(!strncmp(&module
[1], "KERNEL", module
[0]))
1091 /* very old windows file */
1098 DPRINT1("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
1101 HeapFree(GetProcessHeap(), 0, modtab
);
1102 HeapFree(GetProcessHeap(), 0, nametab
);
1103 SetFilePointer(hFile
, CurPos
, NULL
, FILE_BEGIN
);
1108 InternalGetBinaryType(HANDLE hFile
)
1114 unsigned char magic
[4];
1115 unsigned char ignored
[12];
1116 unsigned short type
;
1120 unsigned long magic
;
1121 unsigned long cputype
;
1122 unsigned long cpusubtype
;
1123 unsigned long filetype
;
1125 IMAGE_DOS_HEADER mz
;
1130 if((SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
1131 (!ReadFile(hFile
, &Header
, sizeof(Header
), &Read
, NULL
) ||
1132 (Read
!= sizeof(Header
))))
1134 return BINARY_UNKNOWN
;
1137 if(!memcmp(Header
.elf
.magic
, "\177ELF", sizeof(Header
.elf
.magic
)))
1139 /* FIXME: we don't bother to check byte order, architecture, etc. */
1140 switch(Header
.elf
.type
)
1143 return BINARY_UNIX_EXE
;
1145 return BINARY_UNIX_LIB
;
1147 return BINARY_UNKNOWN
;
1150 /* Mach-o File with Endian set to Big Endian or Little Endian*/
1151 if(Header
.macho
.magic
== 0xFEEDFACE ||
1152 Header
.macho
.magic
== 0xCEFAEDFE)
1154 switch(Header
.macho
.filetype
)
1158 return BINARY_UNIX_LIB
;
1160 return BINARY_UNKNOWN
;
1163 /* Not ELF, try DOS */
1164 if(Header
.mz
.e_magic
== IMAGE_DOS_SIGNATURE
)
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
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
))))
1180 /* Reading the magic field succeeded so
1181 * we will try to determine what type it is.
1183 if(!memcmp(magic
, "PE\0\0", sizeof(magic
)))
1185 IMAGE_FILE_HEADER FileHeader
;
1186 if(!ReadFile(hFile
, &FileHeader
, sizeof(IMAGE_FILE_HEADER
), &Read
, NULL
) ||
1187 (Read
!= sizeof(IMAGE_FILE_HEADER
)))
1192 /* FIXME - detect 32/64 bit */
1194 if(FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
1195 return BINARY_PE_DLL32
;
1196 return BINARY_PE_EXE32
;
1199 if(!memcmp(magic
, "NE", 2))
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.
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
)))
1211 /* Couldn't read header, so abort. */
1215 switch(ne
.ne_exetyp
)
1218 return BINARY_WIN16
;
1222 return InternalIsOS2OrOldWin(hFile
, &Header
.mz
, &ne
);
1227 return BINARY_UNKNOWN
;
1236 LPCWSTR lpApplicationName
,
1237 LPDWORD lpBinaryType
1243 if(!lpApplicationName
|| !lpBinaryType
)
1245 SetLastError(ERROR_INVALID_PARAMETER
);
1249 hFile
= CreateFileW(lpApplicationName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
1250 OPEN_EXISTING
, 0, 0);
1251 if(hFile
== INVALID_HANDLE_VALUE
)
1256 BinType
= InternalGetBinaryType(hFile
);
1261 case BINARY_UNKNOWN
:
1266 * guess from filename
1268 if(!(dot
= wcsrchr(lpApplicationName
, L
'.')))
1272 if(!lstrcmpiW(dot
, L
".COM"))
1274 *lpBinaryType
= SCS_DOS_BINARY
;
1277 if(!lstrcmpiW(dot
, L
".PIF"))
1279 *lpBinaryType
= SCS_PIF_BINARY
;
1284 case BINARY_PE_EXE32
:
1285 case BINARY_PE_DLL32
:
1287 *lpBinaryType
= SCS_32BIT_BINARY
;
1290 case BINARY_PE_EXE64
:
1291 case BINARY_PE_DLL64
:
1293 *lpBinaryType
= SCS_64BIT_BINARY
;
1298 *lpBinaryType
= SCS_WOW_BINARY
;
1303 *lpBinaryType
= SCS_OS216_BINARY
;
1308 *lpBinaryType
= SCS_DOS_BINARY
;
1311 case BINARY_UNIX_EXE
:
1312 case BINARY_UNIX_LIB
:
1318 DPRINT1("Invalid binary type %lu returned!\n", BinType
);
1327 GetBinaryTypeA(IN LPCSTR lpApplicationName
,
1328 OUT LPDWORD lpBinaryType
)
1330 ANSI_STRING ApplicationNameString
;
1331 UNICODE_STRING ApplicationNameW
;
1332 BOOL StringAllocated
= FALSE
, Result
;
1335 RtlInitAnsiString(&ApplicationNameString
, lpApplicationName
);
1337 if (ApplicationNameString
.Length
* sizeof(WCHAR
) >= NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
1339 StringAllocated
= TRUE
;
1340 Status
= RtlAnsiStringToUnicodeString(&ApplicationNameW
, &ApplicationNameString
, TRUE
);
1344 Status
= RtlAnsiStringToUnicodeString(&(NtCurrentTeb()->StaticUnicodeString
), &ApplicationNameString
, FALSE
);
1347 if (!NT_SUCCESS(Status
))
1349 BaseSetLastNTError(Status
);
1353 if (StringAllocated
)
1355 Result
= GetBinaryTypeW(ApplicationNameW
.Buffer
, lpBinaryType
);
1356 RtlFreeUnicodeString(&ApplicationNameW
);
1360 Result
= GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString
.Buffer
, lpBinaryType
);
1371 CmdBatNotification (
1384 ExitVDM(BOOL IsWow
, ULONG iWowTask
)
1386 BASE_API_MESSAGE ApiMessage
;
1387 PBASE_EXIT_VDM ExitVdm
= &ApiMessage
.Data
.ExitVDMRequest
;
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
;
1395 CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
1397 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepExitVDM
),
1400 /* Close the returned wait object handle, if any */
1401 if (NT_SUCCESS(ApiMessage
.Status
) && (ExitVdm
->WaitObjectForVDM
!= NULL
))
1403 CloseHandle(ExitVdm
->WaitObjectForVDM
);
1412 GetNextVDMCommand(PVDM_COMMAND_INFO CommandData
)
1414 BOOL Success
= FALSE
;
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;
1424 * Special case to test whether the VDM is the first one.
1426 if (CommandData
== NULL
)
1429 CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
1431 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepIsFirstVDM
),
1432 sizeof(*IsFirstVdm
));
1433 if (!NT_SUCCESS(ApiMessage
.Status
))
1435 BaseSetLastNTError(ApiMessage
.Status
);
1439 /* Return TRUE if this is the first VDM */
1440 return IsFirstVdm
->FirstVDM
;
1443 /* CommandData != NULL */
1446 * Special case to increment or decrement the reentrancy count.
1448 if ((CommandData
->VDMState
== VDM_INC_REENTER_COUNT
) ||
1449 (CommandData
->VDMState
== VDM_DEC_REENTER_COUNT
))
1451 /* Setup the input parameters */
1452 SetReenterCount
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
1453 SetReenterCount
->fIncDec
= CommandData
->VDMState
;
1456 CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
1458 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepSetReenterCount
),
1459 sizeof(*SetReenterCount
));
1460 if (!NT_SUCCESS(ApiMessage
.Status
))
1462 BaseSetLastNTError(ApiMessage
.Status
);
1471 * Special case to retrieve or set WOW information.
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!
1480 /* Clear the structure */
1481 RtlZeroMemory(GetNextVdmCommand
, sizeof(*GetNextVdmCommand
));
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
;
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
++;
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
)
1519 BaseSetLastNTError(STATUS_NO_MEMORY
);
1523 /* Capture the data */
1525 CsrAllocateMessagePointer(CaptureBuffer
,
1526 sizeof(*GetNextVdmCommand
->StartupInfo
),
1527 (PVOID
*)&GetNextVdmCommand
->StartupInfo
);
1529 if (CommandData
->CmdLen
)
1531 CsrAllocateMessagePointer(CaptureBuffer
,
1532 CommandData
->CmdLen
,
1533 (PVOID
*)&GetNextVdmCommand
->CmdLine
);
1536 if (CommandData
->AppLen
)
1538 CsrAllocateMessagePointer(CaptureBuffer
,
1539 CommandData
->AppLen
,
1540 (PVOID
*)&GetNextVdmCommand
->AppName
);
1543 if (CommandData
->PifLen
)
1545 CsrAllocateMessagePointer(CaptureBuffer
,
1546 CommandData
->PifLen
,
1547 (PVOID
*)&GetNextVdmCommand
->PifFile
);
1550 if (CommandData
->CurDirectoryLen
)
1552 CsrAllocateMessagePointer(CaptureBuffer
,
1553 CommandData
->CurDirectoryLen
,
1554 (PVOID
*)&GetNextVdmCommand
->CurDirectory
);
1557 if (CommandData
->EnvLen
)
1559 CsrAllocateMessagePointer(CaptureBuffer
,
1560 CommandData
->EnvLen
,
1561 (PVOID
*)&GetNextVdmCommand
->Env
);
1564 if (CommandData
->DesktopLen
)
1566 CsrAllocateMessagePointer(CaptureBuffer
,
1567 CommandData
->DesktopLen
,
1568 (PVOID
*)&GetNextVdmCommand
->Desktop
);
1571 if (CommandData
->TitleLen
)
1573 CsrAllocateMessagePointer(CaptureBuffer
,
1574 CommandData
->TitleLen
,
1575 (PVOID
*)&GetNextVdmCommand
->Title
);
1578 if (CommandData
->ReservedLen
)
1580 CsrAllocateMessagePointer(CaptureBuffer
,
1581 CommandData
->ReservedLen
,
1582 (PVOID
*)&GetNextVdmCommand
->Reserved
);
1588 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
1590 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepGetNextVDMCommand
),
1591 sizeof(*GetNextVdmCommand
));
1593 /* Exit the waiting loop if we did not receive any event handle */
1594 if (GetNextVdmCommand
->WaitObjectForVDM
== NULL
)
1597 /* Wait for the event to become signaled and try again */
1598 Status
= NtWaitForSingleObject(GetNextVdmCommand
->WaitObjectForVDM
,
1600 if (Status
!= STATUS_SUCCESS
)
1602 /* Fail if we timed out, or if some other error happened */
1603 BaseSetLastNTError(Status
);
1607 /* Set the retry flag, clear the exit code, and retry a query */
1608 GetNextVdmCommand
->VDMState
|= VDM_FLAG_RETRY
;
1609 GetNextVdmCommand
->ExitCode
= 0;
1612 if (!NT_SUCCESS(Status
))
1614 if (Status
== STATUS_INVALID_PARAMETER
)
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.
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
;
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;
1643 BaseSetLastNTError(Status
);
1647 /* Write back the standard handles */
1648 CommandData
->StdIn
= GetNextVdmCommand
->StdIn
;
1649 CommandData
->StdOut
= GetNextVdmCommand
->StdOut
;
1650 CommandData
->StdErr
= GetNextVdmCommand
->StdErr
;
1652 /* Write back the startup info */
1653 RtlMoveMemory(&CommandData
->StartupInfo
,
1654 GetNextVdmCommand
->StartupInfo
,
1655 sizeof(*GetNextVdmCommand
->StartupInfo
));
1657 if (CommandData
->CmdLen
)
1659 /* Write back the command line */
1660 RtlMoveMemory(CommandData
->CmdLine
,
1661 GetNextVdmCommand
->CmdLine
,
1662 GetNextVdmCommand
->CmdLen
);
1664 /* Set the actual length */
1665 CommandData
->CmdLen
= GetNextVdmCommand
->CmdLen
;
1668 if (CommandData
->AppLen
)
1670 /* Write back the application name */
1671 RtlMoveMemory(CommandData
->AppName
,
1672 GetNextVdmCommand
->AppName
,
1673 GetNextVdmCommand
->AppLen
);
1675 /* Set the actual length */
1676 CommandData
->AppLen
= GetNextVdmCommand
->AppLen
;
1679 if (CommandData
->PifLen
)
1681 /* Write back the PIF file name */
1682 RtlMoveMemory(CommandData
->PifFile
,
1683 GetNextVdmCommand
->PifFile
,
1684 GetNextVdmCommand
->PifLen
);
1686 /* Set the actual length */
1687 CommandData
->PifLen
= GetNextVdmCommand
->PifLen
;
1690 if (CommandData
->CurDirectoryLen
)
1692 /* Write back the current directory */
1693 RtlMoveMemory(CommandData
->CurDirectory
,
1694 GetNextVdmCommand
->CurDirectory
,
1695 GetNextVdmCommand
->CurDirectoryLen
);
1697 /* Set the actual length */
1698 CommandData
->CurDirectoryLen
= GetNextVdmCommand
->CurDirectoryLen
;
1701 if (CommandData
->EnvLen
)
1703 /* Write back the environment */
1704 RtlMoveMemory(CommandData
->Env
,
1705 GetNextVdmCommand
->Env
,
1706 GetNextVdmCommand
->EnvLen
);
1708 /* Set the actual length */
1709 CommandData
->EnvLen
= GetNextVdmCommand
->EnvLen
;
1712 if (CommandData
->DesktopLen
)
1714 /* Write back the desktop name */
1715 RtlMoveMemory(CommandData
->Desktop
,
1716 GetNextVdmCommand
->Desktop
,
1717 GetNextVdmCommand
->DesktopLen
);
1719 /* Set the actual length */
1720 CommandData
->DesktopLen
= GetNextVdmCommand
->DesktopLen
;
1723 if (CommandData
->TitleLen
)
1725 /* Write back the title */
1726 RtlMoveMemory(CommandData
->Title
,
1727 GetNextVdmCommand
->Title
,
1728 GetNextVdmCommand
->TitleLen
);
1730 /* Set the actual length */
1731 CommandData
->TitleLen
= GetNextVdmCommand
->TitleLen
;
1734 if (CommandData
->ReservedLen
)
1736 /* Write back the reserved parameter */
1737 RtlMoveMemory(CommandData
->Reserved
,
1738 GetNextVdmCommand
->Reserved
,
1739 GetNextVdmCommand
->ReservedLen
);
1741 /* Set the actual length */
1742 CommandData
->ReservedLen
= GetNextVdmCommand
->ReservedLen
;
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
;
1754 /* It was successful */
1758 if (CaptureBuffer
!= NULL
) CsrFreeCaptureBuffer(CaptureBuffer
);
1768 GetVDMCurrentDirectories(DWORD cchCurDirs
, PCHAR lpszzCurDirs
)
1770 BASE_API_MESSAGE ApiMessage
;
1771 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest
= &ApiMessage
.Data
.VDMCurrentDirsRequest
;
1772 PCSR_CAPTURE_BUFFER CaptureBuffer
;
1774 /* Allocate the capture buffer */
1775 CaptureBuffer
= CsrAllocateCaptureBuffer(1, cchCurDirs
);
1776 if (CaptureBuffer
== NULL
)
1778 BaseSetLastNTError(STATUS_NO_MEMORY
);
1782 /* Setup the input parameters */
1783 VDMCurrentDirsRequest
->cchCurDirs
= cchCurDirs
;
1784 CsrAllocateMessagePointer(CaptureBuffer
,
1786 (PVOID
*)&VDMCurrentDirsRequest
->lpszzCurDirs
);
1789 CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
1791 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepGetVDMCurDirs
),
1792 sizeof(*VDMCurrentDirsRequest
));
1794 /* Set the last error */
1795 BaseSetLastNTError(ApiMessage
.Status
);
1797 if (NT_SUCCESS(ApiMessage
.Status
))
1799 /* Copy the result */
1800 RtlMoveMemory(lpszzCurDirs
, VDMCurrentDirsRequest
->lpszzCurDirs
, cchCurDirs
);
1803 /* Free the capture buffer */
1804 CsrFreeCaptureBuffer(CaptureBuffer
);
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;
1813 * @implemented (undocumented)
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
)
1830 CONSOLE_API_MESSAGE ApiMessage
;
1831 PCONSOLE_REGISTERVDM RegisterVDMRequest
= &ApiMessage
.Data
.RegisterVDMRequest
;
1832 PCSR_CAPTURE_BUFFER CaptureBuffer
= NULL
;
1834 /* Set up the data to send to the Console Server */
1835 RegisterVDMRequest
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
1836 RegisterVDMRequest
->RegisterFlags
= dwRegisterFlags
;
1838 if (dwRegisterFlags
!= 0)
1840 RegisterVDMRequest
->StartHardwareEvent
= hStartHardwareEvent
;
1841 RegisterVDMRequest
->EndHardwareEvent
= hEndHardwareEvent
;
1842 RegisterVDMRequest
->ErrorHardwareEvent
= hErrorHardwareEvent
;
1844 RegisterVDMRequest
->VDMBufferSize
= dwVDMBufferSize
;
1847 RegisterVDMRequest
->UnusedBufferLength
= dwUnusedBufferLength
;
1849 /* Allocate a Capture Buffer */
1850 CaptureBuffer
= CsrAllocateCaptureBuffer(1, dwUnusedBufferLength
);
1851 if (CaptureBuffer
== NULL
)
1853 DPRINT1("CsrAllocateCaptureBuffer failed!\n");
1854 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1858 /* Capture the buffer to write */
1859 CsrCaptureMessageBuffer(CaptureBuffer
,
1860 (PVOID
)lpUnusedBuffer
,
1861 dwUnusedBufferLength
,
1862 (PVOID
*)&RegisterVDMRequest
->UnusedBuffer
);
1867 // CaptureBuffer = NULL;
1870 /* Call the server */
1871 CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
1873 CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX
, ConsolepRegisterVDM
),
1874 sizeof(*RegisterVDMRequest
));
1876 /* Check for success */
1877 Success
= NT_SUCCESS(ApiMessage
.Status
);
1879 /* Release the capture buffer if needed */
1880 if (CaptureBuffer
) CsrFreeCaptureBuffer(CaptureBuffer
);
1882 /* Retrieve the results */
1885 if (dwRegisterFlags
!= 0)
1889 *lpVideoStateLength
= RegisterVDMRequest
->VideoStateLength
;
1890 *lpVideoState
= RegisterVDMRequest
->VideoState
;
1891 *lpVDMBuffer
= RegisterVDMRequest
->VDMBuffer
;
1893 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1895 SetLastError(ERROR_INVALID_ACCESS
);
1903 BaseSetLastNTError(ApiMessage
.Status
);
1906 /* Return success status */
1916 RegisterWowBaseHandlers (
1944 SetVDMCurrentDirectories(DWORD cchCurDirs
, PCHAR lpszzCurDirs
)
1946 BASE_API_MESSAGE ApiMessage
;
1947 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest
= &ApiMessage
.Data
.VDMCurrentDirsRequest
;
1948 PCSR_CAPTURE_BUFFER CaptureBuffer
;
1950 /* Allocate the capture buffer */
1951 CaptureBuffer
= CsrAllocateCaptureBuffer(1, cchCurDirs
);
1952 if (CaptureBuffer
== NULL
)
1954 BaseSetLastNTError(STATUS_NO_MEMORY
);
1958 /* Setup the input parameters */
1959 VDMCurrentDirsRequest
->cchCurDirs
= cchCurDirs
;
1960 CsrCaptureMessageBuffer(CaptureBuffer
,
1963 (PVOID
*)&VDMCurrentDirsRequest
->lpszzCurDirs
);
1966 CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
1968 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepSetVDMCurDirs
),
1969 sizeof(*VDMCurrentDirsRequest
));
1971 /* Free the capture buffer */
1972 CsrFreeCaptureBuffer(CaptureBuffer
);
1974 /* Set the last error */
1975 BaseSetLastNTError(ApiMessage
.Status
);
1977 return NT_SUCCESS(ApiMessage
.Status
) ? TRUE
: FALSE
;
1985 VDMConsoleOperation (
2000 VDMOperationStarted(IN ULONG Unknown0
)
2002 DPRINT1("VDMOperationStarted(%d)\n", Unknown0
);
2004 return BaseUpdateVDMEntry(VdmEntryUpdateControlCHandler
,