[NTUSER] Release WinSta->spklList at IntWinStaObjectDelete (#4593)
[reactos.git] / win32ss / user / ntuser / winsta.c
index 44a8f40..4c91558 100644 (file)
@@ -14,7 +14,10 @@ DBG_DEFAULT_CHANNEL(UserWinsta);
 
 /* GLOBALS *******************************************************************/
 
-/* Currently active window station */
+/*
+ * The currently active window station. This is the
+ * only one interactive window station on the system.
+ */
 PWINSTATION_OBJECT InputWindowStation = NULL;
 
 /* Winlogon SAS window */
@@ -25,7 +28,7 @@ UNICODE_STRING gustrWindowStationsDir;
 
 /* INITIALIZATION FUNCTIONS ****************************************************/
 
-INIT_FUNCTION
+CODE_SEG("INIT")
 NTSTATUS
 NTAPI
 InitWindowStationImpl(VOID)
@@ -47,11 +50,11 @@ NTSTATUS
 NTAPI
 UserCreateWinstaDirectory(VOID)
 {
-    PPEB Peb;
     NTSTATUS Status;
-    WCHAR wstrWindowStationsDir[MAX_PATH];
+    PPEB Peb;
     OBJECT_ATTRIBUTES ObjectAttributes;
     HANDLE hWinstaDir;
+    WCHAR wstrWindowStationsDir[MAX_PATH];
 
     /* Create the WindowStations directory and cache its path for later use */
     Peb = NtCurrentPeb();
@@ -64,11 +67,14 @@ UserCreateWinstaDirectory(VOID)
     }
     else
     {
-        swprintf(wstrWindowStationsDir,
-                 L"%ws\\%lu%ws",
-                 SESSION_DIR,
-                 Peb->SessionId,
-                 WINSTA_OBJ_DIR);
+        Status = RtlStringCbPrintfW(wstrWindowStationsDir,
+                                    sizeof(wstrWindowStationsDir),
+                                    L"%ws\\%lu%ws",
+                                    SESSION_DIR,
+                                    Peb->SessionId,
+                                    WINSTA_OBJ_DIR);
+        if (!NT_SUCCESS(Status))
+            return Status;
 
         if (!RtlCreateUnicodeString(&gustrWindowStationsDir, wstrWindowStationsDir))
         {
@@ -78,10 +84,10 @@ UserCreateWinstaDirectory(VOID)
 
     InitializeObjectAttributes(&ObjectAttributes,
                                &gustrWindowStationsDir,
-                               0,
+                               OBJ_KERNEL_HANDLE,
                                NULL,
                                NULL);
-    Status = ZwCreateDirectoryObject(&hWinstaDir, 0, &ObjectAttributes);
+    Status = ZwCreateDirectoryObject(&hWinstaDir, DIRECTORY_CREATE_OBJECT, &ObjectAttributes);
     if (!NT_SUCCESS(Status))
     {
         ERR("Could not create %wZ directory (Status 0x%X)\n", &gustrWindowStationsDir,  Status);
@@ -93,7 +99,7 @@ UserCreateWinstaDirectory(VOID)
     return Status;
 }
 
-/* OBJECT CALLBACKS  **********************************************************/
+/* OBJECT CALLBACKS ***********************************************************/
 
 NTSTATUS
 NTAPI
@@ -103,7 +109,18 @@ IntWinStaObjectDelete(
     PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters = Parameters;
     PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)DeleteParameters->Object;
 
-    TRACE("Deleting window station (0x%p)\n", WinSta);
+    TRACE("Deleting window station 0x%p\n", WinSta);
+
+    if (WinSta == InputWindowStation)
+    {
+        ERR("WARNING: Deleting the interactive window station '%wZ'!\n",
+            &(OBJECT_HEADER_TO_NAME_INFO(OBJECT_TO_OBJECT_HEADER(InputWindowStation))->Name));
+
+        /* Only Winlogon can close and delete the interactive window station */
+        ASSERT(gpidLogon == PsGetCurrentProcessId());
+
+        InputWindowStation = NULL;
+    }
 
     WinSta->Flags |= WSS_DYING;
 
@@ -111,7 +128,7 @@ IntWinStaObjectDelete(
 
     RtlDestroyAtomTable(WinSta->AtomTable);
 
-    RtlFreeUnicodeString(&WinSta->Name);
+    UserAssignmentUnlock((PVOID*)&WinSta->spklList);
 
     return STATUS_SUCCESS;
 }
@@ -191,7 +208,7 @@ IntWinStaOkToClose(
 
     ppi = PsGetCurrentProcessWin32Process();
 
-    if(ppi && (OkToCloseParameters->Handle == ppi->hwinsta))
+    if (ppi && (OkToCloseParameters->Handle == ppi->hwinsta))
     {
         return STATUS_ACCESS_DENIED;
     }
@@ -248,6 +265,12 @@ co_IntInitializeDesktopGraphics(VOID)
     UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"DISPLAY");
     PDESKTOP pdesk;
 
+    if (PDEVOBJ_lChangeDisplaySettings(NULL, NULL, NULL, &gpmdev, TRUE) != DISP_CHANGE_SUCCESSFUL)
+    {
+        ERR("PDEVOBJ_lChangeDisplaySettings() failed.\n");
+        return FALSE;
+    }
+
     ScreenDeviceContext = IntGdiCreateDC(&DriverName, NULL, NULL, NULL, FALSE);
     if (NULL == ScreenDeviceContext)
     {
@@ -256,7 +279,7 @@ co_IntInitializeDesktopGraphics(VOID)
     }
     GreSetDCOwner(ScreenDeviceContext, GDI_OBJ_HMGR_PUBLIC);
 
-    if (! IntCreatePrimarySurface())
+    if (!IntCreatePrimarySurface())
     {
         return FALSE;
     }
@@ -266,9 +289,15 @@ co_IntInitializeDesktopGraphics(VOID)
     NtGdiSelectFont(hSystemBM, NtGdiGetStockObject(SYSTEM_FONT));
     GreSetDCOwner(hSystemBM, GDI_OBJ_HMGR_PUBLIC);
 
+    /* Update the system metrics */
+    InitMetrics();
+
+    /* Set new size of the monitor */
+    UserUpdateMonitorSize((HDEV)gpmdev->ppdevGlobal);
+
     /* Update the SERVERINFO */
-    gpsi->aiSysMet[SM_CXSCREEN] = gppdevPrimary->gdiinfo.ulHorzRes;
-    gpsi->aiSysMet[SM_CYSCREEN] = gppdevPrimary->gdiinfo.ulVertRes;
+    gpsi->aiSysMet[SM_CXSCREEN] = gpmdev->ppdevGlobal->gdiinfo.ulHorzRes;
+    gpsi->aiSysMet[SM_CYSCREEN] = gpmdev->ppdevGlobal->gdiinfo.ulVertRes;
     gpsi->Planes        = NtGdiGetDeviceCaps(ScreenDeviceContext, PLANES);
     gpsi->BitsPixel     = NtGdiGetDeviceCaps(ScreenDeviceContext, BITSPIXEL);
     gpsi->BitCount      = gpsi->Planes * gpsi->BitsPixel;
@@ -278,7 +307,9 @@ co_IntInitializeDesktopGraphics(VOID)
         gpsi->PUSIFlags |= PUSIF_PALETTEDISPLAY;
     }
     else
+    {
         gpsi->PUSIFlags &= ~PUSIF_PALETTEDISPLAY;
+    }
     // Font is realized and this dc was previously set to internal DC_ATTR.
     gpsi->cxSysFontChar = IntGetCharDimensions(hSystemBM, &tmw, (DWORD*)&gpsi->cySysFontChar);
     gpsi->tmSysFont     = tmw;
@@ -288,7 +319,7 @@ co_IntInitializeDesktopGraphics(VOID)
     gpsi->ptCursor.y = gpsi->aiSysMet[SM_CYSCREEN] / 2;
 
     /* Attach monitor */
-    UserAttachMonitor((HDEV)gppdevPrimary);
+    UserAttachMonitor((HDEV)gpmdev->ppdevGlobal);
 
     /* Setup the cursor */
     co_IntLoadDefaultCursors();
@@ -304,6 +335,22 @@ co_IntInitializeDesktopGraphics(VOID)
     ASSERT(pdesk);
     co_IntShowDesktop(pdesk, gpsi->aiSysMet[SM_CXSCREEN], gpsi->aiSysMet[SM_CYSCREEN], TRUE);
 
+    /* HACK: display wallpaper on all secondary displays */
+    {
+        PGRAPHICS_DEVICE pGraphicsDevice;
+        UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"DISPLAY");
+        UNICODE_STRING DisplayName;
+        HDC hdc;
+        ULONG iDevNum;
+
+        for (iDevNum = 1; (pGraphicsDevice = EngpFindGraphicsDevice(NULL, iDevNum)) != NULL; iDevNum++)
+        {
+            RtlInitUnicodeString(&DisplayName, pGraphicsDevice->szWinDeviceName);
+            hdc = IntGdiCreateDC(&DriverName, &DisplayName, NULL, NULL, FALSE);
+            IntPaintDesktop(hdc);
+        }
+    }
+
     return TRUE;
 }
 
