|
|
/***************************** 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
}
|