Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2884 lines
88 KiB

/*++
Copyright (c) 1985 - 1999, Microsoft Corporation
Module Name:
srvinit.c
Abstract:
This is the main initialization file for the console
Server.
Author:
Therese Stowell (thereses) 11-Nov-1990
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
CONST PCSR_API_ROUTINE ConsoleServerApiDispatchTable[ConsolepMaxApiNumber - ConsolepOpenConsole] = {
SrvOpenConsole,
SrvGetConsoleInput,
SrvWriteConsoleInput,
SrvReadConsoleOutput,
SrvWriteConsoleOutput,
SrvReadConsoleOutputString,
SrvWriteConsoleOutputString,
SrvFillConsoleOutput,
SrvGetConsoleMode,
SrvGetConsoleNumberOfFonts,
SrvGetConsoleNumberOfInputEvents,
SrvGetConsoleScreenBufferInfo,
SrvGetConsoleCursorInfo,
SrvGetConsoleMouseInfo,
SrvGetConsoleFontInfo,
SrvGetConsoleFontSize,
SrvGetConsoleCurrentFont,
SrvSetConsoleMode,
SrvSetConsoleActiveScreenBuffer,
SrvFlushConsoleInputBuffer,
SrvGetLargestConsoleWindowSize,
SrvSetConsoleScreenBufferSize,
SrvSetConsoleCursorPosition,
SrvSetConsoleCursorInfo,
SrvSetConsoleWindowInfo,
SrvScrollConsoleScreenBuffer,
SrvSetConsoleTextAttribute,
SrvSetConsoleFont,
SrvSetConsoleIcon,
SrvReadConsole,
SrvWriteConsole,
SrvDuplicateHandle,
SrvGetHandleInformation,
SrvSetHandleInformation,
SrvCloseHandle,
SrvVerifyConsoleIoHandle,
SrvAllocConsole,
SrvFreeConsole,
SrvGetConsoleTitle,
SrvSetConsoleTitle,
SrvCreateConsoleScreenBuffer,
SrvInvalidateBitMapRect,
SrvVDMConsoleOperation,
SrvSetConsoleCursor,
SrvShowConsoleCursor,
SrvConsoleMenuControl,
SrvSetConsolePalette,
SrvSetConsoleDisplayMode,
SrvRegisterConsoleVDM,
SrvGetConsoleHardwareState,
SrvSetConsoleHardwareState,
SrvGetConsoleDisplayMode,
SrvAddConsoleAlias,
SrvGetConsoleAlias,
SrvGetConsoleAliasesLength,
SrvGetConsoleAliasExesLength,
SrvGetConsoleAliases,
SrvGetConsoleAliasExes,
SrvExpungeConsoleCommandHistory,
SrvSetConsoleNumberOfCommands,
SrvGetConsoleCommandHistoryLength,
SrvGetConsoleCommandHistory,
SrvSetConsoleCommandHistoryMode,
SrvGetConsoleCP,
SrvSetConsoleCP,
SrvSetConsoleKeyShortcuts,
SrvSetConsoleMenuClose,
SrvConsoleNotifyLastClose,
SrvGenerateConsoleCtrlEvent,
SrvGetConsoleKeyboardLayoutName,
SrvGetConsoleWindow,
#if defined(FE_SB)
SrvGetConsoleCharType,
SrvSetConsoleLocalEUDC,
SrvSetConsoleCursorMode,
SrvGetConsoleCursorMode,
SrvRegisterConsoleOS2,
SrvSetConsoleOS2OemFormat,
#if defined(FE_IME)
SrvGetConsoleNlsMode,
SrvSetConsoleNlsMode,
SrvRegisterConsoleIME,
SrvUnregisterConsoleIME,
#endif // FE_IME
#endif // FE_SB
SrvGetConsoleLangId,
SrvAttachConsole,
SrvGetConsoleSelectionInfo,
SrvGetConsoleProcessList,
};
CONST BOOLEAN ConsoleServerApiServerValidTable[ConsolepMaxApiNumber - ConsolepOpenConsole] = {
FALSE, // OpenConsole
FALSE, // GetConsoleInput,
FALSE, // WriteConsoleInput,
FALSE, // ReadConsoleOutput,
FALSE, // WriteConsoleOutput,
FALSE, // ReadConsoleOutputString,
FALSE, // WriteConsoleOutputString,
FALSE, // FillConsoleOutput,
FALSE, // GetConsoleMode,
FALSE, // GetNumberOfConsoleFonts,
FALSE, // GetNumberOfConsoleInputEvents,
FALSE, // GetConsoleScreenBufferInfo,
FALSE, // GetConsoleCursorInfo,
FALSE, // GetConsoleMouseInfo,
FALSE, // GetConsoleFontInfo,
FALSE, // GetConsoleFontSize,
FALSE, // GetCurrentConsoleFont,
FALSE, // SetConsoleMode,
FALSE, // SetConsoleActiveScreenBuffer,
FALSE, // FlushConsoleInputBuffer,
FALSE, // GetLargestConsoleWindowSize,
FALSE, // SetConsoleScreenBufferSize,
FALSE, // SetConsoleCursorPosition,
FALSE, // SetConsoleCursorInfo,
FALSE, // SetConsoleWindowInfo,
FALSE, // ScrollConsoleScreenBuffer,
FALSE, // SetConsoleTextAttribute,
FALSE, // SetConsoleFont,
FALSE, // SetConsoleIcon
FALSE, // ReadConsole,
FALSE, // WriteConsole,
FALSE, // DuplicateHandle,
FALSE, // GetHandleInformation,
FALSE, // SetHandleInformation,
FALSE, // CloseHandle
FALSE, // VerifyConsoleIoHandle
FALSE, // AllocConsole,
FALSE, // FreeConsole
FALSE, // GetConsoleTitle,
FALSE, // SetConsoleTitle,
FALSE, // CreateConsoleScreenBuffer
FALSE, // InvalidateConsoleBitmapRect
FALSE, // VDMConsoleOperation
FALSE, // SetConsoleCursor,
FALSE, // ShowConsoleCursor
FALSE, // ConsoleMenuControl
FALSE, // SetConsolePalette
FALSE, // SetConsoleDisplayMode
FALSE, // RegisterConsoleVDM,
FALSE, // GetConsoleHardwareState
FALSE, // SetConsoleHardwareState
TRUE, // GetConsoleDisplayMode
FALSE, // AddConsoleAlias,
FALSE, // GetConsoleAlias,
FALSE, // GetConsoleAliasesLength,
FALSE, // GetConsoleAliasExesLength,
FALSE, // GetConsoleAliases,
FALSE, // GetConsoleAliasExes
FALSE, // ExpungeConsoleCommandHistory,
FALSE, // SetConsoleNumberOfCommands,
FALSE, // GetConsoleCommandHistoryLength,
FALSE, // GetConsoleCommandHistory,
FALSE, // SetConsoleCommandHistoryMode
FALSE, // SrvGetConsoleCP,
FALSE, // SrvSetConsoleCP,
FALSE, // SrvSetConsoleKeyShortcuts,
FALSE, // SrvSetConsoleMenuClose
FALSE, // SrvConsoleNotifyLastClose
FALSE, // SrvGenerateConsoleCtrlEvent
FALSE, // SrvGetConsoleKeyboardLayoutName
FALSE, // SrvGetConsoleWindow,
#if defined(FE_SB)
FALSE, // GetConsoleCharType
FALSE, // SrvSetConsoleLocalEUDC,
FALSE, // SrvSetConsoleCursorMode,
FALSE, // SrvGetConsoleCursorMode
FALSE, // SrvRegisterConsoleOS2,
FALSE, // SrvSetConsoleOS2OemFormat,
#if defined(FE_IME)
FALSE, // GetConsoleNlsMode
FALSE, // SetConsoleNlsMode
FALSE, // RegisterConsoleIME
FALSE, // UnregisterConsoleIME
#endif // FE_IME
#endif // FE_SB
FALSE, // GetConsoleLangId
FALSE, // AttachConsole
FALSE, // GetConsoleSelectionInfo,
FALSE, // GetConsoleProcessList
};
#if DBG
CONST PSZ ConsoleServerApiNameTable[ConsolepMaxApiNumber - ConsolepOpenConsole] = {
"SrvOpenConsole",
"SrvGetConsoleInput",
"SrvWriteConsoleInput",
"SrvReadConsoleOutput",
"SrvWriteConsoleOutput",
"SrvReadConsoleOutputString",
"SrvWriteConsoleOutputString",
"SrvFillConsoleOutput",
"SrvGetConsoleMode",
"SrvGetConsoleNumberOfFonts",
"SrvGetConsoleNumberOfInputEvents",
"SrvGetConsoleScreenBufferInfo",
"SrvGetConsoleCursorInfo",
"SrvGetConsoleMouseInfo",
"SrvGetConsoleFontInfo",
"SrvGetConsoleFontSize",
"SrvGetConsoleCurrentFont",
"SrvSetConsoleMode",
"SrvSetConsoleActiveScreenBuffer",
"SrvFlushConsoleInputBuffer",
"SrvGetLargestConsoleWindowSize",
"SrvSetConsoleScreenBufferSize",
"SrvSetConsoleCursorPosition",
"SrvSetConsoleCursorInfo",
"SrvSetConsoleWindowInfo",
"SrvScrollConsoleScreenBuffer",
"SrvSetConsoleTextAttribute",
"SrvSetConsoleFont",
"SrvSetConsoleIcon",
"SrvReadConsole",
"SrvWriteConsole",
"SrvDuplicateHandle",
"SrvGetHandleInformation",
"SrvSetHandleInformation",
"SrvCloseHandle",
"SrvVerifyConsoleIoHandle",
"SrvAllocConsole",
"SrvFreeConsole",
"SrvGetConsoleTitle",
"SrvSetConsoleTitle",
"SrvCreateConsoleScreenBuffer",
"SrvInvalidateBitMapRect",
"SrvVDMConsoleOperation",
"SrvSetConsoleCursor",
"SrvShowConsoleCursor",
"SrvConsoleMenuControl",
"SrvSetConsolePalette",
"SrvSetConsoleDisplayMode",
"SrvRegisterConsoleVDM",
"SrvGetConsoleHardwareState",
"SrvSetConsoleHardwareState",
"SrvGetConsoleDisplayMode",
"SrvAddConsoleAlias",
"SrvGetConsoleAlias",
"SrvGetConsoleAliasesLength",
"SrvGetConsoleAliasExesLength",
"SrvGetConsoleAliases",
"SrvGetConsoleAliasExes",
"SrvExpungeConsoleCommandHistory",
"SrvSetConsoleNumberOfCommands",
"SrvGetConsoleCommandHistoryLength",
"SrvGetConsoleCommandHistory",
"SrvSetConsoleCommandHistoryMode",
"SrvGetConsoleCP",
"SrvSetConsoleCP",
"SrvSetConsoleKeyShortcuts",
"SrvSetConsoleMenuClose",
"SrvConsoleNotifyLastClose",
"SrvGenerateConsoleCtrlEvent",
"SrvGetConsoleKeyboardLayoutName",
"SrvGetConsoleWindow",
#if defined(FE_SB)
"SrvGetConsoleCharType",
"SrvSetConsoleLocalEUDC",
"SrvSetConsoleCursorMode",
"SrvGetConsoleCursorMode",
"SrvRegisterConsoleOS2",
"SrvSetConsoleOS2OemFormat",
#if defined(FE_IME)
"SrvGetConsoleNlsMode",
"SrvSetConsoleNlsMode",
"SrvRegisterConsoleIME",
"SrvUnregisterConsoleIME",
#endif // FE_IME
#endif // FE_SB
"SrvGetConsoleLangId",
"SrvAttachConsole",
"SrvGetConsoleSelectionInfo",
"SrvGetConsoleProcessList",
};
#endif // DBG
BOOL FullScreenInitialized;
CRITICAL_SECTION ConsoleVDMCriticalSection;
PCONSOLE_INFORMATION ConsoleVDMOnSwitching;
CRITICAL_SECTION ConsoleInitWindowsLock;
BOOL fOneTimeInitialized;
UINT OEMCP;
UINT WINDOWSCP;
UINT ConsoleOutputCP;
CONSOLE_REGISTRY_INFO DefaultRegInfo;
#if defined(FE_SB)
BOOLEAN gfIsDBCSACP;
#endif
VOID
UnregisterVDM(
IN PCONSOLE_INFORMATION Console
);
ULONG
NonConsoleProcessShutdown(
PCSR_PROCESS Process,
DWORD dwFlags
);
ULONG
ConsoleClientShutdown(
PCSR_PROCESS Process,
ULONG Flags,
BOOLEAN fFirstPass
);
NTSTATUS
ConsoleClientConnectRoutine(
IN PCSR_PROCESS Process,
IN OUT PVOID ConnectionInfo,
IN OUT PULONG ConnectionInfoLength
);
VOID
ConsoleClientDisconnectRoutine(
IN PCSR_PROCESS Process
);
VOID ConsolePlaySound(
VOID
);
HANDLE ghInstance;
HICON ghDefaultIcon;
HICON ghDefaultSmIcon;
HCURSOR ghNormalCursor;
PWIN32HEAP pConHeap;
DWORD dwConBaseTag;
DWORD gExtendedEditKey;
BOOL gfTrimLeadingZeros;
BOOL gfEnableColorSelection;
BOOL gfLoadConIme;
VOID LoadLinkInfo(
PCONSOLE_INFO ConsoleInfo,
LPWSTR Title,
LPDWORD TitleLength,
LPWSTR CurDir,
LPWSTR AppName
)
{
DWORD dwLinkLen;
WCHAR LinkName[MAX_PATH + 1];
LNKPROPNTCONSOLE linkprops;
LPWSTR pszIconLocation;
int nIconIndex;
ConsoleInfo->uCodePage = OEMCP;
// Do some initialization
ConsoleInfo->hIcon = ghDefaultIcon;
ConsoleInfo->hSmIcon = ghDefaultSmIcon;
pszIconLocation = NULL;
nIconIndex = 0;
// Try to impersonate the client-side thread
if (!CsrImpersonateClient(NULL)) {
ConsoleInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
goto DefaultInit;
}
// Did we get started from a link?
if (ConsoleInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) {
DWORD Success;
DWORD oldLen;
// Get the filename of the link (TitleLength is BYTES, not CHARS)
dwLinkLen = (DWORD)(min(*TitleLength,(MAX_PATH+1)*sizeof(WCHAR)));
RtlCopyMemory(LinkName, Title, dwLinkLen);
LinkName[ MAX_PATH ] = (WCHAR)0;
// Get the title for the window, which is effectively the link file name
oldLen = *TitleLength;
*TitleLength = GetTitleFromLinkName( LinkName, Title );
if (*TitleLength < oldLen)
Title[ *TitleLength / sizeof(WCHAR) ] = L'\0';
// try to get console properties from the link
Success = GetLinkProperties( LinkName,
&linkprops,
sizeof(linkprops)
);
if (Success == LINK_NOINFO) {
ConsoleInfo->dwStartupFlags &= (~STARTF_TITLEISLINKNAME);
goto NormalInit;
}
if (linkprops.pszIconLocation && *linkprops.pszIconLocation) {
pszIconLocation = linkprops.pszIconLocation;
nIconIndex = linkprops.uIcon;
ConsoleInfo->iIconId = 0;
}
// Transfer link settings
ConsoleInfo->dwHotKey = linkprops.uHotKey;
ConsoleInfo->wShowWindow = (WORD)linkprops.uShowCmd;
if (Success == LINK_SIMPLEINFO) {
ConsoleInfo->dwStartupFlags &= (~STARTF_TITLEISLINKNAME);
goto NormalInit;
}
// Transfer console link settings
ConsoleInfo->wFillAttribute = linkprops.console_props.wFillAttribute;
ConsoleInfo->wPopupFillAttribute = linkprops.console_props.wPopupFillAttribute;
RtlCopyMemory( &ConsoleInfo->dwScreenBufferSize,
&linkprops.console_props.dwScreenBufferSize,
sizeof(NT_CONSOLE_PROPS) - FIELD_OFFSET(NT_CONSOLE_PROPS, dwScreenBufferSize)
);
ConsoleInfo->uCodePage = linkprops.fe_console_props.uCodePage;
ConsoleInfo->dwStartupFlags &= ~(STARTF_USESIZE | STARTF_USECOUNTCHARS);
}
NormalInit:
//
// Go get the icon
//
if (pszIconLocation == NULL) {
dwLinkLen = RtlDosSearchPath_U(CurDir,
AppName,
NULL,
sizeof(LinkName),
LinkName,
NULL);
if (dwLinkLen > 0 && dwLinkLen < sizeof(LinkName)) {
pszIconLocation = LinkName;
} else {
pszIconLocation = AppName;
}
}
if (pszIconLocation != NULL) {
HICON hIcon, hSmIcon;
hIcon = hSmIcon = NULL;
PrivateExtractIconExW(pszIconLocation,
nIconIndex,
&hIcon,
&hSmIcon,
1);
/*
* If there is no large icon, use the default ones.
* If there is only a large icon in the resource, do not use
* the default small one but let it be NULL so we'll stretch
* the large one.
*/
if (hIcon != NULL) {
ConsoleInfo->hIcon = hIcon;
ConsoleInfo->hSmIcon = hSmIcon;
}
}
CsrRevertToSelf();
if (!IsValidCodePage(ConsoleInfo->uCodePage)) { // fail safe
ConsoleInfo->uCodePage = OEMCP;
}
if (!(ConsoleInfo->dwStartupFlags & STARTF_TITLEISLINKNAME)) {
CONSOLE_REGISTRY_INFO RegInfo;
DefaultInit:
//
// read values from the registry
//
RegInfo = DefaultRegInfo;
GetRegistryValues(Title, &RegInfo);
//
// If a value isn't specified in STARTUPINFO, then use the one
// from the registry.
//
if (!(ConsoleInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)) {
ConsoleInfo->wFillAttribute = RegInfo.ScreenFill.Attributes;
}
ConsoleInfo->wPopupFillAttribute = RegInfo.PopupFill.Attributes;
if (!(ConsoleInfo->dwStartupFlags & STARTF_USECOUNTCHARS)) {
ConsoleInfo->dwScreenBufferSize = RegInfo.ScreenBufferSize;
}
if (!(ConsoleInfo->dwStartupFlags & STARTF_USESIZE)) {
ConsoleInfo->dwWindowSize = RegInfo.WindowSize;
}
if (!(ConsoleInfo->dwStartupFlags & STARTF_USEPOSITION)) {
ConsoleInfo->dwWindowOrigin = RegInfo.WindowOrigin;
ConsoleInfo->bAutoPosition = RegInfo.AutoPosition;
} else {
ConsoleInfo->bAutoPosition = FALSE;
}
if (!(ConsoleInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)) {
ConsoleInfo->bFullScreen = RegInfo.FullScreen;
} else {
ConsoleInfo->bFullScreen = TRUE;
}
ConsoleInfo->uFontFamily = RegInfo.FontFamily;
ConsoleInfo->uFontWeight = RegInfo.FontWeight;
ConsoleInfo->dwFontSize = RegInfo.FontSize;
RtlCopyMemory(ConsoleInfo->FaceName, RegInfo.FaceName, sizeof(RegInfo.FaceName));
ConsoleInfo->bQuickEdit = RegInfo.QuickEdit;
ConsoleInfo->bInsertMode = RegInfo.InsertMode;
ConsoleInfo->uCursorSize = RegInfo.CursorSize;
ConsoleInfo->uHistoryBufferSize = RegInfo.HistoryBufferSize;
ConsoleInfo->uNumberOfHistoryBuffers = RegInfo.NumberOfHistoryBuffers;
ConsoleInfo->bHistoryNoDup = RegInfo.HistoryNoDup;
RtlCopyMemory(ConsoleInfo->ColorTable, RegInfo.ColorTable, sizeof(RegInfo.ColorTable));
#ifdef FE_SB
ConsoleInfo->uCodePage = RegInfo.CodePage;
#endif
}
}
BOOL
InitWindowClass( VOID )
{
WNDCLASSEX wc;
BOOL retval;
ATOM atomConsoleClass;
ghNormalCursor = LoadCursor(NULL, IDC_ARROW);
ASSERT(ghModuleWin != NULL);
ghDefaultIcon = LoadIcon(ghModuleWin, MAKEINTRESOURCE(IDI_CONSOLE));
ghDefaultSmIcon = LoadImage(ghModuleWin, MAKEINTRESOURCE(IDI_CONSOLE), IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
LR_SHARED);
wc.hIcon = ghDefaultIcon;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
wc.lpfnWndProc = ConsoleWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = GWL_CONSOLE_WNDALLOC;
wc.hInstance = ghInstance;
wc.hCursor = ghNormalCursor;
wc.hbrBackground = CreateSolidBrush(DefaultRegInfo.ColorTable[LOBYTE(DefaultRegInfo.ScreenFill.Attributes >> 4) & 0xF]);
wc.lpszMenuName = NULL;
wc.lpszClassName = CONSOLE_WINDOW_CLASS;
wc.hIconSm = ghDefaultSmIcon;
atomConsoleClass = RegisterClassEx(&wc);
retval = (atomConsoleClass != 0);
if (retval) {
NtUserConsoleControl(ConsoleClassAtom, &atomConsoleClass, sizeof(ATOM));
}
return retval;
}
NTSTATUS
InitWindowsStuff(
HDESK hdesk,
LPDWORD lpdwThreadId)
{
NTSTATUS Status = STATUS_SUCCESS;
CLIENT_ID ClientId;
CONSOLEDESKTOPCONSOLETHREAD ConsoleDesktopInfo;
INPUT_THREAD_INIT_INFO InputThreadInitInfo;
//
// This routine must be done within a critical section to ensure that
// only one thread can initialize at a time. We need a special critical
// section here because Csr calls into ConsoleAddProcessRoutine with
// it's own critical section locked and then tries to grab the
// ConsoleHandleTableLock. If we call CsrAddStaticServerThread here
// with the ConsoleHandleTableLock locked we could get into a deadlock
// situation. This critical section should not be used anywhere else.
//
RtlEnterCriticalSection(&ConsoleInitWindowsLock);
ConsoleDesktopInfo.hdesk = hdesk;
ConsoleDesktopInfo.dwThreadId = (DWORD)-1;
NtUserConsoleControl(ConsoleDesktopConsoleThread, &ConsoleDesktopInfo,
sizeof(ConsoleDesktopInfo));
if (ConsoleDesktopInfo.dwThreadId == 0) {
if (!fOneTimeInitialized) {
#ifdef FE_SB
InitializeDbcsMisc();
#endif // FE_SB
FullScreenInitialized = InitializeFullScreen();
//
// read the registry values
//
GetRegistryValues(L"", &DefaultRegInfo);
//
// allocate buffer for scrolling
//
Status = InitializeScrollBuffer();
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING, "InitWindowsStuff: InitScrollBuffer failed %x", Status);
goto ErrorExit;
}
}
//
// create GetMessage thread
//
Status = NtCreateEvent(&InputThreadInitInfo.InitCompleteEventHandle,
EVENT_ALL_ACCESS,
NULL,
NotificationEvent,
FALSE);
if (!NT_SUCCESS(Status)) {
goto ErrorExit;
}
Status = NtDuplicateObject(NtCurrentProcess(), hdesk,
NtCurrentProcess(), &InputThreadInitInfo.DesktopHandle, 0,
0, DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(Status)) {
NtClose(InputThreadInitInfo.InitCompleteEventHandle);
goto ErrorExit;
}
//
// Create GetMessage thread.
//
Status = RtlCreateUserThread(NtCurrentProcess(),
(PSECURITY_DESCRIPTOR) NULL,
TRUE,
0,
0,
0x5000,
ConsoleInputThread,
&InputThreadInitInfo,
&InputThreadInitInfo.ThreadHandle,
&ClientId);
if (!NT_SUCCESS(Status)) {
NtClose(InputThreadInitInfo.InitCompleteEventHandle);
CloseDesktop(InputThreadInitInfo.DesktopHandle);
goto ErrorExit;
}
CsrAddStaticServerThread(InputThreadInitInfo.ThreadHandle, &ClientId, 0);
NtResumeThread(InputThreadInitInfo.ThreadHandle, NULL);
NtWaitForSingleObject(InputThreadInitInfo.InitCompleteEventHandle, FALSE, NULL);
NtClose(InputThreadInitInfo.InitCompleteEventHandle);
if (!NT_SUCCESS(InputThreadInitInfo.InitStatus)) {
Status = InputThreadInitInfo.InitStatus;
goto ErrorExit;
}
*lpdwThreadId = HandleToUlong(ClientId.UniqueThread);
fOneTimeInitialized=TRUE;
} else {
*lpdwThreadId = ConsoleDesktopInfo.dwThreadId;
}
ErrorExit:
RtlLeaveCriticalSection(&ConsoleInitWindowsLock);
return Status;
}
NTSTATUS
ConServerDllInitialization(
PCSR_SERVER_DLL LoadedServerDll)
/*++
Routine Description:
This routine is called to initialize the server dll. It initializes
the console handle table.
Arguments:
LoadedServerDll - Pointer to console server dll data
Return Value:
--*/
{
NTSTATUS Status;
LoadedServerDll->ApiNumberBase = CONSRV_FIRST_API_NUMBER;
LoadedServerDll->MaxApiNumber = ConsolepMaxApiNumber;
LoadedServerDll->ApiDispatchTable = (PCSR_API_ROUTINE *)ConsoleServerApiDispatchTable;
LoadedServerDll->ApiServerValidTable = (PBOOLEAN)ConsoleServerApiServerValidTable;
#if DBG
LoadedServerDll->ApiNameTable = ConsoleServerApiNameTable;
#endif
LoadedServerDll->PerProcessDataLength = sizeof(CONSOLE_PER_PROCESS_DATA);
LoadedServerDll->ConnectRoutine = ConsoleClientConnectRoutine;
LoadedServerDll->DisconnectRoutine = ConsoleClientDisconnectRoutine;
LoadedServerDll->AddProcessRoutine = ConsoleAddProcessRoutine;
LoadedServerDll->ShutdownProcessRoutine = ConsoleClientShutdown;
ghInstance = LoadedServerDll->ModuleHandle;
// initialize data structures
InitWin32HeapStubs();
pConHeap = Win32HeapCreate(
"CH_Head",
"CH_Tail",
HEAP_GROWABLE | HEAP_CLASS_5 |
#ifdef PRERELEASE
HEAP_TAIL_CHECKING_ENABLED,
#else
0,
#endif // PRERELEASE
NULL, // HeapBase
64 * 1024, // ReserveSize
4096, // CommitSize
NULL, // Lock to use for serialization
NULL); // GrowthThreshold
if (pConHeap == NULL) {
return STATUS_NO_MEMORY;
}
dwConBaseTag = Win32HeapCreateTag( pConHeap,
0,
L"CON!",
L"TMP\0"
L"BMP\0"
L"ALIAS\0"
L"HISTORY\0"
L"TITLE\0"
L"HANDLE\0"
L"CONSOLE\0"
L"ICON\0"
L"BUFFER\0"
L"WAIT\0"
L"FONT\0"
L"SCREEN\0"
#if defined(FE_SB)
L"TMP DBCS\0"
L"SCREEN DBCS\0"
L"EUDC\0"
L"CONVAREA\0"
L"IME\0"
#endif
);
Status = InitializeConsoleHandleTable();
if (!NT_SUCCESS(Status)) {
return Status;
}
Status = RtlInitializeCriticalSectionAndSpinCount(&ConsoleInitWindowsLock,
0x80000000);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Initialize Input thread local message queue
//
Status = RtlInitializeCriticalSectionAndSpinCount(&gInputThreadMsgLock,
0x80000000);
if (!NT_SUCCESS(Status)) {
return Status;
}
InitializeThreadMessages();
#ifdef i386
Status = RtlInitializeCriticalSectionAndSpinCount(&ConsoleVDMCriticalSection,
0x80000000);
if (!NT_SUCCESS(Status)) {
return Status;
}
ConsoleVDMOnSwitching = NULL;
#endif
OEMCP = GetOEMCP();
WINDOWSCP = GetACP();
#if !defined(FE_SB)
ConsoleOutputCP = OEMCP;
#endif
InitializeFonts();
InputThreadTlsIndex = TlsAlloc();
if (InputThreadTlsIndex == 0xFFFFFFFF) {
return STATUS_UNSUCCESSFUL;
}
#if defined(FE_SB)
gfIsDBCSACP = !!IsAvailableFarEastCodePage(WINDOWSCP);
#endif
return STATUS_SUCCESS;
}
BOOL
MapHandle(
IN HANDLE ClientProcessHandle,
IN HANDLE ServerHandle,
OUT PHANDLE ClientHandle
)
{
//
// map event handle into dll's handle space.
//
return DuplicateHandle(NtCurrentProcess(),
ServerHandle,
ClientProcessHandle,
ClientHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS
);
}
VOID
AddProcessToList(
IN OUT PCONSOLE_INFORMATION Console,
IN OUT PCONSOLE_PROCESS_HANDLE ProcessHandleRecord,
IN HANDLE ProcessHandle
)
{
ASSERT(!(Console->Flags & (CONSOLE_TERMINATING | CONSOLE_SHUTTING_DOWN)));
ProcessHandleRecord->ProcessHandle = ProcessHandle;
ProcessHandleRecord->TerminateCount = 0;
InsertHeadList(&Console->ProcessHandleList, &ProcessHandleRecord->ListLink);
SetProcessFocus(ProcessHandleRecord->Process, Console->Flags & CONSOLE_HAS_FOCUS);
}
PCONSOLE_PROCESS_HANDLE
FindProcessInList(
IN PCONSOLE_INFORMATION Console,
IN HANDLE ProcessHandle
)
{
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
PLIST_ENTRY ListHead, ListNext;
ListHead = &Console->ProcessHandleList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
ProcessHandleRecord = CONTAINING_RECORD( ListNext, CONSOLE_PROCESS_HANDLE, ListLink );
if (ProcessHandleRecord->ProcessHandle == ProcessHandle) {
return ProcessHandleRecord;
}
ListNext = ListNext->Flink;
}
return NULL;
}
VOID
RemoveProcessFromList(
IN OUT PCONSOLE_INFORMATION Console,
IN HANDLE ProcessHandle
)
{
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
PLIST_ENTRY ListHead, ListNext;
ListHead = &Console->ProcessHandleList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
ProcessHandleRecord = CONTAINING_RECORD( ListNext, CONSOLE_PROCESS_HANDLE, ListLink );
ListNext = ListNext->Flink;
if (ProcessHandleRecord->ProcessHandle == ProcessHandle) {
RemoveEntryList(&ProcessHandleRecord->ListLink);
ConsoleHeapFree(ProcessHandleRecord);
return;
}
}
RIPMSG1(RIP_ERROR, "RemoveProcessFromList: Process %#p not found", ProcessHandle);
}
NTSTATUS
SetUpConsole(
IN OUT PCONSOLE_INFO ConsoleInfo,
IN DWORD TitleLength,
IN LPWSTR Title,
IN LPWSTR CurDir,
IN LPWSTR AppName,
IN PCONSOLE_PER_PROCESS_DATA ProcessData,
IN BOOLEAN WindowVisible,
IN PUNICODE_STRING pstrDesktopName)
{
NTSTATUS Status;
PCONSOLE_INFORMATION Console;
DWORD ConsoleThreadId;
HWINSTA hwinsta;
HDESK hdesk;
USEROBJECTFLAGS UserObjectFlags;
DWORD Length;
//
// Connect to the windowstation and desktop.
//
if (!CsrImpersonateClient(NULL)) {
return STATUS_BAD_IMPERSONATION_LEVEL;
}
hdesk = NtUserResolveDesktop(CONSOLE_CLIENTPROCESSHANDLE(),
pstrDesktopName,
FALSE,
&hwinsta);
CsrRevertToSelf();
if (hdesk == NULL) {
return STATUS_UNSUCCESSFUL;
}
//
// Need to initialize windows stuff once real console app starts.
// This is because for the time being windows expects the first
// app to be a windows app.
//
Status = InitWindowsStuff(hdesk, &ConsoleThreadId);
if (!NT_SUCCESS(Status)) {
CloseDesktop(hdesk);
CloseWindowStation(hwinsta);
return Status;
}
//
// If the windowstation isn't visible, then neither is the window.
//
if (WindowVisible) {
if (GetUserObjectInformation(hwinsta,
UOI_FLAGS,
&UserObjectFlags,
sizeof(UserObjectFlags),
&Length)) {
if (!(UserObjectFlags.dwFlags & WSF_VISIBLE)) {
WindowVisible = FALSE;
}
}
}
//
// We need to see if we were spawned from a link. If we were, we
// need to call back into the shell to try to get all the console
// information from the link.
//
LoadLinkInfo( ConsoleInfo, Title, &TitleLength, CurDir, AppName );
LockConsoleHandleTable();
Status = AllocateConsoleHandle(&ConsoleInfo->ConsoleHandle);
if (!NT_SUCCESS(Status)) {
UnlockConsoleHandleTable();
CloseDesktop(hdesk);
CloseWindowStation(hwinsta);
return Status;
}
Status = AllocateConsole(ConsoleInfo->ConsoleHandle,
Title,
(USHORT)TitleLength,
CONSOLE_CLIENTPROCESSHANDLE(),
&ConsoleInfo->StdIn,
&ConsoleInfo->StdOut,
&ConsoleInfo->StdErr,
ProcessData,
ConsoleInfo,
WindowVisible,
ConsoleThreadId
);
if (!NT_SUCCESS(Status)) {
FreeConsoleHandle(ConsoleInfo->ConsoleHandle);
UnlockConsoleHandleTable();
CloseDesktop(hdesk);
CloseWindowStation(hwinsta);
return Status;
}
CONSOLE_SETCONSOLEHANDLE(ConsoleInfo->ConsoleHandle);
Status = DereferenceConsoleHandle(ConsoleInfo->ConsoleHandle,&Console);
ASSERT (NT_SUCCESS(Status));
//
// increment console reference count
//
RefConsole(Console);
//
// Save the windowstation and desktop handles so they
// can be used later
//
Console->hWinSta = hwinsta;
Console->hDesk = hdesk;
UnlockConsoleHandleTable();
#if defined(FE_IME)
if (CONSOLE_IS_IME_ENABLED())
{
if (WindowVisible)
{
InitConsoleIMEStuff(Console->hDesk, ConsoleThreadId, Console);
}
}
#endif
return Status;
}
NTSTATUS
ConsoleClientConnectRoutine(
IN PCSR_PROCESS Process,
IN OUT PVOID ConnectionInfo,
IN OUT PULONG ConnectionInfoLength)
/*++
Routine Description:
This routine is called when a new process is created. For processes
without parents, it creates the console. For processes with
parents, it duplicates the handle table.
Arguments:
Process - Pointer to process structure.
ConnectionInfo - Pointer to connection info.
ConnectionInfoLength - Connection info length.
Return Value:
--*/
{
NTSTATUS Status;
PCONSOLE_API_CONNECTINFO p = (PCONSOLE_API_CONNECTINFO)ConnectionInfo;
PCONSOLE_INFORMATION Console;
PCONSOLE_PER_PROCESS_DATA ProcessData;
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
CONSOLEWINDOWSTATIONPROCESS ConsoleWindowStationInfo;
UNICODE_STRING strDesktopName;
CONSOLE_PROCESS_INFO cpi;
if (p == NULL ||
*ConnectionInfoLength != sizeof( *p ) ||
p->AppNameLength > sizeof(p->AppName) ||
p->CurDirLength > sizeof(p->CurDir) ||
p->TitleLength > sizeof(p->Title)) {
RIPMSG0(RIP_ERROR, "CONSRV: bad connection info");
return STATUS_UNSUCCESSFUL;
}
//
// Make sure the strings are NULL terminated.
//
p->AppName[NELEM(p->AppName) - 1] = 0;
p->CurDir[NELEM(p->CurDir) - 1] = 0;
p->Title[NELEM(p->Title) - 1] = 0;
if (CtrlRoutine == NULL) {
CtrlRoutine = p->CtrlRoutine;
}
#if defined(FE_IME)
if (ConsoleIMERoutine == NULL) {
ConsoleIMERoutine = p->ConsoleIMERoutine;
}
#endif
ProcessData = CONSOLE_FROMPROCESSPERPROCESSDATA(Process);
Console = NULL;
//
// If this process is not a console app, stop right here - no
// initialization is needed. Just need to remember that this
// is not a console app so that we do no work during
// ConsoleClientDisconnectRoutine().
//
Status = STATUS_SUCCESS;
if ((CONSOLE_GETCONSOLEAPPFROMPROCESSDATA(ProcessData) = p->ConsoleApp)) {
//
// First call off to USER so it unblocks any app waiting on a call
// to WaitForInputIdle. This way apps calling WinExec() to exec console
// apps will return right away.
//
cpi.dwProcessID = HandleToUlong(CONSOLE_CLIENTPROCESSID());
cpi.dwFlags = (p->ConsoleInfo.ConsoleHandle != NULL) ? 0 : CPI_NEWPROCESSWINDOW;
NtUserConsoleControl(ConsoleNotifyConsoleApplication,
&cpi,
sizeof(CONSOLE_PROCESS_INFO));
//
// create console
//
if (p->ConsoleInfo.ConsoleHandle == NULL) {
ProcessHandleRecord = ConsoleHeapAlloc(HANDLE_TAG, sizeof(CONSOLE_PROCESS_HANDLE));
if (ProcessHandleRecord == NULL) {
Status = STATUS_NO_MEMORY;
goto ErrorExit;
}
//
// We are creating a new console, so derereference
// the parent's console, if any.
//
if (ProcessData->ConsoleHandle != NULL) {
RemoveConsole(ProcessData, Process->ProcessHandle, 0);
}
//
// Get the desktop name.
//
if (p->DesktopLength) {
strDesktopName.Buffer = ConsoleHeapAlloc(TMP_TAG,
p->DesktopLength);
if (strDesktopName.Buffer == NULL) {
Status = STATUS_NO_MEMORY;
goto ErrorExit;
}
Status = NtReadVirtualMemory(Process->ProcessHandle,
(PVOID)p->Desktop,
strDesktopName.Buffer,
p->DesktopLength,
NULL
);
if (!NT_SUCCESS(Status)) {
ConsoleHeapFree(strDesktopName.Buffer);
goto ErrorExit;
}
strDesktopName.MaximumLength = (USHORT)p->DesktopLength;
strDesktopName.Length = (USHORT)(p->DesktopLength - sizeof(WCHAR));
} else {
RtlInitUnicodeString(&strDesktopName, L"Default");
}
ProcessData->RootProcess = TRUE;
Status = SetUpConsole(&p->ConsoleInfo,
p->TitleLength,
p->Title,
p->CurDir,
p->AppName,
ProcessData,
p->WindowVisible,
&strDesktopName);
if (p->DesktopLength) {
ConsoleHeapFree(strDesktopName.Buffer);
}
if (!NT_SUCCESS(Status)) {
goto ErrorExit;
}
// Play the Open sound for console apps
ConsolePlaySound();
Status = RevalidateConsole(p->ConsoleInfo.ConsoleHandle, &Console);
ASSERT (NT_SUCCESS(Status));
} else {
ProcessHandleRecord = NULL;
ProcessData->RootProcess = FALSE;
Status = STATUS_SUCCESS;
if (!(NT_SUCCESS(RevalidateConsole(p->ConsoleInfo.ConsoleHandle, &Console))) ) {
Status = STATUS_PROCESS_IS_TERMINATING;
goto ErrorExit;
}
if (Console->Flags & CONSOLE_SHUTTING_DOWN) {
Status = STATUS_PROCESS_IS_TERMINATING;
goto ErrorExit;
}
Status = MapEventHandles(CONSOLE_CLIENTPROCESSHANDLE(),
Console,
&p->ConsoleInfo
);
if (!NT_SUCCESS(Status)) {
goto ErrorExit;
}
ProcessHandleRecord = FindProcessInList(Console, CONSOLE_CLIENTPROCESSHANDLE());
if (ProcessHandleRecord) {
ProcessHandleRecord->CtrlRoutine = p->CtrlRoutine;
ProcessHandleRecord->PropRoutine = p->PropRoutine;
ProcessHandleRecord = NULL;
}
}
if (NT_SUCCESS(Status)) {
//
// Associate the correct window station with client process
// so they can do Global atom calls.
//
if (DuplicateHandle( NtCurrentProcess(),
Console->hWinSta,
Process->ProcessHandle,
&ConsoleWindowStationInfo.hwinsta,
0,
FALSE,
DUPLICATE_SAME_ACCESS
)
) {
ConsoleWindowStationInfo.dwProcessId = HandleToUlong(CONSOLE_CLIENTPROCESSID());
NtUserConsoleControl(ConsoleWindowStationProcess,
&ConsoleWindowStationInfo,
sizeof(ConsoleWindowStationInfo));
}
if (ProcessHandleRecord) {
ProcessHandleRecord->Process = Process;
ProcessHandleRecord->CtrlRoutine = p->CtrlRoutine;
ProcessHandleRecord->PropRoutine = p->PropRoutine;
AddProcessToList(Console, ProcessHandleRecord, CONSOLE_CLIENTPROCESSHANDLE());
}
SetProcessForegroundRights(Process,
Console->Flags & CONSOLE_HAS_FOCUS);
AllocateCommandHistory(Console,
p->AppNameLength,
p->AppName,
CONSOLE_CLIENTPROCESSHANDLE());
} else {
ErrorExit:
CONSOLE_SETCONSOLEAPPFROMPROCESSDATA(ProcessData, FALSE);
if (ProcessHandleRecord)
ConsoleHeapFree(ProcessHandleRecord);
if (ProcessData->ConsoleHandle != NULL) {
RemoveConsole(ProcessData, Process->ProcessHandle, 0);
}
}
if (Console) {
ConsoleNotifyWinEvent(Console,
EVENT_CONSOLE_START_APPLICATION,
HandleToULong(Process->ClientId.UniqueProcess),
0);
UnlockConsole(Console);
}
} else if (ProcessData->ConsoleHandle != NULL) {
//
// This is a non-console app with a reference to a
// reference to a parent console. Dereference the
// console.
//
RemoveConsole(ProcessData, Process->ProcessHandle, 0);
}
return Status;
}
#if defined(FE_IME)
VOID FreeConsoleIMEStuff(
PCONSOLE_INFORMATION Console)
{
PCONVERSIONAREA_INFORMATION ConvAreaInfo;
PCONVERSIONAREA_INFORMATION ConvAreaInfoNext;
ConvAreaInfo = Console->ConsoleIme.ConvAreaRoot;
while(ConvAreaInfo) {
ConvAreaInfoNext = ConvAreaInfo->ConvAreaNext;
FreeConvAreaScreenBuffer(ConvAreaInfo->ScreenBuffer);
ConsoleHeapFree(ConvAreaInfo);
ConvAreaInfo = ConvAreaInfoNext;
}
if (Console->ConsoleIme.NumberOfConvAreaCompStr) {
ConsoleHeapFree(Console->ConsoleIme.ConvAreaCompStr);
}
if (Console->ConsoleIme.CompStrData) {
ConsoleHeapFree(Console->ConsoleIme.CompStrData);
}
}
#else
#define FreeConsoleIMEStuff(Console)
#endif
NTSTATUS
RemoveConsole(
IN PCONSOLE_PER_PROCESS_DATA ProcessData,
IN HANDLE ProcessHandle,
IN HANDLE ProcessId)
{
ULONG i;
PHANDLE_DATA HandleData;
NTSTATUS Status;
PCONSOLE_INFORMATION Console;
Status = RevalidateConsole(ProcessData->ConsoleHandle, &Console);
//
// If this process isn't using the console, error.
//
if (!NT_SUCCESS(Status)) {
ASSERT(FALSE);
return Status;
}
if (Console->Flags & CONSOLE_NOTIFY_LAST_CLOSE) {
if (Console->ProcessIdLastNotifyClose == ProcessId) {
//
// If this process is the one who wants last close notification,
// remove it.
//
Console->Flags &= ~CONSOLE_NOTIFY_LAST_CLOSE;
NtClose(Console->hProcessLastNotifyClose);
} else if (ProcessData->RootProcess) {
//
// Notify the ntvdm process to terminate if the console root
// process is going away.
//
HANDLE ConsoleHandle;
CONSOLE_PROCESS_TERMINATION_RECORD ProcessHandleList;
Console->Flags &= ~CONSOLE_NOTIFY_LAST_CLOSE;
ConsoleHandle = Console->ConsoleHandle;
ProcessHandleList.ProcessHandle = Console->hProcessLastNotifyClose;
ProcessHandleList.TerminateCount = 0;
ProcessHandleList.CtrlRoutine = CtrlRoutine;
UnlockConsole(Console);
CreateCtrlThread(&ProcessHandleList,
1,
NULL,
SYSTEM_ROOT_CONSOLE_EVENT,
TRUE);
NtClose(ProcessHandleList.ProcessHandle);
Status = RevalidateConsole(ConsoleHandle, &Console);
UserAssert(NT_SUCCESS(Status));
if (!NT_SUCCESS(Status)) {
return STATUS_SUCCESS;
}
}
}
if (Console->VDMProcessId == ProcessId &&
(Console->Flags & CONSOLE_VDM_REGISTERED)) {
Console->Flags &= ~CONSOLE_FULLSCREEN_NOPAINT;
UnregisterVDM(Console);
}
if (ProcessHandle != NULL) {
RemoveProcessFromList(Console, ProcessHandle);
FreeCommandHistory(Console, ProcessHandle);
}
UserAssert(Console->RefCount);
//
// close the process's handles.
//
for (i = 0; i < ProcessData->HandleTableSize; i++) {
if (ProcessData->HandleTablePtr[i].HandleType != CONSOLE_FREE_HANDLE) {
Status = DereferenceIoHandleNoCheck(ProcessData,
LongToHandle(i),
&HandleData);
UserAssert(NT_SUCCESS(Status));
if (HandleData->HandleType & CONSOLE_INPUT_HANDLE) {
Status = CloseInputHandle(ProcessData, Console, HandleData, LongToHandle(i));
} else {
Status = CloseOutputHandle(ProcessData, Console, HandleData, LongToHandle(i), FALSE);
}
}
}
FreeProcessData(ProcessData);
ProcessData->ConsoleHandle = NULL;
//
// Decrement the console reference count. Free the console if it goes to
// zero.
//
DerefConsole(Console);
if (Console->RefCount == 0) {
FreeConsoleIMEStuff(Console);
FreeCon(Console);
} else {
//
// The root process is going away, so we need to reparent it.
//
if (ProcessData->RootProcess) {
PLIST_ENTRY ListHead = Console->ProcessHandleList.Flink;
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
PCSR_THREAD Thread;
HANDLE hThread;
RIPMSG1(RIP_WARNING, "Reparenting console 0x%p", ProcessData);
ProcessHandleRecord = CONTAINING_RECORD(ListHead,
CONSOLE_PROCESS_HANDLE,
ListLink);
ListHead = ProcessHandleRecord->Process->ThreadList.Flink;
Thread = CONTAINING_RECORD(ListHead, CSR_THREAD, Link);
ProcessData = CONSOLE_FROMPROCESSPERPROCESSDATA(ProcessHandleRecord->Process);
UserAssert(ProcessData->RootProcess == FALSE);
ProcessData->RootProcess = TRUE;
Status = NtDuplicateObject(NtCurrentProcess(),
Thread->ThreadHandle,
NtCurrentProcess(),
&hThread,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
if (NT_SUCCESS(Status)) {
/*
* We can only close this handle if the dup call above
* succeeded. If it didn't, then we're going to zombie this
* process, but at least we can keep going.
*/
NtClose(Console->ClientThreadHandle);
Console->ClientThreadHandle = hThread;
} else {
RIPMSGF1(RIP_WARNING,
"Failed to dup thread handle: Status = 0x%x",
Status);
}
}
UnlockConsole(Console);
}
return STATUS_SUCCESS;
}
VOID
ConsoleClientDisconnectRoutine(
IN PCSR_PROCESS Process)
/*++
Routine Description:
This routine is called when a process is destroyed. It closes the
process's handles and frees the console if it's the last reference.
Arguments:
Process - Pointer to process structure.
Return Value:
--*/
{
PCONSOLE_PER_PROCESS_DATA ProcessData;
PCONSOLE_INFORMATION Console;
NTSTATUS Status;
ProcessData = CONSOLE_FROMPROCESSPERPROCESSDATA(Process);
//
// If this process is not a console app, stop right here - no
// disconnect processing is needed, because this app didn't create
// or connect to an existing console.
//
if (ProcessData->ConsoleHandle == NULL) {
#if defined(FE_IME)
if (ProcessData->hDesk) {
//
// If this process is a Console IME,
// should unregister console IME on this desktop.
//
RemoveConsoleIME(Process, HandleToUlong(Process->ClientId.UniqueThread));
}
#endif
return;
}
Status = RevalidateConsole(ProcessData->ConsoleHandle, &Console);
if (NT_SUCCESS(Status)) {
ConsoleNotifyWinEvent(Console,
EVENT_CONSOLE_END_APPLICATION,
HandleToULong(Process->ClientId.UniqueProcess),
0);
UnlockConsole(Console);
} else {
RIPMSG2(RIP_WARNING, "RevalidateConsole returned status 0x%x on console 0x%x", Status, ProcessData->ConsoleHandle);
}
RemoveConsole(ProcessData,
CONSOLE_FROMPROCESSPROCESSHANDLE(Process),
Process->ClientId.UniqueProcess);
CONSOLE_SETCONSOLEAPPFROMPROCESSDATA(ProcessData, FALSE);
}
ULONG
SrvAllocConsole(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_ALLOC_MSG a = (PCONSOLE_ALLOC_MSG)&m->u.ApiMessageData;
PCONSOLE_PER_PROCESS_DATA ProcessData;
NTSTATUS Status;
PCONSOLE_INFORMATION Console;
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
PCSR_PROCESS Process;
UNICODE_STRING strDesktopName;
ProcessData = CONSOLE_PERPROCESSDATA();
ASSERT(!CONSOLE_GETCONSOLEAPPFROMPROCESSDATA(ProcessData));
if (!CsrValidateMessageBuffer(m, &a->Title, a->TitleLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->Desktop, a->DesktopLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->CurDir, a->CurDirLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->AppName, a->AppNameLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->ConsoleInfo, sizeof(*a->ConsoleInfo), sizeof(BYTE))) {
return STATUS_INVALID_PARAMETER;
}
Process = (PCSR_PROCESS)(CSR_SERVER_QUERYCLIENTTHREAD()->Process);
if (a->DesktopLength) {
RtlInitUnicodeString(&strDesktopName, a->Desktop);
} else {
RtlInitUnicodeString(&strDesktopName, L"Default");
}
ProcessHandleRecord = ConsoleHeapAlloc(HANDLE_TAG, sizeof(CONSOLE_PROCESS_HANDLE));
if (ProcessHandleRecord == NULL) {
return (ULONG)STATUS_NO_MEMORY;
}
Status = SetUpConsole(a->ConsoleInfo,
a->TitleLength,
a->Title,
a->CurDir,
a->AppName,
ProcessData,
TRUE,
&strDesktopName);
if (!NT_SUCCESS(Status)) {
ConsoleHeapFree(ProcessHandleRecord);
return Status;
}
CONSOLE_SETCONSOLEAPP(TRUE);
Process->Flags |= CSR_PROCESS_CONSOLEAPP;
Status = RevalidateConsole(a->ConsoleInfo->ConsoleHandle,&Console);
ASSERT (NT_SUCCESS(Status));
ProcessHandleRecord->Process = CSR_SERVER_QUERYCLIENTTHREAD()->Process;
ProcessHandleRecord->CtrlRoutine = a->CtrlRoutine;
ProcessHandleRecord->PropRoutine = a->PropRoutine;
ASSERT (!(Console->Flags & CONSOLE_SHUTTING_DOWN));
AddProcessToList(Console, ProcessHandleRecord, CONSOLE_CLIENTPROCESSHANDLE());
SetProcessForegroundRights(Process, Console->Flags & CONSOLE_HAS_FOCUS);
AllocateCommandHistory(Console,
a->AppNameLength,
a->AppName,
CONSOLE_CLIENTPROCESSHANDLE());
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus);
}
ULONG
SrvFreeConsole(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_FREE_MSG a = (PCONSOLE_FREE_MSG)&m->u.ApiMessageData;
PCONSOLE_PER_PROCESS_DATA ProcessData;
NTSTATUS Status;
ProcessData = CONSOLE_PERPROCESSDATA();
ASSERT (CONSOLE_GETCONSOLEAPPFROMPROCESSDATA(ProcessData));
if (CONSOLE_GETCONSOLEHANDLEFROMPROCESSDATA(ProcessData) != a->ConsoleHandle) {
RIPMSG1(RIP_WARNING, "SrvFreeConsole: invalid console handle %x", a->ConsoleHandle);
return STATUS_INVALID_HANDLE;
}
Status = RemoveConsole(ProcessData,
CONSOLE_CLIENTPROCESSHANDLE(),
CONSOLE_CLIENTPROCESSID());
if (NT_SUCCESS(Status)) {
CONSOLE_SETCONSOLEAPP(FALSE);
}
return Status;
UNREFERENCED_PARAMETER(ReplyStatus);
}
ULONG
SrvAttachConsole(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_ATTACH_MSG a = (PCONSOLE_ATTACH_MSG)&m->u.ApiMessageData;
DWORD ProcessId;
NTSTATUS Status;
PCSR_PROCESS ParentProcess;
PCSR_PROCESS Process;
CLIENT_ID ClientId;
OBJECT_ATTRIBUTES Obja;
HANDLE ProcessHandle;
PCONSOLE_INFORMATION Console;
PCONSOLE_PER_PROCESS_DATA ProcessData;
PCONSOLE_PER_PROCESS_DATA ParentProcessData;
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
Process = (PCSR_PROCESS)(CSR_SERVER_QUERYCLIENTTHREAD()->Process);
//
// Make sure we have a valid buffer
//
if (!CsrValidateMessageBuffer(m, &a->ConsoleInfo, sizeof(*a->ConsoleInfo), sizeof(BYTE))) {
return STATUS_INVALID_PARAMETER;
}
//
// Make sure we're not already attached to a console
//
ProcessData = CONSOLE_FROMPROCESSPERPROCESSDATA(Process);
if (CONSOLE_GETCONSOLEAPPFROMPROCESSDATA(ProcessData)) {
return STATUS_ACCESS_DENIED;
}
//
// Figure out what process we're attaching to.
//
if (a->ProcessId == (DWORD)-1) {
ProcessId = ProcessData->ParentProcessId;
} else {
ProcessId = a->ProcessId;
}
//
// Lock the process we're attaching to so it can't go away.
//
Status = CsrLockProcessByClientId(LongToHandle(ProcessId), &ParentProcess);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Make sure we have access to the process.
//
if (!CsrImpersonateClient(NULL)) {
CsrUnlockProcess(ParentProcess);
return STATUS_BAD_IMPERSONATION_LEVEL;
}
ClientId.UniqueThread = NULL;
ClientId.UniqueProcess = UlongToHandle(ProcessId);
InitializeObjectAttributes(
&Obja,
NULL,
0,
NULL,
NULL
);
Status = NtOpenProcess(
&ProcessHandle,
PROCESS_ALL_ACCESS,
&Obja,
&ClientId
);
CsrRevertToSelf();
if (!NT_SUCCESS(Status)) {
CsrUnlockProcess(ParentProcess);
return Status;
}
NtClose(ProcessHandle);
//
// Add current process to parent process's console.
//
Process->Flags |= CSR_PROCESS_CONSOLEAPP;
ParentProcessData = CONSOLE_FROMPROCESSPERPROCESSDATA(ParentProcess);
*ProcessData = *ParentProcessData;
Status = ConsoleAddProcessRoutine(ParentProcess, Process);
if (NT_SUCCESS(Status)) {
CONSOLE_SETCONSOLEAPP(TRUE);
Status = RevalidateConsole(ProcessData->ConsoleHandle, &Console);
}
CsrUnlockProcess(ParentProcess);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Initialize per process console settings.
//
Status = MapEventHandles(CONSOLE_CLIENTPROCESSHANDLE(),
Console,
a->ConsoleInfo
);
if (!NT_SUCCESS(Status)) {
CONSOLE_SETCONSOLEAPPFROMPROCESSDATA(ProcessData, FALSE);
UnlockConsole(Console);
RemoveConsole(ProcessData, Process->ProcessHandle, 0);
return Status;
}
NtCurrentPeb()->ProcessParameters->ConsoleHandle =
a->ConsoleInfo->ConsoleHandle = ProcessData->ConsoleHandle;
a->ConsoleInfo->StdIn = INDEX_TO_HANDLE(0);
a->ConsoleInfo->StdOut = INDEX_TO_HANDLE(1);
a->ConsoleInfo->StdErr = INDEX_TO_HANDLE(2);
ProcessHandleRecord = FindProcessInList(Console, CONSOLE_CLIENTPROCESSHANDLE());
if (ProcessHandleRecord) {
ProcessHandleRecord->CtrlRoutine = a->CtrlRoutine;
ProcessHandleRecord->PropRoutine = a->PropRoutine;
}
SetProcessForegroundRights(Process,
Console->Flags & CONSOLE_HAS_FOCUS);
UnlockConsole(Console);
return Status;
UNREFERENCED_PARAMETER(ReplyStatus);
}
NTSTATUS
MyRegOpenKey(
IN HANDLE hKey,
IN LPWSTR lpSubKey,
OUT PHANDLE phResult
)
{
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING SubKey;
//
// Convert the subkey to a counted Unicode string.
//
RtlInitUnicodeString( &SubKey, lpSubKey );
//
// Initialize the OBJECT_ATTRIBUTES structure and open the key.
//
InitializeObjectAttributes(
&Obja,
&SubKey,
OBJ_CASE_INSENSITIVE,
hKey,
NULL
);
return NtOpenKey(
phResult,
KEY_READ,
&Obja
);
}
NTSTATUS
MyRegQueryValue(
IN HANDLE hKey,
IN LPWSTR lpValueName,
IN DWORD dwValueLength,
OUT LPBYTE lpData
)
{
UNICODE_STRING ValueName;
ULONG BufferLength;
ULONG ResultLength;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
NTSTATUS Status;
//
// Convert the subkey to a counted Unicode string.
//
RtlInitUnicodeString( &ValueName, lpValueName );
BufferLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + dwValueLength;
KeyValueInformation = ConsoleHeapAlloc(TMP_TAG, BufferLength);
if (KeyValueInformation == NULL)
return STATUS_NO_MEMORY;
Status = NtQueryValueKey(
hKey,
&ValueName,
KeyValuePartialInformation,
KeyValueInformation,
BufferLength,
&ResultLength
);
if (NT_SUCCESS(Status)) {
ASSERT(KeyValueInformation->DataLength <= dwValueLength);
RtlCopyMemory(lpData,
KeyValueInformation->Data,
KeyValueInformation->DataLength);
if (KeyValueInformation->Type == REG_SZ ||
KeyValueInformation->Type == REG_MULTI_SZ
) {
if (KeyValueInformation->DataLength + sizeof(WCHAR) > dwValueLength) {
KeyValueInformation->DataLength -= sizeof(WCHAR);
}
lpData[KeyValueInformation->DataLength++] = 0;
lpData[KeyValueInformation->DataLength] = 0;
}
}
ConsoleHeapFree(KeyValueInformation);
return Status;
}
#if defined(FE_SB)
NTSTATUS
MyRegQueryValueEx(
IN HANDLE hKey,
IN LPWSTR lpValueName,
IN DWORD dwValueLength,
OUT LPBYTE lpData,
OUT LPDWORD lpDataLength
)
{
UNICODE_STRING ValueName;
ULONG BufferLength;
ULONG ResultLength;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
NTSTATUS Status;
//
// Convert the subkey to a counted Unicode string.
//
RtlInitUnicodeString( &ValueName, lpValueName );
BufferLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + dwValueLength;
KeyValueInformation = ConsoleHeapAlloc(TMP_TAG, BufferLength);
if (KeyValueInformation == NULL)
return STATUS_NO_MEMORY;
Status = NtQueryValueKey(
hKey,
&ValueName,
KeyValuePartialInformation,
KeyValueInformation,
BufferLength,
&ResultLength
);
if (NT_SUCCESS(Status)) {
ASSERT(KeyValueInformation->DataLength <= dwValueLength);
RtlCopyMemory(lpData,
KeyValueInformation->Data,
KeyValueInformation->DataLength);
if (lpDataLength)
{
*lpDataLength = KeyValueInformation->DataLength;
}
}
ConsoleHeapFree(KeyValueInformation);
return Status;
}
NTSTATUS
MyRegEnumValue(
IN HANDLE hKey,
IN DWORD dwIndex,
OUT DWORD dwValueLength,
OUT LPWSTR lpValueName,
OUT DWORD dwDataLength,
OUT LPBYTE lpData
)
{
ULONG BufferLength;
ULONG ResultLength;
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
NTSTATUS Status;
//
// Convert the subkey to a counted Unicode string.
//
BufferLength = sizeof(KEY_VALUE_FULL_INFORMATION) + dwValueLength + dwDataLength;
KeyValueInformation = ConsoleHeapAlloc(TMP_TAG, BufferLength);
if (KeyValueInformation == NULL)
return STATUS_NO_MEMORY;
Status = NtEnumerateValueKey(
hKey,
dwIndex,
KeyValueFullInformation,
KeyValueInformation,
BufferLength,
&ResultLength
);
if (NT_SUCCESS(Status)) {
ASSERT(KeyValueInformation->NameLength <= dwValueLength);
RtlMoveMemory(lpValueName,
KeyValueInformation->Name,
KeyValueInformation->NameLength);
lpValueName[ KeyValueInformation->NameLength >> 1 ] = UNICODE_NULL;
ASSERT(KeyValueInformation->DataLength <= dwDataLength);
RtlMoveMemory(lpData,
(PBYTE)KeyValueInformation + KeyValueInformation->DataOffset,
KeyValueInformation->DataLength);
if (KeyValueInformation->Type == REG_SZ) {
if (KeyValueInformation->DataLength + sizeof(WCHAR) > dwDataLength) {
KeyValueInformation->DataLength -= sizeof(WCHAR);
}
lpData[KeyValueInformation->DataLength++] = 0;
lpData[KeyValueInformation->DataLength] = 0;
}
}
ConsoleHeapFree(KeyValueInformation);
return Status;
}
#endif
#define SYSTEM_ROOT (L"%SystemRoot%")
#define SYSTEM_ROOT_LENGTH (sizeof(SYSTEM_ROOT) - sizeof(WCHAR))
LPWSTR
TranslateConsoleTitle(
LPWSTR ConsoleTitle,
PUSHORT pcbTranslatedTitle,
BOOL Unexpand,
BOOL Substitute
)
/*++
Routine Description:
This routine translates path characters into '_' characters because
the NT registry apis do not allow the creation of keys with
names that contain path characters. It also converts absolute paths
into %SystemRoot% relative ones. As an example, if both behaviors were
specified it would convert a title like C:\WINNT\System32\cmd.exe to
%SystemRoot%_System32_cmd.exe.
Arguments:
ConsoleTitle - Pointer to string to translate.
pcbTranslatedTitle - On return, contains size of translated title.
Unexpand - Convert absolute path to %SystemRoot% relative one.
Substitute - Replace '\' with '_' in path.
Return Value:
Pointer to translated title or NULL.
Note:
This routine allocates a buffer that must be freed.
--*/
{
USHORT cbConsoleTitle, i;
USHORT cbSystemRoot;
LPWSTR TranslatedConsoleTitle, Tmp;
cbConsoleTitle = (USHORT)((lstrlenW(ConsoleTitle) + 1) * sizeof(WCHAR));
cbSystemRoot = (USHORT)(lstrlenW(USER_SHARED_DATA->NtSystemRoot) * sizeof(WCHAR));
if (Unexpand && !MyStringCompareW(ConsoleTitle,
USER_SHARED_DATA->NtSystemRoot,
cbSystemRoot,
TRUE)) {
cbConsoleTitle -= cbSystemRoot;
(PBYTE)ConsoleTitle += cbSystemRoot;
cbSystemRoot = SYSTEM_ROOT_LENGTH;
} else {
cbSystemRoot = 0;
}
Tmp = TranslatedConsoleTitle = ConsoleHeapAlloc(TITLE_TAG, cbSystemRoot + cbConsoleTitle);
if (TranslatedConsoleTitle == NULL) {
return NULL;
}
RtlCopyMemory(TranslatedConsoleTitle, SYSTEM_ROOT, cbSystemRoot);
(PBYTE)TranslatedConsoleTitle += cbSystemRoot;
for (i=0;i<cbConsoleTitle;i+=sizeof(WCHAR)) {
if (Substitute && *ConsoleTitle == '\\') {
*TranslatedConsoleTitle++ = (WCHAR)'_';
} else {
*TranslatedConsoleTitle++ = *ConsoleTitle;
}
ConsoleTitle++;
}
if (pcbTranslatedTitle) {
*pcbTranslatedTitle = cbSystemRoot + cbConsoleTitle;
}
return Tmp;
}
ULONG
ConsoleClientShutdown(
PCSR_PROCESS Process,
ULONG Flags,
BOOLEAN fFirstPass
)
{
PCONSOLE_INFORMATION Console;
PCONSOLE_PER_PROCESS_DATA ProcessData;
NTSTATUS Status;
HWND hWnd;
HANDLE TerminationEvent;
HANDLE ConsoleHandle;
NTSTATUS WaitStatus;
USERTHREAD_USEDESKTOPINFO utudi;
//
// Find the console associated with this process
//
ProcessData = CONSOLE_FROMPROCESSPERPROCESSDATA(Process);
//
// If this process is not a console app, stop right here unless
// this is the second pass of shutdown, in which case we'll take
// it.
//
if (!ProcessData || !CONSOLE_GETCONSOLEAPPFROMPROCESSDATA(ProcessData)) {
#if defined(FE_IME)
if (fFirstPass &&
(ProcessData->ConsoleHandle == NULL) &&
(ProcessData->hDesk != NULL))
{
//
// If this process is a Console IME,
// should unregister console IME on this desktop.
//
RemoveConsoleIME(Process, HandleToUlong(Process->ClientId.UniqueThread));
}
#endif
if (fFirstPass) {
return SHUTDOWN_UNKNOWN_PROCESS;
}
return NonConsoleProcessShutdown(Process, Flags);
}
//
// Find the console structure pointer.
//
ConsoleHandle = CONSOLE_GETCONSOLEHANDLEFROMPROCESSDATA(ProcessData);
Status = RevalidateConsole(
ConsoleHandle,
&Console);
if (!NT_SUCCESS(Status)) {
return SHUTDOWN_UNKNOWN_PROCESS;
}
//
// If this is the invisible WOW console, return UNKNOWN so USER
// enumerates 16-bit gui apps.
//
if ((Console->Flags & CONSOLE_NO_WINDOW) &&
(Console->Flags & CONSOLE_WOW_REGISTERED)) {
UnlockConsole(Console);
return SHUTDOWN_UNKNOWN_PROCESS;
}
//
// Sometimes the console structure is around even though the
// hWnd has been NULLed out. In this case, go to non-console
// process shutdown.
//
hWnd = Console->hWnd;
if (hWnd == NULL || !IsWindow(hWnd)) {
UnlockConsole(Console);
return NonConsoleProcessShutdown(Process, Flags);
}
//
// Make a copy of the console termination event
//
Status = NtDuplicateObject(NtCurrentProcess(),
Console->TerminationEvent,
NtCurrentProcess(),
&TerminationEvent,
0,
FALSE,
DUPLICATE_SAME_ACCESS
);
if (!NT_SUCCESS(Status)) {
UnlockConsole(Console);
return NonConsoleProcessShutdown(Process, Flags);
}
//
// Attach to the desktop.
//
utudi.hThread = Console->InputThreadInfo->ThreadHandle;
utudi.drdRestore.pdeskRestore = NULL;
Status = NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseDesktop,
&utudi,
sizeof(utudi));
UnlockConsole(Console);
if (!NT_SUCCESS(Status)) {
NtClose(TerminationEvent);
return NonConsoleProcessShutdown(Process, Flags);
}
//
// We're done looking at this process structure, so dereference it.
//
CsrDereferenceProcess(Process);
//
// Synchronously talk to this console.
//
Status = ShutdownConsole(ConsoleHandle, Flags);
//
// Detach from the desktop.
//
utudi.hThread = NULL;
NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseDesktop,
&utudi,
sizeof(utudi));
//
// If Status == STATUS_PROCESS_IS_TERMINATING, then we should wait
// for the console to exit.
//
if (Status == STATUS_PROCESS_IS_TERMINATING) {
WaitStatus = InternalWaitCancel(TerminationEvent, 500000);
if (WaitStatus == STATUS_WAIT_1) {
Status = SHUTDOWN_CANCEL;
} else if (WaitStatus != STATUS_TIMEOUT) {
Status = SHUTDOWN_KNOWN_PROCESS;
} else {
#if DBG
PLIST_ENTRY ListHead, ListNext;
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
PCSR_PROCESS Process;
RIPMSG0(RIP_ERROR | RIP_THERESMORE, "********************************************");
RIPMSG1(RIP_ERROR | RIP_THERESMORE, "Shutdown wait timed out on console %p", Console);
RIPMSG1(RIP_ERROR | RIP_THERESMORE, "Reference count is %d", Console->RefCount);
RIPMSG0(RIP_ERROR | RIP_THERESMORE, "Dump these processes and see if they're hung");
ListHead = &Console->ProcessHandleList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
ProcessHandleRecord = CONTAINING_RECORD(ListNext, CONSOLE_PROCESS_HANDLE, ListLink);
Process = ProcessHandleRecord->Process;
RIPMSG2(RIP_ERROR | RIP_THERESMORE, "CsrProcess = %p ProcessId = %x", Process, Process->ClientId.UniqueProcess);
ListNext = ListNext->Flink;
}
RIPMSG0(RIP_ERROR, "********************************************");
#endif
Status = SHUTDOWN_CANCEL;
}
}
NtClose(TerminationEvent);
return Status;
}
ULONG
NonConsoleProcessShutdown(
PCSR_PROCESS Process,
DWORD dwFlags
)
{
CONSOLE_PROCESS_TERMINATION_RECORD TerminateRecord;
DWORD EventType;
BOOL Success;
HANDLE ProcessHandle;
Success = DuplicateHandle(NtCurrentProcess(),
Process->ProcessHandle,
NtCurrentProcess(),
&ProcessHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
if (!Success)
ProcessHandle = Process->ProcessHandle;
TerminateRecord.ProcessHandle = ProcessHandle;
TerminateRecord.TerminateCount = 0;
TerminateRecord.CtrlRoutine = CtrlRoutine;
CsrDereferenceProcess(Process);
EventType = CTRL_LOGOFF_EVENT;
if (dwFlags & EWX_SHUTDOWN)
EventType = CTRL_SHUTDOWN_EVENT;
CreateCtrlThread(&TerminateRecord,
1,
NULL,
EventType,
TRUE);
if (Success)
CloseHandle(ProcessHandle);
return SHUTDOWN_KNOWN_PROCESS;
}
VOID
InitializeConsoleAttributes( VOID )
/*++
Routine Description:
This routine initializes default attributes from the current
user's registry values. It gets called during logon/logoff.
Arguments:
none
Return Value:
none
--*/
{
//
// Store default values in structure and mark it
// as invalid (by resetting LastWriteTime).
//
DefaultRegInfo.ScreenFill.Attributes = 0x07; // white on black
DefaultRegInfo.ScreenFill.Char.UnicodeChar = (WCHAR)' ';
DefaultRegInfo.PopupFill.Attributes = 0xf5; // purple on white
DefaultRegInfo.PopupFill.Char.UnicodeChar = (WCHAR)' ';
DefaultRegInfo.InsertMode = FALSE;
DefaultRegInfo.QuickEdit = FALSE;
DefaultRegInfo.AutoPosition = TRUE;
DefaultRegInfo.FullScreen = FALSE;
DefaultRegInfo.ScreenBufferSize.X = 80;
DefaultRegInfo.ScreenBufferSize.Y = 25;
DefaultRegInfo.WindowSize.X = 80;
DefaultRegInfo.WindowSize.Y = 25;
DefaultRegInfo.WindowOrigin.X = 0;
DefaultRegInfo.WindowOrigin.Y = 0;
DefaultRegInfo.FontSize.X = 0;
DefaultRegInfo.FontSize.Y = 0;
DefaultRegInfo.FontFamily = 0;
DefaultRegInfo.FontWeight = 0;
DefaultRegInfo.FaceName[0] = L'\0';
DefaultRegInfo.CursorSize = CURSOR_SMALL_SIZE;
DefaultRegInfo.HistoryBufferSize = DEFAULT_NUMBER_OF_COMMANDS;
DefaultRegInfo.NumberOfHistoryBuffers = DEFAULT_NUMBER_OF_BUFFERS;
DefaultRegInfo.HistoryNoDup = FALSE;
DefaultRegInfo.ColorTable[ 0] = RGB(0, 0, 0 );
DefaultRegInfo.ColorTable[ 1] = RGB(0, 0, 0x80);
DefaultRegInfo.ColorTable[ 2] = RGB(0, 0x80,0 );
DefaultRegInfo.ColorTable[ 3] = RGB(0, 0x80,0x80);
DefaultRegInfo.ColorTable[ 4] = RGB(0x80,0, 0 );
DefaultRegInfo.ColorTable[ 5] = RGB(0x80,0, 0x80);
DefaultRegInfo.ColorTable[ 6] = RGB(0x80,0x80,0 );
DefaultRegInfo.ColorTable[ 7] = RGB(0xC0,0xC0,0xC0);
DefaultRegInfo.ColorTable[ 8] = RGB(0x80,0x80,0x80);
DefaultRegInfo.ColorTable[ 9] = RGB(0, 0, 0xFF);
DefaultRegInfo.ColorTable[10] = RGB(0, 0xFF,0 );
DefaultRegInfo.ColorTable[11] = RGB(0, 0xFF,0xFF);
DefaultRegInfo.ColorTable[12] = RGB(0xFF,0, 0 );
DefaultRegInfo.ColorTable[13] = RGB(0xFF,0, 0xFF);
DefaultRegInfo.ColorTable[14] = RGB(0xFF,0xFF,0 );
DefaultRegInfo.ColorTable[15] = RGB(0xFF,0xFF,0xFF);
#if defined(FE_SB) // scotthsu
DefaultRegInfo.CodePage = OEMCP;
#endif
DefaultRegInfo.LastWriteTime = 0;
//
// Get system metrics for this user
//
InitializeSystemMetrics();
}
VOID
GetRegistryValues(
IN LPWSTR ConsoleTitle,
OUT PCONSOLE_REGISTRY_INFO RegInfo
)
/*++
Routine Description:
This routine reads in values from the registry and places them
in the supplied structure.
Arguments:
ConsoleTitle - name of subkey to open
RegInfo - pointer to structure to receive information
Return Value:
none
--*/
{
HANDLE hCurrentUserKey;
HANDLE hConsoleKey;
HANDLE hTitleKey;
NTSTATUS Status;
LPWSTR TranslatedConsoleTitle;
DWORD dwValue;
DWORD i;
WCHAR awchFaceName[LF_FACESIZE];
WCHAR awchBuffer[64];
KEY_BASIC_INFORMATION KeyInfo;
ULONG ResultLength;
//
// Impersonate the client process
//
if (!CsrImpersonateClient(NULL)) {
RIPMSG0(RIP_WARNING, "GetRegistryValues Impersonate failed");
return;
}
//
// Open the current user registry key
//
Status = RtlOpenCurrentUser(MAXIMUM_ALLOWED, &hCurrentUserKey);
if (!NT_SUCCESS(Status)) {
CsrRevertToSelf();
return;
}
//
// Open the console registry key
//
Status = MyRegOpenKey(hCurrentUserKey,
CONSOLE_REGISTRY_STRING,
&hConsoleKey);
if (!NT_SUCCESS(Status)) {
NtClose(hCurrentUserKey);
CsrRevertToSelf();
return;
}
//
// If we're not reading the default key, check if the default values
// need to be updated
//
Status = NtQueryKey(hConsoleKey,
KeyBasicInformation,
&KeyInfo,
sizeof(KeyInfo),
&ResultLength);
if (!NT_ERROR(Status)) {
if (DefaultRegInfo.LastWriteTime != KeyInfo.LastWriteTime.QuadPart) {
DefaultRegInfo.LastWriteTime = KeyInfo.LastWriteTime.QuadPart;
if (RegInfo != &DefaultRegInfo) {
GetRegistryValues(L"", &DefaultRegInfo);
*RegInfo = DefaultRegInfo;
}
}
}
//
// Open the console title subkey
//
TranslatedConsoleTitle = TranslateConsoleTitle(ConsoleTitle, NULL, TRUE, TRUE);
if (TranslatedConsoleTitle == NULL) {
NtClose(hConsoleKey);
NtClose(hCurrentUserKey);
CsrRevertToSelf();
return;
}
Status = MyRegOpenKey(hConsoleKey,
TranslatedConsoleTitle,
&hTitleKey);
ConsoleHeapFree(TranslatedConsoleTitle);
if (!NT_SUCCESS(Status)) {
TranslatedConsoleTitle = TranslateConsoleTitle(ConsoleTitle, NULL, FALSE, TRUE);
if (TranslatedConsoleTitle) {
Status = MyRegOpenKey(hConsoleKey,
TranslatedConsoleTitle,
&hTitleKey);
ConsoleHeapFree(TranslatedConsoleTitle);
}
}
if (!NT_SUCCESS(Status)) {
NtClose(hConsoleKey);
NtClose(hCurrentUserKey);
CsrRevertToSelf();
return;
}
//
// Initial screen fill
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_FILLATTR,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->ScreenFill.Attributes = (WORD)dwValue;
}
//
// Initial popup fill
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_POPUPATTR,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->PopupFill.Attributes = (WORD)dwValue;
}
//
// Initial insert mode
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_INSERTMODE,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->InsertMode = !!dwValue;
}
//
// Initial quick edit mode
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_QUICKEDIT,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->QuickEdit = !!dwValue;
}
#ifdef i386
//
// Initial full screen mode
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_FULLSCR,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->FullScreen = !!dwValue;
}
#endif
#if defined(FE_SB) // scotthsu
//
// Code Page
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_CODEPAGE,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->CodePage = (UINT)dwValue;
// If this routine specified default settings for console property,
// then make sure code page value when Fae East environment.
// If code page value does not the same to OEMCP and any FE's code page then
// we are override code page value to OEMCP on default console property.
// Because, Far East environment has limitation that doesn not switch to
// another FE's code page by the SetConsoleCP/SetConsoleOutputCP.
//
// Compare of ConsoleTitle and L"" has limit to default property of console.
// It means, this code doesn't care user defined property.
// Content of user defined property has responsibility to themselves.
if (wcscmp(ConsoleTitle, L"") == 0 &&
IsAvailableFarEastCodePage(RegInfo->CodePage) &&
OEMCP != RegInfo->CodePage) {
RegInfo->CodePage = OEMCP;
}
}
#endif // FE_SB
//
// Initial screen buffer size
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_BUFFERSIZE,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->ScreenBufferSize.X = LOWORD(dwValue);
RegInfo->ScreenBufferSize.Y = HIWORD(dwValue);
if (RegInfo->ScreenBufferSize.X <= 0)
RegInfo->ScreenBufferSize.X = 1;
if (RegInfo->ScreenBufferSize.Y <= 0)
RegInfo->ScreenBufferSize.Y = 1;
}
//
// Initial window size
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_WINDOWSIZE,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->WindowSize.X = LOWORD(dwValue);
RegInfo->WindowSize.Y = HIWORD(dwValue);
if (RegInfo->WindowSize.X <= 0)
RegInfo->WindowSize.X = 1;
else if (RegInfo->WindowSize.X > RegInfo->ScreenBufferSize.X)
RegInfo->WindowSize.X = RegInfo->ScreenBufferSize.X;
if (RegInfo->WindowSize.Y <= 0)
RegInfo->WindowSize.Y = 1;
else if (RegInfo->WindowSize.Y > RegInfo->ScreenBufferSize.Y)
RegInfo->WindowSize.Y = RegInfo->ScreenBufferSize.Y;
}
//
// Initial window position
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_WINDOWPOS,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->WindowOrigin.X = LOWORD(dwValue);
RegInfo->WindowOrigin.Y = HIWORD(dwValue);
RegInfo->AutoPosition = FALSE;
}
//
// Initial font size
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_FONTSIZE,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->FontSize.X = LOWORD(dwValue);
RegInfo->FontSize.Y = HIWORD(dwValue);
}
//
// Initial font family
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_FONTFAMILY,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->FontFamily = dwValue;
}
//
// Initial font weight
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_FONTWEIGHT,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->FontWeight = dwValue;
}
//
// Initial font face name
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_FACENAME,
sizeof(awchFaceName), (PBYTE)awchFaceName))) {
RtlCopyMemory(RegInfo->FaceName, awchFaceName, sizeof(awchFaceName));
RegInfo->FaceName[NELEM(RegInfo->FaceName) - 1] = 0;
}
//
// Initial cursor size
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_CURSORSIZE,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->CursorSize = dwValue;
}
//
// Initial history buffer size
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_HISTORYSIZE,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->HistoryBufferSize = dwValue;
}
//
// Initial number of history buffers
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_HISTORYBUFS,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->NumberOfHistoryBuffers = dwValue;
}
//
// Initial history duplication mode
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_HISTORYNODUP,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->HistoryNoDup = dwValue;
}
for (i=0; i<16; i++) {
wsprintf(awchBuffer, CONSOLE_REGISTRY_COLORTABLE, i);
if (NT_SUCCESS(MyRegQueryValue(hTitleKey, awchBuffer,
sizeof(dwValue), (PBYTE)&dwValue))) {
RegInfo->ColorTable[ i ] = dwValue;
}
}
if (RegInfo == &DefaultRegInfo) {
//
// If the common (default) setting has been changed,
//
//
// Get registry for conime flag
//
if (NT_SUCCESS(MyRegQueryValue(hConsoleKey, CONSOLE_REGISTRY_LOAD_CONIME, sizeof dwValue, (PBYTE)&dwValue))) {
gfLoadConIme = (dwValue != 0);
} else {
gfLoadConIme = TRUE;
}
//
// get extended edit mode and keys from registry.
//
if (NT_SUCCESS(MyRegQueryValue(hConsoleKey,
CONSOLE_REGISTRY_EXTENDEDEDITKEY,
sizeof dwValue,
(PBYTE)&dwValue)) &&
dwValue <= 1) {
ExtKeyDefBuf buf;
gExtendedEditKey = dwValue;
//
// Initialize Extended Edit keys
//
InitExtendedEditKeys(NULL);
if (NT_SUCCESS(MyRegQueryValue(hConsoleKey,
CONSOLE_REGISTRY_EXTENDEDEDITKEY_CUSTOM,
sizeof(buf),
(PBYTE)&buf))) {
InitExtendedEditKeys(&buf);
} else {
RIPMSG0(RIP_VERBOSE, "Error reading ExtendedEditkeyCustom.");
}
} else {
gExtendedEditKey = 0;
RIPMSG0(RIP_VERBOSE, "Error reading ExtendedEditkey.");
}
//
// Word delimiters
//
if (gExtendedEditKey) {
// If extended edit key is given, provide extended word delimiters
// by default.
memcpy((LPBYTE)gaWordDelimChars, (LPBYTE)gaWordDelimCharsDefault,
sizeof gaWordDelimChars[0] * WORD_DELIM_MAX);
} else {
// Otherwise, stick to the original word delimiter.
gaWordDelimChars[0] = L'\0';
}
// Read word delimiters from registry
if (NT_SUCCESS(MyRegQueryValue(hConsoleKey,
CONSOLE_REGISTRY_WORD_DELIM,
sizeof awchBuffer,
(PBYTE)awchBuffer))) {
// OK, copy it to the word delimiter array.
wcsncpy(gaWordDelimChars, awchBuffer, WORD_DELIM_MAX);
gaWordDelimChars[WORD_DELIM_MAX - 1] = 0;
}
//
// Read Trim Zero Heading flag
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_TRIMZEROHEADINGS,
sizeof(dwValue), (PBYTE)&dwValue))) {
gfTrimLeadingZeros = dwValue;
} else {
gfTrimLeadingZeros = FALSE;
}
//
// Color selected area function enable flag
//
if (NT_SUCCESS(MyRegQueryValue(hTitleKey,
CONSOLE_REGISTRY_ENABLE_COLOR_SELECTION,
sizeof(dwValue), (PBYTE)&dwValue))) {
gfEnableColorSelection = !!dwValue;
}
else {
gfEnableColorSelection = FALSE;
}
}
//
// Close the registry keys
//
NtClose(hTitleKey);
NtClose(hConsoleKey);
NtClose(hCurrentUserKey);
CsrRevertToSelf();
}
NTSTATUS
GetConsoleLangId(
IN UINT OutputCP,
OUT LANGID* pLangId
)
{
NTSTATUS Status;
if (CONSOLE_IS_DBCS_ENABLED()){
if (pLangId != NULL) {
switch (OutputCP) {
case 932:
*pLangId = MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT);
break;
case 949:
*pLangId = MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
break;
case 936:
*pLangId = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
break;
case 950:
*pLangId = MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
break;
default:
*pLangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
break;
}
}
Status = STATUS_SUCCESS;
}
else {
Status = STATUS_NOT_SUPPORTED;
}
return Status;
}
ULONG
SrvGetConsoleLangId(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_LANGID_MSG a = (PCONSOLE_LANGID_MSG)&m->u.ApiMessageData;
NTSTATUS Status;
PCONSOLE_INFORMATION Console;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
Status = GetConsoleLangId(Console->OutputCP, &a->LangId);
UnlockConsole(Console);
return Status;
UNREFERENCED_PARAMETER(ReplyStatus);
}