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
;
42 HANDLE VdmTaskEvent
= NULL
;
45 * Those menu helpers were taken from the GUI frontend in winsrv.dll
47 typedef struct _VDM_MENUITEM
50 const struct _VDM_MENUITEM
*SubMenu
;
52 } VDM_MENUITEM
, *PVDM_MENUITEM
;
54 static const VDM_MENUITEM VdmMenuItems
[] =
56 { IDS_VDM_QUIT
, NULL
, ID_VDM_QUIT
},
58 { 0, NULL
, 0 } /* End of list */
61 static const VDM_MENUITEM VdmMainMenuItems
[] =
63 { -1, NULL
, 0 }, /* Separator */
64 { IDS_HIDE_MOUSE
, NULL
, ID_SHOWHIDE_MOUSE
}, /* Hide mouse; can be renamed to Show mouse */
65 { IDS_VDM_MENU
, VdmMenuItems
, 0 }, /* ReactOS VDM Menu */
67 { 0, NULL
, 0 } /* End of list */
71 AppendMenuItems(HMENU hMenu
,
72 const VDM_MENUITEM
*Items
)
75 WCHAR szMenuString
[255];
80 if (Items
[i
].uID
!= (UINT
)-1)
82 if (LoadStringW(GetModuleHandle(NULL
),
85 sizeof(szMenuString
) / sizeof(szMenuString
[0])) > 0)
87 if (Items
[i
].SubMenu
!= NULL
)
89 hSubMenu
= CreatePopupMenu();
92 AppendMenuItems(hSubMenu
, Items
[i
].SubMenu
);
94 if (!AppendMenuW(hMenu
,
99 DestroyMenu(hSubMenu
);
120 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
124 CreateVdmMenu(HANDLE ConOutHandle
)
126 hConsoleMenu
= ConsoleMenuControl(ConsoleOutput
,
129 if (hConsoleMenu
== NULL
) return;
131 VdmMenuPos
= GetMenuItemCount(hConsoleMenu
);
132 AppendMenuItems(hConsoleMenu
, VdmMainMenuItems
);
133 DrawMenuBar(GetConsoleWindow());
140 const VDM_MENUITEM
*Items
= VdmMainMenuItems
;
144 DeleteMenu(hConsoleMenu
, VdmMenuPos
, MF_BYPOSITION
);
146 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
148 DrawMenuBar(GetConsoleWindow());
151 static VOID
ShowHideMousePointer(HANDLE ConOutHandle
, BOOLEAN ShowPtr
)
153 WCHAR szMenuString
[255] = L
"";
157 /* Be sure the cursor will be shown */
158 while (ShowConsoleCursor(ConOutHandle
, TRUE
) < 0) ;
162 /* Be sure the cursor will be hidden */
163 while (ShowConsoleCursor(ConOutHandle
, FALSE
) >= 0) ;
166 if (LoadStringW(GetModuleHandle(NULL
),
167 (!ShowPtr
? IDS_SHOW_MOUSE
: IDS_HIDE_MOUSE
),
169 sizeof(szMenuString
) / sizeof(szMenuString
[0])) > 0)
171 ModifyMenu(hConsoleMenu
, ID_SHOWHIDE_MOUSE
,
172 MF_BYCOMMAND
, ID_SHOWHIDE_MOUSE
, szMenuString
);
176 /* PUBLIC FUNCTIONS ***********************************************************/
178 VOID
DisplayMessage(LPCWSTR Format
, ...)
183 va_start(Parameters
, Format
);
184 _vsnwprintf(Buffer
, 256, Format
, Parameters
);
185 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer
);
186 MessageBoxW(NULL
, Buffer
, L
"NTVDM Subsystem", MB_OK
);
190 BOOL WINAPI
ConsoleCtrlHandler(DWORD ControlType
)
195 case CTRL_BREAK_EVENT
:
198 EmulatorInterrupt(0x23);
201 case CTRL_LAST_CLOSE_EVENT
:
203 if (WaitForSingleObject(VdmTaskEvent
, 0) == WAIT_TIMEOUT
)
205 /* Exit immediately */
206 if (CommandThread
) TerminateThread(CommandThread
, 0);
211 /* Stop accepting new commands */
212 AcceptCommands
= FALSE
;
219 /* Stop the VDM if the user logs out or closes the console */
226 VOID
ConsoleInitUI(VOID
)
228 CreateVdmMenu(ConsoleOutput
);
231 VOID
ConsoleCleanupUI(VOID
)
233 /* Display again properly the mouse pointer */
234 if (ShowPointer
) ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
239 DWORD WINAPI
PumpConsoleInput(LPVOID Parameter
)
241 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
242 INPUT_RECORD InputRecord
;
247 /* Make sure the task event is signaled */
248 WaitForSingleObject(VdmTaskEvent
, INFINITE
);
250 /* Wait for an input record */
251 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
253 DWORD LastError
= GetLastError();
254 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
260 /* Check the event type */
261 switch (InputRecord
.EventType
)
265 /* Send it to the PS/2 controller */
266 PS2Dispatch(&InputRecord
);
271 switch (InputRecord
.Event
.MenuEvent
.dwCommandId
)
273 case ID_SHOWHIDE_MOUSE
:
274 ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
275 ShowPointer
= !ShowPointer
;
298 BOOL
ConsoleInit(VOID
)
300 /* Set the handler routine */
301 SetConsoleCtrlHandler(ConsoleCtrlHandler
, TRUE
);
303 /* Enable the CTRL_LAST_CLOSE_EVENT */
304 SetLastConsoleEventActive();
306 /* Get the input handle to the real console, and check for success */
307 ConsoleInput
= CreateFileW(L
"CONIN$",
308 GENERIC_READ
| GENERIC_WRITE
,
309 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
314 if (ConsoleInput
== INVALID_HANDLE_VALUE
)
316 wprintf(L
"FATAL: Cannot retrieve a handle to the console input\n");
320 /* Get the output handle to the real console, and check for success */
321 ConsoleOutput
= CreateFileW(L
"CONOUT$",
322 GENERIC_READ
| GENERIC_WRITE
,
323 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
328 if (ConsoleOutput
== INVALID_HANDLE_VALUE
)
330 CloseHandle(ConsoleInput
);
331 wprintf(L
"FATAL: Cannot retrieve a handle to the console output\n");
335 /* Save the original input and output console modes */
336 if (!GetConsoleMode(ConsoleInput
, &OrgConsoleInputMode
) ||
337 !GetConsoleMode(ConsoleOutput
, &OrgConsoleOutputMode
))
339 CloseHandle(ConsoleOutput
);
340 CloseHandle(ConsoleInput
);
341 wprintf(L
"FATAL: Cannot save console in/out modes\n");
345 /* Initialize the UI */
351 VOID
ConsoleCleanup(VOID
)
353 /* Restore the original input and output console modes */
354 SetConsoleMode(ConsoleOutput
, OrgConsoleOutputMode
);
355 SetConsoleMode(ConsoleInput
, OrgConsoleInputMode
);
360 /* Close the console handles */
361 if (ConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleOutput
);
362 if (ConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleInput
);
365 DWORD WINAPI
CommandThreadProc(LPVOID Parameter
)
367 BOOLEAN First
= TRUE
;
369 VDM_COMMAND_INFO CommandInfo
;
370 CHAR CmdLine
[MAX_PATH
];
371 CHAR AppName
[MAX_PATH
];
372 CHAR PifFile
[MAX_PATH
];
373 CHAR Desktop
[MAX_PATH
];
374 CHAR Title
[MAX_PATH
];
377 UNREFERENCED_PARAMETER(Parameter
);
379 while (AcceptCommands
)
381 /* Clear the structure */
382 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
384 /* Initialize the structure members */
385 CommandInfo
.VDMState
= VDM_FLAG_DOS
;
386 CommandInfo
.CmdLine
= CmdLine
;
387 CommandInfo
.CmdLen
= sizeof(CmdLine
);
388 CommandInfo
.AppName
= AppName
;
389 CommandInfo
.AppLen
= sizeof(AppName
);
390 CommandInfo
.PifFile
= PifFile
;
391 CommandInfo
.PifLen
= sizeof(PifFile
);
392 CommandInfo
.Desktop
= Desktop
;
393 CommandInfo
.DesktopLen
= sizeof(Desktop
);
394 CommandInfo
.Title
= Title
;
395 CommandInfo
.TitleLen
= sizeof(Title
);
396 CommandInfo
.Env
= Env
;
397 CommandInfo
.EnvLen
= sizeof(Env
);
399 if (First
) CommandInfo
.VDMState
|= VDM_FLAG_FIRST_TASK
;
401 /* Wait for the next available VDM */
402 if (!GetNextVDMCommand(&CommandInfo
)) break;
404 /* Start the process from the command line */
405 DPRINT1("Starting '%s'...\n", AppName
);
407 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
, AppName
, CmdLine
, Env
, NULL
, NULL
);
408 if (Result
!= ERROR_SUCCESS
)
410 DisplayMessage(L
"Could not start '%S'. Error: %u", AppName
, Result
);
414 /* Attach to the console */
415 if (!First
) VgaAttachToConsole();
417 /* Perform a screen refresh */
420 /* Start simulation */
421 SetEvent(VdmTaskEvent
);
424 /* Perform another screen refresh */
427 /* Detach from the console */
428 VgaDetachFromConsole(FALSE
);
436 INT
wmain(INT argc
, WCHAR
*argv
[])
441 CHAR ApplicationName
[MAX_PATH
];
442 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
446 WideCharToMultiByte(CP_ACP
, 0, argv
[1], -1, ApplicationName
, sizeof(ApplicationName
), NULL
, NULL
);
448 if (argc
>= 3) WideCharToMultiByte(CP_ACP
, 0, argv
[2], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
449 else strcpy(CommandLine
, "");
453 wprintf(L
"\nReactOS Virtual DOS Machine\n\n"
454 L
"Usage: NTVDM <executable> [<parameters>]\n");
460 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
462 /* Create the task event */
463 VdmTaskEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
464 ASSERT(VdmTaskEvent
!= NULL
);
466 /* Initialize the console */
469 wprintf(L
"FATAL: A problem occurred when trying to initialize the console\n");
473 /* Initialize the emulator */
474 if (!EmulatorInitialize(ConsoleInput
, ConsoleOutput
))
476 wprintf(L
"FATAL: Failed to initialize the emulator\n");
480 /* Initialize the system BIOS */
481 if (!BiosInitialize(NULL
))
483 wprintf(L
"FATAL: Failed to initialize the VDM BIOS.\n");
487 /* Initialize the VDM DOS kernel */
488 if (!DosInitialize(NULL
))
490 wprintf(L
"FATAL: Failed to initialize the VDM DOS kernel.\n");
496 /* Create the GetNextVDMCommand thread */
497 CommandThread
= CreateThread(NULL
, 0, &CommandThreadProc
, NULL
, 0, NULL
);
498 if (CommandThread
== NULL
)
500 wprintf(L
"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
504 /* Wait for the command thread to exit */
505 WaitForSingleObject(CommandThread
, INFINITE
);
507 /* Close the thread handle */
508 CloseHandle(CommandThread
);
512 /* Start the process from the command line */
513 DPRINT1("Starting '%s'...\n", ApplicationName
);
515 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
518 GetEnvironmentStrings(),
521 if (Result
!= ERROR_SUCCESS
)
523 DisplayMessage(L
"Could not start '%S'. Error: %u", ApplicationName
, Result
);
527 /* Start simulation */
528 SetEvent(VdmTaskEvent
);
531 /* Perform another screen refresh */
546 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");