2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
17 #include "hardware/ps2.h"
18 #include "hardware/vga.h"
19 #include "bios/bios.h"
25 * Activate this line if you want to run NTVDM in standalone mode with:
30 /* VARIABLES ******************************************************************/
32 static HANDLE ConsoleInput
= INVALID_HANDLE_VALUE
;
33 static HANDLE ConsoleOutput
= INVALID_HANDLE_VALUE
;
34 static DWORD OrgConsoleInputMode
, OrgConsoleOutputMode
;
35 static BOOLEAN AcceptCommands
= TRUE
;
36 static HANDLE CommandThread
= NULL
;
38 static HMENU hConsoleMenu
= NULL
;
39 static INT VdmMenuPos
= -1;
40 static BOOLEAN ShowPointer
= FALSE
;
43 HANDLE VdmTaskEvent
= NULL
;
46 * Those menu helpers were taken from the GUI frontend in winsrv.dll
48 typedef struct _VDM_MENUITEM
51 const struct _VDM_MENUITEM
*SubMenu
;
53 } VDM_MENUITEM
, *PVDM_MENUITEM
;
55 static const VDM_MENUITEM VdmMenuItems
[] =
57 { IDS_VDM_QUIT
, NULL
, ID_VDM_QUIT
},
59 { 0, NULL
, 0 } /* End of list */
62 static const VDM_MENUITEM VdmMainMenuItems
[] =
64 { -1, NULL
, 0 }, /* Separator */
65 { IDS_HIDE_MOUSE
, NULL
, ID_SHOWHIDE_MOUSE
}, /* Hide mouse; can be renamed to Show mouse */
66 { IDS_VDM_MENU
, VdmMenuItems
, 0 }, /* ReactOS VDM Menu */
68 { 0, NULL
, 0 } /* End of list */
72 AppendMenuItems(HMENU hMenu
,
73 const VDM_MENUITEM
*Items
)
76 WCHAR szMenuString
[255];
81 if (Items
[i
].uID
!= (UINT
)-1)
83 if (LoadStringW(GetModuleHandle(NULL
),
86 sizeof(szMenuString
) / sizeof(szMenuString
[0])) > 0)
88 if (Items
[i
].SubMenu
!= NULL
)
90 hSubMenu
= CreatePopupMenu();
93 AppendMenuItems(hSubMenu
, Items
[i
].SubMenu
);
95 if (!AppendMenuW(hMenu
,
100 DestroyMenu(hSubMenu
);
121 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
125 CreateVdmMenu(HANDLE ConOutHandle
)
127 hConsoleMenu
= ConsoleMenuControl(ConsoleOutput
,
130 if (hConsoleMenu
== NULL
) return;
132 VdmMenuPos
= GetMenuItemCount(hConsoleMenu
);
133 AppendMenuItems(hConsoleMenu
, VdmMainMenuItems
);
134 DrawMenuBar(GetConsoleWindow());
141 const VDM_MENUITEM
*Items
= VdmMainMenuItems
;
145 DeleteMenu(hConsoleMenu
, VdmMenuPos
, MF_BYPOSITION
);
147 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
149 DrawMenuBar(GetConsoleWindow());
152 static VOID
ShowHideMousePointer(HANDLE ConOutHandle
, BOOLEAN ShowPtr
)
154 WCHAR szMenuString
[255] = L
"";
158 /* Be sure the cursor will be shown */
159 while (ShowConsoleCursor(ConOutHandle
, TRUE
) < 0) ;
163 /* Be sure the cursor will be hidden */
164 while (ShowConsoleCursor(ConOutHandle
, FALSE
) >= 0) ;
167 if (LoadStringW(GetModuleHandle(NULL
),
168 (!ShowPtr
? IDS_SHOW_MOUSE
: IDS_HIDE_MOUSE
),
170 sizeof(szMenuString
) / sizeof(szMenuString
[0])) > 0)
172 ModifyMenu(hConsoleMenu
, ID_SHOWHIDE_MOUSE
,
173 MF_BYCOMMAND
, ID_SHOWHIDE_MOUSE
, szMenuString
);
177 /* PUBLIC FUNCTIONS ***********************************************************/
179 VOID
DisplayMessage(LPCWSTR Format
, ...)
184 va_start(Parameters
, Format
);
185 _vsnwprintf(Buffer
, 256, Format
, Parameters
);
186 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer
);
187 MessageBoxW(NULL
, Buffer
, L
"NTVDM Subsystem", MB_OK
);
191 BOOL WINAPI
ConsoleCtrlHandler(DWORD ControlType
)
196 case CTRL_BREAK_EVENT
:
199 EmulatorInterrupt(0x23);
202 case CTRL_LAST_CLOSE_EVENT
:
204 if (WaitForSingleObject(VdmTaskEvent
, 0) == WAIT_TIMEOUT
)
206 /* Exit immediately */
207 if (CommandThread
) TerminateThread(CommandThread
, 0);
212 /* Stop accepting new commands */
213 AcceptCommands
= FALSE
;
220 /* Stop the VDM if the user logs out or closes the console */
227 VOID
ConsoleInitUI(VOID
)
229 CreateVdmMenu(ConsoleOutput
);
232 VOID
ConsoleCleanupUI(VOID
)
234 /* Display again properly the mouse pointer */
235 if (ShowPointer
) ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
240 DWORD WINAPI
PumpConsoleInput(LPVOID Parameter
)
242 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
243 INPUT_RECORD InputRecord
;
248 /* Make sure the task event is signaled */
249 WaitForSingleObject(VdmTaskEvent
, INFINITE
);
251 /* Wait for an input record */
252 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
254 DWORD LastError
= GetLastError();
255 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
261 /* Check the event type */
262 switch (InputRecord
.EventType
)
266 /* Send it to the PS/2 controller */
267 PS2Dispatch(&InputRecord
);
272 switch (InputRecord
.Event
.MenuEvent
.dwCommandId
)
274 case ID_SHOWHIDE_MOUSE
:
275 ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
276 ShowPointer
= !ShowPointer
;
299 BOOL
ConsoleInit(VOID
)
301 /* Set the handler routine */
302 SetConsoleCtrlHandler(ConsoleCtrlHandler
, TRUE
);
304 /* Enable the CTRL_LAST_CLOSE_EVENT */
305 SetLastConsoleEventActive();
307 /* Get the input handle to the real console, and check for success */
308 ConsoleInput
= CreateFileW(L
"CONIN$",
309 GENERIC_READ
| GENERIC_WRITE
,
310 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
315 if (ConsoleInput
== INVALID_HANDLE_VALUE
)
317 wprintf(L
"FATAL: Cannot retrieve a handle to the console input\n");
321 /* Get the output handle to the real console, and check for success */
322 ConsoleOutput
= CreateFileW(L
"CONOUT$",
323 GENERIC_READ
| GENERIC_WRITE
,
324 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
329 if (ConsoleOutput
== INVALID_HANDLE_VALUE
)
331 CloseHandle(ConsoleInput
);
332 wprintf(L
"FATAL: Cannot retrieve a handle to the console output\n");
336 /* Save the original input and output console modes */
337 if (!GetConsoleMode(ConsoleInput
, &OrgConsoleInputMode
) ||
338 !GetConsoleMode(ConsoleOutput
, &OrgConsoleOutputMode
))
340 CloseHandle(ConsoleOutput
);
341 CloseHandle(ConsoleInput
);
342 wprintf(L
"FATAL: Cannot save console in/out modes\n");
346 /* Initialize the UI */
352 VOID
ConsoleCleanup(VOID
)
354 /* Restore the original input and output console modes */
355 SetConsoleMode(ConsoleOutput
, OrgConsoleOutputMode
);
356 SetConsoleMode(ConsoleInput
, OrgConsoleInputMode
);
361 /* Close the console handles */
362 if (ConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleOutput
);
363 if (ConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleInput
);
366 DWORD WINAPI
CommandThreadProc(LPVOID Parameter
)
368 BOOLEAN First
= TRUE
;
370 VDM_COMMAND_INFO CommandInfo
;
371 CHAR CmdLine
[MAX_PATH
];
372 CHAR AppName
[MAX_PATH
];
373 CHAR PifFile
[MAX_PATH
];
374 CHAR Desktop
[MAX_PATH
];
375 CHAR Title
[MAX_PATH
];
378 UNREFERENCED_PARAMETER(Parameter
);
382 /* Clear the structure */
383 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
385 /* Initialize the structure members */
386 CommandInfo
.TaskId
= SessionId
;
387 CommandInfo
.VDMState
= VDM_FLAG_DOS
;
388 CommandInfo
.CmdLine
= CmdLine
;
389 CommandInfo
.CmdLen
= sizeof(CmdLine
);
390 CommandInfo
.AppName
= AppName
;
391 CommandInfo
.AppLen
= sizeof(AppName
);
392 CommandInfo
.PifFile
= PifFile
;
393 CommandInfo
.PifLen
= sizeof(PifFile
);
394 CommandInfo
.Desktop
= Desktop
;
395 CommandInfo
.DesktopLen
= sizeof(Desktop
);
396 CommandInfo
.Title
= Title
;
397 CommandInfo
.TitleLen
= sizeof(Title
);
398 CommandInfo
.Env
= Env
;
399 CommandInfo
.EnvLen
= sizeof(Env
);
401 if (First
) CommandInfo
.VDMState
|= VDM_FLAG_FIRST_TASK
;
403 /* Wait for the next available VDM */
404 if (!GetNextVDMCommand(&CommandInfo
)) break;
406 /* Start the process from the command line */
407 DPRINT1("Starting '%s'...\n", AppName
);
409 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
, AppName
, CmdLine
, Env
, NULL
, NULL
);
410 if (Result
!= ERROR_SUCCESS
)
412 DisplayMessage(L
"Could not start '%S'. Error: %u", AppName
, Result
);
416 /* Attach to the console */
417 if (!First
) VgaAttachToConsole();
419 /* Perform a screen refresh */
422 /* Start simulation */
423 SetEvent(VdmTaskEvent
);
426 /* Perform another screen refresh */
429 /* Detach from the console */
430 VgaDetachFromConsole(FALSE
);
434 while (AcceptCommands
);
439 INT
wmain(INT argc
, WCHAR
*argv
[])
444 CHAR ApplicationName
[MAX_PATH
];
445 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
449 WideCharToMultiByte(CP_ACP
, 0, argv
[1], -1, ApplicationName
, sizeof(ApplicationName
), NULL
, NULL
);
451 if (argc
>= 3) WideCharToMultiByte(CP_ACP
, 0, argv
[2], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
452 else strcpy(CommandLine
, "");
456 wprintf(L
"\nReactOS Virtual DOS Machine\n\n"
457 L
"Usage: NTVDM <executable> [<parameters>]\n");
465 /* Parse the command line arguments */
466 for (i
= 1; i
< argc
; i
++)
468 if (wcsncmp(argv
[i
], L
"-i", 2) == 0)
470 /* This is the session ID */
471 SessionId
= wcstoul(argv
[i
] + 2, &endptr
, 10);
473 /* The VDM hasn't been started from a console, so quit when the task is done */
474 AcceptCommands
= FALSE
;
480 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
482 /* Create the task event */
483 VdmTaskEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
484 ASSERT(VdmTaskEvent
!= NULL
);
486 /* Initialize the console */
489 wprintf(L
"FATAL: A problem occurred when trying to initialize the console\n");
493 /* Initialize the emulator */
494 if (!EmulatorInitialize(ConsoleInput
, ConsoleOutput
))
496 wprintf(L
"FATAL: Failed to initialize the emulator\n");
500 /* Initialize the system BIOS */
501 if (!BiosInitialize(NULL
))
503 wprintf(L
"FATAL: Failed to initialize the VDM BIOS.\n");
507 /* Initialize the VDM DOS kernel */
508 if (!DosInitialize(NULL
))
510 wprintf(L
"FATAL: Failed to initialize the VDM DOS kernel.\n");
516 /* Create the GetNextVDMCommand thread */
517 CommandThread
= CreateThread(NULL
, 0, &CommandThreadProc
, NULL
, 0, NULL
);
518 if (CommandThread
== NULL
)
520 wprintf(L
"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
524 /* Wait for the command thread to exit */
525 WaitForSingleObject(CommandThread
, INFINITE
);
527 /* Close the thread handle */
528 CloseHandle(CommandThread
);
532 /* Start the process from the command line */
533 DPRINT1("Starting '%s'...\n", ApplicationName
);
535 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
538 GetEnvironmentStrings(),
541 if (Result
!= ERROR_SUCCESS
)
543 DisplayMessage(L
"Could not start '%S'. Error: %u", ApplicationName
, Result
);
547 /* Start simulation */
548 SetEvent(VdmTaskEvent
);
551 /* Perform another screen refresh */
566 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");