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)
10 /* INCLUDES *******************************************************************/
25 #include "bios/bios.h"
28 #include "hardware/ps2.h"
30 /* PUBLIC VARIABLES ***********************************************************/
32 WORD CurrentPsp
= SYSTEM_PSP
;
34 /* PRIVATE FUNCTIONS **********************************************************/
36 static inline VOID
DosSetPspCommandLine(WORD Segment
, LPCSTR CommandLine
)
38 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Segment
);
41 * Copy the command line block.
42 * Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
44 PspBlock
->CommandLineSize
= min(*(PBYTE
)CommandLine
, DOS_CMDLINE_LENGTH
);
46 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, DOS_CMDLINE_LENGTH
);
49 static inline VOID
DosSaveState(VOID
)
51 PDOS_REGISTER_STATE State
;
52 WORD StackPointer
= getSP();
54 /* Allocate stack space for the registers */
55 StackPointer
-= sizeof(DOS_REGISTER_STATE
);
56 State
= SEG_OFF_TO_PTR(getSS(), StackPointer
);
59 State
->EAX
= getEAX();
60 State
->ECX
= getECX();
61 State
->EDX
= getEDX();
62 State
->EBX
= getEBX();
63 State
->ESP
= getESP();
64 State
->EBP
= getEBP();
65 State
->ESI
= getESI();
66 State
->EDI
= getEDI();
71 State
->Flags
= getEFLAGS();
74 static inline VOID
DosRestoreState(VOID
)
76 PDOS_REGISTER_STATE State
;
77 WORD StackPointer
= getSP();
79 /* SS:SP points to the stack on the last entry to INT 21h */
80 StackPointer
-= (STACK_FLAGS
+ 1) * 2; /* Interrupt parameters */
81 StackPointer
-= sizeof(DOS_REGISTER_STATE
); /* Pushed state structure */
82 State
= SEG_OFF_TO_PTR(getSS(), StackPointer
);
96 setEFLAGS(State
->Flags
);
99 static WORD
DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL
,
102 PCHAR Ptr
, DestBuffer
= NULL
;
106 /* If we have an environment strings list, compute its size */
109 /* Calculate the size of the environment block */
110 Ptr
= (PCHAR
)Environment
;
111 while (*Ptr
) Ptr
+= strlen(Ptr
) + 1;
112 TotalSize
= (ULONG_PTR
)Ptr
- (ULONG_PTR
)Environment
;
116 /* Empty environment string */
119 /* Add the final environment block NULL-terminator */
122 /* Add the two bytes for the program name tag */
125 /* Add the string buffer size */
126 TotalSize
+= strlen(ProgramName
) + 1;
128 /* Allocate the memory for the environment block */
129 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
130 if (!DestSegment
) return 0;
132 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
134 /* If we have an environment strings list, copy it */
137 Ptr
= (PCHAR
)Environment
;
140 /* Copy the string and NULL-terminate it */
141 strcpy(DestBuffer
, Ptr
);
142 DestBuffer
+= strlen(Ptr
);
143 *(DestBuffer
++) = '\0';
145 /* Move to the next string */
146 Ptr
+= strlen(Ptr
) + 1;
151 /* Empty environment string */
152 *(DestBuffer
++) = '\0';
154 /* NULL-terminate the environment block */
155 *(DestBuffer
++) = '\0';
157 /* Store the special program name tag */
158 *(DestBuffer
++) = LOBYTE(DOS_PROGRAM_NAME_TAG
);
159 *(DestBuffer
++) = HIBYTE(DOS_PROGRAM_NAME_TAG
);
161 /* Copy the program name after the environment block */
162 strcpy(DestBuffer
, ProgramName
);
167 /* PUBLIC FUNCTIONS ***********************************************************/
169 VOID
DosClonePsp(WORD DestSegment
, WORD SourceSegment
)
171 PDOS_PSP DestPsp
= SEGMENT_TO_PSP(DestSegment
);
172 PDOS_PSP SourcePsp
= SEGMENT_TO_PSP(SourceSegment
);
173 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
175 /* Literally copy the PSP first */
176 RtlCopyMemory(DestPsp
, SourcePsp
, sizeof(DOS_PSP
));
178 /* Save the interrupt vectors */
179 DestPsp
->TerminateAddress
= IntVecTable
[0x22];
180 DestPsp
->BreakAddress
= IntVecTable
[0x23];
181 DestPsp
->CriticalAddress
= IntVecTable
[0x24];
184 DestPsp
->ParentPsp
= 0;
186 /* Set the handle table pointers to the internal handle table */
187 DestPsp
->HandleTableSize
= DEFAULT_JFT_SIZE
;
188 DestPsp
->HandleTablePtr
= MAKELONG(0x18, DestSegment
);
190 /* Copy the parent handle table without referencing the SFT */
191 RtlCopyMemory(FAR_POINTER(DestPsp
->HandleTablePtr
),
192 FAR_POINTER(SourcePsp
->HandleTablePtr
),
196 VOID
DosCreatePsp(WORD Segment
, WORD ProgramSize
)
198 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Segment
);
199 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
201 RtlZeroMemory(PspBlock
, sizeof(*PspBlock
));
203 /* Set the exit interrupt */
204 PspBlock
->Exit
[0] = 0xCD; // int 0x20
205 PspBlock
->Exit
[1] = 0x20;
207 /* Set the number of the last paragraph */
208 PspBlock
->LastParagraph
= Segment
+ ProgramSize
- 1;
210 /* Save the interrupt vectors */
211 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
212 PspBlock
->BreakAddress
= IntVecTable
[0x23];
213 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
215 /* Set the parent PSP */
216 PspBlock
->ParentPsp
= CurrentPsp
;
218 /* No environment block yet */
219 PspBlock
->EnvBlock
= 0;
221 /* Copy the parent handle table */
222 DosCopyHandleTable(PspBlock
->HandleTable
);
224 /* Set the handle table pointers to the internal handle table */
225 PspBlock
->HandleTableSize
= DEFAULT_JFT_SIZE
;
226 PspBlock
->HandleTablePtr
= MAKELONG(0x18, Segment
);
228 /* Set the DOS version */
229 PspBlock
->DosVersion
= DOS_VERSION
;
231 /* Set the far call opcodes */
232 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
233 PspBlock
->FarCall
[1] = 0x21;
234 PspBlock
->FarCall
[2] = 0xCB; // retf
237 VOID
DosSetProcessContext(WORD Segment
)
239 CurrentPsp
= Segment
;
240 DiskTransferArea
= MAKELONG(0x80, Segment
);
243 DWORD
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType
,
244 IN LPCSTR ExecutablePath
,
245 IN PDOS_EXEC_PARAM_BLOCK Parameters
,
246 IN LPCSTR CommandLine OPTIONAL
,
247 IN LPCSTR Environment OPTIONAL
,
248 IN DWORD ReturnAddress OPTIONAL
)
250 DWORD Result
= ERROR_SUCCESS
;
251 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
252 LPBYTE Address
= NULL
;
258 CHAR FullPath
[MAX_PATH
];
259 CHAR ShortFullPath
[MAX_PATH
];
261 /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
262 CHAR CmdLineBuffer
[1 + DOS_CMDLINE_LENGTH
];
264 DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
270 /* Try to get the full path to the executable */
271 if (GetFullPathNameA(ExecutablePath
, sizeof(FullPath
), FullPath
, NULL
))
273 /* Try to shorten the full path */
274 if (GetShortPathNameA(FullPath
, ShortFullPath
, sizeof(ShortFullPath
)))
276 /* Use the shortened full path from now on */
277 ExecutablePath
= ShortFullPath
;
281 /* Open a handle to the executable */
282 FileHandle
= CreateFileA(ExecutablePath
,
287 FILE_ATTRIBUTE_NORMAL
,
289 if (FileHandle
== INVALID_HANDLE_VALUE
)
291 Result
= GetLastError();
295 /* Get the file size */
296 FileSize
= GetFileSize(FileHandle
, NULL
);
298 /* Create a mapping object for the file */
299 FileMapping
= CreateFileMapping(FileHandle
,
305 if (FileMapping
== NULL
)
307 Result
= GetLastError();
311 /* Map the file into memory */
312 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
315 Result
= GetLastError();
319 if (LoadType
!= DOS_LOAD_OVERLAY
)
321 /* If an optional Win32 command line is given... */
324 /* ... convert it into DOS format */
327 PBYTE CmdLineSize
= (PBYTE
)CmdLineBuffer
;
328 LPSTR CmdLineStart
= CmdLineBuffer
+ 1;
329 LPSTR CmdLinePtr
= CmdLineStart
;
331 // For debugging purposes
332 RtlFillMemory(CmdLineBuffer
, sizeof(CmdLineBuffer
), 0xFF);
335 * Set the command line: it is either an empty command line or has
336 * the format: " foo bar ..." (with at least one leading whitespace),
337 * and is then always followed by '\r' (and optionally by '\n').
339 CmdLineLen
= (BYTE
)strlen(CommandLine
);
343 * Add the leading space if the command line is not empty
344 * and doesn't already start with some whitespace...
346 if (*CommandLine
&& *CommandLine
!= '\r' && *CommandLine
!= '\n' &&
347 *CommandLine
!= ' ' && *CommandLine
!= '\t')
353 /* Compute the number of characters we need to copy from the original command line */
354 CmdLineLen
= min(CmdLineLen
, DOS_CMDLINE_LENGTH
- *CmdLineSize
);
356 /* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
357 while (CmdLineLen
&& (CommandLine
[CmdLineLen
- 1] == '\r' || CommandLine
[CmdLineLen
- 1] == '\n'))
362 /* Finally, set everything up */
363 *CmdLineSize
+= CmdLineLen
;
364 RtlCopyMemory(CmdLinePtr
, CommandLine
, CmdLineLen
);
365 CmdLineStart
[*CmdLineSize
] = '\r';
367 /* Finally make the pointer point to the static buffer */
368 CommandLine
= CmdLineBuffer
;
373 * ... otherwise, get the one from the parameter block.
374 * Format of the command line: 1 byte for size; 127 bytes for contents.
377 CommandLine
= (LPCSTR
)FAR_POINTER(Parameters
->CommandLine
);
380 /* If no optional environment is given... */
381 if (Environment
== NULL
)
383 /* ... get the one from the parameter block */
385 Environment
= (LPCSTR
)SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
388 /* Copy the environment block to DOS memory */
389 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExecutablePath
);
392 Result
= ERROR_NOT_ENOUGH_MEMORY
;
397 /* Check if this is an EXE file or a COM file */
398 if (Address
[0] == 'M' && Address
[1] == 'Z')
401 PIMAGE_DOS_HEADER Header
;
403 PDWORD RelocationTable
;
406 BOOLEAN LoadHigh
= FALSE
;
408 /* Get the MZ header */
409 Header
= (PIMAGE_DOS_HEADER
)Address
;
411 /* Get the base size of the file, in paragraphs (rounded up) */
412 BaseSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
414 if (LoadType
!= DOS_LOAD_OVERLAY
)
416 DWORD TotalSize
= BaseSize
;
418 /* Add the PSP size, in paragraphs */
419 TotalSize
+= sizeof(DOS_PSP
) >> 4;
421 /* Add the maximum size that should be allocated */
422 TotalSize
+= Header
->e_maxalloc
;
424 if (Header
->e_minalloc
== 0 && Header
->e_maxalloc
== 0)
426 /* This program should be loaded high */
431 /* Make sure it does not pass 0xFFFF */
432 if (TotalSize
> 0xFFFF) TotalSize
= 0xFFFF;
434 /* Try to allocate that much memory */
435 Segment
= DosAllocateMemory((WORD
)TotalSize
, &MaxAllocSize
);
439 /* Check if there's at least enough memory for the minimum size */
440 if (MaxAllocSize
< (BaseSize
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_minalloc
))
442 Result
= DosLastError
;
446 /* Allocate that minimum amount */
447 TotalSize
= MaxAllocSize
;
448 Segment
= DosAllocateMemory((WORD
)TotalSize
, NULL
);
449 ASSERT(Segment
!= 0);
452 /* The process owns its own memory */
453 DosChangeMemoryOwner(Segment
, Segment
);
454 DosChangeMemoryOwner(EnvBlock
, Segment
);
456 /* Set INT 22h to the return address */
457 ((PULONG
)BaseAddress
)[0x22] = ReturnAddress
;
460 DosCreatePsp(Segment
, (WORD
)TotalSize
);
461 DosSetPspCommandLine(Segment
, CommandLine
);
462 SEGMENT_TO_PSP(Segment
)->EnvBlock
= EnvBlock
;
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
;
468 RelocFactor
= LoadSegment
;
473 LoadSegment
= Parameters
->Overlay
.Segment
;
474 RelocFactor
= Parameters
->Overlay
.RelocationFactor
;
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));
482 /* Get the relocation table */
483 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
485 /* Perform relocations */
486 for (i
= 0; i
< Header
->e_crlc
; i
++)
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
]));
492 /* Add the relocation factor to it */
493 *RelocWord
+= RelocFactor
;
496 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
498 /* Save the program state */
499 if (CurrentPsp
!= SYSTEM_PSP
) DosSaveState();
501 /* Set the initial segment registers */
505 /* Set the stack to the location from the header */
506 setSS(LoadSegment
+ Header
->e_ss
);
510 DosSetProcessContext(Segment
);
511 CpuExecute(LoadSegment
+ Header
->e_cs
, Header
->e_ip
);
513 else if (LoadType
== DOS_LOAD_ONLY
)
516 Parameters
->StackLocation
= MAKELONG(Header
->e_sp
, LoadSegment
+ Header
->e_ss
);
517 Parameters
->EntryPoint
= MAKELONG(Header
->e_ip
, LoadSegment
+ Header
->e_cs
);
524 if (LoadType
!= DOS_LOAD_OVERLAY
)
526 /* Find the maximum amount of memory that can be allocated */
527 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
529 /* Make sure it's enough for the whole program and the PSP */
530 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
)))
532 Result
= ERROR_NOT_ENOUGH_MEMORY
;
536 /* Allocate all of it */
537 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
540 Result
= DosLastError
;
544 /* The process owns its own memory */
545 DosChangeMemoryOwner(Segment
, Segment
);
546 DosChangeMemoryOwner(EnvBlock
, Segment
);
548 /* Set INT 22h to the return address */
549 ((PULONG
)BaseAddress
)[0x22] = ReturnAddress
;
552 DosCreatePsp(Segment
, MaxAllocSize
);
553 DosSetPspCommandLine(Segment
, CommandLine
);
554 SEGMENT_TO_PSP(Segment
)->EnvBlock
= EnvBlock
;
556 /* Calculate the segment where the program should be loaded */
557 LoadSegment
= Segment
+ (sizeof(DOS_PSP
) >> 4);
562 LoadSegment
= Parameters
->Overlay
.Segment
;
565 /* Copy the program to the code segment */
566 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment
, 0),
570 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
572 /* Set the initial segment registers */
576 /* Set the stack to the last word of the segment */
581 * Set the value on the stack to 0, so that a near return
582 * jumps to PSP:0000 which has the exit code.
584 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
587 DosSetProcessContext(Segment
);
588 CpuExecute(Segment
, 0x100);
590 else if (LoadType
== DOS_LOAD_ONLY
)
593 Parameters
->StackLocation
= MAKELONG(0xFFFE, Segment
);
594 Parameters
->EntryPoint
= MAKELONG(0x0100, Segment
);
599 if (Result
!= ERROR_SUCCESS
)
601 /* It was not successful, cleanup the DOS memory */
602 if (EnvBlock
) DosFreeMemory(EnvBlock
);
603 if (Segment
) DosFreeMemory(Segment
);
607 if (Address
!= NULL
) UnmapViewOfFile(Address
);
609 /* Close the file mapping object */
610 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
612 /* Close the file handle */
613 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
618 DWORD
DosStartProcess(IN LPCSTR ExecutablePath
,
619 IN LPCSTR CommandLine
,
620 IN LPCSTR Environment OPTIONAL
)
623 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
625 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
632 if (Result
!= ERROR_SUCCESS
) goto Quit
;
634 /* Attach to the console */
636 VidBiosAttachToConsole();
638 // HACK: Simulate a ENTER key release scancode on the PS/2 port because
639 // some apps expect to read a key release scancode (> 0x80) when they
641 // (hbelusca 2 May 2015: I'm not sure it's really useful. See r65012)
642 // IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
643 // IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
645 /* Start simulation */
646 SetEvent(VdmTaskEvent
);
649 /* Detach from the console */
650 VidBiosDetachFromConsole();
658 WORD
DosCreateProcess(LPCSTR ProgramName
,
659 PDOS_EXEC_PARAM_BLOCK Parameters
,
664 LPVOID Environment
= NULL
;
665 VDM_COMMAND_INFO CommandInfo
;
666 CHAR CmdLine
[MAX_PATH
]; // DOS_CMDLINE_LENGTH + 1
667 CHAR AppName
[MAX_PATH
];
668 CHAR PifFile
[MAX_PATH
];
669 CHAR Desktop
[MAX_PATH
];
670 CHAR Title
[MAX_PATH
];
675 STARTUPINFOA StartupInfo
;
676 PROCESS_INFORMATION ProcessInfo
;
678 /* Get the binary type */
679 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
681 /* Initialize Win32-VDM environment */
682 Env
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, EnvSize
);
683 if (Env
== NULL
) return GetLastError();
685 /* Did the caller specify an environment segment? */
686 if (Parameters
->Environment
)
688 /* Yes, use it instead of the parent one */
689 Environment
= SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
692 /* Set up the startup info structure */
693 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
694 StartupInfo
.cb
= sizeof(StartupInfo
);
697 * Convert the DOS command line to Win32-compatible format.
698 * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
700 CmdLineSize
= min(*(PBYTE
)FAR_POINTER(Parameters
->CommandLine
), DOS_CMDLINE_LENGTH
);
701 RtlCopyMemory(CmdLine
,
702 (LPSTR
)FAR_POINTER(Parameters
->CommandLine
) + 1,
704 /* NULL-terminate it */
705 CmdLine
[CmdLineSize
] = '\0';
707 /* Remove any trailing return carriage character and NULL-terminate the command line */
708 CmdLinePtr
= CmdLine
;
709 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r' && *CmdLinePtr
!= '\n') CmdLinePtr
++;
712 /* Create the process */
713 if (!CreateProcessA(ProgramName
,
724 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
725 return GetLastError();
728 /* Check the type of the program */
731 /* These are handled by NTVDM */
735 /* Clear the structure */
736 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
738 /* Initialize the structure members */
739 CommandInfo
.TaskId
= SessionId
;
740 CommandInfo
.VDMState
= VDM_FLAG_NESTED_TASK
| VDM_FLAG_DONT_WAIT
;
741 CommandInfo
.CmdLine
= CmdLine
;
742 CommandInfo
.CmdLen
= sizeof(CmdLine
);
743 CommandInfo
.AppName
= AppName
;
744 CommandInfo
.AppLen
= sizeof(AppName
);
745 CommandInfo
.PifFile
= PifFile
;
746 CommandInfo
.PifLen
= sizeof(PifFile
);
747 CommandInfo
.Desktop
= Desktop
;
748 CommandInfo
.DesktopLen
= sizeof(Desktop
);
749 CommandInfo
.Title
= Title
;
750 CommandInfo
.TitleLen
= sizeof(Title
);
751 CommandInfo
.Env
= Env
;
752 CommandInfo
.EnvLen
= EnvSize
;
755 /* Get the VDM command information */
756 if (!GetNextVDMCommand(&CommandInfo
))
758 if (CommandInfo
.EnvLen
> EnvSize
)
760 /* Expand the environment size */
761 EnvSize
= CommandInfo
.EnvLen
;
762 CommandInfo
.Env
= Env
= RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, Env
, EnvSize
);
764 /* Repeat the request */
765 CommandInfo
.VDMState
|= VDM_FLAG_RETRY
;
769 /* Shouldn't happen */
773 /* Load the executable */
774 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
780 if (Result
== ERROR_SUCCESS
)
782 /* Increment the re-entry count */
783 CommandInfo
.VDMState
= VDM_INC_REENTER_COUNT
;
784 GetNextVDMCommand(&CommandInfo
);
788 DisplayMessage(L
"Could not load '%S'. Error: %u", AppName
, Result
);
794 /* Not handled by NTVDM */
797 /* Wait for the process to finish executing */
798 WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
);
802 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
804 /* Close the handles */
805 CloseHandle(ProcessInfo
.hProcess
);
806 CloseHandle(ProcessInfo
.hThread
);
808 return ERROR_SUCCESS
;
812 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
, WORD KeepResident
)
815 WORD McbSegment
= FIRST_MCB_SEGMENT
;
817 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
818 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
820 VDM_COMMAND_INFO CommandInfo
;
823 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
828 /* Check if this PSP is it's own parent */
829 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
831 if (KeepResident
== 0)
833 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
835 /* Close the handle */
840 /* Free the memory used by the process */
843 /* Get a pointer to the MCB */
844 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
846 /* Make sure the MCB is valid */
847 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z') break;
849 /* Check if this block was allocated by the process */
850 if (CurrentMcb
->OwnerPsp
== Psp
)
854 /* Check if this is the PSP block and we should reduce its size */
855 if ((McbSegment
+ 1) == Psp
&& KeepResident
< CurrentMcb
->Size
)
857 /* Reduce the size of the block */
858 DosResizeMemory(McbSegment
+ 1, KeepResident
, NULL
);
864 /* Free this entire block */
865 DosFreeMemory(McbSegment
+ 1);
869 /* If this was the last block, quit */
870 if (CurrentMcb
->BlockType
== 'Z') break;
872 /* Update the segment and continue */
873 McbSegment
+= CurrentMcb
->Size
+ 1;
877 /* Restore the interrupt vectors */
878 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
879 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
880 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
882 /* Update the current PSP */
883 if (Psp
== CurrentPsp
)
885 CurrentPsp
= PspBlock
->ParentPsp
;
886 if (CurrentPsp
== SYSTEM_PSP
)
888 ResetEvent(VdmTaskEvent
);
896 /* Decrement the re-entry count */
897 CommandInfo
.TaskId
= SessionId
;
898 CommandInfo
.VDMState
= VDM_DEC_REENTER_COUNT
;
899 GetNextVDMCommand(&CommandInfo
);
901 /* Clear the structure */
902 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
904 /* Update the VDM state of the task */
905 CommandInfo
.TaskId
= SessionId
;
906 CommandInfo
.VDMState
= VDM_FLAG_DONT_WAIT
;
907 GetNextVDMCommand(&CommandInfo
);
911 /* Save the return code - Normal termination */
912 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
914 /* Restore the old stack */
915 setSS(HIWORD(SEGMENT_TO_PSP(CurrentPsp
)->LastStack
));
916 setSP(LOWORD(SEGMENT_TO_PSP(CurrentPsp
)->LastStack
));
918 /* Restore the program state */
921 /* Return control to the parent process */
922 CpuExecute(HIWORD(PspBlock
->TerminateAddress
),
923 LOWORD(PspBlock
->TerminateAddress
));