- Implement NtUserResolveDesktop, which opens a desktop and a window station based on a desktop path string (format: "WinSta\Desktop").
- Move NtUserConsoleControl to a more suitable file, and implement the control code that allows CSRSS to assign console input threads to given desktops.
- Simplify a bit the code in NtUserSetInformationThread.

[CONSRV]
Turn the console input thread to a real one. There should be one such thread per desktop. When a console process is started, we open the desktop on which the process is going to start, we create a new input thread (attach it to the desktop) if needed, or reuse the existing one, and then we open the terminal on the correct desktop.

CORE-9346 #resolve #comment Fixed in revision 66662.

svn path=/trunk/; revision=66662
This commit is contained in:
Hermès Bélusca-Maïto 2015-03-11 01:21:29 +00:00
parent 3ea483bc85
commit 444d33d50b
15 changed files with 599 additions and 210 deletions

View file

@ -197,7 +197,9 @@ typedef struct _CONSRV_API_CONNECTINFO
ULONG TitleLength;
WCHAR ConsoleTitle[MAX_PATH + 1]; // Console title or full path to the startup shortcut
ULONG DesktopLength;
PWCHAR Desktop;
PWCHAR Desktop; // Contrary to the case of CONSOLE_ALLOCCONSOLE, the
// desktop string is allocated in the process' heap,
// and CSR will read it via NtReadVirtualMemory.
ULONG AppNameLength;
WCHAR AppName[128]; // Full path of the launched app
ULONG CurDirLength;

View file

