reactos/win32ss/user/consrv/console.c
Hermès Bélusca-Maïto fe98b9ea97 [CONSRV]
Fix what I broke in revision r58447, namely at which process the terminal window belongs to.

svn path=/branches/ros-csrss/; revision=58557
2013-03-20 00:41:47 +00:00

1183 lines
40 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Server DLL
* FILE: win32ss/user/consrv/console.c
* PURPOSE: Console I/O functions
* PROGRAMMERS:
*/
/* INCLUDES *******************************************************************/
#define COBJMACROS
#define NONAMELESSUNION
#include "consrv.h"
#include "settings.h"
#include "guiconsole.h"
#ifdef TUI_CONSOLE
#include "tuiconsole.h"
#endif
#include <shlwapi.h>
#include <shlobj.h>
//#define NDEBUG
#include <debug.h>
/* PRIVATE FUNCTIONS **********************************************************/
#ifdef TUI_CONSOLE
static BOOL
DtbgIsDesktopVisible(VOID)
{
return !((BOOL)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE));
}
#endif
static ULONG
ConSrvConsoleCtrlEventTimeout(DWORD Event,
PCONSOLE_PROCESS_DATA ProcessData,
DWORD Timeout)
{
ULONG Status = ERROR_SUCCESS;
HANDLE Thread;
DPRINT("ConSrvConsoleCtrlEvent Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
if (ProcessData->CtrlDispatcher)
{
Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
ProcessData->CtrlDispatcher,
UlongToPtr(Event), 0, NULL);
if (NULL == Thread)
{
Status = GetLastError();
DPRINT1("Failed thread creation (Error: 0x%x)\n", Status);
}
else
{
DPRINT("We succeeded at creating ProcessData->CtrlDispatcher remote thread, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
WaitForSingleObject(Thread, Timeout);
CloseHandle(Thread);
}
}
return Status;
}
static ULONG
ConSrvConsoleCtrlEvent(DWORD Event,
PCONSOLE_PROCESS_DATA ProcessData)
{
return ConSrvConsoleCtrlEventTimeout(Event, ProcessData, 0);
}
ULONG FASTCALL
ConSrvConsoleProcessCtrlEvent(PCONSOLE Console,
ULONG ProcessGroupId,
DWORD Event)
{
ULONG Status = ERROR_SUCCESS;
PLIST_ENTRY current_entry;
PCONSOLE_PROCESS_DATA current;
/*
* Loop through the process list, from the most recent process
* (the active one) to the oldest one (the first created, i.e.
* the console leader process), and for each, send an event
* (new processes are inserted at the head of the console process list).
*/
current_entry = Console->ProcessList.Flink;
while (current_entry != &Console->ProcessList)
{
current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
current_entry = current_entry->Flink;
/*
* Only processes belonging to the same process group are signaled.
* If the process group ID is zero, then all the processes are signaled.
*/
if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
{
Status = ConSrvConsoleCtrlEvent(Event, current);
}
}
return Status;
}
VOID FASTCALL
ConioPause(PCONSOLE Console, UINT Flags)
{
Console->PauseFlags |= Flags;
if (!Console->UnpauseEvent)
Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
}
VOID FASTCALL
ConioUnpause(PCONSOLE Console, UINT Flags)
{
Console->PauseFlags &= ~Flags;
// if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
if (Console->PauseFlags == 0 && Console->UnpauseEvent)
{
SetEvent(Console->UnpauseEvent);
CloseHandle(Console->UnpauseEvent);
Console->UnpauseEvent = NULL;
CsrNotifyWait(&Console->WriteWaitQueue,
WaitAll,
NULL,
NULL);
if (!IsListEmpty(&Console->WriteWaitQueue))
{
CsrDereferenceWait(&Console->WriteWaitQueue);
}
}
}
static BOOL
LoadShellLinkConsoleInfo(IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
OUT PCONSOLE_INFO ConsoleInfo,
OUT LPWSTR IconPath,
IN SIZE_T IconPathLength,
OUT PINT piIcon)
{
#define PATH_SEPARATOR L'\\'
BOOL RetVal = FALSE;
LPWSTR LinkName = NULL;
SIZE_T Length = 0;
if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
return FALSE;
if (IconPath == NULL || piIcon == NULL)
return FALSE;
IconPath[0] = L'\0';
*piIcon = 0;
/* 1- Find the last path separator if any */
LinkName = wcsrchr(ConsoleStartInfo->ConsoleTitle, PATH_SEPARATOR);
if (LinkName == NULL)
{
LinkName = ConsoleStartInfo->ConsoleTitle;
}
else
{
/* Skip the path separator */
++LinkName;
}
/* 2- Check for the link extension. The name ".lnk" is considered invalid. */
Length = wcslen(LinkName);
if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
return FALSE;
/* 3- It may be a link. Try to retrieve some properties */
HRESULT hRes = CoInitialize(NULL);
if (SUCCEEDED(hRes))
{
/* Get a pointer to the IShellLink interface */
IShellLinkW* pshl = NULL;
hRes = CoCreateInstance(&CLSID_ShellLink,
NULL,
CLSCTX_INPROC_SERVER,
&IID_IShellLinkW,
(LPVOID*)&pshl);
if (SUCCEEDED(hRes))
{
/* Get a pointer to the IPersistFile interface */
IPersistFile* ppf = NULL;
hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hRes))
{
/* Load the shortcut */
hRes = IPersistFile_Load(ppf, ConsoleStartInfo->ConsoleTitle, STGM_READ);
if (SUCCEEDED(hRes))
{
/*
* Finally we can get the properties !
* Update the old ones if needed.
*/
INT ShowCmd = 0;
// WORD HotKey = 0;
/* Get the name of the shortcut */
Length = min(Length - 4,
sizeof(ConsoleStartInfo->ConsoleTitle) / sizeof(ConsoleStartInfo->ConsoleTitle[0]) - 1);
wcsncpy(ConsoleStartInfo->ConsoleTitle, LinkName, Length);
ConsoleStartInfo->ConsoleTitle[Length] = L'\0';
// HACK: Copy also the name of the shortcut
Length = min(Length,
sizeof(ConsoleInfo->ConsoleTitle) / sizeof(ConsoleInfo->ConsoleTitle[0]) - 1);
wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
ConsoleInfo->ConsoleTitle[Length] = L'\0';
/* Get the window showing command */
hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
if (SUCCEEDED(hRes)) ConsoleStartInfo->ShowWindow = (WORD)ShowCmd;
/* Get the hotkey */
// hRes = pshl->GetHotkey(&ShowCmd);
// if (SUCCEEDED(hRes)) ConsoleStartInfo->HotKey = HotKey;
/* Get the icon location, if any */
hRes = IShellLinkW_GetIconLocation(pshl, IconPath, IconPathLength, piIcon);
if (!SUCCEEDED(hRes))
{
IconPath[0] = L'\0';
}
// FIXME: Since we still don't load console properties from the shortcut,
// return false. When this will be done, we will return true instead.
RetVal = FALSE;
}
IPersistFile_Release(ppf);
}
IShellLinkW_Release(pshl);
}
}
CoUninitialize();
return RetVal;
}
NTSTATUS WINAPI
ConSrvInitConsole(OUT PCONSOLE* NewConsole,
IN LPCWSTR AppPath,
IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
IN PCSR_PROCESS ConsoleLeaderProcess)
{
NTSTATUS Status;
SECURITY_ATTRIBUTES SecurityAttributes;
CONSOLE_INFO ConsoleInfo;
SIZE_T Length = 0;
DWORD ProcessId = HandleToUlong(ConsoleLeaderProcess->ClientId.UniqueProcess);
PCONSOLE Console;
PCONSOLE_SCREEN_BUFFER NewBuffer;
BOOL GuiMode;
WCHAR Title[128];
WCHAR IconPath[MAX_PATH + 1] = L"";
INT iIcon = 0;
if (NewConsole == NULL) return STATUS_INVALID_PARAMETER;
*NewConsole = NULL;
/*
* Allocate a console structure
*/
Console = RtlAllocateHeap(ConSrvHeap, HEAP_ZERO_MEMORY, sizeof(CONSOLE));
if (NULL == Console)
{
DPRINT1("Not enough memory for console creation.\n");
return STATUS_NO_MEMORY;
}
/*
* Load the console settings
*/
/* 1. Load the default settings */
ConSrvGetDefaultSettings(&ConsoleInfo, ProcessId);
/* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
Length = min(wcslen(ConsoleStartInfo->ConsoleTitle),
sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1);
wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length);
ConsoleInfo.ConsoleTitle[Length] = L'\0';
/*
* 3. Check whether the process creating the
* console was launched via a shell-link
* (ConsoleInfo.ConsoleTitle may be updated).
*/
if (ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME)
{
if (!LoadShellLinkConsoleInfo(ConsoleStartInfo,
&ConsoleInfo,
IconPath,
MAX_PATH,
&iIcon))
{
ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
}
}
/*
* 4. Load the remaining console settings via the registry.
*/
if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
{
/*
* Either we weren't created by an app launched via a shell-link,
* or we failed to load shell-link console properties.
* Therefore, load the console infos for the application from the registry.
*/
ConSrvReadUserSettings(&ConsoleInfo, ProcessId);
/*
* Now, update them with the properties the user might gave to us
* via the STARTUPINFO structure before calling CreateProcess
* (and which was transmitted via the ConsoleStartInfo structure).
*/
if (ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
{
ConsoleInfo.ScreenAttrib = ConsoleStartInfo->FillAttribute;
}
if (ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
{
ConsoleInfo.ScreenBufferSize = ConsoleStartInfo->ScreenBufferSize;
}
if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW)
{
ConsoleInfo.u.GuiInfo.ShowWindow = ConsoleStartInfo->ShowWindow;
}
if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
{
ConsoleInfo.u.GuiInfo.AutoPosition = FALSE;
ConsoleInfo.u.GuiInfo.WindowOrigin = ConsoleStartInfo->ConsoleWindowOrigin;
}
if (ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
{
// ConsoleInfo.ConsoleSize = ConsoleStartInfo->ConsoleWindowSize;
ConsoleInfo.ConsoleSize.X = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cx;
ConsoleInfo.ConsoleSize.Y = (SHORT)ConsoleStartInfo->ConsoleWindowSize.cy;
}
/*
if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
{
}
*/
}
/*
* Initialize the console
*/
InitializeCriticalSection(&Console->Lock);
Console->ReferenceCount = 0;
InitializeListHead(&Console->ProcessList);
memcpy(Console->Colors, ConsoleInfo.Colors, sizeof(ConsoleInfo.Colors));
Console->Size = ConsoleInfo.ConsoleSize;
/*
* Initialize the input buffer
*/
Console->InputBuffer.Header.Type = CONIO_INPUT_BUFFER_MAGIC;
Console->InputBuffer.Header.Console = Console;
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
SecurityAttributes.lpSecurityDescriptor = NULL;
SecurityAttributes.bInheritHandle = TRUE;
Console->InputBuffer.ActiveEvent = CreateEventW(&SecurityAttributes, TRUE, FALSE, NULL);
if (NULL == Console->InputBuffer.ActiveEvent)
{
DeleteCriticalSection(&Console->Lock);
RtlFreeHeap(ConSrvHeap, 0, Console);
return STATUS_UNSUCCESSFUL;
}
// TODO: Use the values from ConsoleInfo.
Console->InputBuffer.Mode = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT |
ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT;
Console->QuickEdit = ConsoleInfo.QuickEdit;
Console->InsertMode = ConsoleInfo.InsertMode;
InitializeListHead(&Console->InputBuffer.ReadWaitQueue);
InitializeListHead(&Console->InputBuffer.InputEvents);
Console->LineBuffer = NULL;
Console->CodePage = GetOEMCP();
Console->OutputCodePage = GetOEMCP();
/* Initialize a new screen buffer with default settings */
InitializeListHead(&Console->BufferList);
Status = ConSrvCreateScreenBuffer(Console,
&NewBuffer,
ConsoleInfo.ScreenBufferSize,
ConsoleInfo.ScreenAttrib,
ConsoleInfo.PopupAttrib,
TRUE,
ConsoleInfo.CursorSize);
if (!NT_SUCCESS(Status))
{
DPRINT1("ConSrvCreateScreenBuffer: failed, Status = 0x%08lx\n", Status);
CloseHandle(Console->InputBuffer.ActiveEvent);
DeleteCriticalSection(&Console->Lock);
RtlFreeHeap(ConSrvHeap, 0, Console);
return Status;
}
/* Make the new screen buffer active */
Console->ActiveBuffer = NewBuffer;
Console->FullScreen = ConsoleInfo.FullScreen;
InitializeListHead(&Console->WriteWaitQueue);
/*
* Initialize the history buffers
*/
InitializeListHead(&Console->HistoryBuffers);
Console->HistoryBufferSize = ConsoleInfo.HistoryBufferSize;
Console->NumberOfHistoryBuffers = ConsoleInfo.NumberOfHistoryBuffers;
Console->HistoryNoDup = ConsoleInfo.HistoryNoDup;
/* Initialize the console title */
RtlCreateUnicodeString(&Console->OriginalTitle, ConsoleStartInfo->ConsoleTitle);
if (ConsoleStartInfo->ConsoleTitle[0] == L'\0')
{
if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, Title, sizeof(Title) / sizeof(Title[0])))
{
RtlCreateUnicodeString(&Console->Title, Title);
}
else
{
RtlCreateUnicodeString(&Console->Title, L"ReactOS Console");
}
}
else
{
RtlCreateUnicodeString(&Console->Title, ConsoleStartInfo->ConsoleTitle);
}
/*
* If we are not in GUI-mode, start the text-mode terminal emulator.
* If we fail, try to start the GUI-mode terminal emulator.
*/
#ifdef TUI_CONSOLE
GuiMode = DtbgIsDesktopVisible();
#else
GuiMode = TRUE;
#endif
#ifdef TUI_CONSOLE
if (!GuiMode)
{
DPRINT1("CONSRV: Opening text-mode terminal emulator\n");
Status = TuiInitConsole(Console, &ConsoleInfo);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to open text-mode terminal emulator, switching to gui-mode, Status = 0x%08lx\n", Status);
GuiMode = TRUE;
}
}
#endif
/*
* Try to open the GUI-mode terminal emulator. Two cases are possible:
* - We are in GUI-mode, therefore GuiMode == TRUE, the previous test-case
* failed and we start GUI-mode terminal emulator.
* - We are in text-mode, therefore GuiMode == FALSE, the previous test-case
* succeeded BUT we failed at starting text-mode terminal emulator.
* Then GuiMode was switched to TRUE in order to try to open the GUI-mode
* terminal emulator (win32k will automatically switch to graphical mode,
* therefore no additional code is needed).
*/
if (GuiMode)
{
DPRINT1("CONSRV: Opening GUI-mode terminal emulator\n");
Status = GuiInitConsole(Console,
AppPath,
&ConsoleInfo,
IconPath,
iIcon);
if (!NT_SUCCESS(Status))
{
DPRINT1("GuiInitConsole: failed, Status = 0x%08lx\n", Status);
RtlFreeUnicodeString(&Console->Title);
RtlFreeUnicodeString(&Console->OriginalTitle);
ConioDeleteScreenBuffer(NewBuffer);
CloseHandle(Console->InputBuffer.ActiveEvent);
DeleteCriticalSection(&Console->Lock);
RtlFreeHeap(ConSrvHeap, 0, Console);
return Status;
}
}
/* Copy buffer contents to screen */
ConioDrawConsole(Console);
/* Return the newly created console to the caller and a success code too */
*NewConsole = Console;
return STATUS_SUCCESS;
}
VOID WINAPI
ConSrvInitConsoleSupport(VOID)
{
DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
/* Should call LoadKeyboardLayout */
}
VOID WINAPI
ConSrvDeleteConsole(PCONSOLE Console)
{
ConsoleInput *Event;
DPRINT("ConSrvDeleteConsole\n");
/* Drain input event queue */
while (Console->InputBuffer.InputEvents.Flink != &Console->InputBuffer.InputEvents)
{
Event = (ConsoleInput *) Console->InputBuffer.InputEvents.Flink;
Console->InputBuffer.InputEvents.Flink = Console->InputBuffer.InputEvents.Flink->Flink;
Console->InputBuffer.InputEvents.Flink->Flink->Blink = &Console->InputBuffer.InputEvents;
RtlFreeHeap(ConSrvHeap, 0, Event);
}
ConioCleanupConsole(Console);
if (Console->LineBuffer)
RtlFreeHeap(ConSrvHeap, 0, Console->LineBuffer);
while (!IsListEmpty(&Console->HistoryBuffers))
HistoryDeleteBuffer((struct _HISTORY_BUFFER *)Console->HistoryBuffers.Flink);
ConioDeleteScreenBuffer(Console->ActiveBuffer);
if (!IsListEmpty(&Console->BufferList))
{
DPRINT1("BUG: screen buffer list not empty\n");
}
CloseHandle(Console->InputBuffer.ActiveEvent);
if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent);
DeleteCriticalSection(&Console->Lock);
RtlFreeUnicodeString(&Console->OriginalTitle);
RtlFreeUnicodeString(&Console->Title);
IntDeleteAllAliases(Console->Aliases);
RtlFreeHeap(ConSrvHeap, 0, Console);
}
/* PUBLIC SERVER APIS *********************************************************/
CSR_API(SrvOpenConsole)
{
NTSTATUS Status = STATUS_SUCCESS;
PCONSOLE_OPENCONSOLE OpenConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.OpenConsoleRequest;
PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
OpenConsoleRequest->ConsoleHandle = INVALID_HANDLE_VALUE;
RtlEnterCriticalSection(&ProcessData->HandleTableLock);
if (ProcessData->Console)
{
DWORD DesiredAccess = OpenConsoleRequest->Access;
DWORD ShareMode = OpenConsoleRequest->ShareMode;
PCONSOLE Console = ProcessData->Console;
Object_t *Object;
EnterCriticalSection(&Console->Lock);
if (OpenConsoleRequest->HandleType == HANDLE_OUTPUT)
{
Object = &Console->ActiveBuffer->Header;
}
else // HANDLE_INPUT
{
Object = &Console->InputBuffer.Header;
}
if (((DesiredAccess & GENERIC_READ) && Object->ExclusiveRead != 0) ||
((DesiredAccess & GENERIC_WRITE) && Object->ExclusiveWrite != 0) ||
(!(ShareMode & FILE_SHARE_READ) && Object->AccessRead != 0) ||
(!(ShareMode & FILE_SHARE_WRITE) && Object->AccessWrite != 0))
{
DPRINT1("Sharing violation\n");
Status = STATUS_SHARING_VIOLATION;
}
else
{
Status = ConSrvInsertObject(ProcessData,
&OpenConsoleRequest->ConsoleHandle,
Object,
DesiredAccess,
OpenConsoleRequest->Inheritable,
ShareMode);
}
LeaveCriticalSection(&Console->Lock);
}
RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
return Status;
}
CSR_API(SrvAllocConsole)
{
NTSTATUS Status = STATUS_SUCCESS;
PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
if (ProcessData->Console != NULL)
{
DPRINT1("Process already has a console\n");
return STATUS_ACCESS_DENIED;
}
if ( !CsrValidateMessageBuffer(ApiMessage,
(PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
1,
sizeof(CONSOLE_START_INFO)) ||
!CsrValidateMessageBuffer(ApiMessage,
(PVOID*)&AllocConsoleRequest->AppPath,
MAX_PATH + 1,
sizeof(WCHAR)) )
{
return STATUS_INVALID_PARAMETER;
}
/*
* We are about to create a new console. However when ConSrvNewProcess
* was called, we didn't know that we wanted to create a new console and
* therefore, we by default inherited the handles table from our parent
* process. It's only now that we notice that in fact we do not need
* them, because we've created a new console and thus we must use it.
*
* Therefore, free the console we can have and our handles table,
* and recreate a new one later on.
*/
ConSrvRemoveConsole(ProcessData);
/* Initialize a new Console owned by the Console Leader Process */
Status = ConSrvAllocateConsole(ProcessData,
AllocConsoleRequest->AppPath,
&AllocConsoleRequest->InputHandle,
&AllocConsoleRequest->OutputHandle,
&AllocConsoleRequest->ErrorHandle,
AllocConsoleRequest->ConsoleStartInfo);
if (!NT_SUCCESS(Status))
{
DPRINT1("Console allocation failed\n");
return Status;
}
/* Return it to the caller */
AllocConsoleRequest->Console = ProcessData->Console;
/* Input Wait Handle */
AllocConsoleRequest->InputWaitHandle = ProcessData->ConsoleEvent;
/* Set the Property Dialog Handler */
ProcessData->PropDispatcher = AllocConsoleRequest->PropDispatcher;
/* Set the Ctrl Dispatcher */
ProcessData->CtrlDispatcher = AllocConsoleRequest->CtrlDispatcher;
return STATUS_SUCCESS;
}
CSR_API(SrvAttachConsole)
{
NTSTATUS Status = STATUS_SUCCESS;
PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
PCSR_PROCESS SourceProcess = NULL; // The parent process.
PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
if (TargetProcessData->Console != NULL)
{
DPRINT1("Process already has a console\n");
return STATUS_ACCESS_DENIED;
}
/* Check whether we try to attach to the parent's console */
if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
{
PROCESS_BASIC_INFORMATION ProcessInfo;
ULONG Length = sizeof(ProcessInfo);
/* Get the real parent's ID */
Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
ProcessBasicInformation,
&ProcessInfo,
Length, &Length);
if (!NT_SUCCESS(Status))
{
DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status);
return Status;
}
ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
}
/* Lock the source process via its PID */
Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
if (!NT_SUCCESS(Status)) return Status;
SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
if (SourceProcessData->Console == NULL)
{
Status = STATUS_INVALID_HANDLE;
goto Quit;
}
/*
* We are about to create a new console. However when ConSrvNewProcess
* was called, we didn't know that we wanted to create a new console and
* therefore, we by default inherited the handles table from our parent
* process. It's only now that we notice that in fact we do not need
* them, because we've created a new console and thus we must use it.
*
* Therefore, free the console we can have and our handles table,
* and recreate a new one later on.
*/
ConSrvRemoveConsole(TargetProcessData);
/*
* Inherit the console from the parent,
* if any, otherwise return an error.
*/
Status = ConSrvInheritConsole(TargetProcessData,
SourceProcessData->Console,
TRUE,
&AttachConsoleRequest->InputHandle,
&AttachConsoleRequest->OutputHandle,
&AttachConsoleRequest->ErrorHandle);
if (!NT_SUCCESS(Status))
{
DPRINT1("Console inheritance failed\n");
goto Quit;
}
/* Return it to the caller */
AttachConsoleRequest->Console = TargetProcessData->Console;
/* Input Wait Handle */
AttachConsoleRequest->InputWaitHandle = TargetProcessData->ConsoleEvent;
/* Set the Property Dialog Handler */
TargetProcessData->PropDispatcher = AttachConsoleRequest->PropDispatcher;
/* Set the Ctrl Dispatcher */
TargetProcessData->CtrlDispatcher = AttachConsoleRequest->CtrlDispatcher;
Status = STATUS_SUCCESS;
Quit:
/* Unlock the "source" process and exit */
CsrUnlockProcess(SourceProcess);
return Status;
}
CSR_API(SrvFreeConsole)
{
DPRINT1("SrvFreeConsole\n");
ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
return STATUS_SUCCESS;
}
CSR_API(SrvSetConsoleMode)
{
#define CONSOLE_INPUT_MODE_VALID ( ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | \
ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | \
ENABLE_MOUSE_INPUT | \
ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS )
#define CONSOLE_OUTPUT_MODE_VALID ( ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT )
NTSTATUS Status;
PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
Object_t* Object = NULL;
Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
ConsoleModeRequest->ConsoleHandle,
&Object, NULL, GENERIC_WRITE, TRUE, 0);
if (!NT_SUCCESS(Status)) return Status;
Status = STATUS_SUCCESS;
if (CONIO_INPUT_BUFFER_MAGIC == Object->Type)
{
PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
InputBuffer->Mode = ConsoleModeRequest->ConsoleMode & CONSOLE_INPUT_MODE_VALID;
}
else if (CONIO_SCREEN_BUFFER_MAGIC == Object->Type)
{
PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
Buffer->Mode = ConsoleModeRequest->ConsoleMode & CONSOLE_OUTPUT_MODE_VALID;
}
else
{
Status = STATUS_INVALID_HANDLE;
}
ConSrvReleaseObject(Object, TRUE);
return Status;
}
CSR_API(SrvGetConsoleMode)
{
NTSTATUS Status;
PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
Object_t* Object = NULL;
Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
ConsoleModeRequest->ConsoleHandle,
&Object, NULL, GENERIC_READ, TRUE, 0);
if (!NT_SUCCESS(Status)) return Status;
Status = STATUS_SUCCESS;
if (CONIO_INPUT_BUFFER_MAGIC == Object->Type)
{
PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
ConsoleModeRequest->ConsoleMode = InputBuffer->Mode;
}
else if (CONIO_SCREEN_BUFFER_MAGIC == Object->Type)
{
PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
ConsoleModeRequest->ConsoleMode = Buffer->Mode;
}
else
{
Status = STATUS_INVALID_HANDLE;
}
ConSrvReleaseObject(Object, TRUE);
return Status;
}
CSR_API(SrvSetConsoleTitle)
{
NTSTATUS Status;
PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
// PCSR_PROCESS Process = CsrGetClientThread()->Process;
PCONSOLE Console;
PWCHAR Buffer;
if (!CsrValidateMessageBuffer(ApiMessage,
(PVOID)&TitleRequest->Title,
TitleRequest->Length,
sizeof(BYTE)))
{
return STATUS_INVALID_PARAMETER;
}
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Can't get console\n");
return Status;
}
/* Allocate a new buffer to hold the new title (NULL-terminated) */
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, TitleRequest->Length + sizeof(WCHAR));
if (Buffer)
{
/* Free the old title */
RtlFreeUnicodeString(&Console->Title);
/* Copy title to console */
Console->Title.Buffer = Buffer;
Console->Title.Length = TitleRequest->Length;
Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
RtlCopyMemory(Console->Title.Buffer,
TitleRequest->Title,
Console->Title.Length);
Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0';
ConioChangeTitle(Console);
Status = STATUS_SUCCESS;
}
else
{
Status = STATUS_NO_MEMORY;
}
ConSrvReleaseConsole(Console, TRUE);
return Status;
}
CSR_API(SrvGetConsoleTitle)
{
NTSTATUS Status;
PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
// PCSR_PROCESS Process = CsrGetClientThread()->Process;
PCONSOLE Console;
DWORD Length;
if (!CsrValidateMessageBuffer(ApiMessage,
(PVOID)&TitleRequest->Title,
TitleRequest->Length,
sizeof(BYTE)))
{
return STATUS_INVALID_PARAMETER;
}
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Can't get console\n");
return Status;
}
/* Copy title of the console to the user title buffer */
if (TitleRequest->Length >= sizeof(WCHAR))
{
Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
memcpy(TitleRequest->Title, Console->Title.Buffer, Length);
TitleRequest->Title[Length / sizeof(WCHAR)] = L'\0';
}
TitleRequest->Length = Console->Title.Length;
ConSrvReleaseConsole(Console, TRUE);
return STATUS_SUCCESS;
}
/**********************************************************************
* HardwareStateProperty
*
* DESCRIPTION
* Set/Get the value of the HardwareState and switch
* between direct video buffer ouput and GDI windowed
* output.
* ARGUMENTS
* Client hands us a CONSOLE_GETSETHWSTATE object.
* We use the same object to Request.
* NOTE
* ConsoleHwState has the correct size to be compatible
* with NT's, but values are not.
*/
static NTSTATUS FASTCALL
SetConsoleHardwareState(PCONSOLE Console, DWORD ConsoleHwState)
{
DPRINT1("Console Hardware State: %d\n", ConsoleHwState);
if ((CONSOLE_HARDWARE_STATE_GDI_MANAGED == ConsoleHwState)
||(CONSOLE_HARDWARE_STATE_DIRECT == ConsoleHwState))
{
if (Console->HardwareState != ConsoleHwState)
{
/* TODO: implement switching from full screen to windowed mode */
/* TODO: or back; now simply store the hardware state */
Console->HardwareState = ConsoleHwState;
}
return STATUS_SUCCESS;
}
return STATUS_INVALID_PARAMETER_3; /* Client: (handle, set_get, [mode]) */
}
CSR_API(SrvGetConsoleHardwareState)
{
NTSTATUS Status;
PCONSOLE_GETSETHWSTATE HardwareStateRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HardwareStateRequest;
PCONSOLE_SCREEN_BUFFER Buff;
PCONSOLE Console;
Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
HardwareStateRequest->OutputHandle,
&Buff,
GENERIC_READ,
TRUE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to get console handle in SrvGetConsoleHardwareState\n");
return Status;
}
Console = Buff->Header.Console;
HardwareStateRequest->State = Console->HardwareState;
ConSrvReleaseScreenBuffer(Buff, TRUE);
return Status;
}
CSR_API(SrvSetConsoleHardwareState)
{
NTSTATUS Status;
PCONSOLE_GETSETHWSTATE HardwareStateRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.HardwareStateRequest;
PCONSOLE_SCREEN_BUFFER Buff;
PCONSOLE Console;
Status = ConSrvGetScreenBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
HardwareStateRequest->OutputHandle,
&Buff,
GENERIC_READ,
TRUE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to get console handle in SrvSetConsoleHardwareState\n");
return Status;
}
DPRINT("Setting console hardware state.\n");
Console = Buff->Header.Console;
Status = SetConsoleHardwareState(Console, HardwareStateRequest->State);
ConSrvReleaseScreenBuffer(Buff, TRUE);
return Status;
}
CSR_API(SrvGetConsoleWindow)
{
NTSTATUS Status;
PCONSOLE_GETWINDOW GetWindowRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetWindowRequest;
PCONSOLE Console;
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
if (!NT_SUCCESS(Status)) return Status;
GetWindowRequest->WindowHandle = ConioGetConsoleWindowHandle(Console);
ConSrvReleaseConsole(Console, TRUE);
return STATUS_SUCCESS;
}
CSR_API(SrvSetConsoleIcon)
{
NTSTATUS Status;
PCONSOLE_SETICON SetIconRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetIconRequest;
PCONSOLE Console;
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
if (!NT_SUCCESS(Status)) return Status;
Status = (ConioChangeIcon(Console, SetIconRequest->WindowIcon)
? STATUS_SUCCESS
: STATUS_UNSUCCESSFUL);
ConSrvReleaseConsole(Console, TRUE);
return Status;
}
CSR_API(SrvGetConsoleCP)
{
NTSTATUS Status;
PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleCPRequest;
PCONSOLE Console;
DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
ConsoleCPRequest->InputCP ? "Input" : "Output");
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
if (!NT_SUCCESS(Status)) return Status;
ConsoleCPRequest->CodePage = (ConsoleCPRequest->InputCP ? Console->CodePage
: Console->OutputCodePage);
ConSrvReleaseConsole(Console, TRUE);
return STATUS_SUCCESS;
}
CSR_API(SrvSetConsoleCP)
{
NTSTATUS Status;
PCONSOLE_GETSETINPUTOUTPUTCP ConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleCPRequest;
PCONSOLE Console;
DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
ConsoleCPRequest->InputCP ? "Input" : "Output");
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
if (!NT_SUCCESS(Status)) return Status;
if (IsValidCodePage(ConsoleCPRequest->CodePage))
{
if (ConsoleCPRequest->InputCP)
Console->CodePage = ConsoleCPRequest->CodePage;
else
Console->OutputCodePage = ConsoleCPRequest->CodePage;
ConSrvReleaseConsole(Console, TRUE);
return STATUS_SUCCESS;
}
ConSrvReleaseConsole(Console, TRUE);
return STATUS_INVALID_PARAMETER;
}
CSR_API(SrvGetConsoleProcessList)
{
NTSTATUS Status;
PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
PDWORD Buffer;
// PCSR_PROCESS Process = CsrGetClientThread()->Process;
PCONSOLE Console;
PCONSOLE_PROCESS_DATA current;
PLIST_ENTRY current_entry;
ULONG nItems = 0;
if (!CsrValidateMessageBuffer(ApiMessage,
(PVOID)&GetProcessListRequest->pProcessIds,
GetProcessListRequest->nMaxIds,
sizeof(DWORD)))
{
return STATUS_INVALID_PARAMETER;
}
Buffer = GetProcessListRequest->pProcessIds;
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
if (!NT_SUCCESS(Status)) return Status;
for (current_entry = Console->ProcessList.Flink;
current_entry != &Console->ProcessList;
current_entry = current_entry->Flink)
{
current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
if (++nItems <= GetProcessListRequest->nMaxIds)
{
*Buffer++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
}
}
ConSrvReleaseConsole(Console, TRUE);
GetProcessListRequest->nProcessIdsTotal = nItems;
return STATUS_SUCCESS;
}
CSR_API(SrvGenerateConsoleCtrlEvent)
{
NTSTATUS Status;
PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
PCONSOLE Console;
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
if (!NT_SUCCESS(Status)) return Status;
Status = ConSrvConsoleProcessCtrlEvent(Console,
GenerateCtrlEventRequest->ProcessGroup,
GenerateCtrlEventRequest->Event);
ConSrvReleaseConsole(Console, TRUE);
return Status;
}
CSR_API(SrvGetConsoleSelectionInfo)
{
NTSTATUS Status;
PCONSOLE_GETSELECTIONINFO GetSelectionInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetSelectionInfoRequest;
PCONSOLE Console;
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
if (NT_SUCCESS(Status))
{
memset(&GetSelectionInfoRequest->Info, 0, sizeof(CONSOLE_SELECTION_INFO));
if (Console->Selection.dwFlags != 0)
GetSelectionInfoRequest->Info = Console->Selection;
ConSrvReleaseConsole(Console, TRUE);
}
return Status;
}
/* EOF */