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 typedef struct _ENV_INFO
23 } ENV_INFO
, *PENV_INFO
;
25 /* GLOBALS ********************************************************************/
27 ENV_INFO BasepEnvNameType
[] =
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" },
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");
40 /* FUNCTIONS ******************************************************************/
44 BaseIsDosApplication(IN PUNICODE_STRING PathName
,
47 UNICODE_STRING String
;
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
;
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
;
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
;
69 BaseCheckVDM(IN ULONG BinaryType
,
70 IN PCWCH ApplicationName
,
72 IN PCWCH CurrentDirectory
,
73 IN PANSI_STRING AnsiEnvironment
,
74 IN PBASE_API_MESSAGE ApiMessage
,
76 IN DWORD CreationFlags
,
77 IN LPSTARTUPINFOW StartupInfo
,
78 IN HANDLE hUserToken OPTIONAL
)
80 /* This is not supported */
82 return STATUS_NOT_IMPLEMENTED
;
87 BaseUpdateVDMEntry(IN ULONG UpdateIndex
,
88 IN OUT PHANDLE WaitHandle
,
93 BASE_API_MESSAGE ApiMessage
;
94 PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry
= &ApiMessage
.Data
.UpdateVDMEntryRequest
;
96 /* Check what update is being sent */
99 /* VDM is being undone */
102 /* Tell the server how far we had gotten along */
103 UpdateVdmEntry
->iTask
= HandleToUlong(*WaitHandle
);
104 UpdateVdmEntry
->VDMCreationState
= IndexInfo
;
108 /* VDM is ready with a new process handle */
109 case VdmEntryUpdateProcess
:
111 /* Send it the process handle */
112 UpdateVdmEntry
->VDMProcessHandle
= *WaitHandle
;
113 UpdateVdmEntry
->iTask
= IndexInfo
;
118 /* Also check what kind of binary this is for the console handle */
119 if (BinaryType
== BINARY_TYPE_WOW
)
121 /* Magic value for 16-bit apps */
122 UpdateVdmEntry
->ConsoleHandle
= (HANDLE
)-1;
124 else if (UpdateVdmEntry
->iTask
)
126 /* No handle for true VDM */
127 UpdateVdmEntry
->ConsoleHandle
= NULL
;
131 /* Otherwise, use the regular console handle */
132 UpdateVdmEntry
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
135 /* Finally write the index and binary type */
136 UpdateVdmEntry
->EntryIndex
= UpdateIndex
;
137 UpdateVdmEntry
->BinaryType
= BinaryType
;
139 /* Send the message to CSRSS */
140 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
142 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepUpdateVDMEntry
),
143 sizeof(BASE_UPDATE_VDM_ENTRY
));
144 if (!NT_SUCCESS(Status
))
147 BaseSetLastNTError(Status
);
151 /* If this was an update, CSRSS returns a new wait handle */
152 if (UpdateIndex
== VdmEntryUpdateProcess
)
154 /* Return it to the caller */
155 *WaitHandle
= UpdateVdmEntry
->WaitObjectForParent
;
164 BaseCheckForVDM(IN HANDLE ProcessHandle
,
165 OUT LPDWORD ExitCode
)
168 EVENT_BASIC_INFORMATION EventBasicInfo
;
169 BASE_API_MESSAGE ApiMessage
;
170 PBASE_GET_VDM_EXIT_CODE GetVdmExitCode
= &ApiMessage
.Data
.GetVDMExitCodeRequest
;
172 /* It's VDM if the process is actually a wait handle (an event) */
173 Status
= NtQueryEvent(ProcessHandle
,
174 EventBasicInformation
,
176 sizeof(EventBasicInfo
),
178 if (!NT_SUCCESS(Status
)) return FALSE
;
180 /* Setup the input parameters */
181 GetVdmExitCode
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
182 GetVdmExitCode
->hParent
= ProcessHandle
;
185 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
187 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepGetVDMExitCode
),
188 sizeof(BASE_GET_VDM_EXIT_CODE
));
189 if (!NT_SUCCESS(Status
)) return FALSE
;
191 /* Get the exit code from the reply */
192 *ExitCode
= GetVdmExitCode
->ExitCode
;
198 BaseGetVdmConfigInfo(IN LPCWSTR CommandLineReserved
,
201 IN PUNICODE_STRING CmdLineString
,
204 WCHAR Buffer
[MAX_PATH
];
205 WCHAR CommandLine
[MAX_PATH
* 2];
208 /* Clear the buffer in case we fail */
209 CmdLineString
->Buffer
= 0;
211 /* Always return the same size: 16 Mb */
212 *VdmSize
= 0x1000000;
214 /* Get the system directory */
215 Length
= GetSystemDirectoryW(Buffer
, MAX_PATH
);
216 if (!(Length
) || (Length
>= MAX_PATH
))
218 /* Eliminate no path or path too big */
219 SetLastError(ERROR_INVALID_NAME
);
223 /* Check if this is VDM with a DOS Sequence ID */
227 * Build the VDM string for it:
228 * -i%lx : Gives the DOS Sequence ID;
229 * %s%c : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
231 _snwprintf(CommandLine
,
233 L
"\"%s\\ntvdm.exe\" -i%lx %s%c",
236 (BinaryType
== BINARY_TYPE_DOS
) ? L
" " : L
"-w",
237 (BinaryType
== BINARY_TYPE_SEPARATE_WOW
) ? L
's' : L
' ');
242 * Build the string for it without the DOS Sequence ID:
243 * %s%c : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
245 _snwprintf(CommandLine
,
247 L
"\"%s\\ntvdm.exe\" %s%c",
249 (BinaryType
== BINARY_TYPE_DOS
) ? L
" " : L
"-w",
250 (BinaryType
== BINARY_TYPE_SEPARATE_WOW
) ? L
's' : L
' ');
253 /* Create the actual string */
254 return RtlCreateUnicodeString(CmdLineString
, CommandLine
);
259 BaseGetEnvNameType_U(IN PWCHAR Name
,
265 /* Start by assuming unknown type */
268 /* Loop all the environment names */
269 for (i
= 0; i
< (sizeof(BasepEnvNameType
) / sizeof(ENV_INFO
)); i
++)
272 EnvInfo
= &BasepEnvNameType
[i
];
274 /* Check if it matches the name */
275 if ((EnvInfo
->NameLength
== NameLength
) &&
276 !(_wcsnicmp(EnvInfo
->Name
, Name
, NameLength
)))
278 /* It does, return the type */
279 NameType
= EnvInfo
->NameType
;
284 /* Return what we found, or unknown if nothing */
290 BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv
,
291 IN PUNICODE_STRING UnicodeEnv
)
295 /* Clear the ASCII buffer since Rtl creates this for us */
296 if (AnsiEnv
->Buffer
) RtlFreeAnsiString(AnsiEnv
);
298 /* The Unicode buffer is build by hand, though */
299 if (UnicodeEnv
->Buffer
)
301 /* So clear it through the API */
302 NtFreeVirtualMemory(NtCurrentProcess(),
303 (PVOID
*)&UnicodeEnv
->Buffer
,
314 BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment
,
315 IN PANSI_STRING AnsiEnv
,
316 IN PUNICODE_STRING UnicodeEnv
)
319 ULONG RegionSize
, EnvironmentSize
= 0;
320 PWCHAR p
, Environment
, NewEnvironment
= NULL
;
323 /* Make sure we have both strings */
324 if (!(AnsiEnv
) || !(UnicodeEnv
))
327 SetLastError(ERROR_INVALID_PARAMETER
);
331 /* Check if an environment was passed in */
334 /* Nope, create one */
335 Status
= RtlCreateEnvironment(TRUE
, (PWCHAR
*)&Environment
);
336 if (!NT_SUCCESS(Status
)) goto Quickie
;
340 /* Use the one we got */
341 Environment
= lpEnvironment
;
344 /* Do we have something now ? */
347 /* Still not, fail out */
348 SetLastError(ERROR_BAD_ENVIRONMENT
);
352 /* Count how much space the whole environment takes */
354 while ((*p
++ != UNICODE_NULL
) && (*p
!= UNICODE_NULL
)) EnvironmentSize
++;
355 EnvironmentSize
+= sizeof(UNICODE_NULL
);
357 /* Allocate a new copy */
358 RegionSize
= (EnvironmentSize
+ MAX_PATH
) * sizeof(WCHAR
);
359 if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),
360 (PVOID
*)&NewEnvironment
,
366 /* We failed, bail out */
367 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
368 NewEnvironment
= NULL
;
372 /* Begin parsing the new environment */
375 /* FIXME: Code here */
380 /* Initialize the unicode string to hold it */
381 EnvironmentSize
= (p
- NewEnvironment
) * sizeof(WCHAR
);
382 RtlInitEmptyUnicodeString(UnicodeEnv
, NewEnvironment
, (USHORT
)EnvironmentSize
);
383 UnicodeEnv
->Length
= (USHORT
)EnvironmentSize
;
385 /* Create the ASCII version of it */
386 Status
= RtlUnicodeStringToAnsiString(AnsiEnv
, UnicodeEnv
, TRUE
);
387 if (!NT_SUCCESS(Status
))
389 /* Set last error if conversion failure */
390 BaseSetLastNTError(Status
);
394 /* Everything went okay, so return success */
396 NewEnvironment
= NULL
;
400 /* Cleanup path starts here, start by destroying the envrionment copy */
401 if (!(lpEnvironment
) && (Environment
)) RtlDestroyEnvironment(Environment
);
403 /* See if we are here due to failure */
406 /* Initialize the paths to be empty */
407 RtlInitEmptyUnicodeString(UnicodeEnv
, NULL
, 0);
408 RtlInitEmptyAnsiString(AnsiEnv
, NULL
, 0);
410 /* Free the environment copy */
412 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
413 (PVOID
*)&NewEnvironment
,
416 ASSERT(NT_SUCCESS(Status
));
419 /* Return the result */
424 /* Check whether a file is an OS/2 or a very old Windows executable
425 * by testing on import of KERNEL.
427 * FIXME: is reading the module imports the only way of discerning
428 * old Windows binaries from OS/2 ones ? At least it seems so...
431 InternalIsOS2OrOldWin(HANDLE hFile
, IMAGE_DOS_HEADER
*mz
, IMAGE_OS2_HEADER
*ne
)
434 LPWORD modtab
= NULL
;
435 LPSTR nametab
= NULL
;
440 CurPos
= SetFilePointer(hFile
, 0, NULL
, FILE_CURRENT
);
442 /* read modref table */
443 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_modtab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
444 (!(modtab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_cmod
* sizeof(WORD
)))) ||
445 (!(ReadFile(hFile
, modtab
, ne
->ne_cmod
* sizeof(WORD
), &Read
, NULL
))) ||
446 (Read
!= (DWORD
)ne
->ne_cmod
* sizeof(WORD
)))
451 /* read imported names table */
452 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_imptab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
453 (!(nametab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_enttab
- ne
->ne_imptab
))) ||
454 (!(ReadFile(hFile
, nametab
, ne
->ne_enttab
- ne
->ne_imptab
, &Read
, NULL
))) ||
455 (Read
!= (DWORD
)ne
->ne_enttab
- ne
->ne_imptab
))
460 for(i
= 0; i
< ne
->ne_cmod
; i
++)
463 module
= &nametab
[modtab
[i
]];
464 if(!strncmp(&module
[1], "KERNEL", module
[0]))
466 /* very old windows file */
473 DPRINT1("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
476 HeapFree(GetProcessHeap(), 0, modtab
);
477 HeapFree(GetProcessHeap(), 0, nametab
);
478 SetFilePointer(hFile
, CurPos
, NULL
, FILE_BEGIN
);
483 InternalGetBinaryType(HANDLE hFile
)
489 unsigned char magic
[4];
490 unsigned char ignored
[12];
496 unsigned long cputype
;
497 unsigned long cpusubtype
;
498 unsigned long filetype
;
505 if((SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
506 (!ReadFile(hFile
, &Header
, sizeof(Header
), &Read
, NULL
) ||
507 (Read
!= sizeof(Header
))))
509 return BINARY_UNKNOWN
;
512 if(!memcmp(Header
.elf
.magic
, "\177ELF", sizeof(Header
.elf
.magic
)))
514 /* FIXME: we don't bother to check byte order, architecture, etc. */
515 switch(Header
.elf
.type
)
518 return BINARY_UNIX_EXE
;
520 return BINARY_UNIX_LIB
;
522 return BINARY_UNKNOWN
;
525 /* Mach-o File with Endian set to Big Endian or Little Endian*/
526 if(Header
.macho
.magic
== 0xFEEDFACE ||
527 Header
.macho
.magic
== 0xCEFAEDFE)
529 switch(Header
.macho
.filetype
)
533 return BINARY_UNIX_LIB
;
535 return BINARY_UNKNOWN
;
538 /* Not ELF, try DOS */
539 if(Header
.mz
.e_magic
== IMAGE_DOS_SIGNATURE
)
541 /* We do have a DOS image so we will now try to seek into
542 * the file by the amount indicated by the field
543 * "Offset to extended header" and read in the
544 * "magic" field information at that location.
545 * This will tell us if there is more header information
548 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
549 (!ReadFile(hFile
, magic
, sizeof(magic
), &Read
, NULL
) ||
550 (Read
!= sizeof(magic
))))
555 /* Reading the magic field succeeded so
556 * we will try to determine what type it is.
558 if(!memcmp(magic
, "PE\0\0", sizeof(magic
)))
560 IMAGE_FILE_HEADER FileHeader
;
561 if(!ReadFile(hFile
, &FileHeader
, sizeof(IMAGE_FILE_HEADER
), &Read
, NULL
) ||
562 (Read
!= sizeof(IMAGE_FILE_HEADER
)))
567 /* FIXME - detect 32/64 bit */
569 if(FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
570 return BINARY_PE_DLL32
;
571 return BINARY_PE_EXE32
;
574 if(!memcmp(magic
, "NE", 1))
576 /* This is a Windows executable (NE) header. This can
577 * mean either a 16-bit OS/2 or a 16-bit Windows or even a
578 * DOS program (running under a DOS extender). To decide
579 * which, we'll have to read the NE header.
582 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == 1) ||
583 !ReadFile(hFile
, &ne
, sizeof(IMAGE_OS2_HEADER
), &Read
, NULL
) ||
584 (Read
!= sizeof(IMAGE_OS2_HEADER
)))
586 /* Couldn't read header, so abort. */
597 return InternalIsOS2OrOldWin(hFile
, &Header
.mz
, &ne
);
602 return BINARY_UNKNOWN
;
611 LPCWSTR lpApplicationName
,
618 if(!lpApplicationName
|| !lpBinaryType
)
620 SetLastError(ERROR_INVALID_PARAMETER
);
624 hFile
= CreateFileW(lpApplicationName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
625 OPEN_EXISTING
, 0, 0);
626 if(hFile
== INVALID_HANDLE_VALUE
)
631 BinType
= InternalGetBinaryType(hFile
);
641 * guess from filename
643 if(!(dot
= wcsrchr(lpApplicationName
, L
'.')))
647 if(!lstrcmpiW(dot
, L
".COM"))
649 *lpBinaryType
= SCS_DOS_BINARY
;
652 if(!lstrcmpiW(dot
, L
".PIF"))
654 *lpBinaryType
= SCS_PIF_BINARY
;
659 case BINARY_PE_EXE32
:
660 case BINARY_PE_DLL32
:
662 *lpBinaryType
= SCS_32BIT_BINARY
;
665 case BINARY_PE_EXE64
:
666 case BINARY_PE_DLL64
:
668 *lpBinaryType
= SCS_64BIT_BINARY
;
673 *lpBinaryType
= SCS_WOW_BINARY
;
678 *lpBinaryType
= SCS_OS216_BINARY
;
683 *lpBinaryType
= SCS_DOS_BINARY
;
686 case BINARY_UNIX_EXE
:
687 case BINARY_UNIX_LIB
:
693 DPRINT1("Invalid binary type %lu returned!\n", BinType
);
702 GetBinaryTypeA(IN LPCSTR lpApplicationName
,
703 OUT LPDWORD lpBinaryType
)
705 ANSI_STRING ApplicationNameString
;
706 UNICODE_STRING ApplicationNameW
;
707 BOOL StringAllocated
= FALSE
, Result
;
710 RtlInitAnsiString(&ApplicationNameString
, lpApplicationName
);
712 if (ApplicationNameString
.Length
* sizeof(WCHAR
) >= NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
714 StringAllocated
= TRUE
;
715 Status
= RtlAnsiStringToUnicodeString(&ApplicationNameW
, &ApplicationNameString
, TRUE
);
719 Status
= RtlAnsiStringToUnicodeString(&(NtCurrentTeb()->StaticUnicodeString
), &ApplicationNameString
, FALSE
);
722 if (!NT_SUCCESS(Status
))
724 BaseSetLastNTError(Status
);
730 Result
= GetBinaryTypeW(ApplicationNameW
.Buffer
, lpBinaryType
);
731 RtlFreeUnicodeString(&ApplicationNameW
);
735 Result
= GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString
.Buffer
, lpBinaryType
);
759 ExitVDM(BOOL IsWow
, ULONG iWowTask
)
761 BASE_API_MESSAGE ApiMessage
;
762 PBASE_EXIT_VDM ExitVdm
= &ApiMessage
.Data
.ExitVDMRequest
;
764 /* Setup the input parameters */
765 ExitVdm
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
766 ExitVdm
->iWowTask
= IsWow
? iWowTask
: 0; /* Always zero for DOS tasks */
767 ExitVdm
->WaitObjectForVDM
= NULL
;
770 CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
772 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepExitVDM
),
773 sizeof(BASE_EXIT_VDM
));
775 /* Close the returned wait object handle, if any */
776 if (NT_SUCCESS(ApiMessage
.Status
) && (ExitVdm
->WaitObjectForVDM
!= NULL
))
778 CloseHandle(ExitVdm
->WaitObjectForVDM
);
788 GetNextVDMCommand(PGET_NEXT_VDM_COMMAND_DATA CommandData
)
800 GetVDMCurrentDirectories (
839 RegisterWowBaseHandlers (
867 SetVDMCurrentDirectories (
881 VDMConsoleOperation (
896 VDMOperationStarted (