@ -100,6 +100,7 @@ list(APPEND SOURCE
user/ntuser/caret.c
user/ntuser/class.c
user/ntuser/clipboard.c
user/ntuser/console.c
user/ntuser/csr.c
user/ntuser/cursoricon.c
user/ntuser/dde.c

View file

@ -1721,11 +1721,18 @@ NtUserCloseWindowStation(
/* Console commands for NtUserConsoleControl */
typedef enum _CONSOLECONTROL
{
GuiConsoleWndClassAtom,
ConsoleCtrlDesktopConsoleThread = 0,
GuiConsoleWndClassAtom = 1,
ConsoleMakePalettePublic = 5,
ConsoleAcquireDisplayOwnership,
} CONSOLECONTROL, *PCONSOLECONTROL;
typedef struct _DESKTOP_CONSOLE_THREAD
{
HDESK DesktopHandle;
ULONG_PTR ThreadId;
} DESKTOP_CONSOLE_THREAD, *PDESKTOP_CONSOLE_THREAD;
NTSTATUS
APIENTRY
NtUserConsoleControl(
@ -2860,13 +2867,13 @@ NtUserRemoveProp(
HWND hWnd,
ATOM Atom);
DWORD
NTAPI
HDESK
APIENTRY
NtUserResolveDesktop(
DWORD dwUnknown1,
DWORD dwUnknown2,
DWORD dwUnknown3,
DWORD dwUnknown4);
IN HANDLE ProcessHandle,
IN PUNICODE_STRING DesktopPath,
DWORD dwUnknown,
OUT HWINSTA* phWinSta);
DWORD
NTAPI

View file

@ -0,0 +1,160 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Win32k subsystem
* PURPOSE: Console support functions for CONSRV
* FILE: subsystems/win32/win32k/ntuser/console.c
* PROGRAMMER: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
#include <win32k.h>
DBG_DEFAULT_CHANNEL(UserMisc);
NTSTATUS
APIENTRY
NtUserConsoleControl(
IN CONSOLECONTROL ConsoleCtrl,
IN PVOID ConsoleCtrlInfo,
IN ULONG ConsoleCtrlInfoLength)
{
NTSTATUS Status = STATUS_SUCCESS;
/* Allow only the Console Server to perform this operation (via CSRSS) */
if (PsGetCurrentProcess() != gpepCSRSS)
return STATUS_ACCESS_DENIED;
UserEnterExclusive();
switch (ConsoleCtrl)
{
case ConsoleCtrlDesktopConsoleThread:
{
DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo;
PDESKTOP Desktop = NULL;
ULONG_PTR OldThreadId;
if (ConsoleCtrlInfoLength != sizeof(DesktopConsoleThreadInfo))
{
Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
_SEH2_TRY
{
ProbeForWrite(ConsoleCtrlInfo, ConsoleCtrlInfoLength, sizeof(USHORT));
DesktopConsoleThreadInfo = *(PDESKTOP_CONSOLE_THREAD)ConsoleCtrlInfo;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(break);
}
_SEH2_END;
/* Reference the desktop */
Status = ObReferenceObjectByHandle(DesktopConsoleThreadInfo.DesktopHandle,
0,
ExDesktopObjectType,
UserMode,
(PVOID*)&Desktop,
NULL);
if (!NT_SUCCESS(Status)) break;
/* Save the old thread ID, it is always returned to the caller */
OldThreadId = Desktop->dwConsoleThreadId;
/* Set the new console input thread ID for this desktop if required */
if (DesktopConsoleThreadInfo.ThreadId != (ULONG_PTR)INVALID_HANDLE_VALUE)
{
Desktop->dwConsoleThreadId = DesktopConsoleThreadInfo.ThreadId;
}
/* Always return the old thread ID */
DesktopConsoleThreadInfo.ThreadId = OldThreadId;
/* Dereference the desktop */
ObDereferenceObject(Desktop);
/* Return the information back to the caller */
_SEH2_TRY
{
*(PDESKTOP_CONSOLE_THREAD)ConsoleCtrlInfo = DesktopConsoleThreadInfo;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case GuiConsoleWndClassAtom:
{
if (ConsoleCtrlInfoLength != sizeof(ATOM))
{
Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
_SEH2_TRY
{
ProbeForRead(ConsoleCtrlInfo, ConsoleCtrlInfoLength, sizeof(USHORT));
gaGuiConsoleWndClass = *(ATOM*)ConsoleCtrlInfo;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case ConsoleMakePalettePublic:
{
HPALETTE hPalette;
if (ConsoleCtrlInfoLength != sizeof(hPalette))
{
Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
_SEH2_TRY
{
ProbeForRead(ConsoleCtrlInfo, ConsoleCtrlInfoLength, sizeof(USHORT));
hPalette = *(HPALETTE*)ConsoleCtrlInfo;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(break);
}
_SEH2_END;
/* Make the palette handle public */
GreSetObjectOwnerEx(hPalette,
GDI_OBJ_HMGR_PUBLIC,
GDIOBJFLAG_IGNOREPID);
break;
}
case ConsoleAcquireDisplayOwnership:
{
ERR("NtUserConsoleControl - ConsoleAcquireDisplayOwnership is UNIMPLEMENTED\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
}
default:
ERR("Calling invalid control %d in NtUserConsoleControl\n", ConsoleCtrl);
Status = STATUS_INVALID_INFO_CLASS;
break;
}
UserLeave();
return Status;
}
/* EOF */

View file

@ -1650,6 +1650,103 @@ NtUserPaintDesktop(HDC hDC)
return Ret;
}
/*
* NtUserResolveDesktop
*
* The NtUserResolveDesktop function retrieves handles to the desktop and
* the window station specified by the desktop path string.
*
* Parameters
* ProcessHandle
* Handle to a user process.
*
* DesktopPath
* The desktop path string.
*
* Return Value
* Handle to the desktop (direct return value) and
* handle to the associated window station (by pointer).
* NULL in case of failure.
*
* Remarks
* Callable by CSRSS only.
*
* Status
* @implemented
*/
HDESK
APIENTRY
NtUserResolveDesktop(
IN HANDLE ProcessHandle,
IN PUNICODE_STRING DesktopPath,
DWORD dwUnknown,
OUT HWINSTA* phWinSta)
{
NTSTATUS Status;
PEPROCESS Process = NULL;
HWINSTA hWinSta = NULL;
HDESK hDesktop = NULL;
/* Allow only the Console Server to perform this operation (via CSRSS) */
if (PsGetCurrentProcess() != gpepCSRSS)
return NULL;
/* Get the process object the user handle was referencing */
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_QUERY_INFORMATION,
*PsProcessType,
UserMode,
(PVOID*)&Process,
NULL);
if (!NT_SUCCESS(Status)) return NULL;
// UserEnterShared();
_SEH2_TRY
{
UNICODE_STRING CapturedDesktopPath;
/* Capture the user desktop path string */
Status = IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath,
DesktopPath);
if (!NT_SUCCESS(Status)) _SEH2_YIELD(goto Quit);
/* Call the internal function */
Status = IntParseDesktopPath(Process,
&CapturedDesktopPath,
&hWinSta,
&hDesktop);
if (!NT_SUCCESS(Status))
{
ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status);
hWinSta = NULL;
hDesktop = NULL;
}
/* Return the window station handle */
*phWinSta = hWinSta;
/* Free the captured string */
if (CapturedDesktopPath.Buffer)
ExFreePoolWithTag(CapturedDesktopPath.Buffer, TAG_STRING);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
Quit:
// UserLeave();
/* Dereference the process object */
ObDereferenceObject(Process);
/* Return the desktop handle */
return hDesktop;
}
/*
* NtUserSwitchDesktop
*

View file

@ -23,6 +23,10 @@ typedef struct _DESKTOP
PWIN32HEAP pheapDesktop;
ULONG_PTR ulHeapSize;
LIST_ENTRY PtiList;
/* One console input thread per desktop, maintained by CONSRV */
DWORD dwConsoleThreadId;
/* Use for tracking mouse moves. */
PWND spwndTrack;
DWORD htEx;

View file

@ -403,92 +403,6 @@ NtUserYieldTask(VOID)
return 0;
}
NTSTATUS
APIENTRY
NtUserConsoleControl(
IN CONSOLECONTROL ConsoleCtrl,
IN PVOID ConsoleCtrlInfo,
IN ULONG ConsoleCtrlInfoLength)
{
NTSTATUS Status = STATUS_SUCCESS;
/* Allow only the Console Server to perform this operation (via CSRSS) */
if (PsGetCurrentProcess() != gpepCSRSS)
return STATUS_ACCESS_DENIED;
UserEnterExclusive();
switch (ConsoleCtrl)
{
case GuiConsoleWndClassAtom:
{
if (ConsoleCtrlInfoLength != sizeof(ATOM))
{
Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
_SEH2_TRY
{
ProbeForRead(ConsoleCtrlInfo, ConsoleCtrlInfoLength, 1);
gaGuiConsoleWndClass = *(ATOM*)ConsoleCtrlInfo;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case ConsoleMakePalettePublic:
{
HPALETTE hPalette;
if (ConsoleCtrlInfoLength != sizeof(HPALETTE))
{
Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
_SEH2_TRY
{
ProbeForRead(ConsoleCtrlInfo, ConsoleCtrlInfoLength, 1);
hPalette = *(HPALETTE*)ConsoleCtrlInfo;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* Make the palette handle public */
GreSetObjectOwnerEx(hPalette,
GDI_OBJ_HMGR_PUBLIC,
GDIOBJFLAG_IGNOREPID);
break;
}
case ConsoleAcquireDisplayOwnership:
{
ERR("NtUserConsoleControl - ConsoleAcquireDisplayOwnership is UNIMPLEMENTED\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
}
default:
ERR("Calling invalid control %d in NtUserConsoleControl\n", ConsoleCtrl);
Status = STATUS_INVALID_INFO_CLASS;
break;
}
UserLeave();
return Status;
}
DWORD
APIENTRY
NtUserCreateInputContext(
@ -738,15 +652,10 @@ NtUserRegisterRawInputDevices(
return 0;
}
DWORD
APIENTRY
NtUserResolveDesktop(
DWORD dwUnknown1,
DWORD dwUnknown2,
DWORD dwUnknown3,
DWORD dwUnknown4)
DWORD APIENTRY
NtUserResolveDesktopForWOW(DWORD Unknown0)
{
STUB;
STUB
return 0;
}
@ -811,11 +720,11 @@ NtUserSetInformationThread(IN HANDLE ThreadHandle,
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(break);
}
_SEH2_END;
if (NT_SUCCESS(Status))
Status = UserInitiateShutdown(Thread, &CapturedFlags);
Status = UserInitiateShutdown(Thread, &CapturedFlags);
/* Return the modified value to the caller */
_SEH2_TRY
@ -853,12 +762,11 @@ NtUserSetInformationThread(IN HANDLE ThreadHandle,
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(break);
}
_SEH2_END;
if (NT_SUCCESS(Status))
Status = UserEndShutdown(Thread, ShutdownStatus);
Status = UserEndShutdown(Thread, ShutdownStatus);
break;
}
@ -884,12 +792,11 @@ NtUserSetInformationThread(IN HANDLE ThreadHandle,
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(break);
}
_SEH2_END;
if (NT_SUCCESS(Status))
Status = InitCsrApiPort(CsrPortHandle);
Status = InitCsrApiPort(CsrPortHandle);
break;
}
@ -1106,20 +1013,6 @@ NtUserUpdateLayeredWindow(
return 0;
}
/*
* NtUserResolveDesktopForWOW
*
* Status
* @unimplemented
*/
DWORD APIENTRY
NtUserResolveDesktopForWOW(DWORD Unknown0)
{
STUB
return 0;
}
/*
* @unimplemented
*/

View file

@ -342,7 +342,7 @@ NTSTATUS NTAPI
ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
IN OUT PCONSOLE_INFO ConsoleInfo,
IN OUT PVOID ExtraConsoleInfo,
IN ULONG ProcessId);
IN PCSR_PROCESS ConsoleLeaderProcess);
NTSTATUS NTAPI
ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal);
@ -513,12 +513,13 @@ NTSTATUS NTAPI
ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,
OUT PCONSRV_CONSOLE* NewConsole,
IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
IN ULONG ConsoleLeaderProcessId)
IN PCSR_PROCESS ConsoleLeaderProcess)
{
NTSTATUS Status;
HANDLE ConsoleHandle;
PCONSRV_CONSOLE Console;
CONSOLE_INFO ConsoleInfo;
ULONG ConsoleLeaderProcessId = HandleToUlong(ConsoleLeaderProcess->ClientId.UniqueProcess);
SIZE_T Length = 0;
TERMINAL Terminal; /* The ConSrv terminal for this console */
@ -545,7 +546,7 @@ ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,
Status = ConSrvInitTerminal(&Terminal,
&ConsoleInfo,
ConsoleInitInfo,
ConsoleLeaderProcessId);
ConsoleLeaderProcess);
if (!NT_SUCCESS(Status))
{
DPRINT1("CONSRV: Failed to initialize a terminal, Status = 0x%08lx\n", Status);

View file

@ -30,7 +30,7 @@ NTSTATUS NTAPI
ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,
OUT struct _CONSRV_CONSOLE** /* PCONSRV_CONSOLE* */ NewConsole,
IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
IN ULONG ConsoleLeaderProcessId);
IN PCSR_PROCESS ConsoleLeaderProcess);
VOID NTAPI ConSrvDeleteConsole(struct _CONSRV_CONSOLE* /* PCONSRV_CONSOLE */ Console);
NTSTATUS

View file

@ -40,6 +40,11 @@ typedef struct _GUI_CONSOLE_DATA
HANDLE hGuiInitEvent;
HANDLE hGuiTermEvent;
// HANDLE InputThreadHandle;
ULONG_PTR InputThreadId;
HWINSTA WinSta;
HDESK Desktop;
BOOLEAN IsWindowVisible;
POINT OldCursor;

View file

@ -23,8 +23,8 @@
#define CONGUI_UPDATE_TIME 0
#define CONGUI_UPDATE_TIMER 1
#define PM_CREATE_CONSOLE (WM_APP + 1)
#define PM_DESTROY_CONSOLE (WM_APP + 2)
#define PM_CREATE_CONSOLE (WM_APP + 1)
#define PM_DESTROY_CONSOLE (WM_APP + 2)
/* GLOBALS ********************************************************************/
@ -33,13 +33,15 @@ typedef struct _GUI_INIT_INFO
{
PCONSOLE_INFO ConsoleInfo;
PCONSOLE_START_INFO ConsoleStartInfo;
ULONG ProcessId;
ULONG_PTR ProcessId;
HANDLE GuiThreadStartupEvent;
ULONG_PTR InputThreadId;
HWINSTA WinSta;
HDESK Desktop;
BOOLEAN IsWindowVisible;
} GUI_INIT_INFO, *PGUI_INIT_INFO;
static BOOL ConsInitialized = FALSE;
static HANDLE hInputThread = NULL;
static DWORD dwInputThreadId = 0;
static BOOL ConsInitialized = FALSE;
extern HICON ghDefaultIcon;
extern HICON ghDefaultIconSm;
@ -137,20 +139,42 @@ SwitchFullScreen(PGUI_CONSOLE_DATA GuiData, BOOL FullScreen);
VOID
CreateSysMenu(HWND hWnd);
static DWORD NTAPI
static ULONG NTAPI
GuiConsoleInputThread(PVOID Param)
{
PHANDLE GraphicsStartupEvent = (PHANDLE)Param;
NTSTATUS Status;
PCSR_THREAD pcsrt = NULL;
PGUI_INIT_INFO GuiInitInfo = (PGUI_INIT_INFO)Param;
DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo;
ULONG_PTR InputThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread);
LONG WindowCount = 0;
MSG msg;
/*
* This thread dispatches all the console notifications to the
* notification window. It is common for all the console windows.
* notification window. It is common for all the console windows
* in a given desktop in a window station.
*/
/* Assign this console input thread to this desktop */
DesktopConsoleThreadInfo.DesktopHandle = GuiInitInfo->Desktop; // Duplicated desktop handle
DesktopConsoleThreadInfo.ThreadId = InputThreadId;
Status = NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
&DesktopConsoleThreadInfo,
sizeof(DesktopConsoleThreadInfo));
if (!NT_SUCCESS(Status)) goto Quit;
/* Connect this CSR thread to the USER subsystem */
pcsrt = CsrConnectToUser();
if (pcsrt == NULL) goto Quit;
/* Assign the desktop to this thread */
if (!SetThreadDesktop(DesktopConsoleThreadInfo.DesktopHandle)) goto Quit;
/* The thread has been initialized, set the event */
NtSetEvent(*GraphicsStartupEvent, NULL);
NtSetEvent(GuiInitInfo->GuiThreadStartupEvent, NULL);
Status = STATUS_SUCCESS;
while (GetMessageW(&msg, NULL, 0, 0))
{
@ -257,7 +281,7 @@ GuiConsoleInputThread(PVOID Param)
if (InterlockedDecrement(&WindowCount) == 0)
{
DPRINT("CONSRV: Going to quit the Input Thread!!\n");
DPRINT("CONSRV: Going to quit the Input Thread 0x%p\n", InputThreadId);
goto Quit;
}
@ -270,19 +294,41 @@ GuiConsoleInputThread(PVOID Param)
}
Quit:
DPRINT("CONSRV: Quit the Input Thread!!\n");
DPRINT("CONSRV: Quit the Input Thread 0x%p, Status = 0x%08lx\n", InputThreadId, Status);
hInputThread = NULL;
dwInputThreadId = 0;
/* Remove this console input thread from this desktop */
// DesktopConsoleThreadInfo.DesktopHandle;
DesktopConsoleThreadInfo.ThreadId = 0;
NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
&DesktopConsoleThreadInfo,
sizeof(DesktopConsoleThreadInfo));
return 1;
/* Close the duplicated desktop handle */
CloseDesktop(DesktopConsoleThreadInfo.DesktopHandle); // NtUserCloseDesktop
/* Cleanup CSR thread */
if (pcsrt) CsrDereferenceThread(pcsrt);
/* Exit the thread */
RtlExitUserThread(Status);
return 0;
}
// FIXME: Maybe return a NTSTATUS
static BOOL
GuiInit(VOID)
GuiInit(IN PCONSOLE_INIT_INFO ConsoleInitInfo,
IN PCSR_PROCESS ConsoleLeaderProcess,
IN OUT PGUI_INIT_INFO GuiInitInfo)
{
/* Exit if we were already initialized */
// if (ConsInitialized) return TRUE;
BOOL Success = TRUE;
UNICODE_STRING DesktopPath;
DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo;
HWINSTA hWinSta;
HDESK hDesk;
NTSTATUS Status;
HANDLE hInputThread;
CLIENT_ID ClientId;
/*
* Initialize and register the console window class, if needed.
@ -294,39 +340,164 @@ GuiInit(VOID)
}
/*
* Set-up the console input thread
* Set-up the console input thread. We have
* one console input thread per desktop.
*/
if (hInputThread == NULL)
if (!CsrImpersonateClient(NULL))
// return STATUS_BAD_IMPERSONATION_LEVEL;
return FALSE;
if (ConsoleInitInfo->DesktopLength)
{
HANDLE GraphicsStartupEvent;
NTSTATUS Status;
Status = NtCreateEvent(&GraphicsStartupEvent, EVENT_ALL_ACCESS,
NULL, SynchronizationEvent, FALSE);
if (!NT_SUCCESS(Status)) return FALSE;
hInputThread = CreateThread(NULL,
0,
GuiConsoleInputThread,
(PVOID)&GraphicsStartupEvent,
0,
&dwInputThreadId);
if (hInputThread == NULL)
{
NtClose(GraphicsStartupEvent);
DPRINT1("CONSRV: Failed to create graphics console thread.\n");
return FALSE;
}
SetThreadPriority(hInputThread, THREAD_PRIORITY_HIGHEST);
CloseHandle(hInputThread);
WaitForSingleObject(GraphicsStartupEvent, INFINITE);
NtClose(GraphicsStartupEvent);
DesktopPath.MaximumLength = ConsoleInitInfo->DesktopLength;
DesktopPath.Length = DesktopPath.MaximumLength - sizeof(UNICODE_NULL);
DesktopPath.Buffer = ConsoleInitInfo->Desktop;
}
else
{
RtlInitUnicodeString(&DesktopPath, L"Default");
}
// ConsInitialized = TRUE;
hDesk = NtUserResolveDesktop(ConsoleLeaderProcess->ProcessHandle,
&DesktopPath,
0,
&hWinSta);
DPRINT("NtUserResolveDesktop(DesktopPath = '%wZ') returned hDesk = 0x%p; hWinSta = 0x%p\n",
&DesktopPath, hDesk, hWinSta);
return TRUE;
CsrRevertToSelf();
if (hDesk == NULL) return FALSE;
/*
* We need to see whether we need to create a
* new console input thread for this desktop.
*/
DesktopConsoleThreadInfo.DesktopHandle = hDesk;
DesktopConsoleThreadInfo.ThreadId = (ULONG_PTR)INVALID_HANDLE_VALUE; // Special value to say we just want to retrieve the thread ID.
NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
&DesktopConsoleThreadInfo,
sizeof(DesktopConsoleThreadInfo));
DPRINT("NtUserConsoleControl returned ThreadId = 0x%p\n", DesktopConsoleThreadInfo.ThreadId);
/*
* Save the opened window station and desktop handles in the initialization
* structure. They will be used later on, and released, by the GUI frontend.
*/
GuiInitInfo->WinSta = hWinSta;
GuiInitInfo->Desktop = hDesk;
/* Here GuiInitInfo contains original handles */
/* If we already have a console input thread on this desktop... */
if (DesktopConsoleThreadInfo.ThreadId != 0)
{
/* ... just use it... */
DPRINT("Using input thread InputThreadId = 0x%p\n", DesktopConsoleThreadInfo.ThreadId);
GuiInitInfo->InputThreadId = DesktopConsoleThreadInfo.ThreadId;
goto Quit;
}
/* ... otherwise create a new one. */
/* Initialize a startup event for the thread to signal it */
Status = NtCreateEvent(&GuiInitInfo->GuiThreadStartupEvent, EVENT_ALL_ACCESS,
NULL, SynchronizationEvent, FALSE);
if (!NT_SUCCESS(Status))
{
Success = FALSE;
goto Quit;
}
/*
* Duplicate the desktop handle for the console input thread internal needs.
* If it happens to need also a window station handle in the future, then
* it is there that you also need to duplicate the window station handle!
*
* Note also that we are going to temporarily overwrite the stored handles
* in GuiInitInfo because it happens that we use also this structure to give
* the duplicated handles to the input thread that is going to initialize.
* After the input thread finishes its initialization, we restore the handles
* in GuiInitInfo to their old values.
*/
Status = NtDuplicateObject(NtCurrentProcess(),
hDesk,
NtCurrentProcess(),
(PHANDLE)&GuiInitInfo->Desktop,
0, 0, DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(Status))
{
Success = FALSE;
goto Quit;
}
/* Here GuiInitInfo contains duplicated handles */
Status = RtlCreateUserThread(NtCurrentProcess(),
NULL,
TRUE, // Start the thread in suspended state
0,
0,
0,
(PVOID)GuiConsoleInputThread,
(PVOID)GuiInitInfo,
&hInputThread,
&ClientId);
if (NT_SUCCESS(Status))
{
/* Add it as a static server thread and resume it */
CsrAddStaticServerThread(hInputThread, &ClientId, 0);
Status = NtResumeThread(hInputThread, NULL);
}
DPRINT("Thread creation hInputThread = 0x%p, InputThreadId = 0x%p, Status = 0x%08lx\n",
hInputThread, ClientId.UniqueThread, Status);
if (!NT_SUCCESS(Status) || hInputThread == NULL)
{
/* Close the thread's handle */
if (hInputThread) NtClose(hInputThread);
/* We need to close here the duplicated desktop handle */
CloseDesktop(GuiInitInfo->Desktop); // NtUserCloseDesktop
/* Close the startup event and bail out */
NtClose(GuiInitInfo->GuiThreadStartupEvent);
DPRINT1("CONSRV: Failed to create graphics console thread.\n");
Success = FALSE;
goto Quit;
}
/* No need to close hInputThread, this is done by CSR automatically */
/* Wait for the thread to finish its initialization, and close the startup event */
NtWaitForSingleObject(GuiInitInfo->GuiThreadStartupEvent, FALSE, NULL);
NtClose(GuiInitInfo->GuiThreadStartupEvent);
/*
* Save the input thread ID for later use, and restore the original handles.
* The copies are held by the console input thread.
*/
GuiInitInfo->InputThreadId = (ULONG_PTR)ClientId.UniqueThread;
GuiInitInfo->WinSta = hWinSta;
GuiInitInfo->Desktop = hDesk;
/* Here GuiInitInfo contains again original handles */
Quit:
if (!Success)
{
/*
* Close the original handles. Do not use the copies in GuiInitInfo
* because we may have failed in the middle of the duplicate operation
* and the handles stored in GuiInitInfo may have changed.
*/
CloseDesktop(hDesk); // NtUserCloseDesktop
CloseWindowStation(hWinSta); // NtUserCloseWindowStation
}
return Success;
}
@ -362,7 +533,7 @@ GuiInitFrontEnd(IN OUT PFRONTEND This,
ConsoleStartInfo = GuiInitInfo->ConsoleStartInfo;
/* Terminal data allocation */
GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_CONSOLE_DATA));
GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*GuiData));
if (!GuiData)
{
DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
@ -471,9 +642,13 @@ GuiInitFrontEnd(IN OUT PFRONTEND This,
GuiData->LineSelection = FALSE; // Default to block selection
// TODO: Retrieve the selection mode via the registry.
GuiData->InputThreadId = GuiInitInfo->InputThreadId;
GuiData->WinSta = GuiInitInfo->WinSta;
GuiData->Desktop = GuiInitInfo->Desktop;
/* Finally, finish to initialize the frontend structure */
This->Context = GuiData;
if (This->Context2) ConsoleFreeHeap(This->Context2);
ConsoleFreeHeap(This->Context2);
This->Context2 = NULL;
/*
@ -490,10 +665,10 @@ GuiInitFrontEnd(IN OUT PFRONTEND This,
DPRINT("GUI - Checkpoint\n");
/* Create the terminal window */
PostThreadMessageW(dwInputThreadId, PM_CREATE_CONSOLE, 0, (LPARAM)GuiData);
PostThreadMessageW(GuiData->InputThreadId, PM_CREATE_CONSOLE, 0, (LPARAM)GuiData);
/* Wait until initialization has finished */
WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
NtWaitForSingleObject(GuiData->hGuiInitEvent, FALSE, NULL);
DPRINT("OK we created the console window\n");
NtClose(GuiData->hGuiInitEvent);
GuiData->hGuiInitEvent = NULL;
@ -515,12 +690,15 @@ GuiDeinitFrontEnd(IN OUT PFRONTEND This)
PGUI_CONSOLE_DATA GuiData = This->Context;
DPRINT("Send PM_DESTROY_CONSOLE message and wait on hGuiTermEvent...\n");
PostThreadMessageW(dwInputThreadId, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
WaitForSingleObject(GuiData->hGuiTermEvent, INFINITE);
PostThreadMessageW(GuiData->InputThreadId, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
NtWaitForSingleObject(GuiData->hGuiTermEvent, FALSE, NULL);
DPRINT("hGuiTermEvent set\n");
NtClose(GuiData->hGuiTermEvent);
GuiData->hGuiTermEvent = NULL;
CloseDesktop(GuiData->Desktop); // NtUserCloseDesktop
CloseWindowStation(GuiData->WinSta); // NtUserCloseWindowStation
DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
@ -897,9 +1075,9 @@ GuiGetSelectionInfo(IN OUT PFRONTEND This,
if (pSelectionInfo == NULL) return FALSE;
ZeroMemory(pSelectionInfo, sizeof(CONSOLE_SELECTION_INFO));
ZeroMemory(pSelectionInfo, sizeof(*pSelectionInfo));
if (GuiData->Selection.dwFlags != CONSOLE_NO_SELECTION)
RtlCopyMemory(pSelectionInfo, &GuiData->Selection, sizeof(CONSOLE_SELECTION_INFO));
RtlCopyMemory(pSelectionInfo, &GuiData->Selection, sizeof(*pSelectionInfo));
return TRUE;
}
@ -1074,7 +1252,7 @@ NTSTATUS NTAPI
GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
IN OUT PCONSOLE_INFO ConsoleInfo,
IN OUT PVOID ExtraConsoleInfo,
IN ULONG ProcessId)
IN PCSR_PROCESS ConsoleLeaderProcess)
{
PCONSOLE_INIT_INFO ConsoleInitInfo = ExtraConsoleInfo;
PGUI_INIT_INFO GuiInitInfo;
@ -1082,21 +1260,25 @@ GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleInitInfo == NULL)
return STATUS_INVALID_PARAMETER;
/* Initialize GUI terminal emulator common functionalities */
if (!GuiInit()) return STATUS_UNSUCCESSFUL;
/*
* Initialize a private initialization info structure for later use.
* It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
*/
GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO));
GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(*GuiInitInfo));
if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
/* Initialize GUI terminal emulator common functionalities */
if (!GuiInit(ConsoleInitInfo, ConsoleLeaderProcess, GuiInitInfo))
{
ConsoleFreeHeap(GuiInitInfo);
return STATUS_UNSUCCESSFUL;
}
// HACK: We suppose that the pointers will be valid in GuiInitFrontEnd...
// If not, then copy exactly what we need in GuiInitInfo.
GuiInitInfo->ConsoleInfo = ConsoleInfo;
GuiInitInfo->ConsoleStartInfo = ConsoleInitInfo->ConsoleStartInfo;
GuiInitInfo->ProcessId = ProcessId;
GuiInitInfo->ProcessId = HandleToUlong(ConsoleLeaderProcess->ClientId.UniqueProcess);
GuiInitInfo->IsWindowVisible = ConsoleInitInfo->IsWindowVisible;
/* Finally, initialize the frontend structure */

