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 Machine (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 2;
55 String
.Length
= BaseDotPifSuffixName
.Length
;
56 String
.Buffer
= &PathName
->Buffer
[(PathName
->Length
- String
.Length
) / sizeof(WCHAR
)];
57 if (RtlEqualUnicodeString(&String
, &BaseDotPifSuffixName
, TRUE
)) return 3;
60 String
.Length
= BaseDotExeSuffixName
.Length
;
61 String
.Buffer
= &PathName
->Buffer
[(PathName
->Length
- String
.Length
) / sizeof(WCHAR
)];
62 if (RtlEqualUnicodeString(&String
, &BaseDotExeSuffixName
, TRUE
)) return 1;
68 BaseCheckVDM(IN ULONG BinaryType
,
69 IN PCWCH ApplicationName
,
71 IN PCWCH CurrentDirectory
,
72 IN PANSI_STRING AnsiEnvironment
,
73 IN PCSR_API_MESSAGE Msg
,
75 IN DWORD CreationFlags
,
76 IN LPSTARTUPINFOW StartupInfo
)
78 /* This is not supported */
85 BaseUpdateVDMEntry(IN ULONG UpdateIndex
,
86 IN OUT PHANDLE WaitHandle
,
92 ULONG CsrRequest
= MAKE_CSR_API(UPDATE_VDM_ENTRY
, CSR_CONSOLE
);
94 /* Check what update is being sent */
97 /* VDM is being undone */
100 /* Tell the server how far we had gotten along */
101 Msg
.Data
.UpdateVdmEntry
.iTask
= (ULONG
)*WaitHandle
;
102 Msg
.Data
.UpdateVdmEntry
.VDMCreationState
= IndexInfo
;
105 /* VDM is ready with a new process handle */
106 case VdmEntryUpdateProcess
:
108 /* Send it the process handle */
109 Msg
.Data
.UpdateVdmEntry
.VDMProcessHandle
= *WaitHandle
;
110 Msg
.Data
.UpdateVdmEntry
.iTask
= IndexInfo
;
114 /* Also check what kind of binary this is for the console handle */
115 if (BinaryType
== BINARY_TYPE_WOW
)
117 /* Magic value for 16-bit apps */
118 Msg
.Data
.UpdateVdmEntry
.ConsoleHandle
= (HANDLE
)-1;
120 else if (Msg
.Data
.UpdateVdmEntry
.iTask
)
122 /* No handle for true VDM */
123 Msg
.Data
.UpdateVdmEntry
.ConsoleHandle
= 0;
127 /* Otherwise, send the regular consoel handle */
128 Msg
.Data
.UpdateVdmEntry
.ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
131 /* Finally write the index and binary type */
132 Msg
.Data
.UpdateVdmEntry
.EntryIndex
= UpdateIndex
;
133 Msg
.Data
.UpdateVdmEntry
.BinaryType
= BinaryType
;
135 /* Send the message to CSRSS */
136 Status
= CsrClientCallServer(&Msg
, NULL
, CsrRequest
, sizeof(Msg
));
137 if (!(NT_SUCCESS(Status
)) || !(NT_SUCCESS(Msg
.Status
)))
140 BaseSetLastNTError(Msg
.Status
);
144 /* If this was an update, CSRSS returns a new wait handle */
145 if (UpdateIndex
== VdmEntryUpdateProcess
)
147 /* Return it to the caller */
148 *WaitHandle
= Msg
.Data
.UpdateVdmEntry
.WaitObjectForParent
;
157 BaseCheckForVDM(IN HANDLE ProcessHandle
,
158 OUT LPDWORD ExitCode
)
161 EVENT_BASIC_INFORMATION EventBasicInfo
;
163 ULONG CsrRequest
= MAKE_CSR_API(GET_VDM_EXIT_CODE
, CSR_CONSOLE
);
165 /* It's VDM if the process is actually a wait handle (an event) */
166 Status
= NtQueryEvent(ProcessHandle
,
167 EventBasicInformation
,
169 sizeof(EventBasicInfo
),
171 if (!NT_SUCCESS(Status
)) return FALSE
;
173 /* Setup the input parameters */
174 Msg
.Data
.GetVdmExitCode
.ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
175 Msg
.Data
.GetVdmExitCode
.hParent
= ProcessHandle
;
178 Status
= CsrClientCallServer(&Msg
, NULL
, CsrRequest
, sizeof(Msg
));
179 if (!NT_SUCCESS(Status
)) return FALSE
;
181 /* Get the exit code from the reply */
182 *ExitCode
= Msg
.Data
.GetVdmExitCode
.ExitCode
;
188 BaseGetVdmConfigInfo(IN LPCWSTR Reserved
,
191 IN PUNICODE_STRING CmdLineString
,
194 WCHAR Buffer
[MAX_PATH
];
195 WCHAR CommandLine
[MAX_PATH
* 2];
198 /* Clear the buffer in case we fail */
199 CmdLineString
->Buffer
= 0;
201 /* Always return the same size */
202 *VdmSize
= 0x1000000;
204 /* Get the system directory */
205 Length
= GetSystemDirectoryW(Buffer
, MAX_PATH
);
206 if (!(Length
) || (Length
>= MAX_PATH
))
208 /* Eliminate no path or path too big */
209 SetLastError(ERROR_INVALID_NAME
);
213 /* Check if this is VDM with a DOS Sequence ID */
216 /* Build the VDM string for it */
217 _snwprintf(CommandLine
,
219 L
"\"%s\\ntvdm.exe\" -i%lx %s%c",
222 (BinaryType
== 0x10) ? L
" " : L
"-w",
223 (BinaryType
== 0x40) ? 's' : ' ');
227 /* Non-DOS, build the stirng for it without the task ID */
228 _snwprintf(CommandLine
,
230 L
"\"%s\\ntvdm.exe\" %s%c",
232 (BinaryType
== 0x10) ? L
" " : L
"-w",
233 (BinaryType
== 0x40) ? 's' : ' ');
236 /* Create the actual string */
237 return RtlCreateUnicodeString(CmdLineString
, CommandLine
);
242 BaseGetEnvNameType_U(IN PWCHAR Name
,
248 /* Start by assuming unknown type */
251 /* Loop all the environment names */
252 for (i
= 0; i
< (sizeof(BasepEnvNameType
) / sizeof(ENV_INFO
)); i
++)
255 EnvInfo
= &BasepEnvNameType
[i
];
257 /* Check if it matches the name */
258 if ((EnvInfo
->NameLength
== NameLength
) &&
259 !(_wcsnicmp(EnvInfo
->Name
, Name
, NameLength
)))
261 /* It does, return the type */
262 NameType
= EnvInfo
->NameType
;
267 /* Return what we found, or unknown if nothing */
273 BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv
,
274 IN PUNICODE_STRING UnicodeEnv
)
278 /* Clear the ASCII buffer since Rtl creates this for us */
279 if (AnsiEnv
->Buffer
) RtlFreeAnsiString(AnsiEnv
);
281 /* The Unicode buffer is build by hand, though */
282 if (UnicodeEnv
->Buffer
)
284 /* So clear it through the API */
285 NtFreeVirtualMemory(NtCurrentProcess(),
286 (PVOID
*)&UnicodeEnv
->Buffer
,
297 BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment
,
298 IN PANSI_STRING AnsiEnv
,
299 IN PUNICODE_STRING UnicodeEnv
)
302 ULONG RegionSize
, EnvironmentSize
= 0;
303 PWCHAR p
, Environment
, NewEnvironment
;
306 /* Make sure we have both strings */
307 if (!(AnsiEnv
) || !(UnicodeEnv
))
310 SetLastError(ERROR_INVALID_PARAMETER
);
314 /* Check if an environment was passed in */
317 /* Nope, create one */
318 Status
= RtlCreateEnvironment(TRUE
, (PWCHAR
*)&Environment
);
319 if (!NT_SUCCESS(Status
)) goto Quickie
;
323 /* Use the one we got */
324 Environment
= lpEnvironment
;
327 /* Do we have something now ? */
330 /* Still not, fail out */
331 SetLastError(ERROR_BAD_ENVIRONMENT
);
335 /* Count how much space the whole environment takes */
337 while ((*p
++ != UNICODE_NULL
) && (*p
!= UNICODE_NULL
)) EnvironmentSize
++;
338 EnvironmentSize
+= sizeof(UNICODE_NULL
);
340 /* Allocate a new copy */
341 RegionSize
= (EnvironmentSize
+ MAX_PATH
) * sizeof(WCHAR
);
342 if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),
343 (PVOID
*)&NewEnvironment
,
349 /* We failed, bail out */
350 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
351 NewEnvironment
= NULL
;
355 /* Begin parsing the new environment */
358 /* FIXME: Code here */
363 /* Initialize the unicode string to hold it */
364 EnvironmentSize
= (p
- NewEnvironment
) * sizeof(WCHAR
);
365 RtlInitEmptyUnicodeString(UnicodeEnv
, NewEnvironment
, EnvironmentSize
);
366 UnicodeEnv
->Length
= EnvironmentSize
;
368 /* Create the ASCII version of it */
369 Status
= RtlUnicodeStringToAnsiString(AnsiEnv
, UnicodeEnv
, TRUE
);
370 if (!NT_SUCCESS(Status
))
372 /* Set last error if conversion failure */
373 BaseSetLastNTError(Status
);
377 /* Everything went okay, so return success */
379 NewEnvironment
= NULL
;
383 /* Cleanup path starts here, start by destroying the envrionment copy */
384 if (!(lpEnvironment
) && (Environment
)) RtlDestroyEnvironment(Environment
);
386 /* See if we are here due to failure */
389 /* Initialize the paths to be empty */
390 RtlInitEmptyUnicodeString(UnicodeEnv
, NULL
, 0);
391 RtlInitEmptyAnsiString(AnsiEnv
, NULL
, 0);
393 /* Free the environment copy */
395 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
396 (PVOID
*)&NewEnvironment
,
399 ASSERT(NT_SUCCESS(Status
));
402 /* Return the result */
407 /* Check whether a file is an OS/2 or a very old Windows executable
408 * by testing on import of KERNEL.
410 * FIXME: is reading the module imports the only way of discerning
411 * old Windows binaries from OS/2 ones ? At least it seems so...
414 InternalIsOS2OrOldWin(HANDLE hFile
, IMAGE_DOS_HEADER
*mz
, IMAGE_OS2_HEADER
*ne
)
417 LPWORD modtab
= NULL
;
418 LPSTR nametab
= NULL
;
423 CurPos
= SetFilePointer(hFile
, 0, NULL
, FILE_CURRENT
);
425 /* read modref table */
426 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_modtab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
427 (!(modtab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_cmod
* sizeof(WORD
)))) ||
428 (!(ReadFile(hFile
, modtab
, ne
->ne_cmod
* sizeof(WORD
), &Read
, NULL
))) ||
429 (Read
!= (DWORD
)ne
->ne_cmod
* sizeof(WORD
)))
434 /* read imported names table */
435 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_imptab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
436 (!(nametab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_enttab
- ne
->ne_imptab
))) ||
437 (!(ReadFile(hFile
, nametab
, ne
->ne_enttab
- ne
->ne_imptab
, &Read
, NULL
))) ||
438 (Read
!= (DWORD
)ne
->ne_enttab
- ne
->ne_imptab
))
443 for(i
= 0; i
< ne
->ne_cmod
; i
++)
446 module
= &nametab
[modtab
[i
]];
447 if(!strncmp(&module
[1], "KERNEL", module
[0]))
449 /* very old windows file */
456 DPRINT1("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
459 HeapFree(GetProcessHeap(), 0, modtab
);
460 HeapFree(GetProcessHeap(), 0, nametab
);
461 SetFilePointer(hFile
, CurPos
, NULL
, FILE_BEGIN
);
466 InternalGetBinaryType(HANDLE hFile
)
472 unsigned char magic
[4];
473 unsigned char ignored
[12];
479 unsigned long cputype
;
480 unsigned long cpusubtype
;
481 unsigned long filetype
;
488 if((SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
489 (!ReadFile(hFile
, &Header
, sizeof(Header
), &Read
, NULL
) ||
490 (Read
!= sizeof(Header
))))
492 return BINARY_UNKNOWN
;
495 if(!memcmp(Header
.elf
.magic
, "\177ELF", sizeof(Header
.elf
.magic
)))
497 /* FIXME: we don't bother to check byte order, architecture, etc. */
498 switch(Header
.elf
.type
)
501 return BINARY_UNIX_EXE
;
503 return BINARY_UNIX_LIB
;
505 return BINARY_UNKNOWN
;
508 /* Mach-o File with Endian set to Big Endian or Little Endian*/
509 if(Header
.macho
.magic
== 0xFEEDFACE ||
510 Header
.macho
.magic
== 0xCEFAEDFE)
512 switch(Header
.macho
.filetype
)
516 return BINARY_UNIX_LIB
;
518 return BINARY_UNKNOWN
;
521 /* Not ELF, try DOS */
522 if(Header
.mz
.e_magic
== IMAGE_DOS_SIGNATURE
)
524 /* We do have a DOS image so we will now try to seek into
525 * the file by the amount indicated by the field
526 * "Offset to extended header" and read in the
527 * "magic" field information at that location.
528 * This will tell us if there is more header information
531 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
532 (!ReadFile(hFile
, magic
, sizeof(magic
), &Read
, NULL
) ||
533 (Read
!= sizeof(magic
))))
538 /* Reading the magic field succeeded so
539 * we will try to determine what type it is.
541 if(!memcmp(magic
, "PE\0\0", sizeof(magic
)))
543 IMAGE_FILE_HEADER FileHeader
;
544 if(!ReadFile(hFile
, &FileHeader
, sizeof(IMAGE_FILE_HEADER
), &Read
, NULL
) ||
545 (Read
!= sizeof(IMAGE_FILE_HEADER
)))
550 /* FIXME - detect 32/64 bit */
552 if(FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
553 return BINARY_PE_DLL32
;
554 return BINARY_PE_EXE32
;
557 if(!memcmp(magic
, "NE", 1))
559 /* This is a Windows executable (NE) header. This can
560 * mean either a 16-bit OS/2 or a 16-bit Windows or even a
561 * DOS program (running under a DOS extender). To decide
562 * which, we'll have to read the NE header.
565 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == 1) ||
566 !ReadFile(hFile
, &ne
, sizeof(IMAGE_OS2_HEADER
), &Read
, NULL
) ||
567 (Read
!= sizeof(IMAGE_OS2_HEADER
)))
569 /* Couldn't read header, so abort. */
580 return InternalIsOS2OrOldWin(hFile
, &Header
.mz
, &ne
);
585 return BINARY_UNKNOWN
;
594 LPCWSTR lpApplicationName
,
601 if(!lpApplicationName
|| !lpBinaryType
)
603 SetLastError(ERROR_INVALID_PARAMETER
);
607 hFile
= CreateFileW(lpApplicationName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
608 OPEN_EXISTING
, 0, 0);
609 if(hFile
== INVALID_HANDLE_VALUE
)
614 BinType
= InternalGetBinaryType(hFile
);
624 * guess from filename
626 if(!(dot
= wcsrchr(lpApplicationName
, L
'.')))
630 if(!lstrcmpiW(dot
, L
".COM"))
632 *lpBinaryType
= SCS_DOS_BINARY
;
635 if(!lstrcmpiW(dot
, L
".PIF"))
637 *lpBinaryType
= SCS_PIF_BINARY
;
642 case BINARY_PE_EXE32
:
643 case BINARY_PE_DLL32
:
645 *lpBinaryType
= SCS_32BIT_BINARY
;
648 case BINARY_PE_EXE64
:
649 case BINARY_PE_DLL64
:
651 *lpBinaryType
= SCS_64BIT_BINARY
;
656 *lpBinaryType
= SCS_WOW_BINARY
;
661 *lpBinaryType
= SCS_OS216_BINARY
;
666 *lpBinaryType
= SCS_DOS_BINARY
;
669 case BINARY_UNIX_EXE
:
670 case BINARY_UNIX_LIB
:
676 DPRINT1("Invalid binary type returned!\n", BinType
);
685 GetBinaryTypeA(IN LPCSTR lpApplicationName
,
686 OUT LPDWORD lpBinaryType
)
688 ANSI_STRING ApplicationNameString
;
689 UNICODE_STRING ApplicationNameW
;
690 BOOL StringAllocated
= FALSE
, Result
;
693 RtlInitAnsiString(&ApplicationNameString
, lpApplicationName
);
695 if (ApplicationNameString
.Length
* sizeof(WCHAR
) >= NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
697 StringAllocated
= TRUE
;
698 Status
= RtlAnsiStringToUnicodeString(&ApplicationNameW
, &ApplicationNameString
, TRUE
);
702 Status
= RtlAnsiStringToUnicodeString(&(NtCurrentTeb()->StaticUnicodeString
), &ApplicationNameString
, FALSE
);
705 if (!NT_SUCCESS(Status
))
707 BaseSetLastNTError(Status
);
713 Result
= GetBinaryTypeW(ApplicationNameW
.Buffer
, lpBinaryType
);
714 RtlFreeUnicodeString(&ApplicationNameW
);
718 Result
= GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString
.Buffer
, lpBinaryType
);
771 GetVDMCurrentDirectories (
810 RegisterWowBaseHandlers (
838 SetVDMCurrentDirectories (
852 VDMConsoleOperation (
867 VDMOperationStarted (