[CONSRV]
[reactos.git] / reactos / win32ss / user / winsrv / consrv / console.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: consrv/console.c
5 * PURPOSE: Console Management Functions
6 * PROGRAMMERS: Gé van Geldorp
7 * Jeffrey Morlan
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include "consrv.h"
14
15 /* This is for COM usage */
16 #define COBJMACROS
17 #include <shlobj.h>
18
19
20 #include <alias.h>
21 #include <history.h>
22 #include "procinit.h"
23
24 #define NDEBUG
25 #include <debug.h>
26
27 // FIXME: Add this prototype to winternl.h / rtlfuncs.h / ...
28 NTSTATUS NTAPI RtlGetLastNtStatus(VOID);
29
30 /* GLOBALS ********************************************************************/
31
32 /* The list of the ConSrv consoles */
33 static ULONG ConsoleListSize;
34 static PCONSRV_CONSOLE* ConsoleList;
35 static RTL_RESOURCE ListLock;
36
37 #define ConSrvLockConsoleListExclusive() \
38 RtlAcquireResourceExclusive(&ListLock, TRUE)
39
40 #define ConSrvLockConsoleListShared() \
41 RtlAcquireResourceShared(&ListLock, TRUE)
42
43 #define ConSrvUnlockConsoleList() \
44 RtlReleaseResource(&ListLock)
45
46
47 static NTSTATUS
48 InsertConsole(OUT PHANDLE Handle,
49 IN PCONSRV_CONSOLE Console)
50 {
51 #define CONSOLE_HANDLES_INCREMENT 2 * 3
52
53 NTSTATUS Status = STATUS_SUCCESS;
54 ULONG i = 0;
55 PCONSRV_CONSOLE* Block;
56
57 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
58 (ConsoleList != NULL && ConsoleListSize != 0) );
59
60 /* All went right, so add the console to the list */
61 ConSrvLockConsoleListExclusive();
62 DPRINT("Insert in the list\n");
63
64 if (ConsoleList)
65 {
66 for (i = 0; i < ConsoleListSize; i++)
67 {
68 if (ConsoleList[i] == NULL) break;
69 }
70 }
71
72 if (i >= ConsoleListSize)
73 {
74 DPRINT("Creation of a new handles table\n");
75 /* Allocate a new handles table */
76 Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
77 (ConsoleListSize +
78 CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSRV_CONSOLE));
79 if (Block == NULL)
80 {
81 Status = STATUS_UNSUCCESSFUL;
82 goto Quit;
83 }
84
85 /* If we previously had a handles table, free it and use the new one */
86 if (ConsoleList)
87 {
88 /* Copy the handles from the old table to the new one */
89 RtlCopyMemory(Block,
90 ConsoleList,
91 ConsoleListSize * sizeof(PCONSRV_CONSOLE));
92 ConsoleFreeHeap(ConsoleList);
93 }
94 ConsoleList = Block;
95 ConsoleListSize += CONSOLE_HANDLES_INCREMENT;
96 }
97
98 ConsoleList[i] = Console;
99 *Handle = ULongToHandle((i << 2) | 0x3);
100
101 Quit:
102 /* Unlock the console list and return status */
103 ConSrvUnlockConsoleList();
104 return Status;
105 }
106
107 /* Unused */
108 #if 0
109 static NTSTATUS
110 RemoveConsoleByHandle(IN HANDLE Handle)
111 {
112 NTSTATUS Status = STATUS_SUCCESS;
113 PCONSRV_CONSOLE Console;
114
115 BOOLEAN ValidHandle = ((HandleToULong(Handle) & 0x3) == 0x3);
116 ULONG Index = HandleToULong(Handle) >> 2;
117
118 if (!ValidHandle) return STATUS_INVALID_HANDLE;
119
120 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
121 (ConsoleList != NULL && ConsoleListSize != 0) );
122
123 /* Remove the console from the list */
124 ConSrvLockConsoleListExclusive();
125
126 if (Index >= ConsoleListSize ||
127 (Console = ConsoleList[Index]) == NULL)
128 {
129 Status = STATUS_INVALID_HANDLE;
130 goto Quit;
131 }
132
133 ConsoleList[Index] = NULL;
134
135 Quit:
136 /* Unlock the console list and return status */
137 ConSrvUnlockConsoleList();
138 return Status;
139 }
140 #endif
141
142 static NTSTATUS
143 RemoveConsoleByPointer(IN PCONSRV_CONSOLE Console)
144 {
145 ULONG i = 0;
146
147 if (!Console) return STATUS_INVALID_PARAMETER;
148
149 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
150 (ConsoleList != NULL && ConsoleListSize != 0) );
151
152 /* Remove the console from the list */
153 ConSrvLockConsoleListExclusive();
154
155 if (ConsoleList)
156 {
157 for (i = 0; i < ConsoleListSize; i++)
158 {
159 if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
160 }
161 }
162
163 /* Unlock the console list and return */
164 ConSrvUnlockConsoleList();
165 return STATUS_SUCCESS;
166 }
167
168 BOOLEAN NTAPI
169 ConSrvValidateConsole(OUT PCONSRV_CONSOLE* Console,
170 IN HANDLE ConsoleHandle,
171 IN CONSOLE_STATE ExpectedState,
172 IN BOOLEAN LockConsole)
173 {
174 BOOLEAN RetVal = FALSE;
175 PCONSRV_CONSOLE ValidatedConsole;
176
177 BOOLEAN ValidHandle = ((HandleToULong(ConsoleHandle) & 0x3) == 0x3);
178 ULONG Index = HandleToULong(ConsoleHandle) >> 2;
179
180 if (!ValidHandle) return FALSE;
181
182 if (!Console) return FALSE;
183 *Console = NULL;
184
185 /*
186 * Forbid creation or deletion of consoles when
187 * checking for the existence of a console.
188 */
189 ConSrvLockConsoleListShared();
190
191 if (Index >= ConsoleListSize ||
192 (ValidatedConsole = ConsoleList[Index]) == NULL)
193 {
194 /* Unlock the console list and return */
195 ConSrvUnlockConsoleList();
196 return FALSE;
197 }
198
199 ValidatedConsole = ConsoleList[Index];
200
201 /* Unlock the console list and return */
202 ConSrvUnlockConsoleList();
203
204 RetVal = ConDrvValidateConsoleUnsafe((PCONSOLE)ValidatedConsole,
205 ExpectedState,
206 LockConsole);
207 if (RetVal) *Console = ValidatedConsole;
208
209 return RetVal;
210 }
211
212
213 /* PRIVATE FUNCTIONS **********************************************************/
214
215 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
216 static BOOLEAN
217 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,
218 IN PCWSTR Source)
219 {
220 SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR);
221 if (Size > MAXUSHORT) return FALSE;
222
223 UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size);
224 if (UniDest->Buffer == NULL) return FALSE;
225
226 RtlCopyMemory(UniDest->Buffer, Source, Size);
227 UniDest->MaximumLength = (USHORT)Size;
228 UniDest->Length = (USHORT)Size - sizeof(WCHAR);
229
230 return TRUE;
231 }
232
233 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
234 static VOID
235 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
236 {
237 if (UnicodeString->Buffer)
238 {
239 ConsoleFreeHeap(UnicodeString->Buffer);
240 RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
241 }
242 }
243
244 VOID
245 ConioPause(PCONSRV_CONSOLE Console, UINT Flags)
246 {
247 Console->PauseFlags |= Flags;
248 ConDrvPause((PCONSOLE)Console);
249 }
250
251 VOID
252 ConioUnpause(PCONSRV_CONSOLE Console, UINT Flags)
253 {
254 Console->PauseFlags &= ~Flags;
255
256 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
257 if (Console->PauseFlags == 0)
258 {
259 ConDrvUnpause((PCONSOLE)Console);
260
261 CsrNotifyWait(&Console->WriteWaitQueue,
262 TRUE,
263 NULL,
264 NULL);
265 if (!IsListEmpty(&Console->WriteWaitQueue))
266 {
267 CsrDereferenceWait(&Console->WriteWaitQueue);
268 }
269 }
270 }
271
272 NTSTATUS
273 ConSrvGetConsole(IN PCONSOLE_PROCESS_DATA ProcessData,
274 OUT PCONSRV_CONSOLE* Console,
275 IN BOOLEAN LockConsole)
276 {
277 NTSTATUS Status = STATUS_INVALID_HANDLE;
278 PCONSRV_CONSOLE GrabConsole;
279
280 // if (Console == NULL) return STATUS_INVALID_PARAMETER;
281 ASSERT(Console);
282 *Console = NULL;
283
284 if (ConSrvValidateConsole(&GrabConsole,
285 ProcessData->ConsoleHandle,
286 CONSOLE_RUNNING,
287 LockConsole))
288 {
289 InterlockedIncrement(&GrabConsole->ReferenceCount);
290 *Console = GrabConsole;
291 Status = STATUS_SUCCESS;
292 }
293
294 return Status;
295 }
296
297 VOID
298 ConSrvReleaseConsole(IN PCONSRV_CONSOLE Console,
299 IN BOOLEAN WasConsoleLocked)
300 {
301 LONG RefCount = 0;
302
303 if (!Console) return;
304 // if (Console->ReferenceCount == 0) return; // This shouldn't happen
305 ASSERT(Console->ReferenceCount > 0);
306
307 /* The console must be locked */
308 // ASSERT(Console_locked);
309
310 /*
311 * Decrement the reference count. Save the new value too,
312 * because Console->ReferenceCount might be modified after
313 * the console gets unlocked but before we check whether we
314 * can destroy it.
315 */
316 RefCount = _InterlockedDecrement(&Console->ReferenceCount);
317
318 /* Unlock the console if needed */
319 if (WasConsoleLocked) LeaveCriticalSection(&Console->Lock);
320
321 /* Delete the console if needed */
322 if (RefCount <= 0) ConSrvDeleteConsole(Console);
323 }
324
325
326 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
327
328 VOID NTAPI
329 ConSrvInitConsoleSupport(VOID)
330 {
331 DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
332
333 /* Initialize the console list and its lock */
334 ConsoleListSize = 0;
335 ConsoleList = NULL;
336 RtlInitializeResource(&ListLock);
337
338 /* Should call LoadKeyboardLayout */
339 }
340
341 NTSTATUS NTAPI
342 ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
343 IN OUT PCONSOLE_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
539 /* Impersonate the caller in order to retrieve settings in its context */
540 if (!CsrImpersonateClient(NULL))
541 return STATUS_BAD_IMPERSONATION_LEVEL;
542
543 /* 1. Load the default settings */
544 RtlZeroMemory(ConsoleInfo, sizeof(ConsoleInfoBuffer));
545 ConsoleInfo->cbSize = sizeof(ConsoleInfoBuffer);
546 ConCfgGetDefaultSettings(ConsoleInfo);
547
548 /* 2. Get the title of the console (initialize ConsoleInfo->ConsoleTitle) */
549 Length = min(ConsoleInitInfo->TitleLength,
550 (ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR));
551 wcsncpy(ConsoleInfo->ConsoleTitle, ConsoleInitInfo->ConsoleTitle, Length);
552 ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL; // NULL-terminate it.
553
554 /*
555 * 3. 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 * 4. 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);
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 /* 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
693 /* Initialize pausing support */
694 Console->PauseFlags = 0;
695 InitializeListHead(&Console->ReadWaitQueue);
696 InitializeListHead(&Console->WriteWaitQueue);
697
698 /* Initialize the alias and history buffers */
699 Console->Aliases = NULL;
700 InitializeListHead(&Console->HistoryBuffers);
701 Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize;
702 Console->NumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers;
703 Console->HistoryNoDup = ConsoleInfo->HistoryNoDup;
704
705 /* Initialize the Input Line Discipline */
706 Console->LineBuffer = NULL;
707 Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
708 Console->LineComplete = Console->LineUpPressed = FALSE;
709 // LineWakeupMask
710 Console->LineInsertToggle =
711 Console->InsertMode = ConsoleInfo->InsertMode;
712 Console->QuickEdit = ConsoleInfo->QuickEdit;
713
714 /* Popup windows */
715 InitializeListHead(&Console->PopupWindows);
716
717 /* Colour table */
718 RtlCopyMemory(Console->Colors, ConsoleInfo->ColorTable,
719 sizeof(ConsoleInfo->ColorTable));
720
721 /* Create the Initialization Events */
722 Status = NtCreateEvent(&Console->InitEvents[INIT_SUCCESS], EVENT_ALL_ACCESS,
723 NULL, NotificationEvent, FALSE);
724 if (!NT_SUCCESS(Status))
725 {
726 DPRINT1("NtCreateEvent(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
727 ConDrvDeleteConsole(Console);
728 ConSrvDeinitTerminal(&Terminal);
729 return Status;
730 }
731 Status = NtCreateEvent(&Console->InitEvents[INIT_FAILURE], EVENT_ALL_ACCESS,
732 NULL, NotificationEvent, FALSE);
733 if (!NT_SUCCESS(Status))
734 {
735 DPRINT1("NtCreateEvent(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
736 NtClose(Console->InitEvents[INIT_SUCCESS]);
737 ConDrvDeleteConsole(Console);
738 ConSrvDeinitTerminal(&Terminal);
739 return Status;
740 }
741
742 /*
743 * Attach the ConSrv terminal to the console.
744 * This call makes a copy of our local Terminal variable.
745 */
746 Status = ConDrvAttachTerminal(Console, &Terminal);
747 if (!NT_SUCCESS(Status))
748 {
749 DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
750 NtClose(Console->InitEvents[INIT_FAILURE]);
751 NtClose(Console->InitEvents[INIT_SUCCESS]);
752 ConDrvDeleteConsole(Console);
753 ConSrvDeinitTerminal(&Terminal);
754 return Status;
755 }
756 DPRINT("Terminal attached\n");
757
758 /* All went right, so add the console to the list */
759 Status = InsertConsole(&ConsoleHandle, Console);
760
761 // FIXME! We do not support at all asynchronous console creation!
762 NtSetEvent(Console->InitEvents[INIT_SUCCESS], NULL);
763 // NtSetEvent(Console->InitEvents[INIT_FAILURE], NULL);
764
765 /* Return the newly created console to the caller and a success code too */
766 *NewConsoleHandle = ConsoleHandle;
767 *NewConsole = Console;
768 return STATUS_SUCCESS;
769 }
770
771 VOID NTAPI
772 ConSrvDeleteConsole(PCONSRV_CONSOLE Console)
773 {
774 DPRINT("ConSrvDeleteConsole\n");
775
776 // FIXME: Send a terminate message to all the processes owning this console
777
778 /* Remove the console from the list */
779 RemoveConsoleByPointer(Console);
780
781 /* Destroy the Initialization Events */
782 NtClose(Console->InitEvents[INIT_FAILURE]);
783 NtClose(Console->InitEvents[INIT_SUCCESS]);
784
785 /* Clean the Input Line Discipline */
786 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
787
788 /* Clean aliases and history */
789 IntDeleteAllAliases(Console);
790 HistoryDeleteBuffers(Console);
791
792 /* Free the console title */
793 ConsoleFreeUnicodeString(&Console->OriginalTitle);
794 ConsoleFreeUnicodeString(&Console->Title);
795
796 /* Now, call the driver. ConDrvDetachTerminal is called on-demand. */
797 ConDrvDeleteConsole((PCONSOLE)Console);
798
799 /* Deinit the ConSrv terminal */
800 // FIXME!!
801 // ConSrvDeinitTerminal(&Terminal);
802 }
803
804
805
806
807
808
809 static NTSTATUS
810 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
811 IN PCONSOLE_PROCESS_DATA ProcessData,
812 IN ULONG Timeout)
813 {
814 NTSTATUS Status = STATUS_SUCCESS;
815
816 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
817
818 /*
819 * Be sure we effectively have a control routine. It resides in kernel32.dll (client).
820 */
821 if (ProcessData->CtrlRoutine == NULL) return Status;
822
823 _SEH2_TRY
824 {
825 HANDLE Thread = NULL;
826
827 _SEH2_TRY
828 {
829 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
830 ProcessData->CtrlRoutine,
831 UlongToPtr(CtrlEvent), 0, NULL);
832 if (NULL == Thread)
833 {
834 Status = RtlGetLastNtStatus();
835 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
836 }
837 else
838 {
839 DPRINT("ProcessData->CtrlRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n",
840 ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
841 WaitForSingleObject(Thread, Timeout);
842 }
843 }
844 _SEH2_FINALLY
845 {
846 CloseHandle(Thread);
847 }
848 _SEH2_END;
849 }
850 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
851 {
852 Status = _SEH2_GetExceptionCode();
853 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
854 }
855 _SEH2_END;
856
857 return Status;
858 }
859
860 NTSTATUS
861 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
862 IN PCONSOLE_PROCESS_DATA ProcessData)
863 {
864 return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
865 }
866
867 PCONSOLE_PROCESS_DATA NTAPI
868 ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)
869 {
870 if (Console == NULL) return NULL;
871
872 return CONTAINING_RECORD(Console->ProcessList.Blink,
873 CONSOLE_PROCESS_DATA,
874 ConsoleLink);
875 }
876
877 NTSTATUS NTAPI
878 ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,
879 IN OUT PULONG ProcessIdsList,
880 IN ULONG MaxIdListItems,
881 OUT PULONG ProcessIdsTotal)
882 {
883 PCONSOLE_PROCESS_DATA current;
884 PLIST_ENTRY current_entry;
885
886 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
887 return STATUS_INVALID_PARAMETER;
888
889 *ProcessIdsTotal = 0;
890
891 for (current_entry = Console->ProcessList.Flink;
892 current_entry != &Console->ProcessList;
893 current_entry = current_entry->Flink)
894 {
895 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
896 if (++(*ProcessIdsTotal) <= MaxIdListItems)
897 {
898 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
899 }
900 }
901
902 return STATUS_SUCCESS;
903 }
904
905 // ConSrvGenerateConsoleCtrlEvent
906 NTSTATUS NTAPI
907 ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,
908 IN ULONG ProcessGroupId,
909 IN ULONG CtrlEvent)
910 {
911 NTSTATUS Status = STATUS_SUCCESS;
912 PLIST_ENTRY current_entry;
913 PCONSOLE_PROCESS_DATA current;
914
915 /* If the console is already being destroyed, just return */
916 if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
917 return STATUS_UNSUCCESSFUL;
918
919 /*
920 * Loop through the process list, from the most recent process
921 * (the active one) to the oldest one (the first created, i.e.
922 * the console leader process), and for each, send an event
923 * (new processes are inserted at the head of the console process list).
924 */
925 current_entry = Console->ProcessList.Flink;
926 while (current_entry != &Console->ProcessList)
927 {
928 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
929 current_entry = current_entry->Flink;
930
931 /*
932 * Only processes belonging to the same process group are signaled.
933 * If the process group ID is zero, then all the processes are signaled.
934 */
935 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
936 {
937 Status = ConSrvConsoleCtrlEvent(CtrlEvent, current);
938 }
939 }
940
941 return Status;
942 }
943
944
945 /* PUBLIC SERVER APIS *********************************************************/
946
947 CSR_API(SrvAllocConsole)
948 {
949 NTSTATUS Status = STATUS_SUCCESS;
950 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
951 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
952 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
953 CONSOLE_INIT_INFO ConsoleInitInfo;
954
955 if (ProcessData->ConsoleHandle != NULL)
956 {
957 DPRINT1("Process already has a console\n");
958 return STATUS_ACCESS_DENIED;
959 }
960
961 if ( !CsrValidateMessageBuffer(ApiMessage,
962 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
963 1,
964 sizeof(CONSOLE_START_INFO)) ||
965 !CsrValidateMessageBuffer(ApiMessage,
966 (PVOID*)&AllocConsoleRequest->ConsoleTitle,
967 AllocConsoleRequest->TitleLength,
968 sizeof(BYTE)) ||
969 !CsrValidateMessageBuffer(ApiMessage,
970 (PVOID*)&AllocConsoleRequest->Desktop,
971 AllocConsoleRequest->DesktopLength,
972 sizeof(BYTE)) ||
973 !CsrValidateMessageBuffer(ApiMessage,
974 (PVOID*)&AllocConsoleRequest->CurDir,
975 AllocConsoleRequest->CurDirLength,
976 sizeof(BYTE)) ||
977 !CsrValidateMessageBuffer(ApiMessage,
978 (PVOID*)&AllocConsoleRequest->AppName,
979 AllocConsoleRequest->AppNameLength,
980 sizeof(BYTE)) )
981 {
982 return STATUS_INVALID_PARAMETER;
983 }
984
985 /* Initialize the console initialization info structure */
986 ConsoleInitInfo.ConsoleStartInfo = AllocConsoleRequest->ConsoleStartInfo;
987 ConsoleInitInfo.IsWindowVisible = TRUE; // The console window is always visible.
988 ConsoleInitInfo.TitleLength = AllocConsoleRequest->TitleLength;
989 ConsoleInitInfo.ConsoleTitle = AllocConsoleRequest->ConsoleTitle;
990 ConsoleInitInfo.DesktopLength = AllocConsoleRequest->DesktopLength;
991 ConsoleInitInfo.Desktop = AllocConsoleRequest->Desktop;
992 ConsoleInitInfo.AppNameLength = AllocConsoleRequest->AppNameLength;
993 ConsoleInitInfo.AppName = AllocConsoleRequest->AppName;
994 ConsoleInitInfo.CurDirLength = AllocConsoleRequest->CurDirLength;
995 ConsoleInitInfo.CurDir = AllocConsoleRequest->CurDir;
996
997 /* Initialize a new Console owned by the Console Leader Process */
998 Status = ConSrvAllocateConsole(ProcessData,
999 &AllocConsoleRequest->ConsoleStartInfo->InputHandle,
1000 &AllocConsoleRequest->ConsoleStartInfo->OutputHandle,
1001 &AllocConsoleRequest->ConsoleStartInfo->ErrorHandle,
1002 &ConsoleInitInfo);
1003 if (!NT_SUCCESS(Status))
1004 {
1005 DPRINT1("Console allocation failed\n");
1006 return Status;
1007 }
1008
1009 /* Set the Property-Dialog and Control-Dispatcher handlers */
1010 ProcessData->PropRoutine = AllocConsoleRequest->PropRoutine;
1011 ProcessData->CtrlRoutine = AllocConsoleRequest->CtrlRoutine;
1012
1013 return STATUS_SUCCESS;
1014 }
1015
1016 CSR_API(SrvAttachConsole)
1017 {
1018 NTSTATUS Status = STATUS_SUCCESS;
1019 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
1020 PCSR_PROCESS SourceProcess = NULL; // The parent process.
1021 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
1022 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
1023 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
1024
1025 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
1026
1027 if (TargetProcessData->ConsoleHandle != NULL)
1028 {
1029 DPRINT1("Process already has a console\n");
1030 return STATUS_ACCESS_DENIED;
1031 }
1032
1033 if (!CsrValidateMessageBuffer(ApiMessage,
1034 (PVOID*)&AttachConsoleRequest->ConsoleStartInfo,
1035 1,
1036 sizeof(CONSOLE_START_INFO)))
1037 {
1038 return STATUS_INVALID_PARAMETER;
1039 }
1040
1041 /* Check whether we try to attach to the parent's console */
1042 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
1043 {
1044 PROCESS_BASIC_INFORMATION ProcessInfo;
1045 ULONG Length = sizeof(ProcessInfo);
1046
1047 /* Get the real parent's ID */
1048
1049 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
1050 ProcessBasicInformation,
1051 &ProcessInfo,
1052 Length, &Length);
1053 if (!NT_SUCCESS(Status))
1054 {
1055 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = 0x%08lx\n", Status);
1056 return Status;
1057 }
1058
1059 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
1060 }
1061
1062 /* Lock the source process via its PID */
1063 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
1064 if (!NT_SUCCESS(Status)) return Status;
1065
1066 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
1067
1068 if (SourceProcessData->ConsoleHandle == NULL)
1069 {
1070 Status = STATUS_INVALID_HANDLE;
1071 goto Quit;
1072 }
1073
1074 /*
1075 * Inherit the console from the parent,
1076 * if any, otherwise return an error.
1077 */
1078 Status = ConSrvInheritConsole(TargetProcessData,
1079 SourceProcessData->ConsoleHandle,
1080 TRUE,
1081 &AttachConsoleRequest->ConsoleStartInfo->InputHandle,
1082 &AttachConsoleRequest->ConsoleStartInfo->OutputHandle,
1083 &AttachConsoleRequest->ConsoleStartInfo->ErrorHandle,
1084 AttachConsoleRequest->ConsoleStartInfo);
1085 if (!NT_SUCCESS(Status))
1086 {
1087 DPRINT1("Console inheritance failed\n");
1088 goto Quit;
1089 }
1090
1091 /* Set the Property-Dialog and Control-Dispatcher handlers */
1092 TargetProcessData->PropRoutine = AttachConsoleRequest->PropRoutine;
1093 TargetProcessData->CtrlRoutine = AttachConsoleRequest->CtrlRoutine;
1094
1095 Status = STATUS_SUCCESS;
1096
1097 Quit:
1098 /* Unlock the "source" process and exit */
1099 CsrUnlockProcess(SourceProcess);
1100 return Status;
1101 }
1102
1103 CSR_API(SrvFreeConsole)
1104 {
1105 return ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
1106 }
1107
1108 NTSTATUS NTAPI
1109 ConDrvGetConsoleMode(IN PCONSOLE Console,
1110 IN PCONSOLE_IO_OBJECT Object,
1111 OUT PULONG ConsoleMode);
1112 CSR_API(SrvGetConsoleMode)
1113 {
1114 NTSTATUS Status;
1115 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1116 PCONSOLE_IO_OBJECT Object;
1117
1118 PULONG ConsoleMode = &ConsoleModeRequest->Mode;
1119
1120 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1121 ConsoleModeRequest->Handle,
1122 &Object, NULL, GENERIC_READ, TRUE, 0);
1123 if (!NT_SUCCESS(Status)) return Status;
1124
1125 /* Get the standard console modes */
1126 Status = ConDrvGetConsoleMode(Object->Console, Object,
1127 ConsoleMode);
1128 if (NT_SUCCESS(Status))
1129 {
1130 /*
1131 * If getting the console modes succeeds, then retrieve
1132 * the extended CONSRV-specific input modes.
1133 */
1134 if (INPUT_BUFFER == Object->Type)
1135 {
1136 if (Object->Console->InsertMode || Object->Console->QuickEdit)
1137 {
1138 /* Windows also adds ENABLE_EXTENDED_FLAGS, even if it's not documented on MSDN */
1139 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
1140
1141 if (Object->Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
1142 if (Object->Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
1143 }
1144 }
1145 }
1146
1147 ConSrvReleaseObject(Object, TRUE);
1148 return Status;
1149 }
1150
1151 NTSTATUS NTAPI
1152 ConDrvSetConsoleMode(IN PCONSOLE Console,
1153 IN PCONSOLE_IO_OBJECT Object,
1154 IN ULONG ConsoleMode);
1155 CSR_API(SrvSetConsoleMode)
1156 {
1157 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
1158 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
1159
1160 NTSTATUS Status;
1161 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1162 PCONSOLE_IO_OBJECT Object;
1163
1164 ULONG ConsoleMode = ConsoleModeRequest->Mode;
1165
1166 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1167 ConsoleModeRequest->Handle,
1168 &Object, NULL, GENERIC_WRITE, TRUE, 0);
1169 if (!NT_SUCCESS(Status)) return Status;
1170
1171 /* Set the standard console modes (without the CONSRV-specific input modes) */
1172 ConsoleMode &= ~CONSOLE_VALID_CONTROL_MODES; // Remove CONSRV-specific input modes.
1173 Status = ConDrvSetConsoleMode(Object->Console, Object,
1174 ConsoleMode);
1175 if (NT_SUCCESS(Status))
1176 {
1177 /*
1178 * If setting the console modes succeeds, then set
1179 * the extended CONSRV-specific input modes.
1180 */
1181 if (INPUT_BUFFER == Object->Type)
1182 {
1183 ConsoleMode = ConsoleModeRequest->Mode;
1184
1185 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
1186 {
1187 /*
1188 * If we use control mode flags without ENABLE_EXTENDED_FLAGS,
1189 * then consider the flags invalid.
1190 */
1191 if ((ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0)
1192 {
1193 Status = STATUS_INVALID_PARAMETER;
1194 }
1195 else
1196 {
1197 Object->Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
1198 Object->Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
1199 }
1200 }
1201 }
1202 }
1203
1204 ConSrvReleaseObject(Object, TRUE);
1205 return Status;
1206 }
1207
1208 CSR_API(SrvGetConsoleTitle)
1209 {
1210 NTSTATUS Status;
1211 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1212 PCONSRV_CONSOLE Console;
1213 ULONG Length;
1214
1215 if (!CsrValidateMessageBuffer(ApiMessage,
1216 (PVOID)&TitleRequest->Title,
1217 TitleRequest->Length,
1218 sizeof(BYTE)))
1219 {
1220 return STATUS_INVALID_PARAMETER;
1221 }
1222
1223 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1224 if (!NT_SUCCESS(Status))
1225 {
1226 DPRINT1("Can't get console, status %lx\n", Status);
1227 return Status;
1228 }
1229
1230 /* Copy title of the console to the user title buffer */
1231 if (TitleRequest->Unicode)
1232 {
1233 if (TitleRequest->Length >= sizeof(WCHAR))
1234 {
1235 Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
1236 RtlCopyMemory(TitleRequest->Title, Console->Title.Buffer, Length);
1237 ((PWCHAR)TitleRequest->Title)[Length / sizeof(WCHAR)] = UNICODE_NULL;
1238 TitleRequest->Length = Length;
1239 }
1240 else
1241 {
1242 TitleRequest->Length = Console->Title.Length;
1243 }
1244 }
1245 else
1246 {
1247 if (TitleRequest->Length >= sizeof(CHAR))
1248 {
1249 Length = min(TitleRequest->Length - sizeof(CHAR), Console->Title.Length / sizeof(WCHAR));
1250 Length = WideCharToMultiByte(Console->InputCodePage, 0,
1251 Console->Title.Buffer, Length,
1252 TitleRequest->Title, Length,
1253 NULL, NULL);
1254 ((PCHAR)TitleRequest->Title)[Length] = ANSI_NULL;
1255 TitleRequest->Length = Length;
1256 }
1257 else
1258 {
1259 TitleRequest->Length = Console->Title.Length / sizeof(WCHAR);
1260 }
1261 }
1262
1263 ConSrvReleaseConsole(Console, TRUE);
1264 return Status;
1265 }
1266
1267 CSR_API(SrvSetConsoleTitle)
1268 {
1269 NTSTATUS Status;
1270 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1271 PCONSRV_CONSOLE Console;
1272
1273 PWCHAR Buffer;
1274 ULONG Length;
1275
1276 if (!CsrValidateMessageBuffer(ApiMessage,
1277 (PVOID)&TitleRequest->Title,
1278 TitleRequest->Length,
1279 sizeof(BYTE)))
1280 {
1281 return STATUS_INVALID_PARAMETER;
1282 }
1283
1284 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1285 if (!NT_SUCCESS(Status))
1286 {
1287 DPRINT1("Can't get console, status %lx\n", Status);
1288 return Status;
1289 }
1290
1291 if (TitleRequest->Unicode)
1292 {
1293 /* Length is in bytes */
1294 Length = TitleRequest->Length;
1295 }
1296 else
1297 {
1298 /* Use the console input CP for the conversion */
1299 Length = MultiByteToWideChar(Console->InputCodePage, 0,
1300 TitleRequest->Title, TitleRequest->Length,
1301 NULL, 0);
1302 /* The returned Length was in number of wchars, convert it in bytes */
1303 Length *= sizeof(WCHAR);
1304 }
1305
1306 /* Allocate a new buffer to hold the new title (NULL-terminated) */
1307 Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Length + sizeof(WCHAR));
1308 if (!Buffer)
1309 {
1310 Status = STATUS_NO_MEMORY;
1311 goto Quit;
1312 }
1313
1314 /* Free the old title */
1315 ConsoleFreeUnicodeString(&Console->Title);
1316
1317 /* Copy title to console */
1318 Console->Title.Buffer = Buffer;
1319 Console->Title.Length = Length;
1320 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
1321
1322 if (TitleRequest->Unicode)
1323 {
1324 RtlCopyMemory(Console->Title.Buffer, TitleRequest->Title, Console->Title.Length);
1325 }
1326 else
1327 {
1328 MultiByteToWideChar(Console->InputCodePage, 0,
1329 TitleRequest->Title, TitleRequest->Length,
1330 Console->Title.Buffer,
1331 Console->Title.Length / sizeof(WCHAR));
1332 }
1333
1334 /* NULL-terminate */
1335 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = UNICODE_NULL;
1336
1337 TermChangeTitle(Console);
1338 Status = STATUS_SUCCESS;
1339
1340 Quit:
1341 ConSrvReleaseConsole(Console, TRUE);
1342 return Status;
1343 }
1344
1345 NTSTATUS NTAPI
1346 ConDrvGetConsoleCP(IN PCONSOLE Console,
1347 OUT PUINT CodePage,
1348 IN BOOLEAN OutputCP);
1349 CSR_API(SrvGetConsoleCP)
1350 {
1351 NTSTATUS Status;
1352 PCONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleCPRequest;
1353 PCONSRV_CONSOLE Console;
1354
1355 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
1356 GetConsoleCPRequest->OutputCP ? "Output" : "Input");
1357
1358 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1359 if (!NT_SUCCESS(Status)) return Status;
1360
1361 Status = ConDrvGetConsoleCP(Console,
1362 &GetConsoleCPRequest->CodePage,
1363 GetConsoleCPRequest->OutputCP);
1364
1365 ConSrvReleaseConsole(Console, TRUE);
1366 return Status;
1367 }
1368
1369 NTSTATUS NTAPI
1370 ConDrvSetConsoleCP(IN PCONSOLE Console,
1371 IN UINT CodePage,
1372 IN BOOLEAN OutputCP);
1373 CSR_API(SrvSetConsoleCP)
1374 {
1375 NTSTATUS Status = STATUS_INVALID_PARAMETER;
1376 PCONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetConsoleCPRequest;
1377 PCONSRV_CONSOLE Console;
1378
1379 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
1380 SetConsoleCPRequest->OutputCP ? "Output" : "Input");
1381
1382 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1383 if (!NT_SUCCESS(Status)) return Status;
1384
1385 Status = ConDrvSetConsoleCP(Console,
1386 SetConsoleCPRequest->CodePage,
1387 SetConsoleCPRequest->OutputCP);
1388
1389 ConSrvReleaseConsole(Console, TRUE);
1390 return Status;
1391 }
1392
1393 CSR_API(SrvGetConsoleProcessList)
1394 {
1395 NTSTATUS Status;
1396 PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
1397 PCONSRV_CONSOLE Console;
1398
1399 if (!CsrValidateMessageBuffer(ApiMessage,
1400 (PVOID)&GetProcessListRequest->ProcessIdsList,
1401 GetProcessListRequest->ProcessCount,
1402 sizeof(DWORD)))
1403 {
1404 return STATUS_INVALID_PARAMETER;
1405 }
1406
1407 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1408 if (!NT_SUCCESS(Status)) return Status;
1409
1410 Status = ConSrvGetConsoleProcessList(Console,
1411 GetProcessListRequest->ProcessIdsList,
1412 GetProcessListRequest->ProcessCount,
1413 &GetProcessListRequest->ProcessCount);
1414
1415 ConSrvReleaseConsole(Console, TRUE);
1416 return Status;
1417 }
1418
1419 CSR_API(SrvGenerateConsoleCtrlEvent)
1420 {
1421 NTSTATUS Status;
1422 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
1423 PCONSRV_CONSOLE Console;
1424
1425 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1426 if (!NT_SUCCESS(Status)) return Status;
1427
1428 Status = ConSrvConsoleProcessCtrlEvent(Console,
1429 GenerateCtrlEventRequest->ProcessGroupId,
1430 GenerateCtrlEventRequest->CtrlEvent);
1431
1432 ConSrvReleaseConsole(Console, TRUE);
1433 return Status;
1434 }
1435
1436 CSR_API(SrvConsoleNotifyLastClose)
1437 {
1438 NTSTATUS Status;
1439 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1440 PCONSRV_CONSOLE Console;
1441
1442 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1443 if (!NT_SUCCESS(Status)) return Status;
1444
1445 /* Only one process is allowed to be registered for last close notification */
1446 if (!Console->NotifyLastClose)
1447 {
1448 Console->NotifyLastClose = TRUE;
1449 Console->NotifiedLastCloseProcess = ProcessData;
1450 Status = STATUS_SUCCESS;
1451 }
1452 else
1453 {
1454 Status = STATUS_ACCESS_DENIED;
1455 }
1456
1457 ConSrvReleaseConsole(Console, TRUE);
1458 return Status;
1459 }
1460
1461
1462
1463 CSR_API(SrvGetConsoleMouseInfo)
1464 {
1465 NTSTATUS Status;
1466 PCONSOLE_GETMOUSEINFO GetMouseInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetMouseInfoRequest;
1467 PCONSRV_CONSOLE Console;
1468
1469 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1470 if (!NT_SUCCESS(Status)) return Status;
1471
1472 /* Just retrieve the number of buttons of the mouse attached to this console */
1473 GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
1474
1475 ConSrvReleaseConsole(Console, TRUE);
1476 return STATUS_SUCCESS;
1477 }
1478
1479 CSR_API(SrvSetConsoleKeyShortcuts)
1480 {
1481 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1482 return STATUS_NOT_IMPLEMENTED;
1483 }
1484
1485 CSR_API(SrvGetConsoleKeyboardLayoutName)
1486 {
1487 NTSTATUS Status;
1488 PCONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetKbdLayoutNameRequest;
1489 PCONSRV_CONSOLE Console;
1490
1491 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1492 if (!NT_SUCCESS(Status)) return Status;
1493
1494 /* Retrieve the keyboard layout name of the system */
1495 if (GetKbdLayoutNameRequest->Ansi)
1496 GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1497 else
1498 GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1499
1500 ConSrvReleaseConsole(Console, TRUE);
1501 return STATUS_SUCCESS;
1502 }
1503
1504 CSR_API(SrvGetConsoleCharType)
1505 {
1506 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1507 return STATUS_NOT_IMPLEMENTED;
1508 }
1509
1510 CSR_API(SrvSetConsoleLocalEUDC)
1511 {
1512 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1513 return STATUS_NOT_IMPLEMENTED;
1514 }
1515
1516 CSR_API(SrvSetConsoleCursorMode)
1517 {
1518 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1519 return STATUS_NOT_IMPLEMENTED;
1520 }
1521
1522 CSR_API(SrvGetConsoleCursorMode)
1523 {
1524 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1525 return STATUS_NOT_IMPLEMENTED;
1526 }
1527
1528 CSR_API(SrvGetConsoleNlsMode)
1529 {
1530 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1531 return STATUS_NOT_IMPLEMENTED;
1532 }
1533
1534 CSR_API(SrvSetConsoleNlsMode)
1535 {
1536 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1537 return STATUS_NOT_IMPLEMENTED;
1538 }
1539
1540 CSR_API(SrvGetConsoleLangId)
1541 {
1542 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1543 return STATUS_NOT_IMPLEMENTED;
1544 }
1545
1546 /* EOF */