2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Driver DLL
4 * FILE: win32ss/user/winsrv/consrv/condrv/console.c
5 * PURPOSE: Console Management Functions
6 * PROGRAMMERS: Gé van Geldorp
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
11 /* INCLUDES *******************************************************************/
21 /* GLOBALS ********************************************************************/
23 static ULONG CurrentConsoleID
= 0;
25 /* Linked list of consoles */
26 static LIST_ENTRY ConsoleList
;
27 static RTL_RESOURCE ListLock
;
29 #define ConDrvLockConsoleListExclusive() \
30 RtlAcquireResourceExclusive(&ListLock, TRUE)
32 #define ConDrvLockConsoleListShared() \
33 RtlAcquireResourceShared(&ListLock, TRUE)
35 #define ConDrvUnlockConsoleList() \
36 RtlReleaseResource(&ListLock)
40 InsertConsole(IN PCONSOLE Console
)
44 /* All went right, so add the console to the list */
45 ConDrvLockConsoleListExclusive();
47 DPRINT1("Insert in the list\n");
48 InsertTailList(&ConsoleList
, &Console
->ListEntry
);
50 // FIXME: Move this code to the caller function!!
51 /* Get a new console ID */
52 _InterlockedExchange((PLONG
)&Console
->ConsoleID
, CurrentConsoleID
);
53 _InterlockedIncrement((PLONG
)&CurrentConsoleID
);
55 /* Unlock the console list and return success */
56 ConDrvUnlockConsoleList();
57 return STATUS_SUCCESS
;
61 RemoveConsole(IN PCONSOLE Console
)
64 if (!Console
) return STATUS_INVALID_PARAMETER
;
66 /* Remove the console from the list */
67 ConDrvLockConsoleListExclusive();
69 RemoveEntryList(&Console
->ListEntry
);
71 /* Unlock the console list and return success */
72 ConDrvUnlockConsoleList();
73 return STATUS_SUCCESS
;
77 /* PRIVATE FUNCTIONS **********************************************************/
79 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
81 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest
,
84 SIZE_T Size
= (wcslen(Source
) + 1) * sizeof(WCHAR
);
85 if (Size
> MAXUSHORT
) return FALSE
;
87 UniDest
->Buffer
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, Size
);
88 if (UniDest
->Buffer
== NULL
) return FALSE
;
90 RtlCopyMemory(UniDest
->Buffer
, Source
, Size
);
91 UniDest
->MaximumLength
= (USHORT
)Size
;
92 UniDest
->Length
= (USHORT
)Size
- sizeof(WCHAR
);
97 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
99 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString
)
101 if (UnicodeString
->Buffer
)
103 ConsoleFreeHeap(UnicodeString
->Buffer
);
104 RtlZeroMemory(UnicodeString
, sizeof(UNICODE_STRING
));
109 ConDrvPause(PCONSOLE Console
)
111 if (!Console
->UnpauseEvent
)
112 Console
->UnpauseEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
116 ConDrvUnpause(PCONSOLE Console
)
118 if (Console
->UnpauseEvent
)
120 SetEvent(Console
->UnpauseEvent
);
121 CloseHandle(Console
->UnpauseEvent
);
122 Console
->UnpauseEvent
= NULL
;
128 * Console accessibility check helpers
132 ConDrvValidateConsoleState(IN PCONSOLE Console
,
133 IN CONSOLE_STATE ExpectedState
)
135 // if (!Console) return FALSE;
137 /* The console must be locked */
138 // ASSERT(Console_locked);
140 return (Console
->State
== ExpectedState
);
144 ConDrvValidateConsoleUnsafe(IN PCONSOLE Console
,
145 IN CONSOLE_STATE ExpectedState
,
146 IN BOOLEAN LockConsole
)
148 if (!Console
) return FALSE
;
151 * Lock the console to forbid possible console's state changes
152 * (which must be done when the console is already locked).
153 * If we don't want to lock it, it's because the lock is already
154 * held. So there must be no problems.
156 if (LockConsole
) EnterCriticalSection(&Console
->Lock
);
158 // ASSERT(Console_locked);
160 /* Check whether the console's state is what we expect */
161 if (!ConDrvValidateConsoleState(Console
, ExpectedState
))
163 if (LockConsole
) LeaveCriticalSection(&Console
->Lock
);
171 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
174 ConDrvInitConsoleSupport(VOID
)
176 DPRINT("CONSRV: ConDrvInitConsoleSupport()\n");
178 /* Initialize the console list and its lock */
179 InitializeListHead(&ConsoleList
);
180 RtlInitializeResource(&ListLock
);
182 /* Should call LoadKeyboardLayout */
185 /* For resetting the terminal - defined in dummyterm.c */
186 VOID
ResetTerminal(IN PCONSOLE Console
);
189 ConDrvInitConsole(OUT PCONSOLE
* NewConsole
,
190 IN PCONSOLE_INFO ConsoleInfo
)
193 SECURITY_ATTRIBUTES SecurityAttributes
;
194 // CONSOLE_INFO CapturedConsoleInfo;
195 TEXTMODE_BUFFER_INFO ScreenBufferInfo
;
197 PCONSOLE_SCREEN_BUFFER NewBuffer
;
199 WCHAR DefaultTitle
[128];
202 if (NewConsole
== NULL
|| ConsoleInfo
== NULL
)
203 return STATUS_INVALID_PARAMETER
;
208 * Allocate a new console
210 Console
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, sizeof(*Console
));
213 DPRINT1("Not enough memory for console creation.\n");
214 return STATUS_NO_MEMORY
;
218 * Fix the screen buffer size if needed. The rule is:
219 * ScreenBufferSize >= ConsoleSize
221 if (ConsoleInfo
->ScreenBufferSize
.X
< ConsoleInfo
->ConsoleSize
.X
)
222 ConsoleInfo
->ScreenBufferSize
.X
= ConsoleInfo
->ConsoleSize
.X
;
223 if (ConsoleInfo
->ScreenBufferSize
.Y
< ConsoleInfo
->ConsoleSize
.Y
)
224 ConsoleInfo
->ScreenBufferSize
.Y
= ConsoleInfo
->ConsoleSize
.Y
;
227 * Initialize the console
229 Console
->State
= CONSOLE_INITIALIZING
;
230 Console
->ReferenceCount
= 0;
231 InitializeCriticalSection(&Console
->Lock
);
233 /* Initialize the terminal interface */
234 ResetTerminal(Console
);
236 Console
->ConsoleSize
= ConsoleInfo
->ConsoleSize
;
237 Console
->FixedSize
= FALSE
; // Value by default; is reseted by the terminals if needed.
240 * Initialize the input buffer
242 ConSrvInitObject(&Console
->InputBuffer
.Header
, INPUT_BUFFER
, Console
);
244 SecurityAttributes
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
245 SecurityAttributes
.lpSecurityDescriptor
= NULL
;
246 SecurityAttributes
.bInheritHandle
= TRUE
;
247 Console
->InputBuffer
.ActiveEvent
= CreateEventW(&SecurityAttributes
, TRUE
, FALSE
, NULL
);
248 if (NULL
== Console
->InputBuffer
.ActiveEvent
)
250 DeleteCriticalSection(&Console
->Lock
);
251 ConsoleFreeHeap(Console
);
252 return STATUS_UNSUCCESSFUL
;
255 Console
->InputBuffer
.InputBufferSize
= 0; // FIXME!
256 InitializeListHead(&Console
->InputBuffer
.InputEvents
);
257 Console
->InputBuffer
.Mode
= ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
|
258 ENABLE_ECHO_INPUT
| ENABLE_MOUSE_INPUT
;
260 Console
->InsertMode
= ConsoleInfo
->InsertMode
;
261 Console
->LineBuffer
= NULL
;
262 Console
->LinePos
= Console
->LineMaxSize
= Console
->LineSize
= 0;
263 Console
->LineComplete
= Console
->LineUpPressed
= FALSE
;
264 Console
->LineInsertToggle
= Console
->InsertMode
;
267 /* Set-up the code page */
268 Console
->InputCodePage
= Console
->OutputCodePage
= ConsoleInfo
->CodePage
;
270 /* Initialize a new text-mode screen buffer with default settings */
271 ScreenBufferInfo
.ScreenBufferSize
= ConsoleInfo
->ScreenBufferSize
;
272 ScreenBufferInfo
.ScreenAttrib
= ConsoleInfo
->ScreenAttrib
;
273 ScreenBufferInfo
.PopupAttrib
= ConsoleInfo
->PopupAttrib
;
274 ScreenBufferInfo
.IsCursorVisible
= TRUE
;
275 ScreenBufferInfo
.CursorSize
= ConsoleInfo
->CursorSize
;
277 InitializeListHead(&Console
->BufferList
);
278 Status
= ConDrvCreateScreenBuffer(&NewBuffer
,
280 CONSOLE_TEXTMODE_BUFFER
,
282 if (!NT_SUCCESS(Status
))
284 DPRINT1("ConDrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status
);
285 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
286 DeleteCriticalSection(&Console
->Lock
);
287 ConsoleFreeHeap(Console
);
290 /* Make the new screen buffer active */
291 Console
->ActiveBuffer
= NewBuffer
;
292 Console
->UnpauseEvent
= NULL
;
294 /* Initialize the console title */
295 ConsoleCreateUnicodeString(&Console
->OriginalTitle
, ConsoleInfo
->ConsoleTitle
);
297 if (ConsoleInfo
.ConsoleTitle
[0] == L
'\0')
299 if (LoadStringW(ConSrvDllInstance
, IDS_CONSOLE_TITLE
, DefaultTitle
, sizeof(DefaultTitle
) / sizeof(DefaultTitle
[0])))
301 ConsoleCreateUnicodeString(&Console
->Title
, DefaultTitle
);
305 ConsoleCreateUnicodeString(&Console
->Title
, L
"ReactOS Console");
311 ConsoleCreateUnicodeString(&Console
->Title
, ConsoleInfo
->ConsoleTitle
);
316 DPRINT("Console initialized\n");
318 /* All went right, so add the console to the list */
319 Status
= InsertConsole(Console
);
320 if (!NT_SUCCESS(Status
))
323 ConDrvDeleteConsole(Console
);
327 /* The initialization is finished */
328 DPRINT("Change state\n");
329 Console
->State
= CONSOLE_RUNNING
;
331 /* Return the newly created console to the caller and a success code too */
332 *NewConsole
= Console
;
333 return STATUS_SUCCESS
;
337 ConDrvRegisterTerminal(IN PCONSOLE Console
,
338 IN PTERMINAL Terminal
)
342 if (Console
== NULL
|| Terminal
== NULL
)
343 return STATUS_INVALID_PARAMETER
;
345 /* FIXME: Lock the console before ?? */
348 * Attach the terminal to the console. Use now the TermIFace of the console,
349 * and not the user-defined temporary Terminal pointer.
351 Console
->TermIFace
= *Terminal
;
352 Console
->TermIFace
.Console
= Console
;
354 /* Initialize the terminal AFTER having attached it to the console */
355 DPRINT("Finish initialization of terminal\n");
356 Status
= Console
->TermIFace
.Vtbl
->InitTerminal(&Console
->TermIFace
, Console
);
357 if (!NT_SUCCESS(Status
))
359 DPRINT1("Terminal initialization failed, Status = 0x%08lx\n", Status
);
361 /* We failed, detach the terminal from the console */
362 Terminal
->Console
= NULL
; // For the caller
363 ResetTerminal(Console
);
368 /* Copy buffer contents to screen */
370 // ConioDrawConsole(Console);
371 DPRINT("Console drawn\n");
373 DPRINT("Terminal initialization done\n");
374 return STATUS_SUCCESS
;
378 ConDrvDeregisterTerminal(IN PCONSOLE Console
)
380 if (Console
== NULL
) return STATUS_INVALID_PARAMETER
;
382 /* FIXME: Lock the console before ?? */
384 /* Deinitialize the terminal BEFORE detaching it from the console */
385 Console
->TermIFace
.Vtbl
->DeinitTerminal(&Console
->TermIFace
/*, Console*/);
388 * Detach the terminal from the console:
389 * reinitialize the terminal interface.
391 ResetTerminal(Console
);
393 DPRINT("Terminal unregistered\n");
394 return STATUS_SUCCESS
;
398 ConDrvDeleteConsole(IN PCONSOLE Console
)
400 DPRINT("ConDrvDeleteConsole(0x%p)\n", Console
);
403 * Forbid validation of any console by other threads
404 * during the deletion of this console.
406 ConDrvLockConsoleListExclusive();
409 * If the console is already being destroyed, i.e. not running
410 * or finishing to be initialized, just return.
412 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_RUNNING
, TRUE
) &&
413 !ConDrvValidateConsoleUnsafe(Console
, CONSOLE_INITIALIZING
, TRUE
))
415 /* Unlock the console list and return */
416 ConDrvUnlockConsoleList();
421 * We are about to be destroyed. Signal it to other people
422 * so that they can terminate what they are doing, and that
423 * they cannot longer validate the console.
425 Console
->State
= CONSOLE_TERMINATING
;
428 * Allow other threads to finish their job: basically, unlock
429 * all other calls to EnterCriticalSection(&Console->Lock); by
430 * ConDrvValidateConsoleUnsafe functions so that they just see
431 * that we are not in CONSOLE_RUNNING state anymore, or unlock
432 * other concurrent calls to ConDrvDeleteConsole so that they
433 * can see that we are in fact already deleting the console.
435 LeaveCriticalSection(&Console
->Lock
);
436 ConDrvUnlockConsoleList();
438 /* FIXME: Send a terminate message to all the processes owning this console */
440 /* Cleanup the UI-oriented part */
441 DPRINT("Deregister console\n");
442 ConDrvDeregisterTerminal(Console
);
443 DPRINT("Console deregistered\n");
446 * Check that the console is in terminating state before continuing
447 * (the cleanup code must not change the state of the console...
448 * ...unless to cancel console deletion ?).
451 ConDrvLockConsoleListExclusive();
453 if (!ConDrvValidateConsoleUnsafe(Console
, CONSOLE_TERMINATING
, TRUE
))
455 ConDrvUnlockConsoleList();
459 /* We are now in destruction */
460 Console
->State
= CONSOLE_IN_DESTRUCTION
;
462 /* We really delete the console. Reset the count to be sure. */
463 Console
->ReferenceCount
= 0;
465 /* Remove the console from the list */
466 RemoveConsole(Console
);
468 /* Discard all entries in the input event queue */
469 PurgeInputBuffer(Console
);
470 if (Console
->LineBuffer
) ConsoleFreeHeap(Console
->LineBuffer
);
472 /* Delete the last screen buffer */
473 ConioDeleteScreenBuffer(Console
->ActiveBuffer
);
474 Console
->ActiveBuffer
= NULL
;
475 if (!IsListEmpty(&Console
->BufferList
))
477 DPRINT1("BUG: screen buffer list not empty\n");
481 /**/ CloseHandle(Console
->InputBuffer
.ActiveEvent
); /**/
482 if (Console
->UnpauseEvent
) CloseHandle(Console
->UnpauseEvent
);
484 ConsoleFreeUnicodeString(&Console
->OriginalTitle
);
485 ConsoleFreeUnicodeString(&Console
->Title
);
487 DPRINT("ConDrvDeleteConsole - Unlocking\n");
488 LeaveCriticalSection(&Console
->Lock
);
489 DPRINT("ConDrvDeleteConsole - Destroying lock\n");
490 DeleteCriticalSection(&Console
->Lock
);
491 DPRINT("ConDrvDeleteConsole - Lock destroyed ; freeing console\n");
493 ConsoleFreeHeap(Console
);
494 DPRINT("ConDrvDeleteConsole - Console destroyed\n");
496 /* Unlock the console list and return */
497 ConDrvUnlockConsoleList();
501 /* PUBLIC DRIVER APIS *********************************************************/
504 ConDrvGetConsoleMode(IN PCONSOLE Console
,
505 IN PCONSOLE_IO_OBJECT Object
,
506 OUT PULONG ConsoleMode
)
508 NTSTATUS Status
= STATUS_SUCCESS
;
510 if (Console
== NULL
|| Object
== NULL
|| ConsoleMode
== NULL
)
511 return STATUS_INVALID_PARAMETER
;
514 ASSERT(Console
== Object
->Console
);
516 /*** FIXME: */ *ConsoleMode
= 0; /***/
518 if (INPUT_BUFFER
== Object
->Type
)
520 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
522 *ConsoleMode
= InputBuffer
->Mode
;
524 if (Console
->QuickEdit
|| Console
->InsertMode
)
526 // Windows does this, even if it's not documented on MSDN
527 *ConsoleMode
|= ENABLE_EXTENDED_FLAGS
;
529 if (Console
->QuickEdit
) *ConsoleMode
|= ENABLE_QUICK_EDIT_MODE
;
530 if (Console
->InsertMode
) *ConsoleMode
|= ENABLE_INSERT_MODE
;
533 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
535 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
536 *ConsoleMode
= Buffer
->Mode
;
540 Status
= STATUS_INVALID_HANDLE
;
547 ConDrvSetConsoleMode(IN PCONSOLE Console
,
548 IN PCONSOLE_IO_OBJECT Object
,
549 IN ULONG ConsoleMode
)
551 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
552 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
553 #define CONSOLE_VALID_INPUT_MODES ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
554 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
556 #define CONSOLE_VALID_OUTPUT_MODES ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
558 NTSTATUS Status
= STATUS_SUCCESS
;
560 if (Console
== NULL
|| Object
== NULL
)
561 return STATUS_INVALID_PARAMETER
;
564 ASSERT(Console
== Object
->Console
);
566 if (INPUT_BUFFER
== Object
->Type
)
568 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
570 DPRINT("SetConsoleMode(Input, %d)\n", ConsoleMode
);
573 * 1. Only the presence of valid mode flags is allowed.
575 if (ConsoleMode
& ~(CONSOLE_VALID_INPUT_MODES
| CONSOLE_VALID_CONTROL_MODES
))
577 Status
= STATUS_INVALID_PARAMETER
;
582 * 2. If we use control mode flags without ENABLE_EXTENDED_FLAGS,
583 * then consider the flags invalid.
585 if ( (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) &&
586 (ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0 )
588 Status = STATUS_INVALID_PARAMETER;
594 * 3. Now we can continue.
596 if (ConsoleMode
& CONSOLE_VALID_CONTROL_MODES
)
598 Console
->QuickEdit
= !!(ConsoleMode
& ENABLE_QUICK_EDIT_MODE
);
599 Console
->InsertMode
= !!(ConsoleMode
& ENABLE_INSERT_MODE
);
601 InputBuffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_INPUT_MODES
);
603 else if (TEXTMODE_BUFFER
== Object
->Type
|| GRAPHICS_BUFFER
== Object
->Type
)
605 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
607 DPRINT("SetConsoleMode(Output, %d)\n", ConsoleMode
);
609 if (ConsoleMode
& ~CONSOLE_VALID_OUTPUT_MODES
)
611 Status
= STATUS_INVALID_PARAMETER
;
615 Buffer
->Mode
= (ConsoleMode
& CONSOLE_VALID_OUTPUT_MODES
);
620 Status
= STATUS_INVALID_HANDLE
;
628 ConDrvGetConsoleTitle(IN PCONSOLE Console
,
630 IN OUT PVOID TitleBuffer
,
631 IN OUT PULONG BufLength
)
635 if (Console
== NULL
|| TitleBuffer
== NULL
|| BufLength
== NULL
)
636 return STATUS_INVALID_PARAMETER
;
638 /* Copy title of the console to the user title buffer */
641 if (*BufLength
>= sizeof(WCHAR
))
643 Length
= min(*BufLength
- sizeof(WCHAR
), Console
->Title
.Length
);
644 RtlCopyMemory(TitleBuffer
, Console
->Title
.Buffer
, Length
);
645 ((PWCHAR
)TitleBuffer
)[Length
/ sizeof(WCHAR
)] = L
'\0';
650 *BufLength
= Console
->Title
.Length
;
655 if (*BufLength
>= sizeof(CHAR
))
657 Length
= min(*BufLength
- sizeof(CHAR
), Console
->Title
.Length
/ sizeof(WCHAR
));
658 Length
= WideCharToMultiByte(Console
->InputCodePage
, 0,
659 Console
->Title
.Buffer
, Length
,
662 ((PCHAR
)TitleBuffer
)[Length
] = '\0';
667 *BufLength
= Console
->Title
.Length
/ sizeof(WCHAR
);
671 return STATUS_SUCCESS
;
675 ConDrvSetConsoleTitle(IN PCONSOLE Console
,
677 IN PVOID TitleBuffer
,
683 if (Console
== NULL
|| TitleBuffer
== NULL
)
684 return STATUS_INVALID_PARAMETER
;
688 /* Length is in bytes */
693 /* Use the console input CP for the conversion */
694 Length
= MultiByteToWideChar(Console
->InputCodePage
, 0,
695 TitleBuffer
, BufLength
,
697 /* The returned Length was in number of wchars, convert it in bytes */
698 Length
*= sizeof(WCHAR
);
701 /* Allocate a new buffer to hold the new title (NULL-terminated) */
702 Buffer
= ConsoleAllocHeap(HEAP_ZERO_MEMORY
, Length
+ sizeof(WCHAR
));
703 if (!Buffer
) return STATUS_NO_MEMORY
;
705 /* Free the old title */
706 ConsoleFreeUnicodeString(&Console
->Title
);
708 /* Copy title to console */
709 Console
->Title
.Buffer
= Buffer
;
710 Console
->Title
.Length
= Length
;
711 Console
->Title
.MaximumLength
= Console
->Title
.Length
+ sizeof(WCHAR
);
715 RtlCopyMemory(Console
->Title
.Buffer
, TitleBuffer
, Console
->Title
.Length
);
719 MultiByteToWideChar(Console
->InputCodePage
, 0,
720 TitleBuffer
, BufLength
,
721 Console
->Title
.Buffer
,
722 Console
->Title
.Length
/ sizeof(WCHAR
));
726 Console
->Title
.Buffer
[Console
->Title
.Length
/ sizeof(WCHAR
)] = L
'\0';
728 // TermChangeTitle(Console);
729 return STATUS_SUCCESS
;
733 ConDrvGetConsoleCP(IN PCONSOLE Console
,
737 if (Console
== NULL
|| CodePage
== NULL
)
738 return STATUS_INVALID_PARAMETER
;
740 *CodePage
= (OutputCP
? Console
->OutputCodePage
: Console
->InputCodePage
);
742 return STATUS_SUCCESS
;
746 ConDrvSetConsoleCP(IN PCONSOLE Console
,
750 if (Console
== NULL
|| !IsValidCodePage(CodePage
))
751 return STATUS_INVALID_PARAMETER
;
754 Console
->OutputCodePage
= CodePage
;
756 Console
->InputCodePage
= CodePage
;
758 return STATUS_SUCCESS
;