xref: /reactos/win32ss/user/winsrv/consrv/console.c (revision cdf90707)
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
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
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
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
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
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
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
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
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
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     }
469     CoUninitialize();
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
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
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
859 ConioPause(PCONSRV_CONSOLE Console, UCHAR Flags)
860 {
861     Console->PauseFlags |= Flags;
862     ConDrvPause((PCONSOLE)Console);
863 }
864 
865 VOID
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
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
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
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
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
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
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
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
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
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
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
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
1955 CSR_API(SrvSetConsoleKeyShortcuts)
1956 {
1957     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1958     return STATUS_NOT_IMPLEMENTED;
1959 }
1960 
1961 /* API_NUMBER: ConsolepGetKeyboardLayoutName */
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 */
1975 CSR_API(SrvGetConsoleCharType)
1976 {
1977     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1978     return STATUS_NOT_IMPLEMENTED;
1979 }
1980 
1981 /* API_NUMBER: ConsolepSetLocalEUDC */
1982 CSR_API(SrvSetConsoleLocalEUDC)
1983 {
1984     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1985     return STATUS_NOT_IMPLEMENTED;
1986 }
1987 
1988 /* API_NUMBER: ConsolepSetCursorMode */
1989 CSR_API(SrvSetConsoleCursorMode)
1990 {
1991     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1992     return STATUS_NOT_IMPLEMENTED;
1993 }
1994 
1995 /* API_NUMBER: ConsolepGetCursorMode */
1996 CSR_API(SrvGetConsoleCursorMode)
1997 {
1998     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1999     return STATUS_NOT_IMPLEMENTED;
2000 }
2001 
2002 /* API_NUMBER: ConsolepGetNlsMode */
2003 CSR_API(SrvGetConsoleNlsMode)
2004 {
2005     DPRINT1("%s not yet implemented\n", __FUNCTION__);
2006     return STATUS_NOT_IMPLEMENTED;
2007 }
2008 
2009 /* API_NUMBER: ConsolepSetNlsMode */
2010 CSR_API(SrvSetConsoleNlsMode)
2011 {
2012     DPRINT1("%s not yet implemented\n", __FUNCTION__);
2013     return STATUS_NOT_IMPLEMENTED;
2014 }
2015 
2016 /* API_NUMBER: ConsolepGetLangId */
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