reactos/dll/win32/kernel32/client/console/init.c

539 lines
17 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* FILE: dll/win32/kernel32/client/console/init.c
* PURPOSE: Console API Client Initialization
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Aleksey Bragin (aleksey@reactos.org)
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
/* INCLUDES *******************************************************************/
#include <k32.h>
// For Control Panel Applet
#include <cpl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
RTL_CRITICAL_SECTION ConsoleLock;
BOOLEAN ConsoleInitialized = FALSE;
extern HANDLE InputWaitHandle;
static const PWSTR DefaultConsoleTitle = L"ReactOS Console";
/* FUNCTIONS ******************************************************************/
DWORD
WINAPI
PropDialogHandler(IN LPVOID lpThreadParameter)
{
// NOTE: lpThreadParameter corresponds to the client shared section handle.
NTSTATUS Status = STATUS_SUCCESS;
HMODULE hConsoleApplet = NULL;
APPLET_PROC CPlApplet;
static BOOL AlreadyDisplayingProps = FALSE;
WCHAR szBuffer[MAX_PATH];
/*
* Do not launch more than once the console property dialog applet,
* or (albeit less probable), if we are not initialized.
*/
if (!ConsoleInitialized || AlreadyDisplayingProps)
{
/* Close the associated client shared section handle if needed */
if (lpThreadParameter)
CloseHandle((HANDLE)lpThreadParameter);
return STATUS_UNSUCCESSFUL;
}
AlreadyDisplayingProps = TRUE;
/* Load the control applet */
GetSystemDirectoryW(szBuffer, MAX_PATH);
wcscat(szBuffer, L"\\console.dll");
hConsoleApplet = LoadLibraryW(szBuffer);
if (hConsoleApplet == NULL)
{
DPRINT1("Failed to load console.dll\n");
Status = STATUS_UNSUCCESSFUL;
goto Quit;
}
/* Load its main function */
CPlApplet = (APPLET_PROC)GetProcAddress(hConsoleApplet, "CPlApplet");
if (CPlApplet == NULL)
{
DPRINT1("Error: console.dll misses CPlApplet export\n");
Status = STATUS_UNSUCCESSFUL;
goto Quit;
}
/* Initialize the applet */
if (CPlApplet(NULL, CPL_INIT, 0, 0) == FALSE)
{
DPRINT1("Error: failed to initialize console.dll\n");
Status = STATUS_UNSUCCESSFUL;
goto Quit;
}
/* Check the count */
if (CPlApplet(NULL, CPL_GETCOUNT, 0, 0) != 1)
{
DPRINT1("Error: console.dll returned unexpected CPL count\n");
Status = STATUS_UNSUCCESSFUL;
goto Quit;
}
/*
* Start the applet. For Windows compatibility purposes we need
* to pass the client shared section handle (lpThreadParameter)
* via the hWnd parameter of the CPlApplet function.
*/
CPlApplet((HWND)lpThreadParameter, CPL_DBLCLK, 0, 0);
/* We have finished */
CPlApplet(NULL, CPL_EXIT, 0, 0);
Quit:
if (hConsoleApplet) FreeLibrary(hConsoleApplet);
AlreadyDisplayingProps = FALSE;
return Status;
}
static INT
ParseShellInfo(LPCWSTR lpszShellInfo,
LPCWSTR lpszKeyword)
{
DPRINT("ParseShellInfo is UNIMPLEMENTED\n");
return 0;
}
/*
* NOTE:
* The "LPDWORD Length" parameters point on input to the maximum size of
* the buffers that can hold data (if != 0), and on output they hold the
* real size of the data. If "Length" are == 0 on input, then on output
* they receive the full size of the data.
* The "LPWSTR* lpTitle" parameter has a double meaning:
* - when "CaptureTitle" is TRUE, data is copied to the buffer pointed
* by the pointer (*lpTitle).
* - when "CaptureTitle" is FALSE, "*lpTitle" is set to the address of
* the source data.
*/
VOID
SetUpConsoleInfo(IN BOOLEAN CaptureTitle,
IN OUT LPDWORD pTitleLength,
IN OUT LPWSTR* lpTitle OPTIONAL,
IN OUT LPDWORD pDesktopLength,
IN OUT LPWSTR* lpDesktop OPTIONAL,
IN OUT PCONSOLE_START_INFO ConsoleStartInfo)
{
PRTL_USER_PROCESS_PARAMETERS Parameters = NtCurrentPeb()->ProcessParameters;
DWORD Length;
/* Initialize the fields */
ConsoleStartInfo->IconIndex = 0;
ConsoleStartInfo->hIcon = NULL;
ConsoleStartInfo->hIconSm = NULL;
ConsoleStartInfo->dwStartupFlags = Parameters->WindowFlags;
ConsoleStartInfo->nFont = 0;
ConsoleStartInfo->nInputBufferSize = 0;
ConsoleStartInfo->uCodePage = GetOEMCP();
if (lpTitle)
{
LPWSTR Title;
/* If we don't have any title, use the default one */
if (Parameters->WindowTitle.Buffer == NULL)
{
Title = DefaultConsoleTitle;
Length = lstrlenW(DefaultConsoleTitle) * sizeof(WCHAR); // sizeof(DefaultConsoleTitle);
}
else
{
Title = Parameters->WindowTitle.Buffer;
Length = Parameters->WindowTitle.Length;
}
/* Retrieve the needed buffer size */
Length += sizeof(WCHAR);
if (*pTitleLength > 0) Length = min(Length, *pTitleLength);
*pTitleLength = Length;
/* Capture the data if needed, or, return a pointer to it */
if (CaptureTitle)
{
/*
* Length is always >= sizeof(WCHAR). Copy everything but the
* possible trailing NULL character, and then NULL-terminate.
*/
Length -= sizeof(WCHAR);
RtlCopyMemory(*lpTitle, Title, Length);
(*lpTitle)[Length / sizeof(WCHAR)] = UNICODE_NULL;
}
else
{
*lpTitle = Title;
}
}
else
{
*pTitleLength = 0;
}
if (lpDesktop && Parameters->DesktopInfo.Buffer && *Parameters->DesktopInfo.Buffer)
{
/* Retrieve the needed buffer size */
Length = Parameters->DesktopInfo.Length + sizeof(WCHAR);
if (*pDesktopLength > 0) Length = min(Length, *pDesktopLength);
*pDesktopLength = Length;
/* Return a pointer to the data */
*lpDesktop = Parameters->DesktopInfo.Buffer;
}
else
{
*pDesktopLength = 0;
if (lpDesktop) *lpDesktop = NULL;
}
if (Parameters->WindowFlags & STARTF_USEFILLATTRIBUTE)
{
ConsoleStartInfo->wFillAttribute = (WORD)Parameters->FillAttribute;
}
if (Parameters->WindowFlags & STARTF_USECOUNTCHARS)
{
ConsoleStartInfo->dwScreenBufferSize.X = (SHORT)Parameters->CountCharsX;
ConsoleStartInfo->dwScreenBufferSize.Y = (SHORT)Parameters->CountCharsY;
}
if (Parameters->WindowFlags & STARTF_USESHOWWINDOW)
{
ConsoleStartInfo->wShowWindow = (WORD)Parameters->ShowWindowFlags;
}
if (Parameters->WindowFlags & STARTF_USEPOSITION)
{
ConsoleStartInfo->dwWindowOrigin.X = (SHORT)Parameters->StartingX;
ConsoleStartInfo->dwWindowOrigin.Y = (SHORT)Parameters->StartingY;
}
if (Parameters->WindowFlags & STARTF_USESIZE)
{
ConsoleStartInfo->dwWindowSize.X = (SHORT)Parameters->CountX;
ConsoleStartInfo->dwWindowSize.Y = (SHORT)Parameters->CountY;
}
/* Get shell information (ShellInfo.Buffer is NULL-terminated) */
if (Parameters->ShellInfo.Buffer != NULL)
{
ConsoleStartInfo->IconIndex = ParseShellInfo(Parameters->ShellInfo.Buffer, L"dde.");
if ((Parameters->WindowFlags & STARTF_USEHOTKEY) == 0)
ConsoleStartInfo->dwHotKey = ParseShellInfo(Parameters->ShellInfo.Buffer, L"hotkey.");
else
ConsoleStartInfo->dwHotKey = HandleToUlong(Parameters->StandardInput);
}
}
VOID
SetUpHandles(IN PCONSOLE_START_INFO ConsoleStartInfo)
{
PRTL_USER_PROCESS_PARAMETERS Parameters = NtCurrentPeb()->ProcessParameters;
if (ConsoleStartInfo->dwStartupFlags & STARTF_USEHOTKEY)
{
Parameters->WindowFlags &= ~STARTF_USEHOTKEY;
}
if (ConsoleStartInfo->dwStartupFlags & STARTF_SHELLPRIVATE)
{
Parameters->WindowFlags &= ~STARTF_SHELLPRIVATE;
}
/* We got the handles, let's set them */
Parameters->ConsoleHandle = ConsoleStartInfo->ConsoleHandle;
if ((ConsoleStartInfo->dwStartupFlags & STARTF_USESTDHANDLES) == 0)
{
Parameters->StandardInput = ConsoleStartInfo->InputHandle;
Parameters->StandardOutput = ConsoleStartInfo->OutputHandle;
Parameters->StandardError = ConsoleStartInfo->ErrorHandle;
}
}
static BOOLEAN
IsConsoleApp(VOID)
{
PIMAGE_NT_HEADERS ImageNtHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
return (ImageNtHeader && (ImageNtHeader->OptionalHeader.Subsystem ==
IMAGE_SUBSYSTEM_WINDOWS_CUI));
}
static BOOLEAN
ConnectConsole(IN PWSTR SessionDir,
IN PCONSRV_API_CONNECTINFO ConnectInfo,
OUT PBOOLEAN InServerProcess)
{
NTSTATUS Status;
ULONG ConnectInfoSize = sizeof(*ConnectInfo);
ASSERT(SessionDir);
/* Connect to the Console Server */
DPRINT("Connecting to the Console Server...\n");
Status = CsrClientConnectToServer(SessionDir,
CONSRV_SERVERDLL_INDEX,
ConnectInfo,
&ConnectInfoSize,
InServerProcess);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to connect to the Console Server (Status %lx)\n", Status);
return FALSE;
}
/* Nothing to do for server-to-server */
if (*InServerProcess) return TRUE;
/* Nothing to do if this is not a console app */
if (!ConnectInfo->IsConsoleApp) return TRUE;
/* Wait for the connection to finish */
// Is ConnectInfo->ConsoleStartInfo.InitEvents aligned on handle boundary ????
Status = NtWaitForMultipleObjects(MAX_INIT_EVENTS,
ConnectInfo->ConsoleStartInfo.InitEvents,
WaitAny, FALSE, NULL);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return FALSE;
}
NtClose(ConnectInfo->ConsoleStartInfo.InitEvents[INIT_SUCCESS]);
NtClose(ConnectInfo->ConsoleStartInfo.InitEvents[INIT_FAILURE]);
if (Status != INIT_SUCCESS)
{
NtCurrentPeb()->ProcessParameters->ConsoleHandle = NULL;
return FALSE;
}
return TRUE;
}
BOOLEAN
WINAPI
ConDllInitialize(IN ULONG Reason,
IN PWSTR SessionDir)
{
NTSTATUS Status;
PRTL_USER_PROCESS_PARAMETERS Parameters = NtCurrentPeb()->ProcessParameters;
BOOLEAN InServerProcess = FALSE;
CONSRV_API_CONNECTINFO ConnectInfo;
LCID lcid;
if (Reason != DLL_PROCESS_ATTACH)
{
if ((Reason == DLL_THREAD_ATTACH) && IsConsoleApp())
{
/* Sets the current console locale for the new thread */
SetTEBLangID(lcid);
}
else if (Reason == DLL_PROCESS_DETACH)
{
/* Free our resources */
if (ConsoleInitialized != FALSE)
{
ConsoleInitialized = FALSE;
RtlDeleteCriticalSection(&ConsoleLock);
}
}
return TRUE;
}
DPRINT("ConDllInitialize for: %wZ\n"
"Our current console handles are: 0x%p, 0x%p, 0x%p 0x%p\n",
&Parameters->ImagePathName,
Parameters->ConsoleHandle,
Parameters->StandardInput,
Parameters->StandardOutput,
Parameters->StandardError);
/* Initialize our global console DLL lock */
Status = RtlInitializeCriticalSection(&ConsoleLock);
if (!NT_SUCCESS(Status)) return FALSE;
ConsoleInitialized = TRUE;
/* Show by default the console window when applicable */
ConnectInfo.IsWindowVisible = TRUE;
/* If this is a console app, a console will be created/opened */
ConnectInfo.IsConsoleApp = IsConsoleApp();
/* Do nothing if this is not a console app... */
if (!ConnectInfo.IsConsoleApp)
{
DPRINT("Image is not a console application\n");
}
/*
* Handle the special flags given to us by BasePushProcessParameters.
*/
if (Parameters->ConsoleHandle == HANDLE_DETACHED_PROCESS)
{
/* No console to create */
DPRINT("No console to create\n");
/*
* The new process does not inherit its parent's console and cannot
* attach to the console of its parent. The new process can call the
* AllocConsole function at a later time to create a console.
*/
Parameters->ConsoleHandle = NULL; // Do not inherit the parent's console.
ConnectInfo.IsConsoleApp = FALSE; // Do not create any console.
}
else if (Parameters->ConsoleHandle == HANDLE_CREATE_NEW_CONSOLE)
{
/* We'll get the real one soon */
DPRINT("Creating a new separate console\n");
/*
* The new process has a new console, instead of inheriting
* its parent's console.
*/
Parameters->ConsoleHandle = NULL; // Do not inherit the parent's console.
}
else if (Parameters->ConsoleHandle == HANDLE_CREATE_NO_WINDOW)
{
/* We'll get the real one soon */
DPRINT("Creating a new invisible console\n");
/*
* The process is a console application that is being run
* without a console window. Therefore, the console handle
* for the application is not set.
*/
Parameters->ConsoleHandle = NULL; // Do not inherit the parent's console.
ConnectInfo.IsWindowVisible = FALSE; // A console is created but is not shown to the user.
}
else
{
DPRINT("Using existing console: 0x%p\n", Parameters->ConsoleHandle);
}
/* Do nothing if this is not a console app... */
if (!ConnectInfo.IsConsoleApp)
{
/* Do not inherit the parent's console if we are not a console app */
Parameters->ConsoleHandle = NULL;
}
/* Now use the proper console handle */
ConnectInfo.ConsoleStartInfo.ConsoleHandle = Parameters->ConsoleHandle;
/* Initialize the console dispatchers */
ConnectInfo.CtrlRoutine = ConsoleControlDispatcher;
ConnectInfo.PropRoutine = PropDialogHandler;
// ConnectInfo.ImeRoutine = ImeRoutine;
/* Set up the console properties */
if (ConnectInfo.IsConsoleApp && Parameters->ConsoleHandle == NULL)
{
/*
* We can set up the console properties only if we create a new one
* (we do not inherit it from our parent).
*/
LPWSTR ConsoleTitle = ConnectInfo.ConsoleTitle;
ConnectInfo.TitleLength = sizeof(ConnectInfo.ConsoleTitle);
ConnectInfo.DesktopLength = 0; // SetUpConsoleInfo will give us the real length.
SetUpConsoleInfo(TRUE,
&ConnectInfo.TitleLength,
&ConsoleTitle,
&ConnectInfo.DesktopLength,
&ConnectInfo.Desktop,
&ConnectInfo.ConsoleStartInfo);
DPRINT("ConsoleTitle = '%S' - Desktop = '%S'\n",
ConsoleTitle, ConnectInfo.Desktop);
}
else
{
ConnectInfo.TitleLength = 0;
ConnectInfo.DesktopLength = 0;
}
/* Initialize the Input EXE name */
if (ConnectInfo.IsConsoleApp)
{
LPWSTR CurDir = ConnectInfo.CurDir;
LPWSTR AppName = ConnectInfo.AppName;
InitExeName();
ConnectInfo.CurDirLength = sizeof(ConnectInfo.CurDir);
ConnectInfo.AppNameLength = sizeof(ConnectInfo.AppName);
SetUpAppName(TRUE,
&ConnectInfo.CurDirLength,
&CurDir,
&ConnectInfo.AppNameLength,
&AppName);
DPRINT("CurDir = '%S' - AppName = '%S'\n",
CurDir, AppName);
}
else
{
ConnectInfo.CurDirLength = 0;
ConnectInfo.AppNameLength = 0;
}
/*
* Initialize Console Ctrl Handling, that needs to be supported by
* all applications, especially because it is used at shutdown.
*/
InitializeCtrlHandling();
/* Connect to the Console Server */
if (!ConnectConsole(SessionDir,
&ConnectInfo,
&InServerProcess))
{
// DPRINT1("Failed to connect to the Console Server (Status %lx)\n", Status);
return FALSE;
}
/* If we are not doing server-to-server init and if this is a console app... */
if (!InServerProcess && ConnectInfo.IsConsoleApp)
{
/* ... set the handles that we got */
if (Parameters->ConsoleHandle == NULL)
SetUpHandles(&ConnectInfo.ConsoleStartInfo);
InputWaitHandle = ConnectInfo.ConsoleStartInfo.InputWaitHandle;
/* Sets the current console locale for this thread */
SetTEBLangID(lcid);
}
DPRINT("Console setup: 0x%p, 0x%p, 0x%p, 0x%p\n",
Parameters->ConsoleHandle,
Parameters->StandardInput,
Parameters->StandardOutput,
Parameters->StandardError);
return TRUE;
}
/* EOF */