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
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);
|
|
}
|