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