mirror of
https://github.com/reactos/reactos.git
synced 2024-11-09 08:08:38 +00:00
2d0dd58947
svn path=/branches/reactos-yarotows/; revision=47795
2032 lines
53 KiB
C
2032 lines
53 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* PURPOSE: Desktops
|
|
* FILE: subsystems/win32/win32k/ntuser/desktop.c
|
|
* PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
|
|
* REVISION HISTORY:
|
|
* 06-06-2001 CSH Created
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <win32k.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
#define TRACE DPRINT
|
|
#define WARN DPRINT1
|
|
#define ERR DPRINT1
|
|
|
|
static
|
|
VOID
|
|
IntFreeDesktopHeap(
|
|
IN OUT PDESKTOP Desktop
|
|
);
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
/* Currently active desktop */
|
|
PDESKTOP InputDesktop = NULL;
|
|
HDESK InputDesktopHandle = NULL;
|
|
HDC ScreenDeviceContext = NULL;
|
|
BOOL g_PaintDesktopVersion = FALSE;
|
|
|
|
GENERIC_MAPPING IntDesktopMapping =
|
|
{
|
|
STANDARD_RIGHTS_READ | DESKTOP_ENUMERATE |
|
|
DESKTOP_READOBJECTS,
|
|
STANDARD_RIGHTS_WRITE | DESKTOP_CREATEMENU |
|
|
DESKTOP_CREATEWINDOW |
|
|
DESKTOP_HOOKCONTROL |
|
|
DESKTOP_JOURNALPLAYBACK |
|
|
DESKTOP_JOURNALRECORD |
|
|
DESKTOP_WRITEOBJECTS,
|
|
STANDARD_RIGHTS_EXECUTE | DESKTOP_SWITCHDESKTOP,
|
|
STANDARD_RIGHTS_REQUIRED | DESKTOP_CREATEMENU |
|
|
DESKTOP_CREATEWINDOW |
|
|
DESKTOP_ENUMERATE |
|
|
DESKTOP_HOOKCONTROL |
|
|
DESKTOP_JOURNALPLAYBACK |
|
|
DESKTOP_JOURNALRECORD |
|
|
DESKTOP_READOBJECTS |
|
|
DESKTOP_SWITCHDESKTOP |
|
|
DESKTOP_WRITEOBJECTS
|
|
};
|
|
|
|
/* 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;
|
|
PUNICODE_STRING DesktopName;
|
|
|
|
/* 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 its name */
|
|
DesktopName = GET_DESKTOP_NAME(Desktop);
|
|
if (DesktopName)
|
|
{
|
|
/* Compare the name */
|
|
if (RtlEqualUnicodeString(RemainingName,
|
|
DesktopName,
|
|
(Attributes & OBJ_CASE_INSENSITIVE)))
|
|
{
|
|
/* 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, then 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;
|
|
|
|
/* Initialize shell hook window list and set the parent */
|
|
RtlZeroMemory(Desktop, sizeof(DESKTOP));
|
|
InitializeListHead(&Desktop->ShellHookWindows);
|
|
Desktop->rpwinstaParent = (PWINSTATION_OBJECT)ParseObject;
|
|
|
|
/* Put the desktop on the window station's list of associated desktops */
|
|
InsertTailList(&Desktop->rpwinstaParent->DesktopListHead,
|
|
&Desktop->ListEntry);
|
|
|
|
/* Set the desktop object and return success */
|
|
*Object = Desktop;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID APIENTRY
|
|
IntDesktopObjectDelete(PWIN32_DELETEMETHOD_PARAMETERS Parameters)
|
|
{
|
|
PDESKTOP Desktop = (PDESKTOP)Parameters->Object;
|
|
|
|
DPRINT("Deleting desktop (0x%X)\n", Desktop);
|
|
|
|
/* Remove the desktop from the window station's list of associcated desktops */
|
|
RemoveEntryList(&Desktop->ListEntry);
|
|
|
|
IntFreeDesktopHeap(Desktop);
|
|
}
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
InitDesktopImpl(VOID)
|
|
{
|
|
/* Set Desktop Object Attributes */
|
|
ExDesktopObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(DESKTOP);
|
|
ExDesktopObjectType->TypeInfo.GenericMapping = IntDesktopMapping;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
CleanupDesktopImpl(VOID)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static int GetSystemVersionString(LPWSTR buffer)
|
|
{
|
|
RTL_OSVERSIONINFOEXW versionInfo;
|
|
int len;
|
|
|
|
versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
|
|
|
|
if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo)))
|
|
return 0;
|
|
|
|
if (versionInfo.dwMajorVersion <= 4)
|
|
len = swprintf(buffer,
|
|
L"ReactOS Version %d.%d %s Build %d",
|
|
versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
|
|
versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
|
|
else
|
|
len = swprintf(buffer,
|
|
L"ReactOS %s (Build %d)",
|
|
versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
NTSTATUS FASTCALL
|
|
IntParseDesktopPath(PEPROCESS Process,
|
|
PUNICODE_STRING DesktopPath,
|
|
HWINSTA *hWinSta,
|
|
HDESK *hDesktop)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING WinSta, Desktop, FullName;
|
|
BOOL DesktopPresent = FALSE;
|
|
BOOL WinStaPresent = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(hWinSta);
|
|
|
|
*hWinSta = NULL;
|
|
|
|
if(hDesktop != NULL)
|
|
{
|
|
*hDesktop = NULL;
|
|
}
|
|
|
|
RtlInitUnicodeString(&WinSta, NULL);
|
|
RtlInitUnicodeString(&Desktop, NULL);
|
|
|
|
if(DesktopPath != NULL && DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
|
|
{
|
|
PWCHAR c = DesktopPath->Buffer;
|
|
USHORT wl = 0;
|
|
USHORT l = DesktopPath->Length;
|
|
|
|
/*
|
|
* Parse the desktop path string which can be in the form "WinSta\Desktop"
|
|
* or just "Desktop". In latter case WinSta0 will be used.
|
|
*/
|
|
|
|
while(l > 0)
|
|
{
|
|
if(*c == L'\\')
|
|
{
|
|
wl = (ULONG_PTR)c - (ULONG_PTR)DesktopPath->Buffer;
|
|
break;
|
|
}
|
|
l -= sizeof(WCHAR);
|
|
c++;
|
|
}
|
|
|
|
if(wl > 0)
|
|
{
|
|
WinSta.Length = wl;
|
|
WinSta.MaximumLength = wl + sizeof(WCHAR);
|
|
WinSta.Buffer = DesktopPath->Buffer;
|
|
|
|
WinStaPresent = TRUE;
|
|
c++;
|
|
}
|
|
|
|
Desktop.Length = DesktopPath->Length - wl;
|
|
if(wl > 0)
|
|
{
|
|
Desktop.Length -= sizeof(WCHAR);
|
|
}
|
|
if(Desktop.Length > 0)
|
|
{
|
|
Desktop.MaximumLength = Desktop.Length + sizeof(WCHAR);
|
|
Desktop.Buffer = ((wl > 0) ? c : DesktopPath->Buffer);
|
|
DesktopPresent = TRUE;
|
|
}
|
|
}
|
|
|
|
if(!WinStaPresent)
|
|
{
|
|
#if 0
|
|
/* search the process handle table for (inherited) window station
|
|
handles, use a more appropriate one than WinSta0 if possible. */
|
|
if (!ObFindHandleForObject(Process,
|
|
NULL,
|
|
ExWindowStationObjectType,
|
|
NULL,
|
|
(PHANDLE)hWinSta))
|
|
#endif
|
|
{
|
|
/* we had no luck searching for opened handles, use WinSta0 now */
|
|
RtlInitUnicodeString(&WinSta, L"WinSta0");
|
|
}
|
|
}
|
|
|
|
if(!DesktopPresent && hDesktop != NULL)
|
|
{
|
|
#if 0
|
|
/* search the process handle table for (inherited) desktop
|
|
handles, use a more appropriate one than Default if possible. */
|
|
if (!ObFindHandleForObject(Process,
|
|
NULL,
|
|
ExDesktopObjectType,
|
|
NULL,
|
|
(PHANDLE)hDesktop))
|
|
#endif
|
|
{
|
|
/* we had no luck searching for opened handles, use Desktop now */
|
|
RtlInitUnicodeString(&Desktop, L"Default");
|
|
}
|
|
}
|
|
|
|
if(*hWinSta == NULL)
|
|
{
|
|
if(!IntGetFullWindowStationName(&FullName, &WinSta, NULL))
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* open the window station */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&FullName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ObOpenObjectByName(&ObjectAttributes,
|
|
ExWindowStationObjectType,
|
|
KernelMode,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
(HANDLE*)hWinSta);
|
|
|
|
ExFreePoolWithTag(FullName.Buffer, TAG_STRING);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
DPRINT("Failed to reference window station %wZ PID: %d!\n", &WinSta, PsGetCurrentProcessId());
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if(hDesktop != NULL && *hDesktop == NULL)
|
|
{
|
|
if(!IntGetFullWindowStationName(&FullName, &WinSta, &Desktop))
|
|
{
|
|
NtClose(*hWinSta);
|
|
*hWinSta = NULL;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* open the desktop object */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&FullName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ObOpenObjectByName(&ObjectAttributes,
|
|
ExDesktopObjectType,
|
|
KernelMode,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
(HANDLE*)hDesktop);
|
|
|
|
ExFreePoolWithTag(FullName.Buffer, TAG_STRING);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
*hDesktop = NULL;
|
|
NtClose(*hWinSta);
|
|
*hWinSta = NULL;
|
|
SetLastNtError(Status);
|
|
DPRINT("Failed to reference desktop %wZ PID: %d!\n", &Desktop, PsGetCurrentProcessId());
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
SetLastNtError(Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
PDESKTOP FASTCALL
|
|
IntGetActiveDesktop(VOID)
|
|
{
|
|
return InputDesktop;
|
|
}
|
|
|
|
/*
|
|
* returns or creates a handle to the desktop object
|
|
*/
|
|
HDESK FASTCALL
|
|
IntGetDesktopObjectHandle(PDESKTOP DesktopObject)
|
|
{
|
|
NTSTATUS Status;
|
|
HDESK Ret;
|
|
|
|
ASSERT(DesktopObject);
|
|
|
|
if (!ObFindHandleForObject(PsGetCurrentProcess(),
|
|
DesktopObject,
|
|
ExDesktopObjectType,
|
|
NULL,
|
|
(PHANDLE)&Ret))
|
|
{
|
|
Status = ObOpenObjectByPointer(DesktopObject,
|
|
0,
|
|
NULL,
|
|
0,
|
|
ExDesktopObjectType,
|
|
UserMode,
|
|
(PHANDLE)&Ret);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
/* unable to create a handle */
|
|
DPRINT1("Unable to create a desktop handle\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Got handle: %lx\n", Ret);
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
PUSER_MESSAGE_QUEUE FASTCALL
|
|
IntGetFocusMessageQueue(VOID)
|
|
{
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
if (!pdo)
|
|
{
|
|
DPRINT("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)
|
|
{
|
|
DPRINT("No active desktop\n");
|
|
return;
|
|
}
|
|
if(NewQueue != NULL)
|
|
{
|
|
if(NewQueue->Desktop != NULL)
|
|
{
|
|
DPRINT("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);
|
|
IntDereferenceMessageQueue(Old);
|
|
}
|
|
}
|
|
|
|
HWND FASTCALL IntGetDesktopWindow(VOID)
|
|
{
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
if (!pdo)
|
|
{
|
|
DPRINT("No active desktop\n");
|
|
return NULL;
|
|
}
|
|
return pdo->DesktopWindow;
|
|
}
|
|
|
|
PWINDOW_OBJECT FASTCALL UserGetDesktopWindow(VOID)
|
|
{
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
|
|
if (!pdo)
|
|
{
|
|
DPRINT("No active desktop\n");
|
|
return NULL;
|
|
}
|
|
|
|
return UserGetWindowObject(pdo->DesktopWindow);
|
|
}
|
|
|
|
HWND FASTCALL IntGetMessageWindow(VOID)
|
|
{
|
|
PDESKTOP pdo = IntGetActiveDesktop();
|
|
|
|
if (!pdo)
|
|
{
|
|
DPRINT("No active desktop\n");
|
|
return NULL;
|
|
}
|
|
return pdo->spwndMessage->head.h;
|
|
}
|
|
|
|
HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
|
|
{
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
PDESKTOP pdo = pti->rpdesk;
|
|
if (NULL == pdo)
|
|
{
|
|
DPRINT1("Thread doesn't have a desktop\n");
|
|
return NULL;
|
|
}
|
|
return pdo->DesktopWindow;
|
|
}
|
|
|
|
BOOL FASTCALL IntDesktopUpdatePerUserSettings(BOOL bEnable)
|
|
{
|
|
if (bEnable)
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
|
|
NTSTATUS Status;
|
|
|
|
RtlZeroMemory(QueryTable, sizeof(QueryTable));
|
|
|
|
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
QueryTable[0].Name = L"PaintDesktopVersion";
|
|
QueryTable[0].EntryContext = &g_PaintDesktopVersion;
|
|
|
|
/* Query the "PaintDesktopVersion" flag in the "Control Panel\Desktop" key */
|
|
Status = RtlQueryRegistryValues(RTL_REGISTRY_USER,
|
|
L"Control Panel\\Desktop",
|
|
QueryTable, NULL, NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("RtlQueryRegistryValues failed for PaintDesktopVersion (%x)\n",
|
|
Status);
|
|
g_PaintDesktopVersion = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
DPRINT("PaintDesktopVersion = %d\n", g_PaintDesktopVersion);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_PaintDesktopVersion = FALSE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
HDC FASTCALL
|
|
UserGetDesktopDC(ULONG DcType, BOOL EmptyDC, BOOL ValidatehWnd)
|
|
{
|
|
PWINDOW_OBJECT DesktopObject = 0;
|
|
HDC DesktopHDC = 0;
|
|
|
|
if (DcType == DC_TYPE_DIRECT)
|
|
{
|
|
DesktopObject = UserGetDesktopWindow();
|
|
DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
|
|
}
|
|
else
|
|
{
|
|
HDEV hDev;
|
|
hDev = (HDEV)pPrimarySurface;
|
|
DesktopHDC = IntGdiCreateDisplayDC(hDev, DcType, EmptyDC);
|
|
}
|
|
|
|
return DesktopHDC;
|
|
}
|
|
|
|
VOID APIENTRY
|
|
UserRedrawDesktop()
|
|
{
|
|
PWINDOW_OBJECT Window = NULL;
|
|
|
|
Window = UserGetDesktopWindow();
|
|
|
|
IntInvalidateWindows( Window,
|
|
Window->hrgnUpdate,
|
|
RDW_FRAME |
|
|
RDW_ERASE |
|
|
RDW_INVALIDATE |
|
|
RDW_ALLCHILDREN);
|
|
}
|
|
|
|
|
|
NTSTATUS FASTCALL
|
|
co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height)
|
|
{
|
|
CSR_API_MESSAGE Request;
|
|
|
|
Request.Type = MAKE_CSR_API(SHOW_DESKTOP, CSR_GUI);
|
|
Request.Data.ShowDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
|
|
Request.Data.ShowDesktopRequest.Width = Width;
|
|
Request.Data.ShowDesktopRequest.Height = Height;
|
|
|
|
return co_CsrNotify(&Request);
|
|
}
|
|
|
|
NTSTATUS FASTCALL
|
|
IntHideDesktop(PDESKTOP Desktop)
|
|
{
|
|
#if 0
|
|
CSRSS_API_REQUEST Request;
|
|
CSRSS_API_REPLY Reply;
|
|
|
|
Request.Type = CSRSS_HIDE_DESKTOP;
|
|
Request.Data.HideDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
|
|
|
|
return NotifyCsrss(&Request, &Reply);
|
|
#else
|
|
|
|
PWINDOW_OBJECT DesktopWindow;
|
|
PWND DesktopWnd;
|
|
|
|
DesktopWindow = IntGetWindowObject(Desktop->DesktopWindow);
|
|
if (! DesktopWindow)
|
|
{
|
|
return ERROR_INVALID_WINDOW_HANDLE;
|
|
}
|
|
DesktopWnd = DesktopWindow->Wnd;
|
|
DesktopWnd->style &= ~WS_VISIBLE;
|
|
|
|
return STATUS_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
static
|
|
HWND* FASTCALL
|
|
UserBuildShellHookHwndList(PDESKTOP Desktop)
|
|
{
|
|
ULONG entries=0;
|
|
PSHELL_HOOK_WINDOW Current;
|
|
HWND* list;
|
|
|
|
/* fixme: if we save nb elements in desktop, we dont have to loop to find nb entries */
|
|
LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
|
|
entries++;
|
|
|
|
if (!entries) return NULL;
|
|
|
|
list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
|
|
if (list)
|
|
{
|
|
HWND* cursor = list;
|
|
|
|
LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
|
|
*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, LPARAM lParam)
|
|
{
|
|
PDESKTOP Desktop = IntGetActiveDesktop();
|
|
HWND* HwndList;
|
|
|
|
static UINT MsgType = 0;
|
|
|
|
if (!MsgType)
|
|
{
|
|
|
|
/* Too bad, this doesn't work.*/
|
|
#if 0
|
|
UNICODE_STRING Str;
|
|
RtlInitUnicodeString(&Str, L"SHELLHOOK");
|
|
MsgType = UserRegisterWindowMessage(&Str);
|
|
#endif
|
|
|
|
MsgType = IntAddAtom(L"SHELLHOOK");
|
|
|
|
DPRINT("MsgType = %x\n", MsgType);
|
|
if (!MsgType)
|
|
DPRINT1("LastError: %x\n", GetLastNtError());
|
|
}
|
|
|
|
if (!Desktop)
|
|
{
|
|
DPRINT("IntShellHookNotify: No desktop!\n");
|
|
return;
|
|
}
|
|
|
|
HwndList = UserBuildShellHookHwndList(Desktop);
|
|
if (HwndList)
|
|
{
|
|
HWND* cursor = HwndList;
|
|
|
|
for (; *cursor; cursor++)
|
|
{
|
|
DPRINT("Sending notify\n");
|
|
co_IntPostOrSendMessage(*cursor,
|
|
MsgType,
|
|
Message,
|
|
lParam);
|
|
}
|
|
|
|
ExFreePool(HwndList);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
DPRINT("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;
|
|
PSHELL_HOOK_WINDOW Current;
|
|
|
|
LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
|
|
{
|
|
if (Current->hWnd == hWnd)
|
|
{
|
|
RemoveEntryList(&Current->ListEntry);
|
|
ExFreePool(Current);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static VOID
|
|
IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
|
|
{
|
|
if (Desktop->hsectionDesktop != NULL)
|
|
{
|
|
ObDereferenceObject(Desktop->hsectionDesktop);
|
|
Desktop->hsectionDesktop = NULL;
|
|
}
|
|
}
|
|
/* 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
|
|
*/
|
|
|
|
HDESK APIENTRY
|
|
NtUserCreateDesktop(
|
|
POBJECT_ATTRIBUTES poa,
|
|
PUNICODE_STRING lpszDesktopDevice,
|
|
LPDEVMODEW lpdmw,
|
|
DWORD dwFlags,
|
|
ACCESS_MASK dwDesiredAccess)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PTHREADINFO W32Thread;
|
|
PWINSTATION_OBJECT WinStaObject;
|
|
PDESKTOP DesktopObject;
|
|
UNICODE_STRING DesktopName;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HDESK Desktop;
|
|
CSR_API_MESSAGE Request;
|
|
PVOID DesktopHeapSystemBase = NULL;
|
|
SIZE_T DesktopInfoSize;
|
|
UNICODE_STRING SafeDesktopName;
|
|
ULONG DummyContext;
|
|
ULONG_PTR HeapSize = 4 * 1024 * 1024; /* FIXME */
|
|
HWINSTA hWindowStation = NULL ;
|
|
PUNICODE_STRING lpszDesktopName = NULL;
|
|
UNICODE_STRING ClassName, MenuName;
|
|
LARGE_STRING WindowName;
|
|
PWND pWnd = NULL;
|
|
CREATESTRUCTW Cs;
|
|
DECLARE_RETURN(HDESK);
|
|
|
|
DPRINT("Enter NtUserCreateDesktop: %wZ\n", lpszDesktopName);
|
|
UserEnterExclusive();
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead( poa,
|
|
sizeof(OBJECT_ATTRIBUTES),
|
|
1);
|
|
|
|
hWindowStation = poa->RootDirectory;
|
|
lpszDesktopName = poa->ObjectName;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status =_SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END
|
|
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed reading Object Attributes from user space.\n");
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
Status = IntValidateWindowStationHandle(
|
|
hWindowStation,
|
|
KernelMode,
|
|
0, /* FIXME - WINSTA_CREATEDESKTOP */
|
|
&WinStaObject);
|
|
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed validation of window station handle (0x%X), cannot create desktop %wZ\n",
|
|
hWindowStation, lpszDesktopName);
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
if(lpszDesktopName != NULL)
|
|
{
|
|
Status = IntSafeCopyUnicodeString(&SafeDesktopName, lpszDesktopName);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(&SafeDesktopName, NULL);
|
|
}
|
|
|
|
if (! IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
|
|
&SafeDesktopName))
|
|
{
|
|
SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
|
|
ObDereferenceObject(WinStaObject);
|
|
if (lpszDesktopName)
|
|
ExFreePoolWithTag(SafeDesktopName.Buffer, TAG_STRING);
|
|
RETURN( NULL);
|
|
}
|
|
if (lpszDesktopName)
|
|
ExFreePoolWithTag(SafeDesktopName.Buffer, TAG_STRING);
|
|
ObDereferenceObject(WinStaObject);
|
|
|
|
/*
|
|
* Try to open already existing desktop
|
|
*/
|
|
|
|
DPRINT("Trying to open desktop (%wZ)\n", &DesktopName);
|
|
|
|
/* Initialize ObjectAttributes for the desktop object */
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&DesktopName,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ObOpenObjectByName(
|
|
&ObjectAttributes,
|
|
ExDesktopObjectType,
|
|
KernelMode,
|
|
NULL,
|
|
dwDesiredAccess,
|
|
(PVOID)&DummyContext,
|
|
(HANDLE*)&Desktop);
|
|
if (!NT_SUCCESS(Status)) RETURN(NULL);
|
|
if (Status == STATUS_OBJECT_NAME_EXISTS)
|
|
{
|
|
ExFreePoolWithTag(DesktopName.Buffer, TAG_STRING);
|
|
RETURN( Desktop);
|
|
}
|
|
|
|
/* Reference the desktop */
|
|
Status = ObReferenceObjectByHandle(Desktop,
|
|
0,
|
|
ExDesktopObjectType,
|
|
KernelMode,
|
|
(PVOID)&DesktopObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) RETURN(NULL);
|
|
|
|
DesktopObject->hsectionDesktop = NULL;
|
|
DesktopObject->pheapDesktop = UserCreateHeap(&DesktopObject->hsectionDesktop,
|
|
&DesktopHeapSystemBase,
|
|
HeapSize);
|
|
if (DesktopObject->pheapDesktop == NULL)
|
|
{
|
|
ObDereferenceObject(DesktopObject);
|
|
DPRINT1("Failed to create desktop heap!\n");
|
|
RETURN(NULL);
|
|
}
|
|
|
|
DesktopInfoSize = FIELD_OFFSET(DESKTOPINFO,
|
|
szDesktopName[(lpszDesktopName->Length / sizeof(WCHAR)) + 1]);
|
|
|
|
DesktopObject->pDeskInfo = RtlAllocateHeap(DesktopObject->pheapDesktop,
|
|
HEAP_NO_SERIALIZE,
|
|
DesktopInfoSize);
|
|
|
|
if (DesktopObject->pDeskInfo == NULL)
|
|
{
|
|
ObDereferenceObject(DesktopObject);
|
|
DPRINT1("Failed to create the DESKTOP structure!\n");
|
|
RETURN(NULL);
|
|
}
|
|
|
|
RtlZeroMemory(DesktopObject->pDeskInfo,
|
|
DesktopInfoSize);
|
|
|
|
DesktopObject->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
|
|
DesktopObject->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
|
|
RtlCopyMemory(DesktopObject->pDeskInfo->szDesktopName,
|
|
lpszDesktopName->Buffer,
|
|
lpszDesktopName->Length);
|
|
|
|
/* Initialize some local (to win32k) desktop state. */
|
|
InitializeListHead(&DesktopObject->PtiList);
|
|
DesktopObject->ActiveMessageQueue = NULL;
|
|
ExFreePoolWithTag(DesktopName.Buffer, TAG_STRING);
|
|
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to create desktop handle\n");
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
/*
|
|
* Create a handle for CSRSS and notify CSRSS for Creating Desktop Window.
|
|
*
|
|
* Honestly, I believe this is a cleverly written hack that allowed ReactOS
|
|
* to function at the beginning of the project by ramroding the GUI into
|
|
* operation and making the desktop window work from user space.
|
|
* (jt)
|
|
*/
|
|
Request.Type = MAKE_CSR_API(CREATE_DESKTOP, CSR_GUI);
|
|
Status = CsrInsertObject(Desktop,
|
|
GENERIC_ALL,
|
|
(HANDLE*)&Request.Data.CreateDesktopRequest.DesktopHandle);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to create desktop handle for CSRSS\n");
|
|
ZwClose(Desktop);
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
Status = co_CsrNotify(&Request);
|
|
if (! NT_SUCCESS(Status))
|
|
{
|
|
CsrCloseHandle(Request.Data.CreateDesktopRequest.DesktopHandle);
|
|
DPRINT1("Failed to notify CSRSS about new desktop\n");
|
|
ZwClose(Desktop);
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
|
|
W32Thread = PsGetCurrentThreadWin32Thread();
|
|
|
|
if (!W32Thread->rpdesk) IntSetThreadDesktop(DesktopObject,FALSE);
|
|
|
|
/*
|
|
Based on wine/server/window.c in get_desktop_window.
|
|
*/
|
|
|
|
ClassName.Buffer = ((PWSTR)((ULONG_PTR)(WORD)(gpsi->atomSysClass[ICLS_HWNDMESSAGE])));
|
|
ClassName.Length = 0;
|
|
RtlZeroMemory(&MenuName, sizeof(MenuName));
|
|
RtlZeroMemory(&WindowName, sizeof(WindowName));
|
|
|
|
RtlZeroMemory(&Cs, sizeof(Cs));
|
|
Cs.cx = Cs.cy = 100;
|
|
Cs.style = WS_POPUP|WS_CLIPCHILDREN;
|
|
Cs.hInstance = hModClient;
|
|
Cs.lpszName = (LPCWSTR) &WindowName;
|
|
Cs.lpszClass = (LPCWSTR) &ClassName;
|
|
|
|
pWnd = co_UserCreateWindowEx(&Cs, &ClassName, &WindowName);
|
|
if (!pWnd)
|
|
{
|
|
DPRINT1("Failed to create Message window handle\n");
|
|
}
|
|
else
|
|
{
|
|
DesktopObject->spwndMessage = pWnd;
|
|
}
|
|
|
|
RETURN( Desktop);
|
|
|
|
CLEANUP:
|
|
DPRINT("Leave NtUserCreateDesktop, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
/*
|
|
* 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(
|
|
PUNICODE_STRING lpszDesktopName,
|
|
DWORD dwFlags,
|
|
ACCESS_MASK dwDesiredAccess)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HWINSTA WinSta;
|
|
PWINSTATION_OBJECT WinStaObject;
|
|
UNICODE_STRING DesktopName;
|
|
UNICODE_STRING SafeDesktopName;
|
|
NTSTATUS Status;
|
|
HDESK Desktop;
|
|
BOOL Result;
|
|
DECLARE_RETURN(HDESK);
|
|
|
|
DPRINT("Enter NtUserOpenDesktop: %wZ\n", lpszDesktopName);
|
|
UserEnterExclusive();
|
|
|
|
/*
|
|
* Validate the window station handle and compose the fully
|
|
* qualified desktop name
|
|
*/
|
|
|
|
WinSta = UserGetProcessWindowStation();
|
|
Status = IntValidateWindowStationHandle(
|
|
WinSta,
|
|
KernelMode,
|
|
0,
|
|
&WinStaObject);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed validation of window station handle (0x%X)\n", WinSta);
|
|
SetLastNtError(Status);
|
|
RETURN( 0);
|
|
}
|
|
|
|
if(lpszDesktopName != NULL)
|
|
{
|
|
Status = IntSafeCopyUnicodeString(&SafeDesktopName, lpszDesktopName);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN( NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(&SafeDesktopName, NULL);
|
|
}
|
|
|
|
Result = IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
|
|
&SafeDesktopName);
|
|
|
|
if (lpszDesktopName)
|
|
ExFreePoolWithTag(SafeDesktopName.Buffer, TAG_STRING);
|
|
ObDereferenceObject(WinStaObject);
|
|
|
|
|
|
if (!Result)
|
|
{
|
|
SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
|
|
RETURN( 0);
|
|
}
|
|
|
|
|
|
DPRINT("Trying to open desktop (%wZ)\n", &DesktopName);
|
|
|
|
/* Initialize ObjectAttributes for the desktop object */
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&DesktopName,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ObOpenObjectByName(
|
|
&ObjectAttributes,
|
|
ExDesktopObjectType,
|
|
KernelMode,
|
|
NULL,
|
|
dwDesiredAccess,
|
|
NULL,
|
|
(HANDLE*)&Desktop);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
ExFreePool(DesktopName.Buffer);
|
|
RETURN( 0);
|
|
}
|
|
|
|
DPRINT("Successfully opened desktop (%wZ)\n", &DesktopName);
|
|
ExFreePool(DesktopName.Buffer);
|
|
|
|
RETURN( Desktop);
|
|
|
|
CLEANUP:
|
|
DPRINT("Leave NtUserOpenDesktop, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
PDESKTOP Object;
|
|
NTSTATUS Status;
|
|
HDESK Desktop;
|
|
DECLARE_RETURN(HDESK);
|
|
|
|
DPRINT("Enter NtUserOpenInputDesktop\n");
|
|
UserEnterExclusive();
|
|
|
|
DPRINT("About to open input desktop\n");
|
|
|
|
/* Get a pointer to the desktop object */
|
|
|
|
Status = IntValidateDesktopHandle(
|
|
InputDesktopHandle,
|
|
UserMode,
|
|
0,
|
|
&Object);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Validation of input desktop handle (0x%X) failed\n", InputDesktop);
|
|
RETURN((HDESK)0);
|
|
}
|
|
|
|
/* Create a new handle to the object */
|
|
|
|
Status = ObOpenObjectByPointer(
|
|
Object,
|
|
0,
|
|
NULL,
|
|
dwDesiredAccess,
|
|
ExDesktopObjectType,
|
|
UserMode,
|
|
(HANDLE*)&Desktop);
|
|
|
|
ObDereferenceObject(Object);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Successfully opened input desktop\n");
|
|
RETURN((HDESK)Desktop);
|
|
}
|
|
|
|
SetLastNtError(Status);
|
|
RETURN((HDESK)0);
|
|
|
|
CLEANUP:
|
|
DPRINT("Leave NtUserOpenInputDesktop, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
/*
|
|
* 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 Object;
|
|
NTSTATUS Status;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
DPRINT("Enter NtUserCloseDesktop\n");
|
|
UserEnterExclusive();
|
|
|
|
DPRINT("About to close desktop handle (0x%X)\n", hDesktop);
|
|
|
|
Status = IntValidateDesktopHandle(
|
|
hDesktop,
|
|
UserMode,
|
|
0,
|
|
&Object);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
ObDereferenceObject(Object);
|
|
|
|
DPRINT("Closing desktop handle (0x%X)\n", hDesktop);
|
|
|
|
Status = ZwClose(hDesktop);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
RETURN(TRUE);
|
|
|
|
CLEANUP:
|
|
DPRINT("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
RECTL Rect;
|
|
HBRUSH DesktopBrush, PreviousBrush;
|
|
HWND hWndDesktop;
|
|
BOOL doPatBlt = TRUE;
|
|
PWINDOW_OBJECT WndDesktop;
|
|
int len;
|
|
COLORREF color_old;
|
|
UINT align_old;
|
|
int mode_old;
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
PWINSTATION_OBJECT WinSta = pti->rpdesk->rpwinstaParent;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
UserEnterExclusive();
|
|
DPRINT("Enter NtUserPaintDesktop\n");
|
|
|
|
GdiGetClipBox(hDC, &Rect);
|
|
|
|
hWndDesktop = IntGetDesktopWindow();
|
|
|
|
WndDesktop = UserGetWindowObject(hWndDesktop);
|
|
if (!WndDesktop)
|
|
{
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
DesktopBrush = (HBRUSH)WndDesktop->Wnd->pcls->hbrBackground;
|
|
|
|
|
|
/*
|
|
* Paint desktop background
|
|
*/
|
|
|
|
if (WinSta->hbmWallpaper != NULL)
|
|
{
|
|
PWINDOW_OBJECT DeskWin;
|
|
|
|
DeskWin = UserGetWindowObject(hWndDesktop);
|
|
|
|
if (DeskWin)
|
|
{
|
|
SIZE sz;
|
|
int x, y;
|
|
HDC hWallpaperDC;
|
|
|
|
sz.cx = DeskWin->Wnd->rcWindow.right - DeskWin->Wnd->rcWindow.left;
|
|
sz.cy = DeskWin->Wnd->rcWindow.bottom - DeskWin->Wnd->rcWindow.top;
|
|
|
|
if (WinSta->WallpaperMode == wmStretch ||
|
|
WinSta->WallpaperMode == wmTile)
|
|
{
|
|
x = 0;
|
|
y = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Find the upper left corner, can be negtive if the bitmap is bigger then the screen */
|
|
x = (sz.cx / 2) - (WinSta->cxWallpaper / 2);
|
|
y = (sz.cy / 2) - (WinSta->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, WinSta->cxWallpaper, WinSta->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, WinSta->hbmWallpaper);
|
|
|
|
if (WinSta->WallpaperMode == wmStretch)
|
|
{
|
|
if(Rect.right && Rect.bottom)
|
|
NtGdiStretchBlt(hDC,
|
|
x,
|
|
y,
|
|
sz.cx,
|
|
sz.cy,
|
|
hWallpaperDC,
|
|
0,
|
|
0,
|
|
WinSta->cxWallpaper,
|
|
WinSta->cyWallpaper,
|
|
SRCCOPY,
|
|
0);
|
|
|
|
}
|
|
else if (WinSta->WallpaperMode == wmTile)
|
|
{
|
|
/* paint the bitmap across the screen then down */
|
|
for(y = 0; y < Rect.bottom; y += WinSta->cyWallpaper)
|
|
{
|
|
for(x = 0; x < Rect.right; x += WinSta->cxWallpaper)
|
|
{
|
|
NtGdiBitBlt(hDC,
|
|
x,
|
|
y,
|
|
WinSta->cxWallpaper,
|
|
WinSta->cyWallpaper,
|
|
hWallpaperDC,
|
|
0,
|
|
0,
|
|
SRCCOPY,
|
|
0,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NtGdiBitBlt(hDC,
|
|
x,
|
|
y,
|
|
WinSta->cxWallpaper,
|
|
WinSta->cyWallpaper,
|
|
hWallpaperDC,
|
|
0,
|
|
0,
|
|
SRCCOPY,
|
|
0,
|
|
0);
|
|
}
|
|
NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
|
|
NtGdiDeleteObjectApp(hWallpaperDC);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Back ground 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 system version on the desktop background
|
|
*/
|
|
|
|
if (g_PaintDesktopVersion)
|
|
{
|
|
static WCHAR s_wszVersion[256] = {0};
|
|
RECTL rect;
|
|
|
|
if (*s_wszVersion)
|
|
{
|
|
len = wcslen(s_wszVersion);
|
|
}
|
|
else
|
|
{
|
|
len = GetSystemVersionString(s_wszVersion);
|
|
}
|
|
|
|
if (len)
|
|
{
|
|
if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0))
|
|
{
|
|
rect.right = UserGetSystemMetrics(SM_CXSCREEN);
|
|
rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
|
|
}
|
|
|
|
color_old = IntGdiSetTextColor(hDC, RGB(255,255,255));
|
|
align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
|
|
mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
|
|
|
|
GreExtTextOutW(hDC, rect.right-16, rect.bottom-48, 0, NULL, s_wszVersion, len, NULL, 0);
|
|
|
|
IntGdiSetBkMode(hDC, mode_old);
|
|
IntGdiSetTextAlign(hDC, align_old);
|
|
IntGdiSetTextColor(hDC, color_old);
|
|
}
|
|
}
|
|
|
|
RETURN(TRUE);
|
|
|
|
CLEANUP:
|
|
DPRINT("Leave NtUserPaintDesktop, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
|
|
/*
|
|
* NtUserSwitchDesktop
|
|
*
|
|
* Sets the current input (interactive) desktop.
|
|
*
|
|
* Parameters
|
|
* hDesktop
|
|
* Handle to desktop.
|
|
*
|
|
* Return Value
|
|
* Status
|
|
*
|
|
* Status
|
|
* @unimplemented
|
|
*/
|
|
|
|
BOOL APIENTRY
|
|
NtUserSwitchDesktop(HDESK hDesktop)
|
|
{
|
|
PDESKTOP DesktopObject;
|
|
NTSTATUS Status;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
UserEnterExclusive();
|
|
DPRINT("Enter NtUserSwitchDesktop\n");
|
|
|
|
DPRINT("About to switch desktop (0x%X)\n", hDesktop);
|
|
|
|
Status = IntValidateDesktopHandle(
|
|
hDesktop,
|
|
UserMode,
|
|
0,
|
|
&DesktopObject);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
/*
|
|
* Don't allow applications switch the desktop if it's locked, unless the caller
|
|
* is the logon application itself
|
|
*/
|
|
if((DesktopObject->rpwinstaParent->Flags & WSS_LOCKED) &&
|
|
LogonProcess != NULL && LogonProcess != PsGetCurrentProcessWin32Process())
|
|
{
|
|
ObDereferenceObject(DesktopObject);
|
|
DPRINT1("Switching desktop 0x%x denied because the work station is locked!\n", hDesktop);
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
if(DesktopObject->rpwinstaParent != InputWindowStation)
|
|
{
|
|
ObDereferenceObject(DesktopObject);
|
|
DPRINT1("Switching desktop 0x%x denied because desktop doesn't belong to the interactive winsta!\n", hDesktop);
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
/* FIXME: Fail if the process is associated with a secured
|
|
desktop such as Winlogon or Screen-Saver */
|
|
/* FIXME: Connect to input device */
|
|
|
|
/* Set the active desktop in the desktop's window station. */
|
|
InputWindowStation->ActiveDesktop = DesktopObject;
|
|
|
|
/* Set the global state. */
|
|
InputDesktop = DesktopObject;
|
|
InputDesktopHandle = hDesktop;
|
|
|
|
ObDereferenceObject(DesktopObject);
|
|
|
|
RETURN(TRUE);
|
|
|
|
CLEANUP:
|
|
DPRINT("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
/*
|
|
* NtUserResolveDesktopForWOW
|
|
*
|
|
* Status
|
|
* @unimplemented
|
|
*/
|
|
|
|
DWORD APIENTRY
|
|
NtUserResolveDesktopForWOW(DWORD Unknown0)
|
|
{
|
|
UNIMPLEMENTED
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* NtUserGetThreadDesktop
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
HDESK APIENTRY
|
|
NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
|
|
{
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
PDESKTOP DesktopObject;
|
|
HDESK Ret, hThreadDesktop;
|
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|
DECLARE_RETURN(HDESK);
|
|
|
|
UserEnterExclusive();
|
|
DPRINT("Enter NtUserGetThreadDesktop\n");
|
|
|
|
if(!dwThreadId)
|
|
{
|
|
SetLastWin32Error(ERROR_INVALID_PARAMETER);
|
|
RETURN(0);
|
|
}
|
|
|
|
Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
SetLastWin32Error(ERROR_INVALID_PARAMETER);
|
|
RETURN(0);
|
|
}
|
|
|
|
if(Thread->ThreadsProcess == PsGetCurrentProcess())
|
|
{
|
|
/* just return the handle, we queried the desktop handle of a thread running
|
|
in the same context */
|
|
Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
|
|
ObDereferenceObject(Thread);
|
|
RETURN(Ret);
|
|
}
|
|
|
|
/* get the desktop handle and the desktop of the thread */
|
|
if(!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
|
|
!(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
|
|
{
|
|
ObDereferenceObject(Thread);
|
|
DPRINT1("Desktop information of thread 0x%x broken!?\n", dwThreadId);
|
|
RETURN(NULL);
|
|
}
|
|
|
|
/* we could just use DesktopObject instead of looking up the handle, but latter
|
|
may be a bit safer (e.g. when the desktop is being destroyed */
|
|
/* switch into the context of the thread we're trying to get the desktop from,
|
|
so we can use the handle */
|
|
KeAttachProcess(&Thread->ThreadsProcess->Pcb);
|
|
Status = ObReferenceObjectByHandle(hThreadDesktop,
|
|
GENERIC_ALL,
|
|
ExDesktopObjectType,
|
|
UserMode,
|
|
(PVOID*)&DesktopObject,
|
|
&HandleInformation);
|
|
KeDetachProcess();
|
|
|
|
/* the handle couldn't be found, there's nothing to get... */
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
ObDereferenceObject(Thread);
|
|
RETURN(NULL);
|
|
}
|
|
|
|
/* lookup our handle table if we can find a handle to the desktop object,
|
|
if not, create one */
|
|
Ret = IntGetDesktopObjectHandle(DesktopObject);
|
|
|
|
/* all done, we got a valid handle to the desktop */
|
|
ObDereferenceObject(DesktopObject);
|
|
ObDereferenceObject(Thread);
|
|
RETURN(Ret);
|
|
|
|
CLEANUP:
|
|
DPRINT("Leave NtUserGetThreadDesktop, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
static NTSTATUS
|
|
IntUnmapDesktopView(IN PDESKTOP DesktopObject)
|
|
{
|
|
PTHREADINFO ti;
|
|
PPROCESSINFO CurrentWin32Process;
|
|
PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
TRACE("DO %p\n");
|
|
|
|
CurrentWin32Process = PsGetCurrentProcessWin32Process();
|
|
PrevLink = &CurrentWin32Process->HeapMappings.Next;
|
|
|
|
/* unmap if we're the last thread using the desktop */
|
|
HeapMapping = *PrevLink;
|
|
while (HeapMapping != NULL)
|
|
{
|
|
if (HeapMapping->KernelMapping == (PVOID)DesktopObject->pheapDesktop)
|
|
{
|
|
if (--HeapMapping->Count == 0)
|
|
{
|
|
*PrevLink = HeapMapping->Next;
|
|
|
|
Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
|
|
HeapMapping->UserMapping);
|
|
|
|
ObDereferenceObject(DesktopObject);
|
|
|
|
UserHeapFree(HeapMapping);
|
|
break;
|
|
}
|
|
}
|
|
|
|
PrevLink = &HeapMapping->Next;
|
|
HeapMapping = HeapMapping->Next;
|
|
}
|
|
|
|
ti = GetW32ThreadInfo();
|
|
if (ti != NULL)
|
|
{
|
|
GetWin32ClientInfo()->pDeskInfo = NULL;
|
|
}
|
|
GetWin32ClientInfo()->ulClientDelta = 0;
|
|
|
|
return Status;
|
|
}
|
|
|
|
static NTSTATUS
|
|
IntMapDesktopView(IN PDESKTOP DesktopObject)
|
|
{
|
|
PTHREADINFO ti;
|
|
PPROCESSINFO CurrentWin32Process;
|
|
PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
|
|
PVOID UserBase = NULL;
|
|
SIZE_T ViewSize = 0;
|
|
LARGE_INTEGER Offset;
|
|
NTSTATUS Status;
|
|
|
|
CurrentWin32Process = PsGetCurrentProcessWin32Process();
|
|
PrevLink = &CurrentWin32Process->HeapMappings.Next;
|
|
|
|
/* find out if another thread already mapped the desktop heap */
|
|
HeapMapping = *PrevLink;
|
|
while (HeapMapping != NULL)
|
|
{
|
|
if (HeapMapping->KernelMapping == (PVOID)DesktopObject->pheapDesktop)
|
|
{
|
|
HeapMapping->Count++;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PrevLink = &HeapMapping->Next;
|
|
HeapMapping = HeapMapping->Next;
|
|
}
|
|
|
|
/* we're the first, map the heap */
|
|
DPRINT("Noone mapped the desktop heap %p yet, so - map it!\n", DesktopObject->pheapDesktop);
|
|
Offset.QuadPart = 0;
|
|
Status = MmMapViewOfSection(DesktopObject->hsectionDesktop,
|
|
PsGetCurrentProcess(),
|
|
&UserBase,
|
|
0,
|
|
0,
|
|
&Offset,
|
|
&ViewSize,
|
|
ViewUnmap,
|
|
SEC_NO_CHANGE,
|
|
PAGE_EXECUTE_READ); /* would prefer PAGE_READONLY, but thanks to RTL heaps... */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to map desktop\n");
|
|
return Status;
|
|
}
|
|
|
|
/* add the mapping */
|
|
HeapMapping = UserHeapAlloc(sizeof(W32HEAP_USER_MAPPING));
|
|
if (HeapMapping == NULL)
|
|
{
|
|
MmUnmapViewOfSection(PsGetCurrentProcess(),
|
|
UserBase);
|
|
DPRINT1("UserHeapAlloc() failed!\n");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
HeapMapping->Next = NULL;
|
|
HeapMapping->KernelMapping = (PVOID)DesktopObject->pheapDesktop;
|
|
HeapMapping->UserMapping = UserBase;
|
|
HeapMapping->Limit = ViewSize;
|
|
HeapMapping->Count = 1;
|
|
*PrevLink = HeapMapping;
|
|
|
|
ObReferenceObject(DesktopObject);
|
|
|
|
/* create a W32THREADINFO structure if not already done, or update it */
|
|
ti = GetW32ThreadInfo();
|
|
GetWin32ClientInfo()->ulClientDelta = DesktopHeapGetUserDelta();
|
|
if (ti != NULL)
|
|
{
|
|
if (GetWin32ClientInfo()->pDeskInfo == NULL)
|
|
{
|
|
GetWin32ClientInfo()->pDeskInfo =
|
|
(PVOID)((ULONG_PTR)DesktopObject->pDeskInfo -
|
|
GetWin32ClientInfo()->ulClientDelta);
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOL
|
|
IntSetThreadDesktop(IN PDESKTOP DesktopObject,
|
|
IN BOOL FreeOnFailure)
|
|
{
|
|
PDESKTOP OldDesktop;
|
|
PTHREADINFO W32Thread;
|
|
NTSTATUS Status;
|
|
BOOL MapHeap;
|
|
|
|
DPRINT("IntSetThreadDesktop() DO=%p, FOF=%d\n", DesktopObject, FreeOnFailure);
|
|
MapHeap = (PsGetCurrentProcess() != PsInitialSystemProcess);
|
|
W32Thread = PsGetCurrentThreadWin32Thread();
|
|
|
|
if (W32Thread->rpdesk != DesktopObject)
|
|
{
|
|
OldDesktop = W32Thread->rpdesk;
|
|
|
|
if (!IsListEmpty(&W32Thread->WindowListHead))
|
|
{
|
|
DPRINT1("Attempted to change thread desktop although the thread has windows!\n");
|
|
SetLastWin32Error(ERROR_BUSY);
|
|
return FALSE;
|
|
}
|
|
|
|
W32Thread->rpdesk = DesktopObject;
|
|
|
|
if (MapHeap && DesktopObject != NULL)
|
|
{
|
|
Status = IntMapDesktopView(DesktopObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Hack for system threads */
|
|
if (NtCurrentTeb())
|
|
{
|
|
PCLIENTINFO pci = GetWin32ClientInfo();
|
|
pci->ulClientDelta = DesktopHeapGetUserDelta();
|
|
if (DesktopObject)
|
|
{
|
|
pci->pDeskInfo = (PVOID)((ULONG_PTR)DesktopObject->pDeskInfo - pci->ulClientDelta);
|
|
}
|
|
}
|
|
|
|
if (OldDesktop != NULL &&
|
|
!IntCheckProcessDesktopClasses(OldDesktop,
|
|
FreeOnFailure))
|
|
{
|
|
DPRINT1("Failed to move process classes to shared heap!\n");
|
|
|
|
/* failed to move desktop classes to the shared heap,
|
|
unmap the view and return the error */
|
|
if (MapHeap && DesktopObject != NULL)
|
|
IntUnmapDesktopView(DesktopObject);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Remove the thread from the old desktop's list */
|
|
RemoveEntryList(&W32Thread->PtiLink);
|
|
|
|
if (DesktopObject != NULL)
|
|
{
|
|
ObReferenceObject(DesktopObject);
|
|
/* Insert into new desktop's list */
|
|
InsertTailList(&DesktopObject->PtiList, &W32Thread->PtiLink);
|
|
}
|
|
|
|
if (OldDesktop != NULL)
|
|
{
|
|
if (MapHeap)
|
|
{
|
|
IntUnmapDesktopView(OldDesktop);
|
|
}
|
|
|
|
ObDereferenceObject(OldDesktop);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* NtUserSetThreadDesktop
|
|
*
|
|
* Status
|
|
* @implemented
|
|
*/
|
|
|
|
BOOL APIENTRY
|
|
NtUserSetThreadDesktop(HDESK hDesktop)
|
|
{
|
|
PDESKTOP DesktopObject;
|
|
NTSTATUS Status;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
UserEnterExclusive();
|
|
DPRINT("Enter NtUserSetThreadDesktop\n");
|
|
|
|
/* Validate the new desktop. */
|
|
Status = IntValidateDesktopHandle(
|
|
hDesktop,
|
|
UserMode,
|
|
0,
|
|
&DesktopObject);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
/* FIXME: Should check here to see if the thread has any windows. */
|
|
|
|
if (!IntSetThreadDesktop(DesktopObject,
|
|
FALSE))
|
|
{
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
RETURN(TRUE);
|
|
|
|
CLEANUP:
|
|
DPRINT("Leave NtUserSetThreadDesktop, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
/* EOF */
|