a841ab5543f1f110ce96a4b75d05184fa768a33b
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / process.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dos/dos32krnl/process.c
5 * PURPOSE: DOS32 Processes
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #define NDEBUG
13
14 #include "ntvdm.h"
15 #include "emulator.h"
16 #include "cpu/cpu.h"
17
18 #include "dos.h"
19 #include "dos/dem.h"
20 #include "dosfiles.h"
21 #include "handle.h"
22 #include "process.h"
23 #include "memory.h"
24
25 #include "bios/bios.h"
26
27 #include "io.h"
28 #include "hardware/ps2.h"
29
30 /* PUBLIC VARIABLES ***********************************************************/
31
32 WORD CurrentPsp = SYSTEM_PSP;
33
34 /* PRIVATE FUNCTIONS **********************************************************/
35
36 static inline VOID DosSetPspCommandLine(WORD Segment, LPCSTR CommandLine)
37 {
38 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
39
40 /*
41 * Copy the command line block.
42 * Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
43 */
44 PspBlock->CommandLineSize = min(*(PBYTE)CommandLine, DOS_CMDLINE_LENGTH);
45 CommandLine++;
46 RtlCopyMemory(PspBlock->CommandLine, CommandLine, DOS_CMDLINE_LENGTH);
47 }
48
49 static inline VOID DosSaveState(VOID)
50 {
51 PDOS_REGISTER_STATE State;
52 WORD StackPointer = getSP();
53
54 /* Allocate stack space for the registers */
55 StackPointer -= sizeof(DOS_REGISTER_STATE);
56 State = SEG_OFF_TO_PTR(getSS(), StackPointer);
57 setSP(StackPointer);
58
59 /* Save */
60 State->EAX = getEAX();
61 State->ECX = getECX();
62 State->EDX = getEDX();
63 State->EBX = getEBX();
64 State->ESP = getESP();
65 State->EBP = getEBP();
66 State->ESI = getESI();
67 State->EDI = getEDI();
68 State->DS = getDS();
69 State->ES = getES();
70 State->FS = getFS();
71 State->GS = getGS();
72 State->Flags = getEFLAGS();
73 }
74
75 static inline VOID DosRestoreState(VOID)
76 {
77 PDOS_REGISTER_STATE State;
78
79 /* Pop the state structure from the stack */
80 State = SEG_OFF_TO_PTR(getSS(), getSP());
81 setSP(getSP() + sizeof(DOS_REGISTER_STATE));
82
83 /* Restore */
84 setEAX(State->EAX);
85 setECX(State->ECX);
86 setEDX(State->EDX);
87 setEBX(State->EBX);
88 setEBP(State->EBP);
89 setESI(State->ESI);
90 setEDI(State->EDI);
91 setDS(State->DS);
92 setES(State->ES);
93 setFS(State->FS);
94 setGS(State->GS);
95 setEFLAGS(State->Flags);
96 }
97
98 static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
99 LPCSTR ProgramName)
100 {
101 PCHAR Ptr, DestBuffer = NULL;
102 ULONG TotalSize = 0;
103 WORD DestSegment;
104
105 /* If we have an environment strings list, compute its size */
106 if (Environment)
107 {
108 /* Calculate the size of the environment block */
109 Ptr = (PCHAR)Environment;
110 while (*Ptr) Ptr += strlen(Ptr) + 1;
111 TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
112 }
113 else
114 {
115 /* Empty environment string */
116 TotalSize = 1;
117 }
118 /* Add the final environment block NULL-terminator */
119 TotalSize++;
120
121 /* Add the two bytes for the program name tag */
122 TotalSize += 2;
123
124 /* Add the string buffer size */
125 TotalSize += strlen(ProgramName) + 1;
126
127 /* Allocate the memory for the environment block */
128 DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
129 if (!DestSegment) return 0;
130
131 DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
132
133 /* If we have an environment strings list, copy it */
134 if (Environment)
135 {
136 Ptr = (PCHAR)Environment;
137 while (*Ptr)
138 {
139 /* Copy the string and NULL-terminate it */
140 strcpy(DestBuffer, Ptr);
141 DestBuffer += strlen(Ptr);
142 *(DestBuffer++) = '\0';
143
144 /* Move to the next string */
145 Ptr += strlen(Ptr) + 1;
146 }
147 }
148 else
149 {
150 /* Empty environment string */
151 *(DestBuffer++) = '\0';
152 }
153 /* NULL-terminate the environment block */
154 *(DestBuffer++) = '\0';
155
156 /* Store the special program name tag */
157 *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
158 *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
159
160 /* Copy the program name after the environment block */
161 strcpy(DestBuffer, ProgramName);
162
163 return DestSegment;
164 }
165
166 /* PUBLIC FUNCTIONS ***********************************************************/
167
168 VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
169 {
170 PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
171 PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
172 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
173
174 /* Literally copy the PSP first */
175 RtlCopyMemory(DestPsp, SourcePsp, sizeof(DOS_PSP));
176
177 /* Save the interrupt vectors */
178 DestPsp->TerminateAddress = IntVecTable[0x22];
179 DestPsp->BreakAddress = IntVecTable[0x23];
180 DestPsp->CriticalAddress = IntVecTable[0x24];
181
182 /* No parent PSP */
183 DestPsp->ParentPsp = 0;
184
185 /* Set the handle table pointers to the internal handle table */
186 DestPsp->HandleTableSize = DEFAULT_JFT_SIZE;
187 DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
188
189 /* Copy the parent handle table without referencing the SFT */
190 RtlCopyMemory(FAR_POINTER(DestPsp->HandleTablePtr),
191 FAR_POINTER(SourcePsp->HandleTablePtr),
192 DEFAULT_JFT_SIZE);
193 }
194
195 VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
196 {
197 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
198 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
199
200 RtlZeroMemory(PspBlock, sizeof(*PspBlock));
201
202 /* Set the exit interrupt */
203 PspBlock->Exit[0] = 0xCD; // int 0x20
204 PspBlock->Exit[1] = 0x20;
205
206 /* Set the number of the last paragraph */
207 PspBlock->LastParagraph = Segment + ProgramSize - 1;
208
209 /* Save the interrupt vectors */
210 PspBlock->TerminateAddress = IntVecTable[0x22];
211 PspBlock->BreakAddress = IntVecTable[0x23];
212 PspBlock->CriticalAddress = IntVecTable[0x24];
213
214 /* Set the parent PSP */
215 PspBlock->ParentPsp = CurrentPsp;
216
217 /* No environment block yet */
218 PspBlock->EnvBlock = 0;
219
220 /* Copy the parent handle table */
221 DosCopyHandleTable(PspBlock->HandleTable);
222
223 /* Set the handle table pointers to the internal handle table */
224 PspBlock->HandleTableSize = DEFAULT_JFT_SIZE;
225 PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
226
227 /* Set the DOS version */
228 PspBlock->DosVersion = DOS_VERSION;
229
230 /* Set the far call opcodes */
231 PspBlock->FarCall[0] = 0xCD; // int 0x21
232 PspBlock->FarCall[1] = 0x21;
233 PspBlock->FarCall[2] = 0xCB; // retf
234 }
235
236 VOID DosSetProcessContext(WORD Segment)
237 {
238 CurrentPsp = Segment;
239 DiskTransferArea = MAKELONG(0x80, Segment);
240 }
241
242 DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
243 IN LPCSTR ExecutablePath,
244 IN PDOS_EXEC_PARAM_BLOCK Parameters,
245 IN LPCSTR CommandLine OPTIONAL,
246 IN LPCSTR Environment OPTIONAL)
247 {
248 DWORD Result = ERROR_SUCCESS;
249 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
250 LPBYTE Address = NULL;
251 WORD Segment = 0;
252 WORD EnvBlock = 0;
253 WORD LoadSegment;
254 WORD MaxAllocSize;
255 DWORD i, FileSize;
256 CHAR FullPath[MAX_PATH];
257 CHAR ShortFullPath[MAX_PATH];
258
259 /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
260 CHAR CmdLineBuffer[1 + DOS_CMDLINE_LENGTH];
261
262 DPRINT1("DosLoadExecutable(%d, '%s', 0x%08X, 0x%08X, 0x%08X)\n",
263 LoadType,
264 ExecutablePath,
265 Parameters,
266 CommandLine,
267 Environment);
268
269 /* Try to get the full path to the executable */
270 if (GetFullPathNameA(ExecutablePath, sizeof(FullPath), FullPath, NULL))
271 {
272 /* Try to shorten the full path */
273 if (GetShortPathNameA(FullPath, ShortFullPath, sizeof(ShortFullPath)))
274 {
275 /* Use the shortened full path from now on */
276 ExecutablePath = ShortFullPath;
277 }
278 }
279
280 /* Open a handle to the executable */
281 FileHandle = CreateFileA(ExecutablePath,
282 GENERIC_READ,
283 FILE_SHARE_READ,
284 NULL,
285 OPEN_EXISTING,
286 FILE_ATTRIBUTE_NORMAL,
287 NULL);
288 if (FileHandle == INVALID_HANDLE_VALUE)
289 {
290 Result = GetLastError();
291 goto Cleanup;
292 }
293
294 /* Get the file size */
295 FileSize = GetFileSize(FileHandle, NULL);
296
297 /* Create a mapping object for the file */
298 FileMapping = CreateFileMapping(FileHandle,
299 NULL,
300 PAGE_READONLY,
301 0,
302 0,
303 NULL);
304 if (FileMapping == NULL)
305 {
306 Result = GetLastError();
307 goto Cleanup;
308 }
309
310 /* Map the file into memory */
311 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
312 if (Address == NULL)
313 {
314 Result = GetLastError();
315 goto Cleanup;
316 }
317
318 if (LoadType != DOS_LOAD_OVERLAY)
319 {
320 /* If an optional Win32 command line is given... */
321 if (CommandLine)
322 {
323 /* ... convert it into DOS format */
324 BYTE CmdLineLen;
325
326 PBYTE CmdLineSize = (PBYTE)CmdLineBuffer;
327 LPSTR CmdLineStart = CmdLineBuffer + 1;
328 LPSTR CmdLinePtr = CmdLineStart;
329
330 // For debugging purposes
331 RtlFillMemory(CmdLineBuffer, sizeof(CmdLineBuffer), 0xFF);
332
333 /*
334 * Set the command line: it is either an empty command line or has
335 * the format: " foo bar ..." (with at least one leading whitespace),
336 * and is then always followed by '\r' (and optionally by '\n').
337 */
338 CmdLineLen = (BYTE)strlen(CommandLine);
339 *CmdLineSize = 0;
340
341 /*
342 * Add the leading space if the command line is not empty
343 * and doesn't already start with some whitespace...
344 */
345 if (*CommandLine && *CommandLine != '\r' && *CommandLine != '\n' &&
346 *CommandLine != ' ' && *CommandLine != '\t')
347 {
348 (*CmdLineSize)++;
349 *CmdLinePtr++ = ' ';
350 }
351
352 /* Compute the number of characters we need to copy from the original command line */
353 CmdLineLen = min(CmdLineLen, DOS_CMDLINE_LENGTH - *CmdLineSize);
354
355 /* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
356 while (CmdLineLen && (CommandLine[CmdLineLen - 1] == '\r' || CommandLine[CmdLineLen - 1] == '\n'))
357 {
358 CmdLineLen--;
359 }
360
361 /* Finally, set everything up */
362 *CmdLineSize += CmdLineLen;
363 RtlCopyMemory(CmdLinePtr, CommandLine, CmdLineLen);
364 CmdLineStart[*CmdLineSize] = '\r';
365
366 /* Finally make the pointer point to the static buffer */
367 CommandLine = CmdLineBuffer;
368 }
369 else
370 {
371 /*
372 * ... otherwise, get the one from the parameter block.
373 * Format of the command line: 1 byte for size; 127 bytes for contents.
374 */
375 ASSERT(Parameters);
376 CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
377 }
378
379 /* If no optional environment is given... */
380 if (Environment == NULL)
381 {
382 /* ... get the one from the parameter block */
383 ASSERT(Parameters);
384 Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
385 }
386
387 /* Copy the environment block to DOS memory */
388 EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
389 if (EnvBlock == 0)
390 {
391 Result = ERROR_NOT_ENOUGH_MEMORY;
392 goto Cleanup;
393 }
394 }
395
396 /* Check if this is an EXE file or a COM file */
397 if (Address[0] == 'M' && Address[1] == 'Z')
398 {
399 /* EXE file */
400 PIMAGE_DOS_HEADER Header;
401 DWORD BaseSize;
402 PDWORD RelocationTable;
403 PWORD RelocWord;
404 WORD RelocFactor;
405 BOOLEAN LoadHigh = FALSE;
406
407 /* Get the MZ header */
408 Header = (PIMAGE_DOS_HEADER)Address;
409
410 /* Get the base size of the file, in paragraphs (rounded up) */
411 BaseSize = ((((Header->e_cp - (Header->e_cblp != 0)) * 512)
412 + Header->e_cblp + 0x0F) >> 4) - Header->e_cparhdr;
413
414 if (LoadType != DOS_LOAD_OVERLAY)
415 {
416 DWORD TotalSize = BaseSize;
417
418 /* Add the PSP size, in paragraphs */
419 TotalSize += sizeof(DOS_PSP) >> 4;
420
421 /* Add the maximum size that should be allocated */
422 TotalSize += Header->e_maxalloc;
423
424 if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
425 {
426 /* This program should be loaded high */
427 LoadHigh = TRUE;
428 TotalSize = 0xFFFF;
429 }
430
431 /* Make sure it does not pass 0xFFFF */
432 if (TotalSize > 0xFFFF) TotalSize = 0xFFFF;
433
434 /* Try to allocate that much memory */
435 Segment = DosAllocateMemory((WORD)TotalSize, &MaxAllocSize);
436
437 if (Segment == 0)
438 {
439 /* Check if there's at least enough memory for the minimum size */
440 if (MaxAllocSize < (BaseSize + (sizeof(DOS_PSP) >> 4) + Header->e_minalloc))
441 {
442 Result = DosLastError;
443 goto Cleanup;
444 }
445
446 /* Allocate that minimum amount */
447 TotalSize = MaxAllocSize;
448 Segment = DosAllocateMemory((WORD)TotalSize, NULL);
449 ASSERT(Segment != 0);
450 }
451
452 /* The process owns its own memory */
453 DosChangeMemoryOwner(Segment, Segment);
454 DosChangeMemoryOwner(EnvBlock, Segment);
455
456 /* Set INT 22h to the current CS:IP */
457 ((PULONG)BaseAddress)[0x22] = MAKELONG(getIP(), getCS());
458
459 /* Create the PSP */
460 DosCreatePsp(Segment, (WORD)TotalSize);
461 DosSetPspCommandLine(Segment, CommandLine);
462 SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
463
464 /* Calculate the segment where the program should be loaded */
465 if (!LoadHigh) LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
466 else LoadSegment = Segment + TotalSize - BaseSize;
467
468 RelocFactor = LoadSegment;
469 }
470 else
471 {
472 ASSERT(Parameters);
473 LoadSegment = Parameters->Overlay.Segment;
474 RelocFactor = Parameters->Overlay.RelocationFactor;
475 }
476
477 /* Copy the program to the code segment */
478 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
479 Address + (Header->e_cparhdr << 4),
480 min(FileSize - (Header->e_cparhdr << 4), BaseSize << 4));
481
482 /* Get the relocation table */
483 RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
484
485 /* Perform relocations */
486 for (i = 0; i < Header->e_crlc; i++)
487 {
488 /* Get a pointer to the word that needs to be patched */
489 RelocWord = (PWORD)SEG_OFF_TO_PTR(LoadSegment + HIWORD(RelocationTable[i]),
490 LOWORD(RelocationTable[i]));
491
492 /* Add the relocation factor to it */
493 *RelocWord += RelocFactor;
494 }
495
496 if (LoadType == DOS_LOAD_AND_EXECUTE)
497 {
498 /* Save the program state */
499 if (CurrentPsp != SYSTEM_PSP)
500 {
501 /* Push the task state */
502 DosSaveState();
503
504 /* Update the last stack in the PSP */
505 SEGMENT_TO_PSP(CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
506 }
507
508 /* Set the initial segment registers */
509 setDS(Segment);
510 setES(Segment);
511
512 /* Set the stack to the location from the header */
513 setSS(LoadSegment + Header->e_ss);
514 setSP(Header->e_sp);
515
516 /* Execute */
517 DosSetProcessContext(Segment);
518 CpuExecute(LoadSegment + Header->e_cs, Header->e_ip);
519 }
520 else if (LoadType == DOS_LOAD_ONLY)
521 {
522 ASSERT(Parameters);
523 Parameters->StackLocation = MAKELONG(Header->e_sp, LoadSegment + Header->e_ss);
524 Parameters->EntryPoint = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
525 }
526 }
527 else
528 {
529 /* COM file */
530
531 if (LoadType != DOS_LOAD_OVERLAY)
532 {
533 /* Find the maximum amount of memory that can be allocated */
534 DosAllocateMemory(0xFFFF, &MaxAllocSize);
535
536 /* Make sure it's enough for the whole program and the PSP */
537 if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
538 {
539 Result = ERROR_NOT_ENOUGH_MEMORY;
540 goto Cleanup;
541 }
542
543 /* Allocate all of it */
544 Segment = DosAllocateMemory(MaxAllocSize, NULL);
545 if (Segment == 0)
546 {
547 Result = DosLastError;
548 goto Cleanup;
549 }
550
551 /* The process owns its own memory */
552 DosChangeMemoryOwner(Segment, Segment);
553 DosChangeMemoryOwner(EnvBlock, Segment);
554
555 /* Set INT 22h to the current CS:IP */
556 ((PULONG)BaseAddress)[0x22] = MAKELONG(getIP(), getCS());
557
558 /* Create the PSP */
559 DosCreatePsp(Segment, MaxAllocSize);
560 DosSetPspCommandLine(Segment, CommandLine);
561 SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
562
563 /* Calculate the segment where the program should be loaded */
564 LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
565 }
566 else
567 {
568 ASSERT(Parameters);
569 LoadSegment = Parameters->Overlay.Segment;
570 }
571
572 /* Copy the program to the code segment */
573 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
574 Address,
575 FileSize);
576
577 if (LoadType == DOS_LOAD_AND_EXECUTE)
578 {
579 /* Save the program state */
580 if (CurrentPsp != SYSTEM_PSP)
581 {
582 /* Push the task state */
583 DosSaveState();
584
585 /* Update the last stack in the PSP */
586 SEGMENT_TO_PSP(CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
587 }
588
589 /* Set the initial segment registers */
590 setDS(Segment);
591 setES(Segment);
592
593 /* Set the stack to the last word of the segment */
594 setSS(Segment);
595 setSP(0xFFFE);
596
597 /*
598 * Set the value on the stack to 0, so that a near return
599 * jumps to PSP:0000 which has the exit code.
600 */
601 *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
602
603 /* Execute */
604 DosSetProcessContext(Segment);
605 CpuExecute(Segment, 0x100);
606 }
607 else if (LoadType == DOS_LOAD_ONLY)
608 {
609 ASSERT(Parameters);
610 Parameters->StackLocation = MAKELONG(0xFFFE, Segment);
611 Parameters->EntryPoint = MAKELONG(0x0100, Segment);
612 }
613 }
614
615 Cleanup:
616 if (Result != ERROR_SUCCESS)
617 {
618 /* It was not successful, cleanup the DOS memory */
619 if (EnvBlock) DosFreeMemory(EnvBlock);
620 if (Segment) DosFreeMemory(Segment);
621 }
622
623 /* Unmap the file*/
624 if (Address != NULL) UnmapViewOfFile(Address);
625
626 /* Close the file mapping object */
627 if (FileMapping != NULL) CloseHandle(FileMapping);
628
629 /* Close the file handle */
630 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
631
632 return Result;
633 }
634
635 DWORD DosStartProcess(IN LPCSTR ExecutablePath,
636 IN LPCSTR CommandLine,
637 IN LPCSTR Environment OPTIONAL)
638 {
639 DWORD Result;
640
641 SIZE_T CmdLen = strlen(CommandLine);
642 DPRINT1("Starting '%s' ('%.*s')...\n",
643 ExecutablePath,
644 /* Display the command line without the terminating 0d 0a (and skip the terminating NULL) */
645 CmdLen >= 2 ? (CommandLine[CmdLen - 2] == '\r' ? CmdLen - 2
646 : CmdLen)
647 : CmdLen,
648 CommandLine);
649
650 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
651 ExecutablePath,
652 NULL,
653 CommandLine,
654 Environment);
655
656 if (Result != ERROR_SUCCESS) goto Quit;
657
658 #ifndef STANDALONE
659 /* Update console title if we run in a separate console */
660 if (SessionId != 0)
661 SetConsoleTitleA(ExecutablePath);
662 #endif
663
664 /* Attach to the console */
665 ConsoleAttach();
666 VidBiosAttachToConsole();
667
668 /* Start simulation */
669 SetEvent(VdmTaskEvent);
670 CpuSimulate();
671
672 /* Detach from the console */
673 VidBiosDetachFromConsole();
674 ConsoleDetach();
675
676 Quit:
677 return Result;
678 }
679
680 #ifndef STANDALONE
681 WORD DosCreateProcess(LPCSTR ProgramName,
682 PDOS_EXEC_PARAM_BLOCK Parameters)
683 {
684 DWORD Result;
685 DWORD BinaryType;
686 LPVOID Environment = NULL;
687 VDM_COMMAND_INFO CommandInfo;
688 CHAR CmdLine[MAX_PATH + DOS_CMDLINE_LENGTH + 1];
689 CHAR AppName[MAX_PATH];
690 CHAR PifFile[MAX_PATH];
691 CHAR Desktop[MAX_PATH];
692 CHAR Title[MAX_PATH];
693 LPSTR CmdLinePtr;
694 ULONG CmdLineSize;
695 ULONG EnvSize = 256;
696 PVOID Env;
697 STARTUPINFOA StartupInfo;
698 PROCESS_INFORMATION ProcessInfo;
699
700 /* Get the binary type */
701 if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
702
703 /* Initialize Win32-VDM environment */
704 Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
705 if (Env == NULL) return GetLastError();
706
707 /* Did the caller specify an environment segment? */
708 if (Parameters->Environment)
709 {
710 /* Yes, use it instead of the parent one */
711 Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
712 }
713
714 /* Set up the startup info structure */
715 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
716 StartupInfo.cb = sizeof(StartupInfo);
717
718 /*
719 * Convert the DOS command line to Win32-compatible format, by concatenating
720 * the program name with the converted command line.
721 * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
722 */
723 CmdLinePtr = CmdLine;
724 strncpy(CmdLinePtr, ProgramName, MAX_PATH); // Concatenate the program name
725 CmdLinePtr += strlen(CmdLinePtr);
726 *CmdLinePtr++ = ' '; // Add separating space
727
728 CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
729 RtlCopyMemory(CmdLinePtr,
730 (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
731 CmdLineSize);
732 /* NULL-terminate it */
733 CmdLinePtr[CmdLineSize] = '\0';
734
735 /* Remove any trailing return carriage character and NULL-terminate the command line */
736 while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
737 *CmdLinePtr = '\0';
738
739 /* Create the process */
740 if (!CreateProcessA(ProgramName,
741 CmdLine,
742 NULL,
743 NULL,
744 FALSE,
745 0,
746 Environment,
747 NULL,
748 &StartupInfo,
749 &ProcessInfo))
750 {
751 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
752 return GetLastError();
753 }
754
755 /* Check the type of the program */
756 switch (BinaryType)
757 {
758 /* These are handled by NTVDM */
759 case SCS_DOS_BINARY:
760 case SCS_WOW_BINARY:
761 {
762 /* Clear the structure */
763 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
764
765 /* Initialize the structure members */
766 CommandInfo.TaskId = SessionId;
767 CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
768 CommandInfo.CmdLine = CmdLine;
769 CommandInfo.CmdLen = sizeof(CmdLine);
770 CommandInfo.AppName = AppName;
771 CommandInfo.AppLen = sizeof(AppName);
772 CommandInfo.PifFile = PifFile;
773 CommandInfo.PifLen = sizeof(PifFile);
774 CommandInfo.Desktop = Desktop;
775 CommandInfo.DesktopLen = sizeof(Desktop);
776 CommandInfo.Title = Title;
777 CommandInfo.TitleLen = sizeof(Title);
778 CommandInfo.Env = Env;
779 CommandInfo.EnvLen = EnvSize;
780
781 Command:
782 /* Get the VDM command information */
783 if (!GetNextVDMCommand(&CommandInfo))
784 {
785 if (CommandInfo.EnvLen > EnvSize)
786 {
787 /* Expand the environment size */
788 EnvSize = CommandInfo.EnvLen;
789 CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
790
791 /* Repeat the request */
792 CommandInfo.VDMState |= VDM_FLAG_RETRY;
793 goto Command;
794 }
795
796 /* Shouldn't happen */
797 ASSERT(FALSE);
798 }
799
800 /* Load the executable */
801 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
802 AppName,
803 Parameters,
804 CmdLine,
805 Env);
806 if (Result == ERROR_SUCCESS)
807 {
808 /* Increment the re-entry count */
809 CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
810 GetNextVDMCommand(&CommandInfo);
811 }
812 else
813 {
814 DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
815 }
816
817 break;
818 }
819
820 /* Not handled by NTVDM */
821 default:
822 {
823 /* Wait for the process to finish executing */
824 WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
825 }
826 }
827
828 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
829
830 /* Close the handles */
831 CloseHandle(ProcessInfo.hProcess);
832 CloseHandle(ProcessInfo.hThread);
833
834 return ERROR_SUCCESS;
835 }
836 #endif
837
838 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
839 {
840 WORD i;
841 WORD McbSegment = FIRST_MCB_SEGMENT;
842 PDOS_MCB CurrentMcb;
843 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
844 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
845 #ifndef STANDALONE
846 VDM_COMMAND_INFO CommandInfo;
847 #endif
848
849 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
850 Psp,
851 ReturnCode,
852 KeepResident);
853
854 /* Check if this PSP is it's own parent */
855 if (PspBlock->ParentPsp == Psp) goto Done;
856
857 if (KeepResident == 0)
858 {
859 for (i = 0; i < PspBlock->HandleTableSize; i++)
860 {
861 /* Close the handle */
862 DosCloseHandle(i);
863 }
864 }
865
866 /* Free the memory used by the process */
867 while (TRUE)
868 {
869 /* Get a pointer to the MCB */
870 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
871
872 /* Make sure the MCB is valid */
873 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
874
875 /* Check if this block was allocated by the process */
876 if (CurrentMcb->OwnerPsp == Psp)
877 {
878 if (KeepResident)
879 {
880 /* Check if this is the PSP block and we should reduce its size */
881 if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
882 {
883 /* Reduce the size of the block */
884 DosResizeMemory(McbSegment + 1, KeepResident, NULL);
885 break;
886 }
887 }
888 else
889 {
890 /* Free this entire block */
891 DosFreeMemory(McbSegment + 1);
892 }
893 }
894
895 /* If this was the last block, quit */
896 if (CurrentMcb->BlockType == 'Z') break;
897
898 /* Update the segment and continue */
899 McbSegment += CurrentMcb->Size + 1;
900 }
901
902 Done:
903 /* Restore the interrupt vectors */
904 IntVecTable[0x22] = PspBlock->TerminateAddress;
905 IntVecTable[0x23] = PspBlock->BreakAddress;
906 IntVecTable[0x24] = PspBlock->CriticalAddress;
907
908 /* Update the current PSP */
909 if (Psp == CurrentPsp)
910 {
911 CurrentPsp = PspBlock->ParentPsp;
912 if (CurrentPsp == SYSTEM_PSP)
913 {
914 ResetEvent(VdmTaskEvent);
915 CpuUnsimulate();
916 return;
917 }
918 }
919
920 #ifndef STANDALONE
921
922 /* Decrement the re-entry count */
923 CommandInfo.TaskId = SessionId;
924 CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
925 GetNextVDMCommand(&CommandInfo);
926
927 /* Clear the structure */
928 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
929
930 /* Update the VDM state of the task */
931 CommandInfo.TaskId = SessionId;
932 CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
933 GetNextVDMCommand(&CommandInfo);
934
935 #endif
936
937 /* Save the return code - Normal termination */
938 DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
939
940 /* Restore the old stack */
941 setSS(HIWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
942 setSP(LOWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
943
944 /* Are we returning to DOS code? */
945 if (HIWORD(PspBlock->TerminateAddress) == DOS_CODE_SEGMENT)
946 {
947 /* Pop the task state */
948 DosRestoreState();
949 }
950
951 /* Return control to the parent process */
952 CpuExecute(HIWORD(PspBlock->TerminateAddress),
953 LOWORD(PspBlock->TerminateAddress));
954 }
955