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 ApiMessage
,
75 IN DWORD CreationFlags
,
76 IN LPSTARTUPINFOW StartupInfo
)
78 /* This is not supported */
85 BaseUpdateVDMEntry(IN ULONG UpdateIndex
,
86 IN OUT PHANDLE WaitHandle
,
90 #if 0 // Unimplemented in BASESRV
92 BASE_API_MESSAGE ApiMessage
;
93 PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry
= &ApiMessage
.Data
.UpdateVdmEntry
;
95 /* Check what update is being sent */
98 /* VDM is being undone */
101 /* Tell the server how far we had gotten along */
102 UpdateVdmEntry
->iTask
= (ULONG
)*WaitHandle
;
103 UpdateVdmEntry
->VDMCreationState
= IndexInfo
;
106 /* VDM is ready with a new process handle */
107 case VdmEntryUpdateProcess
:
109 /* Send it the process handle */
110 UpdateVdmEntry
->VDMProcessHandle
= *WaitHandle
;
111 UpdateVdmEntry
->iTask
= IndexInfo
;
115 /* Also check what kind of binary this is for the console handle */
116 if (BinaryType
== BINARY_TYPE_WOW
)
118 /* Magic value for 16-bit apps */
119 UpdateVdmEntry
->ConsoleHandle
= (HANDLE
)-1;
121 else if (UpdateVdmEntry
->iTask
)
123 /* No handle for true VDM */
124 UpdateVdmEntry
->ConsoleHandle
= 0;
128 /* Otherwise, send the regular consoel handle */
129 UpdateVdmEntry
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
132 /* Finally write the index and binary type */
133 UpdateVdmEntry
->EntryIndex
= UpdateIndex
;
134 UpdateVdmEntry
->BinaryType
= BinaryType
;
136 /* Send the message to CSRSS */
137 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
139 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepUpdateVDMEntry
),
140 sizeof(BASE_UPDATE_VDM_ENTRY
));
141 if (!NT_SUCCESS(Status
))
144 BaseSetLastNTError(Status
);
148 /* If this was an update, CSRSS returns a new wait handle */
149 if (UpdateIndex
== VdmEntryUpdateProcess
)
151 /* Return it to the caller */
152 *WaitHandle
= UpdateVdmEntry
->WaitObjectForParent
;
161 BaseCheckForVDM(IN HANDLE ProcessHandle
,
162 OUT LPDWORD ExitCode
)
164 #if 0 // Unimplemented in BASESRV
166 EVENT_BASIC_INFORMATION EventBasicInfo
;
167 BASE_API_MESSAGE ApiMessage
;
168 PBASE_GET_VDM_EXIT_CODE GetVdmExitCode
= &ApiMessage
.Data
.GetVdmExitCode
;
170 /* It's VDM if the process is actually a wait handle (an event) */
171 Status
= NtQueryEvent(ProcessHandle
,
172 EventBasicInformation
,
174 sizeof(EventBasicInfo
),
176 if (!NT_SUCCESS(Status
)) return FALSE
;
178 /* Setup the input parameters */
179 GetVdmExitCode
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
180 GetVdmExitCode
->hParent
= ProcessHandle
;
183 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
185 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepGetVDMExitCode
/* BasepCheckVDM */),
186 sizeof(BASE_GET_VDM_EXIT_CODE
));
187 if (!NT_SUCCESS(Status
)) return FALSE
;
189 /* Get the exit code from the reply */
190 *ExitCode
= GetVdmExitCode
->ExitCode
;
197 BaseGetVdmConfigInfo(IN LPCWSTR Reserved
,
200 IN PUNICODE_STRING CmdLineString
,
203 WCHAR Buffer
[MAX_PATH
];
204 WCHAR CommandLine
[MAX_PATH
* 2];
207 /* Clear the buffer in case we fail */
208 CmdLineString
->Buffer
= 0;
210 /* Always return the same size */
211 *VdmSize
= 0x1000000;
213 /* Get the system directory */
214 Length
= GetSystemDirectoryW(Buffer
, MAX_PATH
);
215 if (!(Length
) || (Length
>= MAX_PATH
))
217 /* Eliminate no path or path too big */
218 SetLastError(ERROR_INVALID_NAME
);
222 /* Check if this is VDM with a DOS Sequence ID */
225 /* Build the VDM string for it */
226 _snwprintf(CommandLine
,
228 L
"\"%s\\ntvdm.exe\" -i%lx %s%c",
231 (BinaryType
== 0x10) ? L
" " : L
"-w",
232 (BinaryType
== 0x40) ? 's' : ' ');
236 /* Non-DOS, build the stirng for it without the task ID */
237 _snwprintf(CommandLine
,
239 L
"\"%s\\ntvdm.exe\" %s%c",
241 (BinaryType
== 0x10) ? L
" " : L
"-w",
242 (BinaryType
== 0x40) ? 's' : ' ');
245 /* Create the actual string */
246 return RtlCreateUnicodeString(CmdLineString
, CommandLine
);
251 BaseGetEnvNameType_U(IN PWCHAR Name
,
257 /* Start by assuming unknown type */
260 /* Loop all the environment names */
261 for (i
= 0; i
< (sizeof(BasepEnvNameType
) / sizeof(ENV_INFO
)); i
++)
264 EnvInfo
= &BasepEnvNameType
[i
];
266 /* Check if it matches the name */
267 if ((EnvInfo
->NameLength
== NameLength
) &&
268 !(_wcsnicmp(EnvInfo
->Name
, Name
, NameLength
)))
270 /* It does, return the type */
271 NameType
= EnvInfo
->NameType
;
276 /* Return what we found, or unknown if nothing */
282 BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv
,
283 IN PUNICODE_STRING UnicodeEnv
)
287 /* Clear the ASCII buffer since Rtl creates this for us */
288 if (AnsiEnv
->Buffer
) RtlFreeAnsiString(AnsiEnv
);
290 /* The Unicode buffer is build by hand, though */
291 if (UnicodeEnv
->Buffer
)
293 /* So clear it through the API */
294 NtFreeVirtualMemory(NtCurrentProcess(),
295 (PVOID
*)&UnicodeEnv
->Buffer
,
306 BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment
,
307 IN PANSI_STRING AnsiEnv
,
308 IN PUNICODE_STRING UnicodeEnv
)
311 ULONG RegionSize
, EnvironmentSize
= 0;
312 PWCHAR p
, Environment
, NewEnvironment
;
315 /* Make sure we have both strings */
316 if (!(AnsiEnv
) || !(UnicodeEnv
))
319 SetLastError(ERROR_INVALID_PARAMETER
);
323 /* Check if an environment was passed in */
326 /* Nope, create one */
327 Status
= RtlCreateEnvironment(TRUE
, (PWCHAR
*)&Environment
);
328 if (!NT_SUCCESS(Status
)) goto Quickie
;
332 /* Use the one we got */
333 Environment
= lpEnvironment
;
336 /* Do we have something now ? */
339 /* Still not, fail out */
340 SetLastError(ERROR_BAD_ENVIRONMENT
);
344 /* Count how much space the whole environment takes */
346 while ((*p
++ != UNICODE_NULL
) && (*p
!= UNICODE_NULL
)) EnvironmentSize
++;
347 EnvironmentSize
+= sizeof(UNICODE_NULL
);
349 /* Allocate a new copy */
350 RegionSize
= (EnvironmentSize
+ MAX_PATH
) * sizeof(WCHAR
);
351 if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),
352 (PVOID
*)&NewEnvironment
,
358 /* We failed, bail out */
359 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
360 NewEnvironment
= NULL
;
364 /* Begin parsing the new environment */
367 /* FIXME: Code here */
372 /* Initialize the unicode string to hold it */
373 EnvironmentSize
= (p
- NewEnvironment
) * sizeof(WCHAR
);
374 RtlInitEmptyUnicodeString(UnicodeEnv
, NewEnvironment
, EnvironmentSize
);
375 UnicodeEnv
->Length
= EnvironmentSize
;
377 /* Create the ASCII version of it */
378 Status
= RtlUnicodeStringToAnsiString(AnsiEnv
, UnicodeEnv
, TRUE
);
379 if (!NT_SUCCESS(Status
))
381 /* Set last error if conversion failure */
382 BaseSetLastNTError(Status
);
386 /* Everything went okay, so return success */
388 NewEnvironment
= NULL
;
392 /* Cleanup path starts here, start by destroying the envrionment copy */
393 if (!(lpEnvironment
) && (Environment
)) RtlDestroyEnvironment(Environment
);
395 /* See if we are here due to failure */
398 /* Initialize the paths to be empty */
399 RtlInitEmptyUnicodeString(UnicodeEnv
, NULL
, 0);
400 RtlInitEmptyAnsiString(AnsiEnv
, NULL
, 0);
402 /* Free the environment copy */
404 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
405 (PVOID
*)&NewEnvironment
,
408 ASSERT(NT_SUCCESS(Status
));
411 /* Return the result */
416 /* Check whether a file is an OS/2 or a very old Windows executable
417 * by testing on import of KERNEL.
419 * FIXME: is reading the module imports the only way of discerning
420 * old Windows binaries from OS/2 ones ? At least it seems so...
423 InternalIsOS2OrOldWin(HANDLE hFile
, IMAGE_DOS_HEADER
*mz
, IMAGE_OS2_HEADER
*ne
)
426 LPWORD modtab
= NULL
;
427 LPSTR nametab
= NULL
;
432 CurPos
= SetFilePointer(hFile
, 0, NULL
, FILE_CURRENT
);
434 /* read modref table */
435 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_modtab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
436 (!(modtab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_cmod
* sizeof(WORD
)))) ||
437 (!(ReadFile(hFile
, modtab
, ne
->ne_cmod
* sizeof(WORD
), &Read
, NULL
))) ||
438 (Read
!= (DWORD
)ne
->ne_cmod
* sizeof(WORD
)))
443 /* read imported names table */
444 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_imptab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
445 (!(nametab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_enttab
- ne
->ne_imptab
))) ||
446 (!(ReadFile(hFile
, nametab
, ne
->ne_enttab
- ne
->ne_imptab
, &Read
, NULL
))) ||
447 (Read
!= (DWORD
)ne
->ne_enttab
- ne
->ne_imptab
))
452 for(i
= 0; i
< ne
->ne_cmod
; i
++)
455 module
= &nametab
[modtab
[i
]];
456 if(!strncmp(&module
[1], "KERNEL", module
[0]))
458 /* very old windows file */
465 DPRINT1("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
468 HeapFree(GetProcessHeap(), 0, modtab
);
469 HeapFree(GetProcessHeap(), 0, nametab
);
470 SetFilePointer(hFile
, CurPos
, NULL
, FILE_BEGIN
);
475 InternalGetBinaryType(HANDLE hFile
)
481 unsigned char magic
[4];
482 unsigned char ignored
[12];
488 unsigned long cputype
;
489 unsigned long cpusubtype
;
490 unsigned long filetype
;
497 if((SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
498 (!ReadFile(hFile
, &Header
, sizeof(Header
), &Read
, NULL
) ||
499 (Read
!= sizeof(Header
))))
501 return BINARY_UNKNOWN
;
504 if(!memcmp(Header
.elf
.magic
, "\177ELF", sizeof(Header
.elf
.magic
)))
506 /* FIXME: we don't bother to check byte order, architecture, etc. */
507 switch(Header
.elf
.type
)
510 return BINARY_UNIX_EXE
;
512 return BINARY_UNIX_LIB
;
514 return BINARY_UNKNOWN
;
517 /* Mach-o File with Endian set to Big Endian or Little Endian*/
518 if(Header
.macho
.magic
== 0xFEEDFACE ||
519 Header
.macho
.magic
== 0xCEFAEDFE)
521 switch(Header
.macho
.filetype
)
525 return BINARY_UNIX_LIB
;
527 return BINARY_UNKNOWN
;
530 /* Not ELF, try DOS */
531 if(Header
.mz
.e_magic
== IMAGE_DOS_SIGNATURE
)
533 /* We do have a DOS image so we will now try to seek into
534 * the file by the amount indicated by the field
535 * "Offset to extended header" and read in the
536 * "magic" field information at that location.
537 * This will tell us if there is more header information
540 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
541 (!ReadFile(hFile
, magic
, sizeof(magic
), &Read
, NULL
) ||
542 (Read
!= sizeof(magic
))))
547 /* Reading the magic field succeeded so
548 * we will try to determine what type it is.
550 if(!memcmp(magic
, "PE\0\0", sizeof(magic
)))
552 IMAGE_FILE_HEADER FileHeader
;
553 if(!ReadFile(hFile
, &FileHeader
, sizeof(IMAGE_FILE_HEADER
), &Read
, NULL
) ||
554 (Read
!= sizeof(IMAGE_FILE_HEADER
)))
559 /* FIXME - detect 32/64 bit */
561 if(FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
562 return BINARY_PE_DLL32
;
563 return BINARY_PE_EXE32
;
566 if(!memcmp(magic
, "NE", 1))
568 /* This is a Windows executable (NE) header. This can
569 * mean either a 16-bit OS/2 or a 16-bit Windows or even a
570 * DOS program (running under a DOS extender). To decide
571 * which, we'll have to read the NE header.
574 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == 1) ||
575 !ReadFile(hFile
, &ne
, sizeof(IMAGE_OS2_HEADER
), &Read
, NULL
) ||
576 (Read
!= sizeof(IMAGE_OS2_HEADER
)))
578 /* Couldn't read header, so abort. */
589 return InternalIsOS2OrOldWin(hFile
, &Header
.mz
, &ne
);
594 return BINARY_UNKNOWN
;
603 LPCWSTR lpApplicationName
,
610 if(!lpApplicationName
|| !lpBinaryType
)
612 SetLastError(ERROR_INVALID_PARAMETER
);
616 hFile
= CreateFileW(lpApplicationName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
617 OPEN_EXISTING
, 0, 0);
618 if(hFile
== INVALID_HANDLE_VALUE
)
623 BinType
= InternalGetBinaryType(hFile
);
633 * guess from filename
635 if(!(dot
= wcsrchr(lpApplicationName
, L
'.')))
639 if(!lstrcmpiW(dot
, L
".COM"))
641 *lpBinaryType
= SCS_DOS_BINARY
;
644 if(!lstrcmpiW(dot
, L
".PIF"))
646 *lpBinaryType
= SCS_PIF_BINARY
;
651 case BINARY_PE_EXE32
:
652 case BINARY_PE_DLL32
:
654 *lpBinaryType
= SCS_32BIT_BINARY
;
657 case BINARY_PE_EXE64
:
658 case BINARY_PE_DLL64
:
660 *lpBinaryType
= SCS_64BIT_BINARY
;
665 *lpBinaryType
= SCS_WOW_BINARY
;
670 *lpBinaryType
= SCS_OS216_BINARY
;
675 *lpBinaryType
= SCS_DOS_BINARY
;
678 case BINARY_UNIX_EXE
:
679 case BINARY_UNIX_LIB
:
685 DPRINT1("Invalid binary type returned!\n", BinType
);
694 GetBinaryTypeA(IN LPCSTR lpApplicationName
,
695 OUT LPDWORD lpBinaryType
)
697 ANSI_STRING ApplicationNameString
;
698 UNICODE_STRING ApplicationNameW
;
699 BOOL StringAllocated
= FALSE
, Result
;
702 RtlInitAnsiString(&ApplicationNameString
, lpApplicationName
);
704 if (ApplicationNameString
.Length
* sizeof(WCHAR
) >= NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
706 StringAllocated
= TRUE
;
707 Status
= RtlAnsiStringToUnicodeString(&ApplicationNameW
, &ApplicationNameString
, TRUE
);
711 Status
= RtlAnsiStringToUnicodeString(&(NtCurrentTeb()->StaticUnicodeString
), &ApplicationNameString
, FALSE
);
714 if (!NT_SUCCESS(Status
))
716 BaseSetLastNTError(Status
);
722 Result
= GetBinaryTypeW(ApplicationNameW
.Buffer
, lpBinaryType
);
723 RtlFreeUnicodeString(&ApplicationNameW
);
727 Result
= GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString
.Buffer
, lpBinaryType
);
780 GetVDMCurrentDirectories (
819 RegisterWowBaseHandlers (
847 SetVDMCurrentDirectories (
861 VDMConsoleOperation (
876 VDMOperationStarted (