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