Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5791 lines
184 KiB

/***************************** Module Header ******************************\
* Module Name: desktop.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* This module contains everything related to the desktop support.
*
* History:
* 23-Oct-1990 DarrinM Created.
* 01-Feb-1991 JimA Added new API stubs.
* 11-Feb-1991 JimA Added access checks.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
typedef struct _DESKTOP_CONTEXT {
PUNICODE_STRING pstrDevice;
LPDEVMODE lpDevMode;
DWORD dwFlags;
DWORD dwCallerSessionId;
} DESKTOP_CONTEXT, *PDESKTOP_CONTEXT;
extern BOOL gfGdiEnabled;
/*
* We use these to protect a handle we're currently using from being closed.
*/
PEPROCESS gProcessInUse;
HANDLE gHandleInUse;
/*
* Debug Related Info.
*/
#if DBG
DWORD gDesktopsBusy; // diagnostic
#endif
#ifdef DEBUG_DESK
VOID ValidateDesktop(PDESKTOP pdesk);
#endif
VOID DbgCheckForThreadsOnDesktop(PPROCESSINFO ppi, PDESKTOP pdesk);
VOID FreeView(
PEPROCESS Process,
PDESKTOP pdesk);
NTSTATUS
SetDisconnectDesktopSecurity(
IN HDESK hdeskDisconnect);
#ifdef POOL_INSTR
extern FAST_MUTEX* gpAllocFastMutex; // mutex to syncronize pool allocations
#endif
PVOID DesktopAlloc(
PDESKTOP pdesk,
UINT uSize,
DWORD tag)
{
PVOID ptr;
if (pdesk->dwDTFlags & DF_DESTROYED) {
RIPMSG2(RIP_ERROR,
"DesktopAlloc: tag %d pdesk %#p is destroyed",
tag,
pdesk);
return NULL;
}
ptr = Win32HeapAlloc(pdesk->pheapDesktop, uSize, tag, 0);
if (ptr == NULL && TEST_SRVIF(SRVIF_LOGDESKTOPHEAPFAILURE)) {
/*
* This will be logged at most once per-session so as to avoid
* flooding the event log.
*/
CLEAR_SRVIF(SRVIF_LOGDESKTOPHEAPFAILURE);
UserLogError(NULL, 0, WARNING_DESKTOP_HEAP_ALLOC_FAIL);
}
return ptr;
}
#if DBG
WCHAR s_strName[100];
CONST WCHAR s_strNameNull[] = L"null";
/***************************************************************************\
* GetDesktopName
*
* This is for debug purposes.
*
* Dec-10-1997 CLupu Created.
\***************************************************************************/
LPCWSTR GetDesktopName(
PDESKTOP pdesk)
{
POBJECT_NAME_INFORMATION DesktopObjectName = (POBJECT_NAME_INFORMATION)s_strName;
ULONG DesktopObjectNameLength = sizeof(s_strName) - sizeof(WCHAR);
NTSTATUS Status;
if (pdesk == NULL) {
return s_strNameNull;
}
Status = ObQueryNameString(pdesk,
DesktopObjectName,
DesktopObjectNameLength,
&DesktopObjectNameLength);
if (!NT_SUCCESS(Status)) {
return s_strNameNull;
}
UserAssert(DesktopObjectNameLength + sizeof(WCHAR) < sizeof(s_strName));
DesktopObjectName->Name.Buffer[DesktopObjectName->Name.Length / sizeof(WCHAR)] = 0;
return (LPCWSTR)DesktopObjectName->Name.Buffer;
}
#endif
typedef struct _CST_THREADS {
PVOID pParam;
HANDLE UniqueProcessId;
UINT uID;
} CST_THREADS, *PCST_THREADS;
CST_THREADS gCSTParam[CST_MAX_THREADS];
CST_THREADS gCSTRemoteParam[CST_MAX_THREADS];
/***************************************************************************\
* CSTPop
*
* Pops the first available pointer and ID in gCSTParam or gCSTRemoteParam.
*
* History:
* 31-Mar-00 MHamid Created.
\***************************************************************************/
BOOL CSTPop(
PUINT pThreadID,
PVOID *pParam,
PHANDLE pUniqueProcessId,
BOOL bRemoteThreadStack)
{
UINT i = 0;
PCST_THREADS pCSTParam = bRemoteThreadStack ? gCSTRemoteParam : gCSTParam;
CheckCritIn();
while (i < CST_MAX_THREADS) {
if (pCSTParam[i].pParam) {
*pParam = pCSTParam[i].pParam;
if (NULL != pUniqueProcessId) {
*pUniqueProcessId = pCSTParam[i].UniqueProcessId;
}
*pThreadID = pCSTParam[i].uID;
pCSTParam[i].pParam = NULL;
pCSTParam[i].uID = 0;
return TRUE;
}
i++;
}
return FALSE;
}
/***************************************************************************\
* CSTPush
*
* Push pointer (pParam) and ID in the first empty spot in gCSTParam or
* gCSTRemoteParam.
*
* History:
* 31-Mar-00 MHamid Created.
\***************************************************************************/
BOOL CSTPush(
UINT uThreadID,
PVOID pParam,
HANDLE UniqueProcessId,
BOOL bRemoteThreadStack)
{
UINT i = 0;
PCST_THREADS pCSTParam = bRemoteThreadStack ? gCSTRemoteParam : gCSTParam;
CheckCritIn();
while (i < CST_MAX_THREADS) {
if (!pCSTParam[i].pParam) {
pCSTParam[i].pParam = pParam;
pCSTParam[i].UniqueProcessId = UniqueProcessId;
pCSTParam[i].uID = uThreadID;
return TRUE;
}
i++;
}
return FALSE;
}
/***************************************************************************\
* CSTCleanupStack
*
* Clean up any items left on gCSTParam or gCSTRemoteParam.
*
* History:
* 20-Aug-00 MSadek Created.
\***************************************************************************/
VOID CSTCleanupStack(
BOOL bRemoteThreadStack)
{
UINT uThreadID;
PVOID pObj;
while(CSTPop(&uThreadID, &pObj, NULL, bRemoteThreadStack)) {
switch(uThreadID) {
case CST_RIT:
if (((PRIT_INIT)pObj)->pRitReadyEvent) {
FreeKernelEvent(&((PRIT_INIT)pObj)->pRitReadyEvent);
}
break;
case CST_POWER:
if (((PPOWER_INIT)pObj)->pPowerReadyEvent) {
FreeKernelEvent(&((PPOWER_INIT)pObj)->pPowerReadyEvent);
}
break;
}
}
}
/***************************************************************************\
* GetRemoteProcessId
*
* Return handle to a remote process where a system thread would be created
* (currently, only for ghost thread).
*
* History:
* 20-Aug-00 MSadek Created.
\***************************************************************************/
HANDLE GetRemoteProcessId(
VOID)
{
UINT uThreadID;
PVOID pInitData;
HANDLE UniqueProcessId;
if (!CSTPop(&uThreadID, &pInitData, &UniqueProcessId, TRUE)) {
return NULL;
}
/*
* We should be here only for ghost thread.
*/
UserAssert(uThreadID == CST_GHOST);
CSTPush(uThreadID, pInitData, UniqueProcessId, TRUE);
return UniqueProcessId;
}
/***************************************************************************\
* HandleSystemThreadCreationFailure
*
* Handles the System thread creation failure
*
* History:
* 1-Oct-00 MSadek Created.
\***************************************************************************/
VOID HandleSystemThreadCreationFailure(
BOOL bRemoteThread)
{
UINT uThreadID;
PVOID pObj;
/*
* Should be called only in the context of CSRSS.
*/
if (!ISCSRSS()) {
return;
}
if (!CSTPop(&uThreadID, &pObj, NULL, bRemoteThread)) {
return;
}
if (uThreadID == CST_POWER) {
if (((PPOWER_INIT)pObj)->pPowerReadyEvent) {
KeSetEvent(((PPOWER_INIT)pObj)->pPowerReadyEvent, EVENT_INCREMENT, FALSE);
}
}
}
/***************************************************************************\
* xxxCreateSystemThreads
*
* Call the right thread routine (depending on uThreadID),
* which will wait for its own desired messages.
*
* History:
* 15-Mar-00 MHamid Created.
\***************************************************************************/
VOID xxxCreateSystemThreads(
BOOL bRemoteThread)
{
UINT uThreadID;
PVOID pObj;
/*
* Do not allow any process other than CSRSS to call this function. The
* only exception is the case of the ghost thread since we now allow it
* to launch in the context of the shell process.
*/
if (!bRemoteThread && !ISCSRSS()) {
RIPMSG0(RIP_WARNING,
"xxxCreateSystemThreads get called from a Process other than CSRSS");
return;
}
if (!CSTPop(&uThreadID, &pObj, NULL, bRemoteThread)) {
return;
}
LeaveCrit();
switch (uThreadID) {
case CST_DESKTOP:
xxxDesktopThread(pObj);
break;
case CST_RIT:
RawInputThread(pObj);
break;
case CST_GHOST:
GhostThread(pObj);
break;
case CST_POWER:
VideoPortCalloutThread(pObj);
break;
}
EnterCrit();
}
/***************************************************************************\
* xxxDesktopThread
*
* This thread owns all desktops windows on a windowstation. While waiting
* for messages, it moves the mouse cursor without entering the USER critical
* section. The RIT does the rest of the mouse input processing.
*
* History:
* 03-Dec-1993 JimA Created.
\***************************************************************************/
#define OBJECTS_COUNT 3
VOID xxxDesktopThread(
PTERMINAL pTerm)
{
KPRIORITY Priority;
NTSTATUS Status;
PTHREADINFO ptiCurrent;
PQ pqOriginal;
UNICODE_STRING strThreadName;
PKEVENT *apRITEvents;
HANDLE hevtShutDown;
PKEVENT pEvents[2];
USHORT cEvents = 1;
MSGWAITCALLBACK pfnHidChangeRoutine = NULL;
DWORD nEvents = 0;
UINT idMouseInput;
UINT idDesktopDestroy;
UINT idPumpMessages;
UserAssert(pTerm != NULL);
/*
* Set the desktop thread's priority to low realtime.
*/
#ifdef W2K_COMPAT_PRIORITY
Priority = LOW_REALTIME_PRIORITY;
#else
Priority = LOW_REALTIME_PRIORITY - 4;
#endif
ZwSetInformationThread(NtCurrentThread(),
ThreadPriority,
&Priority,
sizeof(KPRIORITY));
/*
* There are just two TERMINAL structures. One is for the
* interactive windowstation and the other is for all the
* non-interactive windowstations.
*/
if (pTerm->dwTERMF_Flags & TERMF_NOIO) {
RtlInitUnicodeString(&strThreadName, L"NOIO_DT");
} else {
RtlInitUnicodeString(&strThreadName, L"IO_DT");
}
if (!NT_SUCCESS(InitSystemThread(&strThreadName))) {
pTerm->dwTERMF_Flags |= TERMF_DTINITFAILED;
KeSetEvent(pTerm->pEventTermInit, EVENT_INCREMENT, FALSE);
RIPMSG0(RIP_ERROR, "Fail to create the desktop thread");
return;
}
ptiCurrent = PtiCurrentShared();
pTerm->ptiDesktop = ptiCurrent;
pTerm->pqDesktop = pqOriginal = ptiCurrent->pq;
(pqOriginal->cLockCount)++;
ptiCurrent->pDeskInfo = &diStatic;
/*
* Set the winsta to NULL. It will be set to the right windowstation in
* xxxCreateDesktop before pEventInputReady is set.
*/
ptiCurrent->pwinsta = NULL;
/*
* Allocate non-paged array. Include an extra entry for the thread's
* input event.
*/
apRITEvents = UserAllocPoolNonPagedNS((OBJECTS_COUNT * sizeof(PKEVENT)),
TAG_SYSTEM);
if (apRITEvents == NULL) {
pTerm->dwTERMF_Flags |= TERMF_DTINITFAILED;
KeSetEvent(pTerm->pEventTermInit, EVENT_INCREMENT, FALSE);
return;
}
idMouseInput = 0xFFFF;
idDesktopDestroy = 0xFFFF;
/*
* Reference the mouse input event. The system terminal doesn't
* wait for any mouse input.
*/
if (!(pTerm->dwTERMF_Flags & TERMF_NOIO)) {
pfnHidChangeRoutine = (MSGWAITCALLBACK)ProcessDeviceChanges;
idMouseInput = nEvents++;
UserAssert(aDeviceTemplate[DEVICE_TYPE_MOUSE].pkeHidChange);
apRITEvents[idMouseInput] = aDeviceTemplate[DEVICE_TYPE_MOUSE].pkeHidChange;
}
/*
* Create the desktop destruction event.
*/
idDesktopDestroy = nEvents++;
apRITEvents[idDesktopDestroy] = CreateKernelEvent(SynchronizationEvent, FALSE);
if (apRITEvents[idDesktopDestroy] == NULL) {
pTerm->dwTERMF_Flags |= TERMF_DTINITFAILED;
KeSetEvent(pTerm->pEventTermInit, EVENT_INCREMENT, FALSE);
UserFreePool(apRITEvents);
return;
}
pTerm->pEventDestroyDesktop = apRITEvents[idDesktopDestroy];
EnterCrit();
UserAssert(IsWinEventNotifyDeferredOK());
/*
* Set the event that tells the initialization of desktop
* thread is done.
*/
pTerm->dwTERMF_Flags |= TERMF_DTINITSUCCESS;
KeSetEvent(pTerm->pEventTermInit, EVENT_INCREMENT, FALSE);
if (gbRemoteSession) {
WCHAR szName[MAX_SESSION_PATH];
UNICODE_STRING ustrName;
OBJECT_ATTRIBUTES obja;
/*
* Open the shutdown event. This event will be signaled
* from W32WinStationTerminate.
* This is a named event opend by CSR to signal that win32k should
* go away. It's used in ntuser\server\api.c
*/
swprintf(szName,
L"\\Sessions\\%ld\\BaseNamedObjects\\EventShutDownCSRSS",
gSessionId);
RtlInitUnicodeString(&ustrName, szName);
InitializeObjectAttributes(&obja,
&ustrName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenEvent(&hevtShutDown,
EVENT_ALL_ACCESS,
&obja);
if (!NT_SUCCESS(Status)) {
pTerm->dwTERMF_Flags |= TERMF_DTINITFAILED;
if(pTerm->pEventTermInit) {
KeSetEvent(pTerm->pEventTermInit, EVENT_INCREMENT, FALSE);
}
FreeKernelEvent(&apRITEvents[idDesktopDestroy]);
UserFreePool(apRITEvents);
return;
}
ObReferenceObjectByHandle(hevtShutDown,
EVENT_ALL_ACCESS,
*ExEventObjectType,
KernelMode,
&pEvents[1],
NULL);
cEvents++;
}
/*
* Prepare to wait on input ready event.
*/
pEvents[0] = pTerm->pEventInputReady;
ObReferenceObjectByPointer(pEvents[0],
EVENT_ALL_ACCESS,
*ExEventObjectType,
KernelMode);
LeaveCrit();
Status = KeWaitForMultipleObjects(cEvents,
pEvents,
WaitAny,
WrUserRequest,
KernelMode,
FALSE,
NULL,
NULL);
EnterCrit();
ObDereferenceObject(pEvents[0]);
if (cEvents > 1) {
ObDereferenceObject(pEvents[1]);
}
if (Status == WAIT_OBJECT_0 + 1) {
pTerm->dwTERMF_Flags |= TERMF_DTINITFAILED;
if (pTerm->spwndDesktopOwner != NULL) {
xxxCleanupMotherDesktopWindow(pTerm);
}
if (pTerm->pEventTermInit) {
KeSetEvent(pTerm->pEventTermInit, EVENT_INCREMENT, FALSE);
}
FreeKernelEvent(&apRITEvents[idDesktopDestroy]);
UserFreePool(apRITEvents);
if (hevtShutDown) {
ZwClose(hevtShutDown);
}
pqOriginal->cLockCount--;
pTerm->ptiDesktop = NULL;
pTerm->pqDesktop = NULL;
LeaveCrit();
return;
}
/*
* Adjust the event ids
*/
idMouseInput += WAIT_OBJECT_0;
idDesktopDestroy += WAIT_OBJECT_0;
idPumpMessages = WAIT_OBJECT_0 + nEvents;
/*
* message loop lasts until we get a WM_QUIT message
* upon which we shall return from the function
*/
while (TRUE) {
DWORD result;
/*
* Wait for any message sent or posted to this queue, while calling
* ProcessDeviceChanges whenever the mouse change event (pkeHidChange)
* is set.
*/
result = xxxMsgWaitForMultipleObjects(nEvents,
apRITEvents,
pfnHidChangeRoutine,
NULL);
#if DBG
gDesktopsBusy++;
if (gDesktopsBusy >= 2) {
RIPMSG0(RIP_WARNING, "2 or more desktop threads busy");
}
#endif
/*
* result tells us the type of event we have:
* a message or a signalled handle
*
* if there are one or more messages in the queue ...
*/
if (result == (DWORD)idPumpMessages) {
MSG msg;
CheckCritIn();
/*
* read all of the messages in this next loop
* removing each message as we read it
*/
while (xxxPeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
/*
* Instrumentation to catch Windows Bug #210358.
*/
if (msg.message == WM_QUIT && ptiCurrent->cWindows > 1) {
FRE_RIPMSG2(RIP_ERROR, "xxxDesktopThread: WM_QUIT received when %d windows around for pti=%p",
ptiCurrent->cWindows, ptiCurrent);
}
/*
* If it's a quit message we're out of here.
*/
if (msg.message == WM_QUIT && ptiCurrent->cWindows <= 1) {
TRACE_DESKTOP(("WM_QUIT: Destroying the desktop thread. cWindows %d\n",
ptiCurrent->cWindows));
HYDRA_HINT(HH_DTQUITRECEIVED);
/*
* The window station is gone, so
*
* DON'T USE PWINSTA ANYMORE
*/
/*
* We could have received a mouse message in between the
* desktop destroy event and the WM_QUIT message in which
* case we may need to clear spwndTrack again to make sure
* that a window (gotta be the desktop) isn't locked in.
*/
Unlock(&ptiCurrent->rpdesk->spwndTrack);
/*
* If we're running on the last interactive desktop,
* then we never unlocked pdesk->pDeskInfo->spwnd.
* However, it seems to me that the system stops
* running before we make it here; otherwise, (or
* for a Hydra-like thing) we need to unlock that
* window here.....
*/
UserAssert(ptiCurrent->rpdesk != NULL &&
ptiCurrent->rpdesk->pDeskInfo != NULL);
if (ptiCurrent->rpdesk->pDeskInfo->spwnd != NULL) {
Unlock(&ptiCurrent->rpdesk->pDeskInfo->spwnd);
ptiCurrent->rpdesk->dwDTFlags |= DF_QUITUNLOCK;
}
/*
* Because there is no desktop, we need to fake a
* desktop info structure so that the IsHooked()
* macro can test a "valid" fsHooks value.
*/
ptiCurrent->pDeskInfo = &diStatic;
/*
* The desktop window is all that's left, so
* let's exit. The thread cleanup code will
* handle destruction of the window.
*/
/*
* If the thread is not using the original queue,
* destroy it.
*/
UserAssert(pqOriginal->cLockCount);
(pqOriginal->cLockCount)--;
if (ptiCurrent->pq != pqOriginal) {
zzzDestroyQueue(pqOriginal, ptiCurrent);
}
#if DBG
gDesktopsBusy--;
#endif
LeaveCrit();
/*
* Deref the events now that we're done with them.
* Also free the wait array.
*/
FreeKernelEvent(&apRITEvents[idDesktopDestroy]);
UserFreePool(apRITEvents);
pTerm->ptiDesktop = NULL;
pTerm->pqDesktop = NULL;
pTerm->dwTERMF_Flags |= TERMF_DTDESTROYED;
/*
* Terminate the thread by just returning, since we are
* now a user thread.
*/
return;
} else if (msg.message == WM_DESKTOPNOTIFY) {
switch(msg.wParam) {
case DESKTOP_RELOADWALLPAPER:
{
TL tlName;
PUNICODE_STRING pProfileUserName = CreateProfileUserName(&tlName);
xxxSetDeskWallpaper(pProfileUserName, SETWALLPAPER_METRICS);
FreeProfileUserName(pProfileUserName, &tlName);
}
break;
default:
RIPMSG1(RIP_WARNING, "WM_DESKTOPNOTIFY received with unrecognized wParam 0x%x", msg.wParam);
break;
}
continue;
}
UserAssert(msg.message != WM_QUIT);
/*
* Otherwise dispatch it.
*/
xxxDispatchMessage(&msg);
}
} else if (result == idDesktopDestroy) {
PDESKTOP *ppdesk;
PDESKTOP pdesk;
PWND pwnd;
PMENU pmenu;
TL tlpwinsta;
PWINDOWSTATION pwinsta;
TL tlpdesk;
TL tlpwnd;
PDESKTOP pdeskTemp;
HDESK hdeskTemp;
TL tlpdeskTemp;
/*
* Destroy desktops on the destruction list.
*/
for (ppdesk = &pTerm->rpdeskDestroy; *ppdesk != NULL;) {
/*
* Unlink from the list.
*/
pdesk = *ppdesk;
TRACE_DESKTOP(("Destroying desktop '%ws' %#p ...\n",
GetDesktopName(pdesk), pdesk));
UserAssert(!(pdesk->dwDTFlags & DF_DYING));
ThreadLockDesktop(ptiCurrent, pdesk, &tlpdesk, LDLT_FN_DESKTOPTHREAD_DESK);
pwinsta = pdesk->rpwinstaParent;
ThreadLockWinSta(ptiCurrent, pdesk->rpwinstaParent, &tlpwinsta);
LockDesktop(ppdesk, pdesk->rpdeskNext, LDL_TERM_DESKDESTROY1, (ULONG_PTR)pTerm);
UnlockDesktop(&pdesk->rpdeskNext, LDU_DESK_DESKNEXT, 0);
/*
* !!! If this is the current desktop, switch to another one.
*/
if (pdesk == grpdeskRitInput) {
PDESKTOP pdeskNew;
TRACE_DESKTOP(("Destroying the current active desktop\n"));
pdesk->dwDTFlags |= DF_ACTIVEONDESTROY;
if (pwinsta->dwWSF_Flags & WSF_SWITCHLOCK) {
TRACE_DESKTOP(("The windowstation is locked\n"));
/*
* this should be the interactive windowstation
*/
if (pwinsta->dwWSF_Flags & WSF_NOIO) {
FRE_RIPMSG1(RIP_ERROR, "xxxDesktopThread: grpdeskRitInput on non-IO windowstation = %p", grpdeskRitInput);
}
/*
* Switch to the disconnected desktop if the logon desktop
* is being destroyed, or there is no logon desktop, or
* if the logon desktop has already been destroyed.
*/
if (gspdeskDisconnect &&
(pdesk == grpdeskLogon ||
grpdeskLogon == NULL ||
(grpdeskLogon->dwDTFlags & DF_DESKWNDDESTROYED))) {
TRACE_DESKTOP(("disable the screen and switch to the disconnect desktop\n"));
pdesk->dwDTFlags |= DF_SKIPSWITCHDESKTOP;
RemoteDisableScreen();
goto skip;
} else {
TRACE_DESKTOP(("Switch to the logon desktop '%ws' %#p ...\n",
GetDesktopName(grpdeskLogon), grpdeskLogon));
pdeskNew = grpdeskLogon;
}
} else {
pdeskNew = pwinsta->rpdeskList;
if (pdeskNew == pdesk)
pdeskNew = pdesk->rpdeskNext;
/*
* You can hit this if you exit winlogon before
* logging in. I.E. all desktop's close so there is
* no "next" one to switch to. I'm assuming that there
* is a check for a NULL desktop in xxxSwitchDesktop().
*
* You can't switch to a NULL desktop. But this means
* there isn't any input desktop so clear it manually.
*/
if (pdeskNew == NULL) {
TRACE_DESKTOP(("NO INPUT FOR DT FROM THIS POINT ON ...\n"));
ClearWakeBit(ptiCurrent, QS_INPUT | QS_EVENT | QS_MOUSEMOVE, FALSE);
pdesk->dwDTFlags |= DF_DTNONEWDESKTOP;
}
}
TRACE_DESKTOP(("Switch to desktop '%ws' %#p\n",
GetDesktopName(pdeskNew), pdeskNew));
xxxSwitchDesktop(pwinsta, pdeskNew, 0);
}
skip:
/*
* Close the display if this desktop did not use the global
* display.
*/
if ((pdesk->pDispInfo->hDev != NULL) &&
(pdesk->pDispInfo->hDev != gpDispInfo->hDev)) {
TRACE_DESKTOP(("Destroy MDEV\n"));
DrvDestroyMDEV(pdesk->pDispInfo->pmdev);
GreFreePool(pdesk->pDispInfo->pmdev);
pdesk->pDispInfo->pmdev = NULL;
}
if (pdesk->pDispInfo != gpDispInfo) {
UserAssert(pdesk->pDispInfo->pMonitorFirst == NULL);
UserFreePool(pdesk->pDispInfo);
pdesk->pDispInfo = NULL;
}
/*
* Makes sure the IO desktop thread is running on the active destkop.
*/
if (!(pTerm->dwTERMF_Flags & TERMF_NOIO) && (ptiCurrent->rpdesk != grpdeskRitInput)) {
FRE_RIPMSG0(RIP_ERROR, "xxxDesktopThread: desktop thread not originally on grpdeskRitInput");
}
pdeskTemp = ptiCurrent->rpdesk; // save current desktop
hdeskTemp = ptiCurrent->hdesk;
ThreadLockDesktop(ptiCurrent, pdeskTemp, &tlpdeskTemp, LDLT_FN_DESKTOPTHREAD_DESKTEMP);
xxxSetThreadDesktop(NULL, pdesk);
Unlock(&pdesk->spwndForeground);
Unlock(&pdesk->spwndTray);
/*
* Destroy desktop and menu windows.
*/
Unlock(&pdesk->spwndTrack);
pdesk->dwDTFlags &= ~DF_MOUSEMOVETRK;
if (pdesk->spmenuSys != NULL) {
pmenu = pdesk->spmenuSys;
if (UnlockDesktopSysMenu(&pdesk->spmenuSys)) {
_DestroyMenu(pmenu);
}
}
if (pdesk->spmenuDialogSys != NULL) {
pmenu = pdesk->spmenuDialogSys;
if (UnlockDesktopSysMenu(&pdesk->spmenuDialogSys)) {
_DestroyMenu(pmenu);
}
}
if (pdesk->spmenuHScroll != NULL) {
pmenu = pdesk->spmenuHScroll;
if (UnlockDesktopMenu(&pdesk->spmenuHScroll)) {
_DestroyMenu(pmenu);
}
}
if (pdesk->spmenuVScroll != NULL) {
pmenu = pdesk->spmenuVScroll;
if (UnlockDesktopMenu(&pdesk->spmenuVScroll)) {
_DestroyMenu(pmenu);
}
}
/*
* If this desktop doesn't have a pDeskInfo, then something
* is wrong. All desktops should have this until the object
* is freed.
*/
UserAssert(pdesk->pDeskInfo != NULL);
if (pdesk->pDeskInfo) {
if (pdesk->pDeskInfo->spwnd == gspwndFullScreen) {
Unlock(&gspwndFullScreen);
}
if (pdesk->pDeskInfo->spwndShell) {
Unlock(&pdesk->pDeskInfo->spwndShell);
}
if (pdesk->pDeskInfo->spwndBkGnd) {
Unlock(&pdesk->pDeskInfo->spwndBkGnd);
}
if (pdesk->pDeskInfo->spwndTaskman) {
Unlock(&pdesk->pDeskInfo->spwndTaskman);
}
if (pdesk->pDeskInfo->spwndProgman) {
Unlock(&pdesk->pDeskInfo->spwndProgman);
}
}
UserAssert(!(pdesk->dwDTFlags & DF_DYING));
if (pdesk->spwndMessage != NULL) {
pwnd = pdesk->spwndMessage;
if (Unlock(&pdesk->spwndMessage)) {
xxxDestroyWindow(pwnd);
}
}
if (pdesk->spwndTooltip != NULL) {
pwnd = pdesk->spwndTooltip;
if (Unlock(&pdesk->spwndTooltip)) {
xxxDestroyWindow(pwnd);
}
UserAssert(!(pdesk->dwDTFlags & DF_TOOLTIPSHOWING));
}
UserAssert(!(pdesk->dwDTFlags & DF_DYING));
/*
* If the dying desktop is the owner of the desktop owner
* window, reassign it to the first available desktop. This
* is needed to ensure that xxxSetWindowPos will work on
* desktop windows.
*/
if (pTerm->spwndDesktopOwner != NULL &&
pTerm->spwndDesktopOwner->head.rpdesk == pdesk) {
PDESKTOP pdeskR;
/*
* Find out to what desktop the mother desktop window
* should go. Careful with the NOIO case where there
* might be several windowstations using the same
* mother desktop window
*/
if (pTerm->dwTERMF_Flags & TERMF_NOIO) {
PWINDOWSTATION pwinstaW;
pdeskR = NULL;
CheckCritIn();
if (grpWinStaList) {
pwinstaW = grpWinStaList->rpwinstaNext;
while (pwinstaW != NULL) {
if (pwinstaW->rpdeskList != NULL) {
pdeskR = pwinstaW->rpdeskList;
break;
}
pwinstaW = pwinstaW->rpwinstaNext;
}
}
} else {
pdeskR = pwinsta->rpdeskList;
}
if (pdeskR == NULL) {
TRACE_DESKTOP(("DESTROYING THE MOTHER DESKTOP WINDOW %#p\n",
pTerm->spwndDesktopOwner));
xxxCleanupMotherDesktopWindow(pTerm);
} else {
TRACE_DESKTOP(("MOVING THE MOTHER DESKTOP WINDOW %#p to pdesk %#p '%ws'\n",
pTerm->spwndDesktopOwner, pdeskR, GetDesktopName(pdeskR)));
LockDesktop(&(pTerm->spwndDesktopOwner->head.rpdesk),
pdeskR, LDL_MOTHERDESK_DESK1, (ULONG_PTR)(pTerm->spwndDesktopOwner));
}
}
if (pdesk->pDeskInfo && (pdesk->pDeskInfo->spwnd != NULL)) {
UserAssert(!(pdesk->dwDTFlags & DF_DESKWNDDESTROYED));
pwnd = pdesk->pDeskInfo->spwnd;
/*
* Hide this window without activating anyone else.
*/
if (TestWF(pwnd, WFVISIBLE)) {
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
xxxSetWindowPos(pwnd,
NULL,
0,
0,
0,
0,
SWP_HIDEWINDOW | SWP_NOACTIVATE |
SWP_NOMOVE | SWP_NOSIZE |
SWP_NOZORDER | SWP_NOREDRAW |
SWP_NOSENDCHANGING);
ThreadUnlock(&tlpwnd);
}
/*
* A lot of pwnd related code assumes that we always
* have a valid desktop window. So we call
* xxxDestroyWindow first to clean up and then we unlock
* it to free it (now or eventually). However, if we're
* destroying the last destkop, then we don't unlock the
* window since we're are forced to continue running on
* that desktop.
*/
TRACE_DESKTOP(("Destroying the desktop window\n"));
xxxDestroyWindow(pdesk->pDeskInfo->spwnd);
if (pdesk != grpdeskRitInput) {
Unlock(&pdesk->pDeskInfo->spwnd);
pdesk->dwDTFlags |= DF_NOTRITUNLOCK;
} else {
pdesk->dwDTFlags |= DF_ZOMBIE;
/*
* unlock the gspwndShouldBeForeground window
*/
if (ISTS() && gspwndShouldBeForeground != NULL) {
Unlock(&gspwndShouldBeForeground);
}
/*
* This is hit in HYDRA when the last desktop does away
*/
RIPMSG1(RIP_WARNING, "xxxDesktopThread: Running on zombie desk:%#p", pdesk);
}
pdesk->dwDTFlags |= DF_DESKWNDDESTROYED;
}
/*
* Restore the previous desktop.
*
* In NOIO sessions, if pdeskTemp is destroyed, don't bother switching
* back to it since it'll fail (and assert) latter in zzzSetDesktop
*/
if (!(pTerm->dwTERMF_Flags & TERMF_NOIO) ||
!(pdeskTemp->dwDTFlags & (DF_DESKWNDDESTROYED | DF_DYING))) {
xxxSetThreadDesktop(hdeskTemp, pdeskTemp);
}
/*
* Makes sure the IO desktop thread is running on the active destkop.
*/
if (!(pTerm->dwTERMF_Flags & TERMF_NOIO) && (ptiCurrent->rpdesk != grpdeskRitInput)) {
FRE_RIPMSG0(RIP_ERROR, "xxxDesktopThread: desktop thread not back on grpdeskRitInput");
}
ThreadUnlockDesktop(ptiCurrent, &tlpdeskTemp, LDUT_FN_DESKTOPTHREAD_DESKTEMP);
ThreadUnlockWinSta(ptiCurrent, &tlpwinsta);
ThreadUnlockDesktop(ptiCurrent, &tlpdesk, LDUT_FN_DESKTOPTHREAD_DESK);
}
/*
* Wakeup ntinput thread for exit processing
*/
TRACE_DESKTOP(("Wakeup ntinput thread for exit processing\n"));
UserAssert(gpevtDesktopDestroyed != NULL);
KeSetEvent(gpevtDesktopDestroyed, EVENT_INCREMENT, FALSE);
} else if ((NTSTATUS)result == STATUS_USER_APC) {
/*
* Instrumentation to catch Windows Bug #210358.
*/
FRE_RIPMSG1(RIP_ERROR, "xxxDesktopThread: received STATUS_USER_APC for pti=%p", ptiCurrent);
/*
* Perhaps we should repost WM_QUIT to myself?
*/
} else {
RIPMSG1(RIP_ERROR, "Desktop woke up for what? status=%08x", result);
}
#if DBG
gDesktopsBusy--;
#endif
}
}
/***************************************************************************\
* xxxRealizeDesktop
*
* 4/28/97 vadimg created
\***************************************************************************/
VOID xxxRealizeDesktop(PWND pwnd)
{
CheckLock(pwnd);
UserAssert(GETFNID(pwnd) == FNID_DESKTOP);
if (ghpalWallpaper) {
HDC hdc = _GetDC(pwnd);
xxxInternalPaintDesktop(pwnd, hdc, FALSE);
_ReleaseDC(hdc);
}
}
/***************************************************************************\
* xxxDesktopWndProc
*
* History:
* 23-Oct-1990 DarrinM Ported from Win 3.0 sources.
* 08-Aug-1996 jparsons 51725 - added fix to prevent crash on WM_SETICON
\***************************************************************************/
LRESULT xxxDesktopWndProc(
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PTHREADINFO ptiCurrent = PtiCurrent();
HDC hdcT;
PAINTSTRUCT ps;
PWINDOWPOS pwp;
CheckLock(pwnd);
UserAssert(IsWinEventNotifyDeferredOK());
VALIDATECLASSANDSIZE(pwnd, message, wParam, lParam, FNID_DESKTOP, WM_CREATE);
if (pwnd->spwndParent == NULL) {
switch (message) {
case WM_SETICON:
/*
* Cannot allow this as it will cause a callback to user mode
* from the desktop system thread.
*/
RIPMSG0(RIP_WARNING, "Discarding WM_SETICON sent to desktop.");
return 0L;
default:
break;
}
return xxxDefWindowProc(pwnd, message, wParam, lParam);
}
switch (message) {
case WM_WINDOWPOSCHANGING:
/*
* We receive this when switch desktop is called. Just to be
* consistent, set the rit desktop as this thread's desktop.
*/
pwp = (PWINDOWPOS)lParam;
if (!(pwp->flags & SWP_NOZORDER) && pwp->hwndInsertAfter == HWND_TOP) {
xxxSetThreadDesktop(NULL, grpdeskRitInput);
/*
* If some app has taken over the system-palette, we should make
* sure the system is restored. Otherwise, if this is the logon
* desktop, we might not be able to view the dialog correctly.
*/
if (GreGetSystemPaletteUse(gpDispInfo->hdcScreen) != SYSPAL_STATIC) {
GreRealizeDefaultPalette(gpDispInfo->hdcScreen, TRUE);
}
/*
* Let everyone know if the palette has changed.
*/
if (grpdeskRitInput->dwDTFlags & DTF_NEEDSPALETTECHANGED) {
xxxSendNotifyMessage(PWND_BROADCAST,
WM_PALETTECHANGED,
(WPARAM)HWq(pwnd),
0);
grpdeskRitInput->dwDTFlags &= ~DTF_NEEDSPALETTECHANGED;
}
}
break;
case WM_FULLSCREEN: {
TL tlpwndT;
ThreadLockWithPti(ptiCurrent, grpdeskRitInput->pDeskInfo->spwnd, &tlpwndT);
xxxMakeWindowForegroundWithState(grpdeskRitInput->pDeskInfo->spwnd,
GDIFULLSCREEN);
ThreadUnlock(&tlpwndT);
/*
* We have to tell the switch window to repaint if we switched
* modes
*/
if (gspwndAltTab != NULL) {
ThreadLockAlwaysWithPti(ptiCurrent, gspwndAltTab, &tlpwndT);
xxxSendMessage(gspwndAltTab, WM_FULLSCREEN, 0, 0);
ThreadUnlock(&tlpwndT);
}
break;
}
case WM_CLOSE:
/*
* Make sure nobody sends this window a WM_CLOSE and causes it to
* destroy itself.
*/
break;
case WM_SETICON:
/*
* cannot allow this as it will cause a callback to user mode from the
* desktop system thread.
*/
RIPMSG0(RIP_WARNING, "WM_SETICON sent to desktop window was discarded.");
break;
case WM_CREATE: {
TL tlName;
PUNICODE_STRING pProfileUserName = CreateProfileUserName(&tlName);
/*
* Is there a desktop pattern, or bitmap name in WIN.INI?
*/
xxxSetDeskPattern(pProfileUserName, (LPWSTR)-1, TRUE);
FreeProfileUserName(pProfileUserName, &tlName);
/*
* Initialize the system colors before we show the desktop window.
*/
xxxSendNotifyMessage(pwnd, WM_SYSCOLORCHANGE, 0, 0L);
hdcT = _GetDC(pwnd);
xxxInternalPaintDesktop(pwnd, hdcT, FALSE); // use "normal" HDC so SelectPalette() will work
_ReleaseDC(hdcT);
/*
* Save process and thread ids.
*/
xxxSetWindowLong(pwnd,
0,
HandleToUlong(PsGetCurrentProcessId()),
FALSE);
xxxSetWindowLong(pwnd,
4,
HandleToUlong(PsGetCurrentThreadId()),
FALSE);
break;
}
case WM_PALETTECHANGED:
if (HWq(pwnd) == (HWND)wParam) {
break;
}
// FALL THROUGH
case WM_QUERYNEWPALETTE:
xxxRealizeDesktop(pwnd);
break;
case WM_SYSCOLORCHANGE:
/*
* We do the redrawing if someone has changed the sys-colors from
* another desktop and we need to redraw. This is appearent with
* the MATROX card which requires OGL applications to take over
* the entire sys-colors for drawing. When switching desktops, we
* never broadcast the WM_SYSCOLORCHANGE event to tell us to redraw
* This is only a DAYTONA related fix, and should be removed once
* we move the SYSMETS to a per-desktop state.
*
* 05-03-95 : ChrisWil.
*/
xxxRedrawWindow(pwnd,
NULL,
NULL,
RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE);
break;
case WM_ERASEBKGND:
hdcT = (HDC)wParam;
xxxInternalPaintDesktop(pwnd, hdcT, TRUE);
return TRUE;
case WM_PAINT:
xxxBeginPaint(pwnd, (LPPAINTSTRUCT)&ps);
xxxEndPaint(pwnd, (LPPAINTSTRUCT)&ps);
break;
#ifdef HUNGAPP_GHOSTING
case WM_HUNGTHREAD:
{
PWND pwndT = RevalidateHwnd((HWND)lParam);
if (pwndT != NULL && FHungApp(GETPTI(pwndT), CMSHUNGAPPTIMEOUT)) {
TL tlpwnd;
pwndT = GetTopLevelWindow(pwndT);
ThreadLockAlways(pwndT, &tlpwnd);
xxxCreateGhost(pwndT);
ThreadUnlock(&tlpwnd);
}
break;
}
case WM_SCANGHOST:
if (gpEventScanGhosts) {
KeSetEvent(gpEventScanGhosts, EVENT_INCREMENT, FALSE);
}
break;
#endif
case WM_CREATETRAILTIMER:
if (GETMOUSETRAILS() && !gtmridMouseTrails) {
gtmridMouseTrails = InternalSetTimer(NULL,
gtmridMouseTrails,
1000 / MOUSE_TRAILS_FREQ,
HideMouseTrails,
TMRF_RIT);
}
break;
case WM_LBUTTONDBLCLK:
message = WM_SYSCOMMAND;
wParam = SC_TASKLIST;
/*
*** FALL THRU **
*/
default:
return xxxDefWindowProc(pwnd, message, wParam, lParam);
}
return 0L;
}
/***************************************************************************\
* SetDeskPattern
*
* NOTE: the lpszPattern parameter is new for Win 3.1.
*
* History:
* 23-Oct-1990 DarrinM Created stub.
* 22-Apr-1991 DarrinM Ported code from Win 3.1 sources.
\***************************************************************************/
BOOL xxxSetDeskPattern(PUNICODE_STRING pProfileUserName,
LPWSTR lpszPattern,
BOOL fCreation)
{
LPWSTR p;
int i;
UINT val;
WCHAR wszNone[20];
WCHAR wchValue[MAX_PATH];
WORD rgBits[CXYDESKPATTERN];
HBRUSH hBrushTemp;
CheckCritIn();
/*
* Get rid of the old bitmap (if any).
*/
if (ghbmDesktop != NULL) {
GreDeleteObject(ghbmDesktop);
ghbmDesktop = NULL;
}
/*
* Check if a pattern is passed via lpszPattern.
*/
if (lpszPattern != (LPWSTR)LongToPtr(-1)) {
/*
* Yes! Then use that pattern;
*/
p = lpszPattern;
goto GotThePattern;
}
/*
* Else, pickup the pattern selected in WIN.INI.
* Get the "DeskPattern" string from WIN.INI's [Desktop] section.
*/
if (!FastGetProfileStringFromIDW(pProfileUserName,
PMAP_DESKTOP,
STR_DESKPATTERN,
L"",
wchValue,
sizeof(wchValue)/sizeof(WCHAR),
0)) {
return FALSE;
}
ServerLoadString(hModuleWin,
STR_NONE,
wszNone,
sizeof(wszNone)/sizeof(WCHAR));
p = wchValue;
GotThePattern:
/*
* Was a Desk Pattern selected?
*/
if (*p == L'\0' || _wcsicmp(p, wszNone) == 0) {
hBrushTemp = GreCreateSolidBrush(SYSRGB(DESKTOP));
if (hBrushTemp != NULL) {
if (SYSHBR(DESKTOP)) {
GreMarkDeletableBrush(SYSHBR(DESKTOP));
GreDeleteObject(SYSHBR(DESKTOP));
}
GreMarkUndeletableBrush(hBrushTemp);
SYSHBR(DESKTOP) = hBrushTemp;
}
GreSetBrushOwnerPublic(hBrushTemp);
goto SDPExit;
}
/*
* Get eight groups of numbers seprated by non-numeric characters.
*/
for (i = 0; i < CXYDESKPATTERN; i++) {
val = 0;
/*
* Skip over any non-numeric characters, check for null EVERY time.
*/
while (*p && !(*p >= L'0' && *p <= L'9')) {
p++;
}
/*
* Get the next series of digits.
*/
while (*p >= L'0' && *p <= L'9') {
val = val * (UINT)10 + (UINT)(*p++ - L'0');
}
rgBits[i] = (WORD)val;
}
ghbmDesktop = GreCreateBitmap(CXYDESKPATTERN,
CXYDESKPATTERN,
1,
1,
(LPBYTE)rgBits);
if (ghbmDesktop == NULL) {
return FALSE;
}
GreSetBitmapOwner(ghbmDesktop, OBJECT_OWNER_PUBLIC);
RecolorDeskPattern();
SDPExit:
if (!fCreation) {
/*
* Notify everyone that the colors have changed.
*/
xxxSendNotifyMessage(PWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0L);
/*
* Update the entire screen. If this is creation, don't update: the
* screen hasn't drawn, and also there are some things that aren't
* initialized yet.
*/
xxxRedrawScreen();
}
return TRUE;
}
/***************************************************************************\
* RecolorDeskPattern
*
* Remakes the desktop pattern (if it exists) so that it uses the new system
* colors.
*
* History:
* 22-Apr-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID RecolorDeskPattern(
VOID)
{
HBITMAP hbmOldDesk;
HBITMAP hbmOldMem;
HBITMAP hbmMem;
HBRUSH hBrushTemp;
if (ghbmDesktop == NULL) {
return;
}
/*
* Redo the desktop pattern in the new colors.
*/
if (hbmOldDesk = GreSelectBitmap(ghdcMem, ghbmDesktop)) {
if (!SYSMET(SAMEDISPLAYFORMAT)) {
BYTE bmi[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 2];
PBITMAPINFO pbmi = (PBITMAPINFO)bmi;
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = CXYDESKPATTERN;
pbmi->bmiHeader.biHeight = CXYDESKPATTERN;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 1;
pbmi->bmiHeader.biCompression = BI_RGB;
pbmi->bmiHeader.biSizeImage = 0;
pbmi->bmiHeader.biXPelsPerMeter = 0;
pbmi->bmiHeader.biYPelsPerMeter = 0;
pbmi->bmiHeader.biClrUsed = 2;
pbmi->bmiHeader.biClrImportant = 2;
pbmi->bmiColors[0].rgbBlue = (BYTE)((SYSRGB(DESKTOP) >> 16) & 0xff);
pbmi->bmiColors[0].rgbGreen = (BYTE)((SYSRGB(DESKTOP) >> 8) & 0xff);
pbmi->bmiColors[0].rgbRed = (BYTE)((SYSRGB(DESKTOP)) & 0xff);
pbmi->bmiColors[1].rgbBlue = (BYTE)((SYSRGB(WINDOWTEXT) >> 16) & 0xff);
pbmi->bmiColors[1].rgbGreen = (BYTE)((SYSRGB(WINDOWTEXT) >> 8) & 0xff);
pbmi->bmiColors[1].rgbRed = (BYTE)((SYSRGB(WINDOWTEXT)) & 0xff);
hbmMem = GreCreateDIBitmapReal(HDCBITS(),
0,
NULL,
pbmi,
DIB_RGB_COLORS,
sizeof(bmi),
0,
NULL,
0,
NULL,
0,
0,
NULL);
} else {
hbmMem = GreCreateCompatibleBitmap(HDCBITS(),
CXYDESKPATTERN,
CXYDESKPATTERN);
}
if (hbmMem) {
if (hbmOldMem = GreSelectBitmap(ghdcMem2, hbmMem)) {
GreSetTextColor(ghdcMem2, SYSRGB(DESKTOP));
GreSetBkColor(ghdcMem2, SYSRGB(WINDOWTEXT));
GreBitBlt(ghdcMem2,
0,
0,
CXYDESKPATTERN,
CXYDESKPATTERN,
ghdcMem,
0,
0,
SRCCOPY,
0);
if (hBrushTemp = GreCreatePatternBrush(hbmMem)) {
if (SYSHBR(DESKTOP) != NULL) {
GreMarkDeletableBrush(SYSHBR(DESKTOP));
GreDeleteObject(SYSHBR(DESKTOP));
}
GreMarkUndeletableBrush(hBrushTemp);
SYSHBR(DESKTOP) = hBrushTemp;
}
GreSetBrushOwnerPublic(hBrushTemp);
GreSelectBitmap(ghdcMem2, hbmOldMem);
}
GreDeleteObject(hbmMem);
}
GreSelectBitmap(ghdcMem, hbmOldDesk);
}
}
/***************************************************************************\
* GetDesktopHeapSize()
*
* Calculate the desktop heap size
*
* History:
* 27-Nov-2001 Msadek Created it.
\***************************************************************************/
ULONG GetDesktopHeapSize(
USHORT usFlags)
{
ULONG ulHeapSize;
switch (usFlags) {
case DHS_LOGON:
ulHeapSize = USR_LOGONSECT_SIZE;
#ifdef _WIN64
/*
* Increase heap size 50% for Win64 to allow for larger structures.
*/
ulHeapSize = (ulHeapSize * 3) / 2;
#endif
break;
case DHS_DISCONNECT:
ulHeapSize = USR_DISCONNECTSECT_SIZE;
#ifdef _WIN64
/*
* Increase heap size 50% for Win64 to allow for larger structures.
*/
ulHeapSize = (ulHeapSize * 3) / 2;
#endif
break;
case DHS_NOIO:
ulHeapSize = gdwNOIOSectionSize;
break;
default:
ulHeapSize = gdwDesktopSectionSize;
}
return ulHeapSize * 1024;
}
/***************************************************************************\
* xxxCreateDesktop (API)
*
* Create a new desktop object.
*
* History:
* 16-Jan-1991 JimA Created scaffold code.
* 11-Feb-1991 JimA Added access checks.
\***************************************************************************/
NTSTATUS xxxCreateDesktop2(
PWINDOWSTATION pwinsta,
PACCESS_STATE pAccessState,
KPROCESSOR_MODE AccessMode,
PUNICODE_STRING pstrName,
PDESKTOP_CONTEXT Context,
PVOID *pObject)
{
LUID luidCaller;
OBJECT_ATTRIBUTES ObjectAttributes;
PEPROCESS Process;
PDESKTOP pdesk;
PDESKTOPINFO pdi;
ULONG ulHeapSize;
USHORT usSizeFlags = 0;
NTSTATUS Status;
BOOLEAN MemoryAllocated;
PSECURITY_DESCRIPTOR SecurityDescriptor;
CheckCritIn();
/*
* If this is a desktop creation, make sure that the windowstation
* grants create access.
*/
if (!ObCheckCreateObjectAccess(
pwinsta,
WINSTA_CREATEDESKTOP,
pAccessState,
pstrName,
TRUE,
AccessMode,
&Status)) {
return Status;
}
/*
* Fail if the windowstation is locked.
*/
Process = PsGetCurrentProcess();
if (pwinsta->dwWSF_Flags & WSF_OPENLOCK &&
PsGetProcessId(Process) != gpidLogon) {
/*
* If logoff is occuring and the caller does not
* belong to the session that is ending, allow the
* open to proceed.
*/
Status = GetProcessLuid(NULL, &luidCaller);
if (!NT_SUCCESS(Status) ||
!(pwinsta->dwWSF_Flags & WSF_SHUTDOWN) ||
RtlEqualLuid(&luidCaller, &pwinsta->luidEndSession)) {
return STATUS_DEVICE_BUSY;
}
}
/*
* If a devmode has been specified, we also must be able
* to switch desktops.
*/
if (Context->lpDevMode != NULL && (pwinsta->dwWSF_Flags & WSF_OPENLOCK) &&
PsGetProcessId(Process) != gpidLogon) {
return STATUS_DEVICE_BUSY;
}
/*
* Allocate the new object
*/
InitializeObjectAttributes(&ObjectAttributes, pstrName, 0, NULL, NULL);
Status = ObCreateObject(
KernelMode,
*ExDesktopObjectType,
&ObjectAttributes,
UserMode,
NULL,
sizeof(DESKTOP),
0,
0,
&pdesk);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"xxxCreateDesktop2: ObCreateObject failed with Status 0x%x",
Status);
return Status;
}
RtlZeroMemory(pdesk, sizeof(DESKTOP));
/*
* Store the session id of the session who created the desktop
*/
pdesk->dwSessionId = gSessionId;
/*
* Fetch the parents security descriptor
*/
Status = ObGetObjectSecurity(pwinsta,
&SecurityDescriptor,
&MemoryAllocated);
if (!NT_SUCCESS(Status)) {
goto Error;
}
/*
* Create security descriptor.
*/
Status = ObAssignSecurity(pAccessState,
SecurityDescriptor,
pdesk,
*ExDesktopObjectType);
ObReleaseObjectSecurity(SecurityDescriptor, MemoryAllocated);
if (!NT_SUCCESS(Status)) {
goto Error;
}
/*
* Set up desktop heap. The first desktop (logon desktop) uses a
* small heap (128).
*/
if (!(pwinsta->dwWSF_Flags & WSF_NOIO) && (pwinsta->rpdeskList == NULL)) {
usSizeFlags = DHS_LOGON;
} else {
if (pwinsta->dwWSF_Flags & WSF_NOIO) {
usSizeFlags = DHS_NOIO;
} else {
/*
* The disconnected desktop should be small also.
*/
if (gspdeskDisconnect == NULL) {
usSizeFlags = DHS_DISCONNECT;
}
}
}
ulHeapSize = GetDesktopHeapSize(usSizeFlags);
/*
* Create the desktop heap.
*/
pdesk->hsectionDesktop = CreateDesktopHeap(&pdesk->pheapDesktop, ulHeapSize);
if (pdesk->hsectionDesktop == NULL) {
RIPMSGF1(RIP_WARNING,
"CreateDesktopHeap failed for pdesk 0x%p",
pdesk);
/*
* If we fail to create a desktop due to being out of desktop heap,
* write an entry to the event log.
*/
if (TEST_SRVIF(SRVIF_LOGDESKTOPHEAPFAILURE)) {
CLEAR_SRVIF(SRVIF_LOGDESKTOPHEAPFAILURE);
UserLogError(NULL, 0, WARNING_DESKTOP_CREATION_FAILED);
}
goto ErrorOutOfMemory;
}
if (pwinsta->rpdeskList == NULL || (pwinsta->dwWSF_Flags & WSF_NOIO)) {
/*
* The first desktop or invisible desktops must also use the default
* settings. This is because specifying the devmode causes a desktop
* switch, which must be avoided in this case.
*/
Context->lpDevMode = NULL;
}
/*
* Allocate desktopinfo
*/
pdi = (PDESKTOPINFO)DesktopAlloc(pdesk, sizeof(DESKTOPINFO), DTAG_DESKTOPINFO);
if (pdi == NULL) {
RIPMSG0(RIP_WARNING, "xxxCreateDesktop: failed DeskInfo Alloc");
goto ErrorOutOfMemory;
}
/*
* Initialize everything.
*/
pdesk->pDeskInfo = pdi;
InitializeListHead(&pdesk->PtiList);
/*
* If a DEVMODE or another device name is passed in, then use that
* information. Otherwise use the default information (gpDispInfo).
*/
if (Context->lpDevMode) {
BOOL bDisabled = FALSE;
PMDEV pmdev = NULL;
LONG ChangeStat = GRE_DISP_CHANGE_FAILED;
/*
* Allocate a display-info for this device.
*/
pdesk->pDispInfo = (PDISPLAYINFO)UserAllocPoolZInit(
sizeof(DISPLAYINFO), TAG_DISPLAYINFO);
if (!pdesk->pDispInfo) {
RIPMSGF1(RIP_WARNING,
"Failed to allocate pDispInfo for pdesk 0x%p",
pdesk);
goto ErrorOutOfMemory;
}
if ((bDisabled = SafeDisableMDEV()) == TRUE) {
ChangeStat = DrvChangeDisplaySettings(Context->pstrDevice,
NULL,
Context->lpDevMode,
LongToPtr(gdwDesktopId),
UserMode,
FALSE,
TRUE,
NULL,
&pmdev,
GRE_DEFAULT,
FALSE);
}
if (ChangeStat != GRE_DISP_CHANGE_SUCCESSFUL) {
if (bDisabled) {
SafeEnableMDEV();
}
//
// If there is a failure, then repaint the whole screen.
//
RIPMSG1(RIP_WARNING, "xxxCreateDesktop2 callback for pdesk %#p !",
pdesk);
xxxUserResetDisplayDevice();
Status = STATUS_UNSUCCESSFUL;
goto Error;
}
pdesk->pDispInfo->hDev = pmdev->hdevParent;
pdesk->pDispInfo->pmdev = pmdev;
pdesk->dwDesktopId = gdwDesktopId++;
CopyRect(&pdesk->pDispInfo->rcScreen, &gpDispInfo->rcScreen);
pdesk->pDispInfo->dmLogPixels = gpDispInfo->dmLogPixels;
pdesk->pDispInfo->pMonitorFirst = NULL;
pdesk->pDispInfo->pMonitorPrimary = NULL;
} else {
pdesk->pDispInfo = gpDispInfo;
pdesk->dwDesktopId = GW_DESKTOP_ID;
}
/*
* Heap is HEAP_ZERO_MEMORY, so we should be zero-initialized already.
*/
UserAssert(pdi->pvwplShellHook == NULL);
pdi->pvDesktopBase = Win32HeapGetHandle(pdesk->pheapDesktop);
pdi->pvDesktopLimit = (PBYTE)pdi->pvDesktopBase + ulHeapSize;
/*
* Reference the parent windowstation
*/
LockWinSta(&(pdesk->rpwinstaParent), pwinsta);
/*
* Link the desktop into the windowstation list
*/
if (pwinsta->rpdeskList == NULL) {
if (!(pwinsta->dwWSF_Flags & WSF_NOIO)) {
LockDesktop(&grpdeskLogon, pdesk, LDL_DESKLOGON, 0);
}
/*
* Make the first desktop the "owner" of the top desktop window. This
* is needed to ensure that xxxSetWindowPos will work on desktop
* windows.
*/
LockDesktop(&(pwinsta->pTerm->spwndDesktopOwner->head.rpdesk),
pdesk, LDL_MOTHERDESK_DESK2, (ULONG_PTR)(pwinsta->pTerm->spwndDesktopOwner));
}
LockDesktop(&pdesk->rpdeskNext, pwinsta->rpdeskList, LDL_DESK_DESKNEXT1, (ULONG_PTR)pwinsta);
LockDesktop(&pwinsta->rpdeskList, pdesk, LDL_WINSTA_DESKLIST1, (ULONG_PTR)pwinsta);
/*
* Mask off invalid access bits
*/
if (pAccessState->RemainingDesiredAccess & MAXIMUM_ALLOWED) {
pAccessState->RemainingDesiredAccess &= ~MAXIMUM_ALLOWED;
pAccessState->RemainingDesiredAccess |= GENERIC_ALL;
}
RtlMapGenericMask( &pAccessState->RemainingDesiredAccess, (PGENERIC_MAPPING)&DesktopMapping);
pAccessState->RemainingDesiredAccess &=
(DesktopMapping.GenericAll | ACCESS_SYSTEM_SECURITY);
*pObject = pdesk;
/*
* Add the desktop to the global list of desktops in this win32k.
*/
DbgTrackAddDesktop(pdesk);
return STATUS_SUCCESS;
ErrorOutOfMemory:
Status = STATUS_NO_MEMORY;
// fall-through
Error:
LogDesktop(pdesk, LD_DEREF_FN_2CREATEDESKTOP, FALSE, 0);
ObDereferenceObject(pdesk);
UserAssert(!NT_SUCCESS(Status));
return Status;
}
BOOL xxxCreateDisconnectDesktop(
HWINSTA hwinsta,
PWINDOWSTATION pwinsta)
{
UNICODE_STRING strDesktop;
OBJECT_ATTRIBUTES oa;
HDESK hdeskDisconnect;
HRGN hrgn;
NTSTATUS Status;
/*
* Create the empty clipping region for the disconnect desktop.
*/
if ((hrgn = CreateEmptyRgnPublic()) == NULL) {
RIPMSG0(RIP_WARNING, "Creation of empty region for Disconnect Desktop failed ");
return FALSE;
}
/*
* If not created yet, then create the Disconnected desktop
* (used when WinStation is disconnected), and lock the desktop
* and desktop window to ensure they never get deleted.
*/
RtlInitUnicodeString(&strDesktop, L"Disconnect");
InitializeObjectAttributes(&oa, &strDesktop,
OBJ_OPENIF | OBJ_CASE_INSENSITIVE, hwinsta, NULL);
hdeskDisconnect = xxxCreateDesktop(&oa,
KernelMode,
NULL,
NULL,
0,
MAXIMUM_ALLOWED);
if (hdeskDisconnect == NULL) {
RIPMSG0(RIP_WARNING, "Could not create Disconnect desktop");
GreDeleteObject(hrgn);
return FALSE;
}
/*
* Set the disconnect desktop security.
* Keep around an extra reference to the disconnect desktop from
* the CSR so it will stay around even if winlogon exits.
*/
Status = SetDisconnectDesktopSecurity(hdeskDisconnect);
if (NT_SUCCESS(Status)) {
Status = ObReferenceObjectByHandle(hdeskDisconnect,
0,
NULL,
KernelMode,
&gspdeskDisconnect,
NULL);
}
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING, "Disconnect Desktop reference failed 0x%x", Status);
GreDeleteObject(hrgn);
xxxCloseDesktop(hdeskDisconnect, KernelMode);
gspdeskDisconnect = NULL;
return FALSE;
}
LogDesktop(gspdeskDisconnect, LDL_DESKDISCONNECT, TRUE, 0);
/*
* Set the region of the desktop window to be (0, 0, 0, 0) so
* that there is no hittesting going on the 'disconnect' desktop
* But prior to session the null region, we need to null the pointer
* to the existing shared region so that it doesn't get deleted.
*/
UserAssert(gspdeskDisconnect->pDeskInfo != NULL);
gspdeskDisconnect->pDeskInfo->spwnd->hrgnClip = hrgn;
KeAttachProcess(PsGetProcessPcb(gpepCSRSS));
Status = ObOpenObjectByPointer(
gspdeskDisconnect,
0,
NULL,
EVENT_ALL_ACCESS,
NULL,
KernelMode,
&ghDisconnectDesk);
if (NT_SUCCESS(Status)) {
Status = ObOpenObjectByPointer(
pwinsta,
0,
NULL,
EVENT_ALL_ACCESS,
NULL,
KernelMode,
&ghDisconnectWinSta);
}
KeDetachProcess();
if (!NT_SUCCESS(Status)) {
RIPMSG0(RIP_WARNING, "Could not create Disconnect desktop");
GreDeleteObject(hrgn);
gspdeskDisconnect->pDeskInfo->spwnd->hrgnClip = NULL;
if (ghDisconnectDesk != NULL) {
CloseProtectedHandle(ghDisconnectDesk);
ghDisconnectDesk = NULL;
}
xxxCloseDesktop(hdeskDisconnect, KernelMode);
return FALSE;
}
/*
* Don't want to do alot of paints if we disconnected before this.
*/
if (!gbConnected) {
RIPMSG0(RIP_WARNING,
"RemoteDisconnect was issued during CreateDesktop(\"Winlogon\"...");
}
return TRUE;
}
VOID CleanupDirtyDesktops(
VOID)
{
PWINDOWSTATION pwinsta;
PDESKTOP* ppdesk;
CheckCritIn();
for (pwinsta = grpWinStaList; pwinsta != NULL; pwinsta = pwinsta->rpwinstaNext) {
ppdesk = &pwinsta->rpdeskList;
while (*ppdesk != NULL) {
if (!((*ppdesk)->dwDTFlags & DF_DESKCREATED)) {
RIPMSG1(RIP_WARNING, "Desktop %#p in a dirty state", *ppdesk);
if (grpdeskLogon == *ppdesk) {
UnlockDesktop(&grpdeskLogon, LDU_DESKLOGON, 0);
}
if (pwinsta->pTerm->spwndDesktopOwner &&
pwinsta->pTerm->spwndDesktopOwner->head.rpdesk == *ppdesk) {
UnlockDesktop(&(pwinsta->pTerm->spwndDesktopOwner->head.rpdesk),
LDU_MOTHERDESK_DESK, (ULONG_PTR)(pwinsta->pTerm->spwndDesktopOwner));
}
LockDesktop(ppdesk, (*ppdesk)->rpdeskNext, LDL_WINSTA_DESKLIST1, (ULONG_PTR)pwinsta);
} else {
ppdesk = &(*ppdesk)->rpdeskNext;
}
}
}
}
VOID W32FreeDesktop(
PVOID pObj)
{
FRE_RIPMSG1(RIP_WARNING,
"W32FreeDesktop: obj 0x%p is not freed in the regular code path.",
pObj);
ObDereferenceObject(pObj);
}
HDESK xxxCreateDesktop(
POBJECT_ATTRIBUTES ccxObjectAttributes,
KPROCESSOR_MODE ProbeMode,
PUNICODE_STRING ccxpstrDevice,
LPDEVMODE ccxlpdevmode,
DWORD dwFlags,
DWORD dwDesiredAccess)
{
HWINSTA hwinsta;
HDESK hdesk;
DESKTOP_CONTEXT Context;
PDESKTOP pdesk;
PDESKTOPINFO pdi;
PWINDOWSTATION pwinsta;
PDESKTOP pdeskTemp;
HDESK hdeskTemp;
PWND pwndDesktop = NULL;
PWND pwndMessage = NULL;
PWND pwndTooltip = NULL;
TL tlpwnd;
PTHREADINFO ptiCurrent = PtiCurrent();
BOOL fWasNull;
BOOL bSuccess;
PPROCESSINFO ppi;
PPROCESSINFO ppiSave;
PTERMINAL pTerm;
NTSTATUS Status;
DWORD dwDisableHooks;
TL tlW32Desktop;
#if DBG
/*
* Too many jumps in this function to use BEGIN/ENDATOMICHCECK
*/
DWORD dwCritSecUseSave = gdwCritSecUseCount;
#endif
CheckCritIn();
UserAssert(IsWinEventNotifyDeferredOK());
/*
* Capture directory handle and check for create access.
*/
try {
hwinsta = ccxObjectAttributes->RootDirectory;
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
return NULL;
}
if (hwinsta != NULL) {
Status = ObReferenceObjectByHandle(hwinsta,
WINSTA_CREATEDESKTOP,
*ExWindowStationObjectType,
ProbeMode,
&pwinsta,
NULL);
if (NT_SUCCESS(Status)) {
DWORD dwSessionId = pwinsta->dwSessionId;
ObDereferenceObject(pwinsta);
if (dwSessionId != gSessionId) {
/*
* Windows Bug: 418526
* Avoid creating a desktop that belongs to the other
* session.
*/
RIPMSGF1(RIP_WARNING,
"winsta 0x%p belongs to other session",
pwinsta);
return NULL;
}
} else {
RIPNTERR0(Status, RIP_VERBOSE, "ObReferenceObjectByHandle Failed");
return NULL;
}
}
/*
* Set up creation context
*/
Context.lpDevMode = ccxlpdevmode;
Context.pstrDevice = ccxpstrDevice;
Context.dwFlags = dwFlags;
Context.dwCallerSessionId = gSessionId;
/*
* Create the desktop -- the object manager uses try blocks.
*/
Status = ObOpenObjectByName(ccxObjectAttributes,
*ExDesktopObjectType,
ProbeMode,
NULL,
dwDesiredAccess,
&Context,
&hdesk);
if (!NT_SUCCESS(Status)) {
RIPNTERR1(Status,
RIP_WARNING,
"xxxCreateDesktop: ObOpenObjectByName failed with Status 0x%x",
Status);
/*
* Cleanup desktop objects that were created in xxxCreateDesktop2
* but later on the Ob manager failed the creation for other
* reasons (ex: no quota).
*/
CleanupDirtyDesktops();
return NULL;
}
/*
* If the desktop already exists, we're done. This will only happen
* if OBJ_OPENIF was specified.
*/
if (Status == STATUS_OBJECT_NAME_EXISTS) {
SetHandleFlag(hdesk, HF_PROTECTED, TRUE);
RIPMSG0(RIP_WARNING, "xxxCreateDesktop: Object name exists");
return hdesk;
}
/*
* Reference the desktop to finish initialization
*/
Status = ObReferenceObjectByHandle(
hdesk,
0,
*ExDesktopObjectType,
KernelMode,
&pdesk,
NULL);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_VERBOSE, "");
CloseProtectedHandle(hdesk);
return NULL;
}
/*
* Usermode marking such that any hdesk associated with this desktop will
* always be referenced in this mode.
*/
pdesk->dwDTFlags |= DF_DESKCREATED | ((ProbeMode == UserMode) ? DF_USERMODE : 0);
LogDesktop(pdesk, LD_REF_FN_CREATEDESKTOP, TRUE, (ULONG_PTR)PtiCurrent());
pwinsta = pdesk->rpwinstaParent;
pTerm = pwinsta->pTerm;
pdi = pdesk->pDeskInfo;
pdi->ppiShellProcess = NULL;
ppi = PpiCurrent();
if (gpepCSRSS != NULL) {
WIN32_OPENMETHOD_PARAMETERS OpenParams;
/*
* Map the desktop into CSRSS to ensure that the hard error handler
* can get access.
*/
OpenParams.OpenReason = ObOpenHandle;
OpenParams.Process = gpepCSRSS;
OpenParams.Object = pdesk;
OpenParams.GrantedAccess = 0;
OpenParams.HandleCount = 1;
if (!NT_SUCCESS(MapDesktop(&OpenParams))) {
/*
* Desktop mapping failed.
*/
CloseProtectedHandle(hdesk);
LogDesktop(pdesk, LD_DEREF_FN_CREATEDESKTOP2, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdesk);
RIPNTERR0(STATUS_ACCESS_DENIED, RIP_WARNING, "Desktop mapping failed (2)");
return NULL;
}
UserAssert(GetDesktopView(PpiFromProcess(gpepCSRSS), pdesk) != NULL);
}
/*
* Set hook flags
*/
SetHandleFlag(hdesk, HF_DESKTOPHOOK, dwFlags & DF_ALLOWOTHERACCOUNTHOOK);
/*
* Set up to create the desktop window.
*/
fWasNull = (ptiCurrent->ppi->rpdeskStartup == NULL);
pdeskTemp = ptiCurrent->rpdesk; // save current desktop
hdeskTemp = ptiCurrent->hdesk;
/*
* Switch ppi values so window will be created using the
* system's desktop window class.
*/
ppiSave = ptiCurrent->ppi;
ptiCurrent->ppi = pTerm->ptiDesktop->ppi;
/*
* Lock pdesk: with bogus TS protocol, the session
* may be killed in the middle of the initialization.
*/
PushW32ThreadLock(pdesk, &tlW32Desktop, W32FreeDesktop);
DeferWinEventNotify();
BeginAtomicCheck();
if (zzzSetDesktop(ptiCurrent, pdesk, hdesk) == FALSE) {
goto Error;
}
/*
* Create the desktop window
*/
/*
* HACK HACK HACK!!! (adams) In order to create the desktop window
* with the correct desktop, we set the desktop of the current thread
* to the new desktop. But in so doing we allow hooks on the current
* thread to also hook this new desktop. This is bad, because we don't
* want the desktop window to be hooked while it is created. So we
* temporarily disable hooks of the current thread and its desktop,
* and reenable them after switching back to the original desktop.
*/
dwDisableHooks = ptiCurrent->TIF_flags & TIF_DISABLEHOOKS;
ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
pwndDesktop = xxxNVCreateWindowEx(
(DWORD)0,
(PLARGE_STRING)DESKTOPCLASS,
NULL,
(WS_POPUP | WS_CLIPCHILDREN),
pdesk->pDispInfo->rcScreen.left,
pdesk->pDispInfo->rcScreen.top,
pdesk->pDispInfo->rcScreen.right - pdesk->pDispInfo->rcScreen.left,
pdesk->pDispInfo->rcScreen.bottom - pdesk->pDispInfo->rcScreen.top,
NULL,
NULL,
hModuleWin,
NULL,
VER31);
if (pwndDesktop == NULL) {
RIPMSGF1(RIP_WARNING,
"Failed to create the desktop window for pdesk 0x%p",
pdesk);
goto Error;
}
/*
* NOTE: In order for the message window to be created without
* the desktop as it's owner, it needs to be created before
* setting pdi->spwnd to the desktop window. This is a complete
* hack and should be fixed.
*/
pwndMessage = xxxNVCreateWindowEx(
0,
(PLARGE_STRING)gatomMessage,
NULL,
(WS_POPUP | WS_CLIPCHILDREN),
0,
0,
100,
100,
NULL,
NULL,
hModuleWin,
NULL,
VER31);
if (pwndMessage == NULL) {
RIPMSGF0(RIP_WARNING, "Failed to create the message window");
goto Error;
}
/*
* NOTE: Remember what window class this window belongs to.
* Since the message window does not have its own window proc
* (they use xxxDefWindowProc) we have to do it here.
*/
pwndMessage->fnid = FNID_MESSAGEWND;
UserAssert(pdi->spwnd == NULL);
Lock(&(pdi->spwnd), pwndDesktop);
SetFullScreen(pwndDesktop, GDIFULLSCREEN);
/*
* Set this windows to the fullscreen window if we don't have one yet.
*
* Don't set gspwndFullScreen if gfGdiEnabled has been cleared (we may
* be in the middle of a disconnect).
*/
if (!(pwinsta->dwWSF_Flags & WSF_NOIO)) {
UserAssert(gfGdiEnabled == TRUE);
if (gspwndFullScreen == NULL) {
Lock(&(gspwndFullScreen), pwndDesktop);
}
}
/*
* NT Bug 388747: Link the message window to the mother desktop window
* so that it properly has a parent. We will do this before we link the
* desktop window just so the initial message window appears after the
* initial desktop window (a minor optimization, but not necessary).
*/
Lock(&pwndMessage->spwndParent, pTerm->spwndDesktopOwner);
LinkWindow(pwndMessage, NULL, pTerm->spwndDesktopOwner);
Lock(&pdesk->spwndMessage, pwndMessage);
Unlock(&pwndMessage->spwndOwner);
/*
* Link it as a child but don't use WS_CHILD style
*/
LinkWindow(pwndDesktop, NULL, pTerm->spwndDesktopOwner);
Lock(&pwndDesktop->spwndParent, pTerm->spwndDesktopOwner);
Unlock(&pwndDesktop->spwndOwner);
/*
* Make it regional if it's display configuration is regional.
*/
if (!pdesk->pDispInfo->fDesktopIsRect) {
pwndDesktop->hrgnClip = pdesk->pDispInfo->hrgnScreen;
}
/*
* Create shared menu window and tooltip window.
*/
ThreadLock(pdesk->spwndMessage, &tlpwnd);
/*
* Create the tooltip window only for desktops in interactive
* windowstations.
*/
if (!(pwinsta->dwWSF_Flags & WSF_NOIO)) {
pwndTooltip = xxxNVCreateWindowEx(
WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
(PLARGE_STRING)TOOLTIPCLASS,
NULL,
WS_POPUP | WS_BORDER,
0,
0,
100,
100,
pdesk->spwndMessage,
NULL,
hModuleWin,
NULL,
VER31);
if (pwndTooltip == NULL) {
ThreadUnlock(&tlpwnd);
RIPMSGF0(RIP_WARNING, "Failed to create the tooltip window");
goto Error;
}
Lock(&pdesk->spwndTooltip, pwndTooltip);
}
ThreadUnlock(&tlpwnd);
HMChangeOwnerThread(pdi->spwnd, pTerm->ptiDesktop);
HMChangeOwnerThread(pwndMessage, pTerm->ptiDesktop);
if (!(pwinsta->dwWSF_Flags & WSF_NOIO)) {
HMChangeOwnerThread(pwndTooltip, pTerm->ptiDesktop);
}
/*
* Restore caller's ppi
*/
PtiCurrent()->ppi = ppiSave;
/*
* HACK HACK HACK (adams): Renable hooks.
*/
UserAssert(ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
ptiCurrent->TIF_flags = (ptiCurrent->TIF_flags & ~TIF_DISABLEHOOKS) | dwDisableHooks;
/*
* Restore the previous desktop
*/
if (zzzSetDesktop(ptiCurrent, pdeskTemp, hdeskTemp) == FALSE) {
goto Error;
}
EndAtomicCheck();
UserAssert(dwCritSecUseSave == gdwCritSecUseCount);
zzzEndDeferWinEventNotify();
/*
* If this is the first desktop, let the worker threads run now
* that there is someplace to send input to. Reassign the event
* to handle desktop destruction.
*/
if (pTerm->pEventInputReady != NULL) {
/*
* Set the windowstation for RIT and desktop thread
* so when EventInputReady is signaled the RIT and the desktop
* will have a windowstation.
*/
if (!(pTerm->dwTERMF_Flags & TERMF_NOIO)) {
gptiRit->pwinsta = pwinsta;
} else {
/*
* let the desktop thread of the system terminal have
* a rpdesk.
*/
if (zzzSetDesktop(pTerm->ptiDesktop, pdesk, NULL) == FALSE) {
goto Error;
}
}
pTerm->ptiDesktop->pwinsta = pwinsta;
KeSetEvent(pTerm->pEventInputReady, EVENT_INCREMENT, FALSE);
if (!(pTerm->dwTERMF_Flags & TERMF_NOIO)) {
LeaveCrit();
while (grpdeskRitInput == NULL) {
UserSleep(20);
RIPMSG0(RIP_WARNING, "Waiting for grpdeskRitInput to be set ...");
}
EnterCrit();
}
ObDereferenceObject(pTerm->pEventInputReady);
pTerm->pEventInputReady = NULL;
}
/*
* HACK HACK:
* LATER
*
* If we have a devmode passed in, then switch desktops ...
*/
if (ccxlpdevmode) {
TRACE_INIT(("xxxCreateDesktop: about to call switch desktop\n"));
bSuccess = xxxSwitchDesktop(pwinsta, pdesk, SDF_CREATENEW);
UserAssertMsg1(bSuccess,
"Failed to switch desktop 0x%p on create", pdesk);
} else if (pTerm == &gTermIO) {
UserAssert(grpdeskRitInput != NULL);
/*
* Force the window to the bottom of the z-order if there
* is an active desktop so any drawing done on the desktop
* window will not be seen. This will also allow
* IsWindowVisible to work for apps on invisible
* desktops.
*/
ThreadLockWithPti(ptiCurrent, pwndDesktop, &tlpwnd);
xxxSetWindowPos(pwndDesktop, PWND_BOTTOM, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
SWP_NOREDRAW | SWP_NOSIZE | SWP_NOSENDCHANGING);
ThreadUnlock(&tlpwnd);
}
/*
* If it was null when we came in, make it null going out, or else
* we'll have the wrong desktop selected into this.
*/
if (fWasNull)
UnlockDesktop(&ptiCurrent->ppi->rpdeskStartup,
LDU_PPI_DESKSTARTUP1, (ULONG_PTR)(ptiCurrent->ppi));
/*
* Create the disconnect desktop for the console session too.
*/
if (gspdeskDisconnect == NULL && pdesk == grpdeskLogon) {
UserAssert(hdesk != NULL);
/*
* Create the 'disconnect' desktop
*/
if (!xxxCreateDisconnectDesktop(hwinsta, pwinsta)) {
RIPMSG0(RIP_WARNING, "Failed to create the 'disconnect' desktop");
LogDesktop(pdesk, LD_DEREF_FN_CREATEDESKTOP3, FALSE, (ULONG_PTR)PtiCurrent());
PopW32ThreadLock(&tlW32Desktop);
ObDereferenceObject(pdesk);
xxxCloseDesktop(hdesk, KernelMode);
return NULL;
}
/*
* Signal that the disconnect desktop got created.
*/
KeSetEvent(gpEventDiconnectDesktop, EVENT_INCREMENT, FALSE);
HYDRA_HINT(HH_DISCONNECTDESKTOP);
}
Cleanup:
LogDesktop(pdesk, LD_DEREF_FN_CREATEDESKTOP3, FALSE, (ULONG_PTR)PtiCurrent());
PopW32ThreadLock(&tlW32Desktop);
ObDereferenceObject(pdesk);
TRACE_INIT(("xxxCreateDesktop: Leaving\n"));
if (hdesk != NULL) {
SetHandleFlag(hdesk, HF_PROTECTED, TRUE);
}
return hdesk;
Error:
EndAtomicCheck();
UserAssert(dwCritSecUseSave == gdwCritSecUseCount);
if (pwndTooltip != NULL) {
xxxDestroyWindow(pwndTooltip);
Unlock(&pdesk->spwndTooltip);
}
if (pwndMessage != NULL) {
xxxDestroyWindow(pwndMessage);
Unlock(&pdesk->spwndMessage);
}
if (pwndDesktop != NULL) {
xxxDestroyWindow(pwndDesktop);
Unlock(&pdi->spwnd);
Unlock(&gspwndFullScreen);
}
/*
* Restore caller's ppi
*/
PtiCurrent()->ppi = ppiSave;
UserAssert(ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
ptiCurrent->TIF_flags = (ptiCurrent->TIF_flags & ~TIF_DISABLEHOOKS) | dwDisableHooks;
zzzSetDesktop(ptiCurrent, pdeskTemp, hdeskTemp);
CloseProtectedHandle(hdesk);
hdesk = NULL;
zzzEndDeferWinEventNotify();
/*
* If it was null when we came in, make it null going out, or else
* we'll have the wrong desktop selected into this.
*/
if (fWasNull) {
UnlockDesktop(&ptiCurrent->ppi->rpdeskStartup,
LDU_PPI_DESKSTARTUP1,
(ULONG_PTR)ptiCurrent->ppi);
}
goto Cleanup;
}
/***************************************************************************\
* ParseDesktop
*
* Parse a desktop path.
*
* History:
* 14-Jun-1995 JimA Created.
\***************************************************************************/
NTSTATUS ParseDesktop(
PVOID pContainerObject,
POBJECT_TYPE pObjectType,
PACCESS_STATE pAccessState,
KPROCESSOR_MODE AccessMode,
ULONG Attributes,
PUNICODE_STRING pstrCompleteName,
PUNICODE_STRING pstrRemainingName,
PVOID Context,
PSECURITY_QUALITY_OF_SERVICE pqos,
PVOID *pObject)
{
PWINDOWSTATION pwinsta = pContainerObject;
PDESKTOP pdesk;
PUNICODE_STRING pstrName;
NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
*pObject = NULL;
if (Context && ((PDESKTOP_CONTEXT)Context)->dwCallerSessionId != gSessionId) {
/*
* Windows Bug: 418526:
* If it's a creation request from the other session,
* we have to bail out ASAP.
*/
RIPMSGF1(RIP_WARNING,
"Rejecting desktop creation attempt from other session (%d)",
((PDESKTOP_CONTEXT)Context)->dwCallerSessionId);
return STATUS_INVALID_PARAMETER;
}
BEGIN_REENTERCRIT();
UserAssert(OBJECT_TO_OBJECT_HEADER(pContainerObject)->Type == *ExWindowStationObjectType);
UserAssert(pObjectType == *ExDesktopObjectType);
/*
* See if the desktop exists
*/
for (pdesk = pwinsta->rpdeskList; pdesk != NULL; pdesk = pdesk->rpdeskNext) {
pstrName = POBJECT_NAME(pdesk);
if (pstrName && RtlEqualUnicodeString(pstrRemainingName, pstrName,
(BOOLEAN)((Attributes & OBJ_CASE_INSENSITIVE) != 0))) {
if (Context != NULL) {
if (!(Attributes & OBJ_OPENIF)) {
/*
* We are attempting to create a desktop and one
* already exists.
*/
Status = STATUS_OBJECT_NAME_COLLISION;
goto Exit;
} else {
Status = STATUS_OBJECT_NAME_EXISTS;
}
} else {
Status = STATUS_SUCCESS;
}
ObReferenceObject(pdesk);
*pObject = pdesk;
goto Exit;
}
}
/*
* Handle creation request
*/
if (Context != NULL) {
Status = xxxCreateDesktop2(pContainerObject,
pAccessState,
AccessMode,
pstrRemainingName,
Context,
pObject);
}
Exit:
END_REENTERCRIT();
return Status;
UNREFERENCED_PARAMETER(pObjectType);
UNREFERENCED_PARAMETER(pstrCompleteName);
UNREFERENCED_PARAMETER(pqos);
}
/***************************************************************************\
* DestroyDesktop
*
* Called upon last close of a desktop to remove the desktop from the
* desktop list and free all desktop resources.
*
* History:
* 08-Dec-1993 JimA Created.
\***************************************************************************/
BOOL DestroyDesktop(
PDESKTOP pdesk)
{
PWINDOWSTATION pwinsta = pdesk->rpwinstaParent;
PTERMINAL pTerm;
PDESKTOP *ppdesk;
if (pdesk->dwDTFlags & DF_DESTROYED) {
RIPMSG1(RIP_WARNING, "DestroyDesktop: Already destroyed:%#p", pdesk);
return FALSE;
}
/*
* Unlink the desktop, if it has not yet been unlinked.
*/
if (pwinsta != NULL) {
ppdesk = &pwinsta->rpdeskList;
while (*ppdesk != NULL && *ppdesk != pdesk) {
ppdesk = &((*ppdesk)->rpdeskNext);
}
if (*ppdesk != NULL) {
/*
* remove desktop from the list
*/
LockDesktop(ppdesk, pdesk->rpdeskNext, LDL_WINSTA_DESKLIST2, (ULONG_PTR)pwinsta);
UnlockDesktop(&pdesk->rpdeskNext, LDU_DESK_DESKNEXT, (ULONG_PTR)pwinsta);
}
}
/*
* Link it into the destruction list and signal the desktop thread.
*/
pTerm = pwinsta->pTerm;
LockDesktop(&pdesk->rpdeskNext, pTerm->rpdeskDestroy, LDL_DESK_DESKNEXT2, 0);
LockDesktop(&pTerm->rpdeskDestroy, pdesk, LDL_TERM_DESKDESTROY2, (ULONG_PTR)pTerm);
KeSetEvent(pTerm->pEventDestroyDesktop, EVENT_INCREMENT, FALSE);
pdesk->dwDTFlags |= DF_DESTROYED;
TRACE_DESKTOP(("pdesk %#p '%ws' marked as destroyed\n", pdesk, GetDesktopName(pdesk)));
return TRUE;
}
/***************************************************************************\
* FreeDesktop
*
* Called to free desktop object and section when last lock is released.
*
* History:
* 08-Dec-1993 JimA Created.
\***************************************************************************/
NTSTATUS FreeDesktop(
PKWIN32_DELETEMETHOD_PARAMETERS pDeleteParams)
{
PDESKTOP pdesk = (PDESKTOP)pDeleteParams->Object;
NTSTATUS Status = STATUS_SUCCESS;
BEGIN_REENTERCRIT();
UserAssert(OBJECT_TO_OBJECT_HEADER(pDeleteParams->Object)->Type == *ExDesktopObjectType);
#ifdef LOGDESKTOPLOCKS
if (pdesk->pLog != NULL) {
/*
* By the time we get here the lock count for lock/unlock
* tracking code should be 0
*/
if (pdesk->nLockCount != 0) {
RIPMSG3(RIP_WARNING,
"FreeDesktop pdesk %#p, pLog %#p, nLockCount %d should be 0",
pdesk, pdesk->pLog, pdesk->nLockCount);
}
UserFreePool(pdesk->pLog);
pdesk->pLog = NULL;
}
#endif
#if DBG
if (pdesk->pDeskInfo && (pdesk->pDeskInfo->spwnd != NULL)) {
/*
* Assert if the desktop has a desktop window but the flag
* that says the window is destroyed is not set.
*/
UserAssert(pdesk->dwDTFlags & DF_DESKWNDDESTROYED);
}
#endif
/*
* Mark the desktop as dying. Make sure we aren't recursing.
*/
UserAssert(!(pdesk->dwDTFlags & DF_DYING));
pdesk->dwDTFlags |= DF_DYING;
#ifdef DEBUG_DESK
ValidateDesktop(pdesk);
#endif
/*
* If the desktop is mapped into CSR, unmap it. Note the
* handle count values passed in will cause the desktop
* to be unmapped and skip the desktop destruction tests.
*/
FreeView(gpepCSRSS, pdesk);
if (pdesk->pheapDesktop != NULL) {
PVOID hheap = Win32HeapGetHandle(pdesk->pheapDesktop);
Win32HeapDestroy(pdesk->pheapDesktop);
Status = Win32UnmapViewInSessionSpace(hheap);
UserAssert(NT_SUCCESS(Status));
Win32DestroySection(pdesk->hsectionDesktop);
}
UnlockWinSta(&pdesk->rpwinstaParent);
DbgTrackRemoveDesktop(pdesk);
END_REENTERCRIT();
return Status;
}
/***************************************************************************\
* CreateDesktopHeap
*
* Create a new desktop heap
*
* History:
* 27-Jul-1992 JimA Created.
\***************************************************************************/
HANDLE CreateDesktopHeap(
PWIN32HEAP* ppheapRet,
ULONG ulHeapSize)
{
HANDLE hsection;
LARGE_INTEGER SectionSize;
SIZE_T ulViewSize;
NTSTATUS Status;
PWIN32HEAP pheap;
PVOID pHeapBase;
/*
* Create desktop heap section and map it into the kernel
*/
SectionSize.QuadPart = ulHeapSize;
Status = Win32CreateSection(&hsection,
SECTION_ALL_ACCESS,
NULL,
&SectionSize,
PAGE_EXECUTE_READWRITE,
SEC_RESERVE,
NULL,
NULL,
TAG_SECTION_DESKTOP);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_WARNING, "Can't create section for desktop heap.");
return NULL;
}
ulViewSize = ulHeapSize;
pHeapBase = NULL;
Status = Win32MapViewInSessionSpace(hsection, &pHeapBase, &ulViewSize);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status,
RIP_WARNING,
"Can't map section for desktop heap into system space.");
goto Error;
}
/*
* Create desktop heap.
*/
if ((pheap = UserCreateHeap(
hsection,
0,
pHeapBase,
ulHeapSize,
UserCommitDesktopMemory)) == NULL) {
RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_WARNING, "Can't create Desktop heap.");
Win32UnmapViewInSessionSpace(pHeapBase);
Error:
Win32DestroySection(hsection);
*ppheapRet = NULL;
return NULL;
}
UserAssert(Win32HeapGetHandle(pheap) == pHeapBase);
*ppheapRet = pheap;
return hsection;
}
/***************************************************************************\
* GetDesktopView
*
* Determines if a desktop has already been mapped into a process.
*
* History:
* 10-Apr-1995 JimA Created.
\***************************************************************************/
PDESKTOPVIEW GetDesktopView(
PPROCESSINFO ppi,
PDESKTOP pdesk)
{
PDESKTOPVIEW pdv;
if (ppi->Process != gpepCSRSS && pdesk == NULL) {
RIPMSG1(RIP_WARNING, "Process 0x%p isn't CSRSS but pdesk is NULL in GetDesktopView", ppi);
}
for (pdv = ppi->pdvList; pdv != NULL; pdv = pdv->pdvNext) {
if (pdv->pdesk == pdesk) {
break;
}
}
return pdv;
}
/***************************************************************************\
* _MapDesktopObject
*
* Maps a desktop object into the client's address space
*
* History:
* 11-Apr-1995 JimA Created.
\***************************************************************************/
PVOID _MapDesktopObject(
HANDLE h)
{
PDESKOBJHEAD pobj;
PDESKTOPVIEW pdv;
/*
* Validate the handle
*/
pobj = HMValidateHandle(h, TYPE_GENERIC);
if (pobj == NULL) {
return NULL;
}
UserAssert(HMObjectFlags(pobj) & OCF_DESKTOPHEAP);
/*
* Locate the client's view of the desktop. Realistically, this should
* never fail for valid objects.
*/
pdv = GetDesktopView(PpiCurrent(), pobj->rpdesk);
if (pdv == NULL) {
RIPMSG1(RIP_WARNING, "MapDesktopObject: cannot map handle 0x%p", h);
return NULL;
}
UserAssert(pdv->ulClientDelta != 0);
return (PVOID)((PBYTE)pobj - pdv->ulClientDelta);
}
NTSTATUS DesktopOpenProcedure(
PKWIN32_OPENMETHOD_PARAMETERS pOpenParams)
{
PDESKTOP pdesk = (PDESKTOP)pOpenParams->Object;
/*
* Make sure we're not opening a handle for a destroy desktop. If this happens,
* we probably want to fail it.
*/
if (pdesk->dwDTFlags & DF_DESTROYED) {
RIPMSG1(RIP_WARNING,
"DesktopOpenProcedure: Opening a handle to destroyed desktop 0x%p",
pdesk);
return STATUS_ACCESS_DENIED;
}
/*
* Allow desktop open cross session only if no special rights granted.
*/
if (pOpenParams->GrantedAccess & SPECIFIC_RIGHTS_ALL) {
if (PsGetProcessSessionId(pOpenParams->Process) != pdesk->dwSessionId) {
return STATUS_ACCESS_DENIED;
}
}
return STATUS_SUCCESS;
}
/***************************************************************************\
* MapDesktop
*
* Attempts to map a desktop heap into a process.
*
* History:
* 20-Oct-1994 JimA Created.
\***************************************************************************/
NTSTATUS MapDesktop(
PKWIN32_OPENMETHOD_PARAMETERS pOpenParams)
{
PPROCESSINFO ppi;
PDESKTOP pdesk = (PDESKTOP)pOpenParams->Object;
SIZE_T ulViewSize;
LARGE_INTEGER liOffset;
PDESKTOPVIEW pdvNew;
PBYTE pheap;
HANDLE hsectionDesktop;
PBYTE pClientBase;
NTSTATUS Status = STATUS_SUCCESS;
UserAssert(OBJECT_TO_OBJECT_HEADER(pOpenParams->Object)->Type == *ExDesktopObjectType);
TAGMSG2(DBGTAG_Callout,
"Mapping desktop 0x%p into process 0x%p",
pdesk,
pOpenParams->Process);
BEGIN_REENTERCRIT();
/*
* Ignore handle inheritance because MmMapViewOfSection cannot be called
* during process creation.
*/
if (pOpenParams->OpenReason == ObInheritHandle) {
goto Exit;
}
/*
* If there is no ppi, we can't map the desktop.
*/
ppi = PpiFromProcess(pOpenParams->Process);
if (ppi == NULL) {
goto Exit;
}
/*
* Do this here, before we (potentially) attach to the process, so
* we know we're in the right context.
*/
pheap = Win32HeapGetHandle(pdesk->pheapDesktop);
hsectionDesktop = pdesk->hsectionDesktop;
/*
* We should not map a desktop cross session.
*/
if (PsGetProcessSessionId(pOpenParams->Process) != pdesk->dwSessionId) {
FRE_RIPMSG2(RIP_ERROR, "MapDesktop: Trying to map desktop %p into"
" process %p in a differnt session. How we ended up here?",
pdesk, pOpenParams->Process);
Status = STATUS_ACCESS_DENIED;
goto Exit;
}
/*
* If the desktop has already been mapped we're done.
*/
if (GetDesktopView(ppi, pdesk) != NULL) {
goto Exit;
}
/*
* Allocate a view of the desktop.
*/
pdvNew = UserAllocPoolWithQuota(sizeof(*pdvNew), TAG_PROCESSINFO);
if (pdvNew == NULL) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
/*
* Read/write access has been granted. Map the desktop memory into
* the client process.
*/
ulViewSize = 0;
liOffset.QuadPart = 0;
pClientBase = NULL;
Status = MmMapViewOfSection(hsectionDesktop,
pOpenParams->Process,
&pClientBase,
0,
0,
&liOffset,
&ulViewSize,
ViewUnmap,
SEC_NO_CHANGE,
PAGE_EXECUTE_READ);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"MapDesktop - failed to map to client process (Status == 0x%x).",
Status);
RIPNTERR0(Status, RIP_VERBOSE, "");
UserFreePool(pdvNew);
Status = STATUS_NO_MEMORY;
goto Exit;
}
/*
* Link the view into the ppi.
*/
pdvNew->pdesk = pdesk;
pdvNew->ulClientDelta = (ULONG_PTR)(pheap - pClientBase);
pdvNew->pdvNext = ppi->pdvList;
ppi->pdvList = pdvNew;
Exit:
END_REENTERCRIT();
return Status;
}
VOID FreeView(
PEPROCESS Process,
PDESKTOP pdesk)
{
PPROCESSINFO ppi;
NTSTATUS Status;
PDESKTOPVIEW pdv, *ppdv;
/*
* Bug 277291: gpepCSRSS can be NULL when FreeView is
* called from FreeDesktop.
*/
if (Process == NULL) {
return;
}
/*
* If there is no ppi, then the process is gone and nothing needs to be
* unmapped.
*/
ppi = PpiFromProcess(Process);
if (ppi != NULL) {
KAPC_STATE ApcState;
BOOL bAttached;
PBYTE pHeap;
/*
* Before potentially attaching to this process, we need to store
* away this pointer. However, we don't know that this desktop is
* even mapped into this process (previously, Win32HeapGetHandle()
* would only have been called if GetDesktopView returned a non-NULL
* value, meaning the desktop is mapped into the process). Thus,
* we need to explicitly check the desktop's heap ptr before we
* access it.
*/
if (pdesk->pheapDesktop) {
pHeap = (PBYTE)Win32HeapGetHandle(pdesk->pheapDesktop);
} else {
pHeap = NULL;
}
/*
* We should not have any mapped views cross session.
*/
if (PsGetProcessSessionId(Process) != pdesk->dwSessionId) {
KeStackAttachProcess(PsGetProcessPcb(Process), &ApcState);
bAttached = TRUE;
} else {
bAttached = FALSE;
}
pdv = GetDesktopView(ppi, pdesk);
/*
* Because mapping cannot be done when a handle is inherited, there
* may not be a view of the desktop. Only unmap if there is a view.
*/
if (pdv != NULL) {
UserAssert(pHeap != NULL);
if (PsGetProcessSessionId(Process) != pdesk->dwSessionId) {
FRE_RIPMSG2(RIP_ERROR, "FreeView: Trying to free desktop "
"%p view into process %p in differnt session. "
"How we ended up here?",
pdesk, Process);
}
Status = MmUnmapViewOfSection(Process,
pHeap - pdv->ulClientDelta);
UserAssert(NT_SUCCESS(Status) || Status == STATUS_PROCESS_IS_TERMINATING);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING, "FreeView unmap status = 0x%x", Status);
}
/*
* Unlink and delete the view.
*/
for (ppdv = &ppi->pdvList; *ppdv && *ppdv != pdv;
ppdv = &(*ppdv)->pdvNext) {
/* do nothing */;
}
UserAssert(*ppdv);
*ppdv = pdv->pdvNext;
UserFreePool(pdv);
}
/*
* No thread in this process should be on this desktop.
*/
DbgCheckForThreadsOnDesktop(ppi, pdesk);
if (bAttached) {
KeUnstackDetachProcess(&ApcState);
}
}
}
NTSTATUS UnmapDesktop(
PKWIN32_CLOSEMETHOD_PARAMETERS pCloseParams)
{
PDESKTOP pdesk = (PDESKTOP)pCloseParams->Object;
BEGIN_REENTERCRIT();
UserAssert(OBJECT_TO_OBJECT_HEADER(pCloseParams->Object)->Type == *ExDesktopObjectType);
TAGMSG4(DBGTAG_Callout,
"Unmapping desktop 0x%p from process 0x%p (0x%x <-> 0x%x)",
pdesk,
pCloseParams->Process,
PsGetProcessSessionId(pCloseParams->Process),
pdesk->dwSessionId);
/*
* Update cSystemHandles with the correct information.
*/
pCloseParams->SystemHandleCount = (ULONG)(OBJECT_TO_OBJECT_HEADER(pCloseParams->Object)->HandleCount) + 1;
/*
* Only unmap the desktop if this is the last process handle and
* the process is not CSR.
*/
if (pCloseParams->ProcessHandleCount == 1 && pCloseParams->Process != gpepCSRSS) {
FreeView(pCloseParams->Process, pdesk);
}
if (pCloseParams->SystemHandleCount > 2) {
goto Exit;
}
if (pCloseParams->SystemHandleCount == 2 && pdesk->dwConsoleThreadId != 0) {
/*
* If a console thread exists and we're down to two handles, it means
* that the last application handle to the desktop is being closed.
* Terminate the console thread so the desktop can be freed.
*/
TerminateConsole(pdesk);
} else if (pCloseParams->SystemHandleCount == 1) {
/*
* If this is the last handle to this desktop in the system,
* destroy the desktop.
*/
/*
* No pti should be linked to this desktop.
*/
if ((&pdesk->PtiList != pdesk->PtiList.Flink)
|| (&pdesk->PtiList != pdesk->PtiList.Blink)) {
RIPMSG1(RIP_WARNING, "UnmapDesktop: PtiList not Empty. pdesk:%#p", pdesk);
}
DestroyDesktop(pdesk);
}
Exit:
END_REENTERCRIT();
return STATUS_SUCCESS;
}
/***************************************************************************\
* OkayToCloseDesktop
*
* We can only close desktop handles if they're not in use.
*
* History:
* 08-Feb-1999 JerrySh Created.
\***************************************************************************/
NTSTATUS OkayToCloseDesktop(
PKWIN32_OKAYTOCLOSEMETHOD_PARAMETERS pOkCloseParams)
{
PDESKTOP pdesk = (PDESKTOP)pOkCloseParams->Object;
UserAssert(OBJECT_TO_OBJECT_HEADER(pOkCloseParams->Object)->Type == *ExDesktopObjectType);
/*
* Kernel mode code can close anything.
*/
if (pOkCloseParams->PreviousMode == KernelMode) {
return STATUS_SUCCESS;
/*
* Do not allow a user mode process to close a kernel handle of ours.
* It shouldn't. In addition, if this happens cross-session, we will try to
* attach to the system processes and will bugcheck since the seesion
* address space is not mapped into it. Same for the session manager
* process. See bug# 759533.
*/
} else if (PsGetProcessSessionIdEx(pOkCloseParams->Process) == -1) {
return STATUS_ACCESS_DENIED;
}
/*
* We can't close the desktop if we're still initializing it.
*/
if (!(pdesk->dwDTFlags & DF_DESKCREATED)) {
RIPMSG1(RIP_WARNING, "Trying to close desktop %#p during initialization", pdesk);
return STATUS_UNSUCCESSFUL;
}
/*
* We can't close a desktop that's being used.
*/
if (CheckHandleInUse(pOkCloseParams->Handle) || CheckHandleFlag(pOkCloseParams->Process, pdesk->dwSessionId, pOkCloseParams->Handle, HF_PROTECTED)) {
RIPMSG1(RIP_WARNING, "Trying to close desktop %#p while still in use", pdesk);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
/***************************************************************************\
* xxxUserResetDisplayDevice
*
* Called to reset the display device after a switch to another device.
* Used when opening a new device, or when switching back to an old desktop
*
* History:
* 31-May-1994 AndreVa Created.
\***************************************************************************/
VOID xxxUserResetDisplayDevice(
VOID)
{
/*
* Handle early system initialization gracefully.
*/
if (grpdeskRitInput != NULL) {
TL tlpwnd;
gpqCursor = NULL;
/*
* Note that we want to clip the cursor here *before* redrawing the
* desktop window. Otherwise, when we callback apps might encounter
* a cursor position that doesn't make sense.
*/
zzzInternalSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y);
SetPointer(TRUE);
UserAssert(grpdeskRitInput != NULL);
ThreadLock(grpdeskRitInput->pDeskInfo->spwnd, &tlpwnd);
xxxRedrawWindow(grpdeskRitInput->pDeskInfo->spwnd,
NULL,
NULL,
RDW_INVALIDATE | RDW_ERASE | RDW_ERASENOW |
RDW_ALLCHILDREN);
ThreadUnlock(&tlpwnd);
}
}
/***************************************************************************\
* OpenDesktopCompletion
*
* Verifies that a given desktop has successfully opened.
*
* History:
* 03-Oct-1995 JimA Created.
\***************************************************************************/
BOOL OpenDesktopCompletion(
PDESKTOP pdesk,
HDESK hdesk,
DWORD dwFlags,
BOOL* pbShutDown)
{
PPROCESSINFO ppi = PpiCurrent();
PWINDOWSTATION pwinsta;
/*
* Fail if the windowstation is locked.
*/
pwinsta = pdesk->rpwinstaParent;
if (pwinsta->dwWSF_Flags & WSF_OPENLOCK &&
PsGetProcessId(ppi->Process) != gpidLogon) {
LUID luidCaller;
NTSTATUS Status;
/*
* If logoff is occuring and the caller does not
* belong to the session that is ending, allow the
* open to proceed.
*/
Status = GetProcessLuid(NULL, &luidCaller);
if (!NT_SUCCESS(Status) ||
(pwinsta->dwWSF_Flags & WSF_REALSHUTDOWN) ||
RtlEqualLuid(&luidCaller, &pwinsta->luidEndSession)) {
RIPERR0(ERROR_BUSY, RIP_WARNING, "OpenDesktopCompletion failed");
/*
* Set the shut down flag.
*/
*pbShutDown = TRUE;
return FALSE;
}
}
SetHandleFlag(hdesk, HF_DESKTOPHOOK, dwFlags & DF_ALLOWOTHERACCOUNTHOOK);
return TRUE;
}
/***************************************************************************\
* _OpenDesktop (API)
*
* Open a desktop object.
*
* History:
* 16-Jan-1991 JimA Created scaffold code.
* 20-Apr-2001 Mohamed Removed xxx prefix since the function doesn't leave
* the Critical Section.
\***************************************************************************/
HDESK _OpenDesktop(
POBJECT_ATTRIBUTES ccxObjA,
KPROCESSOR_MODE AccessMode,
DWORD dwFlags,
DWORD dwDesiredAccess,
BOOL* pbShutDown)
{
HDESK hdesk;
PDESKTOP pdesk;
NTSTATUS Status;
/*
* Require read/write access
*/
dwDesiredAccess |= DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS;
/*
* Open the desktop -- Ob routines capture Obj attributes.
*/
Status = ObOpenObjectByName(
ccxObjA,
*ExDesktopObjectType,
AccessMode,
NULL,
dwDesiredAccess,
NULL,
&hdesk);
if (!NT_SUCCESS(Status)) {
RIPNTERR1(Status, RIP_VERBOSE, "_OpenDesktop: ObOpenObjectByName failed with Status: 0x%x.", Status);
return NULL;
}
/*
* Reference the desktop
*/
Status = ObReferenceObjectByHandle(
hdesk,
0,
*ExDesktopObjectType,
AccessMode,
&pdesk,
NULL);
if (!NT_SUCCESS(Status)) {
RIPNTERR1(Status, RIP_VERBOSE, "_OpenDesktop: ObReferenceObjectByHandle failed with Status: 0x%x.", Status);
Error:
CloseProtectedHandle(hdesk);
return NULL;
}
if (pdesk->dwSessionId != gSessionId) {
RIPNTERR1(STATUS_INVALID_HANDLE, RIP_WARNING,
"_OpenDesktop pdesk %#p belongs to a different session",
pdesk);
ObDereferenceObject(pdesk);
goto Error;
}
LogDesktop(pdesk, LD_REF_FN_OPENDESKTOP, TRUE, (ULONG_PTR)PtiCurrent());
/*
* Complete the desktop open
*/
if (!OpenDesktopCompletion(pdesk, hdesk, dwFlags, pbShutDown)) {
CloseProtectedHandle(hdesk);
hdesk = NULL;
}
LogDesktop(pdesk, LD_DEREF_FN_OPENDESKTOP, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdesk);
if (hdesk != NULL) {
SetHandleFlag(hdesk, HF_PROTECTED, TRUE);
}
return hdesk;
}
/***************************************************************************\
* xxxSwitchDesktop (API)
*
* Switch input focus to another desktop and bring it to the top of the
* desktops
*
* dwFlags:
* SDF_CREATENEW is set when a new desktop has been created on the device, and
* when we do not want to send another enable\disable
*
* SDF_SLOVERRIDE is set when we want to ignore WSF_SWITCHLOCK being set on
* the desktop's winsta.
*
* History:
* 16-Jan-1991 JimA Created scaffold code.
* 11-Oct-2000 JasonSch Added SDF_SLOVERRIDE flag.
\***************************************************************************/
BOOL xxxSwitchDesktop(
PWINDOWSTATION pwinsta,
PDESKTOP pdesk,
DWORD dwFlags)
{
PETHREAD Thread;
PWND pwndSetForeground;
TL tlpwndChild;
TL tlpwnd;
TL tlhdesk;
PQ pq;
BOOL bUpdateCursor = FALSE;
PLIST_ENTRY pHead, pEntry;
PTHREADINFO pti;
PTHREADINFO ptiCurrent = PtiCurrent();
PTERMINAL pTerm;
NTSTATUS Status;
HDESK hdesk;
BOOL bRet = TRUE;
CheckCritIn();
UserAssert(IsWinEventNotifyDeferredOK());
if (pdesk == NULL) {
return FALSE;
}
if (pdesk == grpdeskRitInput) {
return TRUE;
}
if (pdesk->dwDTFlags & DF_DESTROYED) {
RIPMSG1(RIP_ERROR, "xxxSwitchDesktop: destroyed:%#p", pdesk);
return FALSE;
}
UserAssert(!(pdesk->dwDTFlags & (DF_DESKWNDDESTROYED | DF_DYING)));
if (pwinsta == NULL)
pwinsta = pdesk->rpwinstaParent;
/*
* Get the windowstation, and assert if this process doesn't have one.
*/
UserAssert(pwinsta);
if (pwinsta == NULL) {
RIPMSG1(RIP_WARNING,
"xxxSwitchDesktop: failed for pwinsta NULL pdesk %#p", pdesk);
return FALSE;
}
/*
* Don't allow invisible desktops to become active
*/
if (pwinsta->dwWSF_Flags & WSF_NOIO) {
RIPMSG1(RIP_VERBOSE,
"xxxSwitchDesktop: failed for NOIO pdesk %#p", pdesk);
return FALSE;
}
pTerm = pwinsta->pTerm;
UserAssert(grpdeskRitInput == pwinsta->pdeskCurrent);
TRACE_INIT(("xxxSwitchDesktop: Entering, desktop = %ws, createdNew = %01lx\n", POBJECT_NAME(pdesk), (DWORD)((dwFlags & SDF_CREATENEW) != 0)));
if (grpdeskRitInput) {
TRACE_INIT((" coming from desktop = %ws\n", POBJECT_NAME(grpdeskRitInput)));
}
/*
* Wait if the logon has the windowstation locked
*/
Thread = PsGetCurrentThread();
/*
* Allow switches to the disconnected desktop
*/
if (pdesk != gspdeskDisconnect) {
if (!PsIsSystemThread(Thread) && pdesk != grpdeskLogon &&
(((pwinsta->dwWSF_Flags & WSF_SWITCHLOCK) != 0) &&
(dwFlags & SDF_SLOVERRIDE) == 0) &&
PsGetThreadProcessId(Thread) != gpidLogon) {
return FALSE;
}
}
/*
* We don't allow switching away from the disconnect desktop.
*/
if (gbDesktopLocked && ((!gspdeskDisconnect) || (pdesk != gspdeskDisconnect))) {
TRACE_DESKTOP(("Attempt to switch away from the disconnect desktop\n"));
/*
* we should not lock this global !!! clupu
*/
LockDesktop(&gspdeskShouldBeForeground, pdesk, LDL_DESKSHOULDBEFOREGROUND1, 0);
return TRUE;
}
/*
* HACKHACK LATER !!!
* Where should we really switch the desktop ...
* And we need to send repaint messages to everyone...
*
*/
UserAssert(grpdeskRitInput == pwinsta->pdeskCurrent);
if ((dwFlags & SDF_CREATENEW) == 0 && grpdeskRitInput &&
(grpdeskRitInput->pDispInfo->hDev != pdesk->pDispInfo->hDev)) {
if (grpdeskRitInput->pDispInfo == gpDispInfo) {
if (!SafeDisableMDEV()) {
RIPMSG1(RIP_WARNING, "xxxSwitchDesktop: DrvDisableMDEV failed for pdesk %#p",
grpdeskRitInput);
return FALSE;
}
} else if (!DrvDisableMDEV(grpdeskRitInput->pDispInfo->pmdev, TRUE)) {
RIPMSG1(RIP_WARNING, "xxxSwitchDesktop: DrvDisableMDEV failed for pdesk %#p",
grpdeskRitInput);
return FALSE;
}
SafeEnableMDEV();
bUpdateCursor = TRUE;
}
/*
* Grab a handle to the pdesk.
*/
Status = ObOpenObjectByPointer(pdesk,
OBJ_KERNEL_HANDLE,
NULL,
EVENT_ALL_ACCESS,
NULL,
KernelMode,
&hdesk);
if (!NT_SUCCESS(Status)) {
RIPMSG2(RIP_WARNING, "Could not get a handle for pdesk %#p Status 0x%x",
pdesk, Status);
return FALSE;
}
ThreadLockDesktopHandle(ptiCurrent, &tlhdesk, hdesk);
#if DBG
/*
* The current desktop is now the new desktop.
*/
pwinsta->pdeskCurrent = pdesk;
#endif
/*
* Kill any journalling that is occuring. If an app is journaling to the
* CoolSwitch window, zzzCancelJournalling() will kill the window.
*/
if (ptiCurrent->rpdesk != NULL) {
zzzCancelJournalling();
}
/*
* Remove the cool switch window if it's on the RIT. Sending the message
* is OK because the destination is the RIT, which should never block.
*/
if (gspwndAltTab != NULL) {
TL tlpwndT;
ThreadLockWithPti(ptiCurrent, gspwndAltTab, &tlpwndT);
xxxSendMessage(gspwndAltTab, WM_CLOSE, 0, 0);
ThreadUnlock(&tlpwndT);
}
/*
* Remove all trace of previous active window.
*/
if (grpdeskRitInput != NULL) {
UserAssert(grpdeskRitInput->spwndForeground == NULL);
if (grpdeskRitInput->pDeskInfo->spwnd != NULL) {
if (gpqForeground != NULL) {
Lock(&grpdeskRitInput->spwndForeground,
gpqForeground->spwndActive);
/*
* This is an API so ptiCurrent can pretty much be on any
* state. It might not be in grpdeskRitInput (current) or
* pdesk (the one we're switching to). It can be sharing its
* queue with other threads from another desktop. This is
* tricky because we're calling xxxSetForegroundWindow and
* xxxSetWindowPos but PtiCurrent might be on whatever
* desktop. We cannot cleanly switch ptiCurrent to the
* proper desktop because it might be sharing its queue with
* other threads, own windows, hooks, etc. So this is kind
* of broken.
*
* Old Comment:
* Fixup the current-thread (system) desktop. This could be
* needed in case the xxxSetForegroundWindow() calls
* xxxDeactivate(). There is logic in their which requires
* the desktop. This is only needed temporarily for this case.
*
* We would only go into xxxDeactivate if ptiCurrent->pq ==
* qpqForeground; but if this is the case, then ptiCurrent
* must be in grpdeskRitInput already. So I don't think we
* need this at all. Let's find out. Note that we might
* switch queues while processing the xxxSetForegroundWindow
* call. That should be fine as long as we don't switch
* desktops.
*/
UserAssert(ptiCurrent->pq != gpqForeground ||
ptiCurrent->rpdesk == grpdeskRitInput);
/*
* The SetForegroundWindow call must succed here, so we call
* xxxSetForegroundWindow2() directly.
*/
xxxSetForegroundWindow2(NULL, ptiCurrent, 0);
}
}
}
/*
* Post update events to all queues sending input to the desktop
* that is becoming inactive. This keeps the queues in sync up
* to the desktop switch.
*/
if (grpdeskRitInput != NULL) {
pHead = &grpdeskRitInput->PtiList;
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
pq = pti->pq;
if (pq->QF_flags & QF_UPDATEKEYSTATE) {
PostUpdateKeyStateEvent(pq);
}
/*
* Clear the reset bit to ensure that we can properly reset the
* key state when this desktop again becomes active.
*/
pq->QF_flags &= ~QF_KEYSTATERESET;
}
}
/*
* Are we switching away from a destroyed desktop? If so, we might never
* unlock the pdesk->rpdeskinfo->spwnd.
*/
if (grpdeskRitInput != NULL) {
if (grpdeskRitInput->dwDTFlags & DF_ZOMBIE) {
FRE_RIPMSG1(RIP_ERROR, "xxxSwitchDesktop: switching away from a destroyed desktop. pdesk = %p", grpdeskRitInput);
}
}
/*
* Send the RIT input to the desktop. We do this before any window
* management since DoPaint() uses grpdeskRitInput to go looking for
* windows with update regions.
*/
LockDesktop(&grpdeskRitInput, pdesk, LDL_DESKRITINPUT, 0);
/*
* Free any spbs that are only valid for the previous desktop.
*/
FreeAllSpbs();
/*
* Lock it into the RIT thread (we could use this desktop rather than
* the global grpdeskRitInput to direct input!)
*/
if (zzzSetDesktop(gptiRit, pdesk, NULL) == FALSE) { // DeferWinEventNotify() ?? IANJA ??
bRet = FALSE;
goto Error;
}
/*
* Lock the desktop into the desktop thread. Be sure
* that the thread is using an unattached queue before
* setting the desktop. This is needed to ensure that
* the thread does not using a shared journal queue
* for the old desktop.
*/
if (pTerm->ptiDesktop->pq != pTerm->pqDesktop) {
UserAssert(pTerm->pqDesktop->cThreads == 0);
AllocQueue(NULL, pTerm->pqDesktop);
pTerm->pqDesktop->cThreads++;
zzzAttachToQueue(pTerm->ptiDesktop, pTerm->pqDesktop, NULL, FALSE);
}
if (zzzSetDesktop(pTerm->ptiDesktop, pdesk, NULL) == FALSE) { // DeferWinEventNotify() ?? IANJA ??
bRet = FALSE;
goto Error;
}
/*
* Makes sure the desktop thread is running on the active destkop.
*/
if (pTerm->ptiDesktop->rpdesk != grpdeskRitInput) {
FRE_RIPMSG0(RIP_ERROR, "xxxSwitchDesktop: desktop thread not running on grpdeskRitInput");
}
/*
* Bring the desktop window to the top and invalidate
* everything.
*/
ThreadLockWithPti(ptiCurrent, pdesk->pDeskInfo->spwnd, &tlpwnd);
/*
* Suspend DirectDraw before we bring up the desktop window, so we make
* sure that everything is repainted properly once DirectDraw is disabled.
*/
GreSuspendDirectDraw(pdesk->pDispInfo->hDev, TRUE);
xxxSetWindowPos(pdesk->pDeskInfo->spwnd, // WHAT KEEPS pdesk LOCKED - IANJA ???
NULL,
0,
0,
0,
0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
/*
* At this point, my understanding is that the new desktop window has been
* brought to the front, and therefore the vis-region of any app on any
* other desktop is now NULL.
*
* So this is the appropriate time to resume DirectDraw, which will
* ensure the DirectDraw app can not draw anything in the future.
*
* If this is not the case, then this code needs to be moved to a more
* appropriate location.
*
* [andreva] 6-26-96
*/
GreResumeDirectDraw(pdesk->pDispInfo->hDev, TRUE);
/*
* Find the first visible top-level window.
*/
pwndSetForeground = pdesk->spwndForeground;
if (pwndSetForeground == NULL || HMIsMarkDestroy(pwndSetForeground)) {
pwndSetForeground = pdesk->pDeskInfo->spwnd->spwndChild;
while ((pwndSetForeground != NULL) &&
!TestWF(pwndSetForeground, WFVISIBLE)) {
pwndSetForeground = pwndSetForeground->spwndNext;
}
}
Unlock(&pdesk->spwndForeground);
/*
* Now set it to the foreground.
*/
if (pwndSetForeground == NULL) {
xxxSetForegroundWindow2(NULL, NULL, 0);
} else {
UserAssert(GETPTI(pwndSetForeground)->rpdesk == grpdeskRitInput);
/*
* If the new foreground window is a minimized fullscreen app,
* make it fullscreen.
*/
if (GetFullScreen(pwndSetForeground) == FULLSCREENMIN) {
SetFullScreen(pwndSetForeground, FULLSCREEN);
}
ThreadLockAlwaysWithPti(ptiCurrent, pwndSetForeground, &tlpwndChild);
/*
* The SetForegroundWindow call must succed here, so we call
* xxxSetForegroundWindow2() directly
*/
xxxSetForegroundWindow2(pwndSetForeground, ptiCurrent, 0);
ThreadUnlock(&tlpwndChild);
}
ThreadUnlock(&tlpwnd);
/*
* Overwrite key state of all queues sending input to the new
* active desktop with the current async key state. This
* prevents apps on inactive desktops from spying on active
* desktops. This blows away anything set with SetKeyState,
* but there is no way of preserving this without giving
* away information about what keys were hit on other
* desktops.
*/
pHead = &grpdeskRitInput->PtiList;
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
pti = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
pq = pti->pq;
if (!(pq->QF_flags & QF_KEYSTATERESET)) {
pq->QF_flags |= QF_UPDATEKEYSTATE | QF_KEYSTATERESET;
RtlFillMemory(pq->afKeyRecentDown, CBKEYSTATERECENTDOWN, 0xff);
PostUpdateKeyStateEvent(pq);
}
}
/*
* If there is a hard-error popup up, nuke it and notify the
* hard error thread that it needs to pop it up again.
*/
if (gHardErrorHandler.pti) {
IPostQuitMessage(gHardErrorHandler.pti, 0);
}
/*
* Notify anyone waiting for a desktop switch.
*/
UserAssert(!(pdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO));
KePulseEvent(gpEventSwitchDesktop, EVENT_INCREMENT, FALSE);
/*
* Reset the cursor when we come back from another pdev.
*/
if (bUpdateCursor == TRUE) {
gpqCursor = NULL;
zzzInternalSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y);
SetPointer(TRUE);
}
/*
* If this desktop was not active during last display settings change
* let's now bradcast the settings change to its windows. This code is
* copied from xxxResetDisplayDevice().
*/
if ((pdesk->dwDTFlags & DF_NEWDISPLAYSETTINGS) && pdesk->pDeskInfo && pdesk->pDeskInfo->spwnd) {
pdesk->dwDTFlags &= ~DF_NEWDISPLAYSETTINGS;
xxxBroadcastDisplaySettingsChange(pdesk, TRUE);
}
Error:
ThreadUnlockDesktopHandle(&tlhdesk);
TRACE_INIT(("xxxSwitchDesktop: Leaving\n"));
return bRet;
}
/***************************************************************************\
* zzzSetDesktop
*
* Set desktop and desktop info in the specified pti.
*
* History:
* 23-Dec-1993 JimA Created.
\***************************************************************************/
BOOL zzzSetDesktop(
PTHREADINFO pti,
PDESKTOP pdesk,
HDESK hdesk)
{
PTEB pteb;
OBJECT_HANDLE_INFORMATION ohi;
PDESKTOP pdeskRef;
PDESKTOP pdeskOld;
PCLIENTTHREADINFO pctiOld;
TL tlpdesk;
PTHREADINFO ptiCurrent = PtiCurrent();
BOOL bRet = TRUE;
if (pti == NULL) {
UserAssert(pti);
return FALSE;
}
/*
* A handle without an object pointer is bad news.
*/
UserAssert(pdesk != NULL || hdesk == NULL);
/*
* This desktop must not be destroyed.
*/
if (pdesk != NULL && (pdesk->dwDTFlags & (DF_DESKWNDDESTROYED | DF_DYING)) &&
pdesk != pti->rpdesk) {
/*
* We need to make an exception for the desktop thread where it is
* possible that all remaining desktops are marked for destruction so
* the desktop thread will not be able to run on grpdeskRitInput.
* Windows Bug #422389.
*/
if (pti != gTermIO.ptiDesktop) {
RIPMSG2(RIP_ERROR, "Assigning pti %#p to a dying desktop %#p",
pti, pdesk);
return FALSE;
} else {
UserAssert(pdesk == grpdeskRitInput);
}
}
/*
* Catch reset of important desktops.
*/
UserAssertMsg0(pti->rpdesk == NULL ||
pti->rpdesk->dwConsoleThreadId != TIDq(pti) ||
pti->cWindows == 0,
"Reset of console desktop");
/*
* Clear hook flag.
*/
pti->TIF_flags &= ~TIF_ALLOWOTHERACCOUNTHOOK;
/*
* Get granted access
*/
pti->hdesk = hdesk;
if (hdesk != NULL) {
if (NT_SUCCESS(ObReferenceObjectByHandle(hdesk,
0,
*ExDesktopObjectType,
(pdesk->dwDTFlags & DF_USERMODE)? UserMode : KernelMode,
&pdeskRef,
&ohi))) {
UserAssert(pdesk->dwSessionId == gSessionId);
LogDesktop(pdeskRef, LD_REF_FN_SETDESKTOP, TRUE, (ULONG_PTR)PtiCurrent());
UserAssert(pdeskRef == pdesk);
LogDesktop(pdesk, LD_DEREF_FN_SETDESKTOP, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdeskRef);
pti->amdesk = ohi.GrantedAccess;
if (CheckHandleFlag(NULL, pdesk->dwSessionId, hdesk, HF_DESKTOPHOOK)) {
pti->TIF_flags |= TIF_ALLOWOTHERACCOUNTHOOK;
}
SetHandleFlag(hdesk, HF_PROTECTED, TRUE);
} else {
pti->amdesk = 0;
}
} else {
pti->amdesk = 0;
}
/*
* Do nothing else if the thread has initialized and the desktop is not
* changing.
*/
if (pdesk != NULL && pdesk == pti->rpdesk) {
return TRUE;
}
/*
* Save old pointers for later. Locking the old desktop ensures that we
* will be able to free the CLIENTTHREADINFO structure.
*/
pdeskOld = pti->rpdesk;
ThreadLockDesktop(ptiCurrent, pdeskOld, &tlpdesk, LDLT_FN_SETDESKTOP);
pctiOld = pti->pcti;
/*
* Remove the pti from the current desktop.
*/
if (pti->rpdesk) {
UserAssert(ISATOMICCHECK() || pti->pq == NULL || pti->pq->cThreads == 1);
RemoveEntryList(&pti->PtiLink);
}
LockDesktop(&pti->rpdesk, pdesk, LDL_PTI_DESK, (ULONG_PTR)pti);
/*
* If there is no desktop, we need to fake a desktop info structure so
* that the IsHooked() macro can test a "valid" fsHooks value. Also link
* the pti to the desktop.
*/
if (pdesk != NULL) {
pti->pDeskInfo = pdesk->pDeskInfo;
InsertHeadList(&pdesk->PtiList, &pti->PtiLink);
} else {
pti->pDeskInfo = &diStatic;
}
pteb = PsGetThreadTeb(pti->pEThread);
if (pteb) {
PDESKTOPVIEW pdv;
if (pdesk && (pdv = GetDesktopView(pti->ppi, pdesk))) {
try {
pti->pClientInfo->pDeskInfo =
(PDESKTOPINFO)((PBYTE)pti->pDeskInfo - pdv->ulClientDelta);
pti->pClientInfo->ulClientDelta = pdv->ulClientDelta;
pti->ulClientDelta = pdv->ulClientDelta;
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
bRet = FALSE;
goto Error;
}
} else {
try {
pti->pClientInfo->pDeskInfo = NULL;
pti->pClientInfo->ulClientDelta = 0;
pti->ulClientDelta = 0;
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
bRet = FALSE;
goto Error;
}
/*
* Reset the cursor level to its orginal state.
*/
pti->iCursorLevel = TEST_GTERMF(GTERMF_MOUSE) ? 0 : -1;
if (pti->pq)
pti->pq->iCursorLevel = pti->iCursorLevel;
}
}
/*
* Allocate thread information visible from client, then copy and free
* any old info we have lying around.
*/
if (pdesk != NULL) {
/*
* Do not use DesktopAlloc here because the desktop might
* have DF_DESTROYED set.
*/
pti->pcti = DesktopAllocAlways(pdesk,
sizeof(CLIENTTHREADINFO),
DTAG_CLIENTTHREADINFO);
}
try {
if (pdesk == NULL || pti->pcti == NULL) {
pti->pcti = &(pti->cti);
pti->pClientInfo->pClientThreadInfo = NULL;
} else {
pti->pClientInfo->pClientThreadInfo =
(PCLIENTTHREADINFO)((PBYTE)pti->pcti - pti->pClientInfo->ulClientDelta);
}
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
if (pti->pcti != &(pti->cti)) {
DesktopFree(pdesk, pti->pcti);
}
bRet = FALSE;
goto Error;
}
if (pctiOld != NULL) {
if (pctiOld != pti->pcti) {
RtlCopyMemory(pti->pcti, pctiOld, sizeof(CLIENTTHREADINFO));
}
if (pctiOld != &(pti->cti)) {
DesktopFree(pdeskOld, pctiOld);
}
} else {
RtlZeroMemory(pti->pcti, sizeof(CLIENTTHREADINFO));
}
/*
* If journalling is occuring on the new desktop, attach to
* the journal queue.
* Assert that the pti and the pdesk point to the same deskinfo
* if not, we will check the wrong hooks.
*/
UserAssert(pdesk == NULL || pti->pDeskInfo == pdesk->pDeskInfo);
UserAssert(pti->rpdesk == pdesk);
if (pti->pq != NULL) {
PQ pq = GetJournallingQueue(pti);
if (pq != NULL) {
pq->cThreads++;
zzzAttachToQueue(pti, pq, NULL, FALSE);
}
}
Error:
ThreadUnlockDesktop(ptiCurrent, &tlpdesk, LDUT_FN_SETDESKTOP);
return bRet;
}
/***************************************************************************\
* xxxSetThreadDesktop (API)
*
* Associate the current thread with a desktop.
*
* History:
* 16-Jan-1991 JimA Created stub.
\***************************************************************************/
BOOL xxxSetThreadDesktop(
HDESK hdesk,
PDESKTOP pdesk)
{
PTHREADINFO ptiCurrent;
PPROCESSINFO ppiCurrent;
PQ pqAttach;
ptiCurrent = PtiCurrent();
ppiCurrent = ptiCurrent->ppi;
/*
* If the handle has not been mapped in, do it now.
*/
if (pdesk != NULL) {
WIN32_OPENMETHOD_PARAMETERS OpenParams;
OpenParams.OpenReason = ObOpenHandle;
OpenParams.Process = ppiCurrent->Process;
OpenParams.Object = pdesk;
OpenParams.GrantedAccess = 0;
OpenParams.HandleCount = 1;
if (!NT_SUCCESS(MapDesktop(&OpenParams))) {
return FALSE;
}
UserAssert(GetDesktopView(ppiCurrent, pdesk) != NULL);
}
/*
* Check non-system thread status.
*/
if (PsGetCurrentProcess() != gpepCSRSS) {
/*
* Fail if the non-system thread has any windows or thread hooks.
*/
if (ptiCurrent->cWindows != 0 || ptiCurrent->fsHooks) {
RIPERR0(ERROR_BUSY, RIP_WARNING, "Thread has windows or hooks");
return FALSE;
}
/*
* If this is the first desktop assigned to the process,
* make it the startup desktop.
*/
if (ppiCurrent->rpdeskStartup == NULL && hdesk != NULL) {
LockDesktop(&ppiCurrent->rpdeskStartup, pdesk, LDL_PPI_DESKSTARTUP1, (ULONG_PTR)ppiCurrent);
ppiCurrent->hdeskStartup = hdesk;
}
}
/*
* If the desktop is changing and the thread is sharing a queue, detach
* the thread. This will ensure that threads sharing queues are all on
* the same desktop. This will prevent zzzDestroyQueue from getting
* confused and setting ptiKeyboard and ptiMouse to NULL when a thread
* detachs.
*/
if (ptiCurrent->rpdesk != pdesk) {
if (ptiCurrent->pq->cThreads > 1) {
pqAttach = AllocQueue(NULL, NULL);
if (pqAttach != NULL) {
pqAttach->cThreads++;
zzzAttachToQueue(ptiCurrent, pqAttach, NULL, FALSE);
} else {
RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_WARNING, "Thread could not be detached");
return FALSE;
}
} else if (ptiCurrent->pq == gpqForeground) {
/*
* This thread doesn't own any windows, still it's attached to
* qpgForeground and it's the only thread attached to it. Since
* any threads attached to qpgForeground must be in grpdeskRitInput,
* we must set qpgForeground to NULL here because this thread is
* going to another desktop.
*/
UserAssert(ptiCurrent->pq->spwndActive == NULL);
UserAssert(ptiCurrent->pq->spwndCapture == NULL);
UserAssert(ptiCurrent->pq->spwndFocus == NULL);
UserAssert(ptiCurrent->pq->spwndActivePrev == NULL);
xxxSetForegroundWindow2(NULL, ptiCurrent, 0);
} else if (ptiCurrent->rpdesk == NULL) {
/*
* We need to initialize iCursorLevel.
*/
ptiCurrent->iCursorLevel = TEST_GTERMF(GTERMF_MOUSE) ? 0 : -1;
ptiCurrent->pq->iCursorLevel = ptiCurrent->iCursorLevel;
}
UserAssert(ptiCurrent->pq != gpqForeground);
}
if (zzzSetDesktop(ptiCurrent, pdesk, hdesk) == FALSE) {
return FALSE;
}
return TRUE;
}
/***************************************************************************\
* xxxGetThreadDesktop (API)
*
* Return a handle to the desktop assigned to the specified thread.
*
* History:
* 16-Jan-1991 JimA Created stub.
\***************************************************************************/
HDESK xxxGetThreadDesktop(
DWORD dwThread,
HDESK hdeskConsole,
KPROCESSOR_MODE AccessMode)
{
PTHREADINFO pti = PtiFromThreadId(dwThread);
PPROCESSINFO ppiThread;
HDESK hdesk;
NTSTATUS Status;
if (pti == NULL) {
/*
* If the thread has a console use that desktop. If
* not, then the thread is either invalid or not
* a Win32 thread.
*/
if (hdeskConsole == NULL) {
RIPERR1(ERROR_INVALID_PARAMETER, RIP_VERBOSE,
"xxxGetThreadDesktop: invalid threadId 0x%x",
dwThread);
return NULL;
}
hdesk = hdeskConsole;
ppiThread = PpiFromProcess(gpepCSRSS);
} else {
hdesk = pti->hdesk;
ppiThread = pti->ppi;
}
/*
* If there is no desktop, return NULL with no error
*/
if (hdesk != NULL) {
/*
* If the thread belongs to this process, return the
* handle. Otherwise, enumerate the handle table of
* this process to find a handle with the same
* attributes.
*/
if (ppiThread != PpiCurrent()) {
PVOID pobj;
OBJECT_HANDLE_INFORMATION ohi;
RIPMSG4(RIP_VERBOSE, "[%x.%x] %s called xxxGetThreadDesktop for pti %#p",
PsGetCurrentProcessId(),
PsGetCurrentThreadId(),
PsGetCurrentProcessImageFileName(),
pti);
KeAttachProcess(PsGetProcessPcb(ppiThread->Process));
Status = ObReferenceObjectByHandle(hdesk,
0,
*ExDesktopObjectType,
AccessMode,
&pobj,
&ohi);
KeDetachProcess();
if (!NT_SUCCESS(Status) ||
!ObFindHandleForObject(PsGetCurrentProcess(), pobj, NULL, &ohi, &hdesk)) {
RIPMSG0(RIP_VERBOSE, "Cannot find hdesk for current process");
hdesk = NULL;
} else {
LogDesktop(pobj, LD_REF_FN_GETTHREADDESKTOP, TRUE, (ULONG_PTR)PtiCurrent());
}
if (NT_SUCCESS(Status)) {
LogDesktop(pobj, LD_DEREF_FN_GETTHREADDESKTOP, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pobj);
}
}
if (hdesk == NULL) {
RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "xxxGetThreadDesktop: hdesk is null");
} else {
SetHandleFlag(hdesk, HF_PROTECTED, TRUE);
}
}
return hdesk;
}
/***************************************************************************\
* _GetInputDesktop (API)
*
* Obsolete - kept for compatibility only. Return a handle to the
* desktop currently receiving input. Returns the first handle to
* the input desktop found.
*
* History:
* 16-Jan-1991 JimA Created scaffold code.
\***************************************************************************/
HDESK _GetInputDesktop(
VOID)
{
HDESK hdesk;
if (ObFindHandleForObject(PsGetCurrentProcess(), grpdeskRitInput, NULL, NULL, &hdesk)) {
SetHandleFlag(hdesk, HF_PROTECTED, TRUE);
return hdesk;
} else {
return NULL;
}
}
/***************************************************************************\
* xxxCloseDesktop (API)
*
* Close a reference to a desktop and destroy the desktop if it is no
* longer referenced.
*
* History:
* 16-Jan-1991 JimA Created scaffold code.
* 11-Feb-1991 JimA Added access checks.
\***************************************************************************/
BOOL xxxCloseDesktop(
HDESK hdesk,
KPROCESSOR_MODE AccessMode)
{
PDESKTOP pdesk;
PTHREADINFO ptiT;
PPROCESSINFO ppi;
NTSTATUS Status;
ppi = PpiCurrent();
/*
* Get a pointer to the desktop.
*/
Status = ObReferenceObjectByHandle(
hdesk,
0,
*ExDesktopObjectType,
AccessMode,
&pdesk,
NULL);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_VERBOSE, "");
return FALSE;
}
UserAssert(pdesk->dwSessionId == gSessionId);
LogDesktop(pdesk, LD_REF_FN_CLOSEDESKTOP, TRUE, (ULONG_PTR)PtiCurrent());
if (ppi->Process != gpepCSRSS) {
/*
* Disallow closing of the desktop if the handle is in use by
* any threads in the process.
*/
for (ptiT = ppi->ptiList; ptiT != NULL; ptiT = ptiT->ptiSibling) {
if (ptiT->hdesk == hdesk) {
RIPERR2(ERROR_BUSY, RIP_WARNING,
"CloseDesktop: Desktop %#p still in use by thread %#p",
pdesk, ptiT);
LogDesktop(pdesk, LD_DEREF_FN_CLOSEDESKTOP1, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdesk);
return FALSE;
}
}
/*
* If this is the startup desktop, unlock it
*/
/*
* Bug 41394. Make sure that hdesk == ppi->hdeskStartup. We might
* be getting a handle to the desktop object that is different
* from ppi->hdeskStartup but we still end up
* setting ppi->hdeskStartup to NULL.
*/
if ((pdesk == ppi->rpdeskStartup) && (hdesk == ppi->hdeskStartup)) {
UnlockDesktop(&ppi->rpdeskStartup, LDU_PPI_DESKSTARTUP2, (ULONG_PTR)ppi);
ppi->hdeskStartup = NULL;
}
}
/*
* Clear hook flag
*/
SetHandleFlag(hdesk, HF_DESKTOPHOOK, FALSE);
/*
* Close the handle
*/
Status = CloseProtectedHandle(hdesk);
LogDesktop(pdesk, LD_DEREF_FN_CLOSEDESKTOP2, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdesk);
UserAssert(NT_SUCCESS(Status));
return TRUE;
}
/***************************************************************************\
* TerminateConsole
*
* Post a quit message to a console thread and wait for it to terminate.
*
* History:
* 08-May-1995 JimA Created.
\***************************************************************************/
VOID TerminateConsole(
PDESKTOP pdesk)
{
NTSTATUS Status;
PETHREAD Thread;
PTHREADINFO pti;
if (pdesk->dwConsoleThreadId == 0) {
return;
}
/*
* Locate the console thread.
*/
Status = LockThreadByClientId(LongToHandle(pdesk->dwConsoleThreadId), &Thread);
if (!NT_SUCCESS(Status)) {
return;
}
/*
* Post a quit message to the console.
*/
pti = PtiFromThread(Thread);
if (pti == NULL) {
FRE_RIPMSG1(RIP_ERROR,
"PtiFromThread for CIT 0x%p returned NULL!",
Thread);
}
if (pti != NULL) {
_PostThreadMessage(pti, WM_QUIT, 0, 0);
}
/*
* Clear thread id so we don't post twice
*/
pdesk->dwConsoleThreadId = 0;
UnlockThread(Thread);
}
/***************************************************************************\
* CheckHandleFlag
*
* Returns TRUE if the desktop handle allows other accounts
* to hook this process.
*
* History:
* 07-13-95 JimA Created.
\***************************************************************************/
BOOL CheckHandleFlag(
PEPROCESS Process,
DWORD dwSessionId,
HANDLE hObject,
DWORD dwFlag)
{
ULONG Index = ((PEXHANDLE)&hObject)->Index * HF_LIMIT + dwFlag;
BOOL fRet = FALSE, bAttached = FALSE;
PPROCESSINFO ppi;
KAPC_STATE ApcState;
EnterHandleFlagsCrit();
if (Process == NULL) {
ppi = PpiCurrent();
} else {
if (PsGetProcessSessionId(Process) != dwSessionId) {
KeStackAttachProcess(PsGetProcessPcb(Process), &ApcState);
bAttached = TRUE;
}
ppi = PpiFromProcess(Process);
}
if (ppi != NULL) {
fRet = (Index < ppi->bmHandleFlags.SizeOfBitMap &&
RtlCheckBit(&ppi->bmHandleFlags, Index));
}
if (bAttached) {
KeUnstackDetachProcess(&ApcState);
}
LeaveHandleFlagsCrit();
return fRet;
}
/***************************************************************************\
* SetHandleFlag
*
* Sets and clears the ability of a desktop handle to allow
* other accounts to hook this process.
*
* History:
* 07-13-95 JimA Created.
\***************************************************************************/
BOOL SetHandleFlag(
HANDLE hObject,
DWORD dwFlag,
BOOL fSet)
{
PPROCESSINFO ppi;
ULONG Index = ((PEXHANDLE)&hObject)->Index * HF_LIMIT + dwFlag;
PRTL_BITMAP pbm;
ULONG cBits;
PULONG Buffer;
BOOL fRet = TRUE;
UserAssert(dwFlag < HF_LIMIT);
EnterHandleFlagsCrit();
if ((ppi = PpiCurrent()) != NULL) {
pbm = &ppi->bmHandleFlags;
if (fSet) {
/*
* Expand the bitmap if needed
*/
if (Index >= pbm->SizeOfBitMap) {
/*
* Index is zero-based - cBits is an exact number of dwords
*/
cBits = ((Index + 1) + 0x1F) & ~0x1F;
Buffer = UserAllocPoolWithQuotaZInit(cBits / 8, TAG_PROCESSINFO);
if (Buffer == NULL) {
fRet = FALSE;
goto Exit;
}
if (pbm->Buffer) {
RtlCopyMemory(Buffer, pbm->Buffer, pbm->SizeOfBitMap / 8);
UserFreePool(pbm->Buffer);
}
RtlInitializeBitMap(pbm, Buffer, cBits);
}
RtlSetBits(pbm, Index, 1);
} else if (Index < pbm->SizeOfBitMap) {
RtlClearBits(pbm, Index, 1);
}
}
Exit:
LeaveHandleFlagsCrit();
return fRet;
}
/***************************************************************************\
* CheckHandleInUse
*
* Returns TRUE if the handle is currently in use.
*
* History:
* 02-Jun-1999 JerrySh Created.
\***************************************************************************/
BOOL CheckHandleInUse(
HANDLE hObject)
{
BOOL fRet;
EnterHandleFlagsCrit();
fRet = ((gProcessInUse == PsGetCurrentProcess()) &&
(gHandleInUse == hObject));
LeaveHandleFlagsCrit();
return fRet;
}
/***************************************************************************\
* SetHandleInUse
*
* Mark the handle as in use.
*
* History:
* 02-Jun-1999 JerrySh Created.
\***************************************************************************/
VOID SetHandleInUse(
HANDLE hObject)
{
EnterHandleFlagsCrit();
gProcessInUse = PsGetCurrentProcess();
gHandleInUse = hObject;
LeaveHandleFlagsCrit();
}
/***************************************************************************\
* xxxResolveDesktopForWOW
*
* Checks whether given process has access to the provided windowstation/desktop
* or the defaults if none are specified. (WinSta0\Default).
*
* History:
* 03-Jan-2002 Mohamed Modified to use dynamically allocated VM for
* string buffers and CR for security on handle
* manipulation. Using UserMode handles intentionally
* to undergo the needed security access checks.
\***************************************************************************/
NTSTATUS xxxResolveDesktopForWOW(
IN OUT PUNICODE_STRING pstrDesktop)
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING strDesktop, strWinSta, strStatic;
WCHAR wchStaticBuffer[STATIC_UNICODE_BUFFER_LENGTH];
LPWSTR pszDesktop;
BOOL fWinStaDefaulted;
BOOL fDesktopDefaulted;
HWINSTA hwinsta;
HDESK hdesk;
PUNICODE_STRING pstrStatic;
POBJECT_ATTRIBUTES pObjA = NULL;
SIZE_T cbObjA;
BOOL bShutDown = FALSE;
/*
* Determine windowstation and desktop names.
*/
if (pstrDesktop == NULL) {
return STATUS_INVALID_PARAMETER;
}
strStatic.Length = 0;
strStatic.MaximumLength = STATIC_UNICODE_BUFFER_LENGTH * sizeof(WCHAR);
strStatic.Buffer = wchStaticBuffer;
if (pstrDesktop->Length == 0) {
RtlInitUnicodeString(&strDesktop, L"Default");
fWinStaDefaulted = fDesktopDefaulted = TRUE;
} else {
USHORT cch;
/*
* The name be of the form windowstation\desktop. Parse the string
* to separate out the names.
*/
strWinSta = *pstrDesktop;
cch = strWinSta.Length / sizeof(WCHAR);
pszDesktop = strWinSta.Buffer;
while (cch && *pszDesktop != L'\\') {
cch--;
pszDesktop++;
}
fDesktopDefaulted = FALSE;
if (cch == 0) {
/*
* No windowstation name was specified, only the desktop.
*/
strDesktop = strWinSta;
fWinStaDefaulted = TRUE;
} else {
/*
* Both names were in the string.
*/
strDesktop.Buffer = pszDesktop + 1;
strDesktop.Length = strDesktop.MaximumLength = (cch - 1) * sizeof(WCHAR);
strWinSta.Length = (USHORT)(pszDesktop - strWinSta.Buffer) * sizeof(WCHAR);
/*
* zero terminate the strWinSta buffer so the rebuild of the desktop
* name at the end of the function works.
*/
*pszDesktop = (WCHAR)0;
fWinStaDefaulted = FALSE;
RtlAppendUnicodeToString(&strStatic, (PWSTR)szWindowStationDirectory);
RtlAppendUnicodeToString(&strStatic, L"\\");
RtlAppendUnicodeStringToString(&strStatic, &strWinSta);
}
}
if (fWinStaDefaulted) {
/*
* Default Window Station.
*/
RtlInitUnicodeString(&strWinSta, L"WinSta0");
RtlAppendUnicodeToString(&strStatic, (PWSTR)szWindowStationDirectory);
RtlAppendUnicodeToString(&strStatic, L"\\");
RtlAppendUnicodeStringToString(&strStatic, &strWinSta);
}
/*
* Open the computed windowstation. This will also do an access check.
*/
/*
* Allocate an object attributes structure, a UNICODE_STRING structure and a string
* buffer of suitable length in user address space.
*/
cbObjA = sizeof(*pObjA) + sizeof(*pstrStatic) + STATIC_UNICODE_BUFFER_LENGTH * sizeof(WCHAR);
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
&pObjA, 0, &cbObjA, MEM_COMMIT, PAGE_READWRITE);
pstrStatic = (PUNICODE_STRING)((PBYTE)pObjA + sizeof(*pObjA));
if (NT_SUCCESS(Status)) {
/*
* Note -- the string must be in client-space or the address
* validation in _OpenWindowStation will fail. And we use UserMode
* for KPROCESSOR_MODE to be able to utilize security checks;
* KernelMode would bypass the checks. The side-effect of this is
* that the returned hwinsta and hdesk handles are UserMode handles
* and must be handled with care.
*/
try {
pstrStatic->Length = 0;
pstrStatic->MaximumLength = STATIC_UNICODE_BUFFER_LENGTH * sizeof(WCHAR);
pstrStatic->Buffer = (PWSTR)((PBYTE)pstrStatic + sizeof(*pstrStatic));
RtlCopyUnicodeString(pstrStatic, &strStatic);
InitializeObjectAttributes(pObjA,
pstrStatic,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
Status = GetExceptionCode();
}
if (NT_SUCCESS(Status)) {
hwinsta = _OpenWindowStation(pObjA, MAXIMUM_ALLOWED, UserMode);
} else {
hwinsta = NULL;
}
if (!hwinsta) {
ZwFreeVirtualMemory(NtCurrentProcess(),
&pObjA,
&cbObjA,
MEM_RELEASE);
return STATUS_ACCESS_DENIED;
}
} else {
return STATUS_NO_MEMORY;
}
/*
* Do an access check on the desktop by opening it
*/
/*
* Note -- the string must be in client-space or the
* address validation in _OpenDesktop will fail.
*/
try {
RtlCopyUnicodeString(pstrStatic, &strDesktop);
InitializeObjectAttributes( pObjA,
pstrStatic,
OBJ_CASE_INSENSITIVE,
hwinsta,
NULL);
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
Status = GetExceptionCode();
}
if (NT_SUCCESS(Status)) {
hdesk = _OpenDesktop(pObjA,
UserMode,
0,
MAXIMUM_ALLOWED,
&bShutDown);
} else {
hdesk = NULL;
}
ZwFreeVirtualMemory(NtCurrentProcess(), &pObjA, &cbObjA, MEM_RELEASE);
UserVerify(NT_SUCCESS(ObCloseHandle(hwinsta, UserMode)));
if (!hdesk) {
return STATUS_ACCESS_DENIED;
}
CloseProtectedHandle(hdesk);
/*
* Copy the final Computed String
*/
RtlCopyUnicodeString(pstrDesktop, &strWinSta);
RtlAppendUnicodeToString(pstrDesktop, L"\\");
RtlAppendUnicodeStringToString(pstrDesktop, &strDesktop);
return STATUS_SUCCESS;
}
/***************************************************************************\
* xxxResolveDesktop
*
* Attempts to return handles to a windowstation and desktop associated
* with the logon session.
*
* History:
* 25-Apr-1994 JimA Created.
* 03-Jan-2002 Mohamed Modified it to use dynamically allocated VM for
* string buffers and CR for security on handle
* manipulation. Using UserMode handles intentionally
* to undergo the needed security access checks.
\***************************************************************************/
HDESK xxxResolveDesktop(
HANDLE hProcess,
PUNICODE_STRING pstrDesktop,
HWINSTA *phwinsta,
BOOL fInherit,
BOOL* pbShutDown)
{
PEPROCESS Process;
PPROCESSINFO ppi;
HWINSTA hwinsta;
HDESK hdesk;
PDESKTOP pdesk;
PWINDOWSTATION pwinsta;
BOOL fInteractive;
UNICODE_STRING strDesktop,
strWinSta,
strStatic;
PUNICODE_STRING pstrStatic;
WCHAR wchStaticBuffer[STATIC_UNICODE_BUFFER_LENGTH];
LPWSTR pszDesktop;
POBJECT_ATTRIBUTES pObjA = NULL;
SIZE_T cbObjA;
WCHAR awchName[sizeof(L"Service-0x0000-0000$") / sizeof(WCHAR)];
BOOL fWinStaDefaulted;
BOOL fDesktopDefaulted;
LUID luidService;
NTSTATUS Status;
HWINSTA hwinstaDup;
CheckCritIn();
Status = ObReferenceObjectByHandle(hProcess,
PROCESS_QUERY_INFORMATION,
*PsProcessType,
UserMode,
&Process,
NULL);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING, "xxxResolveDesktop: Could not reference process handle (0x%X)", hProcess);
return NULL;
}
hwinsta = hwinstaDup = NULL;
hdesk = NULL;
strStatic.Length = 0;
strStatic.MaximumLength = STATIC_UNICODE_BUFFER_LENGTH * sizeof(WCHAR);
strStatic.Buffer = wchStaticBuffer;
/*
* If the process already has a windowstation and a startup desktop,
* return them.
*
* Make sure the process has not been destroyed first. Windows NT Bug
* #214643.
*/
ppi = PpiFromProcess(Process);
if (ppi != NULL) {
if (ppi->W32PF_Flags & W32PF_TERMINATED) {
ObDereferenceObject(Process);
RIPMSG1(RIP_WARNING,
"xxxResolveDesktop: ppi 0x%p has been destroyed",
ppi);
return NULL;
}
if (ppi->hwinsta != NULL && ppi->hdeskStartup != NULL) {
/*
* If the target process is the current process, simply return
* the handles. Otherwise, open the objects.
*/
if (Process == PsGetCurrentProcess()) {
hwinsta = ppi->hwinsta;
hdesk = ppi->hdeskStartup;
} else {
Status = ObOpenObjectByPointer(ppi->rpwinsta,
0,
NULL,
MAXIMUM_ALLOWED,
*ExWindowStationObjectType,
UserMode,
&hwinsta);
if (NT_SUCCESS(Status)) {
Status = ObOpenObjectByPointer(ppi->rpdeskStartup,
0,
NULL,
MAXIMUM_ALLOWED,
*ExDesktopObjectType,
UserMode,
&hdesk);
if (!NT_SUCCESS(Status)) {
UserVerify(NT_SUCCESS(ObCloseHandle(hwinsta, UserMode)));
hwinsta = NULL;
}
}
if (!NT_SUCCESS(Status)) {
RIPNTERR2(Status,
RIP_WARNING,
"xxxResolveDesktop: Failed to reference winsta=0x%p or desktop=0x%p",
ppi->rpwinsta,
ppi->rpdeskStartup);
}
}
RIPMSG2(RIP_VERBOSE,
"xxxResolveDesktop: to hwinsta=%#p desktop=%#p",
hwinsta, hdesk);
ObDereferenceObject(Process);
*phwinsta = hwinsta;
return hdesk;
}
}
/*
* Determine windowstation and desktop names.
*/
if (pstrDesktop == NULL || pstrDesktop->Length == 0) {
RtlInitUnicodeString(&strDesktop, L"Default");
fWinStaDefaulted = fDesktopDefaulted = TRUE;
} else {
USHORT cch;
/*
* The name is of the form windowstation\desktop. Parse the string
* to separate out the names.
*/
strWinSta = *pstrDesktop;
cch = strWinSta.Length / sizeof(WCHAR);
pszDesktop = strWinSta.Buffer;
while (cch && *pszDesktop != L'\\') {
cch--;
pszDesktop++;
}
fDesktopDefaulted = FALSE;
if (cch == 0) {
/*
* No windowstation name was specified, only the desktop.
*/
strDesktop = strWinSta;
fWinStaDefaulted = TRUE;
} else {
/*
* Both names were in the string.
*/
strDesktop.Buffer = pszDesktop + 1;
strDesktop.Length = strDesktop.MaximumLength = (cch - 1) * sizeof(WCHAR);
strWinSta.Length = (USHORT)(pszDesktop - strWinSta.Buffer) * sizeof(WCHAR);
fWinStaDefaulted = FALSE;
RtlAppendUnicodeToString(&strStatic, (PWSTR)szWindowStationDirectory);
RtlAppendUnicodeToString(&strStatic, L"\\");
RtlAppendUnicodeStringToString(&strStatic, &strWinSta);
if (!NT_SUCCESS(Status = _UserTestForWinStaAccess(&strStatic,TRUE))) {
RIPMSG3(RIP_WARNING,
"xxxResolveDesktop: Error (0x%x) resolving winsta='%.*ws'",
Status,
strStatic.Length,
strStatic.Buffer);
goto ReturnNull;
}
}
}
/*
* If the desktop name is defaulted, make the handles not inheritable.
*/
if (fDesktopDefaulted) {
fInherit = FALSE;
}
/*
* If a windowstation has not been assigned to this process yet and
* there are existing windowstations, attempt an open.
*/
if (hwinsta == NULL && grpWinStaList) {
/*
* If the windowstation name was defaulted, create a name based on
* the session.
*/
if (fWinStaDefaulted) {
/*
* Default Window Station.
*/
RtlInitUnicodeString(&strWinSta, L"WinSta0");
RtlAppendUnicodeToString(&strStatic, szWindowStationDirectory);
RtlAppendUnicodeToString(&strStatic, L"\\");
RtlAppendUnicodeStringToString(&strStatic, &strWinSta);
if (gbRemoteSession) {
/*
* Fake this out if it's an non-interactive winstation startup.
* We don't want an extra winsta.
*/
fInteractive = NT_SUCCESS(_UserTestForWinStaAccess(&strStatic, TRUE));
} else {
fInteractive = NT_SUCCESS(_UserTestForWinStaAccess(&strStatic,fInherit));
}
if (!fInteractive) {
GetProcessLuid(NULL, &luidService);
swprintf(awchName,
L"Service-0x%x-%x$",
luidService.HighPart,
luidService.LowPart);
RtlInitUnicodeString(&strWinSta, awchName);
}
}
/*
* If no windowstation name was passed in and a windowstation
* handle was inherited, assign it.
*/
if (fWinStaDefaulted) {
if (ObFindHandleForObject(Process, NULL, *ExWindowStationObjectType,
NULL, &hwinsta)) {
/*
* If the handle belongs to another process, dup it into
* this one.
*/
if (Process != PsGetCurrentProcess()) {
Status = ZwDuplicateObject(hProcess,
hwinsta,
NtCurrentProcess(),
&hwinstaDup,
0,
0,
DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(Status)) {
hwinsta = NULL;
} else {
hwinsta = hwinstaDup;
}
}
}
}
/*
* If we were assigned to a windowstation, make sure
* it matches our fInteractive flag
*/
if (hwinsta != NULL) {
Status = ObReferenceObjectByHandle(hwinsta,
0,
*ExWindowStationObjectType,
KernelMode,
&pwinsta,
NULL);
if (NT_SUCCESS(Status)) {
BOOL fIO = (pwinsta->dwWSF_Flags & WSF_NOIO) ? FALSE : TRUE;
if (fIO != fInteractive) {
if (hwinstaDup) {
CloseProtectedHandle(hwinsta);
}
hwinsta = NULL;
}
ObDereferenceObject(pwinsta);
}
}
/*
* If not, open the computed windowstation.
*/
if (NT_SUCCESS(Status) && hwinsta == NULL) {
/*
* Fill in the path to the windowstation
*/
strStatic.Length = 0;
RtlAppendUnicodeToString(&strStatic, szWindowStationDirectory);
RtlAppendUnicodeToString(&strStatic, L"\\");
RtlAppendUnicodeStringToString(&strStatic, &strWinSta);
/*
* Allocate an object attributes structure, a UNICODE_STRING structure and a string
* buffer of suitable length in user address space.
*/
cbObjA = sizeof(*pObjA) + sizeof(*pstrStatic) + STATIC_UNICODE_BUFFER_LENGTH * sizeof(WCHAR);
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
&pObjA, 0, &cbObjA, MEM_COMMIT, PAGE_READWRITE);
pstrStatic = (PUNICODE_STRING)((PBYTE)pObjA + sizeof(*pObjA));
if (NT_SUCCESS(Status)) {
/*
* Note -- the string must be in client-space or the
* address validation in _OpenWindowStation will fail.
*/
try {
pstrStatic->Length = 0;
pstrStatic->MaximumLength = STATIC_UNICODE_BUFFER_LENGTH * sizeof(WCHAR);
pstrStatic->Buffer = (PWSTR)((PBYTE)pstrStatic + sizeof(*pstrStatic));
RtlCopyUnicodeString(pstrStatic, &strStatic);
InitializeObjectAttributes(pObjA,
pstrStatic,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
if (fInherit) {
pObjA->Attributes |= OBJ_INHERIT;
}
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
Status = GetExceptionCode();
}
if (NT_SUCCESS(Status)) {
hwinsta = _OpenWindowStation(pObjA, MAXIMUM_ALLOWED, UserMode);
}
}
}
/*
* Only allow service logons at the console. I don't think our
* win32k exit routines cope with more than one windowstation.
*
* If the open failed and the process is in a non-interactive logon
* session, attempt to create a windowstation and desktop for that
* session. Note that the desktop handle will be closed after the
* desktop has been assigned.
*/
if (!gbRemoteSession && NT_SUCCESS(Status) &&
hwinsta == NULL && !fInteractive && fWinStaDefaulted) {
*phwinsta = xxxConnectService(&strStatic, &hdesk);
/*
* Clean up and leave.
*/
if (pObjA != NULL) {
ZwFreeVirtualMemory(NtCurrentProcess(),
&pObjA,
&cbObjA,
MEM_RELEASE);
}
ObDereferenceObject(Process);
RIPMSG2(RIP_VERBOSE,
"xxxResolveDesktop: xxxConnectService was called"
"to hwinsta=%#p desktop=%#p",
*phwinsta, hdesk);
return hdesk;
}
}
/*
* Attempt to assign a desktop.
*/
if (hwinsta != NULL) {
/*
* Every gui thread needs an associated desktop. We'll use the
* default to start with and the application can override it if it
* wants.
*/
if (hdesk == NULL) {
/*
* If no desktop name was passed in and a desktop handle was
* inherited, assign it.
*/
if (fDesktopDefaulted) {
if (ObFindHandleForObject(Process, NULL, *ExDesktopObjectType,
NULL, &hdesk)) {
/*
* If the handle belongs to another process, dup it into
* this one.
*/
if (Process != PsGetCurrentProcess()) {
HDESK hdeskDup;
Status = ZwDuplicateObject(hProcess,
hdesk,
NtCurrentProcess(),
&hdeskDup,
0,
0,
DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(Status)) {
CloseProtectedHandle(hdesk);
hdesk = NULL;
} else {
hdesk = hdeskDup;
}
}
/*
* Map the desktop into the process.
*/
if (hdesk != NULL && ppi != NULL) {
Status = ObReferenceObjectByHandle(hdesk,
0,
*ExDesktopObjectType,
KernelMode,
&pdesk,
NULL);
if (NT_SUCCESS(Status)) {
LogDesktop(pdesk, LD_REF_FN_RESOLVEDESKTOP, TRUE, (ULONG_PTR)PtiCurrent());
{
WIN32_OPENMETHOD_PARAMETERS OpenParams;
OpenParams.OpenReason = ObOpenHandle;
OpenParams.Process = Process;
OpenParams.Object = pdesk;
OpenParams.GrantedAccess = 0;
OpenParams.HandleCount = 1;
if (!NT_SUCCESS(MapDesktop(&OpenParams))) {
Status = STATUS_NO_MEMORY;
CloseProtectedHandle(hdesk);
hdesk = NULL;
}
}
UserAssert(hdesk == NULL ||
GetDesktopView(ppi, pdesk) != NULL);
LogDesktop(pdesk, LD_DEREF_FN_RESOLVEDESKTOP, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdesk);
} else {
CloseProtectedHandle(hdesk);
hdesk = NULL;
}
}
}
}
/*
* If not, open the desktop.
*/
if (NT_SUCCESS(Status) && hdesk == NULL) {
RtlCopyUnicodeString(&strStatic, &strDesktop);
if (pObjA == NULL) {
/*
* Allocate an object attributes structure, a UNICODE_STRING structure and a string
* buffer of suitable length in user address space.
*/
cbObjA = sizeof(*pObjA) + sizeof(*pstrStatic) + STATIC_UNICODE_BUFFER_LENGTH * sizeof(WCHAR);
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
&pObjA, 0, &cbObjA, MEM_COMMIT, PAGE_READWRITE);
pstrStatic = (PUNICODE_STRING)((PBYTE)pObjA + sizeof(*pObjA));
}
if (NT_SUCCESS(Status)) {
/*
* Note -- the string must be in client-space or the
* address validation in _OpenDesktop will fail.
*/
try {
pstrStatic->Length = 0;
pstrStatic->MaximumLength = STATIC_UNICODE_BUFFER_LENGTH * sizeof(WCHAR);
pstrStatic->Buffer = (PWSTR)((PBYTE)pstrStatic + sizeof(*pstrStatic));
RtlCopyUnicodeString(pstrStatic, &strStatic);
InitializeObjectAttributes( pObjA,
pstrStatic,
OBJ_CASE_INSENSITIVE,
hwinsta,
NULL
);
if (fInherit) {
pObjA->Attributes |= OBJ_INHERIT;
}
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
Status = GetExceptionCode();
}
if (NT_SUCCESS(Status)) {
hdesk = _OpenDesktop(pObjA,
UserMode,
0,
MAXIMUM_ALLOWED,
pbShutDown);
}
}
}
}
if (hdesk == NULL) {
UserVerify(NT_SUCCESS(ObCloseHandle(hwinsta, UserMode)));
hwinsta = NULL;
}
}
goto ExitNormally;
ReturnNull:
UserAssert(hdesk == NULL);
if (hwinsta != NULL) {
UserVerify(NT_SUCCESS(ObCloseHandle(hwinsta, UserMode)));
hwinsta = NULL;
}
ExitNormally:
if (pObjA != NULL) {
ZwFreeVirtualMemory(NtCurrentProcess(), &pObjA, &cbObjA, MEM_RELEASE);
}
ObDereferenceObject(Process);
*phwinsta = hwinsta;
return hdesk;
}
#ifdef REDIRECTION
#define DESKTOP_ALL (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | \
DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | \
DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | \
DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | \
DESKTOP_SWITCHDESKTOP | DESKTOP_QUERY_INFORMATION | \
DESKTOP_REDIRECT | STANDARD_RIGHTS_REQUIRED)
#else
#define DESKTOP_ALL (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | \
DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | \
DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | \
DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | \
DESKTOP_SWITCHDESKTOP | STANDARD_RIGHTS_REQUIRED)
#endif
NTSTATUS
SetDisconnectDesktopSecurity(
IN HDESK hdeskDisconnect)
{
ULONG ulLength;
NTSTATUS Status = STATUS_SUCCESS;
SID_IDENTIFIER_AUTHORITY NtSidAuthority = SECURITY_NT_AUTHORITY;
PACCESS_ALLOWED_ACE pace = NULL;
PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
PSID pSystemSid = NULL;
/*
* Get the well-known system SID.
*/
pSystemSid = UserAllocPoolWithQuota(RtlLengthRequiredSid(1), TAG_SECURITY);
if (pSystemSid != NULL) {
*(RtlSubAuthoritySid(pSystemSid, 0)) = SECURITY_LOCAL_SYSTEM_RID;
Status = RtlInitializeSid(pSystemSid, &NtSidAuthority, (UCHAR)1);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if (!NT_SUCCESS(Status)) {
goto done;
}
/*
* Allocate and ACE that give System all ACCESS (No access to any one else).
*/
pace = AllocAce(NULL, ACCESS_ALLOWED_ACE_TYPE, 0,
DESKTOP_ALL,
pSystemSid, &ulLength);
if (pace == NULL) {
RIPMSG0(RIP_WARNING, "GetDisconnectDesktopSecurityDescriptor: AllocAce for Desktop Attributes failed");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
/*
* Create the security descriptor.
*/
pSecurityDescriptor = CreateSecurityDescriptor(pace, ulLength, FALSE);
if (pSecurityDescriptor == NULL) {
RIPMSG0(RIP_WARNING, "GetDisconnectDesktopSecurityDescriptor: CreateSecurityDescriptor failed");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
/*
* Set security on Disconnected desktop.
*/
Status = ZwSetSecurityObject(hdeskDisconnect,
DACL_SECURITY_INFORMATION,
pSecurityDescriptor);
done:
/*
* Cleanup allocations.
*/
if (pSystemSid != NULL) {
UserFreePool(pSystemSid);
}
if (pace != NULL) {
UserFreePool(pace);
}
if (pSecurityDescriptor != NULL) {
UserFreePool(pSecurityDescriptor);
}
return Status;
}
#ifdef DEBUG_DESK
VOID ValidateDesktop(
PDESKTOP pdesk)
{
/*
* Verify that the desktop has been cleaned out.
*/
PHE pheT, pheMax;
BOOL fDirty = FALSE;
pheMax = &gSharedInfo.aheList[giheLast];
for (pheT = gSharedInfo.aheList; pheT <= pheMax; pheT++) {
switch (pheT->bType) {
case TYPE_WINDOW:
if (((PWND)pheT->phead)->head.rpdesk == pdesk) {
DbgPrint("Window at 0x%p exists\n", pheT->phead);
break;
}
continue;
case TYPE_MENU:
if (((PMENU)pheT->phead)->head.rpdesk == pdesk) {
DbgPrint("Menu at 0x%p exists\n", pheT->phead);
break;
}
continue;
case TYPE_CALLPROC:
if (((PCALLPROCDATA)pheT->phead)->head.rpdesk == pdesk) {
DbgPrint("Callproc at 0x%p exists\n", pheT->phead);
break;
}
continue;
case TYPE_HOOK:
if (((PHOOK)pheT->phead)->head.rpdesk == pdesk) {
DbgPrint("Hook at 0x%p exists\n", pheT->phead);
break;
}
continue;
default:
continue;
}
fDirty = TRUE;
}
UserAssert(!fDirty);
}
#endif
/***************************************************************************\
* DbgCheckForThreadsOnDesktop
*
* Validates that no threads in the process are still on this desktop.
*
* NB: This desktop can be in a different session than the process, so you
* CANNOT deref pdesk here.
*
* History:
* 27-Jun-2001 JasonSch Created.
\***************************************************************************/
VOID DbgCheckForThreadsOnDesktop(
PPROCESSINFO ppi,
PDESKTOP pdesk)
{
#if DBG
PTHREADINFO pti = ppi->ptiList;
while (pti != NULL) {
UserAssertMsg2(pti->rpdesk != pdesk,
"pti 0x%p still on pdesk 0x%p",
pti,
pdesk);
pti = pti->ptiSibling;
}
#else
UNREFERENCED_PARAMETER(ppi);
UNREFERENCED_PARAMETER(pdesk);
#endif
}