[CONSRV] Keep a count of input events in the console queue + code simplifications.
[reactos.git] / win32ss / user / winsrv / consrv / console.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/consrv/console.c
5 * PURPOSE: Console Management Functions
6 * PROGRAMMERS: Gé van Geldorp
7 * Jeffrey Morlan
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include "consrv.h"
14
15 /* This is for COM usage */
16 #define COBJMACROS
17 #include <shlobj.h>
18
19
20 #include <alias.h>
21 #include <history.h>
22 #include "procinit.h"
23
24 #define NDEBUG
25 #include <debug.h>
26
27
28 /* GLOBALS ********************************************************************/
29
30 /* The list of the ConSrv consoles */
31 static ULONG ConsoleListSize;
32 static PCONSRV_CONSOLE* ConsoleList;
33 static RTL_RESOURCE ListLock;
34
35 #define ConSrvLockConsoleListExclusive() \
36 RtlAcquireResourceExclusive(&ListLock, TRUE)
37
38 #define ConSrvLockConsoleListShared() \
39 RtlAcquireResourceShared(&ListLock, TRUE)
40
41 #define ConSrvUnlockConsoleList() \
42 RtlReleaseResource(&ListLock)
43
44
45 static NTSTATUS
46 InsertConsole(OUT PHANDLE Handle,
47 IN PCONSRV_CONSOLE Console)
48 {
49 #define CONSOLE_HANDLES_INCREMENT 2 * 3
50
51 NTSTATUS Status = STATUS_SUCCESS;
52 ULONG i = 0;
53 PCONSRV_CONSOLE* Block;
54
55 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
56 (ConsoleList != NULL && ConsoleListSize != 0) );
57
58 /* All went right, so add the console to the list */
59 ConSrvLockConsoleListExclusive();
60 DPRINT("Insert in the list\n");
61
62 if (ConsoleList)
63 {
64 for (i = 0; i < ConsoleListSize; i++)
65 {
66 if (ConsoleList[i] == NULL) break;
67 }
68 }
69
70 if (i >= ConsoleListSize)
71 {
72 DPRINT("Creation of a new handles table\n");
73 /* Allocate a new handles table */
74 Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
75 (ConsoleListSize +
76 CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSRV_CONSOLE));
77 if (Block == NULL)
78 {
79 Status = STATUS_UNSUCCESSFUL;
80 goto Quit;
81 }
82
83 /* If we previously had a handles table, free it and use the new one */
84 if (ConsoleList)
85 {
86 /* Copy the handles from the old table to the new one */
87 RtlCopyMemory(Block,
88 ConsoleList,
89 ConsoleListSize * sizeof(PCONSRV_CONSOLE));
90 ConsoleFreeHeap(ConsoleList);
91 }
92 ConsoleList = Block;
93 ConsoleListSize += CONSOLE_HANDLES_INCREMENT;
94 }
95
96 ConsoleList[i] = Console;
97 *Handle = ULongToHandle((i << 2) | 0x3);
98
99 Quit:
100 /* Unlock the console list and return status */
101 ConSrvUnlockConsoleList();
102 return Status;
103 }
104
105 /* Unused */
106 #if 0
107 static NTSTATUS
108 RemoveConsoleByHandle(IN HANDLE Handle)
109 {
110 NTSTATUS Status = STATUS_SUCCESS;
111 PCONSRV_CONSOLE Console;
112
113 BOOLEAN ValidHandle = ((HandleToULong(Handle) & 0x3) == 0x3);
114 ULONG Index = HandleToULong(Handle) >> 2;
115
116 if (!ValidHandle) return STATUS_INVALID_HANDLE;
117
118 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
119 (ConsoleList != NULL && ConsoleListSize != 0) );
120
121 /* Remove the console from the list */
122 ConSrvLockConsoleListExclusive();
123
124 if (Index >= ConsoleListSize ||
125 (Console = ConsoleList[Index]) == NULL)
126 {
127 Status = STATUS_INVALID_HANDLE;
128 goto Quit;
129 }
130
131 ConsoleList[Index] = NULL;
132
133 Quit:
134 /* Unlock the console list and return status */
135 ConSrvUnlockConsoleList();
136 return Status;
137 }
138 #endif
139
140 static NTSTATUS
141 RemoveConsoleByPointer(IN PCONSRV_CONSOLE Console)
142 {
143 ULONG i = 0;
144
145 if (!Console) return STATUS_INVALID_PARAMETER;
146
147 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
148 (ConsoleList != NULL && ConsoleListSize != 0) );
149
150 /* Remove the console from the list */
151 ConSrvLockConsoleListExclusive();
152
153 if (ConsoleList)
154 {
155 for (i = 0; i < ConsoleListSize; i++)
156 {
157 if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
158 }
159 }
160
161 /* Unlock the console list and return */
162 ConSrvUnlockConsoleList();
163 return STATUS_SUCCESS;
164 }
165
166 BOOLEAN NTAPI
167 ConSrvValidateConsole(OUT PCONSRV_CONSOLE* Console,
168 IN HANDLE ConsoleHandle,
169 IN CONSOLE_STATE ExpectedState,
170 IN BOOLEAN LockConsole)
171 {
172 BOOLEAN RetVal = FALSE;
173 PCONSRV_CONSOLE ValidatedConsole;
174
175 BOOLEAN ValidHandle = ((HandleToULong(ConsoleHandle) & 0x3) == 0x3);
176 ULONG Index = HandleToULong(ConsoleHandle) >> 2;
177
178 if (!ValidHandle) return FALSE;
179
180 if (!Console) return FALSE;
181 *Console = NULL;
182
183 /*
184 * Forbid creation or deletion of consoles when
185 * checking for the existence of a console.
186 */
187 ConSrvLockConsoleListShared();
188
189 if (Index >= ConsoleListSize ||
190 (ValidatedConsole = ConsoleList[Index]) == NULL)
191 {
192 /* Unlock the console list and return */
193 ConSrvUnlockConsoleList();
194 return FALSE;
195 }
196
197 ValidatedConsole = ConsoleList[Index];
198
199 /* Unlock the console list and return */
200 ConSrvUnlockConsoleList();
201
202 RetVal = ConDrvValidateConsoleUnsafe((PCONSOLE)ValidatedConsole,
203 ExpectedState,
204 LockConsole);
205 if (RetVal) *Console = ValidatedConsole;
206
207 return RetVal;
208 }
209
210
211 /* PRIVATE FUNCTIONS **********************************************************/
212
213 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
214 static BOOLEAN
215 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,
216 IN PCWSTR Source)
217 {
218 SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR);
219 if (Size > MAXUSHORT) return FALSE;
220
221 UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size);
222 if (UniDest->Buffer == NULL) return FALSE;
223
224 RtlCopyMemory(UniDest->Buffer, Source, Size);
225 UniDest->MaximumLength = (USHORT)Size;
226 UniDest->Length = (USHORT)Size - sizeof(WCHAR);
227
228 return TRUE;
229 }
230
231 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
232 static VOID
233 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
234 {
235 if (UnicodeString->Buffer)
236 {
237 ConsoleFreeHeap(UnicodeString->Buffer);
238 RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
239 }
240 }
241
242 VOID
243 ConioPause(PCONSRV_CONSOLE Console, UINT Flags)
244 {
245 Console->PauseFlags |= Flags;
246 ConDrvPause((PCONSOLE)Console);
247 }
248
249 VOID
250 ConioUnpause(PCONSRV_CONSOLE Console, UINT Flags)
251 {
252 Console->PauseFlags &= ~Flags;
253
254 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
255 if (Console->PauseFlags == 0)
256 {
257 ConDrvUnpause((PCONSOLE)Console);
258
259 CsrNotifyWait(&Console->WriteWaitQueue,
260 TRUE,
261 NULL,
262 NULL);
263 if (!IsListEmpty(&Console->WriteWaitQueue))
264 {
265 CsrDereferenceWait(&Console->WriteWaitQueue);
266 }
267 }
268 }
269
270 NTSTATUS
271 ConSrvGetConsole(IN PCONSOLE_PROCESS_DATA ProcessData,
272 OUT PCONSRV_CONSOLE* Console,
273 IN BOOLEAN LockConsole)
274 {
275 NTSTATUS Status = STATUS_INVALID_HANDLE;
276 PCONSRV_CONSOLE GrabConsole;
277
278 // if (Console == NULL) return STATUS_INVALID_PARAMETER;
279 ASSERT(Console);
280 *Console = NULL;
281
282 if (ConSrvValidateConsole(&GrabConsole,
283 ProcessData->ConsoleHandle,
284 CONSOLE_RUNNING,
285 LockConsole))
286 {
287 _InterlockedIncrement(&GrabConsole->ReferenceCount);
288 *Console = GrabConsole;
289 Status = STATUS_SUCCESS;
290 }
291
292 return Status;
293 }
294
295 VOID
296 ConSrvReleaseConsole(IN PCONSRV_CONSOLE Console,
297 IN BOOLEAN IsConsoleLocked)
298 {
299 LONG RefCount = 0;
300
301 if (!Console) return;
302 // if (Console->ReferenceCount == 0) return; // This shouldn't happen
303 ASSERT(Console->ReferenceCount > 0);
304
305 /* The console must be locked */
306 // ASSERT(Console_locked);
307
308 /*
309 * Decrement the reference count. Save the new value too,
310 * because Console->ReferenceCount might be modified after
311 * the console gets unlocked but before we check whether we
312 * can destroy it.
313 */
314 RefCount = _InterlockedDecrement(&Console->ReferenceCount);
315
316 /* Unlock the console if needed */
317 if (IsConsoleLocked) LeaveCriticalSection(&Console->Lock);
318
319 /* Delete the console if needed */
320 if (RefCount <= 0) ConSrvDeleteConsole(Console);
321 }
322
323
324 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
325
326 VOID NTAPI
327 ConSrvInitConsoleSupport(VOID)
328 {
329 DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
330
331 /* Initialize the console list and its lock */
332 ConsoleListSize = 0;
333 ConsoleList = NULL;
334 RtlInitializeResource(&ListLock);
335
336 /* Should call LoadKeyboardLayout */
337 }
338
339 NTSTATUS NTAPI
340 ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
341 IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
342 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
343 IN HANDLE ConsoleLeaderProcessHandle);
344 NTSTATUS NTAPI
345 ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal);
346
347
348 static BOOL
349 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
350 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo)
351 {
352 #define PATH_SEPARATOR L'\\'
353
354 BOOL RetVal = FALSE;
355 HRESULT hRes = S_OK;
356 SIZE_T Length = 0;
357 LPWSTR LinkName = NULL;
358 LPWSTR IconPath = NULL;
359 WCHAR Buffer[MAX_PATH + 1];
360
361 ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
362
363 if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
364 {
365 // return FALSE; // FIXME!! (for icon loading)
366 RetVal = TRUE;
367 goto Finish;
368 }
369
370 /* 1- Find the last path separator if any */
371 LinkName = wcsrchr(ConsoleInfo->ConsoleTitle, PATH_SEPARATOR);
372 if (LinkName == NULL)
373 LinkName = ConsoleInfo->ConsoleTitle;
374 else
375 ++LinkName; // Skip the path separator
376
377 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
378 Length = wcslen(LinkName);
379 if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
380 return FALSE;
381
382 /* 3- It may be a link. Try to retrieve some properties */
383 hRes = CoInitialize(NULL);
384 if (SUCCEEDED(hRes))
385 {
386 /* Get a pointer to the IShellLink interface */
387 IShellLinkW* pshl = NULL;
388 hRes = CoCreateInstance(&CLSID_ShellLink,
389 NULL,
390 CLSCTX_INPROC_SERVER,
391 &IID_IShellLinkW,
392 (LPVOID*)&pshl);
393 if (SUCCEEDED(hRes))
394 {
395 /* Get a pointer to the IPersistFile interface */
396 IPersistFile* ppf = NULL;
397 hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
398 if (SUCCEEDED(hRes))
399 {
400 /* Load the shortcut */
401 hRes = IPersistFile_Load(ppf, ConsoleInfo->ConsoleTitle, STGM_READ);
402 if (SUCCEEDED(hRes))
403 {
404 /*
405 * Finally we can get the properties !
406 * Update the old ones if needed.
407 */
408 INT ShowCmd = 0;
409 // WORD HotKey = 0;
410
411 /* Reset the name of the console with the name of the shortcut */
412 Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
413 (ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR));
414 wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
415 ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL;
416
417 /* Get the window showing command */
418 hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
419 if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->wShowWindow = (WORD)ShowCmd;
420
421 /* Get the hotkey */
422 // hRes = pshl->GetHotkey(&ShowCmd);
423 // if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->HotKey = HotKey;
424
425 /* Get the icon location, if any */
426 hRes = IShellLinkW_GetIconLocation(pshl,
427 Buffer,
428 sizeof(Buffer)/sizeof(Buffer[0]) - 1, // == MAX_PATH
429 &ConsoleInitInfo->ConsoleStartInfo->IconIndex);
430 if (!SUCCEEDED(hRes))
431 {
432 ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
433 }
434 else
435 {
436 IconPath = Buffer;
437 }
438
439 // FIXME: Since we still don't load console properties from the shortcut,
440 // return false. When this will be done, we will return true instead.
441 RetVal = TRUE; // FALSE;
442 }
443 IPersistFile_Release(ppf);
444 }
445 IShellLinkW_Release(pshl);
446 }
447 }
448 CoUninitialize();
449
450 Finish:
451
452 if (RetVal)
453 {
454 /* Get the associated icon, if any */
455 if (IconPath == NULL)
456 {
457 // Question: How to retrieve the full path name
458 // of the app we are going to run??
459 Length = RtlDosSearchPath_U(ConsoleInitInfo->CurDir,
460 ConsoleInitInfo->AppName,
461 NULL,
462 sizeof(Buffer),
463 Buffer,
464 NULL);
465 if (Length > 0 && Length < sizeof(Buffer))
466 IconPath = Buffer;
467 else
468 IconPath = ConsoleInitInfo->AppName;
469
470 // ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
471 }
472 DPRINT("IconPath = '%S' ; IconIndex = %lu\n",
473 IconPath, ConsoleInitInfo->ConsoleStartInfo->IconIndex);
474 if (IconPath && *IconPath)
475 {
476 HICON hIcon = NULL, hIconSm = NULL;
477 /*
478 * FIXME!! Because of a strange bug we have in PrivateExtractIconExW
479 * (see r65683 for more details), we cannot use this API to extract
480 * at the same time the large and small icons from the app.
481 * Instead we just use PrivateExtractIconsW.
482 *
483 PrivateExtractIconExW(IconPath,
484 ConsoleInitInfo->ConsoleStartInfo->IconIndex,
485 &hIcon,
486 &hIconSm,
487 1);
488 */
489 PrivateExtractIconsW(IconPath,
490 ConsoleInitInfo->ConsoleStartInfo->IconIndex,
491 32, 32,
492 &hIcon, NULL, 1, LR_COPYFROMRESOURCE);
493 PrivateExtractIconsW(IconPath,
494 ConsoleInitInfo->ConsoleStartInfo->IconIndex,
495 16, 16,
496 &hIconSm, NULL, 1, LR_COPYFROMRESOURCE);
497
498 DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
499 if (hIcon != NULL) ConsoleInitInfo->ConsoleStartInfo->hIcon = hIcon;
500 if (hIconSm != NULL) ConsoleInitInfo->ConsoleStartInfo->hIconSm = hIconSm;
501 }
502 }
503
504 // FIXME: See the previous FIXME above.
505 RetVal = FALSE;
506
507 return RetVal;
508 }
509
510 NTSTATUS NTAPI
511 ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,
512 OUT PCONSRV_CONSOLE* NewConsole,
513 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
514 IN PCSR_PROCESS ConsoleLeaderProcess)
515 {
516 NTSTATUS Status;
517 HANDLE ConsoleHandle;
518 PCONSRV_CONSOLE Console;
519
520 BYTE ConsoleInfoBuffer[sizeof(CONSOLE_STATE_INFO) + MAX_PATH * sizeof(WCHAR)]; // CONSRV console information
521 PCONSOLE_STATE_INFO ConsoleInfo = (PCONSOLE_STATE_INFO)&ConsoleInfoBuffer;
522 CONSOLE_INFO DrvConsoleInfo; // Console information for CONDRV
523
524 SIZE_T Length = 0;
525
526 TERMINAL Terminal; /* The ConSrv terminal for this console */
527
528 if (NewConsole == NULL || ConsoleInitInfo == NULL)
529 return STATUS_INVALID_PARAMETER;
530
531 *NewConsole = NULL;
532
533 /*
534 * Load the console settings
535 */
536 RtlZeroMemory(ConsoleInfo, sizeof(ConsoleInfoBuffer));
537 ConsoleInfo->cbSize = sizeof(ConsoleInfoBuffer);
538
539 /* 1. Get the title of the console (initialize ConsoleInfo->ConsoleTitle) */
540 Length = min(ConsoleInitInfo->TitleLength,
541 (ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR));
542 wcsncpy(ConsoleInfo->ConsoleTitle, ConsoleInitInfo->ConsoleTitle, Length);
543 ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL; // NULL-terminate it.
544
545 /* 2. Impersonate the caller in order to retrieve settings in its context */
546 if (!CsrImpersonateClient(NULL))
547 return STATUS_BAD_IMPERSONATION_LEVEL;
548
549 /* 3. Load the default settings */
550 ConCfgGetDefaultSettings(ConsoleInfo);
551
552 /*
553 * 4. Load per-application terminal settings.
554 *
555 * Check whether the process creating the console was launched via
556 * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
557 * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
558 */
559 // if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) // FIXME!! (for icon loading)
560 {
561 if (!LoadShellLinkConsoleInfo(ConsoleInfo, ConsoleInitInfo))
562 {
563 ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
564 }
565 }
566
567 /*
568 * 5. Load the remaining console settings via the registry.
569 */
570 if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
571 {
572 /*
573 * Either we weren't created by an app launched via a shell-link,
574 * or we failed to load shell-link console properties.
575 * Therefore, load the console infos for the application from the registry.
576 */
577 ConCfgReadUserSettings(ConsoleInfo, FALSE);
578
579 /*
580 * Now, update them with the properties the user might gave to us
581 * via the STARTUPINFO structure before calling CreateProcess
582 * (and which was transmitted via the ConsoleStartInfo structure).
583 * We therefore overwrite the values read in the registry.
584 */
585 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
586 {
587 ConsoleInfo->ScreenAttributes = (USHORT)ConsoleInitInfo->ConsoleStartInfo->wFillAttribute;
588 }
589 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
590 {
591 ConsoleInfo->ScreenBufferSize = ConsoleInitInfo->ConsoleStartInfo->dwScreenBufferSize;
592 }
593 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
594 {
595 ConsoleInfo->WindowSize = ConsoleInitInfo->ConsoleStartInfo->dwWindowSize;
596 }
597
598 #if 0
599 /*
600 * Now, update them with the properties the user might gave to us
601 * via the STARTUPINFO structure before calling CreateProcess
602 * (and which was transmitted via the ConsoleStartInfo structure).
603 * We therefore overwrite the values read in the registry.
604 */
605 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
606 {
607 ConsoleInfo->AutoPosition = FALSE;
608 ConsoleInfo->WindowPosition.x = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.X;
609 ConsoleInfo->WindowPosition.y = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.Y;
610 }
611 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
612 {
613 ConsoleInfo->FullScreen = TRUE;
614 }
615 #endif
616 }
617
618 /* 6. Revert impersonation */
619 CsrRevertToSelf();
620
621 /* Set-up the code page */
622 ConsoleInfo->CodePage = GetOEMCP();
623
624 /*
625 * Initialize the ConSrv terminal and give it a chance to load
626 * its own settings and override the console settings.
627 */
628 Status = ConSrvInitTerminal(&Terminal,
629 ConsoleInfo,
630 ConsoleInitInfo,
631 ConsoleLeaderProcess->ProcessHandle);
632 if (!NT_SUCCESS(Status))
633 {
634 DPRINT1("CONSRV: Failed to initialize a terminal, Status = 0x%08lx\n", Status);
635 return Status;
636 }
637 DPRINT("CONSRV: Terminal initialized\n");
638
639 /* Initialize a new console via the driver */
640 // DrvConsoleInfo.InputBufferSize = 0;
641 DrvConsoleInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
642 DrvConsoleInfo.ConsoleSize = ConsoleInfo->WindowSize;
643 DrvConsoleInfo.CursorSize = ConsoleInfo->CursorSize;
644 // DrvConsoleInfo.CursorBlinkOn = ConsoleInfo->CursorBlinkOn;
645 DrvConsoleInfo.ScreenAttrib = ConsoleInfo->ScreenAttributes;
646 DrvConsoleInfo.PopupAttrib = ConsoleInfo->PopupAttributes;
647 DrvConsoleInfo.CodePage = ConsoleInfo->CodePage;
648 Status = ConDrvInitConsole(&Console, &DrvConsoleInfo);
649 if (!NT_SUCCESS(Status))
650 {
651 DPRINT1("Creating a new console failed, Status = 0x%08lx\n", Status);
652 ConSrvDeinitTerminal(&Terminal);
653 return Status;
654 }
655
656 ASSERT(Console);
657 DPRINT("Console initialized\n");
658
659 /*** Register ConSrv features ***/
660
661 /* Initialize the console title */
662 #if 0
663 WCHAR DefaultTitle[128];
664 #endif
665 ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle);
666 #if 0
667 if (ConsoleInfo->ConsoleTitle[0] == UNICODE_NULL)
668 {
669 if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
670 {
671 ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
672 }
673 else
674 {
675 ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
676 }
677 }
678 else
679 {
680 #endif
681 ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle);
682 #if 0
683 }
684 #endif
685
686 /* Initialize process support */
687 InitializeListHead(&Console->ProcessList);
688 Console->NotifiedLastCloseProcess = NULL;
689 Console->NotifyLastClose = FALSE;
690 Console->HasFocus = FALSE;
691
692 /* Initialize pausing support */
693 Console->PauseFlags = 0;
694 InitializeListHead(&Console->ReadWaitQueue);
695 InitializeListHead(&Console->WriteWaitQueue);
696
697 /* Initialize the alias and history buffers */
698 Console->Aliases = NULL;
699 InitializeListHead(&Console->HistoryBuffers);
700 Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize;
701 Console->NumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers;
702 Console->HistoryNoDup = ConsoleInfo->HistoryNoDup;
703
704 /* Initialize the Input Line Discipline */
705 Console->LineBuffer = NULL;
706 Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
707 Console->LineComplete = Console->LineUpPressed = FALSE;
708 // LineWakeupMask
709 Console->LineInsertToggle =
710 Console->InsertMode = ConsoleInfo->InsertMode;
711 Console->QuickEdit = ConsoleInfo->QuickEdit;
712
713 /* Popup windows */
714 InitializeListHead(&Console->PopupWindows);
715
716 /* Colour table */
717 RtlCopyMemory(Console->Colors, ConsoleInfo->ColorTable,
718 sizeof(ConsoleInfo->ColorTable));
719
720 /* Create the Initialization Events */
721 Status = NtCreateEvent(&Console->InitEvents[INIT_SUCCESS], EVENT_ALL_ACCESS,
722 NULL, NotificationEvent, FALSE);
723 if (!NT_SUCCESS(Status))
724 {
725 DPRINT1("NtCreateEvent(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
726 ConDrvDeleteConsole(Console);
727 ConSrvDeinitTerminal(&Terminal);
728 return Status;
729 }
730 Status = NtCreateEvent(&Console->InitEvents[INIT_FAILURE], EVENT_ALL_ACCESS,
731 NULL, NotificationEvent, FALSE);
732 if (!NT_SUCCESS(Status))
733 {
734 DPRINT1("NtCreateEvent(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
735 NtClose(Console->InitEvents[INIT_SUCCESS]);
736 ConDrvDeleteConsole(Console);
737 ConSrvDeinitTerminal(&Terminal);
738 return Status;
739 }
740
741 /*
742 * Attach the ConSrv terminal to the console.
743 * This call makes a copy of our local Terminal variable.
744 */
745 Status = ConDrvAttachTerminal(Console, &Terminal);
746 if (!NT_SUCCESS(Status))
747 {
748 DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
749 NtClose(Console->InitEvents[INIT_FAILURE]);
750 NtClose(Console->InitEvents[INIT_SUCCESS]);
751 ConDrvDeleteConsole(Console);
752 ConSrvDeinitTerminal(&Terminal);
753 return Status;
754 }
755 DPRINT("Terminal attached\n");
756
757 /* All went right, so add the console to the list */
758 Status = InsertConsole(&ConsoleHandle, Console);
759
760 // FIXME! We do not support at all asynchronous console creation!
761 NtSetEvent(Console->InitEvents[INIT_SUCCESS], NULL);
762 // NtSetEvent(Console->InitEvents[INIT_FAILURE], NULL);
763
764 /* Return the newly created console to the caller and a success code too */
765 *NewConsoleHandle = ConsoleHandle;
766 *NewConsole = Console;
767 return STATUS_SUCCESS;
768 }
769
770 VOID NTAPI
771 ConSrvDeleteConsole(PCONSRV_CONSOLE Console)
772 {
773 DPRINT("ConSrvDeleteConsole\n");
774
775 // FIXME: Send a terminate message to all the processes owning this console
776
777 /* Remove the console from the list */
778 RemoveConsoleByPointer(Console);
779
780 /* Destroy the Initialization Events */
781 NtClose(Console->InitEvents[INIT_FAILURE]);
782 NtClose(Console->InitEvents[INIT_SUCCESS]);
783
784 /* Clean the Input Line Discipline */
785 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
786
787 /* Clean aliases and history */
788 IntDeleteAllAliases(Console);
789 HistoryDeleteBuffers(Console);
790
791 /* Free the console title */
792 ConsoleFreeUnicodeString(&Console->OriginalTitle);
793 ConsoleFreeUnicodeString(&Console->Title);
794
795 /* Now, call the driver. ConDrvDetachTerminal is called on-demand. */
796 ConDrvDeleteConsole((PCONSOLE)Console);
797
798 /* Deinit the ConSrv terminal */
799 // FIXME!!
800 // ConSrvDeinitTerminal(&Terminal);
801 }
802
803
804
805
806
807
808 NTSTATUS
809 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
810 IN PCONSOLE_PROCESS_DATA ProcessData,
811 IN ULONG Timeout)
812 {
813 NTSTATUS Status = STATUS_SUCCESS;
814
815 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
816
817 /*
818 * Be sure we effectively have a control routine. It resides in kernel32.dll (client).
819 */
820 if (ProcessData->CtrlRoutine == NULL) return Status;
821
822 _SEH2_TRY
823 {
824 HANDLE Thread = NULL;
825
826 _SEH2_TRY
827 {
828 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
829 ProcessData->CtrlRoutine,
830 UlongToPtr(CtrlEvent), 0, NULL);
831 if (NULL == Thread)
832 {
833 Status = RtlGetLastNtStatus();
834 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
835 }
836 else
837 {
838 DPRINT("ProcessData->CtrlRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n",
839 ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
840 WaitForSingleObject(Thread, Timeout);
841 }
842 }
843 _SEH2_FINALLY
844 {
845 CloseHandle(Thread);
846 }
847 _SEH2_END;
848 }
849 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
850 {
851 Status = _SEH2_GetExceptionCode();
852 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
853 }
854 _SEH2_END;
855
856 return Status;
857 }
858
859 NTSTATUS
860 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
861 IN PCONSOLE_PROCESS_DATA ProcessData)
862 {
863 return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
864 }
865
866 PCONSOLE_PROCESS_DATA NTAPI
867 ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)
868 {
869 if (Console == NULL) return NULL;
870
871 return CONTAINING_RECORD(Console->ProcessList.Blink,
872 CONSOLE_PROCESS_DATA,
873 ConsoleLink);
874 }
875
876 NTSTATUS NTAPI
877 ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,
878 IN OUT PULONG ProcessIdsList,
879 IN ULONG MaxIdListItems,
880 OUT PULONG ProcessIdsTotal)
881 {
882 PCONSOLE_PROCESS_DATA current;
883 PLIST_ENTRY current_entry;
884
885 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
886 return STATUS_INVALID_PARAMETER;
887
888 *ProcessIdsTotal = 0;
889
890 for (current_entry = Console->ProcessList.Flink;
891 current_entry != &Console->ProcessList;
892 current_entry = current_entry->Flink)
893 {
894 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
895 if (++(*ProcessIdsTotal) <= MaxIdListItems)
896 {
897 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
898 }
899 }
900
901 return STATUS_SUCCESS;
902 }
903
904 // ConSrvGenerateConsoleCtrlEvent
905 NTSTATUS NTAPI
906 ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,
907 IN ULONG ProcessGroupId,
908 IN ULONG CtrlEvent)
909 {
910 NTSTATUS Status = STATUS_SUCCESS;
911 PLIST_ENTRY current_entry;
912 PCONSOLE_PROCESS_DATA current;
913
914 /* If the console is already being destroyed, just return */
915 if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
916 return STATUS_UNSUCCESSFUL;
917
918 /*
919 * Loop through the process list, from the most recent process
920 * (the active one) to the oldest one (the first created, i.e.
921 * the console leader process), and for each, send an event
922 * (new processes are inserted at the head of the console process list).
923 */
924 current_entry = Console->ProcessList.Flink;
925 while (current_entry != &Console->ProcessList)
926 {
927 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
928 current_entry = current_entry->Flink;
929
930 /*
931 * Only processes belonging to the same process group are signaled.
932 * If the process group ID is zero, then all the processes are signaled.
933 */
934 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
935 {
936 Status = ConSrvConsoleCtrlEvent(CtrlEvent, current);
937 }
938 }
939
940 return Status;
941 }
942
943 VOID
944 ConSrvSetProcessFocus(IN PCSR_PROCESS CsrProcess,
945 IN BOOLEAN SetForeground)
946 {
947 // FIXME: Call NtUserSetInformationProcess (currently unimplemented!)
948 // for setting Win32 foreground/background flags.
949
950 if (SetForeground)
951 CsrSetForegroundPriority(CsrProcess);
952 else
953 CsrSetBackgroundPriority(CsrProcess);
954 }
955
956 NTSTATUS NTAPI
957 ConSrvSetConsoleProcessFocus(IN PCONSRV_CONSOLE Console,
958 IN BOOLEAN SetForeground)
959 {
960 PLIST_ENTRY current_entry;
961 PCONSOLE_PROCESS_DATA current;
962
963 /* If the console is already being destroyed, just return */
964 if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
965 return STATUS_UNSUCCESSFUL;
966
967 /*
968 * Loop through the process list, from the most recent process
969 * to the oldest one, and for each, set its foreground priority.
970 */
971 current_entry = Console->ProcessList.Flink;
972 while (current_entry != &Console->ProcessList)
973 {
974 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
975 current_entry = current_entry->Flink;
976
977 ConSrvSetProcessFocus(current->Process, SetForeground);
978 }
979
980 return STATUS_SUCCESS;
981 }
982
983
984 /* PUBLIC SERVER APIS *********************************************************/
985
986 /* API_NUMBER: ConsolepAlloc */
987 CSR_API(SrvAllocConsole)
988 {
989 NTSTATUS Status = STATUS_SUCCESS;
990 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
991 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
992 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
993 CONSOLE_INIT_INFO ConsoleInitInfo;
994
995 if (ProcessData->ConsoleHandle != NULL)
996 {
997 DPRINT1("Process already has a console\n");
998 return STATUS_ACCESS_DENIED;
999 }
1000
1001 if ( !CsrValidateMessageBuffer(ApiMessage,
1002 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
1003 1,
1004 sizeof(CONSOLE_START_INFO)) ||
1005 !CsrValidateMessageBuffer(ApiMessage,
1006 (PVOID*)&AllocConsoleRequest->ConsoleTitle,
1007 AllocConsoleRequest->TitleLength,
1008 sizeof(BYTE)) ||
1009 !CsrValidateMessageBuffer(ApiMessage,
1010 (PVOID*)&AllocConsoleRequest->Desktop,
1011 AllocConsoleRequest->DesktopLength,
1012 sizeof(BYTE)) ||
1013 !CsrValidateMessageBuffer(ApiMessage,
1014 (PVOID*)&AllocConsoleRequest->CurDir,
1015 AllocConsoleRequest->CurDirLength,
1016 sizeof(BYTE)) ||
1017 !CsrValidateMessageBuffer(ApiMessage,
1018 (PVOID*)&AllocConsoleRequest->AppName,
1019 AllocConsoleRequest->AppNameLength,
1020 sizeof(BYTE)) )
1021 {
1022 return STATUS_INVALID_PARAMETER;
1023 }
1024
1025 /* Initialize the console initialization info structure */
1026 ConsoleInitInfo.ConsoleStartInfo = AllocConsoleRequest->ConsoleStartInfo;
1027 ConsoleInitInfo.IsWindowVisible = TRUE; // The console window is always visible.
1028 ConsoleInitInfo.TitleLength = AllocConsoleRequest->TitleLength;
1029 ConsoleInitInfo.ConsoleTitle = AllocConsoleRequest->ConsoleTitle;
1030 ConsoleInitInfo.DesktopLength = AllocConsoleRequest->DesktopLength;
1031 ConsoleInitInfo.Desktop = AllocConsoleRequest->Desktop;
1032 ConsoleInitInfo.AppNameLength = AllocConsoleRequest->AppNameLength;
1033 ConsoleInitInfo.AppName = AllocConsoleRequest->AppName;
1034 ConsoleInitInfo.CurDirLength = AllocConsoleRequest->CurDirLength;
1035 ConsoleInitInfo.CurDir = AllocConsoleRequest->CurDir;
1036
1037 /* Initialize a new Console owned by the Console Leader Process */
1038 Status = ConSrvAllocateConsole(ProcessData,
1039 &AllocConsoleRequest->ConsoleStartInfo->InputHandle,
1040 &AllocConsoleRequest->ConsoleStartInfo->OutputHandle,
1041 &AllocConsoleRequest->ConsoleStartInfo->ErrorHandle,
1042 &ConsoleInitInfo);
1043 if (!NT_SUCCESS(Status))
1044 {
1045 DPRINT1("Console allocation failed\n");
1046 return Status;
1047 }
1048
1049 /* Set the Property-Dialog and Control-Dispatcher handlers */
1050 ProcessData->PropRoutine = AllocConsoleRequest->PropRoutine;
1051 ProcessData->CtrlRoutine = AllocConsoleRequest->CtrlRoutine;
1052
1053 return STATUS_SUCCESS;
1054 }
1055
1056 /* API_NUMBER: ConsolepAttach */
1057 CSR_API(SrvAttachConsole)
1058 {
1059 NTSTATUS Status = STATUS_SUCCESS;
1060 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
1061 PCSR_PROCESS SourceProcess = NULL; // The parent process.
1062 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
1063 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
1064 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
1065
1066 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
1067
1068 if (TargetProcessData->ConsoleHandle != NULL)
1069 {
1070 DPRINT1("Process already has a console\n");
1071 return STATUS_ACCESS_DENIED;
1072 }
1073
1074 if (!CsrValidateMessageBuffer(ApiMessage,
1075 (PVOID*)&AttachConsoleRequest->ConsoleStartInfo,
1076 1,
1077 sizeof(CONSOLE_START_INFO)))
1078 {
1079 return STATUS_INVALID_PARAMETER;
1080 }
1081
1082 /* Check whether we try to attach to the parent's console */
1083 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
1084 {
1085 PROCESS_BASIC_INFORMATION ProcessInfo;
1086 ULONG Length = sizeof(ProcessInfo);
1087
1088 /* Get the real parent's PID */
1089
1090 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
1091 ProcessBasicInformation,
1092 &ProcessInfo,
1093 Length, &Length);
1094 if (!NT_SUCCESS(Status))
1095 {
1096 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = 0x%08lx\n", Status);
1097 return Status;
1098 }
1099
1100 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
1101 }
1102
1103 /* Lock the source process via its PID */
1104 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
1105 if (!NT_SUCCESS(Status)) return Status;
1106
1107 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
1108
1109 if (SourceProcessData->ConsoleHandle == NULL)
1110 {
1111 Status = STATUS_INVALID_HANDLE;
1112 goto Quit;
1113 }
1114
1115 /*
1116 * Inherit the console from the parent,
1117 * if any, otherwise return an error.
1118 */
1119 Status = ConSrvInheritConsole(TargetProcessData,
1120 SourceProcessData->ConsoleHandle,
1121 TRUE,
1122 &AttachConsoleRequest->ConsoleStartInfo->InputHandle,
1123 &AttachConsoleRequest->ConsoleStartInfo->OutputHandle,
1124 &AttachConsoleRequest->ConsoleStartInfo->ErrorHandle,
1125 AttachConsoleRequest->ConsoleStartInfo);
1126 if (!NT_SUCCESS(Status))
1127 {
1128 DPRINT1("Console inheritance failed\n");
1129 goto Quit;
1130 }
1131
1132 /* Set the Property-Dialog and Control-Dispatcher handlers */
1133 TargetProcessData->PropRoutine = AttachConsoleRequest->PropRoutine;
1134 TargetProcessData->CtrlRoutine = AttachConsoleRequest->CtrlRoutine;
1135
1136 Status = STATUS_SUCCESS;
1137
1138 Quit:
1139 /* Unlock the "source" process and exit */
1140 CsrUnlockProcess(SourceProcess);
1141 return Status;
1142 }
1143
1144 /* API_NUMBER: ConsolepFree */
1145 CSR_API(SrvFreeConsole)
1146 {
1147 return ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
1148 }
1149
1150 NTSTATUS NTAPI
1151 ConDrvGetConsoleMode(IN PCONSOLE Console,
1152 IN PCONSOLE_IO_OBJECT Object,
1153 OUT PULONG ConsoleMode);
1154 /* API_NUMBER: ConsolepGetMode */
1155 CSR_API(SrvGetConsoleMode)
1156 {
1157 NTSTATUS Status;
1158 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1159 PCONSOLE_IO_OBJECT Object;
1160
1161 PULONG ConsoleMode = &ConsoleModeRequest->Mode;
1162
1163 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1164 ConsoleModeRequest->Handle,
1165 &Object, NULL, GENERIC_READ, TRUE, 0);
1166 if (!NT_SUCCESS(Status)) return Status;
1167
1168 /* Get the standard console modes */
1169 Status = ConDrvGetConsoleMode(Object->Console, Object,
1170 ConsoleMode);
1171 if (NT_SUCCESS(Status))
1172 {
1173 /*
1174 * If getting the console modes succeeds, then retrieve
1175 * the extended CONSRV-specific input modes.
1176 */
1177 if (INPUT_BUFFER == Object->Type)
1178 {
1179 if (Object->Console->InsertMode || Object->Console->QuickEdit)
1180 {
1181 /* Windows also adds ENABLE_EXTENDED_FLAGS, even if it's not documented on MSDN */
1182 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
1183
1184 if (Object->Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
1185 if (Object->Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
1186 }
1187 }
1188 }
1189
1190 ConSrvReleaseObject(Object, TRUE);
1191 return Status;
1192 }
1193
1194 NTSTATUS NTAPI
1195 ConDrvSetConsoleMode(IN PCONSOLE Console,
1196 IN PCONSOLE_IO_OBJECT Object,
1197 IN ULONG ConsoleMode);
1198 /* API_NUMBER: ConsolepSetMode */
1199 CSR_API(SrvSetConsoleMode)
1200 {
1201 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
1202 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
1203 // NOTE: Vista+ ENABLE_AUTO_POSITION is also a control mode.
1204
1205 NTSTATUS Status;
1206 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1207 PCONSOLE_IO_OBJECT Object;
1208
1209 ULONG ConsoleMode = ConsoleModeRequest->Mode;
1210
1211 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1212 ConsoleModeRequest->Handle,
1213 &Object, NULL, GENERIC_WRITE, TRUE, 0);
1214 if (!NT_SUCCESS(Status)) return Status;
1215
1216 /* Set the standard console modes (without the CONSRV-specific input modes) */
1217 ConsoleMode &= ~CONSOLE_VALID_CONTROL_MODES; // Remove CONSRV-specific input modes.
1218 Status = ConDrvSetConsoleMode(Object->Console, Object,
1219 ConsoleMode);
1220 if (NT_SUCCESS(Status))
1221 {
1222 /*
1223 * If setting the console modes succeeds, then set
1224 * the extended CONSRV-specific input modes.
1225 */
1226 if (INPUT_BUFFER == Object->Type)
1227 {
1228 ConsoleMode = ConsoleModeRequest->Mode;
1229
1230 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
1231 {
1232 /*
1233 * If we use control mode flags without ENABLE_EXTENDED_FLAGS,
1234 * then consider the flags invalid.
1235 */
1236 if ((ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0)
1237 {
1238 Status = STATUS_INVALID_PARAMETER;
1239 }
1240 else
1241 {
1242 Object->Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
1243 Object->Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
1244 }
1245 }
1246 }
1247 }
1248
1249 ConSrvReleaseObject(Object, TRUE);
1250 return Status;
1251 }
1252
1253 /* API_NUMBER: ConsolepGetTitle */
1254 CSR_API(SrvGetConsoleTitle)
1255 {
1256 NTSTATUS Status;
1257 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1258 PCONSRV_CONSOLE Console;
1259 ULONG Length;
1260
1261 if (!CsrValidateMessageBuffer(ApiMessage,
1262 (PVOID)&TitleRequest->Title,
1263 TitleRequest->Length,
1264 sizeof(BYTE)))
1265 {
1266 return STATUS_INVALID_PARAMETER;
1267 }
1268
1269 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1270 if (!NT_SUCCESS(Status))
1271 {
1272 DPRINT1("Can't get console, status %lx\n", Status);
1273 return Status;
1274 }
1275
1276 /* Copy title of the console to the user title buffer */
1277 if (TitleRequest->Unicode)
1278 {
1279 if (TitleRequest->Length >= sizeof(WCHAR))
1280 {
1281 Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
1282 RtlCopyMemory(TitleRequest->Title, Console->Title.Buffer, Length);
1283 ((PWCHAR)TitleRequest->Title)[Length / sizeof(WCHAR)] = UNICODE_NULL;
1284 TitleRequest->Length = Length;
1285 }
1286 else
1287 {
1288 TitleRequest->Length = Console->Title.Length;
1289 }
1290 }
1291 else
1292 {
1293 if (TitleRequest->Length >= sizeof(CHAR))
1294 {
1295 Length = min(TitleRequest->Length - sizeof(CHAR), Console->Title.Length / sizeof(WCHAR));
1296 Length = WideCharToMultiByte(Console->InputCodePage, 0,
1297 Console->Title.Buffer, Length,
1298 TitleRequest->Title, Length,
1299 NULL, NULL);
1300 ((PCHAR)TitleRequest->Title)[Length] = ANSI_NULL;
1301 TitleRequest->Length = Length;
1302 }
1303 else
1304 {
1305 TitleRequest->Length = Console->Title.Length / sizeof(WCHAR);
1306 }
1307 }
1308
1309 ConSrvReleaseConsole(Console, TRUE);
1310 return Status;
1311 }
1312
1313 /* API_NUMBER: ConsolepSetTitle */
1314 CSR_API(SrvSetConsoleTitle)
1315 {
1316 NTSTATUS Status;
1317 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1318 PCONSRV_CONSOLE Console;
1319
1320 PWCHAR Buffer;
1321 ULONG Length;
1322
1323 if (!CsrValidateMessageBuffer(ApiMessage,
1324 (PVOID)&TitleRequest->Title,
1325 TitleRequest->Length,
1326 sizeof(BYTE)))
1327 {
1328 return STATUS_INVALID_PARAMETER;
1329 }
1330
1331 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1332 if (!NT_SUCCESS(Status))
1333 {
1334 DPRINT1("Can't get console, status %lx\n", Status);
1335 return Status;
1336 }
1337
1338 if (TitleRequest->Unicode)
1339 {
1340 /* Length is in bytes */
1341 Length = TitleRequest->Length;
1342 }
1343 else
1344 {
1345 /* Use the console input CP for the conversion */
1346 Length = MultiByteToWideChar(Console->InputCodePage, 0,
1347 TitleRequest->Title, TitleRequest->Length,
1348 NULL, 0);
1349 /* The returned Length was in number of wchars, convert it in bytes */
1350 Length *= sizeof(WCHAR);
1351 }
1352
1353 /* Allocate a new buffer to hold the new title (NULL-terminated) */
1354 Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Length + sizeof(WCHAR));
1355 if (!Buffer)
1356 {
1357 Status = STATUS_NO_MEMORY;
1358 goto Quit;
1359 }
1360
1361 /* Free the old title */
1362 ConsoleFreeUnicodeString(&Console->Title);
1363
1364 /* Copy title to console */
1365 Console->Title.Buffer = Buffer;
1366 Console->Title.Length = Length;
1367 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
1368
1369 if (TitleRequest->Unicode)
1370 {
1371 RtlCopyMemory(Console->Title.Buffer, TitleRequest->Title, Console->Title.Length);
1372 }
1373 else
1374 {
1375 MultiByteToWideChar(Console->InputCodePage, 0,
1376 TitleRequest->Title, TitleRequest->Length,
1377 Console->Title.Buffer,
1378 Console->Title.Length / sizeof(WCHAR));
1379 }
1380
1381 /* NULL-terminate */
1382 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = UNICODE_NULL;
1383
1384 TermChangeTitle(Console);
1385 Status = STATUS_SUCCESS;
1386
1387 Quit:
1388 ConSrvReleaseConsole(Console, TRUE);
1389 return Status;
1390 }
1391
1392 NTSTATUS NTAPI
1393 ConDrvGetConsoleCP(IN PCONSOLE Console,
1394 OUT PUINT CodePage,
1395 IN BOOLEAN OutputCP);
1396 /* API_NUMBER: ConsolepGetCP */
1397 CSR_API(SrvGetConsoleCP)
1398 {
1399 NTSTATUS Status;
1400 PCONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleCPRequest;
1401 PCONSRV_CONSOLE Console;
1402
1403 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
1404 GetConsoleCPRequest->OutputCP ? "Output" : "Input");
1405
1406 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1407 if (!NT_SUCCESS(Status)) return Status;
1408
1409 Status = ConDrvGetConsoleCP(Console,
1410 &GetConsoleCPRequest->CodePage,
1411 GetConsoleCPRequest->OutputCP);
1412
1413 ConSrvReleaseConsole(Console, TRUE);
1414 return Status;
1415 }
1416
1417 NTSTATUS NTAPI
1418 ConDrvSetConsoleCP(IN PCONSOLE Console,
1419 IN UINT CodePage,
1420 IN BOOLEAN OutputCP);
1421 /* API_NUMBER: ConsolepSetCP */
1422 CSR_API(SrvSetConsoleCP)
1423 {
1424 NTSTATUS Status = STATUS_INVALID_PARAMETER;
1425 PCONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetConsoleCPRequest;
1426 PCONSRV_CONSOLE Console;
1427
1428 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
1429 SetConsoleCPRequest->OutputCP ? "Output" : "Input");
1430
1431 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1432 if (!NT_SUCCESS(Status)) return Status;
1433
1434 Status = ConDrvSetConsoleCP(Console,
1435 SetConsoleCPRequest->CodePage,
1436 SetConsoleCPRequest->OutputCP);
1437
1438 ConSrvReleaseConsole(Console, TRUE);
1439 return Status;
1440 }
1441
1442 /* API_NUMBER: ConsolepGetProcessList */
1443 CSR_API(SrvGetConsoleProcessList)
1444 {
1445 NTSTATUS Status;
1446 PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
1447 PCONSRV_CONSOLE Console;
1448
1449 if (!CsrValidateMessageBuffer(ApiMessage,
1450 (PVOID)&GetProcessListRequest->ProcessIdsList,
1451 GetProcessListRequest->ProcessCount,
1452 sizeof(DWORD)))
1453 {
1454 return STATUS_INVALID_PARAMETER;
1455 }
1456
1457 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1458 if (!NT_SUCCESS(Status)) return Status;
1459
1460 Status = ConSrvGetConsoleProcessList(Console,
1461 GetProcessListRequest->ProcessIdsList,
1462 GetProcessListRequest->ProcessCount,
1463 &GetProcessListRequest->ProcessCount);
1464
1465 ConSrvReleaseConsole(Console, TRUE);
1466 return Status;
1467 }
1468
1469 /* API_NUMBER: ConsolepGenerateCtrlEvent */
1470 CSR_API(SrvGenerateConsoleCtrlEvent)
1471 {
1472 NTSTATUS Status;
1473 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
1474 PCONSRV_CONSOLE Console;
1475
1476 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1477 if (!NT_SUCCESS(Status)) return Status;
1478
1479 Status = ConSrvConsoleProcessCtrlEvent(Console,
1480 GenerateCtrlEventRequest->ProcessGroupId,
1481 GenerateCtrlEventRequest->CtrlEvent);
1482
1483 ConSrvReleaseConsole(Console, TRUE);
1484 return Status;
1485 }
1486
1487 /* API_NUMBER: ConsolepNotifyLastClose */
1488 CSR_API(SrvConsoleNotifyLastClose)
1489 {
1490 NTSTATUS Status;
1491 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1492 PCONSRV_CONSOLE Console;
1493
1494 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1495 if (!NT_SUCCESS(Status)) return Status;
1496
1497 /* Only one process is allowed to be registered for last close notification */
1498 if (!Console->NotifyLastClose)
1499 {
1500 Console->NotifyLastClose = TRUE;
1501 Console->NotifiedLastCloseProcess = ProcessData;
1502 Status = STATUS_SUCCESS;
1503 }
1504 else
1505 {
1506 Status = STATUS_ACCESS_DENIED;
1507 }
1508
1509 ConSrvReleaseConsole(Console, TRUE);
1510 return Status;
1511 }
1512
1513 /* API_NUMBER: ConsolepGetMouseInfo */
1514 CSR_API(SrvGetConsoleMouseInfo)
1515 {
1516 NTSTATUS Status;
1517 PCONSOLE_GETMOUSEINFO GetMouseInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetMouseInfoRequest;
1518 PCONSRV_CONSOLE Console;
1519
1520 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1521 if (!NT_SUCCESS(Status)) return Status;
1522
1523 /* Just retrieve the number of buttons of the mouse attached to this console */
1524 GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
1525
1526 ConSrvReleaseConsole(Console, TRUE);
1527 return STATUS_SUCCESS;
1528 }
1529
1530 /* API_NUMBER: ConsolepSetKeyShortcuts */
1531 CSR_API(SrvSetConsoleKeyShortcuts)
1532 {
1533 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1534 return STATUS_NOT_IMPLEMENTED;
1535 }
1536
1537 /* API_NUMBER: ConsolepGetKeyboardLayoutName */
1538 CSR_API(SrvGetConsoleKeyboardLayoutName)
1539 {
1540 NTSTATUS Status;
1541 PCONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetKbdLayoutNameRequest;
1542 PCONSRV_CONSOLE Console;
1543
1544 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1545 if (!NT_SUCCESS(Status)) return Status;
1546
1547 /* Retrieve the keyboard layout name of the system */
1548 if (GetKbdLayoutNameRequest->Ansi)
1549 GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1550 else
1551 GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1552
1553 ConSrvReleaseConsole(Console, TRUE);
1554 return STATUS_SUCCESS;
1555 }
1556
1557 /* API_NUMBER: ConsolepCharType */
1558 CSR_API(SrvGetConsoleCharType)
1559 {
1560 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1561 return STATUS_NOT_IMPLEMENTED;
1562 }
1563
1564 /* API_NUMBER: ConsolepSetLocalEUDC */
1565 CSR_API(SrvSetConsoleLocalEUDC)
1566 {
1567 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1568 return STATUS_NOT_IMPLEMENTED;
1569 }
1570
1571 /* API_NUMBER: ConsolepSetCursorMode */
1572 CSR_API(SrvSetConsoleCursorMode)
1573 {
1574 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1575 return STATUS_NOT_IMPLEMENTED;
1576 }
1577
1578 /* API_NUMBER: ConsolepGetCursorMode */
1579 CSR_API(SrvGetConsoleCursorMode)
1580 {
1581 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1582 return STATUS_NOT_IMPLEMENTED;
1583 }
1584
1585 /* API_NUMBER: ConsolepGetNlsMode */
1586 CSR_API(SrvGetConsoleNlsMode)
1587 {
1588 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1589 return STATUS_NOT_IMPLEMENTED;
1590 }
1591
1592 /* API_NUMBER: ConsolepSetNlsMode */
1593 CSR_API(SrvSetConsoleNlsMode)
1594 {
1595 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1596 return STATUS_NOT_IMPLEMENTED;
1597 }
1598
1599 /* API_NUMBER: ConsolepGetLangId */
1600 CSR_API(SrvGetConsoleLangId)
1601 {
1602 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1603 return STATUS_NOT_IMPLEMENTED;
1604 }
1605
1606 /* EOF */