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"
24 /* VARIABLES ******************************************************************/
26 static HANDLE ConsoleInput
= INVALID_HANDLE_VALUE
;
27 static HANDLE ConsoleOutput
= INVALID_HANDLE_VALUE
;
28 static DWORD OrgConsoleInputMode
, OrgConsoleOutputMode
;
29 static BOOLEAN AcceptCommands
= TRUE
;
30 static HANDLE CommandThread
= NULL
;
32 static HMENU hConsoleMenu
= NULL
;
33 static INT VdmMenuPos
= -1;
34 static BOOLEAN ShowPointer
= FALSE
;
40 HANDLE VdmTaskEvent
= NULL
;
43 * Those menu helpers were taken from the GUI frontend in winsrv.dll
45 typedef struct _VDM_MENUITEM
48 const struct _VDM_MENUITEM
*SubMenu
;
50 } VDM_MENUITEM
, *PVDM_MENUITEM
;
52 static const VDM_MENUITEM VdmMenuItems
[] =
54 { IDS_VDM_QUIT
, NULL
, ID_VDM_QUIT
},
56 { 0, NULL
, 0 } /* End of list */
59 static const VDM_MENUITEM VdmMainMenuItems
[] =
61 { -1, NULL
, 0 }, /* Separator */
62 { IDS_HIDE_MOUSE
, NULL
, ID_SHOWHIDE_MOUSE
}, /* Hide mouse; can be renamed to Show mouse */
63 { IDS_VDM_MENU
, VdmMenuItems
, 0 }, /* ReactOS VDM Menu */
65 { 0, NULL
, 0 } /* End of list */
69 AppendMenuItems(HMENU hMenu
,
70 const VDM_MENUITEM
*Items
)
73 WCHAR szMenuString
[255];
78 if (Items
[i
].uID
!= (UINT
)-1)
80 if (LoadStringW(GetModuleHandle(NULL
),
83 sizeof(szMenuString
) / sizeof(szMenuString
[0])) > 0)
85 if (Items
[i
].SubMenu
!= NULL
)
87 hSubMenu
= CreatePopupMenu();
90 AppendMenuItems(hSubMenu
, Items
[i
].SubMenu
);
92 if (!AppendMenuW(hMenu
,
97 DestroyMenu(hSubMenu
);
118 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
122 CreateVdmMenu(HANDLE ConOutHandle
)
124 hConsoleMenu
= ConsoleMenuControl(ConOutHandle
,
127 if (hConsoleMenu
== NULL
) return;
129 VdmMenuPos
= GetMenuItemCount(hConsoleMenu
);
130 AppendMenuItems(hConsoleMenu
, VdmMainMenuItems
);
131 DrawMenuBar(GetConsoleWindow());
138 const VDM_MENUITEM
*Items
= VdmMainMenuItems
;
142 DeleteMenu(hConsoleMenu
, VdmMenuPos
, MF_BYPOSITION
);
144 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
146 DrawMenuBar(GetConsoleWindow());
149 static VOID
ShowHideMousePointer(HANDLE ConOutHandle
, BOOLEAN ShowPtr
)
151 WCHAR szMenuString
[255] = L
"";
155 /* Be sure the cursor will be shown */
156 while (ShowConsoleCursor(ConOutHandle
, TRUE
) < 0) ;
160 /* Be sure the cursor will be hidden */
161 while (ShowConsoleCursor(ConOutHandle
, FALSE
) >= 0) ;
164 if (LoadStringW(GetModuleHandle(NULL
),
165 (!ShowPtr
? IDS_SHOW_MOUSE
: IDS_HIDE_MOUSE
),
167 sizeof(szMenuString
) / sizeof(szMenuString
[0])) > 0)
169 ModifyMenu(hConsoleMenu
, ID_SHOWHIDE_MOUSE
,
170 MF_BYCOMMAND
, ID_SHOWHIDE_MOUSE
, szMenuString
);
174 /* PUBLIC FUNCTIONS ***********************************************************/
177 DisplayMessage(LPCWSTR Format
, ...)
182 va_start(Parameters
, Format
);
183 _vsnwprintf(Buffer
, 256, Format
, Parameters
);
184 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer
);
185 MessageBoxW(NULL
, Buffer
, L
"NTVDM Subsystem", MB_OK
);
191 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 */
230 CreateVdmMenu(ConsoleOutput
);
234 ConsoleCleanupUI(VOID
)
236 /* Display again properly the mouse pointer */
237 if (ShowPointer
) ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
245 /* Save the original input and output console modes */
246 if (!GetConsoleMode(ConsoleInput
, &OrgConsoleInputMode
) ||
247 !GetConsoleMode(ConsoleOutput
, &OrgConsoleOutputMode
))
249 CloseHandle(ConsoleOutput
);
250 CloseHandle(ConsoleInput
);
251 wprintf(L
"FATAL: Cannot save console in/out modes\n");
255 /* Initialize the UI */
264 /* Restore the original input and output console modes */
265 SetConsoleMode(ConsoleOutput
, OrgConsoleOutputMode
);
266 SetConsoleMode(ConsoleInput
, OrgConsoleInputMode
);
275 /* Set the handler routine */
276 SetConsoleCtrlHandler(ConsoleCtrlHandler
, TRUE
);
278 /* Enable the CTRL_LAST_CLOSE_EVENT */
279 SetLastConsoleEventActive();
282 * NOTE: The CONIN$ and CONOUT$ "virtual" files
283 * always point to non-redirected console handles.
286 /* Get the input handle to the real console, and check for success */
287 ConsoleInput
= CreateFileW(L
"CONIN$",
288 GENERIC_READ
| GENERIC_WRITE
,
289 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
294 if (ConsoleInput
== INVALID_HANDLE_VALUE
)
296 wprintf(L
"FATAL: Cannot retrieve a handle to the console input\n");
300 /* Get the output handle to the real console, and check for success */
301 ConsoleOutput
= CreateFileW(L
"CONOUT$",
302 GENERIC_READ
| GENERIC_WRITE
,
303 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
308 if (ConsoleOutput
== INVALID_HANDLE_VALUE
)
310 CloseHandle(ConsoleInput
);
311 wprintf(L
"FATAL: Cannot retrieve a handle to the console output\n");
315 /* Effectively attach to the console */
316 return ConsoleAttach();
322 /* Detach from the console */
325 /* Close the console handles */
326 if (ConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleOutput
);
327 if (ConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleInput
);
332 PumpConsoleInput(LPVOID Parameter
)
334 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
335 INPUT_RECORD InputRecord
;
340 /* Make sure the task event is signaled */
341 WaitForSingleObject(VdmTaskEvent
, INFINITE
);
343 /* Wait for an input record */
344 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
346 DWORD LastError
= GetLastError();
347 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
353 /* Check the event type */
354 switch (InputRecord
.EventType
)
358 /* Send it to the PS/2 controller */
359 PS2Dispatch(&InputRecord
);
364 switch (InputRecord
.Event
.MenuEvent
.dwCommandId
)
366 case ID_SHOWHIDE_MOUSE
:
367 ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
368 ShowPointer
= !ShowPointer
;
394 CommandThreadProc(LPVOID Parameter
)
396 BOOLEAN First
= TRUE
;
398 VDM_COMMAND_INFO CommandInfo
;
399 CHAR CmdLine
[MAX_PATH
];
400 CHAR AppName
[MAX_PATH
];
401 CHAR PifFile
[MAX_PATH
];
402 CHAR Desktop
[MAX_PATH
];
403 CHAR Title
[MAX_PATH
];
405 PVOID Env
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, EnvSize
);
407 UNREFERENCED_PARAMETER(Parameter
);
412 /* Clear the structure */
413 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
415 /* Initialize the structure members */
416 CommandInfo
.TaskId
= SessionId
;
417 CommandInfo
.VDMState
= VDM_FLAG_DOS
;
418 CommandInfo
.CmdLine
= CmdLine
;
419 CommandInfo
.CmdLen
= sizeof(CmdLine
);
420 CommandInfo
.AppName
= AppName
;
421 CommandInfo
.AppLen
= sizeof(AppName
);
422 CommandInfo
.PifFile
= PifFile
;
423 CommandInfo
.PifLen
= sizeof(PifFile
);
424 CommandInfo
.Desktop
= Desktop
;
425 CommandInfo
.DesktopLen
= sizeof(Desktop
);
426 CommandInfo
.Title
= Title
;
427 CommandInfo
.TitleLen
= sizeof(Title
);
428 CommandInfo
.Env
= Env
;
429 CommandInfo
.EnvLen
= EnvSize
;
431 if (First
) CommandInfo
.VDMState
|= VDM_FLAG_FIRST_TASK
;
433 if (!GetNextVDMCommand(&CommandInfo
))
435 if (CommandInfo
.EnvLen
> EnvSize
)
437 /* Expand the environment size */
438 EnvSize
= CommandInfo
.EnvLen
;
439 Env
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, Env
, EnvSize
);
447 /* Start the process from the command line */
448 DPRINT1("Starting '%s' ('%s')...\n", AppName
, CmdLine
);
449 Result
= DosStartProcess(AppName
, CmdLine
, Env
);
450 if (Result
!= ERROR_SUCCESS
)
452 DisplayMessage(L
"Could not start '%S'. Error: %u", AppName
, Result
);
459 while (AcceptCommands
);
461 HeapFree(GetProcessHeap(), 0, Env
);
467 wmain(INT argc
, WCHAR
*argv
[])
472 CHAR ApplicationName
[MAX_PATH
];
473 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
477 WideCharToMultiByte(CP_ACP
, 0, argv
[1], -1, ApplicationName
, sizeof(ApplicationName
), NULL
, NULL
);
479 if (argc
>= 3) WideCharToMultiByte(CP_ACP
, 0, argv
[2], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
480 else strcpy(CommandLine
, "");
484 wprintf(L
"\nReactOS Virtual DOS Machine\n\n"
485 L
"Usage: NTVDM <executable> [<parameters>]\n");
494 /* Parse the command line arguments */
495 for (i
= 1; i
< argc
; i
++)
497 if (wcsncmp(argv
[i
], L
"-i", 2) == 0)
499 /* This is the session ID */
500 SessionId
= wcstoul(argv
[i
] + 2, &endptr
, 10);
502 /* The VDM hasn't been started from a console, so quit when the task is done */
503 AcceptCommands
= FALSE
;
509 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
511 /* Create the task event */
512 VdmTaskEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
513 ASSERT(VdmTaskEvent
!= NULL
);
515 /* Initialize the console */
518 wprintf(L
"FATAL: A problem occurred when trying to initialize the console\n");
522 /* Initialize the emulator */
523 if (!EmulatorInitialize(ConsoleInput
, ConsoleOutput
))
525 wprintf(L
"FATAL: Failed to initialize the emulator\n");
529 /* Initialize the system BIOS */
530 if (!BiosInitialize(NULL
))
532 wprintf(L
"FATAL: Failed to initialize the VDM BIOS.\n");
536 /* Initialize the VDM DOS kernel */
537 if (!DosInitialize(NULL
))
539 wprintf(L
"FATAL: Failed to initialize the VDM DOS kernel.\n");
545 /* Create the GetNextVDMCommand thread */
546 CommandThread
= CreateThread(NULL
, 0, &CommandThreadProc
, NULL
, 0, NULL
);
547 if (CommandThread
== NULL
)
549 wprintf(L
"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
553 /* Wait for the command thread to exit */
554 WaitForSingleObject(CommandThread
, INFINITE
);
556 /* Close the thread handle */
557 CloseHandle(CommandThread
);
561 /* Start the process from the command line */
562 DPRINT1("Starting '%s' ('%s')...\n", ApplicationName
, CommandLine
);
563 Result
= DosStartProcess(ApplicationName
,
565 GetEnvironmentStrings());
566 if (Result
!= ERROR_SUCCESS
)
568 DisplayMessage(L
"Could not start '%S'. Error: %u", ApplicationName
, Result
);
584 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");