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 #include "../concfg/font.h" 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 // static ULONG CurrentConsoleID = 0; 31 32 /* The list of the ConSrv consoles */ 33 static ULONG ConsoleListSize; 34 static PCONSRV_CONSOLE* ConsoleList; 35 // static LIST_ENTRY ConDrvConsoleList; 36 static RTL_RESOURCE ListLock; 37 38 #define ConSrvLockConsoleListExclusive() \ 39 RtlAcquireResourceExclusive(&ListLock, TRUE) 40 41 #define ConSrvLockConsoleListShared() \ 42 RtlAcquireResourceShared(&ListLock, TRUE) 43 44 #define ConSrvUnlockConsoleList() \ 45 RtlReleaseResource(&ListLock) 46 47 #if 0 48 static NTSTATUS 49 ConDrvInsertConsole( 50 IN PCONSOLE Console) 51 { 52 ASSERT(Console); 53 54 /* All went right, so add the console to the list */ 55 ConSrvLockConsoleListExclusive(); 56 57 DPRINT("Insert in the list\n"); 58 InsertTailList(&ConDrvConsoleList, &Console->ListEntry); 59 60 // FIXME: Move this code to the caller function!! 61 /* Get a new console ID */ 62 _InterlockedExchange((PLONG)&Console->ConsoleID, CurrentConsoleID); 63 _InterlockedIncrement((PLONG)&CurrentConsoleID); 64 65 /* Unlock the console list and return success */ 66 ConSrvUnlockConsoleList(); 67 return STATUS_SUCCESS; 68 } 69 #endif 70 71 static NTSTATUS 72 InsertConsole( 73 OUT PHANDLE Handle, 74 IN PCONSRV_CONSOLE Console) 75 { 76 #define CONSOLE_HANDLES_INCREMENT 2 * 3 77 78 NTSTATUS Status = STATUS_SUCCESS; 79 ULONG i = 0; 80 PCONSRV_CONSOLE* Block; 81 82 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) || 83 (ConsoleList != NULL && ConsoleListSize != 0) ); 84 85 /* All went right, so add the console to the list */ 86 ConSrvLockConsoleListExclusive(); 87 DPRINT("Insert in the list\n"); 88 89 if (ConsoleList) 90 { 91 for (i = 0; i < ConsoleListSize; i++) 92 { 93 if (ConsoleList[i] == NULL) break; 94 } 95 } 96 97 if (i >= ConsoleListSize) 98 { 99 DPRINT("Creation of a new handles table\n"); 100 /* Allocate a new handles table */ 101 Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY, 102 (ConsoleListSize + 103 CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSRV_CONSOLE)); 104 if (Block == NULL) 105 { 106 Status = STATUS_UNSUCCESSFUL; 107 goto Quit; 108 } 109 110 /* If we previously had a handles table, free it and use the new one */ 111 if (ConsoleList) 112 { 113 /* Copy the handles from the old table to the new one */ 114 RtlCopyMemory(Block, 115 ConsoleList, 116 ConsoleListSize * sizeof(PCONSRV_CONSOLE)); 117 ConsoleFreeHeap(ConsoleList); 118 } 119 ConsoleList = Block; 120 ConsoleListSize += CONSOLE_HANDLES_INCREMENT; 121 } 122 123 ConsoleList[i] = Console; 124 *Handle = ULongToHandle((i << 2) | 0x3); 125 126 Quit: 127 /* Unlock the console list and return status */ 128 ConSrvUnlockConsoleList(); 129 return Status; 130 } 131 132 /* Unused */ 133 #if 0 134 static NTSTATUS 135 RemoveConsoleByHandle(IN HANDLE Handle) 136 { 137 NTSTATUS Status = STATUS_SUCCESS; 138 PCONSRV_CONSOLE Console; 139 140 BOOLEAN ValidHandle = ((HandleToULong(Handle) & 0x3) == 0x3); 141 ULONG Index = HandleToULong(Handle) >> 2; 142 143 if (!ValidHandle) return STATUS_INVALID_HANDLE; 144 145 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) || 146 (ConsoleList != NULL && ConsoleListSize != 0) ); 147 148 /* Remove the console from the list */ 149 ConSrvLockConsoleListExclusive(); 150 151 if (Index >= ConsoleListSize || 152 (Console = ConsoleList[Index]) == NULL) 153 { 154 Status = STATUS_INVALID_HANDLE; 155 goto Quit; 156 } 157 158 ConsoleList[Index] = NULL; 159 160 Quit: 161 /* Unlock the console list and return status */ 162 ConSrvUnlockConsoleList(); 163 return Status; 164 } 165 #endif 166 167 static NTSTATUS 168 RemoveConsoleByPointer(IN PCONSRV_CONSOLE Console) 169 { 170 ULONG i = 0; 171 172 if (!Console) return STATUS_INVALID_PARAMETER; 173 174 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) || 175 (ConsoleList != NULL && ConsoleListSize != 0) ); 176 177 /* Remove the console from the list */ 178 ConSrvLockConsoleListExclusive(); 179 180 if (ConsoleList) 181 { 182 for (i = 0; i < ConsoleListSize; i++) 183 { 184 if (ConsoleList[i] == Console) ConsoleList[i] = NULL; 185 } 186 } 187 188 /* Unlock the console list and return */ 189 ConSrvUnlockConsoleList(); 190 return STATUS_SUCCESS; 191 } 192 193 #if 0 194 static NTSTATUS 195 RemoveConsole(IN PCONSOLE Console) 196 { 197 // ASSERT(Console); 198 if (!Console) return STATUS_INVALID_PARAMETER; 199 200 /* Remove the console from the list */ 201 ConSrvLockConsoleListExclusive(); 202 203 RemoveEntryList(&Console->ListEntry); 204 205 /* Unlock the console list and return success */ 206 ConSrvUnlockConsoleList(); 207 return STATUS_SUCCESS; 208 } 209 #endif 210 211 212 /* PRIVATE FUNCTIONS **********************************************************/ 213 214 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180 215 static BOOLEAN 216 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest, 217 IN PCWSTR Source) 218 { 219 SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR); 220 if (Size > MAXUSHORT) return FALSE; 221 222 UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size); 223 if (UniDest->Buffer == NULL) return FALSE; 224 225 RtlCopyMemory(UniDest->Buffer, Source, Size); 226 UniDest->MaximumLength = (USHORT)Size; 227 UniDest->Length = (USHORT)Size - sizeof(WCHAR); 228 229 return TRUE; 230 } 231 232 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431 233 static VOID 234 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString) 235 { 236 if (UnicodeString->Buffer) 237 { 238 ConsoleFreeHeap(UnicodeString->Buffer); 239 RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING)); 240 } 241 } 242 243 244 /* CONSOLE VALIDATION FUNCTIONS ***********************************************/ 245 246 BOOLEAN NTAPI 247 ConSrvValidateConsole(OUT PCONSRV_CONSOLE* Console, 248 IN HANDLE ConsoleHandle, 249 IN CONSOLE_STATE ExpectedState, 250 IN BOOLEAN LockConsole) 251 { 252 BOOLEAN RetVal = FALSE; 253 PCONSRV_CONSOLE ValidatedConsole; 254 255 BOOLEAN ValidHandle = ((HandleToULong(ConsoleHandle) & 0x3) == 0x3); 256 ULONG Index = HandleToULong(ConsoleHandle) >> 2; 257 258 if (!ValidHandle) return FALSE; 259 260 if (!Console) return FALSE; 261 *Console = NULL; 262 263 /* 264 * Forbid creation or deletion of consoles when 265 * checking for the existence of a console. 266 */ 267 ConSrvLockConsoleListShared(); 268 269 if (Index >= ConsoleListSize || 270 (ValidatedConsole = ConsoleList[Index]) == NULL) 271 { 272 /* Unlock the console list and return */ 273 ConSrvUnlockConsoleList(); 274 return FALSE; 275 } 276 277 ValidatedConsole = ConsoleList[Index]; 278 279 /* Unlock the console list */ 280 ConSrvUnlockConsoleList(); 281 282 RetVal = ConDrvValidateConsoleUnsafe((PCONSOLE)ValidatedConsole, 283 ExpectedState, 284 LockConsole); 285 if (RetVal) *Console = ValidatedConsole; 286 287 return RetVal; 288 } 289 290 NTSTATUS 291 ConSrvGetConsole(IN PCONSOLE_PROCESS_DATA ProcessData, 292 OUT PCONSRV_CONSOLE* Console, 293 IN BOOLEAN LockConsole) 294 { 295 NTSTATUS Status = STATUS_INVALID_HANDLE; 296 PCONSRV_CONSOLE GrabConsole; 297 298 // if (Console == NULL) return STATUS_INVALID_PARAMETER; 299 ASSERT(Console); 300 *Console = NULL; 301 302 if (ConSrvValidateConsole(&GrabConsole, 303 ProcessData->ConsoleHandle, 304 CONSOLE_RUNNING, 305 LockConsole)) 306 { 307 _InterlockedIncrement(&GrabConsole->ReferenceCount); 308 *Console = GrabConsole; 309 Status = STATUS_SUCCESS; 310 } 311 312 return Status; 313 } 314 315 VOID 316 ConSrvReleaseConsole(IN PCONSRV_CONSOLE Console, 317 IN BOOLEAN IsConsoleLocked) 318 { 319 LONG RefCount = 0; 320 321 if (!Console) return; 322 // if (Console->ReferenceCount == 0) return; // This shouldn't happen 323 ASSERT(Console->ReferenceCount > 0); 324 325 /* The console must be locked */ 326 // ASSERT(Console_locked); 327 328 /* 329 * Decrement the reference count. Save the new value too, 330 * because Console->ReferenceCount might be modified after 331 * the console gets unlocked but before we check whether we 332 * can destroy it. 333 */ 334 RefCount = _InterlockedDecrement(&Console->ReferenceCount); 335 336 /* Unlock the console if needed */ 337 if (IsConsoleLocked) LeaveCriticalSection(&Console->Lock); 338 339 /* Delete the console if needed */ 340 if (RefCount <= 0) ConSrvDeleteConsole(Console); 341 } 342 343 344 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/ 345 346 VOID NTAPI 347 ConSrvInitConsoleSupport(VOID) 348 { 349 DPRINT("CONSRV: ConSrvInitConsoleSupport()\n"); 350 351 /* Initialize the console list and its lock */ 352 ConsoleListSize = 0; 353 ConsoleList = NULL; 354 // InitializeListHead(&ConDrvConsoleList); 355 RtlInitializeResource(&ListLock); 356 357 /* Should call LoadKeyboardLayout */ 358 } 359 360 NTSTATUS NTAPI 361 ConSrvInitTerminal(IN OUT PTERMINAL Terminal, 362 IN OUT PCONSOLE_STATE_INFO ConsoleInfo, 363 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo, 364 IN HANDLE ConsoleLeaderProcessHandle); 365 NTSTATUS NTAPI 366 ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal); 367 368 369 static BOOL 370 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_STATE_INFO ConsoleInfo, 371 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo) 372 { 373 #define PATH_SEPARATOR L'\\' 374 375 BOOL RetVal = FALSE; 376 HRESULT hRes = S_OK; 377 SIZE_T Length = 0; 378 LPWSTR LinkName = NULL; 379 LPWSTR IconPath = NULL; 380 WCHAR Buffer[MAX_PATH + 1]; 381 382 ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0; 383 384 if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0) 385 { 386 // return FALSE; // FIXME!! (for icon loading) 387 RetVal = TRUE; 388 goto Finish; 389 } 390 391 /* 1- Find the last path separator if any */ 392 LinkName = wcsrchr(ConsoleInfo->ConsoleTitle, PATH_SEPARATOR); 393 if (LinkName == NULL) 394 LinkName = ConsoleInfo->ConsoleTitle; 395 else 396 ++LinkName; // Skip the path separator 397 398 /* 2- Check for the link extension. The name ".lnk" is considered invalid. */ 399 Length = wcslen(LinkName); 400 if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) ) 401 return FALSE; 402 403 /* 3- It may be a link. Try to retrieve some properties */ 404 hRes = CoInitialize(NULL); 405 if (SUCCEEDED(hRes)) 406 { 407 /* Get a pointer to the IShellLink interface */ 408 IShellLinkW* pshl = NULL; 409 hRes = CoCreateInstance(&CLSID_ShellLink, 410 NULL, 411 CLSCTX_INPROC_SERVER, 412 &IID_IShellLinkW, 413 (LPVOID*)&pshl); 414 if (SUCCEEDED(hRes)) 415 { 416 /* Get a pointer to the IPersistFile interface */ 417 IPersistFile* ppf = NULL; 418 hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf); 419 if (SUCCEEDED(hRes)) 420 { 421 /* Load the shortcut */ 422 hRes = IPersistFile_Load(ppf, ConsoleInfo->ConsoleTitle, STGM_READ); 423 if (SUCCEEDED(hRes)) 424 { 425 /* 426 * Finally we can get the properties ! 427 * Update the old ones if needed. 428 */ 429 INT ShowCmd = 0; 430 // WORD HotKey = 0; 431 432 /* Reset the name of the console with the name of the shortcut */ 433 Length = min(/*Length*/ Length - 4, // 4 == len(".lnk") 434 (ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR)); 435 wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length); 436 ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL; 437 438 /* Get the window showing command */ 439 hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd); 440 if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->wShowWindow = (WORD)ShowCmd; 441 442 /* Get the hotkey */ 443 // hRes = pshl->GetHotkey(&ShowCmd); 444 // if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->HotKey = HotKey; 445 446 /* Get the icon location, if any */ 447 hRes = IShellLinkW_GetIconLocation(pshl, 448 Buffer, 449 sizeof(Buffer)/sizeof(Buffer[0]) - 1, // == MAX_PATH 450 &ConsoleInitInfo->ConsoleStartInfo->IconIndex); 451 if (!SUCCEEDED(hRes)) 452 { 453 ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0; 454 } 455 else 456 { 457 IconPath = Buffer; 458 } 459 460 // FIXME: Since we still don't load console properties from the shortcut, 461 // return false. When this will be done, we will return true instead. 462 RetVal = TRUE; // FALSE; 463 } 464 IPersistFile_Release(ppf); 465 } 466 IShellLinkW_Release(pshl); 467 } 468 } 469 CoUninitialize(); 470 471 Finish: 472 473 if (RetVal) 474 { 475 /* Get the associated icon, if any */ 476 if (IconPath == NULL) 477 { 478 // Question: How to retrieve the full path name 479 // of the app we are going to run?? 480 Length = RtlDosSearchPath_U(ConsoleInitInfo->CurDir, 481 ConsoleInitInfo->AppName, 482 NULL, 483 sizeof(Buffer), 484 Buffer, 485 NULL); 486 if (Length > 0 && Length < sizeof(Buffer)) 487 IconPath = Buffer; 488 else 489 IconPath = ConsoleInitInfo->AppName; 490 491 // ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0; 492 } 493 DPRINT("IconPath = '%S' ; IconIndex = %lu\n", 494 IconPath, ConsoleInitInfo->ConsoleStartInfo->IconIndex); 495 if (IconPath && *IconPath) 496 { 497 HICON hIcon = NULL, hIconSm = NULL; 498 /* 499 * FIXME!! Because of a strange bug we have in PrivateExtractIconExW 500 * (see r65683 for more details), we cannot use this API to extract 501 * at the same time the large and small icons from the app. 502 * Instead we just use PrivateExtractIconsW. 503 * 504 PrivateExtractIconExW(IconPath, 505 ConsoleInitInfo->ConsoleStartInfo->IconIndex, 506 &hIcon, 507 &hIconSm, 508 1); 509 */ 510 PrivateExtractIconsW(IconPath, 511 ConsoleInitInfo->ConsoleStartInfo->IconIndex, 512 32, 32, 513 &hIcon, NULL, 1, LR_COPYFROMRESOURCE); 514 PrivateExtractIconsW(IconPath, 515 ConsoleInitInfo->ConsoleStartInfo->IconIndex, 516 16, 16, 517 &hIconSm, NULL, 1, LR_COPYFROMRESOURCE); 518 519 DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm); 520 if (hIcon != NULL) ConsoleInitInfo->ConsoleStartInfo->hIcon = hIcon; 521 if (hIconSm != NULL) ConsoleInitInfo->ConsoleStartInfo->hIconSm = hIconSm; 522 } 523 } 524 525 // FIXME: See the previous FIXME above. 526 RetVal = FALSE; 527 528 return RetVal; 529 } 530 531 NTSTATUS NTAPI 532 ConSrvInitConsole(OUT PHANDLE NewConsoleHandle, 533 OUT PCONSRV_CONSOLE* NewConsole, 534 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo, 535 IN PCSR_PROCESS ConsoleLeaderProcess) 536 { 537 NTSTATUS Status; 538 HANDLE ConsoleHandle; 539 PCONSRV_CONSOLE Console; 540 541 BYTE ConsoleInfoBuffer[sizeof(CONSOLE_STATE_INFO) + MAX_PATH * sizeof(WCHAR)]; // CONSRV console information 542 PCONSOLE_STATE_INFO ConsoleInfo = (PCONSOLE_STATE_INFO)&ConsoleInfoBuffer; 543 CONSOLE_INFO DrvConsoleInfo; // Console information for CONDRV 544 545 SIZE_T Length = 0; 546 547 TERMINAL Terminal; /* The ConSrv terminal for this console */ 548 549 if (NewConsole == NULL || ConsoleInitInfo == NULL) 550 return STATUS_INVALID_PARAMETER; 551 552 *NewConsole = NULL; 553 554 DPRINT("Initialization of console '%S' for process '%S' on desktop '%S'\n", 555 ConsoleInitInfo->ConsoleTitle ? ConsoleInitInfo->ConsoleTitle : L"n/a", 556 ConsoleInitInfo->AppName ? ConsoleInitInfo->AppName : L"n/a", 557 ConsoleInitInfo->Desktop ? ConsoleInitInfo->Desktop : L"n/a"); 558 559 /* 560 * Load the console settings 561 */ 562 RtlZeroMemory(ConsoleInfo, sizeof(ConsoleInfoBuffer)); 563 ConsoleInfo->cbSize = sizeof(ConsoleInfoBuffer); 564 565 /* 1. Get the title of the console (initialize ConsoleInfo->ConsoleTitle) */ 566 Length = min(ConsoleInitInfo->TitleLength, 567 (ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR)); 568 wcsncpy(ConsoleInfo->ConsoleTitle, ConsoleInitInfo->ConsoleTitle, Length); 569 ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL; // NULL-terminate it. 570 571 /* 2. Impersonate the caller in order to retrieve settings in its context */ 572 if (!CsrImpersonateClient(NULL)) 573 return STATUS_BAD_IMPERSONATION_LEVEL; 574 575 /* 3. Load the default settings */ 576 ConCfgGetDefaultSettings(ConsoleInfo); 577 578 /* 579 * 4. Load per-application terminal settings. 580 * 581 * Check whether the process creating the console was launched via 582 * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the 583 * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too. 584 */ 585 // if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) // FIXME!! (for icon loading) 586 { 587 if (!LoadShellLinkConsoleInfo(ConsoleInfo, ConsoleInitInfo)) 588 { 589 ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME; 590 } 591 } 592 593 /* 594 * 5. Load the remaining console settings via the registry. 595 */ 596 if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0) 597 { 598 /* 599 * Either we weren't created by an app launched via a shell-link, 600 * or we failed to load shell-link console properties. 601 * Therefore, load the console infos for the application from the registry. 602 */ 603 ConCfgReadUserSettings(ConsoleInfo, FALSE); 604 605 /* 606 * Now, update them with the properties the user might gave to us 607 * via the STARTUPINFO structure before calling CreateProcess 608 * (and which was transmitted via the ConsoleStartInfo structure). 609 * We therefore overwrite the values read in the registry. 610 */ 611 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE) 612 { 613 ConsoleInfo->ScreenAttributes = (USHORT)ConsoleInitInfo->ConsoleStartInfo->wFillAttribute; 614 } 615 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS) 616 { 617 ConsoleInfo->ScreenBufferSize = ConsoleInitInfo->ConsoleStartInfo->dwScreenBufferSize; 618 } 619 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE) 620 { 621 ConsoleInfo->WindowSize = ConsoleInitInfo->ConsoleStartInfo->dwWindowSize; 622 } 623 624 #if 0 625 /* 626 * Now, update them with the properties the user might gave to us 627 * via the STARTUPINFO structure before calling CreateProcess 628 * (and which was transmitted via the ConsoleStartInfo structure). 629 * We therefore overwrite the values read in the registry. 630 */ 631 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION) 632 { 633 ConsoleInfo->AutoPosition = FALSE; 634 ConsoleInfo->WindowPosition.x = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.X; 635 ConsoleInfo->WindowPosition.y = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.Y; 636 } 637 if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN) 638 { 639 ConsoleInfo->FullScreen = TRUE; 640 } 641 #endif 642 } 643 644 /* 6. Revert impersonation */ 645 CsrRevertToSelf(); 646 647 /* Set-up the code page */ 648 ConsoleInfo->CodePage = GetOEMCP(); 649 650 /* 651 * Initialize the ConSrv terminal and give it a chance to load 652 * its own settings and override the console settings. 653 */ 654 Status = ConSrvInitTerminal(&Terminal, 655 ConsoleInfo, 656 ConsoleInitInfo, 657 ConsoleLeaderProcess->ProcessHandle); 658 if (!NT_SUCCESS(Status)) 659 { 660 DPRINT1("CONSRV: Failed to initialize a terminal, Status = 0x%08lx\n", Status); 661 return Status; 662 } 663 DPRINT("CONSRV: Terminal initialized\n"); 664 665 /* Initialize a new console via the driver */ 666 // DrvConsoleInfo.InputBufferSize = 0; 667 DrvConsoleInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize; 668 DrvConsoleInfo.ConsoleSize = ConsoleInfo->WindowSize; 669 DrvConsoleInfo.CursorSize = ConsoleInfo->CursorSize; 670 // DrvConsoleInfo.CursorBlinkOn = ConsoleInfo->CursorBlinkOn; 671 DrvConsoleInfo.ScreenAttrib = ConsoleInfo->ScreenAttributes; 672 DrvConsoleInfo.PopupAttrib = ConsoleInfo->PopupAttributes; 673 DrvConsoleInfo.CodePage = ConsoleInfo->CodePage; 674 675 /* 676 * Allocate a new console 677 */ 678 Console = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*Console)); 679 if (Console == NULL) 680 { 681 DPRINT1("Not enough memory for console creation.\n"); 682 ConSrvDeinitTerminal(&Terminal); 683 return STATUS_NO_MEMORY; 684 } 685 686 Status = ConDrvInitConsole((PCONSOLE)Console, &DrvConsoleInfo); 687 if (!NT_SUCCESS(Status)) 688 { 689 DPRINT1("Creating a new console failed, Status = 0x%08lx\n", Status); 690 ConsoleFreeHeap(Console); 691 ConSrvDeinitTerminal(&Terminal); 692 return Status; 693 } 694 695 DPRINT("Console initialized\n"); 696 697 /*** Register ConSrv features ***/ 698 699 /* Initialize the console title */ 700 #if 0 701 WCHAR DefaultTitle[128]; 702 #endif 703 ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle); 704 #if 0 705 if (ConsoleInfo->ConsoleTitle[0] == UNICODE_NULL) 706 { 707 if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0]))) 708 { 709 ConsoleCreateUnicodeString(&Console->Title, DefaultTitle); 710 } 711 else 712 { 713 ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console"); 714 } 715 } 716 else 717 { 718 #endif 719 ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle); 720 #if 0 721 } 722 #endif 723 724 /* Initialize process support */ 725 // InitProcessSupport(Console); 726 InitializeListHead(&Console->ProcessList); 727 Console->NotifiedLastCloseProcess = NULL; 728 Console->NotifyLastClose = FALSE; 729 Console->HasFocus = FALSE; 730 731 /* Initialize pausing support */ 732 Console->PauseFlags = 0; 733 InitializeListHead(&Console->ReadWaitQueue); 734 InitializeListHead(&Console->WriteWaitQueue); 735 736 /* Initialize the alias and history buffers */ 737 // InitAliasesHistory(Console); 738 Console->Aliases = NULL; 739 InitializeListHead(&Console->HistoryBuffers); 740 Console->NumberOfHistoryBuffers = 0; 741 Console->MaxNumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers; 742 Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize; 743 Console->HistoryNoDup = ConsoleInfo->HistoryNoDup; 744 745 /* Initialize the Input Line Discipline */ 746 // InitLineInput(Console); 747 Console->LineBuffer = NULL; 748 Console->LinePos = Console->LineMaxSize = Console->LineSize = 0; 749 Console->LineComplete = Console->LineUpPressed = FALSE; 750 // LineWakeupMask 751 Console->LineInsertToggle = 752 Console->InsertMode = ConsoleInfo->InsertMode; 753 Console->QuickEdit = ConsoleInfo->QuickEdit; 754 755 /* Popup windows */ 756 InitializeListHead(&Console->PopupWindows); 757 758 /* Colour table */ 759 RtlCopyMemory(Console->Colors, ConsoleInfo->ColorTable, 760 sizeof(ConsoleInfo->ColorTable)); 761 762 /* Create the Initialization Events */ 763 Status = NtCreateEvent(&Console->InitEvents[INIT_SUCCESS], EVENT_ALL_ACCESS, 764 NULL, NotificationEvent, FALSE); 765 if (!NT_SUCCESS(Status)) 766 { 767 DPRINT1("NtCreateEvent(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status); 768 ConDrvDeleteConsole((PCONSOLE)Console); 769 ConSrvDeinitTerminal(&Terminal); 770 return Status; 771 } 772 Status = NtCreateEvent(&Console->InitEvents[INIT_FAILURE], EVENT_ALL_ACCESS, 773 NULL, NotificationEvent, FALSE); 774 if (!NT_SUCCESS(Status)) 775 { 776 DPRINT1("NtCreateEvent(InitEvents[INIT_FAILURE]) failed: %lu\n", Status); 777 NtClose(Console->InitEvents[INIT_SUCCESS]); 778 ConDrvDeleteConsole((PCONSOLE)Console); 779 ConSrvDeinitTerminal(&Terminal); 780 return Status; 781 } 782 783 /* 784 * Attach the ConSrv terminal to the console. 785 * This call makes a copy of our local Terminal variable. 786 */ 787 Status = ConDrvAttachTerminal((PCONSOLE)Console, &Terminal); 788 if (!NT_SUCCESS(Status)) 789 { 790 DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status); 791 NtClose(Console->InitEvents[INIT_FAILURE]); 792 NtClose(Console->InitEvents[INIT_SUCCESS]); 793 ConDrvDeleteConsole((PCONSOLE)Console); 794 ConSrvDeinitTerminal(&Terminal); 795 return Status; 796 } 797 DPRINT("Terminal attached\n"); 798 799 /* All went right, so add the console to the list */ 800 #if 0 801 Status = ConDrvInsertConsole((PCONSOLE)Console); 802 if (!NT_SUCCESS(Status)) 803 { 804 /* Fail */ 805 ConDrvDeleteConsole((PCONSOLE)Console); 806 return Status; 807 } 808 #endif 809 Status = InsertConsole(&ConsoleHandle, Console); 810 811 // FIXME! We do not support at all asynchronous console creation! 812 NtSetEvent(Console->InitEvents[INIT_SUCCESS], NULL); 813 // NtSetEvent(Console->InitEvents[INIT_FAILURE], NULL); 814 815 /* Return the newly created console to the caller and a success code too */ 816 *NewConsoleHandle = ConsoleHandle; 817 *NewConsole = Console; 818 return STATUS_SUCCESS; 819 } 820 821 VOID NTAPI 822 ConSrvDeleteConsole(PCONSRV_CONSOLE Console) 823 { 824 DPRINT("ConSrvDeleteConsole\n"); 825 826 // FIXME: Send a terminate message to all the processes owning this console. 827 // NOTE: In principle there should be none, because such processes would 828 // have a reference to the console and thus this function would not have 829 // been called in the first place. 830 831 /* Remove the console from the list */ 832 RemoveConsoleByPointer(Console); 833 834 /* Destroy the Initialization Events */ 835 NtClose(Console->InitEvents[INIT_FAILURE]); 836 NtClose(Console->InitEvents[INIT_SUCCESS]); 837 838 /* Clean the Input Line Discipline */ 839 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer); 840 841 /* Clean aliases and history */ 842 IntDeleteAllAliases(Console); 843 HistoryDeleteBuffers(Console); 844 845 /* Free the console title */ 846 ConsoleFreeUnicodeString(&Console->OriginalTitle); 847 ConsoleFreeUnicodeString(&Console->Title); 848 849 /* Now, call the driver. ConDrvDetachTerminal is called on-demand. */ 850 ConDrvDeleteConsole((PCONSOLE)Console); 851 852 /* Deinit the ConSrv terminal */ 853 // FIXME!! 854 // ConSrvDeinitTerminal(&Terminal); 855 } 856 857 858 VOID 859 ConioPause(PCONSRV_CONSOLE Console, UCHAR Flags) 860 { 861 Console->PauseFlags |= Flags; 862 ConDrvPause((PCONSOLE)Console); 863 } 864 865 VOID 866 ConioUnpause(PCONSRV_CONSOLE Console, UCHAR Flags) 867 { 868 Console->PauseFlags &= ~Flags; 869 870 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0) 871 if (Console->PauseFlags == 0) 872 { 873 ConDrvUnpause((PCONSOLE)Console); 874 875 CsrNotifyWait(&Console->WriteWaitQueue, 876 TRUE, 877 NULL, 878 NULL); 879 if (!IsListEmpty(&Console->WriteWaitQueue)) 880 { 881 CsrDereferenceWait(&Console->WriteWaitQueue); 882 } 883 } 884 } 885 886 887 /* CONSOLE PROCESS INITIALIZATION FUNCTIONS ***********************************/ 888 889 static NTSTATUS 890 ConSrvInitProcessHandles( 891 IN OUT PCONSOLE_PROCESS_DATA ProcessData, 892 IN PCONSRV_CONSOLE Console, 893 OUT PHANDLE pInputHandle, 894 OUT PHANDLE pOutputHandle, 895 OUT PHANDLE pErrorHandle) 896 { 897 NTSTATUS Status; 898 HANDLE InputHandle = INVALID_HANDLE_VALUE, 899 OutputHandle = INVALID_HANDLE_VALUE, 900 ErrorHandle = INVALID_HANDLE_VALUE; 901 902 /* 903 * Initialize the process handles. Use temporary variables to store 904 * the handles values in such a way that, if we fail, we don't 905 * return to the caller invalid handle values. 906 * 907 * Insert the IO handles. 908 */ 909 910 RtlEnterCriticalSection(&ProcessData->HandleTableLock); 911 912 /* Insert the Input handle */ 913 Status = ConSrvInsertObject(ProcessData, 914 &InputHandle, 915 &Console->InputBuffer.Header, 916 GENERIC_READ | GENERIC_WRITE, 917 TRUE, 918 FILE_SHARE_READ | FILE_SHARE_WRITE); 919 if (!NT_SUCCESS(Status)) 920 { 921 DPRINT1("Failed to insert the input handle\n"); 922 RtlLeaveCriticalSection(&ProcessData->HandleTableLock); 923 ConSrvFreeHandlesTable(ProcessData); 924 return Status; 925 } 926 927 /* Insert the Output handle */ 928 Status = ConSrvInsertObject(ProcessData, 929 &OutputHandle, 930 &Console->ActiveBuffer->Header, 931 GENERIC_READ | GENERIC_WRITE, 932 TRUE, 933 FILE_SHARE_READ | FILE_SHARE_WRITE); 934 if (!NT_SUCCESS(Status)) 935 { 936 DPRINT1("Failed to insert the output handle\n"); 937 RtlLeaveCriticalSection(&ProcessData->HandleTableLock); 938 ConSrvFreeHandlesTable(ProcessData); 939 return Status; 940 } 941 942 /* Insert the Error handle */ 943 Status = ConSrvInsertObject(ProcessData, 944 &ErrorHandle, 945 &Console->ActiveBuffer->Header, 946 GENERIC_READ | GENERIC_WRITE, 947 TRUE, 948 FILE_SHARE_READ | FILE_SHARE_WRITE); 949 if (!NT_SUCCESS(Status)) 950 { 951 DPRINT1("Failed to insert the error handle\n"); 952 RtlLeaveCriticalSection(&ProcessData->HandleTableLock); 953 ConSrvFreeHandlesTable(ProcessData); 954 return Status; 955 } 956 957 RtlLeaveCriticalSection(&ProcessData->HandleTableLock); 958 959 /* Return the newly created handles */ 960 *pInputHandle = InputHandle; 961 *pOutputHandle = OutputHandle; 962 *pErrorHandle = ErrorHandle; 963 964 return STATUS_SUCCESS; 965 } 966 967 NTSTATUS 968 ConSrvAllocateConsole( 969 IN OUT PCONSOLE_PROCESS_DATA ProcessData, 970 OUT PHANDLE pInputHandle, 971 OUT PHANDLE pOutputHandle, 972 OUT PHANDLE pErrorHandle, 973 IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo) 974 { 975 NTSTATUS Status = STATUS_SUCCESS; 976 HANDLE ConsoleHandle; 977 PCONSRV_CONSOLE Console; 978 979 /* 980 * We are about to create a new console. However when ConSrvNewProcess() 981 * was called, we didn't know that we wanted to create a new console and 982 * therefore, we by default inherited the handle table from our parent 983 * process. It's only now that we notice that in fact we do not need 984 * them, because we've created a new console and thus we must use it. 985 * 986 * Therefore, free the handle table so that we can recreate 987 * a new one later on. 988 */ 989 ConSrvFreeHandlesTable(ProcessData); 990 991 /* Initialize a new Console owned by this process */ 992 Status = ConSrvInitConsole(&ConsoleHandle, 993 &Console, 994 ConsoleInitInfo, 995 ProcessData->Process); 996 if (!NT_SUCCESS(Status)) 997 { 998 DPRINT1("Console initialization failed\n"); 999 return Status; 1000 } 1001 1002 /* Assign the new console handle */ 1003 ProcessData->ConsoleHandle = ConsoleHandle; 1004 1005 /* Initialize the process handles */ 1006 Status = ConSrvInitProcessHandles(ProcessData, 1007 Console, 1008 pInputHandle, 1009 pOutputHandle, 1010 pErrorHandle); 1011 if (!NT_SUCCESS(Status)) 1012 { 1013 DPRINT1("Failed to initialize the process handles\n"); 1014 ConSrvDeleteConsole(Console); 1015 ProcessData->ConsoleHandle = NULL; 1016 return Status; 1017 } 1018 1019 /* Duplicate the Initialization Events */ 1020 Status = NtDuplicateObject(NtCurrentProcess(), 1021 Console->InitEvents[INIT_SUCCESS], 1022 ProcessData->Process->ProcessHandle, 1023 &ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS], 1024 EVENT_ALL_ACCESS, 0, 0); 1025 if (!NT_SUCCESS(Status)) 1026 { 1027 DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status); 1028 ConSrvFreeHandlesTable(ProcessData); 1029 ConSrvDeleteConsole(Console); 1030 ProcessData->ConsoleHandle = NULL; 1031 return Status; 1032 } 1033 1034 Status = NtDuplicateObject(NtCurrentProcess(), 1035 Console->InitEvents[INIT_FAILURE], 1036 ProcessData->Process->ProcessHandle, 1037 &ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE], 1038 EVENT_ALL_ACCESS, 0, 0); 1039 if (!NT_SUCCESS(Status)) 1040 { 1041 DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status); 1042 NtDuplicateObject(ProcessData->Process->ProcessHandle, 1043 ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS], 1044 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); 1045 ConSrvFreeHandlesTable(ProcessData); 1046 ConSrvDeleteConsole(Console); 1047 ProcessData->ConsoleHandle = NULL; 1048 return Status; 1049 } 1050 1051 /* Duplicate the Input Event */ 1052 Status = NtDuplicateObject(NtCurrentProcess(), 1053 Console->InputBuffer.ActiveEvent, 1054 ProcessData->Process->ProcessHandle, 1055 &ConsoleInitInfo->ConsoleStartInfo->InputWaitHandle, 1056 EVENT_ALL_ACCESS, 0, 0); 1057 if (!NT_SUCCESS(Status)) 1058 { 1059 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status); 1060 NtDuplicateObject(ProcessData->Process->ProcessHandle, 1061 ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE], 1062 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); 1063 NtDuplicateObject(ProcessData->Process->ProcessHandle, 1064 ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS], 1065 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); 1066 ConSrvFreeHandlesTable(ProcessData); 1067 ConSrvDeleteConsole(Console); 1068 ProcessData->ConsoleHandle = NULL; 1069 return Status; 1070 } 1071 1072 /* Mark the process as having a console */ 1073 ProcessData->ConsoleApp = TRUE; 1074 ProcessData->Process->Flags |= CsrProcessIsConsoleApp; 1075 1076 /* Return the console handle to the caller */ 1077 ConsoleInitInfo->ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle; 1078 1079 /* 1080 * Insert the process into the processes list of the console, 1081 * and set its foreground priority. 1082 */ 1083 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink); 1084 ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus); 1085 1086 /* Add a reference count because the process is tied to the console */ 1087 _InterlockedIncrement(&Console->ReferenceCount); 1088 1089 /* Update the internal info of the terminal */ 1090 TermRefreshInternalInfo(Console); 1091 1092 return STATUS_SUCCESS; 1093 } 1094 1095 NTSTATUS 1096 ConSrvInheritConsole( 1097 IN OUT PCONSOLE_PROCESS_DATA ProcessData, 1098 IN HANDLE ConsoleHandle, 1099 IN BOOLEAN CreateNewHandleTable, 1100 OUT PHANDLE pInputHandle, 1101 OUT PHANDLE pOutputHandle, 1102 OUT PHANDLE pErrorHandle, 1103 IN OUT PCONSOLE_START_INFO ConsoleStartInfo) 1104 { 1105 NTSTATUS Status = STATUS_SUCCESS; 1106 PCONSRV_CONSOLE Console; 1107 1108 /* Validate and lock the console */ 1109 if (!ConSrvValidateConsole(&Console, 1110 ConsoleHandle, 1111 CONSOLE_RUNNING, TRUE)) 1112 { 1113 // FIXME: Find another status code 1114 return STATUS_UNSUCCESSFUL; 1115 } 1116 1117 /* Inherit the console */ 1118 ProcessData->ConsoleHandle = ConsoleHandle; 1119 1120 if (CreateNewHandleTable) 1121 { 1122 /* 1123 * We are about to create a new console. However when ConSrvNewProcess() 1124 * was called, we didn't know that we wanted to create a new console and 1125 * therefore, we by default inherited the handle table from our parent 1126 * process. It's only now that we notice that in fact we do not need 1127 * them, because we've created a new console and thus we must use it. 1128 * 1129 * Therefore, free the handle table so that we can recreate 1130 * a new one later on. 1131 */ 1132 ConSrvFreeHandlesTable(ProcessData); 1133 1134 /* Initialize the process handles */ 1135 Status = ConSrvInitProcessHandles(ProcessData, 1136 Console, 1137 pInputHandle, 1138 pOutputHandle, 1139 pErrorHandle); 1140 if (!NT_SUCCESS(Status)) 1141 { 1142 DPRINT1("Failed to initialize the process handles\n"); 1143 ProcessData->ConsoleHandle = NULL; 1144 goto Quit; 1145 } 1146 } 1147 1148 /* Duplicate the Initialization Events */ 1149 Status = NtDuplicateObject(NtCurrentProcess(), 1150 Console->InitEvents[INIT_SUCCESS], 1151 ProcessData->Process->ProcessHandle, 1152 &ConsoleStartInfo->InitEvents[INIT_SUCCESS], 1153 EVENT_ALL_ACCESS, 0, 0); 1154 if (!NT_SUCCESS(Status)) 1155 { 1156 DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status); 1157 ConSrvFreeHandlesTable(ProcessData); 1158 ProcessData->ConsoleHandle = NULL; 1159 goto Quit; 1160 } 1161 1162 Status = NtDuplicateObject(NtCurrentProcess(), 1163 Console->InitEvents[INIT_FAILURE], 1164 ProcessData->Process->ProcessHandle, 1165 &ConsoleStartInfo->InitEvents[INIT_FAILURE], 1166 EVENT_ALL_ACCESS, 0, 0); 1167 if (!NT_SUCCESS(Status)) 1168 { 1169 DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status); 1170 NtDuplicateObject(ProcessData->Process->ProcessHandle, 1171 ConsoleStartInfo->InitEvents[INIT_SUCCESS], 1172 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); 1173 ConSrvFreeHandlesTable(ProcessData); 1174 ProcessData->ConsoleHandle = NULL; 1175 goto Quit; 1176 } 1177 1178 /* Duplicate the Input Event */ 1179 Status = NtDuplicateObject(NtCurrentProcess(), 1180 Console->InputBuffer.ActiveEvent, 1181 ProcessData->Process->ProcessHandle, 1182 &ConsoleStartInfo->InputWaitHandle, 1183 EVENT_ALL_ACCESS, 0, 0); 1184 if (!NT_SUCCESS(Status)) 1185 { 1186 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status); 1187 NtDuplicateObject(ProcessData->Process->ProcessHandle, 1188 ConsoleStartInfo->InitEvents[INIT_FAILURE], 1189 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); 1190 NtDuplicateObject(ProcessData->Process->ProcessHandle, 1191 ConsoleStartInfo->InitEvents[INIT_SUCCESS], 1192 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); 1193 ConSrvFreeHandlesTable(ProcessData); // NOTE: Always free the handle table. 1194 ProcessData->ConsoleHandle = NULL; 1195 goto Quit; 1196 } 1197 1198 /* Mark the process as having a console */ 1199 ProcessData->ConsoleApp = TRUE; 1200 ProcessData->Process->Flags |= CsrProcessIsConsoleApp; 1201 1202 /* Return the console handle to the caller */ 1203 ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle; 1204 1205 /* 1206 * Insert the process into the processes list of the console, 1207 * and set its foreground priority. 1208 */ 1209 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink); 1210 ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus); 1211 1212 /* Add a reference count because the process is tied to the console */ 1213 _InterlockedIncrement(&Console->ReferenceCount); 1214 1215 /* Update the internal info of the terminal */ 1216 TermRefreshInternalInfo(Console); 1217 1218 Status = STATUS_SUCCESS; 1219 1220 Quit: 1221 /* Unlock the console and return */ 1222 LeaveCriticalSection(&Console->Lock); 1223 return Status; 1224 } 1225 1226 NTSTATUS 1227 ConSrvRemoveConsole( 1228 IN OUT PCONSOLE_PROCESS_DATA ProcessData) 1229 { 1230 PCONSRV_CONSOLE Console; 1231 PCONSOLE_PROCESS_DATA ConsoleLeaderProcess; 1232 1233 DPRINT("ConSrvRemoveConsole\n"); 1234 1235 /* Mark the process as not having a console anymore */ 1236 ProcessData->ConsoleApp = FALSE; 1237 ProcessData->Process->Flags &= ~CsrProcessIsConsoleApp; 1238 1239 /* Validate and lock the console */ 1240 if (!ConSrvValidateConsole(&Console, 1241 ProcessData->ConsoleHandle, 1242 CONSOLE_RUNNING, TRUE)) 1243 { 1244 // FIXME: Find another status code 1245 return STATUS_UNSUCCESSFUL; 1246 } 1247 1248 DPRINT("ConSrvRemoveConsole - Locking OK\n"); 1249 1250 /* Retrieve the console leader process */ 1251 ConsoleLeaderProcess = ConSrvGetConsoleLeaderProcess(Console); 1252 1253 /* Close all console handles and free the handle table */ 1254 ConSrvFreeHandlesTable(ProcessData); 1255 1256 /* Detach the process from the console */ 1257 ProcessData->ConsoleHandle = NULL; 1258 1259 /* Remove the process from the console's list of processes */ 1260 RemoveEntryList(&ProcessData->ConsoleLink); 1261 1262 /* Check whether the console should send a last close notification */ 1263 if (Console->NotifyLastClose) 1264 { 1265 /* If we are removing the process which wants the last close notification... */ 1266 if (ProcessData == Console->NotifiedLastCloseProcess) 1267 { 1268 /* ... just reset the flag and the pointer... */ 1269 Console->NotifyLastClose = FALSE; 1270 Console->NotifiedLastCloseProcess = NULL; 1271 } 1272 /* 1273 * ... otherwise, if we are removing the console leader process 1274 * (that cannot be the process wanting the notification, because 1275 * the previous case already dealt with it)... 1276 */ 1277 else if (ProcessData == ConsoleLeaderProcess) 1278 { 1279 /* 1280 * ... reset the flag first (so that we avoid multiple notifications) 1281 * and then send the last close notification. 1282 */ 1283 Console->NotifyLastClose = FALSE; 1284 ConSrvConsoleCtrlEvent(CTRL_LAST_CLOSE_EVENT, Console->NotifiedLastCloseProcess); 1285 1286 /* Only now, reset the pointer */ 1287 Console->NotifiedLastCloseProcess = NULL; 1288 } 1289 } 1290 1291 /* Update the internal info of the terminal */ 1292 TermRefreshInternalInfo(Console); 1293 1294 /* Release the console */ 1295 DPRINT("ConSrvRemoveConsole - Decrement Console->ReferenceCount = %lu\n", Console->ReferenceCount); 1296 ConSrvReleaseConsole(Console, TRUE); 1297 1298 return STATUS_SUCCESS; 1299 } 1300 1301 1302 /* CONSOLE PROCESS MANAGEMENT FUNCTIONS ***************************************/ 1303 1304 NTSTATUS 1305 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent, 1306 IN PCONSOLE_PROCESS_DATA ProcessData, 1307 IN ULONG Timeout) 1308 { 1309 NTSTATUS Status = STATUS_SUCCESS; 1310 1311 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess); 1312 1313 /* 1314 * Be sure we effectively have a control routine. It resides in kernel32.dll (client). 1315 */ 1316 if (ProcessData->CtrlRoutine == NULL) return Status; 1317 1318 _SEH2_TRY 1319 { 1320 HANDLE Thread = NULL; 1321 1322 _SEH2_TRY 1323 { 1324 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0, 1325 ProcessData->CtrlRoutine, 1326 UlongToPtr(CtrlEvent), 0, NULL); 1327 if (NULL == Thread) 1328 { 1329 Status = RtlGetLastNtStatus(); 1330 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status); 1331 } 1332 else 1333 { 1334 DPRINT("ProcessData->CtrlRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", 1335 ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process); 1336 WaitForSingleObject(Thread, Timeout); 1337 } 1338 } 1339 _SEH2_FINALLY 1340 { 1341 CloseHandle(Thread); 1342 } 1343 _SEH2_END; 1344 } 1345 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1346 { 1347 Status = _SEH2_GetExceptionCode(); 1348 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status); 1349 } 1350 _SEH2_END; 1351 1352 return Status; 1353 } 1354 1355 NTSTATUS 1356 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent, 1357 IN PCONSOLE_PROCESS_DATA ProcessData) 1358 { 1359 return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0); 1360 } 1361 1362 PCONSOLE_PROCESS_DATA NTAPI 1363 ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console) 1364 { 1365 if (Console == NULL) return NULL; 1366 1367 return CONTAINING_RECORD(Console->ProcessList.Blink, 1368 CONSOLE_PROCESS_DATA, 1369 ConsoleLink); 1370 } 1371 1372 NTSTATUS NTAPI 1373 ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console, 1374 IN OUT PULONG ProcessIdsList, 1375 IN ULONG MaxIdListItems, 1376 OUT PULONG ProcessIdsTotal) 1377 { 1378 PCONSOLE_PROCESS_DATA current; 1379 PLIST_ENTRY current_entry; 1380 1381 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL) 1382 return STATUS_INVALID_PARAMETER; 1383 1384 *ProcessIdsTotal = 0; 1385 1386 for (current_entry = Console->ProcessList.Flink; 1387 current_entry != &Console->ProcessList; 1388 current_entry = current_entry->Flink) 1389 { 1390 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink); 1391 if (++(*ProcessIdsTotal) <= MaxIdListItems) 1392 { 1393 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess); 1394 } 1395 } 1396 1397 return STATUS_SUCCESS; 1398 } 1399 1400 // ConSrvGenerateConsoleCtrlEvent 1401 NTSTATUS NTAPI 1402 ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console, 1403 IN ULONG ProcessGroupId, 1404 IN ULONG CtrlEvent) 1405 { 1406 NTSTATUS Status = STATUS_SUCCESS; 1407 PLIST_ENTRY current_entry; 1408 PCONSOLE_PROCESS_DATA current; 1409 1410 /* If the console is already being destroyed, just return */ 1411 if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING)) 1412 return STATUS_UNSUCCESSFUL; 1413 1414 /* 1415 * Loop through the process list, from the most recent process 1416 * (the active one) to the oldest one (the first created, i.e. 1417 * the console leader process), and for each, send an event 1418 * (new processes are inserted at the head of the console process list). 1419 */ 1420 current_entry = Console->ProcessList.Flink; 1421 while (current_entry != &Console->ProcessList) 1422 { 1423 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink); 1424 current_entry = current_entry->Flink; 1425 1426 /* 1427 * Only processes belonging to the same process group are signaled. 1428 * If the process group ID is zero, then all the processes are signaled. 1429 */ 1430 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId) 1431 { 1432 Status = ConSrvConsoleCtrlEvent(CtrlEvent, current); 1433 } 1434 } 1435 1436 return Status; 1437 } 1438 1439 VOID 1440 ConSrvSetProcessFocus(IN PCSR_PROCESS CsrProcess, 1441 IN BOOLEAN SetForeground) 1442 { 1443 // FIXME: Call NtUserSetInformationProcess (currently unimplemented!) 1444 // for setting Win32 foreground/background flags. 1445 1446 if (SetForeground) 1447 CsrSetForegroundPriority(CsrProcess); 1448 else 1449 CsrSetBackgroundPriority(CsrProcess); 1450 } 1451 1452 NTSTATUS NTAPI 1453 ConSrvSetConsoleProcessFocus(IN PCONSRV_CONSOLE Console, 1454 IN BOOLEAN SetForeground) 1455 { 1456 PLIST_ENTRY current_entry; 1457 PCONSOLE_PROCESS_DATA current; 1458 1459 /* If the console is already being destroyed, just return */ 1460 if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING)) 1461 return STATUS_UNSUCCESSFUL; 1462 1463 /* 1464 * Loop through the process list, from the most recent process 1465 * to the oldest one, and for each, set its foreground priority. 1466 */ 1467 current_entry = Console->ProcessList.Flink; 1468 while (current_entry != &Console->ProcessList) 1469 { 1470 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink); 1471 current_entry = current_entry->Flink; 1472 1473 ConSrvSetProcessFocus(current->Process, SetForeground); 1474 } 1475 1476 return STATUS_SUCCESS; 1477 } 1478 1479 1480 /* PUBLIC SERVER APIS *********************************************************/ 1481 1482 /* API_NUMBER: ConsolepAlloc */ 1483 CON_API_NOCONSOLE(SrvAllocConsole, 1484 CONSOLE_ALLOCCONSOLE, AllocConsoleRequest) 1485 { 1486 NTSTATUS Status = STATUS_SUCCESS; 1487 CONSOLE_INIT_INFO ConsoleInitInfo; 1488 1489 if (ProcessData->ConsoleHandle != NULL) 1490 { 1491 DPRINT1("Process already has a console\n"); 1492 return STATUS_ACCESS_DENIED; 1493 } 1494 1495 if ( !CsrValidateMessageBuffer(ApiMessage, 1496 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo, 1497 1, 1498 sizeof(CONSOLE_START_INFO)) || 1499 !CsrValidateMessageBuffer(ApiMessage, 1500 (PVOID*)&AllocConsoleRequest->ConsoleTitle, 1501 AllocConsoleRequest->TitleLength, 1502 sizeof(BYTE)) || 1503 !CsrValidateMessageBuffer(ApiMessage, 1504 (PVOID*)&AllocConsoleRequest->Desktop, 1505 AllocConsoleRequest->DesktopLength, 1506 sizeof(BYTE)) || 1507 !CsrValidateMessageBuffer(ApiMessage, 1508 (PVOID*)&AllocConsoleRequest->CurDir, 1509 AllocConsoleRequest->CurDirLength, 1510 sizeof(BYTE)) || 1511 !CsrValidateMessageBuffer(ApiMessage, 1512 (PVOID*)&AllocConsoleRequest->AppName, 1513 AllocConsoleRequest->AppNameLength, 1514 sizeof(BYTE)) ) 1515 { 1516 return STATUS_INVALID_PARAMETER; 1517 } 1518 1519 /* Initialize the console initialization info structure */ 1520 ConsoleInitInfo.ConsoleStartInfo = AllocConsoleRequest->ConsoleStartInfo; 1521 ConsoleInitInfo.IsWindowVisible = TRUE; // The console window is always visible. 1522 ConsoleInitInfo.TitleLength = AllocConsoleRequest->TitleLength; 1523 ConsoleInitInfo.ConsoleTitle = AllocConsoleRequest->ConsoleTitle; 1524 ConsoleInitInfo.DesktopLength = AllocConsoleRequest->DesktopLength; 1525 ConsoleInitInfo.Desktop = AllocConsoleRequest->Desktop; 1526 ConsoleInitInfo.AppNameLength = AllocConsoleRequest->AppNameLength; 1527 ConsoleInitInfo.AppName = AllocConsoleRequest->AppName; 1528 ConsoleInitInfo.CurDirLength = AllocConsoleRequest->CurDirLength; 1529 ConsoleInitInfo.CurDir = AllocConsoleRequest->CurDir; 1530 1531 /* Initialize a new Console owned by the Console Leader Process */ 1532 Status = ConSrvAllocateConsole(ProcessData, 1533 &AllocConsoleRequest->ConsoleStartInfo->InputHandle, 1534 &AllocConsoleRequest->ConsoleStartInfo->OutputHandle, 1535 &AllocConsoleRequest->ConsoleStartInfo->ErrorHandle, 1536 &ConsoleInitInfo); 1537 if (!NT_SUCCESS(Status)) 1538 { 1539 DPRINT1("Console allocation failed\n"); 1540 return Status; 1541 } 1542 1543 /* Set the Property-Dialog and Control-Dispatcher handlers */ 1544 ProcessData->PropRoutine = AllocConsoleRequest->PropRoutine; 1545 ProcessData->CtrlRoutine = AllocConsoleRequest->CtrlRoutine; 1546 1547 return STATUS_SUCCESS; 1548 } 1549 1550 /* API_NUMBER: ConsolepAttach */ 1551 CON_API_NOCONSOLE(SrvAttachConsole, 1552 CONSOLE_ATTACHCONSOLE, AttachConsoleRequest) 1553 { 1554 NTSTATUS Status = STATUS_SUCCESS; 1555 PCSR_PROCESS SourceProcess = NULL; // The parent process. 1556 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves. 1557 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId); 1558 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData; 1559 1560 TargetProcessData = ConsoleGetPerProcessData(TargetProcess); 1561 1562 if (TargetProcessData->ConsoleHandle != NULL) 1563 { 1564 DPRINT1("Process already has a console\n"); 1565 return STATUS_ACCESS_DENIED; 1566 } 1567 1568 if (!CsrValidateMessageBuffer(ApiMessage, 1569 (PVOID*)&AttachConsoleRequest->ConsoleStartInfo, 1570 1, 1571 sizeof(CONSOLE_START_INFO))) 1572 { 1573 return STATUS_INVALID_PARAMETER; 1574 } 1575 1576 /* Check whether we try to attach to the parent's console */ 1577 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS)) 1578 { 1579 PROCESS_BASIC_INFORMATION ProcessInfo; 1580 ULONG Length = sizeof(ProcessInfo); 1581 1582 /* Get the real parent's PID */ 1583 1584 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle, 1585 ProcessBasicInformation, 1586 &ProcessInfo, 1587 Length, &Length); 1588 if (!NT_SUCCESS(Status)) 1589 { 1590 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = 0x%08lx\n", Status); 1591 return Status; 1592 } 1593 1594 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId); 1595 } 1596 1597 /* Lock the source process via its PID */ 1598 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess); 1599 if (!NT_SUCCESS(Status)) return Status; 1600 1601 SourceProcessData = ConsoleGetPerProcessData(SourceProcess); 1602 1603 if (SourceProcessData->ConsoleHandle == NULL) 1604 { 1605 Status = STATUS_INVALID_HANDLE; 1606 goto Quit; 1607 } 1608 1609 /* 1610 * Inherit the console from the parent, 1611 * if any, otherwise return an error. 1612 */ 1613 Status = ConSrvInheritConsole(TargetProcessData, 1614 SourceProcessData->ConsoleHandle, 1615 TRUE, 1616 &AttachConsoleRequest->ConsoleStartInfo->InputHandle, 1617 &AttachConsoleRequest->ConsoleStartInfo->OutputHandle, 1618 &AttachConsoleRequest->ConsoleStartInfo->ErrorHandle, 1619 AttachConsoleRequest->ConsoleStartInfo); 1620 if (!NT_SUCCESS(Status)) 1621 { 1622 DPRINT1("Console inheritance failed\n"); 1623 goto Quit; 1624 } 1625 1626 /* Set the Property-Dialog and Control-Dispatcher handlers */ 1627 TargetProcessData->PropRoutine = AttachConsoleRequest->PropRoutine; 1628 TargetProcessData->CtrlRoutine = AttachConsoleRequest->CtrlRoutine; 1629 1630 Status = STATUS_SUCCESS; 1631 1632 Quit: 1633 /* Unlock the "source" process and exit */ 1634 CsrUnlockProcess(SourceProcess); 1635 return Status; 1636 } 1637 1638 /* API_NUMBER: ConsolepFree */ 1639 CON_API_NOCONSOLE(SrvFreeConsole, 1640 CONSOLE_FREECONSOLE, FreeConsoleRequest) 1641 { 1642 /* 1643 * If this process doesn't have a console handle, bail out immediately. 1644 * Also the passed console handle should be the same as the process' one. 1645 */ 1646 if ((FreeConsoleRequest->ConsoleHandle == NULL) || 1647 (FreeConsoleRequest->ConsoleHandle != ProcessData->ConsoleHandle)) 1648 { 1649 return STATUS_INVALID_HANDLE; // STATUS_ACCESS_DENIED; 1650 } 1651 1652 return ConSrvRemoveConsole(ProcessData); 1653 } 1654 1655 NTSTATUS NTAPI 1656 ConDrvGetConsoleMode(IN PCONSOLE Console, 1657 IN PCONSOLE_IO_OBJECT Object, 1658 OUT PULONG ConsoleMode); 1659 /* API_NUMBER: ConsolepGetMode */ 1660 CON_API(SrvGetConsoleMode, 1661 CONSOLE_GETSETCONSOLEMODE, ConsoleModeRequest) 1662 { 1663 NTSTATUS Status; 1664 PCONSOLE_IO_OBJECT Object; 1665 PULONG ConsoleMode = &ConsoleModeRequest->Mode; 1666 1667 Status = ConSrvGetObject(ProcessData, 1668 ConsoleModeRequest->Handle, 1669 &Object, NULL, GENERIC_READ, TRUE, 0); 1670 if (!NT_SUCCESS(Status)) 1671 return Status; 1672 1673 ASSERT((PCONSOLE)Console == Object->Console); 1674 1675 /* Get the standard console modes */ 1676 Status = ConDrvGetConsoleMode((PCONSOLE)Console, Object, ConsoleMode); 1677 if (NT_SUCCESS(Status)) 1678 { 1679 /* 1680 * If getting the console modes succeeds, then retrieve 1681 * the extended CONSRV-specific input modes. 1682 */ 1683 if (INPUT_BUFFER == Object->Type) 1684 { 1685 if (Console->InsertMode || Console->QuickEdit) 1686 { 1687 /* Windows also adds ENABLE_EXTENDED_FLAGS, even if it's not documented on MSDN */ 1688 *ConsoleMode |= ENABLE_EXTENDED_FLAGS; 1689 1690 if (Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE; 1691 if (Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE; 1692 } 1693 } 1694 } 1695 1696 ConSrvReleaseObject(Object, TRUE); 1697 return Status; 1698 } 1699 1700 NTSTATUS NTAPI 1701 ConDrvSetConsoleMode(IN PCONSOLE Console, 1702 IN PCONSOLE_IO_OBJECT Object, 1703 IN ULONG ConsoleMode); 1704 /* API_NUMBER: ConsolepSetMode */ 1705 CON_API(SrvSetConsoleMode, 1706 CONSOLE_GETSETCONSOLEMODE, ConsoleModeRequest) 1707 { 1708 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \ 1709 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE ) 1710 // NOTE: Vista+ ENABLE_AUTO_POSITION is also a control mode. 1711 1712 NTSTATUS Status; 1713 PCONSOLE_IO_OBJECT Object; 1714 ULONG ConsoleMode = ConsoleModeRequest->Mode; 1715 1716 Status = ConSrvGetObject(ProcessData, 1717 ConsoleModeRequest->Handle, 1718 &Object, NULL, GENERIC_WRITE, TRUE, 0); 1719 if (!NT_SUCCESS(Status)) 1720 return Status; 1721 1722 ASSERT((PCONSOLE)Console == Object->Console); 1723 1724 /* Set the standard console modes (without the CONSRV-specific input modes) */ 1725 ConsoleMode &= ~CONSOLE_VALID_CONTROL_MODES; // Remove CONSRV-specific input modes. 1726 Status = ConDrvSetConsoleMode((PCONSOLE)Console, Object, ConsoleMode); 1727 if (NT_SUCCESS(Status)) 1728 { 1729 /* 1730 * If setting the console modes succeeds, then set 1731 * the extended CONSRV-specific input modes. 1732 */ 1733 if (INPUT_BUFFER == Object->Type) 1734 { 1735 ConsoleMode = ConsoleModeRequest->Mode; 1736 1737 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES) 1738 { 1739 /* 1740 * If we use control mode flags without ENABLE_EXTENDED_FLAGS, 1741 * then consider the flags invalid. 1742 */ 1743 if ((ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0) 1744 { 1745 Status = STATUS_INVALID_PARAMETER; 1746 } 1747 else 1748 { 1749 Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE); 1750 Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE); 1751 } 1752 } 1753 } 1754 } 1755 1756 ConSrvReleaseObject(Object, TRUE); 1757 return Status; 1758 } 1759 1760 /* API_NUMBER: ConsolepGetTitle */ 1761 CON_API(SrvGetConsoleTitle, 1762 CONSOLE_GETSETCONSOLETITLE, TitleRequest) 1763 { 1764 ULONG Length; 1765 1766 if (!CsrValidateMessageBuffer(ApiMessage, 1767 (PVOID)&TitleRequest->Title, 1768 TitleRequest->Length, 1769 sizeof(BYTE))) 1770 { 1771 return STATUS_INVALID_PARAMETER; 1772 } 1773 1774 /* Copy title of the console to the user title buffer */ 1775 if (TitleRequest->Unicode) 1776 { 1777 if (TitleRequest->Length >= sizeof(WCHAR)) 1778 { 1779 Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length); 1780 RtlCopyMemory(TitleRequest->Title, Console->Title.Buffer, Length); 1781 ((PWCHAR)TitleRequest->Title)[Length / sizeof(WCHAR)] = UNICODE_NULL; 1782 TitleRequest->Length = Length; 1783 } 1784 else 1785 { 1786 TitleRequest->Length = Console->Title.Length; 1787 } 1788 } 1789 else 1790 { 1791 if (TitleRequest->Length >= sizeof(CHAR)) 1792 { 1793 Length = min(TitleRequest->Length - sizeof(CHAR), Console->Title.Length / sizeof(WCHAR)); 1794 Length = WideCharToMultiByte(Console->InputCodePage, 0, 1795 Console->Title.Buffer, Length, 1796 TitleRequest->Title, Length, 1797 NULL, NULL); 1798 ((PCHAR)TitleRequest->Title)[Length] = ANSI_NULL; 1799 TitleRequest->Length = Length; 1800 } 1801 else 1802 { 1803 TitleRequest->Length = Console->Title.Length / sizeof(WCHAR); 1804 } 1805 } 1806 1807 return STATUS_SUCCESS; 1808 } 1809 1810 /* API_NUMBER: ConsolepSetTitle */ 1811 CON_API(SrvSetConsoleTitle, 1812 CONSOLE_GETSETCONSOLETITLE, TitleRequest) 1813 { 1814 PWCHAR Buffer; 1815 ULONG Length; 1816 1817 if (!CsrValidateMessageBuffer(ApiMessage, 1818 (PVOID)&TitleRequest->Title, 1819 TitleRequest->Length, 1820 sizeof(BYTE))) 1821 { 1822 return STATUS_INVALID_PARAMETER; 1823 } 1824 1825 if (TitleRequest->Unicode) 1826 { 1827 /* Length is in bytes */ 1828 Length = TitleRequest->Length; 1829 } 1830 else 1831 { 1832 /* Use the console input CP for the conversion */ 1833 Length = MultiByteToWideChar(Console->InputCodePage, 0, 1834 TitleRequest->Title, TitleRequest->Length, 1835 NULL, 0); 1836 /* The returned Length was in number of wchars, convert it in bytes */ 1837 Length *= sizeof(WCHAR); 1838 } 1839 1840 /* Allocate a new buffer to hold the new title (NULL-terminated) */ 1841 Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Length + sizeof(WCHAR)); 1842 if (!Buffer) 1843 return STATUS_NO_MEMORY; 1844 1845 /* Free the old title */ 1846 ConsoleFreeUnicodeString(&Console->Title); 1847 1848 /* Copy title to console */ 1849 Console->Title.Buffer = Buffer; 1850 Console->Title.Length = Length; 1851 Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR); 1852 1853 if (TitleRequest->Unicode) 1854 { 1855 RtlCopyMemory(Console->Title.Buffer, TitleRequest->Title, Console->Title.Length); 1856 } 1857 else 1858 { 1859 MultiByteToWideChar(Console->InputCodePage, 0, 1860 TitleRequest->Title, TitleRequest->Length, 1861 Console->Title.Buffer, 1862 Console->Title.Length / sizeof(WCHAR)); 1863 } 1864 1865 /* NULL-terminate */ 1866 Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = UNICODE_NULL; 1867 1868 TermChangeTitle(Console); 1869 1870 return STATUS_SUCCESS; 1871 } 1872 1873 NTSTATUS NTAPI 1874 ConDrvGetConsoleCP(IN PCONSOLE Console, 1875 OUT PUINT CodePage, 1876 IN BOOLEAN OutputCP); 1877 /* API_NUMBER: ConsolepGetCP */ 1878 CON_API(SrvGetConsoleCP, 1879 CONSOLE_GETINPUTOUTPUTCP, GetConsoleCPRequest) 1880 { 1881 DPRINT("SrvGetConsoleCP, getting %s Code Page\n", 1882 GetConsoleCPRequest->OutputCP ? "Output" : "Input"); 1883 1884 return ConDrvGetConsoleCP((PCONSOLE)Console, 1885 &GetConsoleCPRequest->CodePage, 1886 GetConsoleCPRequest->OutputCP); 1887 } 1888 1889 NTSTATUS NTAPI 1890 ConDrvSetConsoleCP(IN PCONSOLE Console, 1891 IN UINT CodePage, 1892 IN BOOLEAN OutputCP); 1893 /* API_NUMBER: ConsolepSetCP */ 1894 CON_API(SrvSetConsoleCP, 1895 CONSOLE_SETINPUTOUTPUTCP, SetConsoleCPRequest) 1896 { 1897 DPRINT("SrvSetConsoleCP, setting %s Code Page\n", 1898 SetConsoleCPRequest->OutputCP ? "Output" : "Input"); 1899 1900 return ConDrvSetConsoleCP((PCONSOLE)Console, 1901 SetConsoleCPRequest->CodePage, 1902 SetConsoleCPRequest->OutputCP); 1903 } 1904 1905 /* API_NUMBER: ConsolepGetProcessList */ 1906 CON_API(SrvGetConsoleProcessList, 1907 CONSOLE_GETPROCESSLIST, GetProcessListRequest) 1908 { 1909 if (!CsrValidateMessageBuffer(ApiMessage, 1910 (PVOID)&GetProcessListRequest->ProcessIdsList, 1911 GetProcessListRequest->ProcessCount, 1912 sizeof(DWORD))) 1913 { 1914 return STATUS_INVALID_PARAMETER; 1915 } 1916 1917 return ConSrvGetConsoleProcessList(Console, 1918 GetProcessListRequest->ProcessIdsList, 1919 GetProcessListRequest->ProcessCount, 1920 &GetProcessListRequest->ProcessCount); 1921 } 1922 1923 /* API_NUMBER: ConsolepGenerateCtrlEvent */ 1924 CON_API(SrvGenerateConsoleCtrlEvent, 1925 CONSOLE_GENERATECTRLEVENT, GenerateCtrlEventRequest) 1926 { 1927 return ConSrvConsoleProcessCtrlEvent(Console, 1928 GenerateCtrlEventRequest->ProcessGroupId, 1929 GenerateCtrlEventRequest->CtrlEvent); 1930 } 1931 1932 /* API_NUMBER: ConsolepNotifyLastClose */ 1933 CON_API(SrvConsoleNotifyLastClose, 1934 CONSOLE_NOTIFYLASTCLOSE, NotifyLastCloseRequest) 1935 { 1936 /* Only one process is allowed to be registered for last close notification */ 1937 if (Console->NotifyLastClose) 1938 return STATUS_ACCESS_DENIED; 1939 1940 Console->NotifyLastClose = TRUE; 1941 Console->NotifiedLastCloseProcess = ProcessData; 1942 return STATUS_SUCCESS; 1943 } 1944 1945 /* API_NUMBER: ConsolepGetMouseInfo */ 1946 CON_API(SrvGetConsoleMouseInfo, 1947 CONSOLE_GETMOUSEINFO, GetMouseInfoRequest) 1948 { 1949 /* Just retrieve the number of buttons of the mouse attached to this console */ 1950 GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS); 1951 return STATUS_SUCCESS; 1952 } 1953 1954 /* API_NUMBER: ConsolepSetKeyShortcuts */ 1955 CSR_API(SrvSetConsoleKeyShortcuts) 1956 { 1957 DPRINT1("%s not yet implemented\n", __FUNCTION__); 1958 return STATUS_NOT_IMPLEMENTED; 1959 } 1960 1961 /* API_NUMBER: ConsolepGetKeyboardLayoutName */ 1962 CON_API(SrvGetConsoleKeyboardLayoutName, 1963 CONSOLE_GETKBDLAYOUTNAME, GetKbdLayoutNameRequest) 1964 { 1965 /* Retrieve the keyboard layout name of the system */ 1966 if (GetKbdLayoutNameRequest->Ansi) 1967 GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer); 1968 else 1969 GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer); 1970 1971 return STATUS_SUCCESS; 1972 } 1973 1974 /* API_NUMBER: ConsolepCharType */ 1975 CSR_API(SrvGetConsoleCharType) 1976 { 1977 DPRINT1("%s not yet implemented\n", __FUNCTION__); 1978 return STATUS_NOT_IMPLEMENTED; 1979 } 1980 1981 /* API_NUMBER: ConsolepSetLocalEUDC */ 1982 CSR_API(SrvSetConsoleLocalEUDC) 1983 { 1984 DPRINT1("%s not yet implemented\n", __FUNCTION__); 1985 return STATUS_NOT_IMPLEMENTED; 1986 } 1987 1988 /* API_NUMBER: ConsolepSetCursorMode */ 1989 CSR_API(SrvSetConsoleCursorMode) 1990 { 1991 DPRINT1("%s not yet implemented\n", __FUNCTION__); 1992 return STATUS_NOT_IMPLEMENTED; 1993 } 1994 1995 /* API_NUMBER: ConsolepGetCursorMode */ 1996 CSR_API(SrvGetConsoleCursorMode) 1997 { 1998 DPRINT1("%s not yet implemented\n", __FUNCTION__); 1999 return STATUS_NOT_IMPLEMENTED; 2000 } 2001 2002 /* API_NUMBER: ConsolepGetNlsMode */ 2003 CSR_API(SrvGetConsoleNlsMode) 2004 { 2005 DPRINT1("%s not yet implemented\n", __FUNCTION__); 2006 return STATUS_NOT_IMPLEMENTED; 2007 } 2008 2009 /* API_NUMBER: ConsolepSetNlsMode */ 2010 CSR_API(SrvSetConsoleNlsMode) 2011 { 2012 DPRINT1("%s not yet implemented\n", __FUNCTION__); 2013 return STATUS_NOT_IMPLEMENTED; 2014 } 2015 2016 /* API_NUMBER: ConsolepGetLangId */ 2017 CON_API(SrvGetConsoleLangId, 2018 CONSOLE_GETLANGID, LangIdRequest) 2019 { 2020 /* 2021 * Quoting MS Terminal, see function GetConsoleLangId() at 2022 * https://github.com/microsoft/terminal/blob/main/src/host/srvinit.cpp#L655 2023 * "Only attempt to return the Lang ID if the Windows ACP on console 2024 * launch was an East Asian Code Page." 2025 * 2026 * The underlying logic is as follows: 2027 * 2028 * - When the current user's UI language is *not* CJK, the user expects 2029 * to not see any CJK output to the console by default, even if its 2030 * output has been set to a CJK code page (this is possible when CJK 2031 * fonts are installed on the system). That is, of course, unless if 2032 * the attached console program chooses to actually output CJK text. 2033 * Whatever current language of the running program's thread should 2034 * be kept: STATUS_NOT_SUPPORTED is returned. 2035 * 2036 * - When the current user's UI language *is* CJK, the user expects to 2037 * see CJK output to the console by default when its code page is CJK. 2038 * A valid LangId is returned in this case to ensure this. 2039 * However, if the console code page is not CJK, then it is evident 2040 * that CJK text will not be able to be correctly shown, and therefore 2041 * we should fall back to a standard language that can be shown, namely 2042 * en-US english, instead of keeping the current language. 2043 */ 2044 2045 BYTE UserCharSet = CodePageToCharSet(GetACP()); 2046 if (!IsCJKCharSet(UserCharSet)) 2047 return STATUS_NOT_SUPPORTED; 2048 2049 /* Return a "best-suited" language ID corresponding 2050 * to the active console output code page. */ 2051 switch (Console->OutputCodePage) 2052 { 2053 /** ReactOS-specific: do nothing if the code page is UTF-8. This will allow 2054 ** programs to naturally output in whatever current language they are. **/ 2055 case CP_UTF8: 2056 return STATUS_NOT_SUPPORTED; 2057 /** End ReactOS-specific **/ 2058 case CP_JAPANESE: 2059 LangIdRequest->LangId = MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT); 2060 break; 2061 case CP_KOREAN: 2062 LangIdRequest->LangId = MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN); 2063 break; 2064 case CP_CHINESE_SIMPLIFIED: 2065 LangIdRequest->LangId = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED); 2066 break; 2067 case CP_CHINESE_TRADITIONAL: 2068 LangIdRequest->LangId = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL); 2069 break; 2070 default: 2071 /* Default to en-US english otherwise */ 2072 LangIdRequest->LangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); 2073 break; 2074 } 2075 2076 return STATUS_SUCCESS; 2077 } 2078 2079 /* EOF */ 2080