View file

@ -96,7 +96,7 @@ NTSTATUS NTAPI
TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
IN OUT PCONSOLE_INFO ConsoleInfo,
IN OUT PVOID ExtraConsoleInfo,
IN ULONG ProcessId);
IN PCSR_PROCESS ConsoleLeaderProcess);
NTSTATUS NTAPI
TuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd);
#endif
@ -105,7 +105,7 @@ NTSTATUS NTAPI
GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
IN OUT PCONSOLE_INFO ConsoleInfo,
IN OUT PVOID ExtraConsoleInfo,
IN ULONG ProcessId);
IN PCSR_PROCESS ConsoleLeaderProcess);
NTSTATUS NTAPI
GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd);
/***************/
@ -114,7 +114,7 @@ typedef
NTSTATUS (NTAPI *FRONTEND_LOAD)(IN OUT PFRONTEND FrontEnd,
IN OUT PCONSOLE_INFO ConsoleInfo,
IN OUT PVOID ExtraConsoleInfo,
IN ULONG ProcessId);
IN PCSR_PROCESS ConsoleLeaderProcess);
typedef
NTSTATUS (NTAPI *FRONTEND_UNLOAD)(IN OUT PFRONTEND FrontEnd);
@ -156,7 +156,7 @@ static NTSTATUS
ConSrvLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
IN OUT PCONSOLE_INFO ConsoleInfo,
IN OUT PVOID ExtraConsoleInfo,
IN ULONG ProcessId)
IN PCSR_PROCESS ConsoleLeaderProcess)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG i;
@ -171,7 +171,7 @@ ConSrvLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
Status = FrontEndLoadingMethods[i].FrontEndLoad(FrontEnd,
ConsoleInfo,
ExtraConsoleInfo,
ProcessId);
ConsoleLeaderProcess);
if (NT_SUCCESS(Status))
{
/* Save the unload callback */
@ -206,7 +206,7 @@ NTSTATUS NTAPI
ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
IN OUT PCONSOLE_INFO ConsoleInfo,
IN OUT PVOID ExtraConsoleInfo,
IN ULONG ProcessId)
IN PCSR_PROCESS ConsoleLeaderProcess)
{
NTSTATUS Status;
PFRONTEND FrontEnd;
@ -218,7 +218,7 @@ ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
Status = ConSrvLoadFrontEnd(FrontEnd,
ConsoleInfo,
ExtraConsoleInfo,
ProcessId);
ConsoleLeaderProcess);
if (!NT_SUCCESS(Status))
{
DPRINT1("CONSRV: Failed to initialize a frontend, Status = 0x%08lx\n", Status);

View file

@ -942,7 +942,7 @@ NTSTATUS NTAPI
TuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
IN OUT PCONSOLE_INFO ConsoleInfo,
IN OUT PVOID ExtraConsoleInfo,
IN ULONG ProcessId)
IN PCSR_PROCESS ConsoleLeaderProcess)
{
if (FrontEnd == NULL || ConsoleInfo == NULL)
return STATUS_INVALID_PARAMETER;

View file

@ -494,10 +494,14 @@ ConSrvAllocateConsole(PCONSOLE_PROCESS_DATA ProcessData,
ConSrvFreeHandlesTable(ProcessData);
/* Initialize a new Console owned by this process */
DPRINT("Initialization of console '%S' for process '%S' on desktop '%S'\n",
ConsoleInitInfo->ConsoleTitle ? ConsoleInitInfo->ConsoleTitle : L"n/a",
ConsoleInitInfo->AppName ? ConsoleInitInfo->AppName : L"n/a",
ConsoleInitInfo->Desktop ? ConsoleInitInfo->Desktop : L"n/a");
Status = ConSrvInitConsole(&ConsoleHandle,
&Console,
ConsoleInitInfo,
HandleToUlong(ProcessData->Process->ClientId.UniqueProcess));
ProcessData->Process);
if (!NT_SUCCESS(Status))
{
DPRINT1("Console initialization failed\n");

View file

@ -414,7 +414,6 @@ ConSrvConnect(IN PCSR_PROCESS CsrProcess,
NTSTATUS Status = STATUS_SUCCESS;
PCONSRV_API_CONNECTINFO ConnectInfo = (PCONSRV_API_CONNECTINFO)ConnectionInfo;
PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
CONSOLE_INIT_INFO ConsoleInitInfo;
if ( ConnectionInfo == NULL ||
ConnectionInfoLength == NULL ||
@ -433,23 +432,51 @@ ConSrvConnect(IN PCSR_PROCESS CsrProcess,
DPRINT("ConnectInfo->IsConsoleApp = %s\n", ConnectInfo->IsConsoleApp ? "True" : "False");
if (!ConnectInfo->IsConsoleApp) return STATUS_SUCCESS;
/* Initialize the console initialization info structure */
ConsoleInitInfo.ConsoleStartInfo = &ConnectInfo->ConsoleStartInfo;
ConsoleInitInfo.IsWindowVisible = ConnectInfo->IsWindowVisible;
ConsoleInitInfo.TitleLength = ConnectInfo->TitleLength;
ConsoleInitInfo.ConsoleTitle = ConnectInfo->ConsoleTitle;
ConsoleInitInfo.DesktopLength = ConnectInfo->DesktopLength;
ConsoleInitInfo.Desktop = ConnectInfo->Desktop;
ConsoleInitInfo.AppNameLength = ConnectInfo->AppNameLength;
ConsoleInitInfo.AppName = ConnectInfo->AppName;
ConsoleInitInfo.CurDirLength = ConnectInfo->CurDirLength;
ConsoleInitInfo.CurDir = ConnectInfo->CurDir;
/* If we don't inherit from an existing console, then create a new one... */
if (ConnectInfo->ConsoleStartInfo.ConsoleHandle == NULL)
{
CONSOLE_INIT_INFO ConsoleInitInfo;
DPRINT("ConSrvConnect - Allocate a new console\n");
/* Initialize the console initialization info structure */
ConsoleInitInfo.ConsoleStartInfo = &ConnectInfo->ConsoleStartInfo;
ConsoleInitInfo.IsWindowVisible = ConnectInfo->IsWindowVisible;
ConsoleInitInfo.TitleLength = ConnectInfo->TitleLength;
ConsoleInitInfo.ConsoleTitle = ConnectInfo->ConsoleTitle;
ConsoleInitInfo.DesktopLength = 0;
ConsoleInitInfo.Desktop = NULL;
ConsoleInitInfo.AppNameLength = ConnectInfo->AppNameLength;
ConsoleInitInfo.AppName = ConnectInfo->AppName;
ConsoleInitInfo.CurDirLength = ConnectInfo->CurDirLength;
ConsoleInitInfo.CurDir = ConnectInfo->CurDir;
/*
* Contrary to the case of SrvAllocConsole, the desktop string is
* allocated in the process' heap, so we need to retrieve it by
* using NtReadVirtualMemory.
*/
if (ConnectInfo->DesktopLength)
{
ConsoleInitInfo.DesktopLength = ConnectInfo->DesktopLength;
ConsoleInitInfo.Desktop = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
ConsoleInitInfo.DesktopLength);
if (ConsoleInitInfo.Desktop == NULL)
return STATUS_NO_MEMORY;
Status = NtReadVirtualMemory(ProcessData->Process->ProcessHandle,
ConnectInfo->Desktop,
ConsoleInitInfo.Desktop,
ConsoleInitInfo.DesktopLength,
NULL);
if (!NT_SUCCESS(Status))
{
ConsoleFreeHeap(ConsoleInitInfo.Desktop);
return Status;
}
}
/*
* 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
@ -467,6 +494,12 @@ ConSrvConnect(IN PCSR_PROCESS CsrProcess,
&ConnectInfo->ConsoleStartInfo.OutputHandle,
&ConnectInfo->ConsoleStartInfo.ErrorHandle,
&ConsoleInitInfo);
/* Free our local desktop string if any */
if (ConsoleInitInfo.DesktopLength)
ConsoleFreeHeap(ConsoleInitInfo.Desktop);
/* Check for success */
if (!NT_SUCCESS(Status))
{
DPRINT1("Console allocation failed\n");