@@ -383,125 +430,405 @@ CheckWinstaAttributeAccess(ACCESS_MASK DesiredAccess)
  *    @implemented
  */
 
-HWINSTA APIENTRY
-NtUserCreateWindowStation(
-    POBJECT_ATTRIBUTES ObjectAttributes,
-    ACCESS_MASK dwDesiredAccess,
+NTSTATUS
+FASTCALL
+IntCreateWindowStation(
+    OUT HWINSTA* phWinSta,
+    IN POBJECT_ATTRIBUTES ObjectAttributes,
+    IN KPROCESSOR_MODE AccessMode,
+    IN KPROCESSOR_MODE OwnerMode,
+    IN ACCESS_MASK dwDesiredAccess,
     DWORD Unknown2,
     DWORD Unknown3,
     DWORD Unknown4,
     DWORD Unknown5,
     DWORD Unknown6)
 {
-    UNICODE_STRING WindowStationName;
-    PWINSTATION_OBJECT WindowStationObject;
-    HWINSTA WindowStation;
     NTSTATUS Status;
+    HWINSTA hWinSta;
+    PWINSTATION_OBJECT WindowStation;
 
-    TRACE("NtUserCreateWindowStation called\n");
+    TRACE("IntCreateWindowStation called\n");
+
+    ASSERT(phWinSta);
+    *phWinSta = NULL;
 
     Status = ObOpenObjectByName(ObjectAttributes,
                                 ExWindowStationObjectType,
-                                UserMode,
+                                AccessMode,
                                 NULL,
                                 dwDesiredAccess,
                                 NULL,
-                                (PVOID*)&WindowStation);
-
+                                (PVOID*)&hWinSta);
     if (NT_SUCCESS(Status))
     {
-        TRACE("NtUserCreateWindowStation opened window station %wZ\n", ObjectAttributes->ObjectName);
-        return (HWINSTA)WindowStation;
+        TRACE("IntCreateWindowStation opened window station '%wZ'\n",
+              ObjectAttributes->ObjectName);
+        *phWinSta = hWinSta;
+        return Status;
     }
 
     /*
-     * No existing window station found, try to create new one
+     * No existing window station found, try to create a new one.
      */
 
-    /* Capture window station name */
-    _SEH2_TRY
-    {
-        ProbeForRead( ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1);
-        Status = IntSafeCopyUnicodeStringTerminateNULL(&WindowStationName, ObjectAttributes->ObjectName);
-    }
-    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
-    {
-        Status =_SEH2_GetExceptionCode();
-    }
-    _SEH2_END
-
-    if (! NT_SUCCESS(Status))
-    {
-        ERR("Failed reading capturing window station name\n");
-        SetLastNtError(Status);
-        return NULL;
-    }
-
     /* Create the window station object */
-    Status = ObCreateObject(UserMode,
+    Status = ObCreateObject(AccessMode,
                             ExWindowStationObjectType,
                             ObjectAttributes,
-                            UserMode,
+                            OwnerMode,
                             NULL,
                             sizeof(WINSTATION_OBJECT),
                             0,
                             0,
-                            (PVOID*)&WindowStationObject);
-
+                            (PVOID*)&WindowStation);
     if (!NT_SUCCESS(Status))
     {
-        ERR("ObCreateObject failed with %lx for window station %wZ\n", Status, &WindowStationName);
-        ExFreePoolWithTag(WindowStationName.Buffer, TAG_STRING);
-        SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
-        return 0;
+        ERR("ObCreateObject failed for window station '%wZ', Status 0x%08lx\n",
+            ObjectAttributes->ObjectName, Status);
+        SetLastNtError(Status);
+        return Status;
     }
 
     /* Initialize the window station */
-    RtlZeroMemory(WindowStationObject, sizeof(WINSTATION_OBJECT));
+    RtlZeroMemory(WindowStation, sizeof(WINSTATION_OBJECT));
 
-    InitializeListHead(&WindowStationObject->DesktopListHead);
-    WindowStationObject->Name = WindowStationName;
-    WindowStationObject->dwSessionId = NtCurrentPeb()->SessionId;
-    Status = RtlCreateAtomTable(37, &WindowStationObject->AtomTable);
+    InitializeListHead(&WindowStation->DesktopListHead);
+    WindowStation->dwSessionId = NtCurrentPeb()->SessionId;
+    Status = RtlCreateAtomTable(37, &WindowStation->AtomTable);
     if (!NT_SUCCESS(Status))
     {
-        ERR("RtlCreateAtomTable failed with %lx for window station %wZ\n", Status, &WindowStationName);
-        ObDereferenceObject(WindowStationObject);
-        SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
-        return 0;
+        ERR("RtlCreateAtomTable failed for window station '%wZ', Status 0x%08lx\n",
+            ObjectAttributes->ObjectName, Status);
+        ObDereferenceObject(WindowStation);
+        SetLastNtError(Status);
+        return Status;
     }
 
-    Status = ObInsertObject((PVOID)WindowStationObject,
+    Status = ObInsertObject(WindowStation,
                             NULL,
                             dwDesiredAccess,
                             0,
                             NULL,
-                            (PVOID*)&WindowStation);
-
+                            (PVOID*)&hWinSta);
     if (!NT_SUCCESS(Status))
     {
-        ERR("ObInsertObject failed with %lx for window station\n", Status);
-        SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
-        return 0;
+        ERR("ObInsertObject failed for window station, Status 0x%08lx\n", Status);
+        SetLastNtError(Status);
+        return Status;
     }
 
+    // FIXME! TODO: Add this new window station to a linked list
+
     if (InputWindowStation == NULL)
     {
         ERR("Initializing input window station\n");
-        InputWindowStation = WindowStationObject;
 
-        WindowStationObject->Flags &= ~WSS_NOIO;
+        /* Only Winlogon can create the interactive window station */
+        ASSERT(gpidLogon == PsGetCurrentProcessId());
+
+        InputWindowStation = WindowStation;
+        WindowStation->Flags &= ~WSS_NOIO;
 
         InitCursorImpl();
+
+        UserCreateSystemThread(ST_DESKTOP_THREAD);
+        UserCreateSystemThread(ST_RIT);
+
+        /* Desktop functions require the desktop thread running so wait for it to initialize */
+        UserLeaveCo();
+        KeWaitForSingleObject(gpDesktopThreadStartedEvent,
+                              UserRequest,
+                              UserMode,
+                              FALSE,
+                              NULL);
+        UserEnterCo();
+    }
+    else
+    {
+        WindowStation->Flags |= WSS_NOIO;
+    }
+
+    TRACE("IntCreateWindowStation created window station '%wZ' object 0x%p handle 0x%p\n",
+          ObjectAttributes->ObjectName, WindowStation, hWinSta);
+
+    *phWinSta = hWinSta;
+    EngSetLastError(ERROR_SUCCESS);
+
+    return STATUS_SUCCESS;
+}
+
+static VOID
+FreeUserModeWindowStationName(
+    IN OUT PUNICODE_STRING WindowStationName,
+    IN PUNICODE_STRING TebStaticUnicodeString,
+    IN OUT POBJECT_ATTRIBUTES UserModeObjectAttributes OPTIONAL,
+    IN POBJECT_ATTRIBUTES LocalObjectAttributes OPTIONAL)
+{
+    SIZE_T MemSize = 0;
+
+    /* Try to restore the user's UserModeObjectAttributes */
+    if (UserModeObjectAttributes && LocalObjectAttributes)
+    {
+        _SEH2_TRY
+        {
+            ProbeForWrite(UserModeObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
+            *UserModeObjectAttributes = *LocalObjectAttributes;
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            NOTHING;
+        }
+        _SEH2_END;
+    }
+
+    /* Free the user-mode memory */
+    if (WindowStationName && (WindowStationName != TebStaticUnicodeString))
+    {
+        ZwFreeVirtualMemory(ZwCurrentProcess(),
+                            (PVOID*)&WindowStationName,
+                            &MemSize,
+                            MEM_RELEASE);
+    }
+}
+
+static NTSTATUS
+BuildUserModeWindowStationName(
+    IN OUT POBJECT_ATTRIBUTES UserModeObjectAttributes,
+    IN OUT POBJECT_ATTRIBUTES LocalObjectAttributes,
+    OUT PUNICODE_STRING* WindowStationName,
+    OUT PUNICODE_STRING* TebStaticUnicodeString)
+{
+    NTSTATUS Status;
+    SIZE_T MemSize;
+
+    LUID CallerLuid;
+    PTEB Teb;
+    USHORT StrSize;
+
+    *WindowStationName = NULL;
+    *TebStaticUnicodeString = NULL;
+
+    /* Retrieve the current process LUID */
+    Status = GetProcessLuid(NULL, NULL, &CallerLuid);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("Failed to retrieve the caller LUID, Status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Compute the needed string size */
+    MemSize = _scwprintf(L"%wZ\\Service-0x%x-%x$",
+                         &gustrWindowStationsDir,
+                         CallerLuid.HighPart,
+                         CallerLuid.LowPart);
+    MemSize = MemSize * sizeof(WCHAR) + sizeof(UNICODE_NULL);
+    if (MemSize > MAXUSHORT)
+    {
+        ERR("Window station name length is too long.\n");
+        return STATUS_NAME_TOO_LONG;
+    }
+    StrSize = (USHORT)MemSize;
+
+    /*
+     * Check whether it's short enough so that we can use the static buffer
+     * in the TEB. Otherwise continue with virtual memory allocation.
+     */
+    Teb = NtCurrentTeb();
+    if (Teb && (StrSize <= sizeof(Teb->StaticUnicodeBuffer)))
+    {
+        /* We can use the TEB's static unicode string */
+        ASSERT(Teb->StaticUnicodeString.Buffer == Teb->StaticUnicodeBuffer);
+        ASSERT(Teb->StaticUnicodeString.MaximumLength == sizeof(Teb->StaticUnicodeBuffer));
+
+        /* Remember the TEB's static unicode string address for later */
+        *TebStaticUnicodeString = &Teb->StaticUnicodeString;
+
+        *WindowStationName = *TebStaticUnicodeString;
+        (*WindowStationName)->Length = 0;
     }
     else
     {
-        WindowStationObject->Flags |= WSS_NOIO;
+        /* The TEB's static unicode string is too small, allocate some user-mode virtual memory */
+        MemSize += ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID));
+
+        /* Allocate the memory in user-mode */
+        Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
+                                         (PVOID*)WindowStationName,
+                                         0,
+                                         &MemSize,
+                                         MEM_COMMIT,
+                                         PAGE_READWRITE);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status);
+            return Status;
+        }
+
+        RtlInitEmptyUnicodeString(*WindowStationName,
+                                  (PWCHAR)((ULONG_PTR)*WindowStationName +
+                                      ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID))),
+                                  StrSize);
+    }
+
+    /* Build a valid window station name from the LUID */
+    Status = RtlStringCbPrintfW((*WindowStationName)->Buffer,
+                                (*WindowStationName)->MaximumLength,
+                                L"%wZ\\Service-0x%x-%x$",
+                                &gustrWindowStationsDir,
+                                CallerLuid.HighPart,
+                                CallerLuid.LowPart);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
+        goto Quit;
+    }
+    (*WindowStationName)->Length = (USHORT)(wcslen((*WindowStationName)->Buffer) * sizeof(WCHAR));
+
+    /* Try to update the user's UserModeObjectAttributes */
+    _SEH2_TRY
+    {
+        ProbeForWrite(UserModeObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
+        *LocalObjectAttributes = *UserModeObjectAttributes;
+
+        UserModeObjectAttributes->ObjectName = *WindowStationName;
+        UserModeObjectAttributes->RootDirectory = NULL;
+
+        Status = STATUS_SUCCESS;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
+
+Quit:
+    if (!NT_SUCCESS(Status))
+    {
+        /* Release the window station name */
+        FreeUserModeWindowStationName(*WindowStationName,
+                                      *TebStaticUnicodeString,
+                                      NULL, NULL);
     }
 
-    TRACE("NtUserCreateWindowStation created object %p with name %wZ handle %p\n",
-          WindowStation, &WindowStationObject->Name, WindowStation);
-    return WindowStation;
+    return Status;
+}
+
+HWINSTA
+APIENTRY
+NtUserCreateWindowStation(
+    IN POBJECT_ATTRIBUTES ObjectAttributes,
+    IN ACCESS_MASK dwDesiredAccess,
+    DWORD Unknown2,
+    DWORD Unknown3,
+    DWORD Unknown4,
+    DWORD Unknown5,
+    DWORD Unknown6)
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+    HWINSTA hWinSta = NULL;
+    OBJECT_ATTRIBUTES LocalObjectAttributes;
+    PUNICODE_STRING WindowStationName = NULL;
+    PUNICODE_STRING TebStaticUnicodeString = NULL;
+    KPROCESSOR_MODE OwnerMode = UserMode;
+
+    TRACE("NtUserCreateWindowStation called\n");
+
+    /* Capture the object attributes and the window station name */
+    _SEH2_TRY
+    {
+        ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
+        LocalObjectAttributes = *ObjectAttributes;
+        if (LocalObjectAttributes.Length != sizeof(OBJECT_ATTRIBUTES))
+        {
+            ERR("Invalid ObjectAttributes length!\n");
+            Status = STATUS_INVALID_PARAMETER;
+            _SEH2_LEAVE;
+        }
+
+        /*
+         * Check whether the caller provided a window station name together
+         * with a RootDirectory handle.
+         *
+         * If the caller did not provide a window station name, build a new one
+         * based on the logon session identifier for the calling process.
+         * The new name is allocated in user-mode, as the rest of ObjectAttributes
+         * already is, so that the validation performed by the Object Manager
+         * can be done adequately.
+         */
+        if ((LocalObjectAttributes.ObjectName == NULL ||
+             LocalObjectAttributes.ObjectName->Buffer == NULL ||
+             LocalObjectAttributes.ObjectName->Length == 0 ||
+             LocalObjectAttributes.ObjectName->Buffer[0] == UNICODE_NULL)
+            /* &&
+            LocalObjectAttributes.RootDirectory == NULL */)
+        {
+            /* No, build the new window station name */
+            Status = BuildUserModeWindowStationName(ObjectAttributes,
+                                                    &LocalObjectAttributes,
+                                                    &WindowStationName,
+                                                    &TebStaticUnicodeString);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("BuildUserModeWindowStationName() failed, Status 0x%08lx\n", Status);
+                _SEH2_LEAVE;
+            }
+            OwnerMode = KernelMode;
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status =_SEH2_GetExceptionCode();
+        ERR("ObjectAttributes capture failed, Status 0x%08lx\n", Status);
+    }
+    _SEH2_END;
+
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastNtError(Status);
+        return NULL;
+    }
+
+    UserEnterExclusive();
+
+    /* Create the window station */
+    Status = IntCreateWindowStation(&hWinSta,
+                                    ObjectAttributes,
+                                    UserMode,
+                                    OwnerMode,
+                                    dwDesiredAccess,
+                                    Unknown2,
+                                    Unknown3,
+                                    Unknown4,
+                                    Unknown5,
+                                    Unknown6);
+    UserLeave();
+
+    if (NT_SUCCESS(Status))
+    {
+        TRACE("NtUserCreateWindowStation created window station '%wZ' with handle 0x%p\n",
+              ObjectAttributes->ObjectName, hWinSta);
+    }
+    else
+    {
+        ASSERT(hWinSta == NULL);
+        ERR("NtUserCreateWindowStation failed to create window station '%wZ', Status 0x%08lx\n",
+            ObjectAttributes->ObjectName, Status);
+    }
+
+    /* Try to restore the user's ObjectAttributes and release the window station name */
+    FreeUserModeWindowStationName(WindowStationName,
+                                  TebStaticUnicodeString,
+                                  (OwnerMode == KernelMode ? ObjectAttributes : NULL),
+                                  &LocalObjectAttributes);
+
+    if (!NT_SUCCESS(Status))
+    {
+        ASSERT(hWinSta == NULL);
+        SetLastNtError(Status);
+    }
+
+    return hWinSta;
 }
 
 /*
@@ -528,32 +855,130 @@ NtUserCreateWindowStation(
  *    @implemented
  */
 
