mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
db419efbf2
Create the section with PAGE_READWRITE, not PAGE_EXECUTE_READWRITE and map in user mode with PAGE_READONLY, not PAGE_EXECUTE_READ.
Original commit was ea5261f
in 2006. The comments suggest that back then the heap code had issues with it, which is not the case anymore.
3505 lines
108 KiB
C
3505 lines
108 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Win32k subsystem
|
|
* PURPOSE: Desktops
|
|
* FILE: subsystems/win32/win32k/ntuser/desktop.c
|
|
* PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <win32k.h>
|
|
DBG_DEFAULT_CHANNEL(UserDesktop);
|
|
|
|
#include <reactos/buildno.h>
|
|
|
|
static NTSTATUS
|
|
UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta);
|
|
|
|
static NTSTATUS
|
|
IntMapDesktopView(IN PDESKTOP pdesk);
|
|
|
|
static NTSTATUS
|
|
IntUnmapDesktopView(IN PDESKTOP pdesk);
|
|
|
|
static VOID
|
|
IntFreeDesktopHeap(IN PDESKTOP pdesk);
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
/* These can be changed via registry settings.
|
|
* Default values (interactive desktop / non-interactive desktop):
|
|
* Windows 2003 x86: 3 MB / 512 KB (Disconnect: 64 KB, Winlogon: 128 KB)
|
|
* Windows 7 x86: 12 MB / 512 KB
|
|
* Windows 7 x64: 20 MB / 768 KB
|
|
* Windows 10 x64: 20 MB / 4 MB
|
|
* See:
|
|
* - https://dbmentors.blogspot.com/2011/09/desktop-heap-overview.html
|
|
* - https://www.ibm.com/support/pages/using-microsoft-desktop-heap-monitor-dheapmon-determine-desktop-heap-space-and-troubleshoot-filenet-p8-and-image-services-issues
|
|
* - https://www.betaarchive.com/wiki/index.php?title=Microsoft_KB_Archive/184802
|
|
* - https://kb.firedaemon.com/support/solutions/articles/4000086192-windows-service-quota-limits-and-desktop-heap-exhaustion
|
|
* - https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/desktop-heap-limitation-out-of-memory
|
|
*/
|
|
#ifdef _WIN64
|
|
DWORD gdwDesktopSectionSize = 20 * 1024; // 20 MB (Windows 7 style)
|
|
#else
|
|
DWORD gdwDesktopSectionSize = 3 * 1024; // 3 MB (Windows 2003 style)
|
|
#endif
|
|
DWORD gdwNOIOSectionSize = 128;
|
|
DWORD gdwWinlogonSectionSize = 128;
|
|
|
|
/* Currently active desktop */
|
|
PDESKTOP gpdeskInputDesktop = NULL;
|
|
HDC ScreenDeviceContext = NULL;
|
|
PTHREADINFO gptiDesktopThread = NULL;
|
|
HCURSOR gDesktopCursor = NULL;
|
|
PKEVENT gpDesktopThreadStartedEvent = NULL;
|
|
|
|
/* OBJECT CALLBACKS **********************************************************/
|
|
|
|
NTSTATUS
|
|
APIENTRY
|
|
IntDesktopObjectParse(IN PVOID ParseObject,
|
|
IN PVOID ObjectType,
|
|
IN OUT PACCESS_STATE AccessState,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
IN ULONG Attributes,
|
|
IN OUT PUNICODE_STRING CompleteName,
|
|
IN OUT PUNICODE_STRING RemainingName,
|
|
IN OUT PVOID Context OPTIONAL,
|
|
IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
|
|
OUT PVOID *Object)
|
|
{
|
|
NTSTATUS Status;
|
|
PDESKTOP Desktop;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PLIST_ENTRY NextEntry, ListHead;
|
|
PWINSTATION_OBJECT WinStaObject = (PWINSTATION_OBJECT)ParseObject;
|
|
UNICODE_STRING DesktopName;
|
|
PBOOLEAN pContext = (PBOOLEAN) Context;
|
|
|
|
if (pContext)
|
|
*pContext = FALSE;
|
|
|
|
/* Set the list pointers and loop the window station */
|
|
ListHead = &WinStaObject->DesktopListHead;
|
|
NextEntry = ListHead->Flink;
|
|
while (NextEntry != ListHead)
|
|
{
|
|
/* Get the current desktop */
|
|
Desktop = CONTAINING_RECORD(NextEntry, DESKTOP, ListEntry);
|
|
|
|
/* Get the desktop name */
|
|
ASSERT(Desktop->pDeskInfo != NULL);
|
|
RtlInitUnicodeString(&DesktopName, Desktop->pDeskInfo->szDesktopName);
|
|
|
|
/* Compare the name */
|
|
if (RtlEqualUnicodeString(RemainingName,
|
|
&DesktopName,
|
|
(Attributes & OBJ_CASE_INSENSITIVE) != 0))
|
|
{
|
|
/* We found a match. Did this come from a create? */
|
|
if (Context)
|
|
{
|
|
/* Unless OPEN_IF was given, fail with an error */
|
|
if (!(Attributes & OBJ_OPENIF))
|
|
{
|
|
/* Name collision */
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, return with a warning only */
|
|
Status = STATUS_OBJECT_NAME_EXISTS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This was a real open, so this is OK */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Reference the desktop and return it */
|
|
ObReferenceObject(Desktop);
|
|
*Object = Desktop;
|
|
return Status;
|
|
}
|
|
|
|
/* Go to the next desktop */
|
|
NextEntry = NextEntry->Flink;
|
|
}
|
|
|
|
/* If we got here but this isn't a create, just fail */
|
|
if (!Context) return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
/* Create the desktop object */
|
|
InitializeObjectAttributes(&ObjectAttributes, RemainingName, 0, NULL, NULL);
|
|
Status = ObCreateObject(KernelMode,
|
|
ExDesktopObjectType,
|
|
&ObjectAttributes,
|
|
KernelMode,
|
|
NULL,
|
|
sizeof(DESKTOP),
|
|
0,
|
|
0,
|
|
(PVOID*)&Desktop);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Assign security to the desktop we have created */
|
|
Status = IntAssignDesktopSecurityOnParse(WinStaObject, Desktop, AccessState);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(Desktop);
|
|
return Status;
|
|
}
|
|
|
|
/* Initialize the desktop */
|
|
Status = UserInitializeDesktop(Desktop, RemainingName, WinStaObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(Desktop);
|
|
return Status;
|
|
}
|
|
|
|
/* Set the desktop object and return success */
|
|
*Object = Desktop;
|
|
*pContext = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IntDesktopObjectDelete(
|
|
_In_ PVOID Parameters)
|
|
{
|
|
PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters = Parameters;
|
|
PDESKTOP pdesk = (PDESKTOP)DeleteParameters->Object;
|
|
|
|
TRACE("Deleting desktop object 0x%p\n", pdesk);
|
|
|
|
if (pdesk->pDeskInfo &&
|
|
pdesk->pDeskInfo->spwnd)
|
|
{
|
|
ASSERT(pdesk->pDeskInfo->spwnd->spwndChild == NULL);
|
|
co_UserDestroyWindow(pdesk->pDeskInfo->spwnd);
|
|
}
|
|
|
|
if (pdesk->spwndMessage)
|
|
co_UserDestroyWindow(pdesk->spwndMessage);
|
|
|
|
/* Remove the desktop from the window station's list of associated desktops */
|
|
RemoveEntryList(&pdesk->ListEntry);
|
|
|
|
/* Free the heap */
|
|
IntFreeDesktopHeap(pdesk);
|
|
|
|
ObDereferenceObject(pdesk->rpwinstaParent);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IntDesktopOkToClose(
|
|
_In_ PVOID Parameters)
|
|
{
|
|
PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters = Parameters;
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
|
|
if (pti == NULL)
|
|
{
|
|
/* This happens when we leak desktop handles */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Do not allow the current desktop or the initial desktop to be closed */
|
|
if (OkToCloseParameters->Handle == pti->ppi->hdeskStartup ||
|
|
OkToCloseParameters->Handle == pti->hdesk)
|
|
{
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IntDesktopObjectOpen(
|
|
_In_ PVOID Parameters)
|
|
{
|
|
NTSTATUS Ret;
|
|
PWIN32_OPENMETHOD_PARAMETERS OpenParameters = Parameters;
|
|
PPROCESSINFO ppi = PsGetProcessWin32Process(OpenParameters->Process);
|
|
if (ppi == NULL)
|
|
return STATUS_SUCCESS;
|
|
|
|
UserEnterExclusive();
|
|
Ret = IntMapDesktopView((PDESKTOP)OpenParameters->Object);
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IntDesktopObjectClose(
|
|
_In_ PVOID Parameters)
|
|
{
|
|
NTSTATUS Ret;
|
|
PWIN32_CLOSEMETHOD_PARAMETERS CloseParameters = Parameters;
|
|
PPROCESSINFO ppi = PsGetProcessWin32Process(CloseParameters->Process);
|
|
if (ppi == NULL)
|
|
{
|
|
/* This happens when the process leaks desktop handles.
|
|
* At this point the PPROCESSINFO is already destroyed */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
UserEnterExclusive();
|
|
Ret = IntUnmapDesktopView((PDESKTOP)CloseParameters->Object);
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
CODE_SEG("INIT")
|
|
NTSTATUS
|
|
NTAPI
|
|
InitDesktopImpl(VOID)
|
|
{
|
|
GENERIC_MAPPING IntDesktopMapping = { DESKTOP_READ,
|
|
DESKTOP_WRITE,
|
|
DESKTOP_EXECUTE,
|
|
DESKTOP_ALL_ACCESS};
|
|
|
|
/* Set Desktop Object Attributes */
|
|
ExDesktopObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(DESKTOP);
|
|
ExDesktopObjectType->TypeInfo.GenericMapping = IntDesktopMapping;
|
|
ExDesktopObjectType->TypeInfo.ValidAccessMask = DESKTOP_ALL_ACCESS;
|
|
|
|
/* Allocate memory for the event structure */
|
|
gpDesktopThreadStartedEvent = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(KEVENT),
|
|
USERTAG_EVENT);
|
|
if (!gpDesktopThreadStartedEvent)
|
|
{
|
|
ERR("Failed to allocate event!\n");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Initialize the kernel event */
|
|
KeInitializeEvent(gpDesktopThreadStartedEvent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS
|
|
GetSystemVersionString(OUT PWSTR pwszzVersion,
|
|
IN SIZE_T cchDest,
|
|
IN BOOLEAN InSafeMode,
|
|
IN BOOLEAN AppendNtSystemRoot)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RTL_OSVERSIONINFOEXW VerInfo;
|
|
UNICODE_STRING BuildLabString;
|
|
UNICODE_STRING CSDVersionString;
|
|
RTL_QUERY_REGISTRY_TABLE VersionConfigurationTable[] =
|
|
{
|
|
{
|
|
NULL,
|
|
RTL_QUERY_REGISTRY_DIRECT,
|
|
L"BuildLab",
|
|
&BuildLabString,
|
|
REG_NONE, NULL, 0
|
|
},
|
|
{
|
|
NULL,
|
|
RTL_QUERY_REGISTRY_DIRECT,
|
|
L"CSDVersion",
|
|
&CSDVersionString,
|
|
REG_NONE, NULL, 0
|
|
},
|
|
|
|
{0}
|
|
};
|
|
|
|
WCHAR BuildLabBuffer[256];
|
|
WCHAR VersionBuffer[256];
|
|
PWCHAR EndBuffer;
|
|
|
|
VerInfo.dwOSVersionInfoSize = sizeof(VerInfo);
|
|
|
|
/*
|
|
* This call is uniquely used to retrieve the current CSD numbers.
|
|
* All the rest (major, minor, ...) is either retrieved from the
|
|
* SharedUserData structure, or from the registry.
|
|
*/
|
|
RtlGetVersion((PRTL_OSVERSIONINFOW)&VerInfo);
|
|
|
|
/*
|
|
* - Retrieve the BuildLab string from the registry (set by the kernel).
|
|
* - In kernel-mode, szCSDVersion is not initialized. Initialize it
|
|
* and query its value from the registry.
|
|
*/
|
|
RtlZeroMemory(BuildLabBuffer, sizeof(BuildLabBuffer));
|
|
RtlInitEmptyUnicodeString(&BuildLabString,
|
|
BuildLabBuffer,
|
|
sizeof(BuildLabBuffer));
|
|
RtlZeroMemory(VerInfo.szCSDVersion, sizeof(VerInfo.szCSDVersion));
|
|
RtlInitEmptyUnicodeString(&CSDVersionString,
|
|
VerInfo.szCSDVersion,
|
|
sizeof(VerInfo.szCSDVersion));
|
|
Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
|
|
L"",
|
|
VersionConfigurationTable,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Indicate nothing is there */
|
|
BuildLabString.Length = 0;
|
|
CSDVersionString.Length = 0;
|
|
}
|
|
/* NULL-terminate the strings */
|
|
BuildLabString.Buffer[BuildLabString.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
CSDVersionString.Buffer[CSDVersionString.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
EndBuffer = VersionBuffer;
|
|
if ( /* VerInfo.wServicePackMajor != 0 && */ CSDVersionString.Length)
|
|
{
|
|
/* Print the version string */
|
|
Status = RtlStringCbPrintfExW(VersionBuffer,
|
|
sizeof(VersionBuffer),
|
|
&EndBuffer,
|
|
NULL,
|
|
0,
|
|
L": %wZ",
|
|
&CSDVersionString);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* No version, NULL-terminate the string */
|
|
*EndBuffer = UNICODE_NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No version, NULL-terminate the string */
|
|
*EndBuffer = UNICODE_NULL;
|
|
}
|
|
|
|
if (InSafeMode)
|
|
{
|
|
/* String for Safe Mode */
|
|
Status = RtlStringCchPrintfW(pwszzVersion,
|
|
cchDest,
|
|
L"ReactOS Version %S %wZ (NT %u.%u Build %u%s)\n",
|
|
KERNEL_VERSION_STR,
|
|
&BuildLabString,
|
|
SharedUserData->NtMajorVersion,
|
|
SharedUserData->NtMinorVersion,
|
|
(VerInfo.dwBuildNumber & 0xFFFF),
|
|
VersionBuffer);
|
|
|
|
if (AppendNtSystemRoot && NT_SUCCESS(Status))
|
|
{
|
|
Status = RtlStringCbPrintfW(VersionBuffer,
|
|
sizeof(VersionBuffer),
|
|
L" - %s\n",
|
|
SharedUserData->NtSystemRoot);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Replace the last newline by a NULL, before concatenating */
|
|
EndBuffer = wcsrchr(pwszzVersion, L'\n');
|
|
if (EndBuffer) *EndBuffer = UNICODE_NULL;
|
|
|
|
/* The concatenated string has a terminating newline */
|
|
Status = RtlStringCchCatW(pwszzVersion,
|
|
cchDest,
|
|
VersionBuffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Concatenation failed, put back the newline */
|
|
if (EndBuffer) *EndBuffer = L'\n';
|
|
}
|
|
}
|
|
|
|
/* Override any failures as the NtSystemRoot string is optional */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Multi-string for Normal Mode */
|
|
Status = RtlStringCchPrintfW(pwszzVersion,
|
|
cchDest,
|
|
L"ReactOS Version %S\n"
|
|
L"Build %wZ\n"
|
|
L"Reporting NT %u.%u (Build %u%s)\n",
|
|
KERNEL_VERSION_STR,
|
|
&BuildLabString,
|
|
SharedUserData->NtMajorVersion,
|
|
SharedUserData->NtMinorVersion,
|
|
(VerInfo.dwBuildNumber & 0xFFFF),
|
|
VersionBuffer);
|
|
|
|
if (AppendNtSystemRoot && NT_SUCCESS(Status))
|
|
{
|
|
Status = RtlStringCbPrintfW(VersionBuffer,
|
|
sizeof(VersionBuffer),
|
|
L"%s\n",
|
|
SharedUserData->NtSystemRoot);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = RtlStringCchCatW(pwszzVersion,
|
|
cchDest,
|
|
VersionBuffer);
|
|
}
|
|
|
|
/* Override any failures as the NtSystemRoot string is optional */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fall-back string */
|
|
Status = RtlStringCchPrintfW(pwszzVersion,
|
|
cchDest,
|
|
L"ReactOS Version %S %wZ\n",
|
|
KERNEL_VERSION_STR,
|
|
&BuildLabString);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* General failure, NULL-terminate the string */
|
|
pwszzVersion[0] = UNICODE_NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert the string separators (newlines) into NULLs
|
|
* and NULL-terminate the multi-string.
|
|
*/
|
|
while (*pwszzVersion)
|
|
{
|
|
EndBuffer = wcschr(pwszzVersion, L'\n');
|
|
if (!EndBuffer) break;
|
|
pwszzVersion = EndBuffer;
|
|
|
|
*pwszzVersion++ = UNICODE_NULL;
|
|
}
|
|
*pwszzVersion = UNICODE_NULL;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*
|
|
* IntResolveDesktop
|
|
*
|
|
* The IntResolveDesktop function attempts to retrieve valid handles to
|
|
* a desktop and a window station suitable for the specified process.
|
|
* The specified desktop path string is used only as a hint for the resolution.
|
|
*
|
|
* - If the process is already assigned to a window station and a desktop,
|
|
* handles to these objects are returned directly regardless of the specified
|
|
* desktop path string. This is what happens when this function is called for
|
|
* a process that has been already started and connected to the Win32 USER.
|
|
*
|
|
* - If the process is being connected to the Win32 USER, or is in a state
|
|
* where a window station is assigned to it but no desktop yet, the desktop
|
|
* path string is used as a hint for the resolution.
|
|
* A specified window station (if any, otherwise "WinSta0" is used as default)
|
|
* is tested for existence and accessibility. If the checks are OK a handle
|
|
* to it is returned. Otherwise we either fail (the window station does not
|
|
* exist) or, in case a default window station was used, we attempt to open
|
|
* or create a non-interactive Service-0xXXXX-YYYY$ window station. This is
|
|
* typically what happens when a non-interactive process is started while
|
|
* the WinSta0 window station was used as the default one.
|
|
* A specified desktop (if any, otherwise "Default" is used as default)
|
|
* is then tested for existence on the opened window station.
|
|
*
|
|
* - Rules for the choice of the default window station, when none is specified
|
|
* in the desktop path:
|
|
*
|
|
* 1. By default, a SYSTEM process connects to a non-interactive window
|
|
* station, either the Service-0x0-3e7$ (from the SYSTEM LUID) station,
|
|
* or one that has been inherited and that is non-interactive.
|
|
* Only when the interactive window station WinSta0 is specified that
|
|
* the process can connect to it (e.g. the case of interactive services).
|
|
*
|
|
* 2. An interactive process, i.e. a process whose LUID is the same as the
|
|
* one assigned to WinSta0 by Winlogon on user logon, connects by default
|
|
* to the WinSta0 window station, unless it has inherited from another
|
|
* interactive window station (which must be... none other than WinSta0).
|
|
*
|
|
* 3. A non-interactive (but not SYSTEM) process connects by default to
|
|
* a non-interactive Service-0xXXXX-YYYY$ window station (whose name
|
|
* is derived from the process' LUID), or to another non-interactive
|
|
* window station that has been inherited.
|
|
* Otherwise it may be able connect to the interactive WinSta0 only if
|
|
* it has explicit access rights to it.
|
|
*
|
|
* Parameters
|
|
* Process
|
|
* The user process object.
|
|
*
|
|
* DesktopPath
|
|
* The desktop path string used as a hint for desktop resolution.
|
|
*
|
|
* bInherit
|
|
* Whether or not the returned handles are inheritable.
|
|
*
|
|
* phWinSta
|
|
* Pointer to a window station handle.
|
|
*
|
|
* phDesktop
|
|
* Pointer to a desktop handle.
|
|
*
|
|
* Return Value
|
|
* Status code.
|
|
*/
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
IntResolveDesktop(
|
|
IN PEPROCESS Process,
|
|
IN PUNICODE_STRING DesktopPath,
|
|
IN BOOL bInherit,
|
|
OUT HWINSTA* phWinSta,
|
|
OUT HDESK* phDesktop)
|
|
{
|
|
NTSTATUS Status;
|
|
HWINSTA hWinSta = NULL, hWinStaDup = NULL;
|
|
HDESK hDesktop = NULL, hDesktopDup = NULL;
|
|
PPROCESSINFO ppi;
|
|
HANDLE hProcess = NULL;
|
|
LUID ProcessLuid;
|
|
USHORT StrSize;
|
|
SIZE_T MemSize;
|
|
PSECURITY_DESCRIPTOR ServiceSD;
|
|
POBJECT_ATTRIBUTES ObjectAttributes = NULL;
|
|
PUNICODE_STRING ObjectName;
|
|
UNICODE_STRING WinStaName, DesktopName;
|
|
const UNICODE_STRING WinSta0Name = RTL_CONSTANT_STRING(L"WinSta0");
|
|
PWINSTATION_OBJECT WinStaObject;
|
|
HWINSTA hTempWinSta = NULL;
|
|
BOOLEAN bUseDefaultWinSta = FALSE;
|
|
BOOLEAN bInteractive = FALSE;
|
|
BOOLEAN bAccessAllowed = FALSE;
|
|
|
|
ASSERT(UserIsEnteredExclusive());
|
|
|
|
ASSERT(phWinSta);
|
|
ASSERT(phDesktop);
|
|
ASSERT(DesktopPath);
|
|
|
|
*phWinSta = NULL;
|
|
*phDesktop = NULL;
|
|
|
|
ppi = PsGetProcessWin32Process(Process);
|
|
/* ppi is typically NULL for console applications that connect to Win32 USER */
|
|
if (!ppi) TRACE("IntResolveDesktop: ppi is NULL!\n");
|
|
|
|
if (ppi && ppi->hwinsta != NULL && ppi->hdeskStartup != NULL)
|
|
{
|
|
/*
|
|
* If this process is the current one, just return the cached handles.
|
|
* Otherwise, open the window station and desktop objects.
|
|
*/
|
|
if (Process == PsGetCurrentProcess())
|
|
{
|
|
hWinSta = ppi->hwinsta;
|
|
hDesktop = ppi->hdeskStartup;
|
|
}
|
|
else
|
|
{
|
|
Status = ObOpenObjectByPointer(ppi->prpwinsta,
|
|
0,
|
|
NULL,
|
|
MAXIMUM_ALLOWED,
|
|
ExWindowStationObjectType,
|
|
UserMode,
|
|
(PHANDLE)&hWinSta);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("IntResolveDesktop: Could not reference window station 0x%p\n", ppi->prpwinsta);
|
|
SetLastNtError(Status);
|
|
return Status;
|
|
}
|
|
|
|
Status = ObOpenObjectByPointer(ppi->rpdeskStartup,
|
|
0,
|
|
NULL,
|
|
MAXIMUM_ALLOWED,
|
|
ExDesktopObjectType,
|
|
UserMode,
|
|
(PHANDLE)&hDesktop);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("IntResolveDesktop: Could not reference desktop 0x%p\n", ppi->rpdeskStartup);
|
|
ObCloseHandle(hWinSta, UserMode);
|
|
SetLastNtError(Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
*phWinSta = hWinSta;
|
|
*phDesktop = hDesktop;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* We will by default use the default window station and desktop */
|
|
RtlInitEmptyUnicodeString(&WinStaName, NULL, 0);
|
|
RtlInitEmptyUnicodeString(&DesktopName, NULL, 0);
|
|
|
|
/*
|
|
* Parse the desktop path string which can be of the form "WinSta\Desktop"
|
|
* or just "Desktop". In the latter case we use the default window station
|
|
* on which the process is attached to (or if none, "WinSta0").
|
|
*/
|
|
if (DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
|
|
{
|
|
DesktopName = *DesktopPath;
|
|
|
|
/* Find the separator */
|
|
while (DesktopName.Length > 0 && *DesktopName.Buffer &&
|
|
*DesktopName.Buffer != OBJ_NAME_PATH_SEPARATOR)
|
|
{
|
|
DesktopName.Buffer++;
|
|
DesktopName.Length -= sizeof(WCHAR);
|
|
DesktopName.MaximumLength -= sizeof(WCHAR);
|
|
}
|
|
if (DesktopName.Length > 0)
|
|
{
|
|
RtlInitEmptyUnicodeString(&WinStaName, DesktopPath->Buffer,
|
|
DesktopPath->Length - DesktopName.Length);
|
|
// (USHORT)((ULONG_PTR)DesktopName.Buffer - (ULONG_PTR)DesktopPath->Buffer);
|
|
WinStaName.Length = WinStaName.MaximumLength;
|
|
|
|
/* Skip the separator */
|
|
DesktopName.Buffer++;
|
|
DesktopName.Length -= sizeof(WCHAR);
|
|
DesktopName.MaximumLength -= sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
RtlInitEmptyUnicodeString(&WinStaName, NULL, 0);
|
|
DesktopName = *DesktopPath;
|
|
}
|
|
}
|
|
|
|
TRACE("IntResolveDesktop: WinStaName:'%wZ' ; DesktopName:'%wZ'\n", &WinStaName, &DesktopName);
|
|
|
|
/* Retrieve the process LUID */
|
|
Status = GetProcessLuid(NULL, Process, &ProcessLuid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("IntResolveDesktop: Failed to retrieve the process LUID, Status 0x%08lx\n", Status);
|
|
SetLastNtError(Status);
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* If this process is not the current one, obtain a temporary handle
|
|
* to it so that we can perform handles duplication later.
|
|
*/
|
|
if (Process != PsGetCurrentProcess())
|
|
{
|
|
Status = ObOpenObjectByPointer(Process,
|
|
OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
0,
|
|
*PsProcessType,
|
|
KernelMode,
|
|
&hProcess);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("IntResolveDesktop: Failed to obtain a handle to process 0x%p, Status 0x%08lx\n", Process, Status);
|
|
SetLastNtError(Status);
|
|
return Status;
|
|
}
|
|
ASSERT(hProcess);
|
|
}
|
|
|
|
/*
|
|
* If no window station has been specified, search the process handle table
|
|
* for inherited window station handles, otherwise use a default one.
|
|
*/
|
|
if (WinStaName.Buffer == NULL)
|
|
{
|
|
/*
|
|
* We want to find a suitable default window station.
|
|
* For applications that can be interactive, i.e. that have allowed
|
|
* access to the single interactive window station on the system,
|
|
* the default window station is 'WinSta0'.
|
|
* For applications that cannot be interactive, i.e. that do not have
|
|
* access to 'WinSta0' (e.g. non-interactive services), the default
|
|
* window station is 'Service-0xXXXX-YYYY$' (created if needed).
|
|
* Precedence will however be taken by any inherited window station
|
|
* that possesses the required interactivity property.
|
|
*/
|
|
bUseDefaultWinSta = TRUE;
|
|
|
|
/*
|
|
* Use the default 'WinSta0' window station. Whether we should
|
|
* use 'Service-0xXXXX-YYYY$' instead will be determined later.
|
|
*/
|
|
// RtlInitUnicodeString(&WinStaName, L"WinSta0");
|
|
WinStaName = WinSta0Name;
|
|
|
|
if (ObFindHandleForObject(Process,
|
|
NULL,
|
|
ExWindowStationObjectType,
|
|
NULL,
|
|
(PHANDLE)&hWinSta))
|
|
{
|
|
TRACE("IntResolveDesktop: Inherited window station is: 0x%p\n", hWinSta);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If no desktop has been specified, search the process handle table
|
|
* for inherited desktop handles, otherwise use the Default desktop.
|
|
* Note that the inherited desktop that we may use, may not belong
|
|
* to the window station we will connect to.
|
|
*/
|
|
if (DesktopName.Buffer == NULL)
|
|
{
|
|
/* Use a default desktop name */
|
|
RtlInitUnicodeString(&DesktopName, L"Default");
|
|
|
|
if (ObFindHandleForObject(Process,
|
|
NULL,
|
|
ExDesktopObjectType,
|
|
NULL,
|
|
(PHANDLE)&hDesktop))
|
|
{
|
|
TRACE("IntResolveDesktop: Inherited desktop is: 0x%p\n", hDesktop);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* We are going to open either a window station or a desktop.
|
|
* Even if this operation is done from kernel-mode, we should
|
|
* "emulate" an opening from user-mode (i.e. using an ObjectAttributes
|
|
* allocated in user-mode, with AccessMode == UserMode) for the
|
|
* Object Manager to perform proper access validation to the
|
|
* window station or desktop.
|
|
*/
|
|
|
|
/*
|
|
* Estimate the maximum size needed for the window station name
|
|
* and desktop name to be given to ObjectAttributes->ObjectName.
|
|
*/
|
|
StrSize = 0;
|
|
|
|
/* Window station name */
|
|
MemSize = _scwprintf(L"Service-0x%x-%x$", MAXULONG, MAXULONG) * sizeof(WCHAR);
|
|
MemSize = gustrWindowStationsDir.Length + sizeof(OBJ_NAME_PATH_SEPARATOR)
|
|
+ max(WinStaName.Length, MemSize) + sizeof(UNICODE_NULL);
|
|
if (MemSize > MAXUSHORT)
|
|
{
|
|
ERR("IntResolveDesktop: Window station name length is too long.\n");
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Quit;
|
|
}
|
|
StrSize = max(StrSize, (USHORT)MemSize);
|
|
|
|
/* Desktop name */
|
|
MemSize = max(DesktopName.Length + sizeof(UNICODE_NULL), sizeof(L"Default"));
|
|
StrSize = max(StrSize, (USHORT)MemSize);
|
|
|
|
/* Size for the OBJECT_ATTRIBUTES */
|
|
MemSize = ALIGN_UP(sizeof(OBJECT_ATTRIBUTES), sizeof(PVOID));
|
|
|
|
/* Add the string size */
|
|
MemSize += ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID));
|
|
MemSize += StrSize;
|
|
|
|
/* Allocate the memory in user-mode */
|
|
Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
|
|
(PVOID*)&ObjectAttributes,
|
|
0,
|
|
&MemSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status);
|
|
goto Quit;
|
|
}
|
|
|
|
ObjectName = (PUNICODE_STRING)((ULONG_PTR)ObjectAttributes +
|
|
ALIGN_UP(sizeof(OBJECT_ATTRIBUTES), sizeof(PVOID)));
|
|
|
|
RtlInitEmptyUnicodeString(ObjectName,
|
|
(PWCHAR)((ULONG_PTR)ObjectName +
|
|
ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID))),
|
|
StrSize);
|
|
|
|
|
|
/* If we got an inherited window station handle, duplicate and use it */
|
|
if (hWinSta)
|
|
{
|
|
ASSERT(bUseDefaultWinSta);
|
|
|
|
/* Duplicate the handle if it belongs to another process than the current one */
|
|
if (Process != PsGetCurrentProcess())
|
|
{
|
|
ASSERT(hProcess);
|
|
Status = ZwDuplicateObject(hProcess,
|
|
hWinSta,
|
|
ZwCurrentProcess(),
|
|
(PHANDLE)&hWinStaDup,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("IntResolveDesktop: Failed to duplicate the window station handle, Status 0x%08lx\n", Status);
|
|
/* We will use a default window station */
|
|
hWinSta = NULL;
|
|
}
|
|
else
|
|
{
|
|
hWinSta = hWinStaDup;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we have an inherited window station, check whether
|
|
* it is interactive and remember that for later.
|
|
*/
|
|
if (hWinSta)
|
|
{
|
|
ASSERT(bUseDefaultWinSta);
|
|
|
|
/* Reference the inherited window station */
|
|
Status = ObReferenceObjectByHandle(hWinSta,
|
|
0,
|
|
ExWindowStationObjectType,
|
|
KernelMode,
|
|
(PVOID*)&WinStaObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to reference the inherited window station, Status 0x%08lx\n", Status);
|
|
/* We will use a default window station */
|
|
if (hWinStaDup)
|
|
{
|
|
ASSERT(hWinSta == hWinStaDup);
|
|
ObCloseHandle(hWinStaDup, UserMode);
|
|
hWinStaDup = NULL;
|
|
}
|
|
hWinSta = NULL;
|
|
}
|
|
else
|
|
{
|
|
ERR("Process LUID is: 0x%x-%x, inherited window station LUID is: 0x%x-%x\n",
|
|
ProcessLuid.HighPart, ProcessLuid.LowPart,
|
|
WinStaObject->luidUser.HighPart, WinStaObject->luidUser.LowPart);
|
|
|
|
/* Check whether this window station is interactive, and remember it for later */
|
|
bInteractive = !(WinStaObject->Flags & WSS_NOIO);
|
|
|
|
/* Dereference the window station */
|
|
ObDereferenceObject(WinStaObject);
|
|
}
|
|
}
|
|
|
|
/* Build a valid window station name */
|
|
Status = RtlStringCbPrintfW(ObjectName->Buffer,
|
|
ObjectName->MaximumLength,
|
|
L"%wZ\\%wZ",
|
|
&gustrWindowStationsDir,
|
|
&WinStaName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
|
|
goto Quit;
|
|
}
|
|
ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
|
|
|
|
TRACE("Parsed initial window station: '%wZ'\n", ObjectName);
|
|
|
|
/* Try to open the window station */
|
|
InitializeObjectAttributes(ObjectAttributes,
|
|
ObjectName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
if (bInherit)
|
|
ObjectAttributes->Attributes |= OBJ_INHERIT;
|
|
|
|
Status = ObOpenObjectByName(ObjectAttributes,
|
|
ExWindowStationObjectType,
|
|
UserMode,
|
|
NULL,
|
|
WINSTA_ACCESS_ALL,
|
|
NULL,
|
|
(PHANDLE)&hTempWinSta);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to open the window station '%wZ', Status 0x%08lx\n", ObjectName, Status);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// FIXME TODO: Perform a window station access check!!
|
|
// If we fail AND bUseDefaultWinSta == FALSE we just quit.
|
|
//
|
|
|
|
/*
|
|
* Check whether we are opening the (single) interactive
|
|
* window station, and if so, perform an access check.
|
|
*/
|
|
/* Check whether we are allowed to perform interactions */
|
|
if (RtlEqualUnicodeString(&WinStaName, &WinSta0Name, TRUE))
|
|
{
|
|
LUID SystemLuid = SYSTEM_LUID;
|
|
|
|
/* Interactive window station: check for user LUID */
|
|
WinStaObject = InputWindowStation;
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
// TODO: Check also that we compare wrt. window station WinSta0
|
|
// which is the only one that can be interactive on the system.
|
|
if (((!bUseDefaultWinSta || bInherit) && RtlEqualLuid(&ProcessLuid, &SystemLuid)) ||
|
|
RtlEqualLuid(&ProcessLuid, &WinStaObject->luidUser))
|
|
{
|
|
/* We are interactive on this window station */
|
|
bAccessAllowed = TRUE;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Non-interactive window station: we have access since we were able to open it */
|
|
bAccessAllowed = TRUE;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* If we failed, bail out if we were not trying to open the default window station */
|
|
if (!NT_SUCCESS(Status) && !bUseDefaultWinSta) // if (!bAccessAllowed)
|
|
goto Quit;
|
|
|
|
if (/* bAccessAllowed && */ bInteractive || !bAccessAllowed)
|
|
{
|
|
/*
|
|
* Close WinSta0 if the inherited window station is interactive so that
|
|
* we can use it, or we do not have access to the interactive WinSta0.
|
|
*/
|
|
ObCloseHandle(hTempWinSta, UserMode);
|
|
hTempWinSta = NULL;
|
|
}
|
|
if (bInteractive == bAccessAllowed)
|
|
{
|
|
/* Keep using the inherited window station */
|
|
NOTHING;
|
|
}
|
|
else // if (bInteractive != bAccessAllowed)
|
|
{
|
|
/*
|
|
* Close the inherited window station, we will either keep using
|
|
* the interactive WinSta0, or use Service-0xXXXX-YYYY$.
|
|
*/
|
|
if (hWinStaDup)
|
|
{
|
|
ASSERT(hWinSta == hWinStaDup);
|
|
ObCloseHandle(hWinStaDup, UserMode);
|
|
hWinStaDup = NULL;
|
|
}
|
|
hWinSta = hTempWinSta; // hTempWinSta is NULL in case bAccessAllowed == FALSE
|
|
}
|
|
|
|
if (bUseDefaultWinSta)
|
|
{
|
|
if (hWinSta == NULL && !bInteractive)
|
|
{
|
|
/* Build a valid window station name from the LUID */
|
|
Status = RtlStringCbPrintfW(ObjectName->Buffer,
|
|
ObjectName->MaximumLength,
|
|
L"%wZ\\Service-0x%x-%x$",
|
|
&gustrWindowStationsDir,
|
|
ProcessLuid.HighPart,
|
|
ProcessLuid.LowPart);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
|
|
goto Quit;
|
|
}
|
|
ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
|
|
|
|
/*
|
|
* Set up a security descriptor for the new service's window station.
|
|
* A service has an associated window station and desktop. The newly
|
|
* created window station and desktop will get this security descriptor
|
|
* if such objects weren't created before.
|
|
*/
|
|
Status = IntCreateServiceSecurity(&ServiceSD);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to create a security descriptor for service window station, Status 0x%08lx\n", Status);
|
|
goto Quit;
|
|
}
|
|
|
|
/*
|
|
* Create or open the non-interactive window station.
|
|
* NOTE: The non-interactive window station handle is never inheritable.
|
|
*/
|
|
InitializeObjectAttributes(ObjectAttributes,
|
|
ObjectName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
|
|
NULL,
|
|
ServiceSD);
|
|
|
|
Status = IntCreateWindowStation(&hWinSta,
|
|
ObjectAttributes,
|
|
UserMode,
|
|
KernelMode,
|
|
MAXIMUM_ALLOWED,
|
|
0, 0, 0, 0, 0);
|
|
|
|
IntFreeSecurityBuffer(ServiceSD);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ASSERT(hWinSta == NULL);
|
|
ERR("Failed to create or open the non-interactive window station '%wZ', Status 0x%08lx\n",
|
|
ObjectName, Status);
|
|
goto Quit;
|
|
}
|
|
|
|
//
|
|
// FIXME: We might not need to always create or open the "Default"
|
|
// desktop on the Service-0xXXXX-YYYY$ window station; we may need
|
|
// to use another one....
|
|
//
|
|
|
|
/* Create or open the Default desktop on the window station */
|
|
Status = RtlStringCbCopyW(ObjectName->Buffer,
|
|
ObjectName->MaximumLength,
|
|
L"Default");
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status);
|
|
goto Quit;
|
|
}
|
|
ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
|
|
|
|
/*
|
|
* NOTE: The non-interactive desktop handle is never inheritable.
|
|
* The security descriptor is inherited from the newly created
|
|
* window station for the desktop.
|
|
*/
|
|
InitializeObjectAttributes(ObjectAttributes,
|
|
ObjectName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
|
|
hWinSta,
|
|
NULL);
|
|
|
|
Status = IntCreateDesktop(&hDesktop,
|
|
ObjectAttributes,
|
|
UserMode,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
MAXIMUM_ALLOWED);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ASSERT(hDesktop == NULL);
|
|
ERR("Failed to create or open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
|
|
ObjectName, hWinSta, Status);
|
|
}
|
|
|
|
goto Quit;
|
|
}
|
|
/*
|
|
if (hWinSta == NULL)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Quit;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* If we got an inherited desktop handle, duplicate and use it,
|
|
* otherwise open a new desktop.
|
|
*/
|
|
if (hDesktop != NULL)
|
|
{
|
|
/* Duplicate the handle if it belongs to another process than the current one */
|
|
if (Process != PsGetCurrentProcess())
|
|
{
|
|
ASSERT(hProcess);
|
|
Status = ZwDuplicateObject(hProcess,
|
|
hDesktop,
|
|
ZwCurrentProcess(),
|
|
(PHANDLE)&hDesktopDup,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("IntResolveDesktop: Failed to duplicate the desktop handle, Status 0x%08lx\n", Status);
|
|
/* We will use a default desktop */
|
|
hDesktop = NULL;
|
|
}
|
|
else
|
|
{
|
|
hDesktop = hDesktopDup;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((hWinSta != NULL) && (hDesktop == NULL))
|
|
{
|
|
Status = RtlStringCbCopyNW(ObjectName->Buffer,
|
|
ObjectName->MaximumLength,
|
|
DesktopName.Buffer,
|
|
DesktopName.Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status);
|
|
goto Quit;
|
|
}
|
|
ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
|
|
|
|
TRACE("Parsed initial desktop: '%wZ'\n", ObjectName);
|
|
|
|
/* Open the desktop object */
|
|
InitializeObjectAttributes(ObjectAttributes,
|
|
ObjectName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hWinSta,
|
|
NULL);
|
|
if (bInherit)
|
|
ObjectAttributes->Attributes |= OBJ_INHERIT;
|
|
|
|
Status = ObOpenObjectByName(ObjectAttributes,
|
|
ExDesktopObjectType,
|
|
UserMode,
|
|
NULL,
|
|
DESKTOP_ALL_ACCESS,
|
|
NULL,
|
|
(PHANDLE)&hDesktop);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
|
|
ObjectName, hWinSta, Status);
|
|
goto Quit;
|
|
}
|
|
}
|
|
|
|
Quit:
|
|
/* Release the object attributes */
|
|
if (ObjectAttributes)
|
|
{
|
|
MemSize = 0;
|
|
ZwFreeVirtualMemory(ZwCurrentProcess(),
|
|
(PVOID*)&ObjectAttributes,
|
|
&MemSize,
|
|
MEM_RELEASE);
|
|
}
|
|
|
|
/* Close the temporary process handle */
|
|
if (hProcess) // if (Process != PsGetCurrentProcess())
|
|
ObCloseHandle(hProcess, KernelMode);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*phWinSta = hWinSta;
|
|
*phDesktop = hDesktop;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ERR("IntResolveDesktop(%wZ) failed, Status 0x%08lx\n", DesktopPath, Status);
|
|
|
|
if (hDesktopDup)
|
|
ObCloseHandle(hDesktopDup, UserMode);
|
|
if (hWinStaDup)
|
|
ObCloseHandle(hWinStaDup, UserMode);
|
|
|
|
if (hDesktop)
|
|
ObCloseHandle(hDesktop, UserMode);
|
|
if (hWinSta)
|
|
ObCloseHandle(hWinSta, UserMode);
|
|
|
|
SetLastNtError(Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* IntValidateDesktopHandle
|
|
*
|
|
* Validates the desktop handle.
|
|
*
|
|
* Remarks
|
|
* If the function succeeds, the handle remains referenced. If the
|
|
* fucntion fails, last error is set.
|
|
*/
|
|
|
|
NTSTATUS FASTCALL
|
|
IntValidateDesktopHandle(
|
|
HDESK Desktop,
|
|
KPROCESSOR_MODE AccessMode,
|
|
ACCESS_MASK DesiredAccess,
|
|
PDESKTOP *Object)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = ObReferenceObjectByHandle(Desktop,
|
|
DesiredAccess,
|
|
ExDesktopObjectType,
|
|
AccessMode,
|
|
(PVOID*)Object,
|
|
NULL);
|
|
|
|
TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
|
|
Desktop, *Object, DesiredAccess, Status);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
SetLastNtError(Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
PDESKTOP FASTCALL
|
|
IntGetActiveDesktop(VOID)
|
|
{
|
|
return gpdeskInputDesktop;
|
|
}
|
|
|
|
/*
|
|
* Returns or creates a handle to the desktop object
|
|
*/
|
|
HDESK FASTCALL
|
|
IntGetDesktopObjectHandle(PDESKTOP DesktopObject)
|
|
{
|
|
NTSTATUS Status;
|
|
HDESK hDesk;
|
|
|
|
ASSERT(DesktopObject);
|
|
|
|
if (!ObFindHandleForObject(PsGetCurrentProcess(),
|
|
DesktopObject,
|
|
ExDesktopObjectType,
|
|
NULL,
|
|
(PHANDLE)&hDesk))
|
|
{
|
|
Status = ObOpenObjectByPointer(DesktopObject,
|
|
0,
|
|
NULL,
|
|
0,
|
|
ExDesktopObjectType,
|
|
UserMode,
|
|
(PHANDLE)&hDesk);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Unable to create a handle */
|
|
ERR("Unable to create a desktop handle\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE("Got handle: 0x%p\n", hDesk);
|
|
}
|
|
|
|
return hDesk;
|
|
}
|
|
|
|
PUSER_MESSAGE_QUEUE FASTCALL
|
|
IntGetFocusMessageQueue(VOID)
|
|
{
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
if (!pdo)
|
|
{
|
|
TRACE("No active desktop\n");
|
|
return(NULL);
|
|
}
|
|
return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
|
|
}
|
|
|
|
VOID FASTCALL
|
|
IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
|
|
{
|
|
PUSER_MESSAGE_QUEUE Old;
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
if (!pdo)
|
|
{
|
|
TRACE("No active desktop\n");
|
|
return;
|
|
}
|
|
if (NewQueue != NULL)
|
|
{
|
|
if (NewQueue->Desktop != NULL)
|
|
{
|
|
TRACE("Message Queue already attached to another desktop!\n");
|
|
return;
|
|
}
|
|
IntReferenceMessageQueue(NewQueue);
|
|
(void)InterlockedExchangePointer((PVOID*)&NewQueue->Desktop, pdo);
|
|
}
|
|
Old = (PUSER_MESSAGE_QUEUE)InterlockedExchangePointer((PVOID*)&pdo->ActiveMessageQueue, NewQueue);
|
|
if (Old != NULL)
|
|
{
|
|
(void)InterlockedExchangePointer((PVOID*)&Old->Desktop, 0);
|
|
gpqForegroundPrev = Old;
|
|
IntDereferenceMessageQueue(Old);
|
|
}
|
|
// Only one Q can have active foreground even when there are more than one desktop.
|
|
if (NewQueue)
|
|
{
|
|
gpqForeground = pdo->ActiveMessageQueue;
|
|
}
|
|
else
|
|
{
|
|
gpqForeground = NULL;
|
|
ERR("ptiLastInput is CLEARED!!\n");
|
|
ptiLastInput = NULL; // ReactOS hacks... should check for process death.
|
|
}
|
|
}
|
|
|
|
PWND FASTCALL
|
|
IntGetThreadDesktopWindow(PTHREADINFO pti)
|
|
{
|
|
if (!pti) pti = PsGetCurrentThreadWin32Thread();
|
|
if (pti->pDeskInfo) return pti->pDeskInfo->spwnd;
|
|
return NULL;
|
|
}
|
|
|
|
PWND FASTCALL co_GetDesktopWindow(PWND pWnd)
|
|
{
|
|
if (pWnd->head.rpdesk &&
|
|
pWnd->head.rpdesk->pDeskInfo)
|
|
return pWnd->head.rpdesk->pDeskInfo->spwnd;
|
|
return NULL;
|
|
}
|
|
|
|
HWND FASTCALL IntGetDesktopWindow(VOID)
|
|
{
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
if (!pdo)
|
|
{
|
|
TRACE("No active desktop\n");
|
|
return NULL;
|
|
}
|
|
return pdo->DesktopWindow;
|
|
}
|
|
|
|
// Win: _GetDesktopWindow
|
|
PWND FASTCALL UserGetDesktopWindow(VOID)
|
|
{
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
|
|
if (!pdo)
|
|
{
|
|
TRACE("No active desktop\n");
|
|
return NULL;
|
|
}
|
|
// return pdo->pDeskInfo->spwnd;
|
|
return UserGetWindowObject(pdo->DesktopWindow);
|
|
}
|
|
|
|
HWND FASTCALL IntGetMessageWindow(VOID)
|
|
{
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
|
|
if (!pdo)
|
|
{
|
|
TRACE("No active desktop\n");
|
|
return NULL;
|
|
}
|
|
return UserHMGetHandle(pdo->spwndMessage);
|
|
}
|
|
|
|
// Win: _GetMessageWindow
|
|
PWND FASTCALL UserGetMessageWindow(VOID)
|
|
{
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
|
|
if (!pdo)
|
|
{
|
|
TRACE("No active desktop\n");
|
|
return NULL;
|
|
}
|
|
return pdo->spwndMessage;
|
|
}
|
|
|
|
HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
|
|
{
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
PDESKTOP pdo = pti->rpdesk;
|
|
if (NULL == pdo)
|
|
{
|
|
ERR("Thread doesn't have a desktop\n");
|
|
return NULL;
|
|
}
|
|
return pdo->DesktopWindow;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
BOOL FASTCALL
|
|
DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
|
|
{
|
|
PAINTSTRUCT Ps;
|
|
ULONG Value;
|
|
//ERR("DesktopWindowProc\n");
|
|
|
|
*lResult = 0;
|
|
|
|
switch (Msg)
|
|
{
|
|
case WM_NCCREATE:
|
|
if (!Wnd->fnid)
|
|
{
|
|
Wnd->fnid = FNID_DESKTOP;
|
|
}
|
|
*lResult = (LRESULT)TRUE;
|
|
return TRUE;
|
|
|
|
case WM_CREATE:
|
|
Value = HandleToULong(PsGetCurrentProcessId());
|
|
// Save Process ID
|
|
co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_PROCESSID, Value, FALSE);
|
|
Value = HandleToULong(PsGetCurrentThreadId());
|
|
// Save Thread ID
|
|
co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_THREADID, Value, FALSE);
|
|
case WM_CLOSE:
|
|
return TRUE;
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
co_WinPosSetWindowPos(Wnd, 0, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE);
|
|
return TRUE;
|
|
|
|
case WM_ERASEBKGND:
|
|
IntPaintDesktop((HDC)wParam);
|
|
*lResult = 1;
|
|
return TRUE;
|
|
|
|
case WM_PAINT:
|
|
{
|
|
if (IntBeginPaint(Wnd, &Ps))
|
|
{
|
|
IntEndPaint(Wnd, &Ps);
|
|
}
|
|
return TRUE;
|
|
}
|
|
case WM_SYSCOLORCHANGE:
|
|
co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
|
|
return TRUE;
|
|
|
|
case WM_SETCURSOR:
|
|
{
|
|
PCURICON_OBJECT pcurOld, pcurNew;
|
|
pcurNew = UserGetCurIconObject(gDesktopCursor);
|
|
if (!pcurNew)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
pcurNew->CURSORF_flags |= CURSORF_CURRENT;
|
|
pcurOld = UserSetCursor(pcurNew, FALSE);
|
|
if (pcurOld)
|
|
{
|
|
pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
|
|
UserDereferenceObject(pcurOld);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
{
|
|
PWINDOWPOS pWindowPos = (PWINDOWPOS)lParam;
|
|
if ((pWindowPos->flags & SWP_SHOWWINDOW) != 0)
|
|
{
|
|
HDESK hdesk = UserOpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
|
|
IntSetThreadDesktop(hdesk, FALSE);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
TRACE("DWP calling IDWP Msg %d\n",Msg);
|
|
//*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE);
|
|
}
|
|
return TRUE; /* We are done. Do not do any callbacks to user mode */
|
|
}
|
|
|
|
BOOL FASTCALL
|
|
UserMessageWindowProc(PWND pwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
|
|
{
|
|
*lResult = 0;
|
|
|
|
switch(Msg)
|
|
{
|
|
case WM_NCCREATE:
|
|
pwnd->fnid |= FNID_MESSAGEWND;
|
|
*lResult = (LRESULT)TRUE;
|
|
break;
|
|
case WM_DESTROY:
|
|
pwnd->fnid |= FNID_DESTROY;
|
|
break;
|
|
default:
|
|
ERR("UMWP calling IDWP\n");
|
|
*lResult = IntDefWindowProc(pwnd, Msg, wParam, lParam, FALSE);
|
|
}
|
|
|
|
return TRUE; /* We are done. Do not do any callbacks to user mode */
|
|
}
|
|
|
|
VOID NTAPI DesktopThreadMain(VOID)
|
|
{
|
|
BOOL Ret;
|
|
MSG Msg;
|
|
|
|
gptiDesktopThread = PsGetCurrentThreadWin32Thread();
|
|
|
|
UserEnterExclusive();
|
|
|
|
/* Register system classes. This thread does not belong to any desktop so the
|
|
classes will be allocated from the shared heap */
|
|
UserRegisterSystemClasses();
|
|
|
|
KeSetEvent(gpDesktopThreadStartedEvent, IO_NO_INCREMENT, FALSE);
|
|
|
|
while (TRUE)
|
|
{
|
|
Ret = co_IntGetPeekMessage(&Msg, 0, 0, 0, PM_REMOVE, TRUE);
|
|
if (Ret)
|
|
{
|
|
IntDispatchMessage(&Msg);
|
|
}
|
|
}
|
|
|
|
UserLeave();
|
|
}
|
|
|
|
HDC FASTCALL
|
|
UserGetDesktopDC(ULONG DcType, BOOL bAltDc, BOOL ValidatehWnd)
|
|
{
|
|
PWND DesktopObject = 0;
|
|
HDC DesktopHDC = 0;
|
|
|
|
/* This can be called from GDI/DX, so acquire the USER lock */
|
|
UserEnterExclusive();
|
|
|
|
if (DcType == DCTYPE_DIRECT)
|
|
{
|
|
DesktopObject = UserGetDesktopWindow();
|
|
DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
|
|
}
|
|
else
|
|
{
|
|
PMONITOR pMonitor = UserGetPrimaryMonitor();
|
|
DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, bAltDc);
|
|
}
|
|
|
|
UserLeave();
|
|
|
|
return DesktopHDC;
|
|
}
|
|
|
|
VOID APIENTRY
|
|
UserRedrawDesktop(VOID)
|
|
{
|
|
PWND Window = NULL;
|
|
PREGION Rgn;
|
|
|
|
Window = UserGetDesktopWindow();
|
|
Rgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
|
|
|
|
IntInvalidateWindows(Window,
|
|
Rgn,
|
|
RDW_FRAME | RDW_ERASE |
|
|
RDW_INVALIDATE | RDW_ALLCHILDREN);
|
|
|
|
REGION_Delete(Rgn);
|
|
}
|
|
|
|
|
|
NTSTATUS FASTCALL
|
|
co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height, BOOL bRedraw)
|
|
{
|
|
PWND pwnd = Desktop->pDeskInfo->spwnd;
|
|
UINT flags = SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW;
|
|
ASSERT(pwnd);
|
|
|
|
if (!bRedraw)
|
|
flags |= SWP_NOREDRAW;
|
|
|
|
co_WinPosSetWindowPos(pwnd, NULL, 0, 0, Width, Height, flags);
|
|
|
|
if (bRedraw)
|
|
co_UserRedrawWindow( pwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_INVALIDATE );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS FASTCALL
|
|
IntHideDesktop(PDESKTOP Desktop)
|
|
{
|
|
PWND DesktopWnd;
|
|
|
|
DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
|
|
if (! DesktopWnd)
|
|
{
|
|
return ERROR_INVALID_WINDOW_HANDLE;
|
|
}
|
|
DesktopWnd->style &= ~WS_VISIBLE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
HWND* FASTCALL
|
|
UserBuildShellHookHwndList(PDESKTOP Desktop)
|
|
{
|
|
ULONG entries=0;
|
|
PLIST_ENTRY ListEntry;
|
|
PSHELL_HOOK_WINDOW Current;
|
|
HWND* list;
|
|
|
|
/* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
|
|
ListEntry = Desktop->ShellHookWindows.Flink;
|
|
while (ListEntry != &Desktop->ShellHookWindows)
|
|
{
|
|
ListEntry = ListEntry->Flink;
|
|
entries++;
|
|
}
|
|
|
|
if (!entries) return NULL;
|
|
|
|
list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
|
|
if (list)
|
|
{
|
|
HWND* cursor = list;
|
|
|
|
ListEntry = Desktop->ShellHookWindows.Flink;
|
|
while (ListEntry != &Desktop->ShellHookWindows)
|
|
{
|
|
Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
|
|
ListEntry = ListEntry->Flink;
|
|
*cursor++ = Current->hWnd;
|
|
}
|
|
|
|
*cursor = NULL; /* Nullterm list */
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
/*
|
|
* Send the Message to the windows registered for ShellHook
|
|
* notifications. The lParam contents depend on the Message. See
|
|
* MSDN for more details (RegisterShellHookWindow)
|
|
*/
|
|
VOID co_IntShellHookNotify(WPARAM Message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PDESKTOP Desktop = IntGetActiveDesktop();
|
|
HWND* HwndList;
|
|
|
|
if (!gpsi->uiShellMsg)
|
|
{
|
|
gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
|
|
|
|
TRACE("MsgType = %x\n", gpsi->uiShellMsg);
|
|
if (!gpsi->uiShellMsg)
|
|
ERR("LastError: %x\n", EngGetLastError());
|
|
}
|
|
|
|
if (!Desktop)
|
|
{
|
|
TRACE("IntShellHookNotify: No desktop!\n");
|
|
return;
|
|
}
|
|
|
|
// Allow other devices have a shot at foreground.
|
|
if (Message == HSHELL_APPCOMMAND) ptiLastInput = NULL;
|
|
|
|
// FIXME: System Tray Support.
|
|
|
|
HwndList = UserBuildShellHookHwndList(Desktop);
|
|
if (HwndList)
|
|
{
|
|
HWND* cursor = HwndList;
|
|
|
|
for (; *cursor; cursor++)
|
|
{
|
|
TRACE("Sending notify\n");
|
|
UserPostMessage(*cursor,
|
|
gpsi->uiShellMsg,
|
|
Message,
|
|
(Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );
|
|
/* co_IntPostOrSendMessage(*cursor,
|
|
gpsi->uiShellMsg,
|
|
Message,
|
|
(Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
|
|
}
|
|
|
|
ExFreePoolWithTag(HwndList, USERTAG_WINDOWLIST);
|
|
}
|
|
|
|
if (ISITHOOKED(WH_SHELL))
|
|
{
|
|
co_HOOK_CallHooks(WH_SHELL, Message, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add the window to the ShellHookWindows list. The windows
|
|
* on that list get notifications that are important to shell
|
|
* type applications.
|
|
*
|
|
* TODO: Validate the window? I'm not sure if sending these messages to
|
|
* an unsuspecting application that is not your own is a nice thing to do.
|
|
*/
|
|
BOOL IntRegisterShellHookWindow(HWND hWnd)
|
|
{
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
PDESKTOP Desktop = pti->rpdesk;
|
|
PSHELL_HOOK_WINDOW Entry;
|
|
|
|
TRACE("IntRegisterShellHookWindow\n");
|
|
|
|
/* First deregister the window, so we can be sure it's never twice in the
|
|
* list.
|
|
*/
|
|
IntDeRegisterShellHookWindow(hWnd);
|
|
|
|
Entry = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(SHELL_HOOK_WINDOW),
|
|
TAG_WINSTA);
|
|
|
|
if (!Entry)
|
|
return FALSE;
|
|
|
|
Entry->hWnd = hWnd;
|
|
|
|
InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Remove the window from the ShellHookWindows list. The windows
|
|
* on that list get notifications that are important to shell
|
|
* type applications.
|
|
*/
|
|
BOOL IntDeRegisterShellHookWindow(HWND hWnd)
|
|
{
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
PDESKTOP Desktop = pti->rpdesk;
|
|
PLIST_ENTRY ListEntry;
|
|
PSHELL_HOOK_WINDOW Current;
|
|
|
|
// FIXME: This probably shouldn't happen, but it does
|
|
if (Desktop == NULL)
|
|
{
|
|
Desktop = IntGetActiveDesktop();
|
|
if (Desktop == NULL)
|
|
return FALSE;
|
|
}
|
|
|
|
ListEntry = Desktop->ShellHookWindows.Flink;
|
|
while (ListEntry != &Desktop->ShellHookWindows)
|
|
{
|
|
Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
|
|
ListEntry = ListEntry->Flink;
|
|
if (Current->hWnd == hWnd)
|
|
{
|
|
RemoveEntryList(&Current->ListEntry);
|
|
ExFreePoolWithTag(Current, TAG_WINSTA);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static VOID
|
|
IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
|
|
{
|
|
/* FIXME: Disable until unmapping works in mm */
|
|
#if 0
|
|
if (Desktop->pheapDesktop != NULL)
|
|
{
|
|
MmUnmapViewInSessionSpace(Desktop->pheapDesktop);
|
|
Desktop->pheapDesktop = NULL;
|
|
}
|
|
|
|
if (Desktop->hsectionDesktop != NULL)
|
|
{
|
|
ObDereferenceObject(Desktop->hsectionDesktop);
|
|
Desktop->hsectionDesktop = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
BOOL FASTCALL
|
|
IntPaintDesktop(HDC hDC)
|
|
{
|
|
static WCHAR s_wszSafeMode[] = L"Safe Mode"; // FIXME: Localize!
|
|
|
|
RECTL Rect;
|
|
HBRUSH DesktopBrush, PreviousBrush;
|
|
HWND hWndDesktop;
|
|
BOOL doPatBlt = TRUE;
|
|
PWND WndDesktop;
|
|
BOOLEAN InSafeMode;
|
|
|
|
if (GdiGetClipBox(hDC, &Rect) == ERROR)
|
|
return FALSE;
|
|
|
|
hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow;
|
|
|
|
WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd;
|
|
if (!WndDesktop)
|
|
return FALSE;
|
|
|
|
/* Retrieve the current SafeMode state */
|
|
InSafeMode = (UserGetSystemMetrics(SM_CLEANBOOT) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
|
|
|
|
if (!InSafeMode)
|
|
{
|
|
DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
|
|
|
|
/*
|
|
* Paint desktop background
|
|
*/
|
|
if (gspv.hbmWallpaper != NULL)
|
|
{
|
|
SIZE sz;
|
|
int x, y;
|
|
int scaledWidth, scaledHeight;
|
|
int wallpaperX, wallpaperY, wallpaperWidth, wallpaperHeight;
|
|
HDC hWallpaperDC;
|
|
|
|
sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left;
|
|
sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top;
|
|
|
|
if (gspv.WallpaperMode == wmFit ||
|
|
gspv.WallpaperMode == wmFill)
|
|
{
|
|
int scaleNum, scaleDen;
|
|
|
|
// Precision improvement over ((sz.cx / gspv.cxWallpaper) > (sz.cy / gspv.cyWallpaper))
|
|
if ((sz.cx * gspv.cyWallpaper) > (sz.cy * gspv.cxWallpaper))
|
|
{
|
|
if (gspv.WallpaperMode == wmFit)
|
|
{
|
|
scaleNum = sz.cy;
|
|
scaleDen = gspv.cyWallpaper;
|
|
}
|
|
else
|
|
{
|
|
scaleNum = sz.cx;
|
|
scaleDen = gspv.cxWallpaper;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (gspv.WallpaperMode == wmFit)
|
|
{
|
|
scaleNum = sz.cx;
|
|
scaleDen = gspv.cxWallpaper;
|
|
}
|
|
else
|
|
{
|
|
scaleNum = sz.cy;
|
|
scaleDen = gspv.cyWallpaper;
|
|
}
|
|
}
|
|
|
|
scaledWidth = EngMulDiv(gspv.cxWallpaper, scaleNum, scaleDen);
|
|
scaledHeight = EngMulDiv(gspv.cyWallpaper, scaleNum, scaleDen);
|
|
|
|
if (gspv.WallpaperMode == wmFill)
|
|
{
|
|
wallpaperX = (((scaledWidth - sz.cx) * gspv.cxWallpaper) / (2 * scaledWidth));
|
|
wallpaperY = (((scaledHeight - sz.cy) * gspv.cyWallpaper) / (2 * scaledHeight));
|
|
|
|
wallpaperWidth = (sz.cx * gspv.cxWallpaper) / scaledWidth;
|
|
wallpaperHeight = (sz.cy * gspv.cyWallpaper) / scaledHeight;
|
|
}
|
|
}
|
|
|
|
if (gspv.WallpaperMode == wmStretch ||
|
|
gspv.WallpaperMode == wmTile ||
|
|
gspv.WallpaperMode == wmFill)
|
|
{
|
|
x = 0;
|
|
y = 0;
|
|
}
|
|
else if (gspv.WallpaperMode == wmFit)
|
|
{
|
|
x = (sz.cx - scaledWidth) / 2;
|
|
y = (sz.cy - scaledHeight) / 2;
|
|
}
|
|
else
|
|
{
|
|
/* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
|
|
x = (sz.cx / 2) - (gspv.cxWallpaper / 2);
|
|
y = (sz.cy / 2) - (gspv.cyWallpaper / 2);
|
|
}
|
|
|
|
hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
|
|
if (hWallpaperDC != NULL)
|
|
{
|
|
HBITMAP hOldBitmap;
|
|
|
|
/* Fill in the area that the bitmap is not going to cover */
|
|
if (x > 0 || y > 0)
|
|
{
|
|
/* FIXME: Clip out the bitmap
|
|
can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
|
|
once we support DSTINVERT */
|
|
PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
|
|
NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
|
|
NtGdiSelectBrush(hDC, PreviousBrush);
|
|
}
|
|
|
|
/* Do not fill the background after it is painted no matter the size of the picture */
|
|
doPatBlt = FALSE;
|
|
|
|
hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper);
|
|
|
|
if (gspv.WallpaperMode == wmStretch)
|
|
{
|
|
if (Rect.right && Rect.bottom)
|
|
NtGdiStretchBlt(hDC,
|
|
x,
|
|
y,
|
|
sz.cx,
|
|
sz.cy,
|
|
hWallpaperDC,
|
|
0,
|
|
0,
|
|
gspv.cxWallpaper,
|
|
gspv.cyWallpaper,
|
|
SRCCOPY,
|
|
0);
|
|
}
|
|
else if (gspv.WallpaperMode == wmTile)
|
|
{
|
|
/* Paint the bitmap across the screen then down */
|
|
for (y = 0; y < Rect.bottom; y += gspv.cyWallpaper)
|
|
{
|
|
for (x = 0; x < Rect.right; x += gspv.cxWallpaper)
|
|
{
|
|
NtGdiBitBlt(hDC,
|
|
x,
|
|
y,
|
|
gspv.cxWallpaper,
|
|
gspv.cyWallpaper,
|
|
hWallpaperDC,
|
|
0,
|
|
0,
|
|
SRCCOPY,
|
|
0,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
else if (gspv.WallpaperMode == wmFit)
|
|
{
|
|
if (Rect.right && Rect.bottom)
|
|
{
|
|
NtGdiStretchBlt(hDC,
|
|
x,
|
|
y,
|
|
scaledWidth,
|
|
scaledHeight,
|
|
hWallpaperDC,
|
|
0,
|
|
0,
|
|
gspv.cxWallpaper,
|
|
gspv.cyWallpaper,
|
|
SRCCOPY,
|
|
0);
|
|
}
|
|
}
|
|
else if (gspv.WallpaperMode == wmFill)
|
|
{
|
|
if (Rect.right && Rect.bottom)
|
|
{
|
|
NtGdiStretchBlt(hDC,
|
|
x,
|
|
y,
|
|
sz.cx,
|
|
sz.cy,
|
|
hWallpaperDC,
|
|
wallpaperX,
|
|
wallpaperY,
|
|
wallpaperWidth,
|
|
wallpaperHeight,
|
|
SRCCOPY,
|
|
0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NtGdiBitBlt(hDC,
|
|
x,
|
|
y,
|
|
gspv.cxWallpaper,
|
|
gspv.cyWallpaper,
|
|
hWallpaperDC,
|
|
0,
|
|
0,
|
|
SRCCOPY,
|
|
0,
|
|
0);
|
|
}
|
|
NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
|
|
NtGdiDeleteObjectApp(hWallpaperDC);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Black desktop background in Safe Mode */
|
|
DesktopBrush = StockObjects[BLACK_BRUSH];
|
|
}
|
|
|
|
/* Background is set to none, clear the screen */
|
|
if (doPatBlt)
|
|
{
|
|
PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
|
|
NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
|
|
NtGdiSelectBrush(hDC, PreviousBrush);
|
|
}
|
|
|
|
/*
|
|
* Display the system version on the desktop background
|
|
*/
|
|
if (InSafeMode || g_AlwaysDisplayVersion || g_PaintDesktopVersion)
|
|
{
|
|
NTSTATUS Status;
|
|
static WCHAR wszzVersion[1024] = L"\0";
|
|
|
|
/* Only used in normal mode */
|
|
// We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
|
|
static POLYTEXTW VerStrs[4] = {{0},{0},{0},{0}};
|
|
INT i = 0;
|
|
SIZE_T len;
|
|
|
|
HFONT hFont1 = NULL, hFont2 = NULL, hOldFont = NULL;
|
|
COLORREF crText, color_old;
|
|
UINT align_old;
|
|
INT mode_old;
|
|
PDC pdc;
|
|
|
|
if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &Rect, 0))
|
|
{
|
|
Rect.left = Rect.top = 0;
|
|
Rect.right = UserGetSystemMetrics(SM_CXSCREEN);
|
|
Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
|
|
}
|
|
else
|
|
{
|
|
RECTL_vOffsetRect(&Rect, -Rect.left, -Rect.top);
|
|
}
|
|
|
|
/*
|
|
* Set up the fonts (otherwise use default ones)
|
|
*/
|
|
|
|
/* Font for the principal version string */
|
|
hFont1 = GreCreateFontIndirectW(&gspv.ncm.lfCaptionFont);
|
|
/* Font for the secondary version strings */
|
|
hFont2 = GreCreateFontIndirectW(&gspv.ncm.lfMenuFont);
|
|
|
|
if (hFont1)
|
|
hOldFont = NtGdiSelectFont(hDC, hFont1);
|
|
|
|
if (gspv.hbmWallpaper == NULL)
|
|
{
|
|
/* Retrieve the brush fill colour */
|
|
// TODO: The following code constitutes "GreGetBrushColor".
|
|
PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
|
|
pdc = DC_LockDc(hDC);
|
|
if (pdc)
|
|
{
|
|
crText = pdc->eboFill.ulRGBColor;
|
|
DC_UnlockDc(pdc);
|
|
}
|
|
else
|
|
{
|
|
crText = RGB(0, 0, 0);
|
|
}
|
|
NtGdiSelectBrush(hDC, PreviousBrush);
|
|
|
|
/* Adjust text colour according to the brush */
|
|
if (GetRValue(crText) + GetGValue(crText) + GetBValue(crText) > 128 * 3)
|
|
crText = RGB(0, 0, 0);
|
|
else
|
|
crText = RGB(255, 255, 255);
|
|
}
|
|
else
|
|
{
|
|
/* Always use white when the text is displayed on top of a wallpaper */
|
|
crText = RGB(255, 255, 255);
|
|
}
|
|
|
|
color_old = IntGdiSetTextColor(hDC, crText);
|
|
align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
|
|
mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
|
|
|
|
/* Display the system version information */
|
|
if (!*wszzVersion)
|
|
{
|
|
Status = GetSystemVersionString(wszzVersion,
|
|
ARRAYSIZE(wszzVersion),
|
|
InSafeMode,
|
|
g_AlwaysDisplayVersion);
|
|
if (!InSafeMode && NT_SUCCESS(Status) && *wszzVersion)
|
|
{
|
|
PWCHAR pstr = wszzVersion;
|
|
for (i = 0; (i < ARRAYSIZE(VerStrs)) && *pstr; ++i)
|
|
{
|
|
VerStrs[i].n = lstrlenW(pstr);
|
|
VerStrs[i].lpstr = pstr;
|
|
pstr += (VerStrs[i].n + 1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
if (NT_SUCCESS(Status) && *wszzVersion)
|
|
{
|
|
if (!InSafeMode)
|
|
{
|
|
SIZE Size = {0, 0};
|
|
LONG TotalHeight = 0;
|
|
|
|
/* Normal Mode: multiple version information text separated by newlines */
|
|
IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
|
|
|
|
/* Compute the heights of the strings */
|
|
if (hFont1) NtGdiSelectFont(hDC, hFont1);
|
|
for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
|
|
{
|
|
if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
|
|
break;
|
|
|
|
GreGetTextExtentW(hDC, VerStrs[i].lpstr, VerStrs[i].n, &Size, 1);
|
|
VerStrs[i].y = Size.cy; // Store the string height
|
|
TotalHeight += Size.cy;
|
|
|
|
/* While the first string was using hFont1, all the others use hFont2 */
|
|
if (hFont2) NtGdiSelectFont(hDC, hFont2);
|
|
}
|
|
/* The total height must not exceed the screen height */
|
|
TotalHeight = min(TotalHeight, Rect.bottom);
|
|
|
|
/* Display the strings */
|
|
if (hFont1) NtGdiSelectFont(hDC, hFont1);
|
|
for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
|
|
{
|
|
if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
|
|
break;
|
|
|
|
TotalHeight -= VerStrs[i].y;
|
|
GreExtTextOutW(hDC,
|
|
Rect.right - 5,
|
|
Rect.bottom - TotalHeight - 5,
|
|
0, NULL,
|
|
VerStrs[i].lpstr,
|
|
VerStrs[i].n,
|
|
NULL, 0);
|
|
|
|
/* While the first string was using hFont1, all the others use hFont2 */
|
|
if (hFont2) NtGdiSelectFont(hDC, hFont2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (hFont1) NtGdiSelectFont(hDC, hFont1);
|
|
|
|
/* Safe Mode: single version information text in top center */
|
|
len = wcslen(wszzVersion);
|
|
|
|
IntGdiSetTextAlign(hDC, TA_CENTER | TA_TOP);
|
|
GreExtTextOutW(hDC, (Rect.right + Rect.left)/2, Rect.top + 3, 0, NULL, wszzVersion, len, NULL, 0);
|
|
}
|
|
}
|
|
|
|
if (InSafeMode)
|
|
{
|
|
if (hFont1) NtGdiSelectFont(hDC, hFont1);
|
|
|
|
/* Print Safe Mode text in corners */
|
|
len = wcslen(s_wszSafeMode);
|
|
|
|
IntGdiSetTextAlign(hDC, TA_LEFT | TA_TOP);
|
|
GreExtTextOutW(hDC, Rect.left, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
|
|
IntGdiSetTextAlign(hDC, TA_RIGHT | TA_TOP);
|
|
GreExtTextOutW(hDC, Rect.right, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
|
|
IntGdiSetTextAlign(hDC, TA_LEFT | TA_BOTTOM);
|
|
GreExtTextOutW(hDC, Rect.left, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
|
|
IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
|
|
GreExtTextOutW(hDC, Rect.right, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
|
|
}
|
|
|
|
IntGdiSetBkMode(hDC, mode_old);
|
|
IntGdiSetTextAlign(hDC, align_old);
|
|
IntGdiSetTextColor(hDC, color_old);
|
|
|
|
if (hFont2)
|
|
GreDeleteObject(hFont2);
|
|
|
|
if (hFont1)
|
|
{
|
|
NtGdiSelectFont(hDC, hOldFont);
|
|
GreDeleteObject(hFont1);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static NTSTATUS
|
|
UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta)
|
|
{
|
|
static const UNICODE_STRING WinlogonDesktop = RTL_CONSTANT_STRING(L"Winlogon");
|
|
PVOID DesktopHeapSystemBase = NULL;
|
|
ULONG_PTR HeapSize;
|
|
SIZE_T DesktopInfoSize;
|
|
ULONG i;
|
|
|
|
TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk, DesktopName);
|
|
|
|
RtlZeroMemory(pdesk, sizeof(DESKTOP));
|
|
|
|
/* Set desktop size, based on whether the WinSta is interactive or not */
|
|
if (pwinsta == InputWindowStation)
|
|
{
|
|
/* Check if the Desktop is named "Winlogon" */
|
|
if (RtlEqualUnicodeString(DesktopName, &WinlogonDesktop, TRUE))
|
|
{
|
|
HeapSize = gdwWinlogonSectionSize * 1024;
|
|
}
|
|
else
|
|
{
|
|
HeapSize = gdwDesktopSectionSize * 1024;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HeapSize = gdwNOIOSectionSize * 1024;
|
|
}
|
|
|
|
/* Link the desktop with the parent window station */
|
|
ObReferenceObject(pwinsta);
|
|
pdesk->rpwinstaParent = pwinsta;
|
|
InsertTailList(&pwinsta->DesktopListHead, &pdesk->ListEntry);
|
|
|
|
/* Create the desktop heap */
|
|
pdesk->hsectionDesktop = NULL;
|
|
pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop,
|
|
&DesktopHeapSystemBase,
|
|
HeapSize);
|
|
if (pdesk->pheapDesktop == NULL)
|
|
{
|
|
ERR("Failed to create desktop heap!\n");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Create DESKTOPINFO */
|
|
DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName->Length + sizeof(WCHAR);
|
|
pdesk->pDeskInfo = RtlAllocateHeap(pdesk->pheapDesktop,
|
|
HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY,
|
|
DesktopInfoSize);
|
|
if (pdesk->pDeskInfo == NULL)
|
|
{
|
|
ERR("Failed to create the DESKTOP structure!\n");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Initialize the DESKTOPINFO */
|
|
pdesk->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
|
|
pdesk->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
|
|
RtlCopyMemory(pdesk->pDeskInfo->szDesktopName,
|
|
DesktopName->Buffer,
|
|
DesktopName->Length + sizeof(WCHAR));
|
|
for (i = 0; i < NB_HOOKS; i++)
|
|
{
|
|
InitializeListHead(&pdesk->pDeskInfo->aphkStart[i]);
|
|
}
|
|
|
|
InitializeListHead(&pdesk->ShellHookWindows);
|
|
InitializeListHead(&pdesk->PtiList);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* SYSCALLS *******************************************************************/
|
|
|
|
/*
|
|
* NtUserCreateDesktop
|
|
*
|
|
* Creates a new desktop.
|
|
*
|
|
* Parameters
|
|
* poaAttribs
|
|
* Object Attributes.
|
|
*
|
|
* lpszDesktopDevice
|
|
* Name of the device.
|
|
*
|
|
* pDeviceMode
|
|
* Device Mode.
|
|
*
|
|
* dwFlags
|
|
* Interaction flags.
|
|
*
|
|
* dwDesiredAccess
|
|
* Requested type of access.
|
|
*
|
|
*
|
|
* Return Value
|
|
* If the function succeeds, the return value is a handle to the newly
|
|
* created desktop. If the specified desktop already exists, the function
|
|
* succeeds and returns a handle to the existing desktop. When you are
|
|
* finished using the handle, call the CloseDesktop function to close it.
|
|
* If the function fails, the return value is NULL.
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
IntCreateDesktop(
|
|
OUT HDESK* phDesktop,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
IN PUNICODE_STRING lpszDesktopDevice OPTIONAL,
|
|
IN LPDEVMODEW lpdmw OPTIONAL,
|
|
IN DWORD dwFlags,
|
|
IN ACCESS_MASK dwDesiredAccess)
|
|
{
|
|
NTSTATUS Status;
|
|
PDESKTOP pdesk = NULL;
|
|
HDESK hDesk;
|
|
BOOLEAN Context = FALSE;
|
|
UNICODE_STRING ClassName;
|
|
LARGE_STRING WindowName;
|
|
BOOL NoHooks = FALSE;
|
|
PWND pWnd = NULL;
|
|
CREATESTRUCTW Cs;
|
|
PTHREADINFO ptiCurrent;
|
|
PCLS pcls;
|
|
|
|
TRACE("Enter IntCreateDesktop\n");
|
|
|
|
ASSERT(UserIsEnteredExclusive());
|
|
|
|
ASSERT(phDesktop);
|
|
*phDesktop = NULL;
|
|
|
|
ptiCurrent = PsGetCurrentThreadWin32Thread();
|
|
ASSERT(ptiCurrent);
|
|
ASSERT(gptiDesktopThread);
|
|
|
|
/* Turn off hooks when calling any CreateWindowEx from inside win32k */
|
|
NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
|
|
ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
|
|
ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
|
|
|
|
/*
|
|
* Try to open already existing desktop
|
|
*/
|
|
Status = ObOpenObjectByName(ObjectAttributes,
|
|
ExDesktopObjectType,
|
|
AccessMode,
|
|
NULL,
|
|
dwDesiredAccess,
|
|
(PVOID)&Context,
|
|
(PHANDLE)&hDesk);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("ObOpenObjectByName failed to open/create desktop\n");
|
|
goto Quit;
|
|
}
|
|
|
|
/* In case the object was not created (eg if it existed), return now */
|
|
if (Context == FALSE)
|
|
{
|
|
TRACE("IntCreateDesktop opened desktop '%wZ'\n", ObjectAttributes->ObjectName);
|
|
Status = STATUS_SUCCESS;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Reference the desktop */
|
|
Status = ObReferenceObjectByHandle(hDesk,
|
|
0,
|
|
ExDesktopObjectType,
|
|
KernelMode,
|
|
(PVOID*)&pdesk,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to reference desktop object\n");
|
|
goto Quit;
|
|
}
|
|
|
|
/* Get the desktop window class. The thread desktop does not belong to any desktop
|
|
* so the classes created there (including the desktop class) are allocated in the shared heap
|
|
* It would cause problems if we used a class that belongs to the caller
|
|
*/
|
|
ClassName.Buffer = WC_DESKTOP;
|
|
ClassName.Length = 0;
|
|
pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
|
|
if (pcls == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Quit;
|
|
}
|
|
|
|
RtlZeroMemory(&WindowName, sizeof(WindowName));
|
|
RtlZeroMemory(&Cs, sizeof(Cs));
|
|
Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN),
|
|
Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN),
|
|
Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN),
|
|
Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN),
|
|
Cs.style = WS_POPUP|WS_CLIPCHILDREN;
|
|
Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
|
|
Cs.lpszName = (LPCWSTR) &WindowName;
|
|
Cs.lpszClass = (LPCWSTR) &ClassName;
|
|
|
|
/* Use IntCreateWindow instead of co_UserCreateWindowEx because the later expects a thread with a desktop */
|
|
pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk, WINVER);
|
|
if (pWnd == NULL)
|
|
{
|
|
ERR("Failed to create desktop window for the new desktop\n");
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Quit;
|
|
}
|
|
|
|
pdesk->dwSessionId = PsGetCurrentProcessSessionId();
|
|
pdesk->DesktopWindow = UserHMGetHandle(pWnd);
|
|
pdesk->pDeskInfo->spwnd = pWnd;
|
|
pWnd->fnid = FNID_DESKTOP;
|
|
|
|
ClassName.Buffer = MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]);
|
|
ClassName.Length = 0;
|
|
pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
|
|
if (pcls == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Quit;
|
|
}
|
|
|
|
RtlZeroMemory(&WindowName, sizeof(WindowName));
|
|
RtlZeroMemory(&Cs, sizeof(Cs));
|
|
Cs.cx = Cs.cy = 100;
|
|
Cs.style = WS_POPUP|WS_CLIPCHILDREN;
|
|
Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
|
|
Cs.lpszName = (LPCWSTR)&WindowName;
|
|
Cs.lpszClass = (LPCWSTR)&ClassName;
|
|
pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk, WINVER);
|
|
if (pWnd == NULL)
|
|
{
|
|
ERR("Failed to create message window for the new desktop\n");
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Quit;
|
|
}
|
|
|
|
pdesk->spwndMessage = pWnd;
|
|
pWnd->fnid = FNID_MESSAGEWND;
|
|
|
|
/* Now...
|
|
if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
|
|
Create Tooltip. Saved in DesktopObject->spwndTooltip.
|
|
Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
|
|
hWndParent are spwndMessage. Use hModuleWin for server side winproc!
|
|
The rest is same as message window.
|
|
http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
|
|
*/
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Quit:
|
|
if (pdesk != NULL)
|
|
{
|
|
ObDereferenceObject(pdesk);
|
|
}
|
|
if (!NT_SUCCESS(Status) && hDesk != NULL)
|
|
{
|
|
ObCloseHandle(hDesk, AccessMode);
|
|
hDesk = NULL;
|
|
}
|
|
if (!NoHooks)
|
|
{
|
|
ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
|
|
ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
|
|
}
|
|
|
|
TRACE("Leave IntCreateDesktop, Status 0x%08lx\n", Status);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
*phDesktop = hDesk;
|
|
else
|
|
SetLastNtError(Status);
|
|
return Status;
|
|
}
|
|
|
|
HDESK APIENTRY
|
|
NtUserCreateDesktop(
|
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
|
PUNICODE_STRING lpszDesktopDevice,
|
|
LPDEVMODEW lpdmw,
|
|
DWORD dwFlags,
|
|
ACCESS_MASK dwDesiredAccess)
|
|
{
|
|
NTSTATUS Status;
|
|
HDESK hDesk;
|
|
HDESK Ret = NULL;
|
|
|
|
TRACE("Enter NtUserCreateDesktop\n");
|
|
UserEnterExclusive();
|
|
|
|
Status = IntCreateDesktop(&hDesk,
|
|
ObjectAttributes,
|
|
UserMode,
|
|
lpszDesktopDevice,
|
|
lpdmw,
|
|
dwFlags,
|
|
dwDesiredAccess);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("IntCreateDesktop failed, Status 0x%08lx\n", Status);
|
|
// SetLastNtError(Status);
|
|
goto Exit; // Return NULL
|
|
}
|
|
|
|
Ret = hDesk;
|
|
|
|
Exit:
|
|
TRACE("Leave NtUserCreateDesktop, ret=0x%p\n", Ret);
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* NtUserOpenDesktop
|
|
*
|
|
* Opens an existing desktop.
|
|
*
|
|
* Parameters
|
|
* lpszDesktopName
|
|
* Name of the existing desktop.
|
|
*
|
|
* dwFlags
|
|
* Interaction flags.
|
|
*
|
|
* dwDesiredAccess
|
|
* Requested type of access.
|
|
*
|
|
* Return Value
|
|
* Handle to the desktop or zero on failure.
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
HDESK APIENTRY
|
|
NtUserOpenDesktop(
|
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
|
DWORD dwFlags,
|
|
ACCESS_MASK dwDesiredAccess)
|
|
{
|
|
NTSTATUS Status;
|
|
HDESK Desktop;
|
|
|
|
Status = ObOpenObjectByName(
|
|
ObjectAttributes,
|
|
ExDesktopObjectType,
|
|
UserMode,
|
|
NULL,
|
|
dwDesiredAccess,
|
|
NULL,
|
|
(HANDLE*)&Desktop);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to open desktop\n");
|
|
SetLastNtError(Status);
|
|
return NULL;
|
|
}
|
|
|
|
TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
|
|
|
|
return Desktop;
|
|
}
|
|
|
|
HDESK UserOpenInputDesktop(DWORD dwFlags,
|
|
BOOL fInherit,
|
|
ACCESS_MASK dwDesiredAccess)
|
|
{
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
NTSTATUS Status;
|
|
ULONG HandleAttributes = 0;
|
|
HDESK hdesk = NULL;
|
|
|
|
if (!gpdeskInputDesktop)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (pti->ppi->prpwinsta != InputWindowStation)
|
|
{
|
|
ERR("Tried to open input desktop from non interactive winsta!\n");
|
|
EngSetLastError(ERROR_INVALID_FUNCTION);
|
|
return NULL;
|
|
}
|
|
|
|
if (fInherit) HandleAttributes = OBJ_INHERIT;
|
|
|
|
/* Create a new handle to the object */
|
|
Status = ObOpenObjectByPointer(
|
|
gpdeskInputDesktop,
|
|
HandleAttributes,
|
|
NULL,
|
|
dwDesiredAccess,
|
|
ExDesktopObjectType,
|
|
UserMode,
|
|
(PHANDLE)&hdesk);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to open input desktop object\n");
|
|
SetLastNtError(Status);
|
|
}
|
|
|
|
return hdesk;
|
|
}
|
|
|
|
/*
|
|
* NtUserOpenInputDesktop
|
|
*
|
|
* Opens the input (interactive) desktop.
|
|
*
|
|
* Parameters
|
|
* dwFlags
|
|
* Interaction flags.
|
|
*
|
|
* fInherit
|
|
* Inheritance option.
|
|
*
|
|
* dwDesiredAccess
|
|
* Requested type of access.
|
|
*
|
|
* Return Value
|
|
* Handle to the input desktop or zero on failure.
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
HDESK APIENTRY
|
|
NtUserOpenInputDesktop(
|
|
DWORD dwFlags,
|
|
BOOL fInherit,
|
|
ACCESS_MASK dwDesiredAccess)
|
|
{
|
|
HDESK hdesk;
|
|
|
|
UserEnterExclusive();
|
|
TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n", gpdeskInputDesktop);
|
|
|
|
hdesk = UserOpenInputDesktop(dwFlags, fInherit, dwDesiredAccess);
|
|
|
|
TRACE("NtUserOpenInputDesktop returning 0x%p\n", hdesk);
|
|
UserLeave();
|
|
return hdesk;
|
|
}
|
|
|
|
/*
|
|
* NtUserCloseDesktop
|
|
*
|
|
* Closes a desktop handle.
|
|
*
|
|
* Parameters
|
|
* hDesktop
|
|
* Handle to the desktop.
|
|
*
|
|
* Return Value
|
|
* Status
|
|
*
|
|
* Remarks
|
|
* The desktop handle can be created with NtUserCreateDesktop or
|
|
* NtUserOpenDesktop. This function will fail if any thread in the calling
|
|
* process is using the specified desktop handle or if the handle refers
|
|
* to the initial desktop of the calling process.
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
BOOL APIENTRY
|
|
NtUserCloseDesktop(HDESK hDesktop)
|
|
{
|
|
PDESKTOP pdesk;
|
|
NTSTATUS Status;
|
|
BOOL Ret = FALSE;
|
|
|
|
TRACE("NtUserCloseDesktop(0x%p) called\n", hDesktop);
|
|
UserEnterExclusive();
|
|
|
|
if (hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
|
|
{
|
|
ERR("Attempted to close thread desktop\n");
|
|
EngSetLastError(ERROR_BUSY);
|
|
goto Exit; // Return FALSE
|
|
}
|
|
|
|
Status = IntValidateDesktopHandle(hDesktop, UserMode, 0, &pdesk);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Validation of desktop handle 0x%p failed\n", hDesktop);
|
|
goto Exit; // Return FALSE
|
|
}
|
|
|
|
ObDereferenceObject(pdesk);
|
|
|
|
Status = ObCloseHandle(hDesktop, UserMode);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to close desktop handle 0x%p\n", hDesktop);
|
|
SetLastNtError(Status);
|
|
goto Exit; // Return FALSE
|
|
}
|
|
|
|
Ret = TRUE;
|
|
|
|
Exit:
|
|
TRACE("Leave NtUserCloseDesktop, ret=%i\n", Ret);
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* NtUserPaintDesktop
|
|
*
|
|
* The NtUserPaintDesktop function fills the clipping region in the
|
|
* specified device context with the desktop pattern or wallpaper. The
|
|
* function is provided primarily for shell desktops.
|
|
*
|
|
* Parameters
|
|
* hDC
|
|
* Handle to the device context.
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
BOOL APIENTRY
|
|
NtUserPaintDesktop(HDC hDC)
|
|
{
|
|
BOOL Ret;
|
|
|
|
UserEnterExclusive();
|
|
TRACE("Enter NtUserPaintDesktop\n");
|
|
|
|
Ret = IntPaintDesktop(hDC);
|
|
|
|
TRACE("Leave NtUserPaintDesktop, ret=%i\n", Ret);
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* NtUserResolveDesktop
|
|
*
|
|
* The NtUserResolveDesktop function attempts to retrieve valid handles to
|
|
* a desktop and a window station suitable for the specified process.
|
|
* The specified desktop path string is used only as a hint for the resolution.
|
|
*
|
|
* See the description of IntResolveDesktop for more details.
|
|
*
|
|
* Parameters
|
|
* ProcessHandle
|
|
* Handle to a user process.
|
|
*
|
|
* DesktopPath
|
|
* The desktop path string used as a hint for desktop resolution.
|
|
*
|
|
* bInherit
|
|
* Whether or not the returned handles are inheritable.
|
|
*
|
|
* phWinSta
|
|
* Pointer to a window station handle.
|
|
*
|
|
* 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
|
|
NTAPI
|
|
NtUserResolveDesktop(
|
|
IN HANDLE ProcessHandle,
|
|
IN PUNICODE_STRING DesktopPath,
|
|
IN BOOL bInherit,
|
|
OUT HWINSTA* phWinSta)
|
|
{
|
|
NTSTATUS Status;
|
|
PEPROCESS Process;
|
|
HWINSTA hWinSta = NULL;
|
|
HDESK hDesktop = NULL;
|
|
UNICODE_STRING CapturedDesktopPath;
|
|
|
|
/* 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;
|
|
|
|
UserEnterExclusive();
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the handle pointer */
|
|
// ProbeForWriteHandle
|
|
ProbeForWrite(phWinSta, sizeof(HWINSTA), sizeof(HWINSTA));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(goto Quit);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Capture the user desktop path string */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedDesktopPath,
|
|
UserMode,
|
|
DesktopPath);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
/* Call the internal function */
|
|
Status = IntResolveDesktop(Process,
|
|
&CapturedDesktopPath,
|
|
bInherit,
|
|
&hWinSta,
|
|
&hDesktop);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("IntResolveDesktop failed, Status 0x%08lx\n", Status);
|
|
hWinSta = NULL;
|
|
hDesktop = NULL;
|
|
}
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the window station handle */
|
|
*phWinSta = hWinSta;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
/* We failed, close the opened desktop and window station */
|
|
if (hDesktop) ObCloseHandle(hDesktop, UserMode);
|
|
hDesktop = NULL;
|
|
if (hWinSta) ObCloseHandle(hWinSta, UserMode);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Free the captured string */
|
|
ReleaseCapturedUnicodeString(&CapturedDesktopPath, UserMode);
|
|
|
|
Quit:
|
|
UserLeave();
|
|
|
|
/* Dereference the process object */
|
|
ObDereferenceObject(Process);
|
|
|
|
/* Return the desktop handle */
|
|
return hDesktop;
|
|
}
|
|
|
|
/*
|
|
* NtUserSwitchDesktop
|
|
*
|
|
* Sets the current input (interactive) desktop.
|
|
*
|
|
* Parameters
|
|
* hDesktop
|
|
* Handle to desktop.
|
|
*
|
|
* Return Value
|
|
* Status
|
|
*
|
|
* Status
|
|
* @unimplemented
|
|
*/
|
|
|
|
BOOL APIENTRY
|
|
NtUserSwitchDesktop(HDESK hdesk)
|
|
{
|
|
PDESKTOP pdesk;
|
|
NTSTATUS Status;
|
|
BOOL bRedrawDesktop;
|
|
BOOL Ret = FALSE;
|
|
|
|
UserEnterExclusive();
|
|
TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
|
|
|
|
Status = IntValidateDesktopHandle(hdesk, UserMode, 0, &pdesk);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Validation of desktop handle 0x%p failed\n", hdesk);
|
|
goto Exit; // Return FALSE
|
|
}
|
|
|
|
if (PsGetCurrentProcessSessionId() != pdesk->rpwinstaParent->dwSessionId)
|
|
{
|
|
ObDereferenceObject(pdesk);
|
|
ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
|
|
goto Exit; // Return FALSE
|
|
}
|
|
|
|
if (pdesk == gpdeskInputDesktop)
|
|
{
|
|
ObDereferenceObject(pdesk);
|
|
WARN("NtUserSwitchDesktop called for active desktop\n");
|
|
Ret = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Don't allow applications switch the desktop if it's locked, unless the caller
|
|
* is the logon application itself
|
|
*/
|
|
if ((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
|
|
gpidLogon != PsGetCurrentProcessId())
|
|
{
|
|
ObDereferenceObject(pdesk);
|
|
ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
|
|
goto Exit; // Return FALSE
|
|
}
|
|
|
|
if (pdesk->rpwinstaParent != InputWindowStation)
|
|
{
|
|
ObDereferenceObject(pdesk);
|
|
ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
|
|
goto Exit; // Return FALSE
|
|
}
|
|
|
|
/* FIXME: Fail if the process is associated with a secured
|
|
desktop such as Winlogon or Screen-Saver */
|
|
/* FIXME: Connect to input device */
|
|
|
|
TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop, pdesk);
|
|
|
|
bRedrawDesktop = FALSE;
|
|
|
|
/* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
|
|
if (gpdeskInputDesktop != NULL)
|
|
{
|
|
if ((gpdeskInputDesktop->pDeskInfo->spwnd->style & WS_VISIBLE) == WS_VISIBLE)
|
|
bRedrawDesktop = TRUE;
|
|
|
|
/* Hide the previous desktop window */
|
|
IntHideDesktop(gpdeskInputDesktop);
|
|
}
|
|
|
|
/* Set the active desktop in the desktop's window station. */
|
|
InputWindowStation->ActiveDesktop = pdesk;
|
|
|
|
/* Set the global state. */
|
|
gpdeskInputDesktop = pdesk;
|
|
|
|
/* Show the new desktop window */
|
|
co_IntShowDesktop(pdesk, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN), bRedrawDesktop);
|
|
|
|
TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n", gpdeskInputDesktop);
|
|
ObDereferenceObject(pdesk);
|
|
|
|
Ret = TRUE;
|
|
|
|
Exit:
|
|
TRACE("Leave NtUserSwitchDesktop, ret=%i\n", Ret);
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* NtUserGetThreadDesktop
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
HDESK APIENTRY
|
|
NtUserGetThreadDesktop(DWORD dwThreadId, HDESK hConsoleDesktop)
|
|
{
|
|
HDESK hDesk;
|
|
NTSTATUS Status;
|
|
PTHREADINFO pti;
|
|
PEPROCESS Process;
|
|
PDESKTOP DesktopObject;
|
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|
|
|
UserEnterExclusive();
|
|
TRACE("Enter NtUserGetThreadDesktop\n");
|
|
|
|
if (!dwThreadId)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
hDesk = NULL;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Validate the Win32 thread and retrieve its information */
|
|
pti = IntTID2PTI(UlongToHandle(dwThreadId));
|
|
if (pti)
|
|
{
|
|
/* Get the desktop handle of the thread */
|
|
hDesk = pti->hdesk;
|
|
Process = pti->ppi->peProcess;
|
|
}
|
|
else if (hConsoleDesktop)
|
|
{
|
|
/*
|
|
* The thread may belong to a console, so attempt to use the provided
|
|
* console desktop handle as a fallback. Otherwise this means that the
|
|
* thread is either not Win32 or invalid.
|
|
*/
|
|
hDesk = hConsoleDesktop;
|
|
Process = gpepCSRSS;
|
|
}
|
|
else
|
|
{
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
hDesk = NULL;
|
|
goto Quit;
|
|
}
|
|
|
|
if (!hDesk)
|
|
{
|
|
ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
|
|
goto Quit;
|
|
}
|
|
|
|
if (Process == PsGetCurrentProcess())
|
|
{
|
|
/*
|
|
* Just return the handle, since we queried the desktop handle
|
|
* of a thread running in the same context.
|
|
*/
|
|
goto Quit;
|
|
}
|
|
|
|
/*
|
|
* We could just use the cached rpdesk instead of looking up the handle,
|
|
* but it may actually be safer to validate the desktop and get a temporary
|
|
* reference to it so that it does not disappear under us (e.g. when the
|
|
* desktop is being destroyed) during the operation.
|
|
*/
|
|
/*
|
|
* Switch into the context of the thread we are trying to get
|
|
* the desktop from, so we can use the handle.
|
|
*/
|
|
KeAttachProcess(&Process->Pcb);
|
|
Status = ObReferenceObjectByHandle(hDesk,
|
|
0,
|
|
ExDesktopObjectType,
|
|
UserMode,
|
|
(PVOID*)&DesktopObject,
|
|
&HandleInformation);
|
|
KeDetachProcess();
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/*
|
|
* Lookup our handle table if we can find a handle to the desktop object.
|
|
* If not, create one.
|
|
* QUESTION: Do we really need to create a handle in case it doesn't exist??
|
|
*/
|
|
hDesk = IntGetDesktopObjectHandle(DesktopObject);
|
|
|
|
/* All done, we got a valid handle to the desktop */
|
|
ObDereferenceObject(DesktopObject);
|
|
}
|
|
else
|
|
{
|
|
/* The handle could not be found, there is nothing to get... */
|
|
hDesk = NULL;
|
|
}
|
|
|
|
if (!hDesk)
|
|
{
|
|
ERR("Could not retrieve or access desktop for thread 0x%x\n", dwThreadId);
|
|
EngSetLastError(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
Quit:
|
|
TRACE("Leave NtUserGetThreadDesktop, hDesk = 0x%p\n", hDesk);
|
|
UserLeave();
|
|
return hDesk;
|
|
}
|
|
|
|
static NTSTATUS
|
|
IntUnmapDesktopView(IN PDESKTOP pdesk)
|
|
{
|
|
PPROCESSINFO ppi;
|
|
PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
|
|
|
|
ppi = PsGetCurrentProcessWin32Process();
|
|
|
|
/*
|
|
* Unmap if we're the last thread using the desktop.
|
|
* Start the search at the next mapping: skip the first entry
|
|
* as it must be the global user heap mapping.
|
|
*/
|
|
PrevLink = &ppi->HeapMappings.Next;
|
|
HeapMapping = *PrevLink;
|
|
while (HeapMapping != NULL)
|
|
{
|
|
if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
|
|
{
|
|
if (--HeapMapping->Count == 0)
|
|
{
|
|
*PrevLink = HeapMapping->Next;
|
|
|
|
TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
|
|
Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
|
|
HeapMapping->UserMapping);
|
|
|
|
ObDereferenceObject(pdesk);
|
|
|
|
UserHeapFree(HeapMapping);
|
|
break;
|
|
}
|
|
}
|
|
|
|
PrevLink = &HeapMapping->Next;
|
|
HeapMapping = HeapMapping->Next;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
static NTSTATUS
|
|
IntMapDesktopView(IN PDESKTOP pdesk)
|
|
{
|
|
PPROCESSINFO ppi;
|
|
PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
|
|
PVOID UserBase = NULL;
|
|
SIZE_T ViewSize = 0;
|
|
LARGE_INTEGER Offset;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
|
|
|
|
ppi = PsGetCurrentProcessWin32Process();
|
|
|
|
/*
|
|
* Find out if another thread already mapped the desktop heap.
|
|
* Start the search at the next mapping: skip the first entry
|
|
* as it must be the global user heap mapping.
|
|
*/
|
|
PrevLink = &ppi->HeapMappings.Next;
|
|
HeapMapping = *PrevLink;
|
|
while (HeapMapping != NULL)
|
|
{
|
|
if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
|
|
{
|
|
HeapMapping->Count++;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PrevLink = &HeapMapping->Next;
|
|
HeapMapping = HeapMapping->Next;
|
|
}
|
|
|
|
/* We're the first, map the heap */
|
|
Offset.QuadPart = 0;
|
|
Status = MmMapViewOfSection(pdesk->hsectionDesktop,
|
|
PsGetCurrentProcess(),
|
|
&UserBase,
|
|
0,
|
|
0,
|
|
&Offset,
|
|
&ViewSize,
|
|
ViewUnmap,
|
|
SEC_NO_CHANGE,
|
|
PAGE_READONLY);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to map desktop\n");
|
|
return Status;
|
|
}
|
|
|
|
TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
|
|
|
|
/* Add the mapping */
|
|
HeapMapping = UserHeapAlloc(sizeof(*HeapMapping));
|
|
if (HeapMapping == NULL)
|
|
{
|
|
MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
|
|
ERR("UserHeapAlloc() failed!\n");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
HeapMapping->Next = NULL;
|
|
HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
|
|
HeapMapping->UserMapping = UserBase;
|
|
HeapMapping->Limit = ViewSize;
|
|
HeapMapping->Count = 1;
|
|
*PrevLink = HeapMapping;
|
|
|
|
ObReferenceObject(pdesk);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOL
|
|
IntSetThreadDesktop(IN HDESK hDesktop,
|
|
IN BOOL FreeOnFailure)
|
|
{
|
|
PDESKTOP pdesk = NULL, pdeskOld;
|
|
PTHREADINFO pti;
|
|
NTSTATUS Status;
|
|
PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
|
|
PCLIENTINFO pci;
|
|
|
|
ASSERT(NtCurrentTeb());
|
|
|
|
TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
|
|
|
|
pti = PsGetCurrentThreadWin32Thread();
|
|
pci = pti->pClientInfo;
|
|
|
|
/* If the caller gave us a desktop, ensure it is valid */
|
|
if (hDesktop != NULL)
|
|
{
|
|
/* Validate the new desktop. */
|
|
Status = IntValidateDesktopHandle(hDesktop, UserMode, 0, &pdesk);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Validation of desktop handle 0x%p failed\n", hDesktop);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pti->rpdesk == pdesk)
|
|
{
|
|
/* Nothing to do */
|
|
ObDereferenceObject(pdesk);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* Make sure that we don't own any window in the current desktop */
|
|
if (!IsListEmpty(&pti->WindowListHead))
|
|
{
|
|
if (pdesk)
|
|
ObDereferenceObject(pdesk);
|
|
ERR("Attempted to change thread desktop although the thread has windows!\n");
|
|
EngSetLastError(ERROR_BUSY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Desktop is being re-set so clear out foreground. */
|
|
if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
|
|
{
|
|
// Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
|
|
IntSetFocusMessageQueue(NULL);
|
|
}
|
|
|
|
/* Before doing the switch, map the new desktop heap and allocate the new pcti */
|
|
if (pdesk != NULL)
|
|
{
|
|
Status = IntMapDesktopView(pdesk);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to map desktop heap!\n");
|
|
ObDereferenceObject(pdesk);
|
|
SetLastNtError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
pctiNew = DesktopHeapAlloc(pdesk, sizeof(CLIENTTHREADINFO));
|
|
if (pctiNew == NULL)
|
|
{
|
|
ERR("Failed to allocate new pcti\n");
|
|
IntUnmapDesktopView(pdesk);
|
|
ObDereferenceObject(pdesk);
|
|
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Processes, in particular Winlogon.exe, that manage window stations
|
|
* (especially the interactive WinSta0 window station) and desktops,
|
|
* may not be able to connect at startup to a window station and have
|
|
* an associated desktop as well, if none exists on the system already.
|
|
* Because creating a new window station does not affect the window station
|
|
* associated to the process, and because neither by associating a window
|
|
* station to the process nor creating a new desktop on it does associate
|
|
* a startup desktop to that process, the process has to actually assigns
|
|
* one of its threads to a desktop so that it gets automatically an assigned
|
|
* startup desktop.
|
|
*
|
|
* This is what actually happens for Winlogon.exe, which is started without
|
|
* any window station and desktop. By creating the first (and therefore
|
|
* interactive) WinSta0 window station, then assigning WinSta0 to itself
|
|
* and creating the Default desktop on it, and then assigning this desktop
|
|
* to its main thread, Winlogon.exe basically does the similar steps that
|
|
* would have been done automatically at its startup if there were already
|
|
* an existing WinSta0 window station and Default desktop.
|
|
*
|
|
* Of course all this must not be done if we are a SYSTEM or CSRSS thread.
|
|
*/
|
|
// if (pti->ppi->peProcess != gpepCSRSS)
|
|
if (!(pti->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) &&
|
|
pti->ppi->rpdeskStartup == NULL && hDesktop != NULL)
|
|
{
|
|
ERR("The process 0x%p '%s' didn't have an assigned startup desktop before, assigning it now!\n",
|
|
pti->ppi->peProcess, pti->ppi->peProcess->ImageFileName);
|
|
|
|
pti->ppi->hdeskStartup = hDesktop;
|
|
pti->ppi->rpdeskStartup = pdesk;
|
|
}
|
|
|
|
/* free all classes or move them to the shared heap */
|
|
if (pti->rpdesk != NULL)
|
|
{
|
|
if (!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
|
|
{
|
|
ERR("Failed to move process classes to shared heap!\n");
|
|
if (pdesk)
|
|
{
|
|
DesktopHeapFree(pdesk, pctiNew);
|
|
IntUnmapDesktopView(pdesk);
|
|
ObDereferenceObject(pdesk);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
pdeskOld = pti->rpdesk;
|
|
if (pti->pcti != &pti->cti)
|
|
pctiOld = pti->pcti;
|
|
else
|
|
pctiOld = NULL;
|
|
|
|
/* do the switch */
|
|
if (pdesk != NULL)
|
|
{
|
|
pti->rpdesk = pdesk;
|
|
pti->hdesk = hDesktop;
|
|
pti->pDeskInfo = pti->rpdesk->pDeskInfo;
|
|
pti->pcti = pctiNew;
|
|
|
|
pci->ulClientDelta = DesktopHeapGetUserDelta();
|
|
pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
|
|
pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
|
|
|
|
/* initialize the new pcti */
|
|
if (pctiOld != NULL)
|
|
{
|
|
RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
|
|
}
|
|
else
|
|
{
|
|
RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
|
|
pci->fsHooks = pti->fsHooks;
|
|
pci->dwTIFlags = pti->TIF_flags;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pti->rpdesk = NULL;
|
|
pti->hdesk = NULL;
|
|
pti->pDeskInfo = NULL;
|
|
pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
|
|
pci->ulClientDelta = 0;
|
|
pci->pDeskInfo = NULL;
|
|
pci->pClientThreadInfo = NULL;
|
|
}
|
|
|
|
/* clean up the old desktop */
|
|
if (pdeskOld != NULL)
|
|
{
|
|
RemoveEntryList(&pti->PtiLink);
|
|
if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
|
|
IntUnmapDesktopView(pdeskOld);
|
|
ObDereferenceObject(pdeskOld);
|
|
}
|
|
|
|
if (pdesk)
|
|
{
|
|
InsertTailList(&pdesk->PtiList, &pti->PtiLink);
|
|
}
|
|
|
|
TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* NtUserSetThreadDesktop
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
BOOL APIENTRY
|
|
NtUserSetThreadDesktop(HDESK hDesktop)
|
|
{
|
|
BOOL ret = FALSE;
|
|
|
|
UserEnterExclusive();
|
|
|
|
// FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
|
|
// here too and set the NT error level. Q. Is it necessary to have the validation
|
|
// in IntSetThreadDesktop? Is it needed there too?
|
|
if (hDesktop || (!hDesktop && PsGetCurrentProcess() == gpepCSRSS))
|
|
ret = IntSetThreadDesktop(hDesktop, FALSE);
|
|
|
|
UserLeave();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* EOF */
|