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
,
77 IN HANDLE hUserToken OPTIONAL
)
79 /* This is not supported */
86 BaseUpdateVDMEntry(IN ULONG UpdateIndex
,
87 IN OUT PHANDLE WaitHandle
,
91 #if 0 // Unimplemented in BASESRV
93 BASE_API_MESSAGE ApiMessage
;
94 PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry
= &ApiMessage
.Data
.UpdateVdmEntry
;
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
= (ULONG
)*WaitHandle
;
104 UpdateVdmEntry
->VDMCreationState
= IndexInfo
;
107 /* VDM is ready with a new process handle */
108 case VdmEntryUpdateProcess
:
110 /* Send it the process handle */
111 UpdateVdmEntry
->VDMProcessHandle
= *WaitHandle
;
112 UpdateVdmEntry
->iTask
= IndexInfo
;
116 /* Also check what kind of binary this is for the console handle */
117 if (BinaryType
== BINARY_TYPE_WOW
)
119 /* Magic value for 16-bit apps */
120 UpdateVdmEntry
->ConsoleHandle
= (HANDLE
)-1;
122 else if (UpdateVdmEntry
->iTask
)
124 /* No handle for true VDM */
125 UpdateVdmEntry
->ConsoleHandle
= 0;
129 /* Otherwise, send the regular consoel handle */
130 UpdateVdmEntry
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
133 /* Finally write the index and binary type */
134 UpdateVdmEntry
->EntryIndex
= UpdateIndex
;
135 UpdateVdmEntry
->BinaryType
= BinaryType
;
137 /* Send the message to CSRSS */
138 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
140 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepUpdateVDMEntry
),
141 sizeof(BASE_UPDATE_VDM_ENTRY
));
142 if (!NT_SUCCESS(Status
))
145 BaseSetLastNTError(Status
);
149 /* If this was an update, CSRSS returns a new wait handle */
150 if (UpdateIndex
== VdmEntryUpdateProcess
)
152 /* Return it to the caller */
153 *WaitHandle
= UpdateVdmEntry
->WaitObjectForParent
;
162 BaseCheckForVDM(IN HANDLE ProcessHandle
,
163 OUT LPDWORD ExitCode
)
165 #if 0 // Unimplemented in BASESRV
167 EVENT_BASIC_INFORMATION EventBasicInfo
;
168 BASE_API_MESSAGE ApiMessage
;
169 PBASE_GET_VDM_EXIT_CODE GetVdmExitCode
= &ApiMessage
.Data
.GetVdmExitCode
;
171 /* It's VDM if the process is actually a wait handle (an event) */
172 Status
= NtQueryEvent(ProcessHandle
,
173 EventBasicInformation
,
175 sizeof(EventBasicInfo
),
177 if (!NT_SUCCESS(Status
)) return FALSE
;
179 /* Setup the input parameters */
180 GetVdmExitCode
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
181 GetVdmExitCode
->hParent
= ProcessHandle
;
184 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
186 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepGetVDMExitCode
/* BasepCheckVDM */),
187 sizeof(BASE_GET_VDM_EXIT_CODE
));
188 if (!NT_SUCCESS(Status
)) return FALSE
;
190 /* Get the exit code from the reply */
191 *ExitCode
= GetVdmExitCode
->ExitCode
;
198 BaseGetVdmConfigInfo(IN LPCWSTR Reserved
,
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 */
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 */
226 /* Build the VDM string for it */
227 _snwprintf(CommandLine
,
229 L
"\"%s\\ntvdm.exe\" -i%lx %s%c",
232 (BinaryType
== 0x10) ? L
" " : L
"-w",
233 (BinaryType
== 0x40) ? 's' : ' ');
237 /* Non-DOS, build the string for it without the task ID */
238 _snwprintf(CommandLine
,
240 L
"\"%s\\ntvdm.exe\" %s%c",
242 (BinaryType
== 0x10) ? L
" " : L
"-w",
243 (BinaryType
== 0x40) ? 's' : ' ');
246 /* Create the actual string */
247 return RtlCreateUnicodeString(CmdLineString
, CommandLine
);
252 BaseGetEnvNameType_U(IN PWCHAR Name
,
258 /* Start by assuming unknown type */
261 /* Loop all the environment names */
262 for (i
= 0; i
< (sizeof(BasepEnvNameType
) / sizeof(ENV_INFO
)); i
++)
265 EnvInfo
= &BasepEnvNameType
[i
];
267 /* Check if it matches the name */
268 if ((EnvInfo
->NameLength
== NameLength
) &&
269 !(_wcsnicmp(EnvInfo
->Name
, Name
, NameLength
)))
271 /* It does, return the type */
272 NameType
= EnvInfo
->NameType
;
277 /* Return what we found, or unknown if nothing */
283 BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv
,
284 IN PUNICODE_STRING UnicodeEnv
)
288 /* Clear the ASCII buffer since Rtl creates this for us */
289 if (AnsiEnv
->Buffer
) RtlFreeAnsiString(AnsiEnv
);
291 /* The Unicode buffer is build by hand, though */
292 if (UnicodeEnv
->Buffer
)
294 /* So clear it through the API */
295 NtFreeVirtualMemory(NtCurrentProcess(),
296 (PVOID
*)&UnicodeEnv
->Buffer
,
307 BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment
,
308 IN PANSI_STRING AnsiEnv
,
309 IN PUNICODE_STRING UnicodeEnv
)
312 ULONG RegionSize
, EnvironmentSize
= 0;
313 PWCHAR p
, Environment
, NewEnvironment
= NULL
;
316 /* Make sure we have both strings */
317 if (!(AnsiEnv
) || !(UnicodeEnv
))
320 SetLastError(ERROR_INVALID_PARAMETER
);
324 /* Check if an environment was passed in */
327 /* Nope, create one */
328 Status
= RtlCreateEnvironment(TRUE
, (PWCHAR
*)&Environment
);
329 if (!NT_SUCCESS(Status
)) goto Quickie
;
333 /* Use the one we got */
334 Environment
= lpEnvironment
;
337 /* Do we have something now ? */
340 /* Still not, fail out */
341 SetLastError(ERROR_BAD_ENVIRONMENT
);
345 /* Count how much space the whole environment takes */
347 while ((*p
++ != UNICODE_NULL
) && (*p
!= UNICODE_NULL
)) EnvironmentSize
++;
348 EnvironmentSize
+= sizeof(UNICODE_NULL
);
350 /* Allocate a new copy */
351 RegionSize
= (EnvironmentSize
+ MAX_PATH
) * sizeof(WCHAR
);
352 if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),
353 (PVOID
*)&NewEnvironment
,
359 /* We failed, bail out */
360 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
361 NewEnvironment
= NULL
;
365 /* Begin parsing the new environment */
368 /* FIXME: Code here */
373 /* Initialize the unicode string to hold it */
374 EnvironmentSize
= (p
- NewEnvironment
) * sizeof(WCHAR
);
375 RtlInitEmptyUnicodeString(UnicodeEnv
, NewEnvironment
, EnvironmentSize
);
376 UnicodeEnv
->Length
= EnvironmentSize
;
378 /* Create the ASCII version of it */
379 Status
= RtlUnicodeStringToAnsiString(AnsiEnv
, UnicodeEnv
, TRUE
);
380 if (!NT_SUCCESS(Status
))
382 /* Set last error if conversion failure */
383 BaseSetLastNTError(Status
);
387 /* Everything went okay, so return success */
389 NewEnvironment
= NULL
;
393 /* Cleanup path starts here, start by destroying the envrionment copy */
394 if (!(lpEnvironment
) && (Environment
)) RtlDestroyEnvironment(Environment
);
396 /* See if we are here due to failure */
399 /* Initialize the paths to be empty */
400 RtlInitEmptyUnicodeString(UnicodeEnv
, NULL
, 0);
401 RtlInitEmptyAnsiString(AnsiEnv
, NULL
, 0);
403 /* Free the environment copy */
405 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
406 (PVOID
*)&NewEnvironment
,
409 ASSERT(NT_SUCCESS(Status
));
412 /* Return the result */
417 /* Check whether a file is an OS/2 or a very old Windows executable
418 * by testing on import of KERNEL.
420 * FIXME: is reading the module imports the only way of discerning
421 * old Windows binaries from OS/2 ones ? At least it seems so...
424 InternalIsOS2OrOldWin(HANDLE hFile
, IMAGE_DOS_HEADER
*mz
, IMAGE_OS2_HEADER
*ne
)
427 LPWORD modtab
= NULL
;
428 LPSTR nametab
= NULL
;
433 CurPos
= SetFilePointer(hFile
, 0, NULL
, FILE_CURRENT
);
435 /* read modref table */
436 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_modtab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
437 (!(modtab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_cmod
* sizeof(WORD
)))) ||
438 (!(ReadFile(hFile
, modtab
, ne
->ne_cmod
* sizeof(WORD
), &Read
, NULL
))) ||
439 (Read
!= (DWORD
)ne
->ne_cmod
* sizeof(WORD
)))
444 /* read imported names table */
445 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_imptab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
446 (!(nametab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_enttab
- ne
->ne_imptab
))) ||
447 (!(ReadFile(hFile
, nametab
, ne
->ne_enttab
- ne
->ne_imptab
, &Read
, NULL
))) ||
448 (Read
!= (DWORD
)ne
->ne_enttab
- ne
->ne_imptab
))
453 for(i
= 0; i
< ne
->ne_cmod
; i
++)
456 module
= &nametab
[modtab
[i
]];
457 if(!strncmp(&module
[1], "KERNEL", module
[0]))
459 /* very old windows file */
466 DPRINT1("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
469 HeapFree(GetProcessHeap(), 0, modtab
);
470 HeapFree(GetProcessHeap(), 0, nametab
);
471 SetFilePointer(hFile
, CurPos
, NULL
, FILE_BEGIN
);
476 InternalGetBinaryType(HANDLE hFile
)
482 unsigned char magic
[4];
483 unsigned char ignored
[12];
489 unsigned long cputype
;
490 unsigned long cpusubtype
;
491 unsigned long filetype
;
498 if((SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
499 (!ReadFile(hFile
, &Header
, sizeof(Header
), &Read
, NULL
) ||
500 (Read
!= sizeof(Header
))))
502 return BINARY_UNKNOWN
;
505 if(!memcmp(Header
.elf
.magic
, "\177ELF", sizeof(Header
.elf
.magic
)))
507 /* FIXME: we don't bother to check byte order, architecture, etc. */
508 switch(Header
.elf
.type
)
511 return BINARY_UNIX_EXE
;
513 return BINARY_UNIX_LIB
;
515 return BINARY_UNKNOWN
;
518 /* Mach-o File with Endian set to Big Endian or Little Endian*/
519 if(Header
.macho
.magic
== 0xFEEDFACE ||
520 Header
.macho
.magic
== 0xCEFAEDFE)
522 switch(Header
.macho
.filetype
)
526 return BINARY_UNIX_LIB
;
528 return BINARY_UNKNOWN
;
531 /* Not ELF, try DOS */
532 if(Header
.mz
.e_magic
== IMAGE_DOS_SIGNATURE
)
534 /* We do have a DOS image so we will now try to seek into
535 * the file by the amount indicated by the field
536 * "Offset to extended header" and read in the
537 * "magic" field information at that location.
538 * This will tell us if there is more header information
541 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
542 (!ReadFile(hFile
, magic
, sizeof(magic
), &Read
, NULL
) ||
543 (Read
!= sizeof(magic
))))
548 /* Reading the magic field succeeded so
549 * we will try to determine what type it is.
551 if(!memcmp(magic
, "PE\0\0", sizeof(magic
)))
553 IMAGE_FILE_HEADER FileHeader
;
554 if(!ReadFile(hFile
, &FileHeader
, sizeof(IMAGE_FILE_HEADER
), &Read
, NULL
) ||
555 (Read
!= sizeof(IMAGE_FILE_HEADER
)))
560 /* FIXME - detect 32/64 bit */
562 if(FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
563 return BINARY_PE_DLL32
;
564 return BINARY_PE_EXE32
;
567 if(!memcmp(magic
, "NE", 1))
569 /* This is a Windows executable (NE) header. This can
570 * mean either a 16-bit OS/2 or a 16-bit Windows or even a
571 * DOS program (running under a DOS extender). To decide
572 * which, we'll have to read the NE header.
575 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == 1) ||
576 !ReadFile(hFile
, &ne
, sizeof(IMAGE_OS2_HEADER
), &Read
, NULL
) ||
577 (Read
!= sizeof(IMAGE_OS2_HEADER
)))
579 /* Couldn't read header, so abort. */
590 return InternalIsOS2OrOldWin(hFile
, &Header
.mz
, &ne
);
595 return BINARY_UNKNOWN
;
604 LPCWSTR lpApplicationName
,
611 if(!lpApplicationName
|| !lpBinaryType
)
613 SetLastError(ERROR_INVALID_PARAMETER
);
617 hFile
= CreateFileW(lpApplicationName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
618 OPEN_EXISTING
, 0, 0);
619 if(hFile
== INVALID_HANDLE_VALUE
)
624 BinType
= InternalGetBinaryType(hFile
);
634 * guess from filename
636 if(!(dot
= wcsrchr(lpApplicationName
, L
'.')))
640 if(!lstrcmpiW(dot
, L
".COM"))
642 *lpBinaryType
= SCS_DOS_BINARY
;
645 if(!lstrcmpiW(dot
, L
".PIF"))
647 *lpBinaryType
= SCS_PIF_BINARY
;
652 case BINARY_PE_EXE32
:
653 case BINARY_PE_DLL32
:
655 *lpBinaryType
= SCS_32BIT_BINARY
;
658 case BINARY_PE_EXE64
:
659 case BINARY_PE_DLL64
:
661 *lpBinaryType
= SCS_64BIT_BINARY
;
666 *lpBinaryType
= SCS_WOW_BINARY
;
671 *lpBinaryType
= SCS_OS216_BINARY
;
676 *lpBinaryType
= SCS_DOS_BINARY
;
679 case BINARY_UNIX_EXE
:
680 case BINARY_UNIX_LIB
:
686 DPRINT1("Invalid binary type returned!\n", BinType
);
695 GetBinaryTypeA(IN LPCSTR lpApplicationName
,
696 OUT LPDWORD lpBinaryType
)
698 ANSI_STRING ApplicationNameString
;
699 UNICODE_STRING ApplicationNameW
;
700 BOOL StringAllocated
= FALSE
, Result
;
703 RtlInitAnsiString(&ApplicationNameString
, lpApplicationName
);
705 if (ApplicationNameString
.Length
* sizeof(WCHAR
) >= NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
707 StringAllocated
= TRUE
;
708 Status
= RtlAnsiStringToUnicodeString(&ApplicationNameW
, &ApplicationNameString
, TRUE
);
712 Status
= RtlAnsiStringToUnicodeString(&(NtCurrentTeb()->StaticUnicodeString
), &ApplicationNameString
, FALSE
);
715 if (!NT_SUCCESS(Status
))
717 BaseSetLastNTError(Status
);
723 Result
= GetBinaryTypeW(ApplicationNameW
.Buffer
, lpBinaryType
);
724 RtlFreeUnicodeString(&ApplicationNameW
);
728 Result
= GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString
.Buffer
, lpBinaryType
);
781 GetVDMCurrentDirectories (
820 RegisterWowBaseHandlers (
848 SetVDMCurrentDirectories (
862 VDMConsoleOperation (
877 VDMOperationStarted (