-HWINSTA APIENTRY
+HWINSTA
+APIENTRY
 NtUserOpenWindowStation(
-    POBJECT_ATTRIBUTES ObjectAttributes,
-    ACCESS_MASK dwDesiredAccess)
+    IN POBJECT_ATTRIBUTES ObjectAttributes,
+    IN ACCESS_MASK dwDesiredAccess)
 {
-    HWINSTA hwinsta;
-    NTSTATUS Status;
+    NTSTATUS Status = STATUS_SUCCESS;
+    HWINSTA hWinSta = NULL;
+    OBJECT_ATTRIBUTES LocalObjectAttributes;
+    PUNICODE_STRING WindowStationName = NULL;
+    PUNICODE_STRING TebStaticUnicodeString = NULL;
+    KPROCESSOR_MODE OwnerMode = UserMode;
 
+    TRACE("NtUserOpenWindowStation called\n");
+
+    /* Capture the object attributes and the window station name */
+    _SEH2_TRY
+    {
+        ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
+        LocalObjectAttributes = *ObjectAttributes;
+        if (LocalObjectAttributes.Length != sizeof(OBJECT_ATTRIBUTES))
+        {
+            ERR("Invalid ObjectAttributes length!\n");
+            Status = STATUS_INVALID_PARAMETER;
+            _SEH2_LEAVE;
+        }
+
+        /*
+         * Check whether the caller did not provide a window station name,
+         * or provided the special "Service-0x00000000-00000000$" name.
+         *
+         * NOTE: On Windows, the special "Service-0x00000000-00000000$" string
+         * is used instead of an empty name (observed when API-monitoring
+         * OpenWindowStation() called with an empty window station name).
+         */
+        if ((LocalObjectAttributes.ObjectName == NULL ||
+             LocalObjectAttributes.ObjectName->Buffer == NULL ||
+             LocalObjectAttributes.ObjectName->Length == 0 ||
+             LocalObjectAttributes.ObjectName->Buffer[0] == UNICODE_NULL)
+            /* &&
+            LocalObjectAttributes.RootDirectory == NULL */)
+        {
+            /* No, remember that for later */
+            LocalObjectAttributes.ObjectName = NULL;
+        }
+        if (LocalObjectAttributes.ObjectName &&
+            LocalObjectAttributes.ObjectName->Length ==
+                sizeof(L"Service-0x00000000-00000000$") - sizeof(UNICODE_NULL) &&
+            _wcsnicmp(LocalObjectAttributes.ObjectName->Buffer,
+                      L"Service-0x00000000-00000000$",
+                      LocalObjectAttributes.ObjectName->Length / sizeof(WCHAR)) == 0)
+        {
+            /* No, remember that for later */
+            LocalObjectAttributes.ObjectName = NULL;
+        }
+
+        /*
+         * If the caller did not provide a window station name, build a new one
+         * based on the logon session identifier for the calling process.
+         * The new name is allocated in user-mode, as the rest of ObjectAttributes
+         * already is, so that the validation performed by the Object Manager
+         * can be done adequately.
+         */
+        if (!LocalObjectAttributes.ObjectName)
+        {
+            /* No, build the new window station name */
+            Status = BuildUserModeWindowStationName(ObjectAttributes,
+                                                    &LocalObjectAttributes,
+                                                    &WindowStationName,
+                                                    &TebStaticUnicodeString);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("BuildUserModeWindowStationName() failed, Status 0x%08lx\n", Status);
+                _SEH2_LEAVE;
+            }
+            OwnerMode = KernelMode;
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status =_SEH2_GetExceptionCode();
+        ERR("ObjectAttributes capture failed, Status 0x%08lx\n", Status);
+    }
+    _SEH2_END;
+
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastNtError(Status);
+        return NULL;
+    }
+
+    /* Open the window station */
     Status = ObOpenObjectByName(ObjectAttributes,
                                 ExWindowStationObjectType,
                                 UserMode,
                                 NULL,
                                 dwDesiredAccess,
                                 NULL,
-                                (PVOID*)&hwinsta);
+                                (PVOID*)&hWinSta);
+    if (NT_SUCCESS(Status))
+    {
+        TRACE("NtUserOpenWindowStation opened window station '%wZ' with handle 0x%p\n",
+              ObjectAttributes->ObjectName, hWinSta);
+    }
+    else
+    {
+        ASSERT(hWinSta == NULL);
+        ERR("NtUserOpenWindowStation failed to open window station '%wZ', Status 0x%08lx\n",
+            ObjectAttributes->ObjectName, Status);
+    }
+
+    /* Try to restore the user's ObjectAttributes and release the window station name */
+    FreeUserModeWindowStationName(WindowStationName,
+                                  TebStaticUnicodeString,
+                                  (OwnerMode == KernelMode ? ObjectAttributes : NULL),
+                                  &LocalObjectAttributes);
 
     if (!NT_SUCCESS(Status))
     {
-        ERR("NtUserOpenWindowStation failed\n");
+        ASSERT(hWinSta == NULL);
         SetLastNtError(Status);
-        return 0;
     }
 
