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
);
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();
72 State
->Flags
= getEFLAGS();
75 static inline VOID
DosRestoreState(VOID
)
77 PDOS_REGISTER_STATE State
;
79 /* Pop the state structure from the stack */
80 State
= SEG_OFF_TO_PTR(getSS(), getSP());
81 setSP(getSP() + sizeof(DOS_REGISTER_STATE
));
95 setEFLAGS(State
->Flags
);
98 static WORD
DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL
,
101 PCHAR Ptr
, DestBuffer
= NULL
;
105 /* If we have an environment strings list, compute its size */
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
;
115 /* Empty environment string */
118 /* Add the final environment block NULL-terminator */
121 /* Add the two bytes for the program name tag */
124 /* Add the string buffer size */
125 TotalSize
+= strlen(ProgramName
) + 1;
127 /* Allocate the memory for the environment block */
128 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
129 if (!DestSegment
) return 0;
131 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
133 /* If we have an environment strings list, copy it */
136 Ptr
= (PCHAR
)Environment
;
139 /* Copy the string and NULL-terminate it */
140 strcpy(DestBuffer
, Ptr
);
141 DestBuffer
+= strlen(Ptr
);
142 *(DestBuffer
++) = '\0';
144 /* Move to the next string */
145 Ptr
+= strlen(Ptr
) + 1;
150 /* Empty environment string */
151 *(DestBuffer
++) = '\0';
153 /* NULL-terminate the environment block */
154 *(DestBuffer
++) = '\0';
156 /* Store the special program name tag */
157 *(DestBuffer
++) = LOBYTE(DOS_PROGRAM_NAME_TAG
);
158 *(DestBuffer
++) = HIBYTE(DOS_PROGRAM_NAME_TAG
);
160 /* Copy the program name after the environment block */
161 strcpy(DestBuffer
, ProgramName
);
166 /* PUBLIC FUNCTIONS ***********************************************************/
168 VOID
DosClonePsp(WORD DestSegment
, WORD SourceSegment
)
170 PDOS_PSP DestPsp
= SEGMENT_TO_PSP(DestSegment
);
171 PDOS_PSP SourcePsp
= SEGMENT_TO_PSP(SourceSegment
);
172 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
174 /* Literally copy the PSP first */
175 RtlCopyMemory(DestPsp
, SourcePsp
, sizeof(DOS_PSP
));
177 /* Save the interrupt vectors */
178 DestPsp
->TerminateAddress
= IntVecTable
[0x22];
179 DestPsp
->BreakAddress
= IntVecTable
[0x23];
180 DestPsp
->CriticalAddress
= IntVecTable
[0x24];
183 DestPsp
->ParentPsp
= 0;
185 /* Set the handle table pointers to the internal handle table */
186 DestPsp
->HandleTableSize
= DEFAULT_JFT_SIZE
;
187 DestPsp
->HandleTablePtr
= MAKELONG(0x18, DestSegment
);
189 /* Copy the parent handle table without referencing the SFT */
190 RtlCopyMemory(FAR_POINTER(DestPsp
->HandleTablePtr
),
191 FAR_POINTER(SourcePsp
->HandleTablePtr
),
195 VOID
DosCreatePsp(WORD Segment
, WORD ProgramSize
)
197 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Segment
);
198 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
200 RtlZeroMemory(PspBlock
, sizeof(*PspBlock
));
202 /* Set the exit interrupt */
203 PspBlock
->Exit
[0] = 0xCD; // int 0x20
204 PspBlock
->Exit
[1] = 0x20;
206 /* Set the number of the last paragraph */
207 PspBlock
->LastParagraph
= Segment
+ ProgramSize
- 1;
209 /* Save the interrupt vectors */
210 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
211 PspBlock
->BreakAddress
= IntVecTable
[0x23];
212 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
214 /* Set the parent PSP */
215 PspBlock
->ParentPsp
= CurrentPsp
;
217 /* No environment block yet */
218 PspBlock
->EnvBlock
= 0;
220 /* Copy the parent handle table */
221 DosCopyHandleTable(PspBlock
->HandleTable
);
223 /* Set the handle table pointers to the internal handle table */
224 PspBlock
->HandleTableSize
= DEFAULT_JFT_SIZE
;
225 PspBlock
->HandleTablePtr
= MAKELONG(0x18, Segment
);
227 /* Set the DOS version */
228 PspBlock
->DosVersion
= DOS_VERSION
;
230 /* Set the far call opcodes */
231 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
232 PspBlock
->FarCall
[1] = 0x21;
233 PspBlock
->FarCall
[2] = 0xCB; // retf
236 VOID
DosSetProcessContext(WORD Segment
)
238 CurrentPsp
= Segment
;
239 DiskTransferArea
= MAKELONG(0x80, Segment
);
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
)
248 DWORD Result
= ERROR_SUCCESS
;
249 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
250 LPBYTE Address
= NULL
;
256 CHAR FullPath
[MAX_PATH
];
257 CHAR ShortFullPath
[MAX_PATH
];
259 /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
260 CHAR CmdLineBuffer
[1 + DOS_CMDLINE_LENGTH
];
262 DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
267 /* Try to get the full path to the executable */
268 if (GetFullPathNameA(ExecutablePath
, sizeof(FullPath
), FullPath
, NULL
))
270 /* Try to shorten the full path */
271 if (GetShortPathNameA(FullPath
, ShortFullPath
, sizeof(ShortFullPath
)))
273 /* Use the shortened full path from now on */
274 ExecutablePath
= ShortFullPath
;
278 /* Open a handle to the executable */
279 FileHandle
= CreateFileA(ExecutablePath
,
284 FILE_ATTRIBUTE_NORMAL
,
286 if (FileHandle
== INVALID_HANDLE_VALUE
)
288 Result
= GetLastError();
292 /* Get the file size */
293 FileSize
= GetFileSize(FileHandle
, NULL
);
295 /* Create a mapping object for the file */
296 FileMapping
= CreateFileMapping(FileHandle
,
302 if (FileMapping
== NULL
)
304 Result
= GetLastError();
308 /* Map the file into memory */
309 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
312 Result
= GetLastError();
316 if (LoadType
!= DOS_LOAD_OVERLAY
)
318 /* If an optional Win32 command line is given... */
321 /* ... convert it into DOS format */
324 PBYTE CmdLineSize
= (PBYTE
)CmdLineBuffer
;
325 LPSTR CmdLineStart
= CmdLineBuffer
+ 1;
326 LPSTR CmdLinePtr
= CmdLineStart
;
328 // For debugging purposes
329 RtlFillMemory(CmdLineBuffer
, sizeof(CmdLineBuffer
), 0xFF);
332 * Set the command line: it is either an empty command line or has
333 * the format: " foo bar ..." (with at least one leading whitespace),
334 * and is then always followed by '\r' (and optionally by '\n').
336 CmdLineLen
= (BYTE
)strlen(CommandLine
);
340 * Add the leading space if the command line is not empty
341 * and doesn't already start with some whitespace...
343 if (*CommandLine
&& *CommandLine
!= '\r' && *CommandLine
!= '\n' &&
344 *CommandLine
!= ' ' && *CommandLine
!= '\t')
350 /* Compute the number of characters we need to copy from the original command line */
351 CmdLineLen
= min(CmdLineLen
, DOS_CMDLINE_LENGTH
- *CmdLineSize
);
353 /* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
354 while (CmdLineLen
&& (CommandLine
[CmdLineLen
- 1] == '\r' || CommandLine
[CmdLineLen
- 1] == '\n'))
359 /* Finally, set everything up */
360 *CmdLineSize
+= CmdLineLen
;
361 RtlCopyMemory(CmdLinePtr
, CommandLine
, CmdLineLen
);
362 CmdLineStart
[*CmdLineSize
] = '\r';
364 /* Finally make the pointer point to the static buffer */
365 CommandLine
= CmdLineBuffer
;
370 * ... otherwise, get the one from the parameter block.
371 * Format of the command line: 1 byte for size; 127 bytes for contents.
374 CommandLine
= (LPCSTR
)FAR_POINTER(Parameters
->CommandLine
);
377 /* If no optional environment is given... */
378 if (Environment
== NULL
)
380 /* ... get the one from the parameter block */
382 Environment
= (LPCSTR
)SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
385 /* Copy the environment block to DOS memory */
386 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExecutablePath
);
389 Result
= ERROR_NOT_ENOUGH_MEMORY
;
394 /* Check if this is an EXE file or a COM file */
395 if (Address
[0] == 'M' && Address
[1] == 'Z')
398 PIMAGE_DOS_HEADER Header
;
400 PDWORD RelocationTable
;
403 BOOLEAN LoadHigh
= FALSE
;
405 /* Get the MZ header */
406 Header
= (PIMAGE_DOS_HEADER
)Address
;
408 /* Get the base size of the file, in paragraphs (rounded up) */
409 BaseSize
= ((((Header
->e_cp
- (Header
->e_cblp
!= 0)) * 512)
410 + Header
->e_cblp
+ 0x0F) >> 4) - Header
->e_cparhdr
;
412 if (LoadType
!= DOS_LOAD_OVERLAY
)
414 DWORD TotalSize
= BaseSize
;
416 /* Add the PSP size, in paragraphs */
417 TotalSize
+= sizeof(DOS_PSP
) >> 4;
419 /* Add the maximum size that should be allocated */
420 TotalSize
+= Header
->e_maxalloc
;
422 if (Header
->e_minalloc
== 0 && Header
->e_maxalloc
== 0)
424 /* This program should be loaded high */
429 /* Make sure it does not pass 0xFFFF */
430 if (TotalSize
> 0xFFFF) TotalSize
= 0xFFFF;
432 /* Try to allocate that much memory */
433 Segment
= DosAllocateMemory((WORD
)TotalSize
, &MaxAllocSize
);
437 /* Check if there's at least enough memory for the minimum size */
438 if (MaxAllocSize
< (BaseSize
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_minalloc
))
440 Result
= DosLastError
;
444 /* Allocate that minimum amount */
445 TotalSize
= MaxAllocSize
;
446 Segment
= DosAllocateMemory((WORD
)TotalSize
, NULL
);
447 ASSERT(Segment
!= 0);
450 /* The process owns its own memory */
451 DosChangeMemoryOwner(Segment
, Segment
);
452 DosChangeMemoryOwner(EnvBlock
, Segment
);
454 /* Set INT 22h to the current CS:IP */
455 ((PULONG
)BaseAddress
)[0x22] = MAKELONG(getIP(), getCS());
458 DosCreatePsp(Segment
, (WORD
)TotalSize
);
459 DosSetPspCommandLine(Segment
, CommandLine
);
460 SEGMENT_TO_PSP(Segment
)->EnvBlock
= EnvBlock
;
462 /* Calculate the segment where the program should be loaded */
463 if (!LoadHigh
) LoadSegment
= Segment
+ (sizeof(DOS_PSP
) >> 4);
464 else LoadSegment
= Segment
+ TotalSize
- BaseSize
;
466 RelocFactor
= LoadSegment
;
471 LoadSegment
= Parameters
->Overlay
.Segment
;
472 RelocFactor
= Parameters
->Overlay
.RelocationFactor
;
475 /* Copy the program to the code segment */
476 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment
, 0),
477 Address
+ (Header
->e_cparhdr
<< 4),
478 min(FileSize
- (Header
->e_cparhdr
<< 4), BaseSize
<< 4));
480 /* Get the relocation table */
481 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
483 /* Perform relocations */
484 for (i
= 0; i
< Header
->e_crlc
; i
++)
486 /* Get a pointer to the word that needs to be patched */
487 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(LoadSegment
+ HIWORD(RelocationTable
[i
]),
488 LOWORD(RelocationTable
[i
]));
490 /* Add the relocation factor to it */
491 *RelocWord
+= RelocFactor
;
494 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
496 /* Save the program state */
497 if (CurrentPsp
!= SYSTEM_PSP
)
499 /* Push the task state */
502 /* Update the last stack in the PSP */
503 SEGMENT_TO_PSP(CurrentPsp
)->LastStack
= MAKELONG(getSP(), getSS());
506 /* Set the initial segment registers */
510 /* Set the stack to the location from the header */
511 setSS(LoadSegment
+ Header
->e_ss
);
515 DosSetProcessContext(Segment
);
516 CpuExecute(LoadSegment
+ Header
->e_cs
, Header
->e_ip
);
518 else if (LoadType
== DOS_LOAD_ONLY
)
521 Parameters
->StackLocation
= MAKELONG(Header
->e_sp
, LoadSegment
+ Header
->e_ss
);
522 Parameters
->EntryPoint
= MAKELONG(Header
->e_ip
, LoadSegment
+ Header
->e_cs
);
529 if (LoadType
!= DOS_LOAD_OVERLAY
)
531 /* Find the maximum amount of memory that can be allocated */
532 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
534 /* Make sure it's enough for the whole program and the PSP */
535 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
)))
537 Result
= ERROR_NOT_ENOUGH_MEMORY
;
541 /* Allocate all of it */
542 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
545 Result
= DosLastError
;
549 /* The process owns its own memory */
550 DosChangeMemoryOwner(Segment
, Segment
);
551 DosChangeMemoryOwner(EnvBlock
, Segment
);
553 /* Set INT 22h to the current CS:IP */
554 ((PULONG
)BaseAddress
)[0x22] = MAKELONG(getIP(), getCS());
557 DosCreatePsp(Segment
, MaxAllocSize
);
558 DosSetPspCommandLine(Segment
, CommandLine
);
559 SEGMENT_TO_PSP(Segment
)->EnvBlock
= EnvBlock
;
561 /* Calculate the segment where the program should be loaded */
562 LoadSegment
= Segment
+ (sizeof(DOS_PSP
) >> 4);
567 LoadSegment
= Parameters
->Overlay
.Segment
;
570 /* Copy the program to the code segment */
571 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment
, 0),
575 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
577 /* Save the program state */
578 if (CurrentPsp
!= SYSTEM_PSP
)
580 /* Push the task state */
583 /* Update the last stack in the PSP */
584 SEGMENT_TO_PSP(CurrentPsp
)->LastStack
= MAKELONG(getSP(), getSS());
587 /* Set the initial segment registers */
591 /* Set the stack to the last word of the segment */
596 * Set the value on the stack to 0, so that a near return
597 * jumps to PSP:0000 which has the exit code.
599 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
602 DosSetProcessContext(Segment
);
603 CpuExecute(Segment
, 0x100);
605 else if (LoadType
== DOS_LOAD_ONLY
)
608 Parameters
->StackLocation
= MAKELONG(0xFFFE, Segment
);
609 Parameters
->EntryPoint
= MAKELONG(0x0100, Segment
);
614 if (Result
!= ERROR_SUCCESS
)
616 /* It was not successful, cleanup the DOS memory */
617 if (EnvBlock
) DosFreeMemory(EnvBlock
);
618 if (Segment
) DosFreeMemory(Segment
);
622 if (Address
!= NULL
) UnmapViewOfFile(Address
);
624 /* Close the file mapping object */
625 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
627 /* Close the file handle */
628 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
633 DWORD
DosStartProcess(IN LPCSTR ExecutablePath
,
634 IN LPCSTR CommandLine
,
635 IN LPCSTR Environment OPTIONAL
)
639 SIZE_T CmdLen
= strlen(CommandLine
);
640 DPRINT1("Starting '%s' ('%.*s')...\n",
642 /* Display the command line without the terminating 0d 0a (and skip the terminating NULL) */
643 CmdLen
>= 2 ? (CommandLine
[CmdLen
- 2] == '\r' ? CmdLen
- 2
648 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
654 if (Result
!= ERROR_SUCCESS
) goto Quit
;
657 /* Update console title if we run in a separate console */
659 SetConsoleTitleA(ExecutablePath
);
662 /* Attach to the console */
664 VidBiosAttachToConsole();
666 /* Start simulation */
667 SetEvent(VdmTaskEvent
);
670 /* Detach from the console */
671 VidBiosDetachFromConsole();
679 WORD
DosCreateProcess(LPCSTR ProgramName
,
680 PDOS_EXEC_PARAM_BLOCK Parameters
)
684 LPVOID Environment
= NULL
;
685 VDM_COMMAND_INFO CommandInfo
;
686 CHAR CmdLine
[MAX_PATH
]; // DOS_CMDLINE_LENGTH + 1
687 CHAR AppName
[MAX_PATH
];
688 CHAR PifFile
[MAX_PATH
];
689 CHAR Desktop
[MAX_PATH
];
690 CHAR Title
[MAX_PATH
];
695 STARTUPINFOA StartupInfo
;
696 PROCESS_INFORMATION ProcessInfo
;
698 /* Get the binary type */
699 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
701 /* Initialize Win32-VDM environment */
702 Env
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, EnvSize
);
703 if (Env
== NULL
) return GetLastError();
705 /* Did the caller specify an environment segment? */
706 if (Parameters
->Environment
)
708 /* Yes, use it instead of the parent one */
709 Environment
= SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
712 /* Set up the startup info structure */
713 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
714 StartupInfo
.cb
= sizeof(StartupInfo
);
717 * Convert the DOS command line to Win32-compatible format.
718 * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
720 CmdLineSize
= min(*(PBYTE
)FAR_POINTER(Parameters
->CommandLine
), DOS_CMDLINE_LENGTH
);
721 RtlCopyMemory(CmdLine
,
722 (LPSTR
)FAR_POINTER(Parameters
->CommandLine
) + 1,
724 /* NULL-terminate it */
725 CmdLine
[CmdLineSize
] = '\0';
727 /* Remove any trailing return carriage character and NULL-terminate the command line */
728 CmdLinePtr
= CmdLine
;
729 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r' && *CmdLinePtr
!= '\n') CmdLinePtr
++;
732 /* Create the process */
733 if (!CreateProcessA(ProgramName
,
744 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
745 return GetLastError();
748 /* Check the type of the program */
751 /* These are handled by NTVDM */
755 /* Clear the structure */
756 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
758 /* Initialize the structure members */
759 CommandInfo
.TaskId
= SessionId
;
760 CommandInfo
.VDMState
= VDM_FLAG_NESTED_TASK
| VDM_FLAG_DONT_WAIT
;
761 CommandInfo
.CmdLine
= CmdLine
;
762 CommandInfo
.CmdLen
= sizeof(CmdLine
);
763 CommandInfo
.AppName
= AppName
;
764 CommandInfo
.AppLen
= sizeof(AppName
);
765 CommandInfo
.PifFile
= PifFile
;
766 CommandInfo
.PifLen
= sizeof(PifFile
);
767 CommandInfo
.Desktop
= Desktop
;
768 CommandInfo
.DesktopLen
= sizeof(Desktop
);
769 CommandInfo
.Title
= Title
;
770 CommandInfo
.TitleLen
= sizeof(Title
);
771 CommandInfo
.Env
= Env
;
772 CommandInfo
.EnvLen
= EnvSize
;
775 /* Get the VDM command information */
776 if (!GetNextVDMCommand(&CommandInfo
))
778 if (CommandInfo
.EnvLen
> EnvSize
)
780 /* Expand the environment size */
781 EnvSize
= CommandInfo
.EnvLen
;
782 CommandInfo
.Env
= Env
= RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, Env
, EnvSize
);
784 /* Repeat the request */
785 CommandInfo
.VDMState
|= VDM_FLAG_RETRY
;
789 /* Shouldn't happen */
793 /* Load the executable */
794 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
799 if (Result
== ERROR_SUCCESS
)
801 /* Increment the re-entry count */
802 CommandInfo
.VDMState
= VDM_INC_REENTER_COUNT
;
803 GetNextVDMCommand(&CommandInfo
);
807 DisplayMessage(L
"Could not load '%S'. Error: %u", AppName
, Result
);
813 /* Not handled by NTVDM */
816 /* Wait for the process to finish executing */
817 WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
);
821 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
823 /* Close the handles */
824 CloseHandle(ProcessInfo
.hProcess
);
825 CloseHandle(ProcessInfo
.hThread
);
827 return ERROR_SUCCESS
;
831 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
, WORD KeepResident
)
834 WORD McbSegment
= FIRST_MCB_SEGMENT
;
836 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
837 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
839 VDM_COMMAND_INFO CommandInfo
;
842 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
847 /* Check if this PSP is it's own parent */
848 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
850 if (KeepResident
== 0)
852 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
854 /* Close the handle */
859 /* Free the memory used by the process */
862 /* Get a pointer to the MCB */
863 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
865 /* Make sure the MCB is valid */
866 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z') break;
868 /* Check if this block was allocated by the process */
869 if (CurrentMcb
->OwnerPsp
== Psp
)
873 /* Check if this is the PSP block and we should reduce its size */
874 if ((McbSegment
+ 1) == Psp
&& KeepResident
< CurrentMcb
->Size
)
876 /* Reduce the size of the block */
877 DosResizeMemory(McbSegment
+ 1, KeepResident
, NULL
);
883 /* Free this entire block */
884 DosFreeMemory(McbSegment
+ 1);
888 /* If this was the last block, quit */
889 if (CurrentMcb
->BlockType
== 'Z') break;
891 /* Update the segment and continue */
892 McbSegment
+= CurrentMcb
->Size
+ 1;
896 /* Restore the interrupt vectors */
897 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
898 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
899 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
901 /* Update the current PSP */
902 if (Psp
== CurrentPsp
)
904 CurrentPsp
= PspBlock
->ParentPsp
;
905 if (CurrentPsp
== SYSTEM_PSP
)
907 ResetEvent(VdmTaskEvent
);
915 /* Decrement the re-entry count */
916 CommandInfo
.TaskId
= SessionId
;
917 CommandInfo
.VDMState
= VDM_DEC_REENTER_COUNT
;
918 GetNextVDMCommand(&CommandInfo
);
920 /* Clear the structure */
921 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
923 /* Update the VDM state of the task */
924 CommandInfo
.TaskId
= SessionId
;
925 CommandInfo
.VDMState
= VDM_FLAG_DONT_WAIT
;
926 GetNextVDMCommand(&CommandInfo
);
930 /* Save the return code - Normal termination */
931 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
933 /* Restore the old stack */
934 setSS(HIWORD(SEGMENT_TO_PSP(CurrentPsp
)->LastStack
));
935 setSP(LOWORD(SEGMENT_TO_PSP(CurrentPsp
)->LastStack
));
937 /* Are we returning to DOS code? */
938 if (HIWORD(PspBlock
->TerminateAddress
) == DOS_CODE_SEGMENT
)
940 /* Pop the task state */
944 /* Return control to the parent process */
945 CpuExecute(HIWORD(PspBlock
->TerminateAddress
),
946 LOWORD(PspBlock
->TerminateAddress
));