[CONSRV]
[reactos.git] / win32ss / user / consrv / console.c
1 /*
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
6 * PROGRAMMERS:
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #define COBJMACROS
12 #define NONAMELESSUNION
13
14 #include "consrv.h"
15 #include "settings.h"
16 #include "guiconsole.h"
17
18 #ifdef TUI_CONSOLE
19 #include "tuiconsole.h"
20 #endif
21
22 #include <shlwapi.h>
23 #include <shlobj.h>
24
25 //#define NDEBUG
26 #include <debug.h>
27
28
29 /* FUNCTIONS *****************************************************************/
30
31 BOOL FASTCALL
32 DtbgIsDesktopVisible(VOID)
33 {
34 return !((BOOL)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE));
35 }
36
37 VOID FASTCALL
38 ConSrvConsoleCtrlEventTimeout(DWORD Event,
39 PCONSOLE_PROCESS_DATA ProcessData,
40 DWORD Timeout)
41 {
42 HANDLE Thread;
43
44 DPRINT("ConSrvConsoleCtrlEvent Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
45
46 if (ProcessData->CtrlDispatcher)
47 {
48 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
49 ProcessData->CtrlDispatcher,
50 UlongToPtr(Event), 0, NULL);
51 if (NULL == Thread)
52 {
53 DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError());
54 return;
55 }
56
57 DPRINT("We succeeded at creating ProcessData->CtrlDispatcher remote thread, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
58 WaitForSingleObject(Thread, Timeout);
59 CloseHandle(Thread);
60 }
61 }
62
63 VOID FASTCALL
64 ConSrvConsoleCtrlEvent(DWORD Event, PCONSOLE_PROCESS_DATA ProcessData)
65 {
66 ConSrvConsoleCtrlEventTimeout(Event, ProcessData, 0);
67 }
68
69 VOID FASTCALL
70 ConioPause(PCONSOLE Console, UINT Flags)
71 {
72 Console->PauseFlags |= Flags;
73 if (!Console->UnpauseEvent)
74 Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
75 }
76
77 VOID FASTCALL
78 ConioUnpause(PCONSOLE Console, UINT Flags)
79 {
80 Console->PauseFlags &= ~Flags;
81
82 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
83 if (Console->PauseFlags == 0 && Console->UnpauseEvent)
84 {
85 SetEvent(Console->UnpauseEvent);
86 CloseHandle(Console->UnpauseEvent);
87 Console->UnpauseEvent = NULL;
88
89 CsrNotifyWait(&Console->WriteWaitQueue,
90 WaitAll,
91 NULL,
92 NULL);
93 if (!IsListEmpty(&Console->WriteWaitQueue))
94 {
95 CsrDereferenceWait(&Console->WriteWaitQueue);
96 }
97 }
98 }
99
100 static BOOL
101 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
102 OUT PCONSOLE_INFO ConsoleInfo,
103 OUT LPWSTR IconPath,
104 IN SIZE_T IconPathLength,
105 OUT PINT piIcon)
106 {
107 #define PATH_SEPARATOR L'\\'
108
109 BOOL RetVal = FALSE;
110 LPWSTR LinkName = NULL;
111 SIZE_T Length = 0;
112
113 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
114 return FALSE;
115
116 if (IconPath == NULL || piIcon == NULL)
117 return FALSE;
118
119 IconPath[0] = L'\0';
120 *piIcon = 0;
121
122 /* 1- Find the last path separator if any */
123 LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR);
124 if (LinkName == NULL)
125 {
126 LinkName = ConsoleStartInfo->ConsoleTitle;
127 }
128 else
129 {
130 /* Skip the path separator */
131 ++LinkName;
132 }
133
134 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
135 Length = wcslen(LinkName);
136 if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
137 return FALSE;
138
139 /* 3- It may be a link. Try to retrieve some properties */
140 HRESULT hRes = CoInitialize(NULL);
141 if (SUCCEEDED(hRes))
142 {
143 /* Get a pointer to the IShellLink interface */
144 IShellLinkW* pshl = NULL;
145 hRes = CoCreateInstance(&CLSID_ShellLink,
146 NULL,
147 CLSCTX_INPROC_SERVER,
148 &IID_IShellLinkW,
149 (LPVOID*)&pshl);
150 if (SUCCEEDED(hRes))
151 {
152 /* Get a pointer to the IPersistFile interface */
153 IPersistFile* ppf = NULL;
154 hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
155 if (SUCCEEDED(hRes))
156 {
157 /* Load the shortcut */
158 hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ);
159 if (SUCCEEDED(hRes))
160 {
161 /*
162 * Finally we can get the properties !
163 * Update the old ones if needed.
164 */
165 INT ShowCmd = 0;
166 // WORD HotKey = 0;
167
168 /* Get the name of the shortcut */
169 Length = min(Length - 4,
170 sizeof(ConsoleStartInfo->ConsoleTitle) / sizeof(ConsoleStartInfo->ConsoleTitle[0]) - 1);
171 wcsncpy(ConsoleStartInfo->ConsoleTitle, LinkName, Length);
172 ConsoleStartInfo->ConsoleTitle[Length] = L'\0';
173
174 // HACK: Copy also the name of the shortcut
175 Length = min(Length,
176 sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1);
177 wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
178 ConsoleInfo->ConsoleTitle[Length] = L'\0';
179
180 /* Get the window showing command */
181 hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
182 if (SUCCEEDED(hRes)) ConsoleStartInfo->ShowWindow = (WORD)ShowCmd;
183
184 /* Get the hotkey */
185 // hRes = pshl->GetHotkey(&ShowCmd);
186 // if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey;
187
188 /* Get the icon location, if any */
189 hRes = IShellLinkW_GetIconLocation(pshl, IconPath, IconPathLength, piIcon);
190 if (!SUCCEEDED(hRes))
191 {
192 IconPath[0] = L'\0';
193 }
194
195 // FIXME: Since we still don't load console properties from the shortcut,
196 // return false. When this will be done, we will return true instead.
197 RetVal = FALSE;
198 }
199 IPersistFile_Release(ppf);
200 }
201 IShellLinkW_Release(pshl);
202 }
203 }
204 CoUninitialize();
205
206 return RetVal;
207 }
208
209 NTSTATUS WINAPI
210 ConSrvInitConsole(OUT PCONSOLE* NewConsole,
211 IN LPCWSTR AppPath,
212 IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
213 IN PCSR_PROCESS ConsoleLeaderProcess)
214 {
215 NTSTATUS Status;
216 SECURITY_ATTRIBUTES SecurityAttributes;
217 CONSOLE_INFO ConsoleInfo;
218 SIZE_T Length = 0;
219 DWORD ProcessId = HandleToUlong(ConsoleLeaderProcess->ClientId.UniqueProcess);
220 PCONSOLE Console;
221 PCONSOLE_SCREEN_BUFFER NewBuffer;
222 BOOL GuiMode;
223 WCHAR Title[128];
224 WCHAR IconPath[MAX_PATH + 1] = L"";
225 INT iIcon = 0;
226
227 if (NewConsole == NULL) return STATUS_INVALID_PARAMETER;
228 *NewConsole = NULL;
229
230 /*
231 * Allocate a console structure
232 */
233 Console = RtlAllocateHeap(ConSrvHeap, HEAP_ZERO_MEMORY, sizeof(CONSOLE));
234 if (NULL == Console)
235 {
236 DPRINT1("Not enough memory for console creation.\n");
237 return STATUS_NO_MEMORY;
238 }
239
240 /*
241 * Load the console settings
242 */
243
244 /* 1. Load the default settings */
245 ConSrvGetDefaultSettings(&ConsoleInfo, ProcessId);
246
247 /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
248 Length = min(wcslen(ConsoleStartInfo->ConsoleTitle),
249 sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1);
250 wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length);
251 ConsoleInfo.ConsoleTitle[Length] = L'\0';
252
253 /*
254 * 3. Check whether the process creating the
255 * console was launched via a shell-link
256 * (ConsoleInfo.ConsoleTitle may be updated).
257 */
258 if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME)
259 {
260 if (!LoadShellLinkConsoleInfo(ConsoleStartInfo,
261 &ConsoleInfo,
262 IconPath,
263 MAX_PATH,
264 &iIcon))
265 {
266 ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
267 }
268 }
269
270 /*
271 * 4. Load the remaining console settings via the registry.
272 */
273 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
274 {
275 /*
276 * Either we weren't created by an app launched via a shell-link,
277 * or we failed to load shell-link console properties.
278 * Therefore, load the console infos for the application from the registry.
279 */
280 ConSrvReadUserSettings(&ConsoleInfo, ProcessId);
281
282 /*
283 * Now, update them with the properties the user might gave to us
284 * via the STARTUPINFO structure before calling CreateProcess
285 * (and which was transmitted via the ConsoleStartInfo structure).
286 */
287 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
288 {
289 ConsoleInfo.ScreenAttrib = ConsoleStartInfo->FillAttribute;
290 }
291 if (ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
292 {
293 ConsoleInfo.ScreenBufferSize = ConsoleStartInfo->ScreenBufferSize;
294 }
295 if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW)
296 {
297 ConsoleInfo.u.GuiInfo.ShowWindow = ConsoleStartInfo->ShowWindow;
298 }
299 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
300 {
301 ConsoleInfo.u.GuiInfo.AutoPosition = FALSE;
302 ConsoleInfo.u.GuiInfo.WindowOrigin = ConsoleStartInfo->ConsoleWindowOrigin;
303 }
304 if (ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
305 {
306 // ConsoleInfo.ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
307 ConsoleInfo.ConsoleSize.X = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cx;
308 ConsoleInfo.ConsoleSize.Y = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cy;
309 }
310 /*
311 if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
312 {
313 }
314 */
315 }
316
317 /*
318 * Initialize the console
319 */
320 InitializeCriticalSection(&Console->Lock);
321 Console->ReferenceCount = 0;
322 InitializeListHead(&Console->ProcessList);
323 memcpy(Console->Colors, ConsoleInfo.Colors, sizeof(ConsoleInfo.Colors));
324 Console->Size = ConsoleInfo.ConsoleSize;
325
326 /*
327 * Initialize the input buffer
328 */
329 Console->InputBuffer.Header.Type = CONIO_INPUT_BUFFER_MAGIC;
330 Console->InputBuffer.Header.Console = Console;
331
332 SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
333 SecurityAttributes.lpSecurityDescriptor = NULL;
334 SecurityAttributes.bInheritHandle = TRUE;
335 Console->InputBuffer.ActiveEvent = CreateEventW(&SecurityAttributes, TRUE, FALSE, NULL);
336 if (NULL == Console->InputBuffer.ActiveEvent)
337 {
338 DeleteCriticalSection(&Console->Lock);
339 RtlFreeHeap(ConSrvHeap, 0, Console);
340 return STATUS_UNSUCCESSFUL;
341 }
342
343 // TODO: Use the values from ConsoleInfo.
344 Console->InputBuffer.Mode = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT |
345 ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT;
346 Console->QuickEdit = ConsoleInfo.QuickEdit;
347 Console->InsertMode = ConsoleInfo.InsertMode;
348 InitializeListHead(&Console->InputBuffer.ReadWaitQueue);
349 InitializeListHead(&Console->InputBuffer.InputEvents);
350 Console->LineBuffer = NULL;
351 Console->CodePage = GetOEMCP();
352 Console->OutputCodePage = GetOEMCP();
353
354 /* Initialize a new screen buffer with default settings */
355 InitializeListHead(&Console->BufferList);
356 Status = ConSrvCreateScreenBuffer(Console,
357 &NewBuffer,
358 ConsoleInfo.ScreenBufferSize,
359 ConsoleInfo.ScreenAttrib,
360 ConsoleInfo.PopupAttrib,
361 TRUE,
362 ConsoleInfo.CursorSize);
363 if (!NT_SUCCESS(Status))
364 {
365 DPRINT1("ConSrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
366 CloseHandle(Console->InputBuffer.ActiveEvent);
367 DeleteCriticalSection(&Console->Lock);
368 RtlFreeHeap(ConSrvHeap, 0, Console);
369 return Status;
370 }
371 /* Make the new screen buffer active */
372 Console->ActiveBuffer = NewBuffer;
373 Console->FullScreen = ConsoleInfo.FullScreen;
374 InitializeListHead(&Console->WriteWaitQueue);
375
376 /*
377 * Initialize the history buffers
378 */
379 InitializeListHead(&Console->HistoryBuffers);
380 Console->HistoryBufferSize = ConsoleInfo.HistoryBufferSize;
381 Console->NumberOfHistoryBuffers = ConsoleInfo.NumberOfHistoryBuffers;
382 Console->HistoryNoDup = ConsoleInfo.HistoryNoDup;
383
384 /* Initialize the console title */
385 RtlCreateUnicodeString(&Console->OriginalTitle, ConsoleStartInfo->ConsoleTitle);
386 if (ConsoleStartInfo->ConsoleTitle[0] == L'\0')
387 {
388 if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, Title, sizeof(Title) / sizeof(Title[0])))
389 {
390 RtlCreateUnicodeString(&Console->Title, Title);
391 }
392 else
393 {
394 RtlCreateUnicodeString(&Console->Title, L"ReactOS Console");
395 }
396 }
397 else
398 {
399 RtlCreateUnicodeString(&Console->Title, ConsoleStartInfo->ConsoleTitle);
400 }
401
402 /*
403 * If we are not in GUI-mode, start the text-mode terminal emulator.
404 * If we fail, try to start the GUI-mode terminal emulator.
405 */
406 #ifdef TUI_CONSOLE
407 GuiMode = DtbgIsDesktopVisible();
408 #else
409 GuiMode = TRUE;
410 #endif
411
412 #ifdef TUI_CONSOLE
413 if (!GuiMode)
414 {
415 DPRINT1("CONSRV: Opening text-mode terminal emulator\n");
416 Status = TuiInitConsole(Console, &ConsoleInfo);
417 if (!NT_SUCCESS(Status))
418 {
419 DPRINT1("Failed to open text-mode terminal emulator, switching to gui-mode, Status = 0x%08lx\n", Status);
420 GuiMode = TRUE;
421 }
422 }
423 #endif
424
425 /*
426 * Try to open the GUI-mode terminal emulator. Two cases are possible:
427 * - We are in GUI-mode, therefore GuiMode == TRUE, the previous test-case
428 * failed and we start GUI-mode terminal emulator.
429 * - We are in text-mode, therefore GuiMode == FALSE, the previous test-case
430 * succeeded BUT we failed at starting text-mode terminal emulator.
431 * Then GuiMode was switched to TRUE in order to try to open the GUI-mode
432 * terminal emulator (win32k will automatically switch to graphical mode,
433 * therefore no additional code is needed).
434 */
435 if (GuiMode)
436 {
437 DPRINT1("CONSRV: Opening GUI-mode terminal emulator\n");
438 Status = GuiInitConsole(Console,
439 AppPath,
440 &ConsoleInfo,
441 IconPath,
442 iIcon);
443 if (!NT_SUCCESS(Status))
444 {
445 DPRINT1("GuiInitConsole: failed, Status = 0x%08lx\n", Status);
446 RtlFreeUnicodeString(&Console->Title);
447 RtlFreeUnicodeString(&Console->OriginalTitle);
448 ConioDeleteScreenBuffer(NewBuffer);
449 CloseHandle(Console->InputBuffer.ActiveEvent);
450 DeleteCriticalSection(&Console->Lock);
451 RtlFreeHeap(ConSrvHeap, 0, Console);
452 return Status;
453 }
454 }
455
456 /* Copy buffer contents to screen */
457 ConioDrawConsole(Console);
458
459 /* Return the newly created console to the caller and a success code too */
460 *NewConsole = Console;
461 return STATUS_SUCCESS;
462 }
463
464 VOID WINAPI
465 ConSrvInitConsoleSupport(VOID)
466 {
467 DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
468
469 /* Should call LoadKeyboardLayout */
470 }
471
472 VOID WINAPI
473 ConSrvDeleteConsole(PCONSOLE Console)
474 {
475 ConsoleInput *Event;
476
477 DPRINT("ConSrvDeleteConsole\n");
478
479 /* Drain input event queue */
480 while (Console->InputBuffer.InputEvents.Flink != &Console->InputBuffer.InputEvents)
481 {
482 Event = (ConsoleInput *) Console->InputBuffer.InputEvents.Flink;
483 Console->InputBuffer.InputEvents.Flink = Console->InputBuffer.InputEvents.Flink->Flink;
484 Console->InputBuffer.InputEvents.Flink->Flink->Blink = &Console->InputBuffer.InputEvents;
485 RtlFreeHeap(ConSrvHeap, 0, Event);
486 }
487
488 ConioCleanupConsole(Console);
489 if (Console->LineBuffer)
490 RtlFreeHeap(ConSrvHeap, 0, Console->LineBuffer);
491 while (!IsListEmpty(&Console->HistoryBuffers))
492 HistoryDeleteBuffer((struct _HISTORY_BUFFER *)Console->HistoryBuffers.Flink);
493
494 ConioDeleteScreenBuffer(Console->ActiveBuffer);
495 if (!IsListEmpty(&Console->BufferList))
496 {
497 DPRINT1("BUG: screen buffer list not empty\n");
498 }
499
500 CloseHandle(Console->InputBuffer.ActiveEvent);
501 if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
502 DeleteCriticalSection(&Console->Lock);
503
504 RtlFreeUnicodeString(&Console->OriginalTitle);
505 RtlFreeUnicodeString(&Console->Title);
506 IntDeleteAllAliases(Console->Aliases);
507 RtlFreeHeap(ConSrvHeap, 0, Console);
508 }
509
510 CSR_API(SrvOpenConsole)
511 {
512 NTSTATUS Status = STATUS_SUCCESS;
513 PCONSOLE_OPENCONSOLE OpenConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.OpenConsoleRequest;
514 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
515
516 OpenConsoleRequest->ConsoleHandle = INVALID_HANDLE_VALUE;
517
518 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
519
520 if (ProcessData->Console)
521 {
522 DWORD DesiredAccess = OpenConsoleRequest->Access;
523 DWORD ShareMode = OpenConsoleRequest->ShareMode;
524
525 PCONSOLE Console = ProcessData->Console;
526 Object_t *Object;
527
528 EnterCriticalSection(&Console->Lock);
529
530 if (OpenConsoleRequest->HandleType == HANDLE_OUTPUT)
531 {
532 Object = &Console->ActiveBuffer->Header;
533 }
534 else // HANDLE_INPUT
535 {
536 Object = &Console->InputBuffer.Header;
537 }
538
539 if (((DesiredAccess & GENERIC_READ) && Object->ExclusiveRead != 0) ||
540 ((DesiredAccess & GENERIC_WRITE) && Object->ExclusiveWrite != 0) ||
541 (!(ShareMode & FILE_SHARE_READ) && Object->AccessRead != 0) ||
542 (!(ShareMode & FILE_SHARE_WRITE) && Object->AccessWrite != 0))
543 {
544 DPRINT1("Sharing violation\n");
545 Status = STATUS_SHARING_VIOLATION;
546 }
547 else
548 {
549 Status = ConSrvInsertObject(ProcessData,
550 &OpenConsoleRequest->ConsoleHandle,
551 Object,
552 DesiredAccess,
553 OpenConsoleRequest->Inheritable,
554 ShareMode);
555 }
556
557 LeaveCriticalSection(&Console->Lock);
558 }
559
560 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
561
562 return Status;
563 }
564
565 CSR_API(SrvAllocConsole)
566 {
567 NTSTATUS Status = STATUS_SUCCESS;
568 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
569 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
570 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
571
572 if (ProcessData->Console != NULL)
573 {
574 DPRINT1("Process already has a console\n");
575 return STATUS_ACCESS_DENIED;
576 }
577
578 if ( !CsrValidateMessageBuffer(ApiMessage,
579 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
580 1,
581 sizeof(CONSOLE_START_INFO)) ||
582 !CsrValidateMessageBuffer(ApiMessage,
583 (PVOID*)&AllocConsoleRequest->AppPath,
584 MAX_PATH + 1,
585 sizeof(WCHAR)) )
586 {
587 return STATUS_INVALID_PARAMETER;
588 }
589
590 /*
591 * We are about to create a new console. However when ConSrvNewProcess
592 * was called, we didn't know that we wanted to create a new console and
593 * therefore, we by default inherited the handles table from our parent
594 * process. It's only now that we notice that in fact we do not need
595 * them, because we've created a new console and thus we must use it.
596 *
597 * Therefore, free the console we can have and our handles table,
598 * and recreate a new one later on.
599 */
600 ConSrvRemoveConsole(ProcessData);
601 // ConSrvFreeHandlesTable(ProcessData);
602
603 /* Initialize a new Console owned by the Console Leader Process */
604 Status = ConSrvAllocateConsole(ProcessData,
605 AllocConsoleRequest->AppPath,
606 &AllocConsoleRequest->InputHandle,
607 &AllocConsoleRequest->OutputHandle,
608 &AllocConsoleRequest->ErrorHandle,
609 AllocConsoleRequest->ConsoleStartInfo);
610 if (!NT_SUCCESS(Status))
611 {
612 DPRINT1("Console allocation failed\n");
613 return Status;
614 }
615
616 /* Return it to the caller */
617 AllocConsoleRequest->Console = ProcessData->Console;
618
619 /* Input Wait Handle */
620 AllocConsoleRequest->InputWaitHandle = ProcessData->ConsoleEvent;
621
622 /* Set the Property Dialog Handler */
623 ProcessData->PropDispatcher = AllocConsoleRequest->PropDispatcher;
624
625 /* Set the Ctrl Dispatcher */
626 ProcessData->CtrlDispatcher = AllocConsoleRequest->CtrlDispatcher;
627
628 return STATUS_SUCCESS;
629 }
630
631 CSR_API(SrvAttachConsole)
632 {
633 NTSTATUS Status = STATUS_SUCCESS;
634 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
635 PCSR_PROCESS SourceProcess = NULL; // The parent process.
636 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
637 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
638 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
639
640 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
641
642 if (TargetProcessData->Console != NULL)
643 {
644 DPRINT1("Process already has a console\n");
645 return STATUS_ACCESS_DENIED;
646 }
647
648 /* Check whether we try to attach to the parent's console */
649 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
650 {
651 PROCESS_BASIC_INFORMATION ProcessInfo;
652 ULONG Length = sizeof(ProcessInfo);
653
654 /* Get the real parent's ID */
655
656 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
657 ProcessBasicInformation,
658 &ProcessInfo,
659 Length, &Length);
660 if (!NT_SUCCESS(Status))
661 {
662 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status);
663 return Status;
664 }
665
666 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
667 }
668
669 /* Lock the source process via its PID */
670 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
671 if (!NT_SUCCESS(Status)) return Status;
672
673 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
674
675 if (SourceProcessData->Console == NULL)
676 {
677 Status = STATUS_INVALID_HANDLE;
678 goto Quit;
679 }
680
681 /*
682 * We are about to create a new console. However when ConSrvNewProcess
683 * was called, we didn't know that we wanted to create a new console and
684 * therefore, we by default inherited the handles table from our parent
685 * process. It's only now that we notice that in fact we do not need
686 * them, because we've created a new console and thus we must use it.
687 *
688 * Therefore, free the console we can have and our handles table,
689 * and recreate a new one later on.
690 */
691 ConSrvRemoveConsole(TargetProcessData);
692 // ConSrvFreeHandlesTable(TargetProcessData);
693
694 /*
695 * Inherit the console from the parent,
696 * if any, otherwise return an error.
697 */
698 Status = ConSrvInheritConsole(TargetProcessData,
699 SourceProcessData->Console,
700 TRUE,
701 &AttachConsoleRequest->InputHandle,
702 &AttachConsoleRequest->OutputHandle,
703 &AttachConsoleRequest->ErrorHandle);
704 if (!NT_SUCCESS(Status))
705 {
706 DPRINT1("Console inheritance failed\n");
707 goto Quit;
708 }
709
710 /* Return it to the caller */
711 AttachConsoleRequest->Console = TargetProcessData->Console;
712
713 /* Input Wait Handle */
714 AttachConsoleRequest->InputWaitHandle = TargetProcessData->ConsoleEvent;
715
716 /* Set the Property Dialog Handler */
717 TargetProcessData->PropDispatcher = AttachConsoleRequest->PropDispatcher;
718
719 /* Set the Ctrl Dispatcher */
720 TargetProcessData->CtrlDispatcher = AttachConsoleRequest->CtrlDispatcher;
721
722 Status = STATUS_SUCCESS;
723
724 Quit:
725 /* Unlock the "source" process and exit */
726 CsrUnlockProcess(SourceProcess);
727 return Status;
728 }
729
730 CSR_API(SrvFreeConsole)
731 {
732 DPRINT1("SrvFreeConsole\n");
733 ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
734 return STATUS_SUCCESS;
735 }
736
737 CSR_API(SrvSetConsoleMode)
738 {
739 #define CONSOLE_INPUT_MODE_VALID ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
740 ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
741 ENABLE_MOUSE_INPUT | \
742 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS )
743 #define CONSOLE_OUTPUT_MODE_VALID ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
744
745 NTSTATUS Status;
746 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
747 Object_t* Object = NULL;
748
749 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
750 ConsoleModeRequest->ConsoleHandle,
751 &Object, NULL, GENERIC_WRITE, TRUE, 0);
752 if (!NT_SUCCESS(Status)) return Status;
753
754 Status = STATUS_SUCCESS;
755
756 if (CONIO_INPUT_BUFFER_MAGIC == Object->Type)
757 {
758 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
759 InputBuffer->Mode = ConsoleModeRequest->ConsoleMode & CONSOLE_INPUT_MODE_VALID;
760 }
761 else if (CONIO_SCREEN_BUFFER_MAGIC == Object->Type)
762 {
763 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
764 Buffer->Mode = ConsoleModeRequest->ConsoleMode & CONSOLE_OUTPUT_MODE_VALID;
765 }
766 else
767 {
768 Status = STATUS_INVALID_HANDLE;
769 }
770
771 ConSrvReleaseObject(Object, TRUE);
772
773 return Status;
774 }
775
776 CSR_API(SrvGetConsoleMode)
777 {
778 NTSTATUS Status;
779 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
780 Object_t* Object = NULL;
781
782 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
783 ConsoleModeRequest->ConsoleHandle,
784 &Object, NULL, GENERIC_READ, TRUE, 0);
785 if (!NT_SUCCESS(Status)) return Status;
786
787 Status = STATUS_SUCCESS;
788
789 if (CONIO_INPUT_BUFFER_MAGIC == Object->Type)
790 {
791 PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
792 ConsoleModeRequest->ConsoleMode = InputBuffer->Mode;
793 }
794 else if (CONIO_SCREEN_BUFFER_MAGIC == Object->Type)
795 {
796 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
797 ConsoleModeRequest->ConsoleMode = Buffer->Mode;
798 }
799 else
800 {
801 Status = STATUS_INVALID_HANDLE;
802 }
803
804 ConSrvReleaseObject(Object, TRUE);
805
806 return Status;
807 }
808
809 CSR_API(SrvSetConsoleTitle)
810 {
811 NTSTATUS Status;
812 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
813 // PCSR_PROCESS Process = CsrGetClientThread()->Process;
814 PCONSOLE Console;
815 PWCHAR Buffer;
816
817 if (!CsrValidateMessageBuffer(ApiMessage,
818 (PVOID)&TitleRequest->Title,
819 TitleRequest->Length,
820 sizeof(BYTE)))
821 {
822 return STATUS_INVALID_PARAMETER;
823 }
824
825 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
826 if (!NT_SUCCESS(Status))
827 {
828 DPRINT1("Can't get console\n");
829 return Status;
830 }
831
832 /* Allocate a new buffer to hold the new title (NULL-terminated) */
833 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, TitleRequest->Length + sizeof(WCHAR));
834 if (Buffer)
835 {
836 /* Free the old title */
837 RtlFreeUnicodeString(&Console->Title);
838
839 /* Copy title to console */
840 Console->Title.Buffer = Buffer;
841 Console->Title.Length = TitleRequest->Length;
842 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
843 RtlCopyMemory(Console->Title.Buffer,
844 TitleRequest->Title,
845 Console->Title.Length);
846 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
847
848 ConioChangeTitle(Console);
849 Status = STATUS_SUCCESS;
850 }
851 else
852 {
853 Status = STATUS_NO_MEMORY;
854 }
855
856 ConSrvReleaseConsole(Console, TRUE);
857 return Status;
858 }
859
860 CSR_API(SrvGetConsoleTitle)
861 {
862 NTSTATUS Status;
863 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
864 // PCSR_PROCESS Process = CsrGetClientThread()->Process;
865 PCONSOLE Console;
866 DWORD Length;
867
868 if (!CsrValidateMessageBuffer(ApiMessage,
869 (PVOID)&TitleRequest->Title,
870 TitleRequest->Length,
871 sizeof(BYTE)))
872 {
873 return STATUS_INVALID_PARAMETER;
874 }
875
876 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
877 if (!NT_SUCCESS(Status))
878 {
879 DPRINT1("Can't get console\n");
880 return Status;
881 }
882
883 /* Copy title of the console to the user title buffer */
884 if (TitleRequest->Length >= sizeof(WCHAR))
885 {
886 Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
887 memcpy(TitleRequest->Title, Console->Title.Buffer, Length);
888 TitleRequest->Title[Length / sizeof(WCHAR)] = L'\0';
889 }
890
891 TitleRequest->Length = Console->Title.Length;
892
893 ConSrvReleaseConsole(Console, TRUE);
894 return STATUS_SUCCESS;
895 }
896
897 /**********************************************************************
898 * HardwareStateProperty
899 *
900 * DESCRIPTION
901 * Set/Get the value of the HardwareState and switch
902 * between direct video buffer ouput and GDI windowed
903 * output.
904 * ARGUMENTS
905 * Client hands us a CONSOLE_GETSETHWSTATE object.
906 * We use the same object to Request.
907 * NOTE
908 * ConsoleHwState has the correct size to be compatible
909 * with NT's, but values are not.
910 */
911 static NTSTATUS FASTCALL
912 SetConsoleHardwareState(PCONSOLE Console, DWORD ConsoleHwState)
913 {
914 DPRINT1("Console Hardware State: %d\n", ConsoleHwState);
915
916 if ((CONSOLE_HARDWARE_STATE_GDI_MANAGED == ConsoleHwState)
917 ||(CONSOLE_HARDWARE_STATE_DIRECT == ConsoleHwState))
918 {
919 if (Console->HardwareState != ConsoleHwState)
920 {
921 /* TODO: implement switching from full screen to windowed mode */
922 /* TODO: or back; now simply store the hardware state */
923 Console->HardwareState = ConsoleHwState;
924 }
925
926 return STATUS_SUCCESS;
927 }
928
929 return STATUS_INVALID_PARAMETER_3; /* Client: (handle, set_get, [mode]) */
930 }
931
932 CSR_API(SrvGetConsoleHardwareState)
933 {
934 NTSTATUS Status;
935 PCONSOLE_GETSETHWSTATE HardwareStateRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HardwareStateRequest;
936 PCONSOLE_SCREEN_BUFFER Buff;
937 PCONSOLE Console;
938
939 Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
940 HardwareStateRequest->OutputHandle,
941 &Buff,
942 GENERIC_READ,
943 TRUE);
944 if (!NT_SUCCESS(Status))
945 {
946 DPRINT1("Failed to get console handle in SrvGetConsoleHardwareState\n");
947 return Status;
948 }
949
950 Console = Buff->Header.Console;
951 HardwareStateRequest->State = Console->HardwareState;
952
953 ConSrvReleaseScreenBuffer(Buff, TRUE);
954
955 return Status;
956 }
957
958 CSR_API(SrvSetConsoleHardwareState)
959 {
960 NTSTATUS Status;
961 PCONSOLE_GETSETHWSTATE HardwareStateRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HardwareStateRequest;
962 PCONSOLE_SCREEN_BUFFER Buff;
963 PCONSOLE Console;
964
965 Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
966 HardwareStateRequest->OutputHandle,
967 &Buff,
968 GENERIC_READ,
969 TRUE);
970 if (!NT_SUCCESS(Status))
971 {
972 DPRINT1("Failed to get console handle in SrvSetConsoleHardwareState\n");
973 return Status;
974 }
975
976 DPRINT("Setting console hardware state.\n");
977 Console = Buff->Header.Console;
978 Status = SetConsoleHardwareState(Console, HardwareStateRequest->State);
979
980 ConSrvReleaseScreenBuffer(Buff, TRUE);
981
982 return Status;
983 }
984
985 CSR_API(SrvGetConsoleWindow)
986 {
987 NTSTATUS Status;
988 PCONSOLE_GETWINDOW GetWindowRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetWindowRequest;
989 PCONSOLE Console;
990
991 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
992 if (!NT_SUCCESS(Status)) return Status;
993
994 GetWindowRequest->WindowHandle = Console->TermIFace.Vtbl->GetConsoleWindowHandle(Console);
995 ConSrvReleaseConsole(Console, TRUE);
996
997 return STATUS_SUCCESS;
998 }
999
1000 CSR_API(SrvSetConsoleIcon)
1001 {
1002 NTSTATUS Status;
1003 PCONSOLE_SETICON SetIconRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetIconRequest;
1004 PCONSOLE Console;
1005
1006 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1007 if (!NT_SUCCESS(Status)) return Status;
1008
1009 Status = (ConioChangeIcon(Console, SetIconRequest->WindowIcon)
1010 ? STATUS_SUCCESS
1011 : STATUS_UNSUCCESSFUL);
1012
1013 ConSrvReleaseConsole(Console, TRUE);
1014
1015 return Status;
1016 }
1017
1018 CSR_API(SrvGetConsoleCP)
1019 {
1020 NTSTATUS Status;
1021 PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleCPRequest;
1022 PCONSOLE Console;
1023
1024 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
1025 ConsoleCPRequest->InputCP ? "Input" : "Output");
1026
1027 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1028 if (!NT_SUCCESS(Status)) return Status;
1029
1030 ConsoleCPRequest->CodePage = (ConsoleCPRequest->InputCP ? Console->CodePage
1031 : Console->OutputCodePage);
1032 ConSrvReleaseConsole(Console, TRUE);
1033 return STATUS_SUCCESS;
1034 }
1035
1036 CSR_API(SrvSetConsoleCP)
1037 {
1038 NTSTATUS Status;
1039 PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleCPRequest;
1040 PCONSOLE Console;
1041
1042 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
1043 ConsoleCPRequest->InputCP ? "Input" : "Output");
1044
1045 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1046 if (!NT_SUCCESS(Status)) return Status;
1047
1048 if (IsValidCodePage(ConsoleCPRequest->CodePage))
1049 {
1050 if (ConsoleCPRequest->InputCP)
1051 Console->CodePage = ConsoleCPRequest->CodePage;
1052 else
1053 Console->OutputCodePage = ConsoleCPRequest->CodePage;
1054
1055 ConSrvReleaseConsole(Console, TRUE);
1056 return STATUS_SUCCESS;
1057 }
1058
1059 ConSrvReleaseConsole(Console, TRUE);
1060 return STATUS_INVALID_PARAMETER;
1061 }
1062
1063 CSR_API(SrvGetConsoleProcessList)
1064 {
1065 NTSTATUS Status;
1066 PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
1067 PDWORD Buffer;
1068 // PCSR_PROCESS Process = CsrGetClientThread()->Process;
1069 PCONSOLE Console;
1070 PCONSOLE_PROCESS_DATA current;
1071 PLIST_ENTRY current_entry;
1072 ULONG nItems = 0;
1073
1074 if (!CsrValidateMessageBuffer(ApiMessage,
1075 (PVOID)&GetProcessListRequest->pProcessIds,
1076 GetProcessListRequest->nMaxIds,
1077 sizeof(DWORD)))
1078 {
1079 return STATUS_INVALID_PARAMETER;
1080 }
1081
1082 Buffer = GetProcessListRequest->pProcessIds;
1083
1084 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1085 if (!NT_SUCCESS(Status)) return Status;
1086
1087 for (current_entry = Console->ProcessList.Flink;
1088 current_entry != &Console->ProcessList;
1089 current_entry = current_entry->Flink)
1090 {
1091 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1092 if (++nItems <= GetProcessListRequest->nMaxIds)
1093 {
1094 *Buffer++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
1095 }
1096 }
1097
1098 ConSrvReleaseConsole(Console, TRUE);
1099
1100 GetProcessListRequest->nProcessIdsTotal = nItems;
1101 return STATUS_SUCCESS;
1102 }
1103
1104 CSR_API(SrvGenerateConsoleCtrlEvent)
1105 {
1106 NTSTATUS Status;
1107 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
1108 PCONSOLE Console;
1109 PCONSOLE_PROCESS_DATA current;
1110 PLIST_ENTRY current_entry;
1111 DWORD Group;
1112
1113 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1114 if (!NT_SUCCESS(Status)) return Status;
1115
1116 Group = GenerateCtrlEventRequest->ProcessGroup;
1117 Status = STATUS_INVALID_PARAMETER;
1118 for (current_entry = Console->ProcessList.Flink;
1119 current_entry != &Console->ProcessList;
1120 current_entry = current_entry->Flink)
1121 {
1122 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1123 if (Group == 0 || current->Process->ProcessGroupId == Group)
1124 {
1125 ConSrvConsoleCtrlEvent(GenerateCtrlEventRequest->Event, current);
1126 Status = STATUS_SUCCESS;
1127 }
1128 }
1129
1130 ConSrvReleaseConsole(Console, TRUE);
1131
1132 return Status;
1133 }
1134
1135 CSR_API(SrvGetConsoleSelectionInfo)
1136 {
1137 NTSTATUS Status;
1138 PCONSOLE_GETSELECTIONINFO GetSelectionInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetSelectionInfoRequest;
1139 PCONSOLE Console;
1140
1141 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1142 if (NT_SUCCESS(Status))
1143 {
1144 memset(&GetSelectionInfoRequest->Info, 0, sizeof(CONSOLE_SELECTION_INFO));
1145 if (Console->Selection.dwFlags != 0)
1146 GetSelectionInfoRequest->Info = Console->Selection;
1147 ConSrvReleaseConsole(Console, TRUE);
1148 }
1149
1150 return Status;
1151 }
1152
1153 /* EOF */