-    TRACE("Opened window station %wZ with handle %p\n", ObjectAttributes->ObjectName, hwinsta);
-
-    return hwinsta;
+    return hWinSta;
 }
 
 /*
@@ -570,7 +995,7 @@ NtUserOpenWindowStation(
  *
  * Remarks
  *    The window station handle can be created with NtUserCreateWindowStation
- *    or NtUserOpenWindowStation. Attemps to close a handle to the window
+ *    or NtUserOpenWindowStation. Attempts to close a handle to the window
  *    station assigned to the calling process will fail.
  *
  * Status
@@ -597,8 +1022,7 @@ NtUserCloseWindowStation(
                                             UserMode,
                                             0,
                                             &Object,
-                                            0);
-
+                                            NULL);
     if (!NT_SUCCESS(Status))
     {
         ERR("Validation of window station handle (%p) failed\n", hWinSta);
@@ -670,7 +1094,11 @@ NtUserGetObjectInformation(
     NTSTATUS Status;
     PWINSTATION_OBJECT WinStaObject = NULL;
     PDESKTOP DesktopObject = NULL;
+    POBJECT_HEADER ObjectHeader;
+    POBJECT_HEADER_NAME_INFO NameInfo;
+    OBJECT_HANDLE_INFORMATION HandleInfo;
     USEROBJECTFLAGS ObjectFlags;
+    PUNICODE_STRING pStrNameU = NULL;
     PVOID pvData = NULL;
     SIZE_T nDataSize = 0;
 
@@ -688,13 +1116,13 @@ NtUserGetObjectInformation(
     _SEH2_END;
 
     /* Try window station */
