* Sync up to trunk head (r65353).
[reactos.git] / 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 /*
667 * Attach the ConSrv terminal to the console.
668 * This call makes a copy of our local Terminal variable.
669 */
670 Status = ConDrvRegisterTerminal(Console, &Terminal);
671 if (!NT_SUCCESS(Status))
672 {
673 DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
674 ConDrvDeleteConsole(Console);
675 ConSrvDeinitTerminal(&Terminal);
676 return Status;
677 }
678 DPRINT("Terminal registered\n");
679
680 /* All went right, so add the console to the list */
681 Status = InsertConsole(&ConsoleHandle, Console);
682
683 /* Return the newly created console to the caller and a success code too */
684 *NewConsoleHandle = ConsoleHandle;
685 *NewConsole = Console;
686 return STATUS_SUCCESS;
687 }
688
689 VOID NTAPI
690 ConSrvDeleteConsole(PCONSRV_CONSOLE Console)
691 {
692 DPRINT("ConSrvDeleteConsole\n");
693
694 // FIXME: Send a terminate message to all the processes owning this console
695
696 /* Remove the console from the list */
697 RemoveConsoleByPointer(Console);
698
699 /* Clean the Input Line Discipline */
700 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
701
702 /* Clean aliases and history */
703 IntDeleteAllAliases(Console);
704 HistoryDeleteBuffers(Console);
705
706 /* Free the console title */
707 ConsoleFreeUnicodeString(&Console->OriginalTitle);
708 ConsoleFreeUnicodeString(&Console->Title);
709
710 /* Now, call the driver. ConDrvDeregisterTerminal is called on-demand. */
711 ConDrvDeleteConsole((PCONSOLE)Console);
712
713 /* Deinit the ConSrv terminal */
714 // FIXME!!
715 // ConSrvDeinitTerminal(&Terminal); // &ConSrvConsole->Console->TermIFace
716 }
717
718
719
720
721
722
723 static NTSTATUS
724 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
725 IN PCONSOLE_PROCESS_DATA ProcessData,
726 IN ULONG Timeout)
727 {
728 NTSTATUS Status = STATUS_SUCCESS;
729
730 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
731
732 if (ProcessData->CtrlRoutine)
733 {
734 _SEH2_TRY
735 {
736 HANDLE Thread = NULL;
737
738 _SEH2_TRY
739 {
740 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
741 ProcessData->CtrlRoutine,
742 UlongToPtr(CtrlEvent), 0, NULL);
743 if (NULL == Thread)
744 {
745 Status = RtlGetLastNtStatus();
746 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
747 }
748 else
749 {
750 DPRINT("ProcessData->CtrlRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n",
751 ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
752 WaitForSingleObject(Thread, Timeout);
753 }
754 }
755 _SEH2_FINALLY
756 {
757 CloseHandle(Thread);
758 }
759 _SEH2_END;
760 }
761 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
762 {
763 Status = _SEH2_GetExceptionCode();
764 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
765 }
766 _SEH2_END;
767 }
768
769 return Status;
770 }
771
772 NTSTATUS
773 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
774 IN PCONSOLE_PROCESS_DATA ProcessData)
775 {
776 return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
777 }
778
779 PCONSOLE_PROCESS_DATA NTAPI
780 ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)
781 {
782 if (Console == NULL) return NULL;
783
784 return CONTAINING_RECORD(Console->ProcessList.Blink,
785 CONSOLE_PROCESS_DATA,
786 ConsoleLink);
787 }
788
789 NTSTATUS NTAPI
790 ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,
791 IN OUT PULONG ProcessIdsList,
792 IN ULONG MaxIdListItems,
793 OUT PULONG ProcessIdsTotal)
794 {
795 PCONSOLE_PROCESS_DATA current;
796 PLIST_ENTRY current_entry;
797
798 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
799 return STATUS_INVALID_PARAMETER;
800
801 *ProcessIdsTotal = 0;
802
803 for (current_entry = Console->ProcessList.Flink;
804 current_entry != &Console->ProcessList;
805 current_entry = current_entry->Flink)
806 {
807 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
808 if (++(*ProcessIdsTotal) <= MaxIdListItems)
809 {
810 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
811 }
812 }
813
814 return STATUS_SUCCESS;
815 }
816
817 // ConSrvGenerateConsoleCtrlEvent
818 NTSTATUS NTAPI
819 ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,
820 IN ULONG ProcessGroupId,
821 IN ULONG CtrlEvent)
822 {
823 NTSTATUS Status = STATUS_SUCCESS;
824 PLIST_ENTRY current_entry;
825 PCONSOLE_PROCESS_DATA current;
826
827 /* If the console is already being destroyed, just return */
828 if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
829 return STATUS_UNSUCCESSFUL;
830
831 /*
832 * Loop through the process list, from the most recent process
833 * (the active one) to the oldest one (the first created, i.e.
834 * the console leader process), and for each, send an event
835 * (new processes are inserted at the head of the console process list).
836 */
837 current_entry = Console->ProcessList.Flink;
838 while (current_entry != &Console->ProcessList)
839 {
840 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
841 current_entry = current_entry->Flink;
842
843 /*
844 * Only processes belonging to the same process group are signaled.
845 * If the process group ID is zero, then all the processes are signaled.
846 */
847 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
848 {
849 Status = ConSrvConsoleCtrlEvent(CtrlEvent, current);
850 }
851 }
852
853 return Status;
854 }
855
856
857 /* PUBLIC SERVER APIS *********************************************************/
858
859 CSR_API(SrvAllocConsole)
860 {
861 NTSTATUS Status = STATUS_SUCCESS;
862 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
863 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
864 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
865 CONSOLE_INIT_INFO ConsoleInitInfo;
866
867 if (ProcessData->ConsoleHandle != NULL)
868 {
869 DPRINT1("Process already has a console\n");
870 return STATUS_ACCESS_DENIED;
871 }
872
873 if ( !CsrValidateMessageBuffer(ApiMessage,
874 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
875 1,
876 sizeof(CONSOLE_START_INFO)) ||
877 !CsrValidateMessageBuffer(ApiMessage,
878 (PVOID*)&AllocConsoleRequest->ConsoleTitle,
879 AllocConsoleRequest->TitleLength,
880 sizeof(BYTE)) ||
881 !CsrValidateMessageBuffer(ApiMessage,
882 (PVOID*)&AllocConsoleRequest->Desktop,
883 AllocConsoleRequest->DesktopLength,
884 sizeof(BYTE)) ||
885 !CsrValidateMessageBuffer(ApiMessage,
886 (PVOID*)&AllocConsoleRequest->CurDir,
887 AllocConsoleRequest->CurDirLength,
888 sizeof(BYTE)) ||
889 !CsrValidateMessageBuffer(ApiMessage,
890 (PVOID*)&AllocConsoleRequest->AppName,
891 AllocConsoleRequest->AppNameLength,
892 sizeof(BYTE)) )
893 {
894 return STATUS_INVALID_PARAMETER;
895 }
896
897 /* Initialize the console initialization info structure */
898 ConsoleInitInfo.ConsoleStartInfo = AllocConsoleRequest->ConsoleStartInfo;
899 ConsoleInitInfo.TitleLength = AllocConsoleRequest->TitleLength;
900 ConsoleInitInfo.ConsoleTitle = AllocConsoleRequest->ConsoleTitle;
901 ConsoleInitInfo.DesktopLength = AllocConsoleRequest->DesktopLength;
902 ConsoleInitInfo.Desktop = AllocConsoleRequest->Desktop;
903 ConsoleInitInfo.AppNameLength = AllocConsoleRequest->AppNameLength;
904 ConsoleInitInfo.AppName = AllocConsoleRequest->AppName;
905 ConsoleInitInfo.CurDirLength = AllocConsoleRequest->CurDirLength;
906 ConsoleInitInfo.CurDir = AllocConsoleRequest->CurDir;
907
908 /* Initialize a new Console owned by the Console Leader Process */
909 Status = ConSrvAllocateConsole(ProcessData,
910 &AllocConsoleRequest->ConsoleStartInfo->InputHandle,
911 &AllocConsoleRequest->ConsoleStartInfo->OutputHandle,
912 &AllocConsoleRequest->ConsoleStartInfo->ErrorHandle,
913 &ConsoleInitInfo);
914 if (!NT_SUCCESS(Status))
915 {
916 DPRINT1("Console allocation failed\n");
917 return Status;
918 }
919
920 /* Mark the process as having a console */
921 ProcessData->ConsoleApp = TRUE;
922 CsrProcess->Flags |= CsrProcessIsConsoleApp;
923
924 /* Return the console handle and the input wait handle to the caller */
925 AllocConsoleRequest->ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
926 AllocConsoleRequest->ConsoleStartInfo->InputWaitHandle = ProcessData->InputWaitHandle;
927
928 /* Set the Property-Dialog and Control-Dispatcher handlers */
929 ProcessData->PropRoutine = AllocConsoleRequest->PropRoutine;
930 ProcessData->CtrlRoutine = AllocConsoleRequest->CtrlRoutine;
931
932 return STATUS_SUCCESS;
933 }
934
935 CSR_API(SrvAttachConsole)
936 {
937 NTSTATUS Status = STATUS_SUCCESS;
938 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
939 PCSR_PROCESS SourceProcess = NULL; // The parent process.
940 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
941 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
942 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
943
944 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
945
946 if (TargetProcessData->ConsoleHandle != NULL)
947 {
948 DPRINT1("Process already has a console\n");
949 return STATUS_ACCESS_DENIED;
950 }
951
952 if (!CsrValidateMessageBuffer(ApiMessage,
953 (PVOID*)&AttachConsoleRequest->ConsoleStartInfo,
954 1,
955 sizeof(CONSOLE_START_INFO)))
956 {
957 return STATUS_INVALID_PARAMETER;
958 }
959
960 /* Check whether we try to attach to the parent's console */
961 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
962 {
963 PROCESS_BASIC_INFORMATION ProcessInfo;
964 ULONG Length = sizeof(ProcessInfo);
965
966 /* Get the real parent's ID */
967
968 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
969 ProcessBasicInformation,
970 &ProcessInfo,
971 Length, &Length);
972 if (!NT_SUCCESS(Status))
973 {
974 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status);
975 return Status;
976 }
977
978 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
979 }
980
981 /* Lock the source process via its PID */
982 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
983 if (!NT_SUCCESS(Status)) return Status;
984
985 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
986
987 if (SourceProcessData->ConsoleHandle == NULL)
988 {
989 Status = STATUS_INVALID_HANDLE;
990 goto Quit;
991 }
992
993 /*
994 * Inherit the console from the parent,
995 * if any, otherwise return an error.
996 */
997 Status = ConSrvInheritConsole(TargetProcessData,
998 SourceProcessData->ConsoleHandle,
999 TRUE,
1000 &AttachConsoleRequest->ConsoleStartInfo->InputHandle,
1001 &AttachConsoleRequest->ConsoleStartInfo->OutputHandle,
1002 &AttachConsoleRequest->ConsoleStartInfo->ErrorHandle);
1003 if (!NT_SUCCESS(Status))
1004 {
1005 DPRINT1("Console inheritance failed\n");
1006 goto Quit;
1007 }
1008
1009 /* Mark the process as having a console */
1010 TargetProcessData->ConsoleApp = TRUE;
1011 TargetProcess->Flags |= CsrProcessIsConsoleApp;
1012
1013 /* Return the console handle and the input wait handle to the caller */
1014 AttachConsoleRequest->ConsoleStartInfo->ConsoleHandle = TargetProcessData->ConsoleHandle;
1015 AttachConsoleRequest->ConsoleStartInfo->InputWaitHandle = TargetProcessData->InputWaitHandle;
1016
1017 /* Set the Property-Dialog and Control-Dispatcher handlers */
1018 TargetProcessData->PropRoutine = AttachConsoleRequest->PropRoutine;
1019 TargetProcessData->CtrlRoutine = AttachConsoleRequest->CtrlRoutine;
1020
1021 Status = STATUS_SUCCESS;
1022
1023 Quit:
1024 /* Unlock the "source" process and exit */
1025 CsrUnlockProcess(SourceProcess);
1026 return Status;
1027 }
1028
1029 CSR_API(SrvFreeConsole)
1030 {
1031 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
1032 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
1033
1034 ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
1035
1036 /* Mark the process as not having a console anymore */
1037 ProcessData->ConsoleApp = FALSE;
1038 CsrProcess->Flags &= ~CsrProcessIsConsoleApp;
1039
1040 return STATUS_SUCCESS;
1041 }
1042
1043 NTSTATUS NTAPI
1044 ConDrvGetConsoleMode(IN PCONSOLE Console,
1045 IN PCONSOLE_IO_OBJECT Object,
1046 OUT PULONG ConsoleMode);
1047 CSR_API(SrvGetConsoleMode)
1048 {
1049 NTSTATUS Status;
1050 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1051 PCONSOLE_IO_OBJECT Object;
1052
1053 PULONG ConsoleMode = &ConsoleModeRequest->Mode;
1054
1055 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1056 ConsoleModeRequest->Handle,
1057 &Object, NULL, GENERIC_READ, TRUE, 0);
1058 if (!NT_SUCCESS(Status)) return Status;
1059
1060 /* Get the standard console modes */
1061 Status = ConDrvGetConsoleMode(Object->Console, Object,
1062 ConsoleMode);
1063 if (NT_SUCCESS(Status))
1064 {
1065 /*
1066 * If getting the console modes succeeds, then retrieve
1067 * the extended CONSRV-specific input modes.
1068 */
1069 if (INPUT_BUFFER == Object->Type)
1070 {
1071 if (Object->Console->InsertMode || Object->Console->QuickEdit)
1072 {
1073 /* Windows also adds ENABLE_EXTENDED_FLAGS, even if it's not documented on MSDN */
1074 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
1075
1076 if (Object->Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
1077 if (Object->Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
1078 }
1079 }
1080 }
1081
1082 ConSrvReleaseObject(Object, TRUE);
1083 return Status;
1084 }
1085
1086 NTSTATUS NTAPI
1087 ConDrvSetConsoleMode(IN PCONSOLE Console,
1088 IN PCONSOLE_IO_OBJECT Object,
1089 IN ULONG ConsoleMode);
1090 CSR_API(SrvSetConsoleMode)
1091 {
1092 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
1093 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
1094
1095 NTSTATUS Status;
1096 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1097 PCONSOLE_IO_OBJECT Object;
1098
1099 ULONG ConsoleMode = ConsoleModeRequest->Mode;
1100
1101 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1102 ConsoleModeRequest->Handle,
1103 &Object, NULL, GENERIC_WRITE, TRUE, 0);
1104 if (!NT_SUCCESS(Status)) return Status;
1105
1106 /* Set the standard console modes (without the CONSRV-specific input modes) */
1107 ConsoleMode &= ~CONSOLE_VALID_CONTROL_MODES; // Remove CONSRV-specific input modes.
1108 Status = ConDrvSetConsoleMode(Object->Console, Object,
1109 ConsoleMode);
1110 if (NT_SUCCESS(Status))
1111 {
1112 /*
1113 * If setting the console modes succeeds, then set
1114 * the extended CONSRV-specific input modes.
1115 */
1116 if (INPUT_BUFFER == Object->Type)
1117 {
1118 ConsoleMode = ConsoleModeRequest->Mode;
1119
1120 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
1121 {
1122 /*
1123 * If we use control mode flags without ENABLE_EXTENDED_FLAGS,
1124 * then consider the flags invalid.
1125 */
1126 if ((ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0)
1127 {
1128 Status = STATUS_INVALID_PARAMETER;
1129 }
1130 else
1131 {
1132 Object->Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
1133 Object->Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
1134 }
1135 }
1136 }
1137 }
1138
1139 ConSrvReleaseObject(Object, TRUE);
1140 return Status;
1141 }
1142
1143 CSR_API(SrvGetConsoleTitle)
1144 {
1145 NTSTATUS Status;
1146 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1147 PCONSRV_CONSOLE Console;
1148 ULONG Length;
1149
1150 if (!CsrValidateMessageBuffer(ApiMessage,
1151 (PVOID)&TitleRequest->Title,
1152 TitleRequest->Length,
1153 sizeof(BYTE)))
1154 {
1155 return STATUS_INVALID_PARAMETER;
1156 }
1157
1158 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1159 if (!NT_SUCCESS(Status))
1160 {
1161 DPRINT1("Can't get console\n");
1162 return Status;
1163 }
1164
1165 /* Copy title of the console to the user title buffer */
1166 if (TitleRequest->Unicode)
1167 {
1168 if (TitleRequest->Length >= sizeof(WCHAR))
1169 {
1170 Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
1171 RtlCopyMemory(TitleRequest->Title, Console->Title.Buffer, Length);
1172 ((PWCHAR)TitleRequest->Title)[Length / sizeof(WCHAR)] = L'\0';
1173 TitleRequest->Length = Length;
1174 }
1175 else
1176 {
1177 TitleRequest->Length = Console->Title.Length;
1178 }
1179 }
1180 else
1181 {
1182 if (TitleRequest->Length >= sizeof(CHAR))
1183 {
1184 Length = min(TitleRequest->Length - sizeof(CHAR), Console->Title.Length / sizeof(WCHAR));
1185 Length = WideCharToMultiByte(Console->InputCodePage, 0,
1186 Console->Title.Buffer, Length,
1187 TitleRequest->Title, Length,
1188 NULL, NULL);
1189 ((PCHAR)TitleRequest->Title)[Length] = '\0';
1190 TitleRequest->Length = Length;
1191 }
1192 else
1193 {
1194 TitleRequest->Length = Console->Title.Length / sizeof(WCHAR);
1195 }
1196 }
1197
1198 ConSrvReleaseConsole(Console, TRUE);
1199 return Status;
1200 }
1201
1202 CSR_API(SrvSetConsoleTitle)
1203 {
1204 NTSTATUS Status;
1205 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1206 PCONSRV_CONSOLE Console;
1207
1208 PWCHAR Buffer;
1209 ULONG Length;
1210
1211 if (!CsrValidateMessageBuffer(ApiMessage,
1212 (PVOID)&TitleRequest->Title,
1213 TitleRequest->Length,
1214 sizeof(BYTE)))
1215 {
1216 return STATUS_INVALID_PARAMETER;
1217 }
1218
1219 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1220 if (!NT_SUCCESS(Status))
1221 {
1222 DPRINT1("Can't get console\n");
1223 return Status;
1224 }
1225
1226 if (TitleRequest->Unicode)
1227 {
1228 /* Length is in bytes */
1229 Length = TitleRequest->Length;
1230 }
1231 else
1232 {
1233 /* Use the console input CP for the conversion */
1234 Length = MultiByteToWideChar(Console->InputCodePage, 0,
1235 TitleRequest->Title, TitleRequest->Length,
1236 NULL, 0);
1237 /* The returned Length was in number of wchars, convert it in bytes */
1238 Length *= sizeof(WCHAR);
1239 }
1240
1241 /* Allocate a new buffer to hold the new title (NULL-terminated) */
1242 Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Length + sizeof(WCHAR));
1243 if (!Buffer)
1244 {
1245 Status = STATUS_NO_MEMORY;
1246 goto Quit;
1247 }
1248
1249 /* Free the old title */
1250 ConsoleFreeUnicodeString(&Console->Title);
1251
1252 /* Copy title to console */
1253 Console->Title.Buffer = Buffer;
1254 Console->Title.Length = Length;
1255 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
1256
1257 if (TitleRequest->Unicode)
1258 {
1259 RtlCopyMemory(Console->Title.Buffer, TitleRequest->Title, Console->Title.Length);
1260 }
1261 else
1262 {
1263 MultiByteToWideChar(Console->InputCodePage, 0,
1264 TitleRequest->Title, TitleRequest->Length,
1265 Console->Title.Buffer,
1266 Console->Title.Length / sizeof(WCHAR));
1267 }
1268
1269 /* NULL-terminate */
1270 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
1271
1272 TermChangeTitle(Console);
1273 Status = STATUS_SUCCESS;
1274
1275 Quit:
1276 ConSrvReleaseConsole(Console, TRUE);
1277 return Status;
1278 }
1279
1280 NTSTATUS NTAPI
1281 ConDrvGetConsoleCP(IN PCONSOLE Console,
1282 OUT PUINT CodePage,
1283 IN BOOLEAN OutputCP);
1284 CSR_API(SrvGetConsoleCP)
1285 {
1286 NTSTATUS Status;
1287 PCONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleCPRequest;
1288 PCONSRV_CONSOLE Console;
1289
1290 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
1291 GetConsoleCPRequest->OutputCP ? "Output" : "Input");
1292
1293 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1294 if (!NT_SUCCESS(Status)) return Status;
1295
1296 Status = ConDrvGetConsoleCP(Console,
1297 &GetConsoleCPRequest->CodePage,
1298 GetConsoleCPRequest->OutputCP);
1299
1300 ConSrvReleaseConsole(Console, TRUE);
1301 return Status;
1302 }
1303
1304 NTSTATUS NTAPI
1305 ConDrvSetConsoleCP(IN PCONSOLE Console,
1306 IN UINT CodePage,
1307 IN BOOLEAN OutputCP);
1308 CSR_API(SrvSetConsoleCP)
1309 {
1310 NTSTATUS Status = STATUS_INVALID_PARAMETER;
1311 PCONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetConsoleCPRequest;
1312 PCONSRV_CONSOLE Console;
1313
1314 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
1315 SetConsoleCPRequest->OutputCP ? "Output" : "Input");
1316
1317 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1318 if (!NT_SUCCESS(Status)) return Status;
1319
1320 Status = ConDrvSetConsoleCP(Console,
1321 SetConsoleCPRequest->CodePage,
1322 SetConsoleCPRequest->OutputCP);
1323
1324 ConSrvReleaseConsole(Console, TRUE);
1325 return Status;
1326 }
1327
1328 CSR_API(SrvGetConsoleProcessList)
1329 {
1330 NTSTATUS Status;
1331 PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
1332 PCONSRV_CONSOLE Console;
1333
1334 if (!CsrValidateMessageBuffer(ApiMessage,
1335 (PVOID)&GetProcessListRequest->ProcessIdsList,
1336 GetProcessListRequest->ProcessCount,
1337 sizeof(DWORD)))
1338 {
1339 return STATUS_INVALID_PARAMETER;
1340 }
1341
1342 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1343 if (!NT_SUCCESS(Status)) return Status;
1344
1345 Status = ConSrvGetConsoleProcessList(Console,
1346 GetProcessListRequest->ProcessIdsList,
1347 GetProcessListRequest->ProcessCount,
1348 &GetProcessListRequest->ProcessCount);
1349
1350 ConSrvReleaseConsole(Console, TRUE);
1351 return Status;
1352 }
1353
1354 CSR_API(SrvGenerateConsoleCtrlEvent)
1355 {
1356 NTSTATUS Status;
1357 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
1358 PCONSRV_CONSOLE Console;
1359
1360 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1361 if (!NT_SUCCESS(Status)) return Status;
1362
1363 Status = ConSrvConsoleProcessCtrlEvent(Console,
1364 GenerateCtrlEventRequest->ProcessGroupId,
1365 GenerateCtrlEventRequest->CtrlEvent);
1366
1367 ConSrvReleaseConsole(Console, TRUE);
1368 return Status;
1369 }
1370
1371 CSR_API(SrvConsoleNotifyLastClose)
1372 {
1373 NTSTATUS Status;
1374 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1375 PCONSRV_CONSOLE Console;
1376
1377 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1378 if (!NT_SUCCESS(Status)) return Status;
1379
1380 /* Only one process is allowed to be registered for last close notification */
1381 if (!Console->NotifyLastClose)
1382 {
1383 Console->NotifyLastClose = TRUE;
1384 Console->NotifiedLastCloseProcess = ProcessData;
1385 Status = STATUS_SUCCESS;
1386 }
1387 else
1388 {
1389 Status = STATUS_ACCESS_DENIED;
1390 }
1391
1392 ConSrvReleaseConsole(Console, TRUE);
1393 return Status;
1394 }
1395
1396
1397
1398 CSR_API(SrvGetConsoleMouseInfo)
1399 {
1400 NTSTATUS Status;
1401 PCONSOLE_GETMOUSEINFO GetMouseInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetMouseInfoRequest;
1402 PCONSRV_CONSOLE Console;
1403
1404 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1405 if (!NT_SUCCESS(Status)) return Status;
1406
1407 /* Just retrieve the number of buttons of the mouse attached to this console */
1408 GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
1409
1410 ConSrvReleaseConsole(Console, TRUE);
1411 return STATUS_SUCCESS;
1412 }
1413
1414 CSR_API(SrvSetConsoleKeyShortcuts)
1415 {
1416 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1417 return STATUS_NOT_IMPLEMENTED;
1418 }
1419
1420 CSR_API(SrvGetConsoleKeyboardLayoutName)
1421 {
1422 NTSTATUS Status;
1423 PCONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetKbdLayoutNameRequest;
1424 PCONSRV_CONSOLE Console;
1425
1426 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1427 if (!NT_SUCCESS(Status)) return Status;
1428
1429 /* Retrieve the keyboard layout name of the system */
1430 if (GetKbdLayoutNameRequest->Ansi)
1431 GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1432 else
1433 GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1434
1435 ConSrvReleaseConsole(Console, TRUE);
1436 return STATUS_SUCCESS;
1437 }
1438
1439 CSR_API(SrvGetConsoleCharType)
1440 {
1441 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1442 return STATUS_NOT_IMPLEMENTED;
1443 }
1444
1445 CSR_API(SrvSetConsoleLocalEUDC)
1446 {
1447 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1448 return STATUS_NOT_IMPLEMENTED;
1449 }
1450
1451 CSR_API(SrvSetConsoleCursorMode)
1452 {
1453 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1454 return STATUS_NOT_IMPLEMENTED;
1455 }
1456
1457 CSR_API(SrvGetConsoleCursorMode)
1458 {
1459 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1460 return STATUS_NOT_IMPLEMENTED;
1461 }
1462
1463 CSR_API(SrvGetConsoleNlsMode)
1464 {
1465 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1466 return STATUS_NOT_IMPLEMENTED;
1467 }
1468
1469 CSR_API(SrvSetConsoleNlsMode)
1470 {
1471 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1472 return STATUS_NOT_IMPLEMENTED;
1473 }
1474
1475 CSR_API(SrvGetConsoleLangId)
1476 {
1477 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1478 return STATUS_NOT_IMPLEMENTED;
1479 }
1480
1481 /* EOF */