mirror of
https://github.com/reactos/reactos.git
synced 2024-08-28 22:28:36 +00:00
1586 lines
52 KiB
C
1586 lines
52 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Console Server DLL
|
|
* FILE: win32ss/user/winsrv/consrv/console.c
|
|
* PURPOSE: Console Management Functions
|
|
* PROGRAMMERS: Gé van Geldorp
|
|
* Jeffrey Morlan
|
|
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "consrv.h"
|
|
|
|
/* This is for COM usage */
|
|
#define COBJMACROS
|
|
#include <shlobj.h>
|
|
|
|
|
|
#include <alias.h>
|
|
#include <history.h>
|
|
#include "procinit.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
/* The list of the ConSrv consoles */
|
|
static ULONG ConsoleListSize;
|
|
static PCONSRV_CONSOLE* ConsoleList;
|
|
static RTL_RESOURCE ListLock;
|
|
|
|
#define ConSrvLockConsoleListExclusive() \
|
|
RtlAcquireResourceExclusive(&ListLock, TRUE)
|
|
|
|
#define ConSrvLockConsoleListShared() \
|
|
RtlAcquireResourceShared(&ListLock, TRUE)
|
|
|
|
#define ConSrvUnlockConsoleList() \
|
|
RtlReleaseResource(&ListLock)
|
|
|
|
|
|
static NTSTATUS
|
|
InsertConsole(OUT PHANDLE Handle,
|
|
IN PCONSRV_CONSOLE Console)
|
|
{
|
|
#define CONSOLE_HANDLES_INCREMENT 2 * 3
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG i = 0;
|
|
PCONSRV_CONSOLE* Block;
|
|
|
|
ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
|
|
(ConsoleList != NULL && ConsoleListSize != 0) );
|
|
|
|
/* All went right, so add the console to the list */
|
|
ConSrvLockConsoleListExclusive();
|
|
DPRINT("Insert in the list\n");
|
|
|
|
if (ConsoleList)
|
|
{
|
|
for (i = 0; i < ConsoleListSize; i++)
|
|
{
|
|
if (ConsoleList[i] == NULL) break;
|
|
}
|
|
}
|
|
|
|
if (i >= ConsoleListSize)
|
|
{
|
|
DPRINT("Creation of a new handles table\n");
|
|
/* Allocate a new handles table */
|
|
Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
|
|
(ConsoleListSize +
|
|
CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSRV_CONSOLE));
|
|
if (Block == NULL)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Quit;
|
|
}
|
|
|
|
/* If we previously had a handles table, free it and use the new one */
|
|
if (ConsoleList)
|
|
{
|
|
/* Copy the handles from the old table to the new one */
|
|
RtlCopyMemory(Block,
|
|
ConsoleList,
|
|
ConsoleListSize * sizeof(PCONSRV_CONSOLE));
|
|
ConsoleFreeHeap(ConsoleList);
|
|
}
|
|
ConsoleList = Block;
|
|
ConsoleListSize += CONSOLE_HANDLES_INCREMENT;
|
|
}
|
|
|
|
ConsoleList[i] = Console;
|
|
*Handle = ULongToHandle((i << 2) | 0x3);
|
|
|
|
Quit:
|
|
/* Unlock the console list and return status */
|
|
ConSrvUnlockConsoleList();
|
|
return Status;
|
|
}
|
|
|
|
/* Unused */
|
|
#if 0
|
|
static NTSTATUS
|
|
RemoveConsoleByHandle(IN HANDLE Handle)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
BOOLEAN ValidHandle = ((HandleToULong(Handle) & 0x3) == 0x3);
|
|
ULONG Index = HandleToULong(Handle) >> 2;
|
|
|
|
if (!ValidHandle) return STATUS_INVALID_HANDLE;
|
|
|
|
ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
|
|
(ConsoleList != NULL && ConsoleListSize != 0) );
|
|
|
|
/* Remove the console from the list */
|
|
ConSrvLockConsoleListExclusive();
|
|
|
|
if (Index >= ConsoleListSize ||
|
|
(Console = ConsoleList[Index]) == NULL)
|
|
{
|
|
Status = STATUS_INVALID_HANDLE;
|
|
goto Quit;
|
|
}
|
|
|
|
ConsoleList[Index] = NULL;
|
|
|
|
Quit:
|
|
/* Unlock the console list and return status */
|
|
ConSrvUnlockConsoleList();
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
static NTSTATUS
|
|
RemoveConsoleByPointer(IN PCONSRV_CONSOLE Console)
|
|
{
|
|
ULONG i = 0;
|
|
|
|
if (!Console) return STATUS_INVALID_PARAMETER;
|
|
|
|
ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
|
|
(ConsoleList != NULL && ConsoleListSize != 0) );
|
|
|
|
/* Remove the console from the list */
|
|
ConSrvLockConsoleListExclusive();
|
|
|
|
if (ConsoleList)
|
|
{
|
|
for (i = 0; i < ConsoleListSize; i++)
|
|
{
|
|
if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
|
|
}
|
|
}
|
|
|
|
/* Unlock the console list and return */
|
|
ConSrvUnlockConsoleList();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
ConSrvValidateConsole(OUT PCONSRV_CONSOLE* Console,
|
|
IN HANDLE ConsoleHandle,
|
|
IN CONSOLE_STATE ExpectedState,
|
|
IN BOOLEAN LockConsole)
|
|
{
|
|
BOOLEAN RetVal = FALSE;
|
|
PCONSRV_CONSOLE ValidatedConsole;
|
|
|
|
BOOLEAN ValidHandle = ((HandleToULong(ConsoleHandle) & 0x3) == 0x3);
|
|
ULONG Index = HandleToULong(ConsoleHandle) >> 2;
|
|
|
|
if (!ValidHandle) return FALSE;
|
|
|
|
if (!Console) return FALSE;
|
|
*Console = NULL;
|
|
|
|
/*
|
|
* Forbid creation or deletion of consoles when
|
|
* checking for the existence of a console.
|
|
*/
|
|
ConSrvLockConsoleListShared();
|
|
|
|
if (Index >= ConsoleListSize ||
|
|
(ValidatedConsole = ConsoleList[Index]) == NULL)
|
|
{
|
|
/* Unlock the console list and return */
|
|
ConSrvUnlockConsoleList();
|
|
return FALSE;
|
|
}
|
|
|
|
ValidatedConsole = ConsoleList[Index];
|
|
|
|
/* Unlock the console list and return */
|
|
ConSrvUnlockConsoleList();
|
|
|
|
RetVal = ConDrvValidateConsoleUnsafe((PCONSOLE)ValidatedConsole,
|
|
ExpectedState,
|
|
LockConsole);
|
|
if (RetVal) *Console = ValidatedConsole;
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
// Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
|
|
static BOOLEAN
|
|
ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,
|
|
IN PCWSTR Source)
|
|
{
|
|
SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR);
|
|
if (Size > MAXUSHORT) return FALSE;
|
|
|
|
UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size);
|
|
if (UniDest->Buffer == NULL) return FALSE;
|
|
|
|
RtlCopyMemory(UniDest->Buffer, Source, Size);
|
|
UniDest->MaximumLength = (USHORT)Size;
|
|
UniDest->Length = (USHORT)Size - sizeof(WCHAR);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
|
|
static VOID
|
|
ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
|
|
{
|
|
if (UnicodeString->Buffer)
|
|
{
|
|
ConsoleFreeHeap(UnicodeString->Buffer);
|
|
RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ConioPause(PCONSRV_CONSOLE Console, UINT Flags)
|
|
{
|
|
Console->PauseFlags |= Flags;
|
|
ConDrvPause((PCONSOLE)Console);
|
|
}
|
|
|
|
VOID
|
|
ConioUnpause(PCONSRV_CONSOLE Console, UINT Flags)
|
|
{
|
|
Console->PauseFlags &= ~Flags;
|
|
|
|
// if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
|
|
if (Console->PauseFlags == 0)
|
|
{
|
|
ConDrvUnpause((PCONSOLE)Console);
|
|
|
|
CsrNotifyWait(&Console->WriteWaitQueue,
|
|
TRUE,
|
|
NULL,
|
|
NULL);
|
|
if (!IsListEmpty(&Console->WriteWaitQueue))
|
|
{
|
|
CsrDereferenceWait(&Console->WriteWaitQueue);
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
ConSrvGetConsole(IN PCONSOLE_PROCESS_DATA ProcessData,
|
|
OUT PCONSRV_CONSOLE* Console,
|
|
IN BOOLEAN LockConsole)
|
|
{
|
|
NTSTATUS Status = STATUS_INVALID_HANDLE;
|
|
PCONSRV_CONSOLE GrabConsole;
|
|
|
|
// if (Console == NULL) return STATUS_INVALID_PARAMETER;
|
|
ASSERT(Console);
|
|
*Console = NULL;
|
|
|
|
if (ConSrvValidateConsole(&GrabConsole,
|
|
ProcessData->ConsoleHandle,
|
|
CONSOLE_RUNNING,
|
|
LockConsole))
|
|
{
|
|
InterlockedIncrement(&GrabConsole->ReferenceCount);
|
|
*Console = GrabConsole;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
ConSrvReleaseConsole(IN PCONSRV_CONSOLE Console,
|
|
IN BOOLEAN WasConsoleLocked)
|
|
{
|
|
LONG RefCount = 0;
|
|
|
|
if (!Console) return;
|
|
// if (Console->ReferenceCount == 0) return; // This shouldn't happen
|
|
ASSERT(Console->ReferenceCount > 0);
|
|
|
|
/* The console must be locked */
|
|
// ASSERT(Console_locked);
|
|
|
|
/*
|
|
* Decrement the reference count. Save the new value too,
|
|
* because Console->ReferenceCount might be modified after
|
|
* the console gets unlocked but before we check whether we
|
|
* can destroy it.
|
|
*/
|
|
RefCount = _InterlockedDecrement(&Console->ReferenceCount);
|
|
|
|
/* Unlock the console if needed */
|
|
if (WasConsoleLocked) LeaveCriticalSection(&Console->Lock);
|
|
|
|
/* Delete the console if needed */
|
|
if (RefCount <= 0) ConSrvDeleteConsole(Console);
|
|
}
|
|
|
|
|
|
/* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
|
|
|
|
VOID NTAPI
|
|
ConSrvInitConsoleSupport(VOID)
|
|
{
|
|
DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
|
|
|
|
/* Initialize the console list and its lock */
|
|
ConsoleListSize = 0;
|
|
ConsoleList = NULL;
|
|
RtlInitializeResource(&ListLock);
|
|
|
|
/* Should call LoadKeyboardLayout */
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
|
|
IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
|
|
IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
|
|
IN HANDLE ConsoleLeaderProcessHandle);
|
|
NTSTATUS NTAPI
|
|
ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal);
|
|
|
|
|
|
static BOOL
|
|
LoadShellLinkConsoleInfo(IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
|
|
IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo)
|
|
{
|
|
#define PATH_SEPARATOR L'\\'
|
|
|
|
BOOL RetVal = FALSE;
|
|
HRESULT hRes = S_OK;
|
|
SIZE_T Length = 0;
|
|
LPWSTR LinkName = NULL;
|
|
LPWSTR IconPath = NULL;
|
|
WCHAR Buffer[MAX_PATH + 1];
|
|
|
|
ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
|
|
|
|
if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
|
|
{
|
|
// return FALSE; // FIXME!! (for icon loading)
|
|
RetVal = TRUE;
|
|
goto Finish;
|
|
}
|
|
|
|
/* 1- Find the last path separator if any */
|
|
LinkName = wcsrchr(ConsoleInfo->ConsoleTitle, PATH_SEPARATOR);
|
|
if (LinkName == NULL)
|
|
LinkName = ConsoleInfo->ConsoleTitle;
|
|
else
|
|
++LinkName; // Skip the path separator
|
|
|
|
/* 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 */
|
|
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, ConsoleInfo->ConsoleTitle, STGM_READ);
|
|
if (SUCCEEDED(hRes))
|
|
{
|
|
/*
|
|
* Finally we can get the properties !
|
|
* Update the old ones if needed.
|
|
*/
|
|
INT ShowCmd = 0;
|
|
// WORD HotKey = 0;
|
|
|
|
/* Reset the name of the console with the name of the shortcut */
|
|
Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
|
|
(ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR));
|
|
wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
|
|
ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL;
|
|
|
|
/* Get the window showing command */
|
|
hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
|
|
if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->wShowWindow = (WORD)ShowCmd;
|
|
|
|
/* Get the hotkey */
|
|
// hRes = pshl->GetHotkey(&ShowCmd);
|
|
// if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->HotKey = HotKey;
|
|
|
|
/* Get the icon location, if any */
|
|
hRes = IShellLinkW_GetIconLocation(pshl,
|
|
Buffer,
|
|
sizeof(Buffer)/sizeof(Buffer[0]) - 1, // == MAX_PATH
|
|
&ConsoleInitInfo->ConsoleStartInfo->IconIndex);
|
|
if (!SUCCEEDED(hRes))
|
|
{
|
|
ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
IconPath = Buffer;
|
|
}
|
|
|
|
// 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 = TRUE; // FALSE;
|
|
}
|
|
IPersistFile_Release(ppf);
|
|
}
|
|
IShellLinkW_Release(pshl);
|
|
}
|
|
}
|
|
CoUninitialize();
|
|
|
|
Finish:
|
|
|
|
if (RetVal)
|
|
{
|
|
/* Get the associated icon, if any */
|
|
if (IconPath == NULL)
|
|
{
|
|
// Question: How to retrieve the full path name
|
|
// of the app we are going to run??
|
|
Length = RtlDosSearchPath_U(ConsoleInitInfo->CurDir,
|
|
ConsoleInitInfo->AppName,
|
|
NULL,
|
|
sizeof(Buffer),
|
|
Buffer,
|
|
NULL);
|
|
if (Length > 0 && Length < sizeof(Buffer))
|
|
IconPath = Buffer;
|
|
else
|
|
IconPath = ConsoleInitInfo->AppName;
|
|
|
|
// ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
|
|
}
|
|
DPRINT("IconPath = '%S' ; IconIndex = %lu\n",
|
|
IconPath, ConsoleInitInfo->ConsoleStartInfo->IconIndex);
|
|
if (IconPath && *IconPath)
|
|
{
|
|
HICON hIcon = NULL, hIconSm = NULL;
|
|
/*
|
|
* FIXME!! Because of a strange bug we have in PrivateExtractIconExW
|
|
* (see r65683 for more details), we cannot use this API to extract
|
|
* at the same time the large and small icons from the app.
|
|
* Instead we just use PrivateExtractIconsW.
|
|
*
|
|
PrivateExtractIconExW(IconPath,
|
|
ConsoleInitInfo->ConsoleStartInfo->IconIndex,
|
|
&hIcon,
|
|
&hIconSm,
|
|
1);
|
|
*/
|
|
PrivateExtractIconsW(IconPath,
|
|
ConsoleInitInfo->ConsoleStartInfo->IconIndex,
|
|
32, 32,
|
|
&hIcon, NULL, 1, LR_COPYFROMRESOURCE);
|
|
PrivateExtractIconsW(IconPath,
|
|
ConsoleInitInfo->ConsoleStartInfo->IconIndex,
|
|
16, 16,
|
|
&hIconSm, NULL, 1, LR_COPYFROMRESOURCE);
|
|
|
|
DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
|
|
if (hIcon != NULL) ConsoleInitInfo->ConsoleStartInfo->hIcon = hIcon;
|
|
if (hIconSm != NULL) ConsoleInitInfo->ConsoleStartInfo->hIconSm = hIconSm;
|
|
}
|
|
}
|
|
|
|
// FIXME: See the previous FIXME above.
|
|
RetVal = FALSE;
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,
|
|
OUT PCONSRV_CONSOLE* NewConsole,
|
|
IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
|
|
IN PCSR_PROCESS ConsoleLeaderProcess)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE ConsoleHandle;
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
BYTE ConsoleInfoBuffer[sizeof(CONSOLE_STATE_INFO) + MAX_PATH * sizeof(WCHAR)]; // CONSRV console information
|
|
PCONSOLE_STATE_INFO ConsoleInfo = (PCONSOLE_STATE_INFO)&ConsoleInfoBuffer;
|
|
CONSOLE_INFO DrvConsoleInfo; // Console information for CONDRV
|
|
|
|
SIZE_T Length = 0;
|
|
|
|
TERMINAL Terminal; /* The ConSrv terminal for this console */
|
|
|
|
if (NewConsole == NULL || ConsoleInitInfo == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
*NewConsole = NULL;
|
|
|
|
/*
|
|
* Load the console settings
|
|
*/
|
|
RtlZeroMemory(ConsoleInfo, sizeof(ConsoleInfoBuffer));
|
|
ConsoleInfo->cbSize = sizeof(ConsoleInfoBuffer);
|
|
|
|
/* 1. Get the title of the console (initialize ConsoleInfo->ConsoleTitle) */
|
|
Length = min(ConsoleInitInfo->TitleLength,
|
|
(ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR));
|
|
wcsncpy(ConsoleInfo->ConsoleTitle, ConsoleInitInfo->ConsoleTitle, Length);
|
|
ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL; // NULL-terminate it.
|
|
|
|
/* 2. Impersonate the caller in order to retrieve settings in its context */
|
|
if (!CsrImpersonateClient(NULL))
|
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|
|
|
/* 3. Load the default settings */
|
|
ConCfgGetDefaultSettings(ConsoleInfo);
|
|
|
|
/*
|
|
* 4. Load per-application terminal settings.
|
|
*
|
|
* Check whether the process creating the console was launched via
|
|
* a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
|
|
* name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
|
|
*/
|
|
// if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) // FIXME!! (for icon loading)
|
|
{
|
|
if (!LoadShellLinkConsoleInfo(ConsoleInfo, ConsoleInitInfo))
|
|
{
|
|
ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 5. Load the remaining console settings via the registry.
|
|
*/
|
|
if ((ConsoleInitInfo->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.
|
|
*/
|
|
ConCfgReadUserSettings(ConsoleInfo, FALSE);
|
|
|
|
/*
|
|
* 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).
|
|
* We therefore overwrite the values read in the registry.
|
|
*/
|
|
if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
|
|
{
|
|
ConsoleInfo->ScreenAttributes = (USHORT)ConsoleInitInfo->ConsoleStartInfo->wFillAttribute;
|
|
}
|
|
if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
|
|
{
|
|
ConsoleInfo->ScreenBufferSize = ConsoleInitInfo->ConsoleStartInfo->dwScreenBufferSize;
|
|
}
|
|
if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
|
|
{
|
|
ConsoleInfo->WindowSize = ConsoleInitInfo->ConsoleStartInfo->dwWindowSize;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* 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).
|
|
* We therefore overwrite the values read in the registry.
|
|
*/
|
|
if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
|
|
{
|
|
ConsoleInfo->AutoPosition = FALSE;
|
|
ConsoleInfo->WindowPosition.x = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.X;
|
|
ConsoleInfo->WindowPosition.y = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.Y;
|
|
}
|
|
if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
|
|
{
|
|
ConsoleInfo->FullScreen = TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* 6. Revert impersonation */
|
|
CsrRevertToSelf();
|
|
|
|
/* Set-up the code page */
|
|
ConsoleInfo->CodePage = GetOEMCP();
|
|
|
|
/*
|
|
* Initialize the ConSrv terminal and give it a chance to load
|
|
* its own settings and override the console settings.
|
|
*/
|
|
Status = ConSrvInitTerminal(&Terminal,
|
|
ConsoleInfo,
|
|
ConsoleInitInfo,
|
|
ConsoleLeaderProcess->ProcessHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("CONSRV: Failed to initialize a terminal, Status = 0x%08lx\n", Status);
|
|
return Status;
|
|
}
|
|
DPRINT("CONSRV: Terminal initialized\n");
|
|
|
|
/* Initialize a new console via the driver */
|
|
// DrvConsoleInfo.InputBufferSize = 0;
|
|
DrvConsoleInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
|
|
DrvConsoleInfo.ConsoleSize = ConsoleInfo->WindowSize;
|
|
DrvConsoleInfo.CursorSize = ConsoleInfo->CursorSize;
|
|
// DrvConsoleInfo.CursorBlinkOn = ConsoleInfo->CursorBlinkOn;
|
|
DrvConsoleInfo.ScreenAttrib = ConsoleInfo->ScreenAttributes;
|
|
DrvConsoleInfo.PopupAttrib = ConsoleInfo->PopupAttributes;
|
|
DrvConsoleInfo.CodePage = ConsoleInfo->CodePage;
|
|
Status = ConDrvInitConsole(&Console, &DrvConsoleInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Creating a new console failed, Status = 0x%08lx\n", Status);
|
|
ConSrvDeinitTerminal(&Terminal);
|
|
return Status;
|
|
}
|
|
|
|
ASSERT(Console);
|
|
DPRINT("Console initialized\n");
|
|
|
|
/*** Register ConSrv features ***/
|
|
|
|
/* Initialize the console title */
|
|
#if 0
|
|
WCHAR DefaultTitle[128];
|
|
#endif
|
|
ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle);
|
|
#if 0
|
|
if (ConsoleInfo->ConsoleTitle[0] == UNICODE_NULL)
|
|
{
|
|
if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
|
|
{
|
|
ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
|
|
}
|
|
else
|
|
{
|
|
ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle);
|
|
#if 0
|
|
}
|
|
#endif
|
|
|
|
/* Initialize process support */
|
|
InitializeListHead(&Console->ProcessList);
|
|
Console->NotifiedLastCloseProcess = NULL;
|
|
Console->NotifyLastClose = FALSE;
|
|
Console->HasFocus = FALSE;
|
|
|
|
/* Initialize pausing support */
|
|
Console->PauseFlags = 0;
|
|
InitializeListHead(&Console->ReadWaitQueue);
|
|
InitializeListHead(&Console->WriteWaitQueue);
|
|
|
|
/* Initialize the alias and history buffers */
|
|
Console->Aliases = NULL;
|
|
InitializeListHead(&Console->HistoryBuffers);
|
|
Console->HistoryBufferSize = ConsoleInfo->HistoryBufferSize;
|
|
Console->NumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers;
|
|
Console->HistoryNoDup = ConsoleInfo->HistoryNoDup;
|
|
|
|
/* Initialize the Input Line Discipline */
|
|
Console->LineBuffer = NULL;
|
|
Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
|
|
Console->LineComplete = Console->LineUpPressed = FALSE;
|
|
// LineWakeupMask
|
|
Console->LineInsertToggle =
|
|
Console->InsertMode = ConsoleInfo->InsertMode;
|
|
Console->QuickEdit = ConsoleInfo->QuickEdit;
|
|
|
|
/* Popup windows */
|
|
InitializeListHead(&Console->PopupWindows);
|
|
|
|
/* Colour table */
|
|
RtlCopyMemory(Console->Colors, ConsoleInfo->ColorTable,
|
|
sizeof(ConsoleInfo->ColorTable));
|
|
|
|
/* Create the Initialization Events */
|
|
Status = NtCreateEvent(&Console->InitEvents[INIT_SUCCESS], EVENT_ALL_ACCESS,
|
|
NULL, NotificationEvent, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtCreateEvent(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
|
|
ConDrvDeleteConsole(Console);
|
|
ConSrvDeinitTerminal(&Terminal);
|
|
return Status;
|
|
}
|
|
Status = NtCreateEvent(&Console->InitEvents[INIT_FAILURE], EVENT_ALL_ACCESS,
|
|
NULL, NotificationEvent, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtCreateEvent(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
|
|
NtClose(Console->InitEvents[INIT_SUCCESS]);
|
|
ConDrvDeleteConsole(Console);
|
|
ConSrvDeinitTerminal(&Terminal);
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* Attach the ConSrv terminal to the console.
|
|
* This call makes a copy of our local Terminal variable.
|
|
*/
|
|
Status = ConDrvAttachTerminal(Console, &Terminal);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
|
|
NtClose(Console->InitEvents[INIT_FAILURE]);
|
|
NtClose(Console->InitEvents[INIT_SUCCESS]);
|
|
ConDrvDeleteConsole(Console);
|
|
ConSrvDeinitTerminal(&Terminal);
|
|
return Status;
|
|
}
|
|
DPRINT("Terminal attached\n");
|
|
|
|
/* All went right, so add the console to the list */
|
|
Status = InsertConsole(&ConsoleHandle, Console);
|
|
|
|
// FIXME! We do not support at all asynchronous console creation!
|
|
NtSetEvent(Console->InitEvents[INIT_SUCCESS], NULL);
|
|
// NtSetEvent(Console->InitEvents[INIT_FAILURE], NULL);
|
|
|
|
/* Return the newly created console to the caller and a success code too */
|
|
*NewConsoleHandle = ConsoleHandle;
|
|
*NewConsole = Console;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID NTAPI
|
|
ConSrvDeleteConsole(PCONSRV_CONSOLE Console)
|
|
{
|
|
DPRINT("ConSrvDeleteConsole\n");
|
|
|
|
// FIXME: Send a terminate message to all the processes owning this console
|
|
|
|
/* Remove the console from the list */
|
|
RemoveConsoleByPointer(Console);
|
|
|
|
/* Destroy the Initialization Events */
|
|
NtClose(Console->InitEvents[INIT_FAILURE]);
|
|
NtClose(Console->InitEvents[INIT_SUCCESS]);
|
|
|
|
/* Clean the Input Line Discipline */
|
|
if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
|
|
|
|
/* Clean aliases and history */
|
|
IntDeleteAllAliases(Console);
|
|
HistoryDeleteBuffers(Console);
|
|
|
|
/* Free the console title */
|
|
ConsoleFreeUnicodeString(&Console->OriginalTitle);
|
|
ConsoleFreeUnicodeString(&Console->Title);
|
|
|
|
/* Now, call the driver. ConDrvDetachTerminal is called on-demand. */
|
|
ConDrvDeleteConsole((PCONSOLE)Console);
|
|
|
|
/* Deinit the ConSrv terminal */
|
|
// FIXME!!
|
|
// ConSrvDeinitTerminal(&Terminal);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static NTSTATUS
|
|
ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
|
|
IN PCONSOLE_PROCESS_DATA ProcessData,
|
|
IN ULONG Timeout)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
|
|
|
|
/*
|
|
* Be sure we effectively have a control routine. It resides in kernel32.dll (client).
|
|
*/
|
|
if (ProcessData->CtrlRoutine == NULL) return Status;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
HANDLE Thread = NULL;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
|
|
ProcessData->CtrlRoutine,
|
|
UlongToPtr(CtrlEvent), 0, NULL);
|
|
if (NULL == Thread)
|
|
{
|
|
Status = RtlGetLastNtStatus();
|
|
DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
|
|
}
|
|
else
|
|
{
|
|
DPRINT("ProcessData->CtrlRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n",
|
|
ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
|
|
WaitForSingleObject(Thread, Timeout);
|
|
}
|
|
}
|
|
_SEH2_FINALLY
|
|
{
|
|
CloseHandle(Thread);
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
|
|
}
|
|
_SEH2_END;
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
|
|
IN PCONSOLE_PROCESS_DATA ProcessData)
|
|
{
|
|
return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
|
|
}
|
|
|
|
PCONSOLE_PROCESS_DATA NTAPI
|
|
ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)
|
|
{
|
|
if (Console == NULL) return NULL;
|
|
|
|
return CONTAINING_RECORD(Console->ProcessList.Blink,
|
|
CONSOLE_PROCESS_DATA,
|
|
ConsoleLink);
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,
|
|
IN OUT PULONG ProcessIdsList,
|
|
IN ULONG MaxIdListItems,
|
|
OUT PULONG ProcessIdsTotal)
|
|
{
|
|
PCONSOLE_PROCESS_DATA current;
|
|
PLIST_ENTRY current_entry;
|
|
|
|
if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
*ProcessIdsTotal = 0;
|
|
|
|
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 (++(*ProcessIdsTotal) <= MaxIdListItems)
|
|
{
|
|
*ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// ConSrvGenerateConsoleCtrlEvent
|
|
NTSTATUS NTAPI
|
|
ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,
|
|
IN ULONG ProcessGroupId,
|
|
IN ULONG CtrlEvent)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PLIST_ENTRY current_entry;
|
|
PCONSOLE_PROCESS_DATA current;
|
|
|
|
/* If the console is already being destroyed, just return */
|
|
if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
/*
|
|
* 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(CtrlEvent, current);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
ConSrvSetProcessFocus(IN PCSR_PROCESS CsrProcess,
|
|
IN BOOLEAN SetForeground)
|
|
{
|
|
// FIXME: Call NtUserSetInformationProcess (currently unimplemented!)
|
|
// for setting Win32 foreground/background flags.
|
|
|
|
if (SetForeground)
|
|
CsrSetForegroundPriority(CsrProcess);
|
|
else
|
|
CsrSetBackgroundPriority(CsrProcess);
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConSrvSetConsoleProcessFocus(IN PCONSRV_CONSOLE Console,
|
|
IN BOOLEAN SetForeground)
|
|
{
|
|
PLIST_ENTRY current_entry;
|
|
PCONSOLE_PROCESS_DATA current;
|
|
|
|
/* If the console is already being destroyed, just return */
|
|
if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
/*
|
|
* Loop through the process list, from the most recent process
|
|
* to the oldest one, and for each, set its foreground priority.
|
|
*/
|
|
current_entry = Console->ProcessList.Flink;
|
|
while (current_entry != &Console->ProcessList)
|
|
{
|
|
current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
|
|
current_entry = current_entry->Flink;
|
|
|
|
ConSrvSetProcessFocus(current->Process, SetForeground);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* PUBLIC SERVER APIS *********************************************************/
|
|
|
|
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);
|
|
CONSOLE_INIT_INFO ConsoleInitInfo;
|
|
|
|
if (ProcessData->ConsoleHandle != 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->ConsoleTitle,
|
|
AllocConsoleRequest->TitleLength,
|
|
sizeof(BYTE)) ||
|
|
!CsrValidateMessageBuffer(ApiMessage,
|
|
(PVOID*)&AllocConsoleRequest->Desktop,
|
|
AllocConsoleRequest->DesktopLength,
|
|
sizeof(BYTE)) ||
|
|
!CsrValidateMessageBuffer(ApiMessage,
|
|
(PVOID*)&AllocConsoleRequest->CurDir,
|
|
AllocConsoleRequest->CurDirLength,
|
|
sizeof(BYTE)) ||
|
|
!CsrValidateMessageBuffer(ApiMessage,
|
|
(PVOID*)&AllocConsoleRequest->AppName,
|
|
AllocConsoleRequest->AppNameLength,
|
|
sizeof(BYTE)) )
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Initialize the console initialization info structure */
|
|
ConsoleInitInfo.ConsoleStartInfo = AllocConsoleRequest->ConsoleStartInfo;
|
|
ConsoleInitInfo.IsWindowVisible = TRUE; // The console window is always visible.
|
|
ConsoleInitInfo.TitleLength = AllocConsoleRequest->TitleLength;
|
|
ConsoleInitInfo.ConsoleTitle = AllocConsoleRequest->ConsoleTitle;
|
|
ConsoleInitInfo.DesktopLength = AllocConsoleRequest->DesktopLength;
|
|
ConsoleInitInfo.Desktop = AllocConsoleRequest->Desktop;
|
|
ConsoleInitInfo.AppNameLength = AllocConsoleRequest->AppNameLength;
|
|
ConsoleInitInfo.AppName = AllocConsoleRequest->AppName;
|
|
ConsoleInitInfo.CurDirLength = AllocConsoleRequest->CurDirLength;
|
|
ConsoleInitInfo.CurDir = AllocConsoleRequest->CurDir;
|
|
|
|
/* Initialize a new Console owned by the Console Leader Process */
|
|
Status = ConSrvAllocateConsole(ProcessData,
|
|
&AllocConsoleRequest->ConsoleStartInfo->InputHandle,
|
|
&AllocConsoleRequest->ConsoleStartInfo->OutputHandle,
|
|
&AllocConsoleRequest->ConsoleStartInfo->ErrorHandle,
|
|
&ConsoleInitInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Console allocation failed\n");
|
|
return Status;
|
|
}
|
|
|
|
/* Set the Property-Dialog and Control-Dispatcher handlers */
|
|
ProcessData->PropRoutine = AllocConsoleRequest->PropRoutine;
|
|
ProcessData->CtrlRoutine = AllocConsoleRequest->CtrlRoutine;
|
|
|
|
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->ConsoleHandle != NULL)
|
|
{
|
|
DPRINT1("Process already has a console\n");
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if (!CsrValidateMessageBuffer(ApiMessage,
|
|
(PVOID*)&AttachConsoleRequest->ConsoleStartInfo,
|
|
1,
|
|
sizeof(CONSOLE_START_INFO)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* 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 PID */
|
|
|
|
Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
|
|
ProcessBasicInformation,
|
|
&ProcessInfo,
|
|
Length, &Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = 0x%08lx\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->ConsoleHandle == NULL)
|
|
{
|
|
Status = STATUS_INVALID_HANDLE;
|
|
goto Quit;
|
|
}
|
|
|
|
/*
|
|
* Inherit the console from the parent,
|
|
* if any, otherwise return an error.
|
|
*/
|
|
Status = ConSrvInheritConsole(TargetProcessData,
|
|
SourceProcessData->ConsoleHandle,
|
|
TRUE,
|
|
&AttachConsoleRequest->ConsoleStartInfo->InputHandle,
|
|
&AttachConsoleRequest->ConsoleStartInfo->OutputHandle,
|
|
&AttachConsoleRequest->ConsoleStartInfo->ErrorHandle,
|
|
AttachConsoleRequest->ConsoleStartInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Console inheritance failed\n");
|
|
goto Quit;
|
|
}
|
|
|
|
/* Set the Property-Dialog and Control-Dispatcher handlers */
|
|
TargetProcessData->PropRoutine = AttachConsoleRequest->PropRoutine;
|
|
TargetProcessData->CtrlRoutine = AttachConsoleRequest->CtrlRoutine;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Quit:
|
|
/* Unlock the "source" process and exit */
|
|
CsrUnlockProcess(SourceProcess);
|
|
return Status;
|
|
}
|
|
|
|
CSR_API(SrvFreeConsole)
|
|
{
|
|
return ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvGetConsoleMode(IN PCONSOLE Console,
|
|
IN PCONSOLE_IO_OBJECT Object,
|
|
OUT PULONG ConsoleMode);
|
|
CSR_API(SrvGetConsoleMode)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
|
|
PCONSOLE_IO_OBJECT Object;
|
|
|
|
PULONG ConsoleMode = &ConsoleModeRequest->Mode;
|
|
|
|
Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
|
|
ConsoleModeRequest->Handle,
|
|
&Object, NULL, GENERIC_READ, TRUE, 0);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Get the standard console modes */
|
|
Status = ConDrvGetConsoleMode(Object->Console, Object,
|
|
ConsoleMode);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/*
|
|
* If getting the console modes succeeds, then retrieve
|
|
* the extended CONSRV-specific input modes.
|
|
*/
|
|
if (INPUT_BUFFER == Object->Type)
|
|
{
|
|
if (Object->Console->InsertMode || Object->Console->QuickEdit)
|
|
{
|
|
/* Windows also adds ENABLE_EXTENDED_FLAGS, even if it's not documented on MSDN */
|
|
*ConsoleMode |= ENABLE_EXTENDED_FLAGS;
|
|
|
|
if (Object->Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
|
|
if (Object->Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
|
|
}
|
|
}
|
|
}
|
|
|
|
ConSrvReleaseObject(Object, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvSetConsoleMode(IN PCONSOLE Console,
|
|
IN PCONSOLE_IO_OBJECT Object,
|
|
IN ULONG ConsoleMode);
|
|
CSR_API(SrvSetConsoleMode)
|
|
{
|
|
#define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
|
|
ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
|
|
|
|
NTSTATUS Status;
|
|
PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
|
|
PCONSOLE_IO_OBJECT Object;
|
|
|
|
ULONG ConsoleMode = ConsoleModeRequest->Mode;
|
|
|
|
Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
|
|
ConsoleModeRequest->Handle,
|
|
&Object, NULL, GENERIC_WRITE, TRUE, 0);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Set the standard console modes (without the CONSRV-specific input modes) */
|
|
ConsoleMode &= ~CONSOLE_VALID_CONTROL_MODES; // Remove CONSRV-specific input modes.
|
|
Status = ConDrvSetConsoleMode(Object->Console, Object,
|
|
ConsoleMode);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/*
|
|
* If setting the console modes succeeds, then set
|
|
* the extended CONSRV-specific input modes.
|
|
*/
|
|
if (INPUT_BUFFER == Object->Type)
|
|
{
|
|
ConsoleMode = ConsoleModeRequest->Mode;
|
|
|
|
if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
|
|
{
|
|
/*
|
|
* If we use control mode flags without ENABLE_EXTENDED_FLAGS,
|
|
* then consider the flags invalid.
|
|
*/
|
|
if ((ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
Object->Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
|
|
Object->Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ConSrvReleaseObject(Object, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
CSR_API(SrvGetConsoleTitle)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
|
|
PCONSRV_CONSOLE Console;
|
|
ULONG 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, status %lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Copy title of the console to the user title buffer */
|
|
if (TitleRequest->Unicode)
|
|
{
|
|
if (TitleRequest->Length >= sizeof(WCHAR))
|
|
{
|
|
Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
|
|
RtlCopyMemory(TitleRequest->Title, Console->Title.Buffer, Length);
|
|
((PWCHAR)TitleRequest->Title)[Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
TitleRequest->Length = Length;
|
|
}
|
|
else
|
|
{
|
|
TitleRequest->Length = Console->Title.Length;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (TitleRequest->Length >= sizeof(CHAR))
|
|
{
|
|
Length = min(TitleRequest->Length - sizeof(CHAR), Console->Title.Length / sizeof(WCHAR));
|
|
Length = WideCharToMultiByte(Console->InputCodePage, 0,
|
|
Console->Title.Buffer, Length,
|
|
TitleRequest->Title, Length,
|
|
NULL, NULL);
|
|
((PCHAR)TitleRequest->Title)[Length] = ANSI_NULL;
|
|
TitleRequest->Length = Length;
|
|
}
|
|
else
|
|
{
|
|
TitleRequest->Length = Console->Title.Length / sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
ConSrvReleaseConsole(Console, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
CSR_API(SrvSetConsoleTitle)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
PWCHAR Buffer;
|
|
ULONG 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, status %lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
if (TitleRequest->Unicode)
|
|
{
|
|
/* Length is in bytes */
|
|
Length = TitleRequest->Length;
|
|
}
|
|
else
|
|
{
|
|
/* Use the console input CP for the conversion */
|
|
Length = MultiByteToWideChar(Console->InputCodePage, 0,
|
|
TitleRequest->Title, TitleRequest->Length,
|
|
NULL, 0);
|
|
/* The returned Length was in number of wchars, convert it in bytes */
|
|
Length *= sizeof(WCHAR);
|
|
}
|
|
|
|
/* Allocate a new buffer to hold the new title (NULL-terminated) */
|
|
Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Length + sizeof(WCHAR));
|
|
if (!Buffer)
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Free the old title */
|
|
ConsoleFreeUnicodeString(&Console->Title);
|
|
|
|
/* Copy title to console */
|
|
Console->Title.Buffer = Buffer;
|
|
Console->Title.Length = Length;
|
|
Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
|
|
|
|
if (TitleRequest->Unicode)
|
|
{
|
|
RtlCopyMemory(Console->Title.Buffer, TitleRequest->Title, Console->Title.Length);
|
|
}
|
|
else
|
|
{
|
|
MultiByteToWideChar(Console->InputCodePage, 0,
|
|
TitleRequest->Title, TitleRequest->Length,
|
|
Console->Title.Buffer,
|
|
Console->Title.Length / sizeof(WCHAR));
|
|
}
|
|
|
|
/* NULL-terminate */
|
|
Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
TermChangeTitle(Console);
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Quit:
|
|
ConSrvReleaseConsole(Console, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvGetConsoleCP(IN PCONSOLE Console,
|
|
OUT PUINT CodePage,
|
|
IN BOOLEAN OutputCP);
|
|
CSR_API(SrvGetConsoleCP)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleCPRequest;
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
|
|
GetConsoleCPRequest->OutputCP ? "Output" : "Input");
|
|
|
|
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
Status = ConDrvGetConsoleCP(Console,
|
|
&GetConsoleCPRequest->CodePage,
|
|
GetConsoleCPRequest->OutputCP);
|
|
|
|
ConSrvReleaseConsole(Console, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
ConDrvSetConsoleCP(IN PCONSOLE Console,
|
|
IN UINT CodePage,
|
|
IN BOOLEAN OutputCP);
|
|
CSR_API(SrvSetConsoleCP)
|
|
{
|
|
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
PCONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetConsoleCPRequest;
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
|
|
SetConsoleCPRequest->OutputCP ? "Output" : "Input");
|
|
|
|
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
Status = ConDrvSetConsoleCP(Console,
|
|
SetConsoleCPRequest->CodePage,
|
|
SetConsoleCPRequest->OutputCP);
|
|
|
|
ConSrvReleaseConsole(Console, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
CSR_API(SrvGetConsoleProcessList)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
if (!CsrValidateMessageBuffer(ApiMessage,
|
|
(PVOID)&GetProcessListRequest->ProcessIdsList,
|
|
GetProcessListRequest->ProcessCount,
|
|
sizeof(DWORD)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
Status = ConSrvGetConsoleProcessList(Console,
|
|
GetProcessListRequest->ProcessIdsList,
|
|
GetProcessListRequest->ProcessCount,
|
|
&GetProcessListRequest->ProcessCount);
|
|
|
|
ConSrvReleaseConsole(Console, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
CSR_API(SrvGenerateConsoleCtrlEvent)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
Status = ConSrvConsoleProcessCtrlEvent(Console,
|
|
GenerateCtrlEventRequest->ProcessGroupId,
|
|
GenerateCtrlEventRequest->CtrlEvent);
|
|
|
|
ConSrvReleaseConsole(Console, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
CSR_API(SrvConsoleNotifyLastClose)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Only one process is allowed to be registered for last close notification */
|
|
if (!Console->NotifyLastClose)
|
|
{
|
|
Console->NotifyLastClose = TRUE;
|
|
Console->NotifiedLastCloseProcess = ProcessData;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
ConSrvReleaseConsole(Console, TRUE);
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
CSR_API(SrvGetConsoleMouseInfo)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_GETMOUSEINFO GetMouseInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetMouseInfoRequest;
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Just retrieve the number of buttons of the mouse attached to this console */
|
|
GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
|
|
|
|
ConSrvReleaseConsole(Console, TRUE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
CSR_API(SrvSetConsoleKeyShortcuts)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(SrvGetConsoleKeyboardLayoutName)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetKbdLayoutNameRequest;
|
|
PCONSRV_CONSOLE Console;
|
|
|
|
Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Retrieve the keyboard layout name of the system */
|
|
if (GetKbdLayoutNameRequest->Ansi)
|
|
GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
|
|
else
|
|
GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
|
|
|
|
ConSrvReleaseConsole(Console, TRUE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
CSR_API(SrvGetConsoleCharType)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(SrvSetConsoleLocalEUDC)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(SrvSetConsoleCursorMode)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(SrvGetConsoleCursorMode)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(SrvGetConsoleNlsMode)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(SrvSetConsoleNlsMode)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(SrvGetConsoleLangId)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* EOF */
|