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