[WIN32K]
[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 PCSR_PROCESS ConsoleLeaderProcess);
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 /*
480 * FIXME!! Because of a strange bug we have in PrivateExtractIconExW
481 * (see r65683 for more details), we cannot use this API to extract
482 * at the same time the large and small icons from the app.
483 * Instead we just use PrivateExtractIconsW.
484 *
485 PrivateExtractIconExW(IconPath,
486 ConsoleInitInfo->ConsoleStartInfo->IconIndex,
487 &hIcon,
488 &hIconSm,
489 1);
490 */
491 PrivateExtractIconsW(IconPath,
492 ConsoleInitInfo->ConsoleStartInfo->IconIndex,
493 32, 32,
494 &hIcon, NULL, 1, LR_COPYFROMRESOURCE);
495 PrivateExtractIconsW(IconPath,
496 ConsoleInitInfo->ConsoleStartInfo->IconIndex,
497 16, 16,
498 &hIconSm, NULL, 1, LR_COPYFROMRESOURCE);
499
500 DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
501 if (hIcon != NULL) ConsoleInitInfo->ConsoleStartInfo->hIcon = hIcon;
502 if (hIconSm != NULL) ConsoleInitInfo->ConsoleStartInfo->hIconSm = hIconSm;
503 }
504 }
505
506 // FIXME: See the previous FIXME above.
507 RetVal = FALSE;
508
509 return RetVal;
510 }
511
512 NTSTATUS NTAPI
513 ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,
514 OUT PCONSRV_CONSOLE* NewConsole,
515 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
516 IN PCSR_PROCESS ConsoleLeaderProcess)
517 {
518 NTSTATUS Status;
519 HANDLE ConsoleHandle;
520 PCONSRV_CONSOLE Console;
521 CONSOLE_INFO ConsoleInfo;
522 ULONG ConsoleLeaderProcessId = HandleToUlong(ConsoleLeaderProcess->ClientId.UniqueProcess);
523 SIZE_T Length = 0;
524
525 TERMINAL Terminal; /* The ConSrv terminal for this console */
526
527 if (NewConsole == NULL || ConsoleInitInfo == NULL)
528 return STATUS_INVALID_PARAMETER;
529
530 *NewConsole = NULL;
531
532 /*
533 * Load the console settings
534 */
535
536 /* 1. Load the default settings */
537 ConSrvGetDefaultSettings(&ConsoleInfo, ConsoleLeaderProcessId);
538
539 /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
540 Length = min(ConsoleInitInfo->TitleLength,
541 sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1);
542 wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleInitInfo->ConsoleTitle, Length);
543 ConsoleInfo.ConsoleTitle[Length] = L'\0'; // NULL-terminate it.
544
545 /* 3. Initialize the ConSrv terminal */
546 Status = ConSrvInitTerminal(&Terminal,
547 &ConsoleInfo,
548 ConsoleInitInfo,
549 ConsoleLeaderProcess);
550 if (!NT_SUCCESS(Status))
551 {
552 DPRINT1("CONSRV: Failed to initialize a terminal, Status = 0x%08lx\n", Status);
553 return Status;
554 }
555 DPRINT("CONSRV: Terminal initialized\n");
556
557 /*
558 * Load per-application terminal settings.
559 *
560 * Check whether the process creating the console was launched via
561 * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
562 * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
563 */
564 // if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) // FIXME!! (for icon loading)
565 {
566 if (!LoadShellLinkConsoleInfo(&ConsoleInfo, ConsoleInitInfo))
567 {
568 ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
569 }
570 }
571
572 /*
573 * 4. Load the remaining console settings via the registry.
574 */
575 if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
576 {
577 /*
578 * Either we weren't created by an app launched via a shell-link,
579 * or we failed to load shell-link console properties.
580 * Therefore, load the console infos for the application from the registry.
581 */
582 ConSrvReadUserSettings(&ConsoleInfo, ConsoleLeaderProcessId);
583
584 /*
585 * Now, update them with the properties the user might gave to us
586 * via the STARTUPINFO structure before calling CreateProcess
587 * (and which was transmitted via the ConsoleStartInfo structure).
588 * We therefore overwrite the values read in the registry.
589 */
590 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
591 {
592 ConsoleInfo.ScreenAttrib = (USHORT)ConsoleInitInfo->ConsoleStartInfo->wFillAttribute;
593 }
594 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
595 {
596 ConsoleInfo.ScreenBufferSize = ConsoleInitInfo->ConsoleStartInfo->dwScreenBufferSize;
597 }
598 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
599 {
600 ConsoleInfo.ConsoleSize = ConsoleInitInfo->ConsoleStartInfo->dwWindowSize;
601 }
602 }
603
604 /* Set-up the code page */
605 ConsoleInfo.CodePage = GetOEMCP();
606
607 /* Initialize a new console via the driver */
608 Status = ConDrvInitConsole(&Console, &ConsoleInfo);
609 if (!NT_SUCCESS(Status))
610 {
611 DPRINT1("Creating a new console failed, Status = 0x%08lx\n", Status);
612 ConSrvDeinitTerminal(&Terminal);
613 return Status;
614 }
615
616 ASSERT(Console);
617 DPRINT("Console initialized\n");
618
619 /*** Register ConSrv features ***/
620
621 /* Initialize the console title */
622 #if 0
623 WCHAR DefaultTitle[128];
624 #endif
625 ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo.ConsoleTitle);
626 #if 0
627 if (ConsoleInfo.ConsoleTitle[0] == L'\0')
628 {
629 if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
630 {
631 ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
632 }
633 else
634 {
635 ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
636 }
637 }
638 else
639 {
640 #endif
641 ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo.ConsoleTitle);
642 #if 0
643 }
644 #endif
645
646 /* Initialize process support */
647 InitializeListHead(&Console->ProcessList);
648 Console->NotifiedLastCloseProcess = NULL;
649 Console->NotifyLastClose = FALSE;
650
651 /* Initialize pausing support */
652 Console->PauseFlags = 0;
653 InitializeListHead(&Console->ReadWaitQueue);
654 InitializeListHead(&Console->WriteWaitQueue);
655
656 /* Initialize the alias and history buffers */
657 Console->Aliases = NULL;
658 InitializeListHead(&Console->HistoryBuffers);
659 Console->HistoryBufferSize = ConsoleInfo.HistoryBufferSize;
660 Console->NumberOfHistoryBuffers = ConsoleInfo.NumberOfHistoryBuffers;
661 Console->HistoryNoDup = ConsoleInfo.HistoryNoDup;
662
663 /* Initialize the Input Line Discipline */
664 Console->LineBuffer = NULL;
665 Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
666 Console->LineComplete = Console->LineUpPressed = FALSE;
667 // LineWakeupMask
668 Console->LineInsertToggle =
669 Console->InsertMode = ConsoleInfo.InsertMode;
670 Console->QuickEdit = ConsoleInfo.QuickEdit;
671
672 /* Popup windows */
673 InitializeListHead(&Console->PopupWindows);
674
675 /* Colour table */
676 memcpy(Console->Colors, ConsoleInfo.Colors, sizeof(ConsoleInfo.Colors));
677
678 /* Create the Initialization Events */
679 Status = NtCreateEvent(&Console->InitEvents[INIT_SUCCESS], EVENT_ALL_ACCESS,
680 NULL, NotificationEvent, FALSE);
681 if (!NT_SUCCESS(Status))
682 {
683 DPRINT1("NtCreateEvent(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
684 ConDrvDeleteConsole(Console);
685 ConSrvDeinitTerminal(&Terminal);
686 return Status;
687 }
688 Status = NtCreateEvent(&Console->InitEvents[INIT_FAILURE], EVENT_ALL_ACCESS,
689 NULL, NotificationEvent, FALSE);
690 if (!NT_SUCCESS(Status))
691 {
692 DPRINT1("NtCreateEvent(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
693 NtClose(Console->InitEvents[INIT_SUCCESS]);
694 ConDrvDeleteConsole(Console);
695 ConSrvDeinitTerminal(&Terminal);
696 return Status;
697 }
698
699 /*
700 * Attach the ConSrv terminal to the console.
701 * This call makes a copy of our local Terminal variable.
702 */
703 Status = ConDrvRegisterTerminal(Console, &Terminal);
704 if (!NT_SUCCESS(Status))
705 {
706 DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
707 NtClose(Console->InitEvents[INIT_FAILURE]);
708 NtClose(Console->InitEvents[INIT_SUCCESS]);
709 ConDrvDeleteConsole(Console);
710 ConSrvDeinitTerminal(&Terminal);
711 return Status;
712 }
713 DPRINT("Terminal registered\n");
714
715 /* All went right, so add the console to the list */
716 Status = InsertConsole(&ConsoleHandle, Console);
717
718 // FIXME! We do not support at all asynchronous console creation!
719 NtSetEvent(Console->InitEvents[INIT_SUCCESS], NULL);
720 // NtSetEvent(Console->InitEvents[INIT_FAILURE], NULL);
721
722 /* Return the newly created console to the caller and a success code too */
723 *NewConsoleHandle = ConsoleHandle;
724 *NewConsole = Console;
725 return STATUS_SUCCESS;
726 }
727
728 VOID NTAPI
729 ConSrvDeleteConsole(PCONSRV_CONSOLE Console)
730 {
731 DPRINT("ConSrvDeleteConsole\n");
732
733 // FIXME: Send a terminate message to all the processes owning this console
734
735 /* Remove the console from the list */
736 RemoveConsoleByPointer(Console);
737
738 /* Destroy the Initialization Events */
739 NtClose(Console->InitEvents[INIT_FAILURE]);
740 NtClose(Console->InitEvents[INIT_SUCCESS]);
741
742 /* Clean the Input Line Discipline */
743 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
744
745 /* Clean aliases and history */
746 IntDeleteAllAliases(Console);
747 HistoryDeleteBuffers(Console);
748
749 /* Free the console title */
750 ConsoleFreeUnicodeString(&Console->OriginalTitle);
751 ConsoleFreeUnicodeString(&Console->Title);
752
753 /* Now, call the driver. ConDrvDeregisterTerminal is called on-demand. */
754 ConDrvDeleteConsole((PCONSOLE)Console);
755
756 /* Deinit the ConSrv terminal */
757 // FIXME!!
758 // ConSrvDeinitTerminal(&Terminal);
759 }
760
761
762
763
764
765
766 static NTSTATUS
767 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
768 IN PCONSOLE_PROCESS_DATA ProcessData,
769 IN ULONG Timeout)
770 {
771 NTSTATUS Status = STATUS_SUCCESS;
772
773 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
774
775 if (ProcessData->CtrlRoutine)
776 {
777 _SEH2_TRY
778 {
779 HANDLE Thread = NULL;
780
781 _SEH2_TRY
782 {
783 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
784 ProcessData->CtrlRoutine,
785 UlongToPtr(CtrlEvent), 0, NULL);
786 if (NULL == Thread)
787 {
788 Status = RtlGetLastNtStatus();
789 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
790 }
791 else
792 {
793 DPRINT("ProcessData->CtrlRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n",
794 ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
795 WaitForSingleObject(Thread, Timeout);
796 }
797 }
798 _SEH2_FINALLY
799 {
800 CloseHandle(Thread);
801 }
802 _SEH2_END;
803 }
804 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
805 {
806 Status = _SEH2_GetExceptionCode();
807 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
808 }
809 _SEH2_END;
810 }
811
812 return Status;
813 }
814
815 NTSTATUS
816 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
817 IN PCONSOLE_PROCESS_DATA ProcessData)
818 {
819 return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
820 }
821
822 PCONSOLE_PROCESS_DATA NTAPI
823 ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)
824 {
825 if (Console == NULL) return NULL;
826
827 return CONTAINING_RECORD(Console->ProcessList.Blink,
828 CONSOLE_PROCESS_DATA,
829 ConsoleLink);
830 }
831
832 NTSTATUS NTAPI
833 ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,
834 IN OUT PULONG ProcessIdsList,
835 IN ULONG MaxIdListItems,
836 OUT PULONG ProcessIdsTotal)
837 {
838 PCONSOLE_PROCESS_DATA current;
839 PLIST_ENTRY current_entry;
840
841 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
842 return STATUS_INVALID_PARAMETER;
843
844 *ProcessIdsTotal = 0;
845
846 for (current_entry = Console->ProcessList.Flink;
847 current_entry != &Console->ProcessList;
848 current_entry = current_entry->Flink)
849 {
850 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
851 if (++(*ProcessIdsTotal) <= MaxIdListItems)
852 {
853 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
854 }
855 }
856
857 return STATUS_SUCCESS;
858 }
859
860 // ConSrvGenerateConsoleCtrlEvent
861 NTSTATUS NTAPI
862 ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,
863 IN ULONG ProcessGroupId,
864 IN ULONG CtrlEvent)
865 {
866 NTSTATUS Status = STATUS_SUCCESS;
867 PLIST_ENTRY current_entry;
868 PCONSOLE_PROCESS_DATA current;
869
870 /* If the console is already being destroyed, just return */
871 if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
872 return STATUS_UNSUCCESSFUL;
873
874 /*
875 * Loop through the process list, from the most recent process
876 * (the active one) to the oldest one (the first created, i.e.
877 * the console leader process), and for each, send an event
878 * (new processes are inserted at the head of the console process list).
879 */
880 current_entry = Console->ProcessList.Flink;
881 while (current_entry != &Console->ProcessList)
882 {
883 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
884 current_entry = current_entry->Flink;
885
886 /*
887 * Only processes belonging to the same process group are signaled.
888 * If the process group ID is zero, then all the processes are signaled.
889 */
890 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
891 {
892 Status = ConSrvConsoleCtrlEvent(CtrlEvent, current);
893 }
894 }
895
896 return Status;
897 }
898
899
900 /* PUBLIC SERVER APIS *********************************************************/
901
902 CSR_API(SrvAllocConsole)
903 {
904 NTSTATUS Status = STATUS_SUCCESS;
905 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
906 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
907 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
908 CONSOLE_INIT_INFO ConsoleInitInfo;
909
910 if (ProcessData->ConsoleHandle != NULL)
911 {
912 DPRINT1("Process already has a console\n");
913 return STATUS_ACCESS_DENIED;
914 }
915
916 if ( !CsrValidateMessageBuffer(ApiMessage,
917 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
918 1,
919 sizeof(CONSOLE_START_INFO)) ||
920 !CsrValidateMessageBuffer(ApiMessage,
921 (PVOID*)&AllocConsoleRequest->ConsoleTitle,
922 AllocConsoleRequest->TitleLength,
923 sizeof(BYTE)) ||
924 !CsrValidateMessageBuffer(ApiMessage,
925 (PVOID*)&AllocConsoleRequest->Desktop,
926 AllocConsoleRequest->DesktopLength,
927 sizeof(BYTE)) ||
928 !CsrValidateMessageBuffer(ApiMessage,
929 (PVOID*)&AllocConsoleRequest->CurDir,
930 AllocConsoleRequest->CurDirLength,
931 sizeof(BYTE)) ||
932 !CsrValidateMessageBuffer(ApiMessage,
933 (PVOID*)&AllocConsoleRequest->AppName,
934 AllocConsoleRequest->AppNameLength,
935 sizeof(BYTE)) )
936 {
937 return STATUS_INVALID_PARAMETER;
938 }
939
940 /* Initialize the console initialization info structure */
941 ConsoleInitInfo.ConsoleStartInfo = AllocConsoleRequest->ConsoleStartInfo;
942 ConsoleInitInfo.IsWindowVisible = TRUE; // The console window is always visible.
943 ConsoleInitInfo.TitleLength = AllocConsoleRequest->TitleLength;
944 ConsoleInitInfo.ConsoleTitle = AllocConsoleRequest->ConsoleTitle;
945 ConsoleInitInfo.DesktopLength = AllocConsoleRequest->DesktopLength;
946 ConsoleInitInfo.Desktop = AllocConsoleRequest->Desktop;
947 ConsoleInitInfo.AppNameLength = AllocConsoleRequest->AppNameLength;
948 ConsoleInitInfo.AppName = AllocConsoleRequest->AppName;
949 ConsoleInitInfo.CurDirLength = AllocConsoleRequest->CurDirLength;
950 ConsoleInitInfo.CurDir = AllocConsoleRequest->CurDir;
951
952 /* Initialize a new Console owned by the Console Leader Process */
953 Status = ConSrvAllocateConsole(ProcessData,
954 &AllocConsoleRequest->ConsoleStartInfo->InputHandle,
955 &AllocConsoleRequest->ConsoleStartInfo->OutputHandle,
956 &AllocConsoleRequest->ConsoleStartInfo->ErrorHandle,
957 &ConsoleInitInfo);
958 if (!NT_SUCCESS(Status))
959 {
960 DPRINT1("Console allocation failed\n");
961 return Status;
962 }
963
964 /* Set the Property-Dialog and Control-Dispatcher handlers */
965 ProcessData->PropRoutine = AllocConsoleRequest->PropRoutine;
966 ProcessData->CtrlRoutine = AllocConsoleRequest->CtrlRoutine;
967
968 return STATUS_SUCCESS;
969 }
970
971 CSR_API(SrvAttachConsole)
972 {
973 NTSTATUS Status = STATUS_SUCCESS;
974 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
975 PCSR_PROCESS SourceProcess = NULL; // The parent process.
976 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
977 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
978 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
979
980 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
981
982 if (TargetProcessData->ConsoleHandle != NULL)
983 {
984 DPRINT1("Process already has a console\n");
985 return STATUS_ACCESS_DENIED;
986 }
987
988 if (!CsrValidateMessageBuffer(ApiMessage,
989 (PVOID*)&AttachConsoleRequest->ConsoleStartInfo,
990 1,
991 sizeof(CONSOLE_START_INFO)))
992 {
993 return STATUS_INVALID_PARAMETER;
994 }
995
996 /* Check whether we try to attach to the parent's console */
997 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
998 {
999 PROCESS_BASIC_INFORMATION ProcessInfo;
1000 ULONG Length = sizeof(ProcessInfo);
1001
1002 /* Get the real parent's ID */
1003
1004 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
1005 ProcessBasicInformation,
1006 &ProcessInfo,
1007 Length, &Length);
1008 if (!NT_SUCCESS(Status))
1009 {
1010 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = 0x%08lx\n", Status);
1011 return Status;
1012 }
1013
1014 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
1015 }
1016
1017 /* Lock the source process via its PID */
1018 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
1019 if (!NT_SUCCESS(Status)) return Status;
1020
1021 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
1022
1023 if (SourceProcessData->ConsoleHandle == NULL)
1024 {
1025 Status = STATUS_INVALID_HANDLE;
1026 goto Quit;
1027 }
1028
1029 /*
1030 * Inherit the console from the parent,
1031 * if any, otherwise return an error.
1032 */
1033 Status = ConSrvInheritConsole(TargetProcessData,
1034 SourceProcessData->ConsoleHandle,
1035 TRUE,
1036 &AttachConsoleRequest->ConsoleStartInfo->InputHandle,
1037 &AttachConsoleRequest->ConsoleStartInfo->OutputHandle,
1038 &AttachConsoleRequest->ConsoleStartInfo->ErrorHandle,
1039 AttachConsoleRequest->ConsoleStartInfo);
1040 if (!NT_SUCCESS(Status))
1041 {
1042 DPRINT1("Console inheritance failed\n");
1043 goto Quit;
1044 }
1045
1046 /* Set the Property-Dialog and Control-Dispatcher handlers */
1047 TargetProcessData->PropRoutine = AttachConsoleRequest->PropRoutine;
1048 TargetProcessData->CtrlRoutine = AttachConsoleRequest->CtrlRoutine;
1049
1050 Status = STATUS_SUCCESS;
1051
1052 Quit:
1053 /* Unlock the "source" process and exit */
1054 CsrUnlockProcess(SourceProcess);
1055 return Status;
1056 }
1057
1058 CSR_API(SrvFreeConsole)
1059 {
1060 return ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
1061 }
1062
1063 NTSTATUS NTAPI
1064 ConDrvGetConsoleMode(IN PCONSOLE Console,
1065 IN PCONSOLE_IO_OBJECT Object,
1066 OUT PULONG ConsoleMode);
1067 CSR_API(SrvGetConsoleMode)
1068 {
1069 NTSTATUS Status;
1070 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1071 PCONSOLE_IO_OBJECT Object;
1072
1073 PULONG ConsoleMode = &ConsoleModeRequest->Mode;
1074
1075 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1076 ConsoleModeRequest->Handle,
1077 &Object, NULL, GENERIC_READ, TRUE, 0);
1078 if (!NT_SUCCESS(Status)) return Status;
1079
1080 /* Get the standard console modes */
1081 Status = ConDrvGetConsoleMode(Object->Console, Object,
1082 ConsoleMode);
1083 if (NT_SUCCESS(Status))
1084 {
1085 /*
1086 * If getting the console modes succeeds, then retrieve
1087 * the extended CONSRV-specific input modes.
1088 */
1089 if (INPUT_BUFFER == Object->Type)
1090 {
1091 if (Object->Console->InsertMode || Object->Console->QuickEdit)
1092 {
1093 /* Windows also adds ENABLE_EXTENDED_FLAGS, even if it's not documented on MSDN */
1094 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
1095
1096 if (Object->Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
1097 if (Object->Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
1098 }
1099 }
1100 }
1101
1102 ConSrvReleaseObject(Object, TRUE);
1103 return Status;
1104 }
1105
1106 NTSTATUS NTAPI
1107 ConDrvSetConsoleMode(IN PCONSOLE Console,
1108 IN PCONSOLE_IO_OBJECT Object,
1109 IN ULONG ConsoleMode);
1110 CSR_API(SrvSetConsoleMode)
1111 {
1112 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
1113 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
1114
1115 NTSTATUS Status;
1116 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1117 PCONSOLE_IO_OBJECT Object;
1118
1119 ULONG ConsoleMode = ConsoleModeRequest->Mode;
1120
1121 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1122 ConsoleModeRequest->Handle,
1123 &Object, NULL, GENERIC_WRITE, TRUE, 0);
1124 if (!NT_SUCCESS(Status)) return Status;
1125
1126 /* Set the standard console modes (without the CONSRV-specific input modes) */
1127 ConsoleMode &= ~CONSOLE_VALID_CONTROL_MODES; // Remove CONSRV-specific input modes.
1128 Status = ConDrvSetConsoleMode(Object->Console, Object,
1129 ConsoleMode);
1130 if (NT_SUCCESS(Status))
1131 {
1132 /*
1133 * If setting the console modes succeeds, then set
1134 * the extended CONSRV-specific input modes.
1135 */
1136 if (INPUT_BUFFER == Object->Type)
1137 {
1138 ConsoleMode = ConsoleModeRequest->Mode;
1139
1140 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
1141 {
1142 /*
1143 * If we use control mode flags without ENABLE_EXTENDED_FLAGS,
1144 * then consider the flags invalid.
1145 */
1146 if ((ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0)
1147 {
1148 Status = STATUS_INVALID_PARAMETER;
1149 }
1150 else
1151 {
1152 Object->Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
1153 Object->Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
1154 }
1155 }
1156 }
1157 }
1158
1159 ConSrvReleaseObject(Object, TRUE);
1160 return Status;
1161 }
1162
1163 CSR_API(SrvGetConsoleTitle)
1164 {
1165 NTSTATUS Status;
1166 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1167 PCONSRV_CONSOLE Console;
1168 ULONG Length;
1169
1170 if (!CsrValidateMessageBuffer(ApiMessage,
1171 (PVOID)&TitleRequest->Title,
1172 TitleRequest->Length,
1173 sizeof(BYTE)))
1174 {
1175 return STATUS_INVALID_PARAMETER;
1176 }
1177
1178 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1179 if (!NT_SUCCESS(Status))
1180 {
1181 DPRINT1("Can't get console\n");
1182 return Status;
1183 }
1184
1185 /* Copy title of the console to the user title buffer */
1186 if (TitleRequest->Unicode)
1187 {
1188 if (TitleRequest->Length >= sizeof(WCHAR))
1189 {
1190 Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
1191 RtlCopyMemory(TitleRequest->Title, Console->Title.Buffer, Length);
1192 ((PWCHAR)TitleRequest->Title)[Length / sizeof(WCHAR)] = L'\0';
1193 TitleRequest->Length = Length;
1194 }
1195 else
1196 {
1197 TitleRequest->Length = Console->Title.Length;
1198 }
1199 }
1200 else
1201 {
1202 if (TitleRequest->Length >= sizeof(CHAR))
1203 {
1204 Length = min(TitleRequest->Length - sizeof(CHAR), Console->Title.Length / sizeof(WCHAR));
1205 Length = WideCharToMultiByte(Console->InputCodePage, 0,
1206 Console->Title.Buffer, Length,
1207 TitleRequest->Title, Length,
1208 NULL, NULL);
1209 ((PCHAR)TitleRequest->Title)[Length] = '\0';
1210 TitleRequest->Length = Length;
1211 }
1212 else
1213 {
1214 TitleRequest->Length = Console->Title.Length / sizeof(WCHAR);
1215 }
1216 }
1217
1218 ConSrvReleaseConsole(Console, TRUE);
1219 return Status;
1220 }
1221
1222 CSR_API(SrvSetConsoleTitle)
1223 {
1224 NTSTATUS Status;
1225 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1226 PCONSRV_CONSOLE Console;
1227
1228 PWCHAR Buffer;
1229 ULONG Length;
1230
1231 if (!CsrValidateMessageBuffer(ApiMessage,
1232 (PVOID)&TitleRequest->Title,
1233 TitleRequest->Length,
1234 sizeof(BYTE)))
1235 {
1236 return STATUS_INVALID_PARAMETER;
1237 }
1238
1239 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1240 if (!NT_SUCCESS(Status))
1241 {
1242 DPRINT1("Can't get console\n");
1243 return Status;
1244 }
1245
1246 if (TitleRequest->Unicode)
1247 {
1248 /* Length is in bytes */
1249 Length = TitleRequest->Length;
1250 }
1251 else
1252 {
1253 /* Use the console input CP for the conversion */
1254 Length = MultiByteToWideChar(Console->InputCodePage, 0,
1255 TitleRequest->Title, TitleRequest->Length,
1256 NULL, 0);
1257 /* The returned Length was in number of wchars, convert it in bytes */
1258 Length *= sizeof(WCHAR);
1259 }
1260
1261 /* Allocate a new buffer to hold the new title (NULL-terminated) */
1262 Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Length + sizeof(WCHAR));
1263 if (!Buffer)
1264 {
1265 Status = STATUS_NO_MEMORY;
1266 goto Quit;
1267 }
1268
1269 /* Free the old title */
1270 ConsoleFreeUnicodeString(&Console->Title);
1271
1272 /* Copy title to console */
1273 Console->Title.Buffer = Buffer;
1274 Console->Title.Length = Length;
1275 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
1276
1277 if (TitleRequest->Unicode)
1278 {
1279 RtlCopyMemory(Console->Title.Buffer, TitleRequest->Title, Console->Title.Length);
1280 }
1281 else
1282 {
1283 MultiByteToWideChar(Console->InputCodePage, 0,
1284 TitleRequest->Title, TitleRequest->Length,
1285 Console->Title.Buffer,
1286 Console->Title.Length / sizeof(WCHAR));
1287 }
1288
1289 /* NULL-terminate */
1290 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
1291
1292 TermChangeTitle(Console);
1293 Status = STATUS_SUCCESS;
1294
1295 Quit:
1296 ConSrvReleaseConsole(Console, TRUE);
1297 return Status;
1298 }
1299
1300 NTSTATUS NTAPI
1301 ConDrvGetConsoleCP(IN PCONSOLE Console,
1302 OUT PUINT CodePage,
1303 IN BOOLEAN OutputCP);
1304 CSR_API(SrvGetConsoleCP)
1305 {
1306 NTSTATUS Status;
1307 PCONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleCPRequest;
1308 PCONSRV_CONSOLE Console;
1309
1310 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
1311 GetConsoleCPRequest->OutputCP ? "Output" : "Input");
1312
1313 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1314 if (!NT_SUCCESS(Status)) return Status;
1315
1316 Status = ConDrvGetConsoleCP(Console,
1317 &GetConsoleCPRequest->CodePage,
1318 GetConsoleCPRequest->OutputCP);
1319
1320 ConSrvReleaseConsole(Console, TRUE);
1321 return Status;
1322 }
1323
1324 NTSTATUS NTAPI
1325 ConDrvSetConsoleCP(IN PCONSOLE Console,
1326 IN UINT CodePage,
1327 IN BOOLEAN OutputCP);
1328 CSR_API(SrvSetConsoleCP)
1329 {
1330 NTSTATUS Status = STATUS_INVALID_PARAMETER;
1331 PCONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetConsoleCPRequest;
1332 PCONSRV_CONSOLE Console;
1333
1334 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
1335 SetConsoleCPRequest->OutputCP ? "Output" : "Input");
1336
1337 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1338 if (!NT_SUCCESS(Status)) return Status;
1339
1340 Status = ConDrvSetConsoleCP(Console,
1341 SetConsoleCPRequest->CodePage,
1342 SetConsoleCPRequest->OutputCP);
1343
1344 ConSrvReleaseConsole(Console, TRUE);
1345 return Status;
1346 }
1347
1348 CSR_API(SrvGetConsoleProcessList)
1349 {
1350 NTSTATUS Status;
1351 PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
1352 PCONSRV_CONSOLE Console;
1353
1354 if (!CsrValidateMessageBuffer(ApiMessage,
1355 (PVOID)&GetProcessListRequest->ProcessIdsList,
1356 GetProcessListRequest->ProcessCount,
1357 sizeof(DWORD)))
1358 {
1359 return STATUS_INVALID_PARAMETER;
1360 }
1361
1362 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1363 if (!NT_SUCCESS(Status)) return Status;
1364
1365 Status = ConSrvGetConsoleProcessList(Console,
1366 GetProcessListRequest->ProcessIdsList,
1367 GetProcessListRequest->ProcessCount,
1368 &GetProcessListRequest->ProcessCount);
1369
1370 ConSrvReleaseConsole(Console, TRUE);
1371 return Status;
1372 }
1373
1374 CSR_API(SrvGenerateConsoleCtrlEvent)
1375 {
1376 NTSTATUS Status;
1377 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
1378 PCONSRV_CONSOLE Console;
1379
1380 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1381 if (!NT_SUCCESS(Status)) return Status;
1382
1383 Status = ConSrvConsoleProcessCtrlEvent(Console,
1384 GenerateCtrlEventRequest->ProcessGroupId,
1385 GenerateCtrlEventRequest->CtrlEvent);
1386
1387 ConSrvReleaseConsole(Console, TRUE);
1388 return Status;
1389 }
1390
1391 CSR_API(SrvConsoleNotifyLastClose)
1392 {
1393 NTSTATUS Status;
1394 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1395 PCONSRV_CONSOLE Console;
1396
1397 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1398 if (!NT_SUCCESS(Status)) return Status;
1399
1400 /* Only one process is allowed to be registered for last close notification */
1401 if (!Console->NotifyLastClose)
1402 {
1403 Console->NotifyLastClose = TRUE;
1404 Console->NotifiedLastCloseProcess = ProcessData;
1405 Status = STATUS_SUCCESS;
1406 }
1407 else
1408 {
1409 Status = STATUS_ACCESS_DENIED;
1410 }
1411
1412 ConSrvReleaseConsole(Console, TRUE);
1413 return Status;
1414 }
1415
1416
1417
1418 CSR_API(SrvGetConsoleMouseInfo)
1419 {
1420 NTSTATUS Status;
1421 PCONSOLE_GETMOUSEINFO GetMouseInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetMouseInfoRequest;
1422 PCONSRV_CONSOLE Console;
1423
1424 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1425 if (!NT_SUCCESS(Status)) return Status;
1426
1427 /* Just retrieve the number of buttons of the mouse attached to this console */
1428 GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
1429
1430 ConSrvReleaseConsole(Console, TRUE);
1431 return STATUS_SUCCESS;
1432 }
1433
1434 CSR_API(SrvSetConsoleKeyShortcuts)
1435 {
1436 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1437 return STATUS_NOT_IMPLEMENTED;
1438 }
1439
1440 CSR_API(SrvGetConsoleKeyboardLayoutName)
1441 {
1442 NTSTATUS Status;
1443 PCONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetKbdLayoutNameRequest;
1444 PCONSRV_CONSOLE Console;
1445
1446 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1447 if (!NT_SUCCESS(Status)) return Status;
1448
1449 /* Retrieve the keyboard layout name of the system */
1450 if (GetKbdLayoutNameRequest->Ansi)
1451 GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1452 else
1453 GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1454
1455 ConSrvReleaseConsole(Console, TRUE);
1456 return STATUS_SUCCESS;
1457 }
1458
1459 CSR_API(SrvGetConsoleCharType)
1460 {
1461 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1462 return STATUS_NOT_IMPLEMENTED;
1463 }
1464
1465 CSR_API(SrvSetConsoleLocalEUDC)
1466 {
1467 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1468 return STATUS_NOT_IMPLEMENTED;
1469 }
1470
1471 CSR_API(SrvSetConsoleCursorMode)
1472 {
1473 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1474 return STATUS_NOT_IMPLEMENTED;
1475 }
1476
1477 CSR_API(SrvGetConsoleCursorMode)
1478 {
1479 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1480 return STATUS_NOT_IMPLEMENTED;
1481 }
1482
1483 CSR_API(SrvGetConsoleNlsMode)
1484 {
1485 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1486 return STATUS_NOT_IMPLEMENTED;
1487 }
1488
1489 CSR_API(SrvSetConsoleNlsMode)
1490 {
1491 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1492 return STATUS_NOT_IMPLEMENTED;
1493 }
1494
1495 CSR_API(SrvGetConsoleLangId)
1496 {
1497 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1498 return STATUS_NOT_IMPLEMENTED;
1499 }
1500
1501 /* EOF */