2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/ntvdm.c
5 * PURPOSE: Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
16 #include "bios/bios.h"
23 /* VARIABLES ******************************************************************/
25 static HANDLE ConsoleInput
= INVALID_HANDLE_VALUE
;
26 static HANDLE ConsoleOutput
= INVALID_HANDLE_VALUE
;
27 static DWORD OrgConsoleInputMode
, OrgConsoleOutputMode
;
29 // Command line of NTVDM
34 static HMENU hConsoleMenu
= NULL
;
35 static INT VdmMenuPos
= -1;
36 static BOOLEAN ShowPointer
= FALSE
;
39 * Those menu helpers were taken from the GUI frontend in winsrv.dll
41 typedef struct _VDM_MENUITEM
44 const struct _VDM_MENUITEM
*SubMenu
;
46 } VDM_MENUITEM
, *PVDM_MENUITEM
;
48 static const VDM_MENUITEM VdmMenuItems
[] =
50 { IDS_VDM_DUMPMEM_TXT
, NULL
, ID_VDM_DUMPMEM_TXT
},
51 { IDS_VDM_DUMPMEM_BIN
, NULL
, ID_VDM_DUMPMEM_BIN
},
52 { IDS_VDM_QUIT
, NULL
, ID_VDM_QUIT
},
54 { 0, NULL
, 0 } /* End of list */
57 static const VDM_MENUITEM VdmMainMenuItems
[] =
59 { -1, NULL
, 0 }, /* Separator */
60 { IDS_HIDE_MOUSE
, NULL
, ID_SHOWHIDE_MOUSE
}, /* Hide mouse; can be renamed to Show mouse */
61 { IDS_VDM_MENU
, VdmMenuItems
, 0 }, /* ReactOS VDM Menu */
63 { 0, NULL
, 0 } /* End of list */
67 AppendMenuItems(HMENU hMenu
,
68 const VDM_MENUITEM
*Items
)
71 WCHAR szMenuString
[255];
76 if (Items
[i
].uID
!= (UINT
)-1)
78 if (LoadStringW(GetModuleHandle(NULL
),
81 ARRAYSIZE(szMenuString
)) > 0)
83 if (Items
[i
].SubMenu
!= NULL
)
85 hSubMenu
= CreatePopupMenu();
88 AppendMenuItems(hSubMenu
, Items
[i
].SubMenu
);
90 if (!AppendMenuW(hMenu
,
95 DestroyMenu(hSubMenu
);
116 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
120 VdmMenuExists(HMENU hConsoleMenu
)
123 MenuPos
= GetMenuItemCount(hConsoleMenu
);
125 /* Check for the presence of one of the VDM menu items */
126 for (i
= 0; i
<= MenuPos
; i
++)
128 if (GetMenuItemID(hConsoleMenu
, i
) == ID_SHOWHIDE_MOUSE
)
130 /* Set VdmMenuPos to the position of the existing menu */
139 CreateVdmMenu(HANDLE ConOutHandle
)
141 hConsoleMenu
= ConsoleMenuControl(ConOutHandle
,
144 if (hConsoleMenu
== NULL
) return;
146 /* Get the position where we are going to insert our menu items */
147 VdmMenuPos
= GetMenuItemCount(hConsoleMenu
);
149 /* Really add the menu if it doesn't already exist (in case eg. NTVDM crashed) */
150 if (!VdmMenuExists(hConsoleMenu
))
152 AppendMenuItems(hConsoleMenu
, VdmMainMenuItems
);
153 DrawMenuBar(GetConsoleWindow());
161 const VDM_MENUITEM
*Items
= VdmMainMenuItems
;
165 DeleteMenu(hConsoleMenu
, VdmMenuPos
, MF_BYPOSITION
);
167 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
169 DrawMenuBar(GetConsoleWindow());
172 static VOID
ShowHideMousePointer(HANDLE ConOutHandle
, BOOLEAN ShowPtr
)
174 WCHAR szMenuString
[255] = L
"";
178 /* Be sure the cursor will be shown */
179 while (ShowConsoleCursor(ConOutHandle
, TRUE
) < 0) ;
183 /* Be sure the cursor will be hidden */
184 while (ShowConsoleCursor(ConOutHandle
, FALSE
) >= 0) ;
187 if (LoadStringW(GetModuleHandle(NULL
),
188 (!ShowPtr
? IDS_SHOW_MOUSE
: IDS_HIDE_MOUSE
),
190 ARRAYSIZE(szMenuString
)) > 0)
192 ModifyMenu(hConsoleMenu
, ID_SHOWHIDE_MOUSE
,
193 MF_BYCOMMAND
, ID_SHOWHIDE_MOUSE
, szMenuString
);
197 static VOID
EnableExtraHardware(HANDLE ConsoleInput
)
201 if (GetConsoleMode(ConsoleInput
, &ConInMode
))
204 // GetNumberOfConsoleMouseButtons();
205 // GetSystemMetrics(SM_CMOUSEBUTTONS);
206 // GetSystemMetrics(SM_MOUSEPRESENT);
210 /* Support mouse input events if there is a mouse on the system */
211 ConInMode
|= ENABLE_MOUSE_INPUT
;
216 /* Do not support mouse input events if there is no mouse on the system */
217 ConInMode
&= ~ENABLE_MOUSE_INPUT
;
221 SetConsoleMode(ConsoleInput
, ConInMode
);
225 /* PUBLIC FUNCTIONS ***********************************************************/
228 DisplayMessage(IN LPCWSTR Format
, ...)
230 #ifndef WIN2K_COMPLIANT
231 WCHAR StaticBuffer
[256];
232 LPWSTR Buffer
= StaticBuffer
; // Use the static buffer by default.
234 WCHAR Buffer
[2048]; // Large enough. If not, increase it by hand.
239 va_start(Parameters
, Format
);
241 #ifndef WIN2K_COMPLIANT
243 * Retrieve the message length and if it is too long, allocate
244 * an auxiliary buffer; otherwise use the static buffer.
245 * The string is built to be NULL-terminated.
247 MsgLen
= _vscwprintf(Format
, Parameters
);
248 if (MsgLen
>= ARRAYSIZE(StaticBuffer
))
250 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, (MsgLen
+ 1) * sizeof(WCHAR
));
253 /* Allocation failed, use the static buffer and display a suitable error message */
254 Buffer
= StaticBuffer
;
255 Format
= L
"DisplayMessage()\nOriginal message is too long and allocating an auxiliary buffer failed.";
256 MsgLen
= wcslen(Format
);
260 MsgLen
= ARRAYSIZE(Buffer
) - 1;
263 RtlZeroMemory(Buffer
, (MsgLen
+ 1) * sizeof(WCHAR
));
264 _vsnwprintf(Buffer
, MsgLen
, Format
, Parameters
);
268 /* Display the message */
269 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer
);
270 MessageBoxW(NULL
, Buffer
, L
"NTVDM Subsystem", MB_OK
);
272 #ifndef WIN2K_COMPLIANT
273 /* Free the buffer if needed */
274 if (Buffer
!= StaticBuffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
279 ConsoleCleanup(VOID
);
282 VdmShutdown(BOOLEAN Immediate
)
285 * Immediate = TRUE: Immediate shutdown;
286 * FALSE: Delayed shutdown.
288 BOOLEAN MustShutdown
;
290 /* First notify DOS to see whether we can shut down now */
291 MustShutdown
= DosShutdown(Immediate
);
293 * In case we perform an immediate shutdown, or the DOS says
294 * we can shut down, do it now.
296 MustShutdown
= MustShutdown
|| Immediate
;
306 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
307 /* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
314 ConsoleCtrlHandler(DWORD ControlType
)
318 case CTRL_LAST_CLOSE_EVENT
:
320 /* Delayed shutdown */
321 DPRINT1("NTVDM delayed killing in the CTRL_LAST_CLOSE_EVENT CtrlHandler!\n");
328 /* Stop the VDM if the user logs out or closes the console */
329 DPRINT1("Killing NTVDM in the 'default' CtrlHandler!\n");
339 CreateVdmMenu(ConsoleOutput
);
343 ConsoleCleanupUI(VOID
)
345 /* Display again properly the mouse pointer */
346 if (ShowPointer
) ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
354 /* Save the original input and output console modes */
355 if (!GetConsoleMode(ConsoleInput
, &OrgConsoleInputMode
) ||
356 !GetConsoleMode(ConsoleOutput
, &OrgConsoleOutputMode
))
358 CloseHandle(ConsoleOutput
);
359 CloseHandle(ConsoleInput
);
360 wprintf(L
"FATAL: Cannot save console in/out modes\n");
364 /* Set the console input mode */
365 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
366 // upon console window events (screen buffer resize, ...).
367 SetConsoleMode(ConsoleInput
, 0 /* | ENABLE_WINDOW_INPUT */);
368 EnableExtraHardware(ConsoleInput
);
370 /* Set the console output mode */
371 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
373 /* Initialize the UI */
385 /* Restore the original input and output console modes */
386 SetConsoleMode(ConsoleOutput
, OrgConsoleOutputMode
);
387 SetConsoleMode(ConsoleInput
, OrgConsoleInputMode
);
393 /* Set the handler routine */
394 SetConsoleCtrlHandler(ConsoleCtrlHandler
, TRUE
);
396 /* Enable the CTRL_LAST_CLOSE_EVENT */
397 SetLastConsoleEventActive();
400 * NOTE: The CONIN$ and CONOUT$ "virtual" files
401 * always point to non-redirected console handles.
404 /* Get the input handle to the real console, and check for success */
405 ConsoleInput
= CreateFileW(L
"CONIN$",
406 GENERIC_READ
| GENERIC_WRITE
,
407 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
412 if (ConsoleInput
== INVALID_HANDLE_VALUE
)
414 wprintf(L
"FATAL: Cannot retrieve a handle to the console input\n");
418 /* Get the output handle to the real console, and check for success */
419 ConsoleOutput
= CreateFileW(L
"CONOUT$",
420 GENERIC_READ
| GENERIC_WRITE
,
421 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
426 if (ConsoleOutput
== INVALID_HANDLE_VALUE
)
428 CloseHandle(ConsoleInput
);
429 wprintf(L
"FATAL: Cannot retrieve a handle to the console output\n");
433 /* Effectively attach to the console */
434 return ConsoleAttach();
440 /* Detach from the console */
443 /* Close the console handles */
444 if (ConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleOutput
);
445 if (ConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleInput
);
448 VOID
MenuEventHandler(PMENU_EVENT_RECORD MenuEvent
)
450 switch (MenuEvent
->dwCommandId
)
452 case ID_SHOWHIDE_MOUSE
:
453 ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
454 ShowPointer
= !ShowPointer
;
457 case ID_VDM_DUMPMEM_TXT
:
461 case ID_VDM_DUMPMEM_BIN
:
467 // EmulatorTerminate();
469 /* Nothing runs, so exit immediately */
470 DPRINT1("Killing NTVDM via console menu!\n");
479 VOID
FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent
)
481 DPRINT1("Focus events not handled\n");
485 LoadGlobalSettings(VOID
)
487 // FIXME: These strings should be localized.
488 #define ERROR_MEMORYVDD L"Insufficient memory to load installable Virtual Device Drivers."
489 #define ERROR_REGVDD L"Virtual Device Driver format in the registry is invalid."
490 #define ERROR_LOADVDD L"An installable Virtual Device Driver failed Dll initialization."
496 LPCWSTR NTVDMKeyName
= L
"SYSTEM\\CurrentControlSet\\Control\\NTVDM";
498 /* Try to open the NTVDM registry key */
499 Error
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
504 if (Error
== ERROR_FILE_NOT_FOUND
)
506 /* If the key just doesn't exist, don't do anything else */
509 else if (Error
!= ERROR_SUCCESS
)
511 /* The key exists but there was an access error: display an error and quit */
512 DisplayMessage(ERROR_REGVDD
);
528 RegCloseKey(hNTVDMKey
);
533 wmain(INT argc
, WCHAR
*argv
[])
542 wprintf(L
"\nReactOS Virtual DOS Machine\n\n"
543 L
"Usage: NTVDM <executable> [<parameters>]\n");
549 #ifdef ADVANCED_DEBUGGING
553 printf("Waiting for debugger (10 secs)..");
557 if (IsDebuggerPresent())
564 printf("Continue\n");
568 /* Load global VDM settings */
569 LoadGlobalSettings();
571 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
573 /* Initialize the console */
576 wprintf(L
"FATAL: A problem occurred when trying to initialize the console\n");
580 /* Initialize the emulator */
581 if (!EmulatorInitialize(ConsoleInput
, ConsoleOutput
))
583 wprintf(L
"FATAL: Failed to initialize the emulator\n");
587 /* Initialize the system BIOS and option ROMs */
588 if (!BiosInitialize(NULL
, NULL
))
590 wprintf(L
"FATAL: Failed to initialize the VDM BIOS.\n");
594 /* Let's go! Start simulation */