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 #include "../concfg/font.h"
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 // static ULONG CurrentConsoleID = 0;
31
32 /* The list of the ConSrv consoles */
33 static ULONG ConsoleListSize;
34 static PCONSRV_CONSOLE* ConsoleList;
35 // static LIST_ENTRY ConDrvConsoleList;
36 static RTL_RESOURCE ListLock;
37
38 #define ConSrvLockConsoleListExclusive() \
39 RtlAcquireResourceExclusive(&ListLock, TRUE)
40
41 #define ConSrvLockConsoleListShared() \
42 RtlAcquireResourceShared(&ListLock, TRUE)
43
44 #define ConSrvUnlockConsoleList() \
45 RtlReleaseResource(&ListLock)
46
47 #if 0
48 static NTSTATUS
49 ConDrvInsertConsole(
50 IN PCONSOLE Console)
51 {
52 ASSERT(Console);
53
54 /* All went right, so add the console to the list */
55 ConSrvLockConsoleListExclusive();
56
57 DPRINT("Insert in the list\n");
58 InsertTailList(&ConDrvConsoleList, &Console->ListEntry);
59
60 // FIXME: Move this code to the caller function!!
61 /* Get a new console ID */
62 _InterlockedExchange((PLONG)&Console->ConsoleID, CurrentConsoleID);
63 _InterlockedIncrement((PLONG)&CurrentConsoleID);
64
65 /* Unlock the console list and return success */
66 ConSrvUnlockConsoleList();
67 return STATUS_SUCCESS;
68 }
69 #endif
70
71 static NTSTATUS
InsertConsole(OUT PHANDLE Handle,IN PCONSRV_CONSOLE Console)72 InsertConsole(
73 OUT PHANDLE Handle,
74 IN PCONSRV_CONSOLE Console)
75 {
76 #define CONSOLE_HANDLES_INCREMENT 2 * 3
77
78 NTSTATUS Status = STATUS_SUCCESS;
79 ULONG i = 0;
80 PCONSRV_CONSOLE* Block;
81
82 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
83 (ConsoleList != NULL && ConsoleListSize != 0) );
84
85 /* All went right, so add the console to the list */
86 ConSrvLockConsoleListExclusive();
87 DPRINT("Insert in the list\n");
88
89 if (ConsoleList)
90 {
91 for (i = 0; i < ConsoleListSize; i++)
92 {
93 if (ConsoleList[i] == NULL) break;
94 }
95 }
96
97 if (i >= ConsoleListSize)
98 {
99 DPRINT("Creation of a new handles table\n");
100 /* Allocate a new handles table */
101 Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
102 (ConsoleListSize +
103 CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSRV_CONSOLE));
104 if (Block == NULL)
105 {
106 Status = STATUS_UNSUCCESSFUL;
107 goto Quit;
108 }
109
110 /* If we previously had a handles table, free it and use the new one */
111 if (ConsoleList)
112 {
113 /* Copy the handles from the old table to the new one */
114 RtlCopyMemory(Block,
115 ConsoleList,
116 ConsoleListSize * sizeof(PCONSRV_CONSOLE));
117 ConsoleFreeHeap(ConsoleList);
118 }
119 ConsoleList = Block;
120 ConsoleListSize += CONSOLE_HANDLES_INCREMENT;
121 }
122
123 ConsoleList[i] = Console;
124 *Handle = ULongToHandle((i << 2) | 0x3);
125
126 Quit:
127 /* Unlock the console list and return status */
128 ConSrvUnlockConsoleList();
129 return Status;
130 }
131
132 /* Unused */
133 #if 0
134 static NTSTATUS
135 RemoveConsoleByHandle(IN HANDLE Handle)
136 {
137 NTSTATUS Status = STATUS_SUCCESS;
138 PCONSRV_CONSOLE Console;
139
140 BOOLEAN ValidHandle = ((HandleToULong(Handle) & 0x3) == 0x3);
141 ULONG Index = HandleToULong(Handle) >> 2;
142
143 if (!ValidHandle) return STATUS_INVALID_HANDLE;
144
145 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
146 (ConsoleList != NULL && ConsoleListSize != 0) );
147
148 /* Remove the console from the list */
149 ConSrvLockConsoleListExclusive();
150
151 if (Index >= ConsoleListSize ||
152 (Console = ConsoleList[Index]) == NULL)
153 {
154 Status = STATUS_INVALID_HANDLE;
155 goto Quit;
156 }
157
158 ConsoleList[Index] = NULL;
159
160 Quit:
161 /* Unlock the console list and return status */
162 ConSrvUnlockConsoleList();
163 return Status;
164 }
165 #endif
166
167 static NTSTATUS
RemoveConsoleByPointer(IN PCONSRV_CONSOLE Console)168 RemoveConsoleByPointer(IN PCONSRV_CONSOLE Console)
169 {
170 ULONG i = 0;
171
172 if (!Console) return STATUS_INVALID_PARAMETER;
173
174 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
175 (ConsoleList != NULL && ConsoleListSize != 0) );
176
177 /* Remove the console from the list */
178 ConSrvLockConsoleListExclusive();
179
180 if (ConsoleList)
181 {
182 for (i = 0; i < ConsoleListSize; i++)
183 {
184 if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
185 }
186 }
187
188 /* Unlock the console list and return */
189 ConSrvUnlockConsoleList();
190 return STATUS_SUCCESS;
191 }
192
193 #if 0
194 static NTSTATUS
195 RemoveConsole(IN PCONSOLE Console)
196 {
197 // ASSERT(Console);
198 if (!Console) return STATUS_INVALID_PARAMETER;
199
200 /* Remove the console from the list */
201 ConSrvLockConsoleListExclusive();
202
203 RemoveEntryList(&Console->ListEntry);
204
205 /* Unlock the console list and return success */
206 ConSrvUnlockConsoleList();
207 return STATUS_SUCCESS;
208 }
209 #endif
210
211
212 /* PRIVATE FUNCTIONS **********************************************************/
213
214 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
215 static BOOLEAN
ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,IN PCWSTR Source)216 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,
217 IN PCWSTR Source)
218 {
219 SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR);
220 if (Size > MAXUSHORT) return FALSE;
221
222 UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size);
223 if (UniDest->Buffer == NULL) return FALSE;
224
225 RtlCopyMemory(UniDest->Buffer, Source, Size);
226 UniDest->MaximumLength = (USHORT)Size;
227 UniDest->Length = (USHORT)Size - sizeof(WCHAR);
228
229 return TRUE;
230 }
231
232 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
233 static VOID
ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)234 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
235 {
236 if (UnicodeString->Buffer)
237 {
238 ConsoleFreeHeap(UnicodeString->Buffer);
239 RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
240 }
241 }
242
243
244 /* CONSOLE VALIDATION FUNCTIONS ***********************************************/
245
246 BOOLEAN NTAPI
ConSrvValidateConsole(OUT PCONSRV_CONSOLE * Console,IN HANDLE ConsoleHandle,IN CONSOLE_STATE ExpectedState,IN BOOLEAN LockConsole)247 ConSrvValidateConsole(OUT PCONSRV_CONSOLE* Console,
248 IN HANDLE ConsoleHandle,
249 IN CONSOLE_STATE ExpectedState,
250 IN BOOLEAN LockConsole)
251 {
252 BOOLEAN RetVal = FALSE;
253 PCONSRV_CONSOLE ValidatedConsole;
254
255 BOOLEAN ValidHandle = ((HandleToULong(ConsoleHandle) & 0x3) == 0x3);
256 ULONG Index = HandleToULong(ConsoleHandle) >> 2;
257
258 if (!ValidHandle) return FALSE;
259
260 if (!Console) return FALSE;
261 *Console = NULL;
262
263 /*
264 * Forbid creation or deletion of consoles when
265 * checking for the existence of a console.
266 */
267 ConSrvLockConsoleListShared();
268
269 if (Index >= ConsoleListSize ||
270 (ValidatedConsole = ConsoleList[Index]) == NULL)
271 {
272 /* Unlock the console list and return */
273 ConSrvUnlockConsoleList();
274 return FALSE;
275 }
276
277 ValidatedConsole = ConsoleList[Index];
278
279 /* Unlock the console list */
280 ConSrvUnlockConsoleList();
281
282 RetVal = ConDrvValidateConsoleUnsafe((PCONSOLE)ValidatedConsole,
283 ExpectedState,
284 LockConsole);
285 if (RetVal) *Console = ValidatedConsole;
286
287 return RetVal;
288 }
289
290 NTSTATUS
ConSrvGetConsole(IN PCONSOLE_PROCESS_DATA ProcessData,OUT PCONSRV_CONSOLE * Console,IN BOOLEAN LockConsole)291 ConSrvGetConsole(IN PCONSOLE_PROCESS_DATA ProcessData,
292 OUT PCONSRV_CONSOLE* Console,
293 IN BOOLEAN LockConsole)
294 {
295 NTSTATUS Status = STATUS_INVALID_HANDLE;
296 PCONSRV_CONSOLE GrabConsole;
297
298 // if (Console == NULL) return STATUS_INVALID_PARAMETER;
299 ASSERT(Console);
300 *Console = NULL;
301
302 if (ConSrvValidateConsole(&GrabConsole,
303 ProcessData->ConsoleHandle,
304 CONSOLE_RUNNING,
305 LockConsole))
306 {
307 _InterlockedIncrement(&GrabConsole->ReferenceCount);
308 *Console = GrabConsole;
309 Status = STATUS_SUCCESS;
310 }
311
312 return Status;
313 }
314
315 VOID
ConSrvReleaseConsole(IN PCONSRV_CONSOLE Console,IN BOOLEAN IsConsoleLocked)316 ConSrvReleaseConsole(IN PCONSRV_CONSOLE Console,
317 IN BOOLEAN IsConsoleLocked)
318 {
319 LONG RefCount = 0;
320
321 if (!Console) return;
322 // if (Console->ReferenceCount == 0) return; // This shouldn't happen
323 ASSERT(Console->ReferenceCount > 0);
324
325 /* The console must be locked */
326 // ASSERT(Console_locked);
327
328 /*
329 * Decrement the reference count. Save the new value too,
330 * because Console->ReferenceCount might be modified after
331 * the console gets unlocked but before we check whether we
332 * can destroy it.
333 */
334 RefCount = _InterlockedDecrement(&Console->ReferenceCount);
335
336 /* Unlock the console if needed */
337 if (IsConsoleLocked) LeaveCriticalSection(&Console->Lock);
338
339 /* Delete the console if needed */
340 if (RefCount <= 0) ConSrvDeleteConsole(Console);
341 }
342
343
344 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
345
346 VOID NTAPI
ConSrvInitConsoleSupport(VOID)347 ConSrvInitConsoleSupport(VOID)
348 {
349 DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
350
351 /* Initialize the console list and its lock */
352 ConsoleListSize = 0;
353 ConsoleList = NULL;
354 // InitializeListHead(&ConDrvConsoleList);
355 RtlInitializeResource(&ListLock);
356
357 /* Should call LoadKeyboardLayout */
358 }
359
360 NTSTATUS NTAPI
361 ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
362 IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
363 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
364 IN HANDLE ConsoleLeaderProcessHandle);
365 NTSTATUS NTAPI
366 ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal);
367
368
369 static BOOL
LoadShellLinkConsoleInfo(IN OUT PCONSOLE_STATE_INFO ConsoleInfo,IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo)370 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
371 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo)
372 {
373 #define PATH_SEPARATOR L'\\'
374
375 BOOL RetVal = FALSE;
376 HRESULT hRes = S_OK;
377 SIZE_T Length = 0;
378 LPWSTR LinkName = NULL;
379 LPWSTR IconPath = NULL;
380 WCHAR Buffer[MAX_PATH + 1];
381
382 ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
383
384 if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
385 {
386 // return FALSE; // FIXME!! (for icon loading)
387 RetVal = TRUE;
388 goto Finish;
389 }
390
391 /* 1- Find the last path separator if any */
392 LinkName = wcsrchr(ConsoleInfo->ConsoleTitle, PATH_SEPARATOR);
393 if (LinkName == NULL)
394 LinkName = ConsoleInfo->ConsoleTitle;
395 else
396 ++LinkName; // Skip the path separator
397
398 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
399 Length = wcslen(LinkName);
400 if ( (Length <= 4) || (_wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
401 return FALSE;
402
403 /* 3- It may be a link. Try to retrieve some properties */
404 hRes = CoInitialize(NULL);
405 if (SUCCEEDED(hRes))
406 {
407 /* Get a pointer to the IShellLink interface */
408 IShellLinkW* pshl = NULL;
409 hRes = CoCreateInstance(&CLSID_ShellLink,
410 NULL,
411 CLSCTX_INPROC_SERVER,
412 &IID_IShellLinkW,
413 (LPVOID*)&pshl);
414 if (SUCCEEDED(hRes))
415 {
416 /* Get a pointer to the IPersistFile interface */
417 IPersistFile* ppf = NULL;
418 hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
419 if (SUCCEEDED(hRes))
420 {
421 /* Load the shortcut */
422 hRes = IPersistFile_Load(ppf, ConsoleInfo->ConsoleTitle, STGM_READ);
423 if (SUCCEEDED(hRes))
424 {
425 /*
426 * Finally we can get the properties !
427 * Update the old ones if needed.
428 */
429 INT ShowCmd = 0;
430 // WORD HotKey = 0;
431
432 /* Reset the name of the console with the name of the shortcut */
433 Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
434 (ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR));
435 wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
436 ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL;
437
438 /* Get the window showing command */
439 hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
440 if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->wShowWindow = (WORD)ShowCmd;
441
442 /* Get the hotkey */
443 // hRes = pshl->GetHotkey(&ShowCmd);
444 // if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->HotKey = HotKey;
445
446 /* Get the icon location, if any */
447 hRes = IShellLinkW_GetIconLocation(pshl,
448 Buffer,
449 sizeof(Buffer)/sizeof(Buffer[0]) - 1, // == MAX_PATH
450 &ConsoleInitInfo->ConsoleStartInfo->IconIndex);
451 if (!SUCCEEDED(hRes))
452 {
453 ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
454 }
455 else
456 {
457 IconPath = Buffer;
458 }
459
460 // FIXME: Since we still don't load console properties from the shortcut,
461 // return false. When this will be done, we will return true instead.
462 RetVal = TRUE; // FALSE;
463 }
464 IPersistFile_Release(ppf);
465 }
466 IShellLinkW_Release(pshl);
467 }
468 CoUninitialize();
469 }
470
471 Finish:
472
473 if (RetVal)
474 {
475 /* Get the associated icon, if any */
476 if (IconPath == NULL)
477 {
478 // Question: How to retrieve the full path name
479 // of the app we are going to run??
480 Length = RtlDosSearchPath_U(ConsoleInitInfo->CurDir,
481 ConsoleInitInfo->AppName,
482 NULL,
483 sizeof(Buffer),
484 Buffer,
485 NULL);
486 if (Length > 0 && Length < sizeof(Buffer))
487 IconPath = Buffer;
488 else
489 IconPath = ConsoleInitInfo->AppName;
490
491 // ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
492 }
493 DPRINT("IconPath = '%S' ; IconIndex = %lu\n",
494 IconPath, ConsoleInitInfo->ConsoleStartInfo->IconIndex);
495 if (IconPath && *IconPath)
496 {
497 HICON hIcon = NULL, hIconSm = NULL;
498 /*
499 * FIXME!! Because of a strange bug we have in PrivateExtractIconExW
500 * (see r65683 for more details), we cannot use this API to extract
501 * at the same time the large and small icons from the app.
502 * Instead we just use PrivateExtractIconsW.
503 *
504 PrivateExtractIconExW(IconPath,
505 ConsoleInitInfo->ConsoleStartInfo->IconIndex,
506 &hIcon,
507 &hIconSm,
508 1);
509 */
510 PrivateExtractIconsW(IconPath,
511 ConsoleInitInfo->ConsoleStartInfo->IconIndex,
512 32, 32,
513 &hIcon, NULL, 1, LR_COPYFROMRESOURCE);
514 PrivateExtractIconsW(IconPath,
515 ConsoleInitInfo->ConsoleStartInfo->IconIndex,
516 16, 16,
517 &hIconSm, NULL, 1, LR_COPYFROMRESOURCE);
518
519 DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
520 if (hIcon != NULL) ConsoleInitInfo->ConsoleStartInfo->hIcon = hIcon;
521 if (hIconSm != NULL) ConsoleInitInfo->ConsoleStartInfo->hIconSm = hIconSm;
522 }
523 }
524
525 // FIXME: See the previous FIXME above.
526 RetVal = FALSE;
527
528 return RetVal;
529 }
530
531 NTSTATUS NTAPI
ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,OUT PCONSRV_CONSOLE * NewConsole,IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,IN PCSR_PROCESS ConsoleLeaderProcess)532 ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,
533 OUT PCONSRV_CONSOLE* NewConsole,
534 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
535 IN PCSR_PROCESS ConsoleLeaderProcess)
536 {
537 NTSTATUS Status;
538 HANDLE ConsoleHandle;
539 PCONSRV_CONSOLE Console;
540
541 BYTE ConsoleInfoBuffer[sizeof(CONSOLE_STATE_INFO) + MAX_PATH * sizeof(WCHAR)]; // CONSRV console information
542 PCONSOLE_STATE_INFO ConsoleInfo = (PCONSOLE_STATE_INFO)&ConsoleInfoBuffer;
543 CONSOLE_INFO DrvConsoleInfo; // Console information for CONDRV
544
545 SIZE_T Length = 0;
546
547 TERMINAL Terminal; /* The ConSrv terminal for this console */
548
549 if (NewConsole == NULL || ConsoleInitInfo == NULL)
550 return STATUS_INVALID_PARAMETER;
551
552 *NewConsole = NULL;
553
554 DPRINT("Initialization of console '%S' for process '%S' on desktop '%S'\n",
555 ConsoleInitInfo->ConsoleTitle ? ConsoleInitInfo->ConsoleTitle : L"n/a",
556 ConsoleInitInfo->AppName ? ConsoleInitInfo->AppName : L"n/a",
557 ConsoleInitInfo->Desktop ? ConsoleInitInfo->Desktop : L"n/a");
558
559 /*
560 * Load the console settings
561 */
562 RtlZeroMemory(ConsoleInfo, sizeof(ConsoleInfoBuffer));
563 ConsoleInfo->cbSize = sizeof(ConsoleInfoBuffer);
564
565 /* 1. Get the title of the console (initialize ConsoleInfo->ConsoleTitle) */
566 Length = min(ConsoleInitInfo->TitleLength,
567 (ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR));
568 wcsncpy(ConsoleInfo->ConsoleTitle, ConsoleInitInfo->ConsoleTitle, Length);
569 ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL; // NULL-terminate it.
570
571 /* 2. Impersonate the caller in order to retrieve settings in its context */
572 if (!CsrImpersonateClient(NULL))
573 return STATUS_BAD_IMPERSONATION_LEVEL;
574
575 /* 3. Load the default settings */
576 ConCfgGetDefaultSettings(ConsoleInfo);
577
578 /*
579 * 4. Load per-application terminal settings.
580 *
581 * Check whether the process creating the console was launched via
582 * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
583 * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
584 */
585 // if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) // FIXME!! (for icon loading)
586 {
587 if (!LoadShellLinkConsoleInfo(ConsoleInfo, ConsoleInitInfo))
588 {
589 ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
590 }
591 }
592
593 /*
594 * 5. Load the remaining console settings via the registry.
595 */
596 if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
597 {
598 /*
599 * Either we weren't created by an app launched via a shell-link,
600 * or we failed to load shell-link console properties.
601 * Therefore, load the console infos for the application from the registry.
602 */
603 ConCfgReadUserSettings(ConsoleInfo, FALSE);
604
605 /*
606 * Now, update them with the properties the user might gave to us
607 * via the STARTUPINFO structure before calling CreateProcess
608 * (and which was transmitted via the ConsoleStartInfo structure).
609 * We therefore overwrite the values read in the registry.
610 */
611 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
612 {
613 ConsoleInfo->ScreenAttributes = (USHORT)ConsoleInitInfo->ConsoleStartInfo->wFillAttribute;
614 }
615 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
616 {
617 ConsoleInfo->ScreenBufferSize = ConsoleInitInfo->ConsoleStartInfo->dwScreenBufferSize;
618 }
619 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
620 {
621 ConsoleInfo->WindowSize = ConsoleInitInfo->ConsoleStartInfo->dwWindowSize;
622 }
623
624 #if 0
625 /*
626 * Now, update them with the properties the user might gave to us
627 * via the STARTUPINFO structure before calling CreateProcess
628 * (and which was transmitted via the ConsoleStartInfo structure).
629 * We therefore overwrite the values read in the registry.
630 */
631 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
632 {
633 ConsoleInfo->AutoPosition = FALSE;
634 ConsoleInfo->WindowPosition.x = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.X;
635 ConsoleInfo->WindowPosition.y = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.Y;
636 }
637 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
638 {
639 ConsoleInfo->FullScreen = TRUE;
640 }
641 #endif
642 }
643
644 /* 6. Revert impersonation */
645 CsrRevertToSelf();
646
647 /* Set-up the code page */
648 ConsoleInfo->CodePage = GetOEMCP();
649
650 /*
651 * Initialize the ConSrv terminal and give it a chance to load
652 * its own settings and override the console settings.
653 */
654 Status = ConSrvInitTerminal(&Terminal,
655 ConsoleInfo,
656 ConsoleInitInfo,
657 ConsoleLeaderProcess->ProcessHandle);
658 if (!NT_SUCCESS(Status))
659 {
660 DPRINT1("CONSRV: Failed to initialize a terminal, Status = 0x%08lx\n", Status);
661 return Status;
662 }
663 DPRINT("CONSRV: Terminal initialized\n");
664
665 /* Initialize a new console via the driver */
666 // DrvConsoleInfo.InputBufferSize = 0;
667 DrvConsoleInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
668 DrvConsoleInfo.ConsoleSize = ConsoleInfo->WindowSize;
669 DrvConsoleInfo.CursorSize = ConsoleInfo->CursorSize;
670 // DrvConsoleInfo.CursorBlinkOn = ConsoleInfo->CursorBlinkOn;
671 DrvConsoleInfo.ScreenAttrib = ConsoleInfo->ScreenAttributes;
672 DrvConsoleInfo.PopupAttrib = ConsoleInfo->PopupAttributes;
673 DrvConsoleInfo.CodePage = ConsoleInfo->CodePage;
674
675 /*
676 * Allocate a new console
677 */
678 Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*Console));
679 if (Console == NULL)
680 {
681 DPRINT1("Not enough memory for console creation.\n");
682 ConSrvDeinitTerminal(&Terminal);
683 return STATUS_NO_MEMORY;
684 }
685
686 Status = ConDrvInitConsole((PCONSOLE)Console, &DrvConsoleInfo);
687 if (!NT_SUCCESS(Status))
688 {
689 DPRINT1("Creating a new console failed, Status = 0x%08lx\n", Status);
690 ConsoleFreeHeap(Console);
691 ConSrvDeinitTerminal(&Terminal);
692 return Status;
693 }
694
695 DPRINT("Console initialized\n");
696
697 /*** Register ConSrv features ***/
698
699 /* Initialize the console title */
700 #if 0
701 WCHAR DefaultTitle[128];
702 #endif
703 ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle);
704 #if 0
705 if (ConsoleInfo->ConsoleTitle[0] == UNICODE_NULL)
706 {
707 if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
708 {
709 ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
710 }
711 else
712 {
713 ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
714 }
715 }
716 else
717 {
718 #endif
719 ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle);
720 #if 0
721 }
722 #endif
723
724 /* Initialize process support */
725 // InitProcessSupport(Console);
726 InitializeListHead(&Console->ProcessList);
727 Console->NotifiedLastCloseProcess = NULL;
728 Console->NotifyLastClose = FALSE;
729 Console->HasFocus = FALSE;
730
731 /* Initialize pausing support */
732 Console->PauseFlags = 0;
733 InitializeListHead(&Console->ReadWaitQueue);
734 InitializeListHead(&Console->WriteWaitQueue);
735
736 /* Initialize the alias and history buffers */
737 // InitAliasesHistory(Console);
738 Console->Aliases = NULL;
739 InitializeListHead(&Console->HistoryBuffers);
740 Console->NumberOfHistoryBuffers = 0;
741 Console->MaxNumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers;
742 Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize;
743 Console->HistoryNoDup = ConsoleInfo->HistoryNoDup;
744
745 /* Initialize the Input Line Discipline */
746 // InitLineInput(Console);
747 Console->LineBuffer = NULL;
748 Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
749 Console->LineComplete = Console->LineUpPressed = FALSE;
750 // LineWakeupMask
751 Console->LineInsertToggle =
752 Console->InsertMode = ConsoleInfo->InsertMode;
753 Console->QuickEdit = ConsoleInfo->QuickEdit;
754
755 /* Popup windows */
756 InitializeListHead(&Console->PopupWindows);
757
758 /* Colour table */
759 RtlCopyMemory(Console->Colors, ConsoleInfo->ColorTable,
760 sizeof(ConsoleInfo->ColorTable));
761
762 /* Create the Initialization Events */
763 Status = NtCreateEvent(&Console->InitEvents[INIT_SUCCESS], EVENT_ALL_ACCESS,
764 NULL, NotificationEvent, FALSE);
765 if (!NT_SUCCESS(Status))
766 {
767 DPRINT1("NtCreateEvent(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
768 ConDrvDeleteConsole((PCONSOLE)Console);
769 ConSrvDeinitTerminal(&Terminal);
770 return Status;
771 }
772 Status = NtCreateEvent(&Console->InitEvents[INIT_FAILURE], EVENT_ALL_ACCESS,
773 NULL, NotificationEvent, FALSE);
774 if (!NT_SUCCESS(Status))
775 {
776 DPRINT1("NtCreateEvent(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
777 NtClose(Console->InitEvents[INIT_SUCCESS]);
778 ConDrvDeleteConsole((PCONSOLE)Console);
779 ConSrvDeinitTerminal(&Terminal);
780 return Status;
781 }
782
783 /*
784 * Attach the ConSrv terminal to the console.
785 * This call makes a copy of our local Terminal variable.
786 */
787 Status = ConDrvAttachTerminal((PCONSOLE)Console, &Terminal);
788 if (!NT_SUCCESS(Status))
789 {
790 DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
791 NtClose(Console->InitEvents[INIT_FAILURE]);
792 NtClose(Console->InitEvents[INIT_SUCCESS]);
793 ConDrvDeleteConsole((PCONSOLE)Console);
794 ConSrvDeinitTerminal(&Terminal);
795 return Status;
796 }
797 DPRINT("Terminal attached\n");
798
799 /* All went right, so add the console to the list */
800 #if 0
801 Status = ConDrvInsertConsole((PCONSOLE)Console);
802 if (!NT_SUCCESS(Status))
803 {
804 /* Fail */
805 ConDrvDeleteConsole((PCONSOLE)Console);
806 return Status;
807 }
808 #endif
809 Status = InsertConsole(&ConsoleHandle, Console);
810
811 // FIXME! We do not support at all asynchronous console creation!
812 NtSetEvent(Console->InitEvents[INIT_SUCCESS], NULL);
813 // NtSetEvent(Console->InitEvents[INIT_FAILURE], NULL);
814
815 /* Return the newly created console to the caller and a success code too */
816 *NewConsoleHandle = ConsoleHandle;
817 *NewConsole = Console;
818 return STATUS_SUCCESS;
819 }
820
821 VOID NTAPI
ConSrvDeleteConsole(PCONSRV_CONSOLE Console)822 ConSrvDeleteConsole(PCONSRV_CONSOLE Console)
823 {
824 DPRINT("ConSrvDeleteConsole\n");
825
826 // FIXME: Send a terminate message to all the processes owning this console.
827 // NOTE: In principle there should be none, because such processes would
828 // have a reference to the console and thus this function would not have
829 // been called in the first place.
830
831 /* Remove the console from the list */
832 RemoveConsoleByPointer(Console);
833
834 /* Destroy the Initialization Events */
835 NtClose(Console->InitEvents[INIT_FAILURE]);
836 NtClose(Console->InitEvents[INIT_SUCCESS]);
837
838 /* Clean the Input Line Discipline */
839 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
840
841 /* Clean aliases and history */
842 IntDeleteAllAliases(Console);
843 HistoryDeleteBuffers(Console);
844
845 /* Free the console title */
846 ConsoleFreeUnicodeString(&Console->OriginalTitle);
847 ConsoleFreeUnicodeString(&Console->Title);
848
849 /* Now, call the driver. ConDrvDetachTerminal is called on-demand. */
850 ConDrvDeleteConsole((PCONSOLE)Console);
851
852 /* Deinit the ConSrv terminal */
853 // FIXME!!
854 // ConSrvDeinitTerminal(&Terminal);
855 }
856
857
858 VOID
ConioPause(PCONSRV_CONSOLE Console,UCHAR Flags)859 ConioPause(PCONSRV_CONSOLE Console, UCHAR Flags)
860 {
861 Console->PauseFlags |= Flags;
862 ConDrvPause((PCONSOLE)Console);
863 }
864
865 VOID
ConioUnpause(PCONSRV_CONSOLE Console,UCHAR Flags)866 ConioUnpause(PCONSRV_CONSOLE Console, UCHAR Flags)
867 {
868 Console->PauseFlags &= ~Flags;
869
870 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
871 if (Console->PauseFlags == 0)
872 {
873 ConDrvUnpause((PCONSOLE)Console);
874
875 CsrNotifyWait(&Console->WriteWaitQueue,
876 TRUE,
877 NULL,
878 NULL);
879 if (!IsListEmpty(&Console->WriteWaitQueue))
880 {
881 CsrDereferenceWait(&Console->WriteWaitQueue);
882 }
883 }
884 }
885
886
887 /* CONSOLE PROCESS INITIALIZATION FUNCTIONS ***********************************/
888
889 static NTSTATUS
ConSrvInitProcessHandles(IN OUT PCONSOLE_PROCESS_DATA ProcessData,IN PCONSRV_CONSOLE Console,OUT PHANDLE pInputHandle,OUT PHANDLE pOutputHandle,OUT PHANDLE pErrorHandle)890 ConSrvInitProcessHandles(
891 IN OUT PCONSOLE_PROCESS_DATA ProcessData,
892 IN PCONSRV_CONSOLE Console,
893 OUT PHANDLE pInputHandle,
894 OUT PHANDLE pOutputHandle,
895 OUT PHANDLE pErrorHandle)
896 {
897 NTSTATUS Status;
898 HANDLE InputHandle = INVALID_HANDLE_VALUE,
899 OutputHandle = INVALID_HANDLE_VALUE,
900 ErrorHandle = INVALID_HANDLE_VALUE;
901
902 /*
903 * Initialize the process handles. Use temporary variables to store
904 * the handles values in such a way that, if we fail, we don't
905 * return to the caller invalid handle values.
906 *
907 * Insert the IO handles.
908 */
909
910 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
911
912 /* Insert the Input handle */
913 Status = ConSrvInsertObject(ProcessData,
914 &InputHandle,
915 &Console->InputBuffer.Header,
916 GENERIC_READ | GENERIC_WRITE,
917 TRUE,
918 FILE_SHARE_READ | FILE_SHARE_WRITE);
919 if (!NT_SUCCESS(Status))
920 {
921 DPRINT1("Failed to insert the input handle\n");
922 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
923 ConSrvFreeHandlesTable(ProcessData);
924 return Status;
925 }
926
927 /* Insert the Output handle */
928 Status = ConSrvInsertObject(ProcessData,
929 &OutputHandle,
930 &Console->ActiveBuffer->Header,
931 GENERIC_READ | GENERIC_WRITE,
932 TRUE,
933 FILE_SHARE_READ | FILE_SHARE_WRITE);
934 if (!NT_SUCCESS(Status))
935 {
936 DPRINT1("Failed to insert the output handle\n");
937 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
938 ConSrvFreeHandlesTable(ProcessData);
939 return Status;
940 }
941
942 /* Insert the Error handle */
943 Status = ConSrvInsertObject(ProcessData,
944 &ErrorHandle,
945 &Console->ActiveBuffer->Header,
946 GENERIC_READ | GENERIC_WRITE,
947 TRUE,
948 FILE_SHARE_READ | FILE_SHARE_WRITE);
949 if (!NT_SUCCESS(Status))
950 {
951 DPRINT1("Failed to insert the error handle\n");
952 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
953 ConSrvFreeHandlesTable(ProcessData);
954 return Status;
955 }
956
957 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
958
959 /* Return the newly created handles */
960 *pInputHandle = InputHandle;
961 *pOutputHandle = OutputHandle;
962 *pErrorHandle = ErrorHandle;
963
964 return STATUS_SUCCESS;
965 }
966
967 NTSTATUS
ConSrvAllocateConsole(IN OUT PCONSOLE_PROCESS_DATA ProcessData,OUT PHANDLE pInputHandle,OUT PHANDLE pOutputHandle,OUT PHANDLE pErrorHandle,IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo)968 ConSrvAllocateConsole(
969 IN OUT PCONSOLE_PROCESS_DATA ProcessData,
970 OUT PHANDLE pInputHandle,
971 OUT PHANDLE pOutputHandle,
972 OUT PHANDLE pErrorHandle,
973 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo)
974 {
975 NTSTATUS Status = STATUS_SUCCESS;
976 HANDLE ConsoleHandle;
977 PCONSRV_CONSOLE Console;
978
979 /*
980 * We are about to create a new console. However when ConSrvNewProcess()
981 * was called, we didn't know that we wanted to create a new console and
982 * therefore, we by default inherited the handle table from our parent
983 * process. It's only now that we notice that in fact we do not need
984 * them, because we've created a new console and thus we must use it.
985 *
986 * Therefore, free the handle table so that we can recreate
987 * a new one later on.
988 */
989 ConSrvFreeHandlesTable(ProcessData);
990
991 /* Initialize a new Console owned by this process */
992 Status = ConSrvInitConsole(&ConsoleHandle,
993 &Console,
994 ConsoleInitInfo,
995 ProcessData->Process);
996 if (!NT_SUCCESS(Status))
997 {
998 DPRINT1("Console initialization failed\n");
999 return Status;
1000 }
1001
1002 /* Assign the new console handle */
1003 ProcessData->ConsoleHandle = ConsoleHandle;
1004
1005 /* Initialize the process handles */
1006 Status = ConSrvInitProcessHandles(ProcessData,
1007 Console,
1008 pInputHandle,
1009 pOutputHandle,
1010 pErrorHandle);
1011 if (!NT_SUCCESS(Status))
1012 {
1013 DPRINT1("Failed to initialize the process handles\n");
1014 ConSrvDeleteConsole(Console);
1015 ProcessData->ConsoleHandle = NULL;
1016 return Status;
1017 }
1018
1019 /* Duplicate the Initialization Events */
1020 Status = NtDuplicateObject(NtCurrentProcess(),
1021 Console->InitEvents[INIT_SUCCESS],
1022 ProcessData->Process->ProcessHandle,
1023 &ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS],
1024 EVENT_ALL_ACCESS, 0, 0);
1025 if (!NT_SUCCESS(Status))
1026 {
1027 DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
1028 ConSrvFreeHandlesTable(ProcessData);
1029 ConSrvDeleteConsole(Console);
1030 ProcessData->ConsoleHandle = NULL;
1031 return Status;
1032 }
1033
1034 Status = NtDuplicateObject(NtCurrentProcess(),
1035 Console->InitEvents[INIT_FAILURE],
1036 ProcessData->Process->ProcessHandle,
1037 &ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE],
1038 EVENT_ALL_ACCESS, 0, 0);
1039 if (!NT_SUCCESS(Status))
1040 {
1041 DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
1042 NtDuplicateObject(ProcessData->Process->ProcessHandle,
1043 ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS],
1044 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
1045 ConSrvFreeHandlesTable(ProcessData);
1046 ConSrvDeleteConsole(Console);
1047 ProcessData->ConsoleHandle = NULL;
1048 return Status;
1049 }
1050
1051 /* Duplicate the Input Event */
1052 Status = NtDuplicateObject(NtCurrentProcess(),
1053 Console->InputBuffer.ActiveEvent,
1054 ProcessData->Process->ProcessHandle,
1055 &ConsoleInitInfo->ConsoleStartInfo->InputWaitHandle,
1056 EVENT_ALL_ACCESS, 0, 0);
1057 if (!NT_SUCCESS(Status))
1058 {
1059 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
1060 NtDuplicateObject(ProcessData->Process->ProcessHandle,
1061 ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE],
1062 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
1063 NtDuplicateObject(ProcessData->Process->ProcessHandle,
1064 ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS],
1065 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
1066 ConSrvFreeHandlesTable(ProcessData);
1067 ConSrvDeleteConsole(Console);
1068 ProcessData->ConsoleHandle = NULL;
1069 return Status;
1070 }
1071
1072 /* Mark the process as having a console */
1073 ProcessData->ConsoleApp = TRUE;
1074 ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
1075
1076 /* Return the console handle to the caller */
1077 ConsoleInitInfo->ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
1078
1079 /*
1080 * Insert the process into the processes list of the console,
1081 * and set its foreground priority.
1082 */
1083 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
1084 ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus);
1085
1086 /* Add a reference count because the process is tied to the console */
1087 _InterlockedIncrement(&Console->ReferenceCount);
1088
1089 /* Update the internal info of the terminal */
1090 TermRefreshInternalInfo(Console);
1091
1092 return STATUS_SUCCESS;
1093 }
1094
1095 NTSTATUS
ConSrvInheritConsole(IN OUT PCONSOLE_PROCESS_DATA ProcessData,IN HANDLE ConsoleHandle,IN BOOLEAN CreateNewHandleTable,OUT PHANDLE pInputHandle,OUT PHANDLE pOutputHandle,OUT PHANDLE pErrorHandle,IN OUT PCONSOLE_START_INFO ConsoleStartInfo)1096 ConSrvInheritConsole(
1097 IN OUT PCONSOLE_PROCESS_DATA ProcessData,
1098 IN HANDLE ConsoleHandle,
1099 IN BOOLEAN CreateNewHandleTable,
1100 OUT PHANDLE pInputHandle,
1101 OUT PHANDLE pOutputHandle,
1102 OUT PHANDLE pErrorHandle,
1103 IN OUT PCONSOLE_START_INFO ConsoleStartInfo)
1104 {
1105 NTSTATUS Status = STATUS_SUCCESS;
1106 PCONSRV_CONSOLE Console;
1107
1108 /* Validate and lock the console */
1109 if (!ConSrvValidateConsole(&Console,
1110 ConsoleHandle,
1111 CONSOLE_RUNNING, TRUE))
1112 {
1113 // FIXME: Find another status code
1114 return STATUS_UNSUCCESSFUL;
1115 }
1116
1117 /* Inherit the console */
1118 ProcessData->ConsoleHandle = ConsoleHandle;
1119
1120 if (CreateNewHandleTable)
1121 {
1122 /*
1123 * We are about to create a new console. However when ConSrvNewProcess()
1124 * was called, we didn't know that we wanted to create a new console and
1125 * therefore, we by default inherited the handle table from our parent
1126 * process. It's only now that we notice that in fact we do not need
1127 * them, because we've created a new console and thus we must use it.
1128 *
1129 * Therefore, free the handle table so that we can recreate
1130 * a new one later on.
1131 */
1132 ConSrvFreeHandlesTable(ProcessData);
1133
1134 /* Initialize the process handles */
1135 Status = ConSrvInitProcessHandles(ProcessData,
1136 Console,
1137 pInputHandle,
1138 pOutputHandle,
1139 pErrorHandle);
1140 if (!NT_SUCCESS(Status))
1141 {
1142 DPRINT1("Failed to initialize the process handles\n");
1143 ProcessData->ConsoleHandle = NULL;
1144 goto Quit;
1145 }
1146 }
1147
1148 /* Duplicate the Initialization Events */
1149 Status = NtDuplicateObject(NtCurrentProcess(),
1150 Console->InitEvents[INIT_SUCCESS],
1151 ProcessData->Process->ProcessHandle,
1152 &ConsoleStartInfo->InitEvents[INIT_SUCCESS],
1153 EVENT_ALL_ACCESS, 0, 0);
1154 if (!NT_SUCCESS(Status))
1155 {
1156 DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
1157 ConSrvFreeHandlesTable(ProcessData);
1158 ProcessData->ConsoleHandle = NULL;
1159 goto Quit;
1160 }
1161
1162 Status = NtDuplicateObject(NtCurrentProcess(),
1163 Console->InitEvents[INIT_FAILURE],
1164 ProcessData->Process->ProcessHandle,
1165 &ConsoleStartInfo->InitEvents[INIT_FAILURE],
1166 EVENT_ALL_ACCESS, 0, 0);
1167 if (!NT_SUCCESS(Status))
1168 {
1169 DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
1170 NtDuplicateObject(ProcessData->Process->ProcessHandle,
1171 ConsoleStartInfo->InitEvents[INIT_SUCCESS],
1172 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
1173 ConSrvFreeHandlesTable(ProcessData);
1174 ProcessData->ConsoleHandle = NULL;
1175 goto Quit;
1176 }
1177
1178 /* Duplicate the Input Event */
1179 Status = NtDuplicateObject(NtCurrentProcess(),
1180 Console->InputBuffer.ActiveEvent,
1181 ProcessData->Process->ProcessHandle,
1182 &ConsoleStartInfo->InputWaitHandle,
1183 EVENT_ALL_ACCESS, 0, 0);
1184 if (!NT_SUCCESS(Status))
1185 {
1186 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
1187 NtDuplicateObject(ProcessData->Process->ProcessHandle,
1188 ConsoleStartInfo->InitEvents[INIT_FAILURE],
1189 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
1190 NtDuplicateObject(ProcessData->Process->ProcessHandle,
1191 ConsoleStartInfo->InitEvents[INIT_SUCCESS],
1192 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
1193 ConSrvFreeHandlesTable(ProcessData); // NOTE: Always free the handle table.
1194 ProcessData->ConsoleHandle = NULL;
1195 goto Quit;
1196 }
1197
1198 /* Mark the process as having a console */
1199 ProcessData->ConsoleApp = TRUE;
1200 ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
1201
1202 /* Return the console handle to the caller */
1203 ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
1204
1205 /*
1206 * Insert the process into the processes list of the console,
1207 * and set its foreground priority.
1208 */
1209 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
1210 ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus);
1211
1212 /* Add a reference count because the process is tied to the console */
1213 _InterlockedIncrement(&Console->ReferenceCount);
1214
1215 /* Update the internal info of the terminal */
1216 TermRefreshInternalInfo(Console);
1217
1218 Status = STATUS_SUCCESS;
1219
1220 Quit:
1221 /* Unlock the console and return */
1222 LeaveCriticalSection(&Console->Lock);
1223 return Status;
1224 }
1225
1226 NTSTATUS
ConSrvRemoveConsole(IN OUT PCONSOLE_PROCESS_DATA ProcessData)1227 ConSrvRemoveConsole(
1228 IN OUT PCONSOLE_PROCESS_DATA ProcessData)
1229 {
1230 PCONSRV_CONSOLE Console;
1231 PCONSOLE_PROCESS_DATA ConsoleLeaderProcess;
1232
1233 DPRINT("ConSrvRemoveConsole\n");
1234
1235 /* Mark the process as not having a console anymore */
1236 ProcessData->ConsoleApp = FALSE;
1237 ProcessData->Process->Flags &= ~CsrProcessIsConsoleApp;
1238
1239 /* Validate and lock the console */
1240 if (!ConSrvValidateConsole(&Console,
1241 ProcessData->ConsoleHandle,
1242 CONSOLE_RUNNING, TRUE))
1243 {
1244 // FIXME: Find another status code
1245 return STATUS_UNSUCCESSFUL;
1246 }
1247
1248 DPRINT("ConSrvRemoveConsole - Locking OK\n");
1249
1250 /* Retrieve the console leader process */
1251 ConsoleLeaderProcess = ConSrvGetConsoleLeaderProcess(Console);
1252
1253 /* Close all console handles and free the handle table */
1254 ConSrvFreeHandlesTable(ProcessData);
1255
1256 /* Detach the process from the console */
1257 ProcessData->ConsoleHandle = NULL;
1258
1259 /* Remove the process from the console's list of processes */
1260 RemoveEntryList(&ProcessData->ConsoleLink);
1261
1262 /* Check whether the console should send a last close notification */
1263 if (Console->NotifyLastClose)
1264 {
1265 /* If we are removing the process which wants the last close notification... */
1266 if (ProcessData == Console->NotifiedLastCloseProcess)
1267 {
1268 /* ... just reset the flag and the pointer... */
1269 Console->NotifyLastClose = FALSE;
1270 Console->NotifiedLastCloseProcess = NULL;
1271 }
1272 /*
1273 * ... otherwise, if we are removing the console leader process
1274 * (that cannot be the process wanting the notification, because
1275 * the previous case already dealt with it)...
1276 */
1277 else if (ProcessData == ConsoleLeaderProcess)
1278 {
1279 /*
1280 * ... reset the flag first (so that we avoid multiple notifications)
1281 * and then send the last close notification.
1282 */
1283 Console->NotifyLastClose = FALSE;
1284 ConSrvConsoleCtrlEvent(CTRL_LAST_CLOSE_EVENT, Console->NotifiedLastCloseProcess);
1285
1286 /* Only now, reset the pointer */
1287 Console->NotifiedLastCloseProcess = NULL;
1288 }
1289 }
1290
1291 /* Update the internal info of the terminal */
1292 TermRefreshInternalInfo(Console);
1293
1294 /* Release the console */
1295 DPRINT("ConSrvRemoveConsole - Decrement Console->ReferenceCount = %lu\n", Console->ReferenceCount);
1296 ConSrvReleaseConsole(Console, TRUE);
1297
1298 return STATUS_SUCCESS;
1299 }
1300
1301
1302 /* CONSOLE PROCESS MANAGEMENT FUNCTIONS ***************************************/
1303
1304 NTSTATUS
ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,IN PCONSOLE_PROCESS_DATA ProcessData,IN ULONG Timeout)1305 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
1306 IN PCONSOLE_PROCESS_DATA ProcessData,
1307 IN ULONG Timeout)
1308 {
1309 NTSTATUS Status = STATUS_SUCCESS;
1310
1311 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
1312
1313 /*
1314 * Be sure we effectively have a control routine. It resides in kernel32.dll (client).
1315 */
1316 if (ProcessData->CtrlRoutine == NULL) return Status;
1317
1318 _SEH2_TRY
1319 {
1320 HANDLE Thread = NULL;
1321
1322 _SEH2_TRY
1323 {
1324 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
1325 ProcessData->CtrlRoutine,
1326 UlongToPtr(CtrlEvent), 0, NULL);
1327 if (NULL == Thread)
1328 {
1329 Status = RtlGetLastNtStatus();
1330 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
1331 }
1332 else
1333 {
1334 DPRINT("ProcessData->CtrlRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n",
1335 ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
1336 WaitForSingleObject(Thread, Timeout);
1337 }
1338 }
1339 _SEH2_FINALLY
1340 {
1341 CloseHandle(Thread);
1342 }
1343 _SEH2_END;
1344 }
1345 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1346 {
1347 Status = _SEH2_GetExceptionCode();
1348 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
1349 }
1350 _SEH2_END;
1351
1352 return Status;
1353 }
1354
1355 NTSTATUS
ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,IN PCONSOLE_PROCESS_DATA ProcessData)1356 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
1357 IN PCONSOLE_PROCESS_DATA ProcessData)
1358 {
1359 return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
1360 }
1361
1362 PCONSOLE_PROCESS_DATA NTAPI
ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)1363 ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)
1364 {
1365 if (Console == NULL) return NULL;
1366
1367 return CONTAINING_RECORD(Console->ProcessList.Blink,
1368 CONSOLE_PROCESS_DATA,
1369 ConsoleLink);
1370 }
1371
1372 NTSTATUS NTAPI
ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,IN OUT PULONG ProcessIdsList,IN ULONG MaxIdListItems,OUT PULONG ProcessIdsTotal)1373 ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,
1374 IN OUT PULONG ProcessIdsList,
1375 IN ULONG MaxIdListItems,
1376 OUT PULONG ProcessIdsTotal)
1377 {
1378 PCONSOLE_PROCESS_DATA current;
1379 PLIST_ENTRY current_entry;
1380
1381 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
1382 return STATUS_INVALID_PARAMETER;
1383
1384 *ProcessIdsTotal = 0;
1385
1386 for (current_entry = Console->ProcessList.Flink;
1387 current_entry != &Console->ProcessList;
1388 current_entry = current_entry->Flink)
1389 {
1390 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1391 if (++(*ProcessIdsTotal) <= MaxIdListItems)
1392 {
1393 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
1394 }
1395 }
1396
1397 return STATUS_SUCCESS;
1398 }
1399
1400 // ConSrvGenerateConsoleCtrlEvent
1401 NTSTATUS NTAPI
ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,IN ULONG ProcessGroupId,IN ULONG CtrlEvent)1402 ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,
1403 IN ULONG ProcessGroupId,
1404 IN ULONG CtrlEvent)
1405 {
1406 NTSTATUS Status = STATUS_SUCCESS;
1407 PLIST_ENTRY current_entry;
1408 PCONSOLE_PROCESS_DATA current;
1409
1410 /* If the console is already being destroyed, just return */
1411 if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
1412 return STATUS_UNSUCCESSFUL;
1413
1414 /*
1415 * Loop through the process list, from the most recent process
1416 * (the active one) to the oldest one (the first created, i.e.
1417 * the console leader process), and for each, send an event
1418 * (new processes are inserted at the head of the console process list).
1419 */
1420 current_entry = Console->ProcessList.Flink;
1421 while (current_entry != &Console->ProcessList)
1422 {
1423 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1424 current_entry = current_entry->Flink;
1425
1426 /*
1427 * Only processes belonging to the same process group are signaled.
1428 * If the process group ID is zero, then all the processes are signaled.
1429 */
1430 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
1431 {
1432 Status = ConSrvConsoleCtrlEvent(CtrlEvent, current);
1433 }
1434 }
1435
1436 return Status;
1437 }
1438
1439 VOID
ConSrvSetProcessFocus(IN PCSR_PROCESS CsrProcess,IN BOOLEAN SetForeground)1440 ConSrvSetProcessFocus(IN PCSR_PROCESS CsrProcess,
1441 IN BOOLEAN SetForeground)
1442 {
1443 // FIXME: Call NtUserSetInformationProcess (currently unimplemented!)
1444 // for setting Win32 foreground/background flags.
1445
1446 if (SetForeground)
1447 CsrSetForegroundPriority(CsrProcess);
1448 else
1449 CsrSetBackgroundPriority(CsrProcess);
1450 }
1451
1452 NTSTATUS NTAPI
ConSrvSetConsoleProcessFocus(IN PCONSRV_CONSOLE Console,IN BOOLEAN SetForeground)1453 ConSrvSetConsoleProcessFocus(IN PCONSRV_CONSOLE Console,
1454 IN BOOLEAN SetForeground)
1455 {
1456 PLIST_ENTRY current_entry;
1457 PCONSOLE_PROCESS_DATA current;
1458
1459 /* If the console is already being destroyed, just return */
1460 if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
1461 return STATUS_UNSUCCESSFUL;
1462
1463 /*
1464 * Loop through the process list, from the most recent process
1465 * to the oldest one, and for each, set its foreground priority.
1466 */
1467 current_entry = Console->ProcessList.Flink;
1468 while (current_entry != &Console->ProcessList)
1469 {
1470 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
1471 current_entry = current_entry->Flink;
1472
1473 ConSrvSetProcessFocus(current->Process, SetForeground);
1474 }
1475
1476 return STATUS_SUCCESS;
1477 }
1478
1479
1480 /* PUBLIC SERVER APIS *********************************************************/
1481
1482 /* API_NUMBER: ConsolepAlloc */
CON_API_NOCONSOLE(SrvAllocConsole,CONSOLE_ALLOCCONSOLE,AllocConsoleRequest)1483 CON_API_NOCONSOLE(SrvAllocConsole,
1484 CONSOLE_ALLOCCONSOLE, AllocConsoleRequest)
1485 {
1486 NTSTATUS Status = STATUS_SUCCESS;
1487 CONSOLE_INIT_INFO ConsoleInitInfo;
1488
1489 if (ProcessData->ConsoleHandle != NULL)
1490 {
1491 DPRINT1("Process already has a console\n");
1492 return STATUS_ACCESS_DENIED;
1493 }
1494
1495 if ( !CsrValidateMessageBuffer(ApiMessage,
1496 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
1497 1,
1498 sizeof(CONSOLE_START_INFO)) ||
1499 !CsrValidateMessageBuffer(ApiMessage,
1500 (PVOID*)&AllocConsoleRequest->ConsoleTitle,
1501 AllocConsoleRequest->TitleLength,
1502 sizeof(BYTE)) ||
1503 !CsrValidateMessageBuffer(ApiMessage,
1504 (PVOID*)&AllocConsoleRequest->Desktop,
1505 AllocConsoleRequest->DesktopLength,
1506 sizeof(BYTE)) ||
1507 !CsrValidateMessageBuffer(ApiMessage,
1508 (PVOID*)&AllocConsoleRequest->CurDir,
1509 AllocConsoleRequest->CurDirLength,
1510 sizeof(BYTE)) ||
1511 !CsrValidateMessageBuffer(ApiMessage,
1512 (PVOID*)&AllocConsoleRequest->AppName,
1513 AllocConsoleRequest->AppNameLength,
1514 sizeof(BYTE)) )
1515 {
1516 return STATUS_INVALID_PARAMETER;
1517 }
1518
1519 /* Initialize the console initialization info structure */
1520 ConsoleInitInfo.ConsoleStartInfo = AllocConsoleRequest->ConsoleStartInfo;
1521 ConsoleInitInfo.IsWindowVisible = TRUE; // The console window is always visible.
1522 ConsoleInitInfo.TitleLength = AllocConsoleRequest->TitleLength;
1523 ConsoleInitInfo.ConsoleTitle = AllocConsoleRequest->ConsoleTitle;
1524 ConsoleInitInfo.DesktopLength = AllocConsoleRequest->DesktopLength;
1525 ConsoleInitInfo.Desktop = AllocConsoleRequest->Desktop;
1526 ConsoleInitInfo.AppNameLength = AllocConsoleRequest->AppNameLength;
1527 ConsoleInitInfo.AppName = AllocConsoleRequest->AppName;
1528 ConsoleInitInfo.CurDirLength = AllocConsoleRequest->CurDirLength;
1529 ConsoleInitInfo.CurDir = AllocConsoleRequest->CurDir;
1530
1531 /* Initialize a new Console owned by the Console Leader Process */
1532 Status = ConSrvAllocateConsole(ProcessData,
1533 &AllocConsoleRequest->ConsoleStartInfo->InputHandle,
1534 &AllocConsoleRequest->ConsoleStartInfo->OutputHandle,
1535 &AllocConsoleRequest->ConsoleStartInfo->ErrorHandle,
1536 &ConsoleInitInfo);
1537 if (!NT_SUCCESS(Status))
1538 {
1539 DPRINT1("Console allocation failed\n");
1540 return Status;
1541 }
1542
1543 /* Set the Property-Dialog and Control-Dispatcher handlers */
1544 ProcessData->PropRoutine = AllocConsoleRequest->PropRoutine;
1545 ProcessData->CtrlRoutine = AllocConsoleRequest->CtrlRoutine;
1546
1547 return STATUS_SUCCESS;
1548 }
1549
1550 /* API_NUMBER: ConsolepAttach */
CON_API_NOCONSOLE(SrvAttachConsole,CONSOLE_ATTACHCONSOLE,AttachConsoleRequest)1551 CON_API_NOCONSOLE(SrvAttachConsole,
1552 CONSOLE_ATTACHCONSOLE, AttachConsoleRequest)
1553 {
1554 NTSTATUS Status = STATUS_SUCCESS;
1555 PCSR_PROCESS SourceProcess = NULL; // The parent process.
1556 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
1557 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
1558 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
1559
1560 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
1561
1562 if (TargetProcessData->ConsoleHandle != NULL)
1563 {
1564 DPRINT1("Process already has a console\n");
1565 return STATUS_ACCESS_DENIED;
1566 }
1567
1568 if (!CsrValidateMessageBuffer(ApiMessage,
1569 (PVOID*)&AttachConsoleRequest->ConsoleStartInfo,
1570 1,
1571 sizeof(CONSOLE_START_INFO)))
1572 {
1573 return STATUS_INVALID_PARAMETER;
1574 }
1575
1576 /* Check whether we try to attach to the parent's console */
1577 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
1578 {
1579 PROCESS_BASIC_INFORMATION ProcessInfo;
1580 ULONG Length = sizeof(ProcessInfo);
1581
1582 /* Get the real parent's PID */
1583
1584 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
1585 ProcessBasicInformation,
1586 &ProcessInfo,
1587 Length, &Length);
1588 if (!NT_SUCCESS(Status))
1589 {
1590 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = 0x%08lx\n", Status);
1591 return Status;
1592 }
1593
1594 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
1595 }
1596
1597 /* Lock the source process via its PID */
1598 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
1599 if (!NT_SUCCESS(Status)) return Status;
1600
1601 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
1602
1603 if (SourceProcessData->ConsoleHandle == NULL)
1604 {
1605 Status = STATUS_INVALID_HANDLE;
1606 goto Quit;
1607 }
1608
1609 /*
1610 * Inherit the console from the parent,
1611 * if any, otherwise return an error.
1612 */
1613 Status = ConSrvInheritConsole(TargetProcessData,
1614 SourceProcessData->ConsoleHandle,
1615 TRUE,
1616 &AttachConsoleRequest->ConsoleStartInfo->InputHandle,
1617 &AttachConsoleRequest->ConsoleStartInfo->OutputHandle,
1618 &AttachConsoleRequest->ConsoleStartInfo->ErrorHandle,
1619 AttachConsoleRequest->ConsoleStartInfo);
1620 if (!NT_SUCCESS(Status))
1621 {
1622 DPRINT1("Console inheritance failed\n");
1623 goto Quit;
1624 }
1625
1626 /* Set the Property-Dialog and Control-Dispatcher handlers */
1627 TargetProcessData->PropRoutine = AttachConsoleRequest->PropRoutine;
1628 TargetProcessData->CtrlRoutine = AttachConsoleRequest->CtrlRoutine;
1629
1630 Status = STATUS_SUCCESS;
1631
1632 Quit:
1633 /* Unlock the "source" process and exit */
1634 CsrUnlockProcess(SourceProcess);
1635 return Status;
1636 }
1637
1638 /* API_NUMBER: ConsolepFree */
CON_API_NOCONSOLE(SrvFreeConsole,CONSOLE_FREECONSOLE,FreeConsoleRequest)1639 CON_API_NOCONSOLE(SrvFreeConsole,
1640 CONSOLE_FREECONSOLE, FreeConsoleRequest)
1641 {
1642 /*
1643 * If this process doesn't have a console handle, bail out immediately.
1644 * Also the passed console handle should be the same as the process' one.
1645 */
1646 if ((FreeConsoleRequest->ConsoleHandle == NULL) ||
1647 (FreeConsoleRequest->ConsoleHandle != ProcessData->ConsoleHandle))
1648 {
1649 return STATUS_INVALID_HANDLE; // STATUS_ACCESS_DENIED;
1650 }
1651
1652 return ConSrvRemoveConsole(ProcessData);
1653 }
1654
1655 NTSTATUS NTAPI
1656 ConDrvGetConsoleMode(IN PCONSOLE Console,
1657 IN PCONSOLE_IO_OBJECT Object,
1658 OUT PULONG ConsoleMode);
1659 /* API_NUMBER: ConsolepGetMode */
CON_API(SrvGetConsoleMode,CONSOLE_GETSETCONSOLEMODE,ConsoleModeRequest)1660 CON_API(SrvGetConsoleMode,
1661 CONSOLE_GETSETCONSOLEMODE, ConsoleModeRequest)
1662 {
1663 NTSTATUS Status;
1664 PCONSOLE_IO_OBJECT Object;
1665 PULONG ConsoleMode = &ConsoleModeRequest->Mode;
1666
1667 Status = ConSrvGetObject(ProcessData,
1668 ConsoleModeRequest->Handle,
1669 &Object, NULL, GENERIC_READ, TRUE, 0);
1670 if (!NT_SUCCESS(Status))
1671 return Status;
1672
1673 ASSERT((PCONSOLE)Console == Object->Console);
1674
1675 /* Get the standard console modes */
1676 Status = ConDrvGetConsoleMode((PCONSOLE)Console, Object, ConsoleMode);
1677 if (NT_SUCCESS(Status))
1678 {
1679 /*
1680 * If getting the console modes succeeds, then retrieve
1681 * the extended CONSRV-specific input modes.
1682 */
1683 if (INPUT_BUFFER == Object->Type)
1684 {
1685 if (Console->InsertMode || Console->QuickEdit)
1686 {
1687 /* Windows also adds ENABLE_EXTENDED_FLAGS, even if it's not documented on MSDN */
1688 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
1689
1690 if (Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
1691 if (Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
1692 }
1693 }
1694 }
1695
1696 ConSrvReleaseObject(Object, TRUE);
1697 return Status;
1698 }
1699
1700 NTSTATUS NTAPI
1701 ConDrvSetConsoleMode(IN PCONSOLE Console,
1702 IN PCONSOLE_IO_OBJECT Object,
1703 IN ULONG ConsoleMode);
1704 /* API_NUMBER: ConsolepSetMode */
CON_API(SrvSetConsoleMode,CONSOLE_GETSETCONSOLEMODE,ConsoleModeRequest)1705 CON_API(SrvSetConsoleMode,
1706 CONSOLE_GETSETCONSOLEMODE, ConsoleModeRequest)
1707 {
1708 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
1709 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
1710 // NOTE: Vista+ ENABLE_AUTO_POSITION is also a control mode.
1711
1712 NTSTATUS Status;
1713 PCONSOLE_IO_OBJECT Object;
1714 ULONG ConsoleMode = ConsoleModeRequest->Mode;
1715
1716 Status = ConSrvGetObject(ProcessData,
1717 ConsoleModeRequest->Handle,
1718 &Object, NULL, GENERIC_WRITE, TRUE, 0);
1719 if (!NT_SUCCESS(Status))
1720 return Status;
1721
1722 ASSERT((PCONSOLE)Console == Object->Console);
1723
1724 /* Set the standard console modes (without the CONSRV-specific input modes) */
1725 ConsoleMode &= ~CONSOLE_VALID_CONTROL_MODES; // Remove CONSRV-specific input modes.
1726 Status = ConDrvSetConsoleMode((PCONSOLE)Console, Object, ConsoleMode);
1727 if (NT_SUCCESS(Status))
1728 {
1729 /*
1730 * If setting the console modes succeeds, then set
1731 * the extended CONSRV-specific input modes.
1732 */
1733 if (INPUT_BUFFER == Object->Type)
1734 {
1735 ConsoleMode = ConsoleModeRequest->Mode;
1736
1737 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
1738 {
1739 /*
1740 * If we use control mode flags without ENABLE_EXTENDED_FLAGS,
1741 * then consider the flags invalid.
1742 */
1743 if ((ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0)
1744 {
1745 Status = STATUS_INVALID_PARAMETER;
1746 }
1747 else
1748 {
1749 Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
1750 Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
1751 }
1752 }
1753 }
1754 }
1755
1756 ConSrvReleaseObject(Object, TRUE);
1757 return Status;
1758 }
1759
1760 /* API_NUMBER: ConsolepGetTitle */
CON_API(SrvGetConsoleTitle,CONSOLE_GETSETCONSOLETITLE,TitleRequest)1761 CON_API(SrvGetConsoleTitle,
1762 CONSOLE_GETSETCONSOLETITLE, TitleRequest)
1763 {
1764 ULONG Length;
1765
1766 if (!CsrValidateMessageBuffer(ApiMessage,
1767 (PVOID)&TitleRequest->Title,
1768 TitleRequest->Length,
1769 sizeof(BYTE)))
1770 {
1771 return STATUS_INVALID_PARAMETER;
1772 }
1773
1774 /* Copy title of the console to the user title buffer */
1775 if (TitleRequest->Unicode)
1776 {
1777 if (TitleRequest->Length >= sizeof(WCHAR))
1778 {
1779 Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
1780 RtlCopyMemory(TitleRequest->Title, Console->Title.Buffer, Length);
1781 ((PWCHAR)TitleRequest->Title)[Length / sizeof(WCHAR)] = UNICODE_NULL;
1782 TitleRequest->Length = Length;
1783 }
1784 else
1785 {
1786 TitleRequest->Length = Console->Title.Length;
1787 }
1788 }
1789 else
1790 {
1791 if (TitleRequest->Length >= sizeof(CHAR))
1792 {
1793 Length = min(TitleRequest->Length - sizeof(CHAR), Console->Title.Length / sizeof(WCHAR));
1794 Length = WideCharToMultiByte(Console->InputCodePage, 0,
1795 Console->Title.Buffer, Length,
1796 TitleRequest->Title, Length,
1797 NULL, NULL);
1798 ((PCHAR)TitleRequest->Title)[Length] = ANSI_NULL;
1799 TitleRequest->Length = Length;
1800 }
1801 else
1802 {
1803 TitleRequest->Length = Console->Title.Length / sizeof(WCHAR);
1804 }
1805 }
1806
1807 return STATUS_SUCCESS;
1808 }
1809
1810 /* API_NUMBER: ConsolepSetTitle */
CON_API(SrvSetConsoleTitle,CONSOLE_GETSETCONSOLETITLE,TitleRequest)1811 CON_API(SrvSetConsoleTitle,
1812 CONSOLE_GETSETCONSOLETITLE, TitleRequest)
1813 {
1814 PWCHAR Buffer;
1815 ULONG Length;
1816
1817 if (!CsrValidateMessageBuffer(ApiMessage,
1818 (PVOID)&TitleRequest->Title,
1819 TitleRequest->Length,
1820 sizeof(BYTE)))
1821 {
1822 return STATUS_INVALID_PARAMETER;
1823 }
1824
1825 if (TitleRequest->Unicode)
1826 {
1827 /* Length is in bytes */
1828 Length = TitleRequest->Length;
1829 }
1830 else
1831 {
1832 /* Use the console input CP for the conversion */
1833 Length = MultiByteToWideChar(Console->InputCodePage, 0,
1834 TitleRequest->Title, TitleRequest->Length,
1835 NULL, 0);
1836 /* The returned Length was in number of wchars, convert it in bytes */
1837 Length *= sizeof(WCHAR);
1838 }
1839
1840 /* Allocate a new buffer to hold the new title (NULL-terminated) */
1841 Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Length + sizeof(WCHAR));
1842 if (!Buffer)
1843 return STATUS_NO_MEMORY;
1844
1845 /* Free the old title */
1846 ConsoleFreeUnicodeString(&Console->Title);
1847
1848 /* Copy title to console */
1849 Console->Title.Buffer = Buffer;
1850 Console->Title.Length = Length;
1851 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
1852
1853 if (TitleRequest->Unicode)
1854 {
1855 RtlCopyMemory(Console->Title.Buffer, TitleRequest->Title, Console->Title.Length);
1856 }
1857 else
1858 {
1859 MultiByteToWideChar(Console->InputCodePage, 0,
1860 TitleRequest->Title, TitleRequest->Length,
1861 Console->Title.Buffer,
1862 Console->Title.Length / sizeof(WCHAR));
1863 }
1864
1865 /* NULL-terminate */
1866 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = UNICODE_NULL;
1867
1868 TermChangeTitle(Console);
1869
1870 return STATUS_SUCCESS;
1871 }
1872
1873 NTSTATUS NTAPI
1874 ConDrvGetConsoleCP(IN PCONSOLE Console,
1875 OUT PUINT CodePage,
1876 IN BOOLEAN OutputCP);
1877 /* API_NUMBER: ConsolepGetCP */
CON_API(SrvGetConsoleCP,CONSOLE_GETINPUTOUTPUTCP,GetConsoleCPRequest)1878 CON_API(SrvGetConsoleCP,
1879 CONSOLE_GETINPUTOUTPUTCP, GetConsoleCPRequest)
1880 {
1881 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
1882 GetConsoleCPRequest->OutputCP ? "Output" : "Input");
1883
1884 return ConDrvGetConsoleCP((PCONSOLE)Console,
1885 &GetConsoleCPRequest->CodePage,
1886 GetConsoleCPRequest->OutputCP);
1887 }
1888
1889 NTSTATUS NTAPI
1890 ConDrvSetConsoleCP(IN PCONSOLE Console,
1891 IN UINT CodePage,
1892 IN BOOLEAN OutputCP);
1893 /* API_NUMBER: ConsolepSetCP */
CON_API(SrvSetConsoleCP,CONSOLE_SETINPUTOUTPUTCP,SetConsoleCPRequest)1894 CON_API(SrvSetConsoleCP,
1895 CONSOLE_SETINPUTOUTPUTCP, SetConsoleCPRequest)
1896 {
1897 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
1898 SetConsoleCPRequest->OutputCP ? "Output" : "Input");
1899
1900 return ConDrvSetConsoleCP((PCONSOLE)Console,
1901 SetConsoleCPRequest->CodePage,
1902 SetConsoleCPRequest->OutputCP);
1903 }
1904
1905 /* API_NUMBER: ConsolepGetProcessList */
CON_API(SrvGetConsoleProcessList,CONSOLE_GETPROCESSLIST,GetProcessListRequest)1906 CON_API(SrvGetConsoleProcessList,
1907 CONSOLE_GETPROCESSLIST, GetProcessListRequest)
1908 {
1909 if (!CsrValidateMessageBuffer(ApiMessage,
1910 (PVOID)&GetProcessListRequest->ProcessIdsList,
1911 GetProcessListRequest->ProcessCount,
1912 sizeof(DWORD)))
1913 {
1914 return STATUS_INVALID_PARAMETER;
1915 }
1916
1917 return ConSrvGetConsoleProcessList(Console,
1918 GetProcessListRequest->ProcessIdsList,
1919 GetProcessListRequest->ProcessCount,
1920 &GetProcessListRequest->ProcessCount);
1921 }
1922
1923 /* API_NUMBER: ConsolepGenerateCtrlEvent */
CON_API(SrvGenerateConsoleCtrlEvent,CONSOLE_GENERATECTRLEVENT,GenerateCtrlEventRequest)1924 CON_API(SrvGenerateConsoleCtrlEvent,
1925 CONSOLE_GENERATECTRLEVENT, GenerateCtrlEventRequest)
1926 {
1927 return ConSrvConsoleProcessCtrlEvent(Console,
1928 GenerateCtrlEventRequest->ProcessGroupId,
1929 GenerateCtrlEventRequest->CtrlEvent);
1930 }
1931
1932 /* API_NUMBER: ConsolepNotifyLastClose */
CON_API(SrvConsoleNotifyLastClose,CONSOLE_NOTIFYLASTCLOSE,NotifyLastCloseRequest)1933 CON_API(SrvConsoleNotifyLastClose,
1934 CONSOLE_NOTIFYLASTCLOSE, NotifyLastCloseRequest)
1935 {
1936 /* Only one process is allowed to be registered for last close notification */
1937 if (Console->NotifyLastClose)
1938 return STATUS_ACCESS_DENIED;
1939
1940 Console->NotifyLastClose = TRUE;
1941 Console->NotifiedLastCloseProcess = ProcessData;
1942 return STATUS_SUCCESS;
1943 }
1944
1945 /* API_NUMBER: ConsolepGetMouseInfo */
CON_API(SrvGetConsoleMouseInfo,CONSOLE_GETMOUSEINFO,GetMouseInfoRequest)1946 CON_API(SrvGetConsoleMouseInfo,
1947 CONSOLE_GETMOUSEINFO, GetMouseInfoRequest)
1948 {
1949 /* Just retrieve the number of buttons of the mouse attached to this console */
1950 GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
1951 return STATUS_SUCCESS;
1952 }
1953
1954 /* API_NUMBER: ConsolepSetKeyShortcuts */
CSR_API(SrvSetConsoleKeyShortcuts)1955 CSR_API(SrvSetConsoleKeyShortcuts)
1956 {
1957 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1958 return STATUS_NOT_IMPLEMENTED;
1959 }
1960
1961 /* API_NUMBER: ConsolepGetKeyboardLayoutName */
CON_API(SrvGetConsoleKeyboardLayoutName,CONSOLE_GETKBDLAYOUTNAME,GetKbdLayoutNameRequest)1962 CON_API(SrvGetConsoleKeyboardLayoutName,
1963 CONSOLE_GETKBDLAYOUTNAME, GetKbdLayoutNameRequest)
1964 {
1965 /* Retrieve the keyboard layout name of the system */
1966 if (GetKbdLayoutNameRequest->Ansi)
1967 GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1968 else
1969 GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1970
1971 return STATUS_SUCCESS;
1972 }
1973
1974 /* API_NUMBER: ConsolepCharType */
CSR_API(SrvGetConsoleCharType)1975 CSR_API(SrvGetConsoleCharType)
1976 {
1977 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1978 return STATUS_NOT_IMPLEMENTED;
1979 }
1980
1981 /* API_NUMBER: ConsolepSetLocalEUDC */
CSR_API(SrvSetConsoleLocalEUDC)1982 CSR_API(SrvSetConsoleLocalEUDC)
1983 {
1984 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1985 return STATUS_NOT_IMPLEMENTED;
1986 }
1987
1988 /* API_NUMBER: ConsolepSetCursorMode */
CSR_API(SrvSetConsoleCursorMode)1989 CSR_API(SrvSetConsoleCursorMode)
1990 {
1991 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1992 return STATUS_NOT_IMPLEMENTED;
1993 }
1994
1995 /* API_NUMBER: ConsolepGetCursorMode */
CSR_API(SrvGetConsoleCursorMode)1996 CSR_API(SrvGetConsoleCursorMode)
1997 {
1998 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1999 return STATUS_NOT_IMPLEMENTED;
2000 }
2001
2002 /* API_NUMBER: ConsolepGetNlsMode */
CSR_API(SrvGetConsoleNlsMode)2003 CSR_API(SrvGetConsoleNlsMode)
2004 {
2005 DPRINT1("%s not yet implemented\n", __FUNCTION__);
2006 return STATUS_NOT_IMPLEMENTED;
2007 }
2008
2009 /* API_NUMBER: ConsolepSetNlsMode */
CSR_API(SrvSetConsoleNlsMode)2010 CSR_API(SrvSetConsoleNlsMode)
2011 {
2012 DPRINT1("%s not yet implemented\n", __FUNCTION__);
2013 return STATUS_NOT_IMPLEMENTED;
2014 }
2015
2016 /* API_NUMBER: ConsolepGetLangId */
CON_API(SrvGetConsoleLangId,CONSOLE_GETLANGID,LangIdRequest)2017 CON_API(SrvGetConsoleLangId,
2018 CONSOLE_GETLANGID, LangIdRequest)
2019 {
2020 /*
2021 * Quoting MS Terminal, see function GetConsoleLangId() at
2022 * https://github.com/microsoft/terminal/blob/main/src/host/srvinit.cpp#L655
2023 * "Only attempt to return the Lang ID if the Windows ACP on console
2024 * launch was an East Asian Code Page."
2025 *
2026 * The underlying logic is as follows:
2027 *
2028 * - When the current user's UI language is *not* CJK, the user expects
2029 * to not see any CJK output to the console by default, even if its
2030 * output has been set to a CJK code page (this is possible when CJK
2031 * fonts are installed on the system). That is, of course, unless if
2032 * the attached console program chooses to actually output CJK text.
2033 * Whatever current language of the running program's thread should
2034 * be kept: STATUS_NOT_SUPPORTED is returned.
2035 *
2036 * - When the current user's UI language *is* CJK, the user expects to
2037 * see CJK output to the console by default when its code page is CJK.
2038 * A valid LangId is returned in this case to ensure this.
2039 * However, if the console code page is not CJK, then it is evident
2040 * that CJK text will not be able to be correctly shown, and therefore
2041 * we should fall back to a standard language that can be shown, namely
2042 * en-US english, instead of keeping the current language.
2043 */
2044
2045 BYTE UserCharSet = CodePageToCharSet(GetACP());
2046 if (!IsCJKCharSet(UserCharSet))
2047 return STATUS_NOT_SUPPORTED;
2048
2049 /* Return a "best-suited" language ID corresponding
2050 * to the active console output code page. */
2051 switch (Console->OutputCodePage)
2052 {
2053 /** ReactOS-specific: do nothing if the code page is UTF-8. This will allow
2054 ** programs to naturally output in whatever current language they are. **/
2055 case CP_UTF8:
2056 return STATUS_NOT_SUPPORTED;
2057 /** End ReactOS-specific **/
2058 case CP_JAPANESE:
2059 LangIdRequest->LangId = MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT);
2060 break;
2061 case CP_KOREAN:
2062 LangIdRequest->LangId = MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
2063 break;
2064 case CP_CHINESE_SIMPLIFIED:
2065 LangIdRequest->LangId = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
2066 break;
2067 case CP_CHINESE_TRADITIONAL:
2068 LangIdRequest->LangId = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
2069 break;
2070 default:
2071 /* Default to en-US english otherwise */
2072 LangIdRequest->LangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
2073 break;
2074 }
2075
2076 return STATUS_SUCCESS;
2077 }
2078
2079 /* EOF */
2080