From 444d33d50ba67db670d8139695ff5137c53ad1c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Wed, 11 Mar 2015 01:21:29 +0000 Subject: [PATCH] [WIN32K] - 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 --- reactos/include/reactos/subsys/win/conmsg.h | 4 +- reactos/win32ss/CMakeLists.txt | 1 + reactos/win32ss/include/ntuser.h | 21 +- reactos/win32ss/user/ntuser/console.c | 160 ++++++++++ reactos/win32ss/user/ntuser/desktop.c | 97 ++++++ reactos/win32ss/user/ntuser/desktop.h | 4 + reactos/win32ss/user/ntuser/ntstubs.c | 125 +------- reactos/win32ss/user/winsrv/consrv/console.c | 7 +- reactos/win32ss/user/winsrv/consrv/console.h | 2 +- .../user/winsrv/consrv/frontends/gui/conwnd.h | 5 + .../winsrv/consrv/frontends/gui/guiterm.c | 302 ++++++++++++++---- .../user/winsrv/consrv/frontends/terminal.c | 14 +- .../winsrv/consrv/frontends/tui/tuiterm.c | 2 +- reactos/win32ss/user/winsrv/consrv/handle.c | 6 +- reactos/win32ss/user/winsrv/consrv/init.c | 59 +++- 15 files changed, 599 insertions(+), 210 deletions(-) create mode 100644 reactos/win32ss/user/ntuser/console.c diff --git a/reactos/include/reactos/subsys/win/conmsg.h b/reactos/include/reactos/subsys/win/conmsg.h index 26201b320c8..82b560b01b3 100644 --- a/reactos/include/reactos/subsys/win/conmsg.h +++ b/reactos/include/reactos/subsys/win/conmsg.h @@ -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; diff --git a/reactos/win32ss/CMakeLists.txt b/reactos/win32ss/CMakeLists.txt index 67876ffaa6f..f85a2430a49 100644 --- a/reactos/win32ss/CMakeLists.txt +++ b/reactos/win32ss/CMakeLists.txt @@ -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 diff --git a/reactos/win32ss/include/ntuser.h b/reactos/win32ss/include/ntuser.h index 75f5c41a02f..ef6d5ae4cf1 100644 --- a/reactos/win32ss/include/ntuser.h +++ b/reactos/win32ss/include/ntuser.h @@ -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 diff --git a/reactos/win32ss/user/ntuser/console.c b/reactos/win32ss/user/ntuser/console.c new file mode 100644 index 00000000000..378933b7d25 --- /dev/null +++ b/reactos/win32ss/user/ntuser/console.c @@ -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 +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 */ diff --git a/reactos/win32ss/user/ntuser/desktop.c b/reactos/win32ss/user/ntuser/desktop.c index 12959c18c1e..1a8973f51f8 100644 --- a/reactos/win32ss/user/ntuser/desktop.c +++ b/reactos/win32ss/user/ntuser/desktop.c @@ -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 * diff --git a/reactos/win32ss/user/ntuser/desktop.h b/reactos/win32ss/user/ntuser/desktop.h index 9b0156ee6db..0bab5454983 100644 --- a/reactos/win32ss/user/ntuser/desktop.h +++ b/reactos/win32ss/user/ntuser/desktop.h @@ -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; diff --git a/reactos/win32ss/user/ntuser/ntstubs.c b/reactos/win32ss/user/ntuser/ntstubs.c index 6b1f91e172e..4338a89dae4 100644 --- a/reactos/win32ss/user/ntuser/ntstubs.c +++ b/reactos/win32ss/user/ntuser/ntstubs.c @@ -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 */ diff --git a/reactos/win32ss/user/winsrv/consrv/console.c b/reactos/win32ss/user/winsrv/consrv/console.c index c8390d044d3..93b2fa9f6fe 100644 --- a/reactos/win32ss/user/winsrv/consrv/console.c +++ b/reactos/win32ss/user/winsrv/consrv/console.c @@ -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); diff --git a/reactos/win32ss/user/winsrv/consrv/console.h b/reactos/win32ss/user/winsrv/consrv/console.h index b8906edce04..2754712fd27 100644 --- a/reactos/win32ss/user/winsrv/consrv/console.h +++ b/reactos/win32ss/user/winsrv/consrv/console.h @@ -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 diff --git a/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.h b/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.h index a102ef5f42b..a1d577cfe39 100644 --- a/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.h +++ b/reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.h @@ -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; diff --git a/reactos/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c b/reactos/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c index 9e3c0a1f6e8..123d3f3c7ad 100644 --- a/reactos/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c +++ b/reactos/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c @@ -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 */ diff --git a/reactos/win32ss/user/winsrv/consrv/frontends/terminal.c b/reactos/win32ss/user/winsrv/consrv/frontends/terminal.c index 306aa4192cd..0538b1fdf11 100644 --- a/reactos/win32ss/user/winsrv/consrv/frontends/terminal.c +++ b/reactos/win32ss/user/winsrv/consrv/frontends/terminal.c @@ -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); diff --git a/reactos/win32ss/user/winsrv/consrv/frontends/tui/tuiterm.c b/reactos/win32ss/user/winsrv/consrv/frontends/tui/tuiterm.c index 18940282cc9..85826fa168f 100644 --- a/reactos/win32ss/user/winsrv/consrv/frontends/tui/tuiterm.c +++ b/reactos/win32ss/user/winsrv/consrv/frontends/tui/tuiterm.c @@ -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; diff --git a/reactos/win32ss/user/winsrv/consrv/handle.c b/reactos/win32ss/user/winsrv/consrv/handle.c index 96ef381199a..0fbbe517ea9 100644 --- a/reactos/win32ss/user/winsrv/consrv/handle.c +++ b/reactos/win32ss/user/winsrv/consrv/handle.c @@ -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"); diff --git a/reactos/win32ss/user/winsrv/consrv/init.c b/reactos/win32ss/user/winsrv/consrv/init.c index a0a47487706..478d2f5eacd 100644 --- a/reactos/win32ss/user/winsrv/consrv/init.c +++ b/reactos/win32ss/user/winsrv/consrv/init.c @@ -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");