2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/console.c
5 * PURPOSE: Console I/O functions
9 /* INCLUDES *******************************************************************/
12 #define NONAMELESSUNION
18 #include "frontends/gui/guiterm.h"
21 #include "frontends/tui/tuiterm.h"
32 /* GLOBALS ********************************************************************/
34 LIST_ENTRY ConsoleList
; /* The list of all the allocated consoles */
35 /*static*/ RTL_RESOURCE ListLock
;
38 /* PRIVATE FUNCTIONS **********************************************************/
42 DtbgIsDesktopVisible(VOID
)
44 return !((BOOL
)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE
));
49 ConSrvConsoleCtrlEventTimeout(DWORD Event
,
50 PCONSOLE_PROCESS_DATA ProcessData
,
53 ULONG Status
= ERROR_SUCCESS
;
55 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData
->Process
->ClientId
.UniqueProcess
);
57 if (ProcessData
->CtrlDispatcher
)
65 Thread
= CreateRemoteThread(ProcessData
->Process
->ProcessHandle
, NULL
, 0,
66 ProcessData
->CtrlDispatcher
,
67 UlongToPtr(Event
), 0, NULL
);
70 Status
= GetLastError();
71 DPRINT1("Failed thread creation (Error: 0x%x)\n", Status
);
75 DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData
->Process
->ClientId
.UniqueProcess
, ProcessData
->Process
);
76 WaitForSingleObject(Thread
, Timeout
);
85 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
87 Status
= RtlNtStatusToDosError(_SEH2_GetExceptionCode());
88 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = %08X\n", Status
);
97 ConSrvConsoleCtrlEvent(DWORD Event
,
98 PCONSOLE_PROCESS_DATA ProcessData
)
100 return ConSrvConsoleCtrlEventTimeout(Event
, ProcessData
, 0);
104 ConSrvConsoleProcessCtrlEvent(PCONSOLE Console
,
105 ULONG ProcessGroupId
,
108 ULONG Status
= ERROR_SUCCESS
;
109 PLIST_ENTRY current_entry
;
110 PCONSOLE_PROCESS_DATA current
;
112 /* If the console is already being destroyed, just return */
113 if (!ConSrvValidateConsole(Console
, CONSOLE_RUNNING
, FALSE
))
114 return STATUS_UNSUCCESSFUL
;
117 * Loop through the process list, from the most recent process
118 * (the active one) to the oldest one (the first created, i.e.
119 * the console leader process), and for each, send an event
120 * (new processes are inserted at the head of the console process list).
122 current_entry
= Console
->ProcessList
.Flink
;
123 while (current_entry
!= &Console
->ProcessList
)
125 current
= CONTAINING_RECORD(current_entry
, CONSOLE_PROCESS_DATA
, ConsoleLink
);
126 current_entry
= current_entry
->Flink
;
129 * Only processes belonging to the same process group are signaled.
130 * If the process group ID is zero, then all the processes are signaled.
132 if (ProcessGroupId
== 0 || current
->Process
->ProcessGroupId
== ProcessGroupId
)
134 Status
= ConSrvConsoleCtrlEvent(Event
, current
);
142 ConioPause(PCONSOLE Console
, UINT Flags
)
144 Console
->PauseFlags
|= Flags
;
145 if (!Console
->UnpauseEvent
)
146 Console
->UnpauseEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
150 ConioUnpause(PCONSOLE Console
, UINT Flags
)
152 Console
->PauseFlags
&= ~Flags
;
154 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
155 if (Console
->PauseFlags
== 0 && Console
->UnpauseEvent
)
157 SetEvent(Console
->UnpauseEvent
);
158 CloseHandle(Console
->UnpauseEvent
);
159 Console
->UnpauseEvent
= NULL
;
161 CsrNotifyWait(&Console
->WriteWaitQueue
,
165 if (!IsListEmpty(&Console
->WriteWaitQueue
))
167 CsrDereferenceWait(&Console
->WriteWaitQueue
);
173 ConSrvInitConsoleSupport(VOID
)
175 DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
177 /* Initialize the console list and its lock */
178 InitializeListHead(&ConsoleList
);
179 RtlInitializeResource(&ListLock
);
181 /* Should call LoadKeyboardLayout */
185 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo
,
186 IN OUT PCONSOLE_INFO ConsoleInfo
,
188 IN SIZE_T IconPathLength
,
191 #define PATH_SEPARATOR L'\\'
194 LPWSTR LinkName
= NULL
;
197 if ((ConsoleStartInfo
->dwStartupFlags
& STARTF_TITLEISLINKNAME
) == 0)
200 if (IconPath
== NULL
|| piIcon
== NULL
)
206 /* 1- Find the last path separator if any */
207 LinkName
= wcsrchr(ConsoleStartInfo
->ConsoleTitle
, PATH_SEPARATOR
);
208 if (LinkName
== NULL
)
210 LinkName
= ConsoleStartInfo
->ConsoleTitle
;
214 /* Skip the path separator */
218 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
219 Length
= wcslen(LinkName
);
220 if ( (Length
<= 4) || (wcsicmp(LinkName
+ (Length
- 4), L
".lnk") != 0) )
223 /* 3- It may be a link. Try to retrieve some properties */
224 HRESULT hRes
= CoInitialize(NULL
);
227 /* Get a pointer to the IShellLink interface */
228 IShellLinkW
* pshl
= NULL
;
229 hRes
= CoCreateInstance(&CLSID_ShellLink
,
231 CLSCTX_INPROC_SERVER
,
236 /* Get a pointer to the IPersistFile interface */
237 IPersistFile
* ppf
= NULL
;
238 hRes
= IPersistFile_QueryInterface(pshl
, &IID_IPersistFile
, (LPVOID
*)&ppf
);
241 /* Load the shortcut */
242 hRes
= IPersistFile_Load(ppf
, ConsoleStartInfo
->ConsoleTitle
, STGM_READ
);
246 * Finally we can get the properties !
247 * Update the old ones if needed.
252 /* Reset the name of the console with the name of the shortcut */
253 Length
= min(/*Length*/ Length
- 4, // 4 == len(".lnk")
254 sizeof(ConsoleInfo
->ConsoleTitle
) / sizeof(ConsoleInfo
->ConsoleTitle
[0]) - 1);
255 wcsncpy(ConsoleInfo
->ConsoleTitle
, LinkName
, Length
);
256 ConsoleInfo
->ConsoleTitle
[Length
] = L
'\0';
258 /* Get the window showing command */
259 hRes
= IShellLinkW_GetShowCmd(pshl
, &ShowCmd
);
260 if (SUCCEEDED(hRes
)) ConsoleStartInfo
->ShowWindow
= (WORD
)ShowCmd
;
263 // hRes = pshl->GetHotkey(&ShowCmd);
264 // if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey;
266 /* Get the icon location, if any */
267 hRes
= IShellLinkW_GetIconLocation(pshl
, IconPath
, IconPathLength
, piIcon
);
268 if (!SUCCEEDED(hRes
))
273 // FIXME: Since we still don't load console properties from the shortcut,
274 // return false. When this will be done, we will return true instead.
277 IPersistFile_Release(ppf
);
279 IShellLinkW_Release(pshl
);
288 ConSrvInitConsole(OUT PCONSOLE
* NewConsole
,
289 IN OUT PCONSOLE_START_INFO ConsoleStartInfo
,
290 IN PCSR_PROCESS ConsoleLeaderProcess
)
293 SECURITY_ATTRIBUTES SecurityAttributes
;
294 CONSOLE_INFO ConsoleInfo
;
296 DWORD ProcessId
= HandleToUlong(ConsoleLeaderProcess
->ClientId
.UniqueProcess
);
298 PCONSOLE_SCREEN_BUFFER NewBuffer
;
301 WCHAR IconPath
[MAX_PATH
+ 1] = L
"";
304 if (NewConsole
== NULL
) return STATUS_INVALID_PARAMETER
;
308 * Allocate a console structure
310 Console
= RtlAllocateHeap(ConSrvHeap
, HEAP_ZERO_MEMORY
, sizeof(CONSOLE
));
313 DPRINT1("Not enough memory for console creation.\n");
314 return STATUS_NO_MEMORY
;
318 * Load the console settings
321 /* 1. Load the default settings */
322 ConSrvGetDefaultSettings(&ConsoleInfo
, ProcessId
);
324 /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
325 Length
= min(wcslen(ConsoleStartInfo
->ConsoleTitle
),
326 sizeof(ConsoleInfo
.ConsoleTitle
) / sizeof(ConsoleInfo
.ConsoleTitle
[0]) - 1);
327 wcsncpy(ConsoleInfo
.ConsoleTitle
, ConsoleStartInfo
->ConsoleTitle
, Length
);
328 ConsoleInfo
.ConsoleTitle
[Length
] = L
'\0';
331 * 3. Check whether the process creating the console was launched
332 * via a shell-link. ConsoleInfo.ConsoleTitle may be updated by
333 * the name of the shortcut.
335 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_TITLEISLINKNAME
)
337 if (!LoadShellLinkConsoleInfo(ConsoleStartInfo
,
343 ConsoleStartInfo
->dwStartupFlags
&= ~STARTF_TITLEISLINKNAME
;
348 * 4. Load the remaining console settings via the registry.
350 if ((ConsoleStartInfo
->dwStartupFlags
& STARTF_TITLEISLINKNAME
) == 0)
353 * Either we weren't created by an app launched via a shell-link,
354 * or we failed to load shell-link console properties.
355 * Therefore, load the console infos for the application from the registry.
357 ConSrvReadUserSettings(&ConsoleInfo
, ProcessId
);
360 * Now, update them with the properties the user might gave to us
361 * via the STARTUPINFO structure before calling CreateProcess
362 * (and which was transmitted via the ConsoleStartInfo structure).
363 * We therefore overwrite the values read in the registry.
365 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USEFILLATTRIBUTE
)
367 ConsoleInfo
.ScreenAttrib
= ConsoleStartInfo
->FillAttribute
;
369 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USECOUNTCHARS
)
371 ConsoleInfo
.ScreenBufferSize
= ConsoleStartInfo
->ScreenBufferSize
;
373 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USESIZE
)
375 // ConsoleInfo->ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
376 ConsoleInfo
.ConsoleSize
.X
= (SHORT
)ConsoleStartInfo
->ConsoleWindowSize
.cx
;
377 ConsoleInfo
.ConsoleSize
.Y
= (SHORT
)ConsoleStartInfo
->ConsoleWindowSize
.cy
;
380 if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
387 * Initialize the console
389 Console
->State
= CONSOLE_INITIALIZING
;
390 InitializeCriticalSection(&Console
->Lock
);
391 Console
->ReferenceCount
= 0;
392 InitializeListHead(&Console
->ProcessList
);
393 memcpy(Console
->Colors
, ConsoleInfo
.Colors
, sizeof(ConsoleInfo
.Colors
));
394 Console
->ConsoleSize
= ConsoleInfo
.ConsoleSize
;
397 * Initialize the input buffer
399 Console
->InputBuffer
.Header
.Type
= CONIO_INPUT_BUFFER_MAGIC
;
400 Console
->InputBuffer
.Header
.Console
= Console
;
402 SecurityAttributes
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
403 SecurityAttributes
.lpSecurityDescriptor
= NULL
;
404 SecurityAttributes
.bInheritHandle
= TRUE
;
405 Console
->InputBuffer
.ActiveEvent
= CreateEventW(&SecurityAttributes
, TRUE
, FALSE
, NULL
);
406 if (NULL
== Console
->InputBuffer
.ActiveEvent
)
408 DeleteCriticalSection(&Console
->Lock
);
409 RtlFreeHeap(ConSrvHeap
, 0, Console
);
410 return STATUS_UNSUCCESSFUL
;
413 // TODO: Use the values from ConsoleInfo.
414 Console
->InputBuffer
.Mode
= ENABLE_LINE_INPUT
| ENABLE_ECHO_INPUT
|
415 ENABLE_PROCESSED_INPUT
| ENABLE_MOUSE_INPUT
;
416 Console
->QuickEdit
= ConsoleInfo
.QuickEdit
;
417 Console
->InsertMode
= ConsoleInfo
.InsertMode
;
418 InitializeListHead(&Console
->InputBuffer
.ReadWaitQueue
);
419 InitializeListHead(&Console
->InputBuffer
.InputEvents
);
420 Console
->LineBuffer
= NULL
;
421 Console
->CodePage
= GetOEMCP();
422 Console
->OutputCodePage
= GetOEMCP();
424 /* Initialize a new screen buffer with default settings */
425 InitializeListHead(&Console
->BufferList
);
426 Status
= ConSrvCreateScreenBuffer(Console
,
428 ConsoleInfo
.ScreenBufferSize
,
429 ConsoleInfo
.ScreenAttrib
,
430 ConsoleInfo
.PopupAttrib
,
432 ConsoleInfo
.CursorSize
);
433 if (!NT_SUCCESS(Status
))
435 DPRINT1("ConSrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status
);
436 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
437 DeleteCriticalSection(&Console
->Lock
);
438 RtlFreeHeap(ConSrvHeap
, 0, Console
);
441 /* Make the new screen buffer active */
442 Console
->ActiveBuffer
= NewBuffer
;
443 Console
->FullScreen
= ConsoleInfo
.FullScreen
;
444 InitializeListHead(&Console
->WriteWaitQueue
);
447 * Initialize the history buffers
449 InitializeListHead(&Console
->HistoryBuffers
);
450 Console
->HistoryBufferSize
= ConsoleInfo
.HistoryBufferSize
;
451 Console
->NumberOfHistoryBuffers
= ConsoleInfo
.NumberOfHistoryBuffers
;
452 Console
->HistoryNoDup
= ConsoleInfo
.HistoryNoDup
;
454 /* Initialize the console title */
455 RtlCreateUnicodeString(&Console
->OriginalTitle
, ConsoleInfo
.ConsoleTitle
);
456 if (ConsoleInfo
.ConsoleTitle
[0] == L
'\0')
458 if (LoadStringW(ConSrvDllInstance
, IDS_CONSOLE_TITLE
, Title
, sizeof(Title
) / sizeof(Title
[0])))
460 RtlCreateUnicodeString(&Console
->Title
, Title
);
464 RtlCreateUnicodeString(&Console
->Title
, L
"ReactOS Console");
469 RtlCreateUnicodeString(&Console
->Title
, ConsoleInfo
.ConsoleTitle
);
472 /* Lock the console until its initialization is finished */
473 // EnterCriticalSection(&Console->Lock);
476 * If we are not in GUI-mode, start the text-mode terminal emulator.
477 * If we fail, try to start the GUI-mode terminal emulator.
480 GuiMode
= DtbgIsDesktopVisible();
488 DPRINT1("CONSRV: Opening text-mode terminal emulator\n");
489 Status
= TuiInitConsole(Console
,
493 if (!NT_SUCCESS(Status
))
495 DPRINT1("Failed to open text-mode terminal emulator, switching to gui-mode, Status = 0x%08lx\n", Status
);
502 * Try to open the GUI-mode terminal emulator. Two cases are possible:
503 * - We are in GUI-mode, therefore GuiMode == TRUE, the previous test-case
504 * failed and we start GUI-mode terminal emulator.
505 * - We are in text-mode, therefore GuiMode == FALSE, the previous test-case
506 * succeeded BUT we failed at starting text-mode terminal emulator.
507 * Then GuiMode was switched to TRUE in order to try to open the GUI-mode
508 * terminal emulator (Win32k will automatically switch to graphical mode,
509 * therefore no additional code is needed).
513 DPRINT1("CONSRV: Opening GUI-mode terminal emulator\n");
514 Status
= GuiInitConsole(Console
,
520 if (!NT_SUCCESS(Status
))
522 DPRINT1("GuiInitConsole: failed, Status = 0x%08lx\n", Status
);
523 RtlFreeUnicodeString(&Console
->Title
);
524 RtlFreeUnicodeString(&Console
->OriginalTitle
);
525 ConioDeleteScreenBuffer(NewBuffer
);
526 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
527 // LeaveCriticalSection(&Console->Lock);
528 DeleteCriticalSection(&Console
->Lock
);
529 RtlFreeHeap(ConSrvHeap
, 0, Console
);
534 DPRINT1("Terminal initialized\n");
536 /* All went right, so add the console to the list */
537 ConSrvLockConsoleListExclusive();
538 DPRINT1("Insert in the list\n");
539 InsertTailList(&ConsoleList
, &Console
->Entry
);
541 /* The initialization is finished */
542 DPRINT1("Change state\n");
543 Console
->State
= CONSOLE_RUNNING
;
545 /* Unlock the console */
546 // LeaveCriticalSection(&Console->Lock);
548 /* Unlock the console list */
549 ConSrvUnlockConsoleList();
551 /* Copy buffer contents to screen */
552 ConioDrawConsole(Console
);
553 DPRINT1("Console drawn\n");
555 /* Return the newly created console to the caller and a success code too */
556 *NewConsole
= Console
;
557 return STATUS_SUCCESS
;
561 ConSrvDeleteConsole(PCONSOLE Console
)
563 PLIST_ENTRY CurrentEntry
;
566 DPRINT1("ConSrvDeleteConsole\n");
569 * Forbid validation of any console by other threads
570 * during the deletion of this console.
572 ConSrvLockConsoleListExclusive();
574 /* Check the existence of the console, and if it's ok, continue */
575 if (!ConSrvValidatePointer(Console
))
577 /* Unlock the console list and return */
578 ConSrvUnlockConsoleList();
583 * If the console is already being destroyed
584 * (thus not running), just return.
586 if (!ConSrvValidateConsoleUnsafe(Console
, CONSOLE_RUNNING
, TRUE
))
588 /* Unlock the console list and return */
589 ConSrvUnlockConsoleList();
594 * We are about to be destroyed. Signal it to other people
595 * so that they can terminate what they are doing, and that
596 * they cannot longer validate the console.
598 Console
->State
= CONSOLE_TERMINATING
;
601 * Allow other threads to finish their job: basically, unlock
602 * all other calls to EnterCriticalSection(&Console->Lock); by
603 * ConSrvValidateConsole(Unsafe) functions so that they just see
604 * that we are not in CONSOLE_RUNNING state anymore, or unlock
605 * other concurrent calls to ConSrvDeleteConsole so that they
606 * can see that we are in fact already deleting the console.
608 LeaveCriticalSection(&Console
->Lock
);
609 ConSrvUnlockConsoleList();
611 /* FIXME: Send a terminate message to all the processes owning this console */
613 /* Cleanup the UI-oriented part */
614 ConioCleanupConsole(Console
);
617 * Check that the console is in terminating state before continuing
618 * (the cleanup code must not change the state of the console...
619 * ...unless to cancel console deletion ?).
622 ConSrvLockConsoleListExclusive();
624 /* Re-check the existence of the console, and if it's ok, continue */
625 if (!ConSrvValidatePointer(Console
))
627 /* Unlock the console list and return */
628 ConSrvUnlockConsoleList();
632 if (!ConSrvValidateConsoleUnsafe(Console
, CONSOLE_TERMINATING
, TRUE
))
634 ConSrvUnlockConsoleList();
638 /* We are in destruction */
639 Console
->State
= CONSOLE_IN_DESTRUCTION
;
641 /* Remove the console from the list */
642 RemoveEntryList(&Console
->Entry
);
644 /* Reset the count to be sure */
645 Console
->ReferenceCount
= 0;
647 /* Discard all entries in the input event queue */
648 while (!IsListEmpty(&Console
->InputBuffer
.InputEvents
))
650 CurrentEntry
= RemoveHeadList(&Console
->InputBuffer
.InputEvents
);
651 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
652 RtlFreeHeap(ConSrvHeap
, 0, Event
);
655 if (Console
->LineBuffer
)
656 RtlFreeHeap(ConSrvHeap
, 0, Console
->LineBuffer
);
657 while (!IsListEmpty(&Console
->HistoryBuffers
))
658 HistoryDeleteBuffer((struct _HISTORY_BUFFER
*)Console
->HistoryBuffers
.Flink
);
660 ConioDeleteScreenBuffer(Console
->ActiveBuffer
);
661 if (!IsListEmpty(&Console
->BufferList
))
663 DPRINT1("BUG: screen buffer list not empty\n");
666 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
667 if (Console
->UnpauseEvent
) CloseHandle(Console
->UnpauseEvent
);
669 RtlFreeUnicodeString(&Console
->OriginalTitle
);
670 RtlFreeUnicodeString(&Console
->Title
);
671 IntDeleteAllAliases(Console
->Aliases
);
673 DPRINT1("ConSrvDeleteConsole - Unlocking\n");
674 LeaveCriticalSection(&Console
->Lock
);
675 DPRINT1("ConSrvDeleteConsole - Destroying lock\n");
676 DeleteCriticalSection(&Console
->Lock
);
677 DPRINT1("ConSrvDeleteConsole - Lock destroyed ; freeing console\n");
679 RtlFreeHeap(ConSrvHeap
, 0, Console
);
680 DPRINT1("ConSrvDeleteConsole - Console freed\n");
682 /* Unlock the console list and return */
683 ConSrvUnlockConsoleList();
687 /* PUBLIC SERVER APIS *********************************************************/
689 CSR_API(SrvOpenConsole
)
692 PCONSOLE_OPENCONSOLE OpenConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.OpenConsoleRequest
;
693 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
696 DWORD DesiredAccess
= OpenConsoleRequest
->Access
;
697 DWORD ShareMode
= OpenConsoleRequest
->ShareMode
;
700 OpenConsoleRequest
->ConsoleHandle
= INVALID_HANDLE_VALUE
;
702 Status
= ConSrvGetConsole(ProcessData
, &Console
, TRUE
);
703 if (!NT_SUCCESS(Status
))
705 DPRINT1("Can't get console\n");
709 RtlEnterCriticalSection(&ProcessData
->HandleTableLock
);
712 * Open a handle to either the active screen buffer or the input buffer.
714 if (OpenConsoleRequest
->HandleType
== HANDLE_OUTPUT
)
716 Object
= &Console
->ActiveBuffer
->Header
;
720 Object
= &Console
->InputBuffer
.Header
;
723 if (((DesiredAccess
& GENERIC_READ
) && Object
->ExclusiveRead
!= 0) ||
724 ((DesiredAccess
& GENERIC_WRITE
) && Object
->ExclusiveWrite
!= 0) ||
725 (!(ShareMode
& FILE_SHARE_READ
) && Object
->AccessRead
!= 0) ||
726 (!(ShareMode
& FILE_SHARE_WRITE
) && Object
->AccessWrite
!= 0))
728 DPRINT1("Sharing violation\n");
729 Status
= STATUS_SHARING_VIOLATION
;
733 Status
= ConSrvInsertObject(ProcessData
,
734 &OpenConsoleRequest
->ConsoleHandle
,
737 OpenConsoleRequest
->Inheritable
,
741 RtlLeaveCriticalSection(&ProcessData
->HandleTableLock
);
743 ConSrvReleaseConsole(Console
, TRUE
);
747 CSR_API(SrvAllocConsole
)
749 NTSTATUS Status
= STATUS_SUCCESS
;
750 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.AllocConsoleRequest
;
751 PCSR_PROCESS CsrProcess
= CsrGetClientThread()->Process
;
752 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrProcess
);
754 if (ProcessData
->Console
!= NULL
)
756 DPRINT1("Process already has a console\n");
757 return STATUS_ACCESS_DENIED
;
760 if (!CsrValidateMessageBuffer(ApiMessage
,
761 (PVOID
*)&AllocConsoleRequest
->ConsoleStartInfo
,
763 sizeof(CONSOLE_START_INFO
)))
765 return STATUS_INVALID_PARAMETER
;
769 * We are about to create a new console. However when ConSrvNewProcess
770 * was called, we didn't know that we wanted to create a new console and
771 * therefore, we by default inherited the handles table from our parent
772 * process. It's only now that we notice that in fact we do not need
773 * them, because we've created a new console and thus we must use it.
775 * Therefore, free the console we can have and our handles table,
776 * and recreate a new one later on.
778 ConSrvRemoveConsole(ProcessData
);
780 /* Initialize a new Console owned by the Console Leader Process */
781 Status
= ConSrvAllocateConsole(ProcessData
,
782 &AllocConsoleRequest
->InputHandle
,
783 &AllocConsoleRequest
->OutputHandle
,
784 &AllocConsoleRequest
->ErrorHandle
,
785 AllocConsoleRequest
->ConsoleStartInfo
);
786 if (!NT_SUCCESS(Status
))
788 DPRINT1("Console allocation failed\n");
792 /* Return it to the caller */
793 AllocConsoleRequest
->Console
= ProcessData
->Console
;
795 /* Input Wait Handle */
796 AllocConsoleRequest
->InputWaitHandle
= ProcessData
->ConsoleEvent
;
798 /* Set the Property Dialog Handler */
799 ProcessData
->PropDispatcher
= AllocConsoleRequest
->PropDispatcher
;
801 /* Set the Ctrl Dispatcher */
802 ProcessData
->CtrlDispatcher
= AllocConsoleRequest
->CtrlDispatcher
;
804 return STATUS_SUCCESS
;
807 CSR_API(SrvAttachConsole
)
809 NTSTATUS Status
= STATUS_SUCCESS
;
810 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.AttachConsoleRequest
;
811 PCSR_PROCESS SourceProcess
= NULL
; // The parent process.
812 PCSR_PROCESS TargetProcess
= CsrGetClientThread()->Process
; // Ourselves.
813 HANDLE ProcessId
= ULongToHandle(AttachConsoleRequest
->ProcessId
);
814 PCONSOLE_PROCESS_DATA SourceProcessData
, TargetProcessData
;
816 TargetProcessData
= ConsoleGetPerProcessData(TargetProcess
);
818 if (TargetProcessData
->Console
!= NULL
)
820 DPRINT1("Process already has a console\n");
821 return STATUS_ACCESS_DENIED
;
824 /* Check whether we try to attach to the parent's console */
825 if (ProcessId
== ULongToHandle(ATTACH_PARENT_PROCESS
))
827 PROCESS_BASIC_INFORMATION ProcessInfo
;
828 ULONG Length
= sizeof(ProcessInfo
);
830 /* Get the real parent's ID */
832 Status
= NtQueryInformationProcess(TargetProcess
->ProcessHandle
,
833 ProcessBasicInformation
,
836 if (!NT_SUCCESS(Status
))
838 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status
);
842 ProcessId
= ULongToHandle(ProcessInfo
.InheritedFromUniqueProcessId
);
845 /* Lock the source process via its PID */
846 Status
= CsrLockProcessByClientId(ProcessId
, &SourceProcess
);
847 if (!NT_SUCCESS(Status
)) return Status
;
849 SourceProcessData
= ConsoleGetPerProcessData(SourceProcess
);
851 if (SourceProcessData
->Console
== NULL
)
853 Status
= STATUS_INVALID_HANDLE
;
858 * We are about to create a new console. However when ConSrvNewProcess
859 * was called, we didn't know that we wanted to create a new console and
860 * therefore, we by default inherited the handles table from our parent
861 * process. It's only now that we notice that in fact we do not need
862 * them, because we've created a new console and thus we must use it.
864 * Therefore, free the console we can have and our handles table,
865 * and recreate a new one later on.
867 ConSrvRemoveConsole(TargetProcessData
);
870 * Inherit the console from the parent,
871 * if any, otherwise return an error.
873 Status
= ConSrvInheritConsole(TargetProcessData
,
874 SourceProcessData
->Console
,
876 &AttachConsoleRequest
->InputHandle
,
877 &AttachConsoleRequest
->OutputHandle
,
878 &AttachConsoleRequest
->ErrorHandle
);
879 if (!NT_SUCCESS(Status
))
881 DPRINT1("Console inheritance failed\n");
885 /* Return it to the caller */
886 AttachConsoleRequest
->Console
= TargetProcessData
->Console
;
888 /* Input Wait Handle */
889 AttachConsoleRequest
->InputWaitHandle
= TargetProcessData
->ConsoleEvent
;
891 /* Set the Property Dialog Handler */
892 TargetProcessData
->PropDispatcher
= AttachConsoleRequest
->PropDispatcher
;
894 /* Set the Ctrl Dispatcher */
895 TargetProcessData
->CtrlDispatcher
= AttachConsoleRequest
->CtrlDispatcher
;
897 Status
= STATUS_SUCCESS
;
900 /* Unlock the "source" process and exit */
901 CsrUnlockProcess(SourceProcess
);
905 CSR_API(SrvFreeConsole
)
907 ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
));
908 return STATUS_SUCCESS
;
911 CSR_API(SrvSetConsoleMode
)
913 #define CONSOLE_INPUT_MODE_VALID ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
914 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
915 ENABLE_MOUSE_INPUT | \
916 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS )
917 #define CONSOLE_OUTPUT_MODE_VALID ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
920 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ConsoleModeRequest
;
921 Object_t
* Object
= NULL
;
923 Status
= ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
924 ConsoleModeRequest
->ConsoleHandle
,
925 &Object
, NULL
, GENERIC_WRITE
, TRUE
, 0);
926 if (!NT_SUCCESS(Status
)) return Status
;
928 Status
= STATUS_SUCCESS
;
930 if (CONIO_INPUT_BUFFER_MAGIC
== Object
->Type
)
932 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
933 InputBuffer
->Mode
= ConsoleModeRequest
->ConsoleMode
& CONSOLE_INPUT_MODE_VALID
;
935 else if (CONIO_SCREEN_BUFFER_MAGIC
== Object
->Type
)
937 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
938 Buffer
->Mode
= ConsoleModeRequest
->ConsoleMode
& CONSOLE_OUTPUT_MODE_VALID
;
942 Status
= STATUS_INVALID_HANDLE
;
945 ConSrvReleaseObject(Object
, TRUE
);
949 CSR_API(SrvGetConsoleMode
)
952 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ConsoleModeRequest
;
953 Object_t
* Object
= NULL
;
955 Status
= ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
956 ConsoleModeRequest
->ConsoleHandle
,
957 &Object
, NULL
, GENERIC_READ
, TRUE
, 0);
958 if (!NT_SUCCESS(Status
)) return Status
;
960 Status
= STATUS_SUCCESS
;
962 if (CONIO_INPUT_BUFFER_MAGIC
== Object
->Type
)
964 PCONSOLE_INPUT_BUFFER InputBuffer
= (PCONSOLE_INPUT_BUFFER
)Object
;
965 ConsoleModeRequest
->ConsoleMode
= InputBuffer
->Mode
;
967 else if (CONIO_SCREEN_BUFFER_MAGIC
== Object
->Type
)
969 PCONSOLE_SCREEN_BUFFER Buffer
= (PCONSOLE_SCREEN_BUFFER
)Object
;
970 ConsoleModeRequest
->ConsoleMode
= Buffer
->Mode
;
974 Status
= STATUS_INVALID_HANDLE
;
977 ConSrvReleaseObject(Object
, TRUE
);
981 CSR_API(SrvSetConsoleTitle
)
984 PCONSOLE_GETSETCONSOLETITLE TitleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.TitleRequest
;
985 // PCSR_PROCESS Process = CsrGetClientThread()->Process;
989 if (!CsrValidateMessageBuffer(ApiMessage
,
990 (PVOID
)&TitleRequest
->Title
,
991 TitleRequest
->Length
,
994 return STATUS_INVALID_PARAMETER
;
997 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
998 if (!NT_SUCCESS(Status
))
1000 DPRINT1("Can't get console\n");
1004 /* Allocate a new buffer to hold the new title (NULL-terminated) */
1005 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, TitleRequest
->Length
+ sizeof(WCHAR
));
1008 /* Free the old title */
1009 RtlFreeUnicodeString(&Console
->Title
);
1011 /* Copy title to console */
1012 Console
->Title
.Buffer
= Buffer
;
1013 Console
->Title
.Length
= TitleRequest
->Length
;
1014 Console
->Title
.MaximumLength
= Console
->Title
.Length
+ sizeof(WCHAR
);
1015 RtlCopyMemory(Console
->Title
.Buffer
,
1016 TitleRequest
->Title
,
1017 Console
->Title
.Length
);
1018 Console
->Title
.Buffer
[Console
->Title
.Length
/ sizeof(WCHAR
)] = L
'\0';
1020 ConioChangeTitle(Console
);
1021 Status
= STATUS_SUCCESS
;
1025 Status
= STATUS_NO_MEMORY
;
1028 ConSrvReleaseConsole(Console
, TRUE
);
1032 CSR_API(SrvGetConsoleTitle
)
1035 PCONSOLE_GETSETCONSOLETITLE TitleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.TitleRequest
;
1036 // PCSR_PROCESS Process = CsrGetClientThread()->Process;
1040 if (!CsrValidateMessageBuffer(ApiMessage
,
1041 (PVOID
)&TitleRequest
->Title
,
1042 TitleRequest
->Length
,
1045 return STATUS_INVALID_PARAMETER
;
1048 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
1049 if (!NT_SUCCESS(Status
))
1051 DPRINT1("Can't get console\n");
1055 /* Copy title of the console to the user title buffer */
1056 if (TitleRequest
->Length
>= sizeof(WCHAR
))
1058 Length
= min(TitleRequest
->Length
- sizeof(WCHAR
), Console
->Title
.Length
);
1059 memcpy(TitleRequest
->Title
, Console
->Title
.Buffer
, Length
);
1060 TitleRequest
->Title
[Length
/ sizeof(WCHAR
)] = L
'\0';
1063 TitleRequest
->Length
= Console
->Title
.Length
;
1065 ConSrvReleaseConsole(Console
, TRUE
);
1066 return STATUS_SUCCESS
;
1069 /**********************************************************************
1070 * HardwareStateProperty
1073 * Set/Get the value of the HardwareState and switch
1074 * between direct video buffer ouput and GDI windowed
1077 * Client hands us a CONSOLE_GETSETHWSTATE object.
1078 * We use the same object to Request.
1080 * ConsoleHwState has the correct size to be compatible
1081 * with NT's, but values are not.
1083 static NTSTATUS FASTCALL
1084 SetConsoleHardwareState(PCONSOLE Console
, DWORD ConsoleHwState
)
1086 DPRINT1("Console Hardware State: %d\n", ConsoleHwState
);
1088 if ((CONSOLE_HARDWARE_STATE_GDI_MANAGED
== ConsoleHwState
)
1089 ||(CONSOLE_HARDWARE_STATE_DIRECT
== ConsoleHwState
))
1091 if (Console
->HardwareState
!= ConsoleHwState
)
1093 /* TODO: implement switching from full screen to windowed mode */
1094 /* TODO: or back; now simply store the hardware state */
1095 Console
->HardwareState
= ConsoleHwState
;
1098 return STATUS_SUCCESS
;
1101 return STATUS_INVALID_PARAMETER_3
; /* Client: (handle, set_get, [mode]) */
1104 CSR_API(SrvGetConsoleHardwareState
)
1107 PCONSOLE_GETSETHWSTATE HardwareStateRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.HardwareStateRequest
;
1108 PCONSOLE_SCREEN_BUFFER Buff
;
1111 Status
= ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
1112 HardwareStateRequest
->OutputHandle
,
1116 if (!NT_SUCCESS(Status
))
1118 DPRINT1("Failed to get console handle in SrvGetConsoleHardwareState\n");
1122 Console
= Buff
->Header
.Console
;
1123 HardwareStateRequest
->State
= Console
->HardwareState
;
1125 ConSrvReleaseScreenBuffer(Buff
, TRUE
);
1130 CSR_API(SrvSetConsoleHardwareState
)
1133 PCONSOLE_GETSETHWSTATE HardwareStateRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.HardwareStateRequest
;
1134 PCONSOLE_SCREEN_BUFFER Buff
;
1137 Status
= ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
1138 HardwareStateRequest
->OutputHandle
,
1142 if (!NT_SUCCESS(Status
))
1144 DPRINT1("Failed to get console handle in SrvSetConsoleHardwareState\n");
1148 DPRINT("Setting console hardware state.\n");
1149 Console
= Buff
->Header
.Console
;
1150 Status
= SetConsoleHardwareState(Console
, HardwareStateRequest
->State
);
1152 ConSrvReleaseScreenBuffer(Buff
, TRUE
);
1157 CSR_API(SrvGetConsoleWindow
)
1160 PCONSOLE_GETWINDOW GetWindowRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetWindowRequest
;
1163 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
1164 if (!NT_SUCCESS(Status
)) return Status
;
1166 GetWindowRequest
->WindowHandle
= ConioGetConsoleWindowHandle(Console
);
1167 ConSrvReleaseConsole(Console
, TRUE
);
1169 return STATUS_SUCCESS
;
1172 CSR_API(SrvSetConsoleIcon
)
1175 PCONSOLE_SETICON SetIconRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.SetIconRequest
;
1178 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
1179 if (!NT_SUCCESS(Status
)) return Status
;
1181 Status
= (ConioChangeIcon(Console
, SetIconRequest
->WindowIcon
)
1183 : STATUS_UNSUCCESSFUL
);
1185 ConSrvReleaseConsole(Console
, TRUE
);
1190 CSR_API(SrvGetConsoleCP
)
1193 PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ConsoleCPRequest
;
1196 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
1197 ConsoleCPRequest
->InputCP
? "Input" : "Output");
1199 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
1200 if (!NT_SUCCESS(Status
)) return Status
;
1202 ConsoleCPRequest
->CodePage
= (ConsoleCPRequest
->InputCP
? Console
->CodePage
1203 : Console
->OutputCodePage
);
1204 ConSrvReleaseConsole(Console
, TRUE
);
1205 return STATUS_SUCCESS
;
1208 CSR_API(SrvSetConsoleCP
)
1211 PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ConsoleCPRequest
;
1214 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
1215 ConsoleCPRequest
->InputCP
? "Input" : "Output");
1217 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
1218 if (!NT_SUCCESS(Status
)) return Status
;
1220 if (IsValidCodePage(ConsoleCPRequest
->CodePage
))
1222 if (ConsoleCPRequest
->InputCP
)
1223 Console
->CodePage
= ConsoleCPRequest
->CodePage
;
1225 Console
->OutputCodePage
= ConsoleCPRequest
->CodePage
;
1227 ConSrvReleaseConsole(Console
, TRUE
);
1228 return STATUS_SUCCESS
;
1231 ConSrvReleaseConsole(Console
, TRUE
);
1232 return STATUS_INVALID_PARAMETER
;
1235 CSR_API(SrvGetConsoleProcessList
)
1238 PCONSOLE_GETPROCESSLIST GetProcessListRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetProcessListRequest
;
1240 // PCSR_PROCESS Process = CsrGetClientThread()->Process;
1242 PCONSOLE_PROCESS_DATA current
;
1243 PLIST_ENTRY current_entry
;
1246 if (!CsrValidateMessageBuffer(ApiMessage
,
1247 (PVOID
)&GetProcessListRequest
->pProcessIds
,
1248 GetProcessListRequest
->nMaxIds
,
1251 return STATUS_INVALID_PARAMETER
;
1254 Buffer
= GetProcessListRequest
->pProcessIds
;
1256 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
1257 if (!NT_SUCCESS(Status
)) return Status
;
1259 for (current_entry
= Console
->ProcessList
.Flink
;
1260 current_entry
!= &Console
->ProcessList
;
1261 current_entry
= current_entry
->Flink
)
1263 current
= CONTAINING_RECORD(current_entry
, CONSOLE_PROCESS_DATA
, ConsoleLink
);
1264 if (++nItems
<= GetProcessListRequest
->nMaxIds
)
1266 *Buffer
++ = HandleToUlong(current
->Process
->ClientId
.UniqueProcess
);
1270 ConSrvReleaseConsole(Console
, TRUE
);
1272 GetProcessListRequest
->nProcessIdsTotal
= nItems
;
1273 return STATUS_SUCCESS
;
1276 CSR_API(SrvGenerateConsoleCtrlEvent
)
1279 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GenerateCtrlEventRequest
;
1282 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
1283 if (!NT_SUCCESS(Status
)) return Status
;
1285 Status
= ConSrvConsoleProcessCtrlEvent(Console
,
1286 GenerateCtrlEventRequest
->ProcessGroup
,
1287 GenerateCtrlEventRequest
->Event
);
1289 ConSrvReleaseConsole(Console
, TRUE
);
1293 CSR_API(SrvGetConsoleSelectionInfo
)
1296 PCONSOLE_GETSELECTIONINFO GetSelectionInfoRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetSelectionInfoRequest
;
1299 Status
= ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
, TRUE
);
1300 if (NT_SUCCESS(Status
))
1302 memset(&GetSelectionInfoRequest
->Info
, 0, sizeof(CONSOLE_SELECTION_INFO
));
1303 if (Console
->Selection
.dwFlags
!= 0)
1304 GetSelectionInfoRequest
->Info
= Console
->Selection
;
1305 ConSrvReleaseConsole(Console
, TRUE
);