[WIN32K]
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 11 Mar 2015 01:21:29 +0000 (01:21 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 11 Mar 2015 01:21:29 +0000 (01:21 +0000)
- 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

15 files changed:
reactos/include/reactos/subsys/win/conmsg.h
reactos/win32ss/CMakeLists.txt
reactos/win32ss/include/ntuser.h
reactos/win32ss/user/ntuser/console.c [new file with mode: 0644]
reactos/win32ss/user/ntuser/desktop.c
reactos/win32ss/user/ntuser/desktop.h
reactos/win32ss/user/ntuser/ntstubs.c
reactos/win32ss/user/winsrv/consrv/console.c
reactos/win32ss/user/winsrv/consrv/console.h
reactos/win32ss/user/winsrv/consrv/frontends/gui/conwnd.h
reactos/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c
reactos/win32ss/user/winsrv/consrv/frontends/terminal.c
reactos/win32ss/user/winsrv/consrv/frontends/tui/tuiterm.c
reactos/win32ss/user/winsrv/consrv/handle.c
reactos/win32ss/user/winsrv/consrv/init.c

index 26201b3..82b560b 100644 (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;
index 67876ff..f85a243 100644 (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
index 75f5c41..ef6d5ae 100644 (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
diff --git a/reactos/win32ss/user/ntuser/console.c b/reactos/win32ss/user/ntuser/console.c
new file mode 100644 (file)
index 0000000..378933b
--- /dev/null
@@ -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 */
index 12959c1..1a8973f 100644 (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
  *
index 9b0156e..0bab545 100644 (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;
index 6b1f91e..4338a89 100644 (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
  */
index c8390d0..93b2fa9 100644 (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);
index b8906ed..2754712 100644 (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
index a102ef5..a1d577c 100644 (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;
index 9e3c0a1..123d3f3 100644 (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);
+        DesktopPath.MaximumLength = ConsoleInitInfo->DesktopLength;
+        DesktopPath.Length = DesktopPath.MaximumLength - sizeof(UNICODE_NULL);
+        DesktopPath.Buffer = ConsoleInitInfo->Desktop;
+    }
+    else
+    {
+        RtlInitUnicodeString(&DesktopPath, L"Default");
+    }
 
-        WaitForSingleObject(GraphicsStartupEvent, INFINITE);
-        NtClose(GraphicsStartupEvent);
+    hDesk = NtUserResolveDesktop(ConsoleLeaderProcess->ProcessHandle,
+                                 &DesktopPath,
+                                 0,
+                                 &hWinSta);
+    DPRINT("NtUserResolveDesktop(DesktopPath = '%wZ') returned hDesk = 0x%p; hWinSta = 0x%p\n",
+           &DesktopPath, hDesk, hWinSta);
+
+    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;
     }
 
-    // ConsInitialized = TRUE;
+    /* ... otherwise create a new one. */
 
-    return TRUE;
+    /* 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 */
index 306aa41..0538b1f 100644 (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);
index 1894028..85826fa 100644 (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;
index 96ef381..0fbbe51 100644 (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");
index a0a4748..478d2f5 100644 (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");