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