-    TRACE("Trying to open window station %p\n", hObject);
+    TRACE("Trying to open window station 0x%p\n", hObject);
     Status = ObReferenceObjectByHandle(hObject,
                                        0,
                                        ExWindowStationObjectType,
                                        UserMode,
                                        (PVOID*)&WinStaObject,
-                                       NULL);
+                                       &HandleInfo);
 
     if (Status == STATUS_OBJECT_TYPE_MISMATCH)
     {
@@ -720,23 +1148,8 @@ NtUserGetObjectInformation(
     {
         case UOI_FLAGS:
         {
-            OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo;
-            ULONG BytesWritten;
-
             ObjectFlags.fReserved = FALSE;
-
-            /* Check whether this handle is inheritable */
-            Status = ZwQueryObject(hObject,
-                                   ObjectHandleFlagInformation,
-                                   &HandleInfo,
-                                   sizeof(OBJECT_HANDLE_ATTRIBUTE_INFORMATION),
-                                   &BytesWritten);
-            if (!NT_SUCCESS(Status))
-            {
-                ERR("ZwQueryObject failed, Status 0x%08lx\n", Status);
-                break;
-            }
-            ObjectFlags.fInherit = HandleInfo.Inherit;
+            ObjectFlags.fInherit = !!(HandleInfo.HandleAttributes & OBJ_INHERIT);
 
             ObjectFlags.dwFlags = 0;
             if (WinStaObject != NULL)
@@ -763,8 +1176,22 @@ NtUserGetObjectInformation(
         {
             if (WinStaObject != NULL)
             {
-                pvData = WinStaObject->Name.Buffer;
-                nDataSize = WinStaObject->Name.Length + sizeof(WCHAR);
+                ObjectHeader = OBJECT_TO_OBJECT_HEADER(WinStaObject);
+                NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
+
+                if (NameInfo && (NameInfo->Name.Length > 0))
+                {
+                    /* Named window station */
+                    pStrNameU = &NameInfo->Name;
+                    nDataSize = pStrNameU->Length + sizeof(UNICODE_NULL);
+                }
+                else
+                {
+                    /* Unnamed window station (should never happen!) */
+                    ASSERT(FALSE);
+                    pStrNameU = NULL;
+                    nDataSize = sizeof(UNICODE_NULL);
+                }
                 Status = STATUS_SUCCESS;
             }
             else if (DesktopObject != NULL)
@@ -784,14 +1211,16 @@ NtUserGetObjectInformation(
         {
             if (WinStaObject != NULL)
             {
-                pvData = L"WindowStation";
-                nDataSize = sizeof(L"WindowStation");
+                ObjectHeader = OBJECT_TO_OBJECT_HEADER(WinStaObject);
+                pStrNameU = &ObjectHeader->Type->Name;
+                nDataSize = pStrNameU->Length + sizeof(UNICODE_NULL);
                 Status = STATUS_SUCCESS;
             }
             else if (DesktopObject != NULL)
             {
-                pvData = L"Desktop";
-                nDataSize = sizeof(L"Desktop");
+                ObjectHeader = OBJECT_TO_OBJECT_HEADER(DesktopObject);
+                pStrNameU = &ObjectHeader->Type->Name;
+                nDataSize = pStrNameU->Length + sizeof(UNICODE_NULL);
                 Status = STATUS_SUCCESS;
             }
             else
@@ -821,10 +1250,25 @@ Exit:
             *nLengthNeeded = nDataSize;
 
         /* Try to copy data to caller */
-        if (Status == STATUS_SUCCESS)
+        if (Status == STATUS_SUCCESS && (nDataSize > 0))
         {
             TRACE("Trying to copy data to caller (len = %lu, len needed = %lu)\n", nLength, nDataSize);
-            RtlCopyMemory(pvInformation, pvData, nDataSize);
+            if (pvData)
+            {
+                /* Copy the data */
+                RtlCopyMemory(pvInformation, pvData, nDataSize);
+            }
+            else if (pStrNameU)
+            {
+                /* Copy and NULL-terminate the string */
+                RtlCopyMemory(pvInformation, pStrNameU->Buffer, pStrNameU->Length);
+                ((PWCHAR)pvInformation)[pStrNameU->Length / sizeof(WCHAR)] = UNICODE_NULL;
+            }
+            else
+            {
+                /* Zero the memory */
+                RtlZeroMemory(pvInformation, nDataSize);
+            }
         }
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
@@ -893,8 +1337,6 @@ NtUserSetObjectInformation(
 }
 
 
-
-
 HWINSTA FASTCALL
 UserGetProcessWindowStation(VOID)
 {
@@ -927,11 +1369,11 @@ NtUserGetProcessWindowStation(VOID)
 BOOL FASTCALL
 UserSetProcessWindowStation(HWINSTA hWindowStation)
 {
-    PPROCESSINFO ppi;
     NTSTATUS Status;
-    HWINSTA hwinstaOld;
+    PPROCESSINFO ppi;
     OBJECT_HANDLE_INFORMATION ObjectHandleInfo;
     PWINSTATION_OBJECT NewWinSta = NULL, OldWinSta;
+    HWINSTA hCacheWinSta;
 
     ppi = PsGetCurrentProcessWin32Process();
 
@@ -945,15 +1387,14 @@ UserSetProcessWindowStation(HWINSTA hWindowStation)
                                                 &ObjectHandleInfo);
         if (!NT_SUCCESS(Status))
         {
-            TRACE("Validation of window station handle (%p) failed\n",
-                  hWindowStation);
+            TRACE("Validation of window station handle 0x%p failed\n", hWindowStation);
             SetLastNtError(Status);
             return FALSE;
         }
     }
 
     OldWinSta = ppi->prpwinsta;
-    hwinstaOld = PsGetProcessWin32WindowStation(ppi->peProcess);
+    hCacheWinSta = PsGetProcessWin32WindowStation(ppi->peProcess);
 
     /* Dereference the previous window station */
     if (OldWinSta != NULL)
@@ -961,17 +1402,64 @@ UserSetProcessWindowStation(HWINSTA hWindowStation)
         ObDereferenceObject(OldWinSta);
     }
 
-    /* Check if we have a stale handle (it should happen for console apps) */
-    if (hwinstaOld != ppi->hwinsta)
-    {
-        ObCloseHandle(hwinstaOld, UserMode);
-    }
-
     /*
-     * FIXME: Don't allow changing the window station if there are threads that are attached to desktops and own GUI objects.
+     * FIXME: Don't allow changing the window station if there are threads that are attached to desktops and own GUI objects?
      */
 
-    PsSetProcessWindowStation(ppi->peProcess, hWindowStation);
+    /* Close the cached EPROCESS window station handle if needed */
+    if (hCacheWinSta != NULL)
+    {
+        /* Reference the window station */
+        Status = ObReferenceObjectByHandle(hCacheWinSta,
+                                           0,
+                                           ExWindowStationObjectType,
+                                           UserMode,
+                                           (PVOID*)&OldWinSta,
+                                           NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("Failed to reference the inherited window station, Status 0x%08lx\n", Status);
+            /* We failed, reset the cache */
+            hCacheWinSta = NULL;
+            PsSetProcessWindowStation(ppi->peProcess, hCacheWinSta);
+        }
+        else
+        {
+            /*
+             * Close the old handle and reset the cache only
+             * if we are setting a different window station.
+             */
+            if (NewWinSta != OldWinSta)
+            {
+                ObCloseHandle(hCacheWinSta, UserMode);
+                hCacheWinSta = NULL;
+                PsSetProcessWindowStation(ppi->peProcess, hCacheWinSta);
+            }
+
+            /* Dereference the window station */
+            ObDereferenceObject(OldWinSta);
+        }
+    }
+
+    /* Duplicate and save a new cached EPROCESS window station handle */
+    if ((hCacheWinSta == NULL) && (hWindowStation != NULL))
+    {
+        Status = ZwDuplicateObject(ZwCurrentProcess(),
+                                   hWindowStation,
+                                   ZwCurrentProcess(),
+                                   (PHANDLE)&hCacheWinSta,
+                                   0,
+                                   0,
+                                   DUPLICATE_SAME_ACCESS);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("UserSetProcessWindowStation: Failed to duplicate the window station handle, Status 0x%08lx\n", Status);
+        }
+        else
+        {
+            PsSetProcessWindowStation(ppi->peProcess, hCacheWinSta);
+        }
+    }
 
     ppi->prpwinsta = NewWinSta;
     ppi->hwinsta = hWindowStation;
@@ -991,7 +1479,7 @@ UserSetProcessWindowStation(HWINSTA hWindowStation)
     {
         ppi->W32PF_flags |= W32PF_IOWINSTA;
     }
-    else // Might be closed if the handle is null.
+    else /* Might be closed if the handle is NULL */
     {
         ppi->W32PF_flags &= ~W32PF_IOWINSTA;
     }
@@ -1057,7 +1545,7 @@ NtUserLockWindowStation(HWINSTA hWindowStation)
                                             UserMode,
                                             0,
                                             &Object,
-                                            0);
+                                            NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Validation of window station handle (%p) failed\n",
@@ -1102,7 +1590,7 @@ NtUserUnlockWindowStation(HWINSTA hWindowStation)
                                             UserMode,
                                             0,
                                             &Object,
-                                            0);
+                                            NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Validation of window station handle (%p) failed\n",
@@ -1133,6 +1621,14 @@ BuildWindowStationNameList(
     POBJECT_DIRECTORY_INFORMATION DirEntry;
     WCHAR NullWchar;
 
+    //
+    // FIXME: Fully wrong! Since, by calling NtUserCreateWindowStation
+    // with judicious parameters one can create window stations elsewhere
+    // than in Windows\WindowStations directory, Win32k definitely MUST
+    // maintain a list of window stations it has created, and not rely
+    // on the enumeration of Windows\WindowStations !!!
+    //
+
     /*
      * Try to open the directory.
      */
@@ -1318,7 +1814,7 @@ BuildDesktopNameList(
                                             UserMode,
                                             0,
                                             &WindowStation,
-                                            0);
+                                            NULL);
     if (! NT_SUCCESS(Status))
     {
         return Status;
@@ -1580,5 +2076,4 @@ Leave:
     return Ret;
 }
 
-
 /* EOF */