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 PCSR_API_MESSAGE ApiMessage
,
76 IN DWORD CreationFlags
,
77 IN LPSTARTUPINFOW StartupInfo
,
78 IN HANDLE hUserToken OPTIONAL
)
80 /* This is not supported */
87 BaseUpdateVDMEntry(IN ULONG UpdateIndex
,
88 IN OUT PHANDLE WaitHandle
,
92 #if 0 // Unimplemented in BASESRV
94 BASE_API_MESSAGE ApiMessage
;
95 PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry
= &ApiMessage
.Data
.UpdateVdmEntry
;
97 /* Check what update is being sent */
100 /* VDM is being undone */
103 /* Tell the server how far we had gotten along */
104 UpdateVdmEntry
->iTask
= (ULONG
)*WaitHandle
;
105 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
;
117 /* Also check what kind of binary this is for the console handle */
118 if (BinaryType
== BINARY_TYPE_WOW
)
120 /* Magic value for 16-bit apps */
121 UpdateVdmEntry
->ConsoleHandle
= (HANDLE
)-1;
123 else if (UpdateVdmEntry
->iTask
)
125 /* No handle for true VDM */
126 UpdateVdmEntry
->ConsoleHandle
= 0;
130 /* Otherwise, send the regular consoel handle */
131 UpdateVdmEntry
->ConsoleHandle
= NtCurrentPeb()->ProcessParameters
->ConsoleHandle
;
134 /* Finally write the index and binary type */
135 UpdateVdmEntry
->EntryIndex
= UpdateIndex
;
136 UpdateVdmEntry
->BinaryType
= BinaryType
;
138 /* Send the message to CSRSS */
139 Status
= CsrClientCallServer((PCSR_API_MESSAGE
)&ApiMessage
,
141 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX
, BasepUpdateVDMEntry
),
142 sizeof(BASE_UPDATE_VDM_ENTRY
));
143 if (!NT_SUCCESS(Status
))
146 BaseSetLastNTError(Status
);
150 /* If this was an update, CSRSS returns a new wait handle */
151 if (UpdateIndex
== VdmEntryUpdateProcess
)
153 /* Return it to the caller */
154 *WaitHandle
= UpdateVdmEntry
->WaitObjectForParent
;
163 BaseCheckForVDM(IN HANDLE ProcessHandle
,
164 OUT LPDWORD ExitCode
)
166 #if 0 // Unimplemented in BASESRV
168 EVENT_BASIC_INFORMATION EventBasicInfo
;
169 BASE_API_MESSAGE ApiMessage
;
170 PBASE_GET_VDM_EXIT_CODE GetVdmExitCode
= &ApiMessage
.Data
.GetVdmExitCode
;
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
/* BasepCheckVDM */),
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
;
199 BaseGetVdmConfigInfo(IN LPCWSTR Reserved
,
202 IN PUNICODE_STRING CmdLineString
,
205 WCHAR Buffer
[MAX_PATH
];
206 WCHAR CommandLine
[MAX_PATH
* 2];
209 /* Clear the buffer in case we fail */
210 CmdLineString
->Buffer
= 0;
212 /* Always return the same size */
213 *VdmSize
= 0x1000000;
215 /* Get the system directory */
216 Length
= GetSystemDirectoryW(Buffer
, MAX_PATH
);
217 if (!(Length
) || (Length
>= MAX_PATH
))
219 /* Eliminate no path or path too big */
220 SetLastError(ERROR_INVALID_NAME
);
224 /* Check if this is VDM with a DOS Sequence ID */
227 /* Build the VDM string for it */
228 _snwprintf(CommandLine
,
230 L
"\"%s\\ntvdm.exe\" -i%lx %s%c",
233 (BinaryType
== 0x10) ? L
" " : L
"-w",
234 (BinaryType
== 0x40) ? 's' : ' ');
238 /* Non-DOS, build the string for it without the task ID */
239 _snwprintf(CommandLine
,
241 L
"\"%s\\ntvdm.exe\" %s%c",
243 (BinaryType
== 0x10) ? L
" " : L
"-w",
244 (BinaryType
== 0x40) ? 's' : ' ');
247 /* Create the actual string */
248 return RtlCreateUnicodeString(CmdLineString
, CommandLine
);
253 BaseGetEnvNameType_U(IN PWCHAR Name
,
259 /* Start by assuming unknown type */
262 /* Loop all the environment names */
263 for (i
= 0; i
< (sizeof(BasepEnvNameType
) / sizeof(ENV_INFO
)); i
++)
266 EnvInfo
= &BasepEnvNameType
[i
];
268 /* Check if it matches the name */
269 if ((EnvInfo
->NameLength
== NameLength
) &&
270 !(_wcsnicmp(EnvInfo
->Name
, Name
, NameLength
)))
272 /* It does, return the type */
273 NameType
= EnvInfo
->NameType
;
278 /* Return what we found, or unknown if nothing */
284 BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv
,
285 IN PUNICODE_STRING UnicodeEnv
)
289 /* Clear the ASCII buffer since Rtl creates this for us */
290 if (AnsiEnv
->Buffer
) RtlFreeAnsiString(AnsiEnv
);
292 /* The Unicode buffer is build by hand, though */
293 if (UnicodeEnv
->Buffer
)
295 /* So clear it through the API */
296 NtFreeVirtualMemory(NtCurrentProcess(),
297 (PVOID
*)&UnicodeEnv
->Buffer
,
308 BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment
,
309 IN PANSI_STRING AnsiEnv
,
310 IN PUNICODE_STRING UnicodeEnv
)
313 ULONG RegionSize
, EnvironmentSize
= 0;
314 PWCHAR p
, Environment
, NewEnvironment
= NULL
;
317 /* Make sure we have both strings */
318 if (!(AnsiEnv
) || !(UnicodeEnv
))
321 SetLastError(ERROR_INVALID_PARAMETER
);
325 /* Check if an environment was passed in */
328 /* Nope, create one */
329 Status
= RtlCreateEnvironment(TRUE
, (PWCHAR
*)&Environment
);
330 if (!NT_SUCCESS(Status
)) goto Quickie
;
334 /* Use the one we got */
335 Environment
= lpEnvironment
;
338 /* Do we have something now ? */
341 /* Still not, fail out */
342 SetLastError(ERROR_BAD_ENVIRONMENT
);
346 /* Count how much space the whole environment takes */
348 while ((*p
++ != UNICODE_NULL
) && (*p
!= UNICODE_NULL
)) EnvironmentSize
++;
349 EnvironmentSize
+= sizeof(UNICODE_NULL
);
351 /* Allocate a new copy */
352 RegionSize
= (EnvironmentSize
+ MAX_PATH
) * sizeof(WCHAR
);
353 if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),
354 (PVOID
*)&NewEnvironment
,
360 /* We failed, bail out */
361 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
362 NewEnvironment
= NULL
;
366 /* Begin parsing the new environment */
369 /* FIXME: Code here */
374 /* Initialize the unicode string to hold it */
375 EnvironmentSize
= (p
- NewEnvironment
) * sizeof(WCHAR
);
376 RtlInitEmptyUnicodeString(UnicodeEnv
, NewEnvironment
, EnvironmentSize
);
377 UnicodeEnv
->Length
= EnvironmentSize
;
379 /* Create the ASCII version of it */
380 Status
= RtlUnicodeStringToAnsiString(AnsiEnv
, UnicodeEnv
, TRUE
);
381 if (!NT_SUCCESS(Status
))
383 /* Set last error if conversion failure */
384 BaseSetLastNTError(Status
);
388 /* Everything went okay, so return success */
390 NewEnvironment
= NULL
;
394 /* Cleanup path starts here, start by destroying the envrionment copy */
395 if (!(lpEnvironment
) && (Environment
)) RtlDestroyEnvironment(Environment
);
397 /* See if we are here due to failure */
400 /* Initialize the paths to be empty */
401 RtlInitEmptyUnicodeString(UnicodeEnv
, NULL
, 0);
402 RtlInitEmptyAnsiString(AnsiEnv
, NULL
, 0);
404 /* Free the environment copy */
406 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
407 (PVOID
*)&NewEnvironment
,
410 ASSERT(NT_SUCCESS(Status
));
413 /* Return the result */
418 /* Check whether a file is an OS/2 or a very old Windows executable
419 * by testing on import of KERNEL.
421 * FIXME: is reading the module imports the only way of discerning
422 * old Windows binaries from OS/2 ones ? At least it seems so...
425 InternalIsOS2OrOldWin(HANDLE hFile
, IMAGE_DOS_HEADER
*mz
, IMAGE_OS2_HEADER
*ne
)
428 LPWORD modtab
= NULL
;
429 LPSTR nametab
= NULL
;
434 CurPos
= SetFilePointer(hFile
, 0, NULL
, FILE_CURRENT
);
436 /* read modref table */
437 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_modtab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
438 (!(modtab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_cmod
* sizeof(WORD
)))) ||
439 (!(ReadFile(hFile
, modtab
, ne
->ne_cmod
* sizeof(WORD
), &Read
, NULL
))) ||
440 (Read
!= (DWORD
)ne
->ne_cmod
* sizeof(WORD
)))
445 /* read imported names table */
446 if((SetFilePointer(hFile
, mz
->e_lfanew
+ ne
->ne_imptab
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
447 (!(nametab
= HeapAlloc(GetProcessHeap(), 0, ne
->ne_enttab
- ne
->ne_imptab
))) ||
448 (!(ReadFile(hFile
, nametab
, ne
->ne_enttab
- ne
->ne_imptab
, &Read
, NULL
))) ||
449 (Read
!= (DWORD
)ne
->ne_enttab
- ne
->ne_imptab
))
454 for(i
= 0; i
< ne
->ne_cmod
; i
++)
457 module
= &nametab
[modtab
[i
]];
458 if(!strncmp(&module
[1], "KERNEL", module
[0]))
460 /* very old windows file */
467 DPRINT1("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
470 HeapFree(GetProcessHeap(), 0, modtab
);
471 HeapFree(GetProcessHeap(), 0, nametab
);
472 SetFilePointer(hFile
, CurPos
, NULL
, FILE_BEGIN
);
477 InternalGetBinaryType(HANDLE hFile
)
483 unsigned char magic
[4];
484 unsigned char ignored
[12];
490 unsigned long cputype
;
491 unsigned long cpusubtype
;
492 unsigned long filetype
;
499 if((SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
500 (!ReadFile(hFile
, &Header
, sizeof(Header
), &Read
, NULL
) ||
501 (Read
!= sizeof(Header
))))
503 return BINARY_UNKNOWN
;
506 if(!memcmp(Header
.elf
.magic
, "\177ELF", sizeof(Header
.elf
.magic
)))
508 /* FIXME: we don't bother to check byte order, architecture, etc. */
509 switch(Header
.elf
.type
)
512 return BINARY_UNIX_EXE
;
514 return BINARY_UNIX_LIB
;
516 return BINARY_UNKNOWN
;
519 /* Mach-o File with Endian set to Big Endian or Little Endian*/
520 if(Header
.macho
.magic
== 0xFEEDFACE ||
521 Header
.macho
.magic
== 0xCEFAEDFE)
523 switch(Header
.macho
.filetype
)
527 return BINARY_UNIX_LIB
;
529 return BINARY_UNKNOWN
;
532 /* Not ELF, try DOS */
533 if(Header
.mz
.e_magic
== IMAGE_DOS_SIGNATURE
)
535 /* We do have a DOS image so we will now try to seek into
536 * the file by the amount indicated by the field
537 * "Offset to extended header" and read in the
538 * "magic" field information at that location.
539 * This will tell us if there is more header information
542 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
) ||
543 (!ReadFile(hFile
, magic
, sizeof(magic
), &Read
, NULL
) ||
544 (Read
!= sizeof(magic
))))
549 /* Reading the magic field succeeded so
550 * we will try to determine what type it is.
552 if(!memcmp(magic
, "PE\0\0", sizeof(magic
)))
554 IMAGE_FILE_HEADER FileHeader
;
555 if(!ReadFile(hFile
, &FileHeader
, sizeof(IMAGE_FILE_HEADER
), &Read
, NULL
) ||
556 (Read
!= sizeof(IMAGE_FILE_HEADER
)))
561 /* FIXME - detect 32/64 bit */
563 if(FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
564 return BINARY_PE_DLL32
;
565 return BINARY_PE_EXE32
;
568 if(!memcmp(magic
, "NE", 1))
570 /* This is a Windows executable (NE) header. This can
571 * mean either a 16-bit OS/2 or a 16-bit Windows or even a
572 * DOS program (running under a DOS extender). To decide
573 * which, we'll have to read the NE header.
576 if((SetFilePointer(hFile
, Header
.mz
.e_lfanew
, NULL
, FILE_BEGIN
) == 1) ||
577 !ReadFile(hFile
, &ne
, sizeof(IMAGE_OS2_HEADER
), &Read
, NULL
) ||
578 (Read
!= sizeof(IMAGE_OS2_HEADER
)))
580 /* Couldn't read header, so abort. */
591 return InternalIsOS2OrOldWin(hFile
, &Header
.mz
, &ne
);
596 return BINARY_UNKNOWN
;
605 LPCWSTR lpApplicationName
,
612 if(!lpApplicationName
|| !lpBinaryType
)
614 SetLastError(ERROR_INVALID_PARAMETER
);
618 hFile
= CreateFileW(lpApplicationName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
619 OPEN_EXISTING
, 0, 0);
620 if(hFile
== INVALID_HANDLE_VALUE
)
625 BinType
= InternalGetBinaryType(hFile
);
635 * guess from filename
637 if(!(dot
= wcsrchr(lpApplicationName
, L
'.')))
641 if(!lstrcmpiW(dot
, L
".COM"))
643 *lpBinaryType
= SCS_DOS_BINARY
;
646 if(!lstrcmpiW(dot
, L
".PIF"))
648 *lpBinaryType
= SCS_PIF_BINARY
;
653 case BINARY_PE_EXE32
:
654 case BINARY_PE_DLL32
:
656 *lpBinaryType
= SCS_32BIT_BINARY
;
659 case BINARY_PE_EXE64
:
660 case BINARY_PE_DLL64
:
662 *lpBinaryType
= SCS_64BIT_BINARY
;
667 *lpBinaryType
= SCS_WOW_BINARY
;
672 *lpBinaryType
= SCS_OS216_BINARY
;
677 *lpBinaryType
= SCS_DOS_BINARY
;
680 case BINARY_UNIX_EXE
:
681 case BINARY_UNIX_LIB
:
687 DPRINT1("Invalid binary type returned!\n", BinType
);
696 GetBinaryTypeA(IN LPCSTR lpApplicationName
,
697 OUT LPDWORD lpBinaryType
)
699 ANSI_STRING ApplicationNameString
;
700 UNICODE_STRING ApplicationNameW
;
701 BOOL StringAllocated
= FALSE
, Result
;
704 RtlInitAnsiString(&ApplicationNameString
, lpApplicationName
);
706 if (ApplicationNameString
.Length
* sizeof(WCHAR
) >= NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
708 StringAllocated
= TRUE
;
709 Status
= RtlAnsiStringToUnicodeString(&ApplicationNameW
, &ApplicationNameString
, TRUE
);
713 Status
= RtlAnsiStringToUnicodeString(&(NtCurrentTeb()->StaticUnicodeString
), &ApplicationNameString
, FALSE
);
716 if (!NT_SUCCESS(Status
))
718 BaseSetLastNTError(Status
);
724 Result
= GetBinaryTypeW(ApplicationNameW
.Buffer
, lpBinaryType
);
725 RtlFreeUnicodeString(&ApplicationNameW
);
729 Result
= GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString
.Buffer
, lpBinaryType
);
782 GetVDMCurrentDirectories (
821 RegisterWowBaseHandlers (
849 SetVDMCurrentDirectories (
863 VDMConsoleOperation (
878 VDMOperationStarted (