Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3741 lines
110 KiB

/***************************** Module Header ******************************\
* Module Name: desktop.c
*
* Copyright (c) 1985-1996, 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;
} DESKTOP_CONTEXT, *PDESKTOP_CONTEXT;
/*
* Debug Related Info.
*/
#ifdef DEBUG
DWORD gDesktopsBusy = 0; // diagnostic
#endif
VOID FreeView(
PEPROCESS Process,
PDESKTOP pdesk);
/***************************************************************************\
* DesktopThread
*
* 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.
\***************************************************************************/
#ifdef LOCK_MOUSE_CODE
#pragma alloc_text(MOUSE, DesktopThread)
#endif
VOID DesktopThread(
PDESKTOPTHREADINIT pdti)
{
KPRIORITY Priority;
PTHREADINFO ptiCurrent;
PQ pqOriginal;
PWINDOWSTATION pwinsta;
PKEVENT *apRITEvents;
PKEVENT pEvent;
/*
* Set the desktop thread's priority to low realtime.
*/
Priority = LOW_REALTIME_PRIORITY;
ZwSetInformationThread(NtCurrentThread(),
ThreadPriority,
&Priority,
sizeof(KPRIORITY));
InitSystemThread(POBJECT_NAME(pdti->pwinsta));
ptiCurrent = PtiCurrentShared();
pwinsta = pdti->pwinsta;
pwinsta->ptiDesktop = ptiCurrent;
pwinsta->pqDesktop = pqOriginal = ptiCurrent->pq;
(pqOriginal->cLockCount)++;
ptiCurrent->pDeskInfo = gpdiStatic;
/*
* Since this is a system-thread, we set the pwinsta. This is
* referenced in DoPaint() in determining which desktop needs
* painting.
*/
ptiCurrent->pwinsta = pwinsta;
/*
* Allocate non-paged array. Include an extra entry for
* the thread's input event.
*/
apRITEvents = ExAllocatePoolWithTag(NonPagedPool,
(3 * sizeof(PKEVENT)),
TAG_SYSTEM);
/*
* Reference the mouse input event. This should be a per-winsta event.
*/
ObReferenceObjectByHandle(ghevtMouseInput,
EVENT_ALL_ACCESS,
NULL,
KernelMode,
&apRITEvents[0],
NULL);
/*
* Create the desktop destruction event.
*/
apRITEvents[1] = CreateKernelEvent(SynchronizationEvent, FALSE);
pwinsta->pEventDestroyDesktop = apRITEvents[1];
EnterCrit();
KeSetEvent(pdti->pEvent, EVENT_INCREMENT, FALSE);
pEvent = pwinsta->pEventInputReady;
ObReferenceObjectByPointer(pEvent,
EVENT_ALL_ACCESS,
ExEventObjectType,
KernelMode);
LeaveCrit();
KeWaitForSingleObject(pEvent, WrUserRequest, KernelMode, FALSE, NULL);
ObDereferenceObject(pEvent);
EnterCrit();
/*
* 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, calling
* MouseApcProcedure whenever ghevtMouseInput is set.
*/
result = xxxMsgWaitForMultipleObjects(2,
apRITEvents,
FALSE,
INFINITE,
QS_ALLINPUT,
MouseApcProcedure);
#ifdef DEBUG
gDesktopsBusy++; // diagnostic
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)(WAIT_OBJECT_0 + 2)) {
/*
* block-local variable
*/
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)) {
/*
* if it's a quit message we're out of here
*/
if (msg.message == WM_QUIT && ptiCurrent->cWindows == 1) {
/*
* The window station is gone, so
*
* DON'T USE PWINSTA ANYMORE
*/
/*
* 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 = gpdiStatic;
/*
* 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) {
UserAssert(pqOriginal != gpqForeground);
DestroyQueue(pqOriginal, ptiCurrent);
}
#ifdef DEBUG
gDesktopsBusy--; // diagnostic
#endif
LeaveCrit();
/*
* Deref the events now that we're done with them.
* Also free the wait array.
*/
ObDereferenceObject(apRITEvents[0]);
UserFreePool(apRITEvents[1]);
UserFreePool(apRITEvents);
/*
* Terminate the thread. This will call the
* Win32k thread cleanup code.
*/
PsTerminateSystemThread(0);
}
/*
* otherwise dispatch it
*/
xxxDispatchMessage(&msg);
} // end of PeekMessage while loop
} else if (result == (WAIT_OBJECT_0 + 1)) {
PDESKTOP *ppdesk;
PDESKTOP pdesk;
PWND pwnd;
PMENU pmenu;
TL tlpwinsta;
TL tlpdesk;
TL tlpwnd;
PDESKTOP pdeskTemp;
HDESK hdeskTemp;
TL tlpdeskTemp;
/*
* Destroy desktops on the destruction list.
*/
ThreadLockWinSta(ptiCurrent, pwinsta, &tlpwinsta);
for (ppdesk = &pwinsta->rpdeskDestroy; *ppdesk != NULL; ) {
/*
* Unlink from the list.
*/
pdesk = *ppdesk;
ThreadLockDesktop(ptiCurrent, pdesk, &tlpdesk);
LockDesktop(ppdesk, pdesk->rpdeskNext);
UnlockDesktop(&pdesk->rpdeskNext);
/*
* !!! If this is the current desktop, switch to another one.
*/
if (pdesk == grpdeskRitInput) {
PDESKTOP pdeskNew;
if (pwinsta->dwFlags & WSF_SWITCHLOCK) {
pdeskNew = pwinsta->rpdeskLogon;
} else {
pdeskNew = pwinsta->rpdeskList;
if (pdeskNew == pdesk)
pdeskNew = pdesk->rpdeskNext;
UserAssert(pdeskNew);
}
xxxSwitchDesktop(pwinsta, pdeskNew, FALSE);
}
/*
* Close the display if this desktop did not use
* the global display. Note that pdesk->hsem is
* taken care of by the close.
* We must also mark this device as free so we can create
* a desktop on it again
*/
if ((pdesk->pDispInfo->hDev != NULL) &&
(pdesk->pDispInfo->hDev != gpDispInfo->hDev)) {
GreDestroyHDEV(pdesk->pDispInfo->hDev);
UserFreeDevice(pdesk->pDispInfo->pDevInfo);
}
/*
* Destroy desktop and menu windows.
*/
pdeskTemp = ptiCurrent->rpdesk; // save current desktop
hdeskTemp = ptiCurrent->hdesk;
ThreadLockDesktop(ptiCurrent, pdeskTemp, &tlpdeskTemp);
_SetThreadDesktop(NULL, pdesk);
Unlock(&pdesk->spwndForeground);
Unlock(&pdesk->spwndTray);
if (pdesk->spmenuSys != NULL) {
pmenu = pdesk->spmenuSys;
if (Unlock(&pdesk->spmenuSys))
_DestroyMenu(pmenu);
}
if (pdesk->spmenuDialogSys != NULL) {
pmenu = pdesk->spmenuDialogSys;
if (Unlock(&pdesk->spmenuDialogSys))
_DestroyMenu(pmenu);
}
if (pdesk->pDesktopDevmode) {
UserFreePool(pdesk->pDesktopDevmode);
pdesk->pDesktopDevmode = NULL;
}
/*
* If this desktop doesn't have a pDeskInfo, then
* something is wrong. All desktops should have
* this until the object is freed.
*/
if (pdesk->pDeskInfo == NULL) {
RIPMSG0(RIP_ERROR,
"DesktopThread: There is no pDeskInfo for this desktop");
}
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);
}
if (pdesk->spwndMenu != NULL) {
pwnd = pdesk->spwndMenu;
/*
* 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);
}
if (Unlock(&pdesk->spwndMenu)) {
xxxDestroyWindow(pwnd);
}
}
if (pdesk->pDeskInfo && (pdesk->pDeskInfo->spwnd != NULL)) {
PVOID pDestroy;
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);
}
pDestroy = Unlock(&pdesk->pDeskInfo->spwnd);
/*
* Put the pwnd back in the desktop so that threads
* that have not yet done cleanup can still find a
* desktop window. We don't want to lock the window
* because it will prevent the desktop from being
* freed when all cleanup is complete. Note that
* this assignment is benign if pwnd is invalid.
*/
pdesk->pDeskInfo->spwnd = pwnd;
if (pDestroy != NULL) {
xxxDestroyWindow(pwnd);
}
pdesk->dwDTFlags |= DF_DESKWNDDESTROYED;
}
/*
* 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 (pwinsta->spwndDesktopOwner->head.rpdesk == pdesk) {
LockDesktop(&(pwinsta->spwndDesktopOwner->head.rpdesk),
pwinsta->rpdeskList);
}
/*
* Restore the previous desktop
*/
_SetThreadDesktop(hdeskTemp, pdeskTemp);
ThreadUnlockDesktop(ptiCurrent, &tlpdeskTemp);
ThreadUnlockDesktop(ptiCurrent, &tlpdesk);
}
ThreadUnlockWinSta(ptiCurrent, &tlpwinsta);
} else {
RIPMSG0(RIP_WARNING, "Desktop woke up for what?");
}
#ifdef DEBUG
gDesktopsBusy--; // diagnostic
#endif
}
}
/***************************************************************************\
* xxxInvalidateIconicWindows
*
*
*
* History:
* 27-Jun-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID xxxInvalidateIconicWindows(
PWND pwndParent,
PWND pwndPaletteChanging)
{
PTHREADINFO ptiCurrent = PtiCurrent();
PWND pwnd;
PWND pwndNext;
TL tlpwndNext;
TL tlpwnd;
CheckLock(pwndParent);
CheckLock(pwndPaletteChanging);
pwnd = pwndParent->spwndChild;
while (pwnd != NULL) {
pwndNext = pwnd->spwndNext;
ThreadLockWithPti(ptiCurrent, pwndNext, &tlpwndNext);
if (pwnd != pwndPaletteChanging && TestWF(pwnd, WFMINIMIZED)) {
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
xxxRedrawWindow(pwnd,
NULL,
NULL,
RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_NOCHILDREN);
ThreadUnlock(&tlpwnd);
}
pwnd = pwndNext;
ThreadUnlock(&tlpwndNext);
}
}
/***************************************************************************\
* 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
\***************************************************************************/
LONG xxxDesktopWndProc(
PWND pwnd,
UINT message,
DWORD wParam,
LPARAM lParam)
{
PTHREADINFO ptiCurrent = PtiCurrent();
HDC hdcT;
PAINTSTRUCT ps;
PDESKWND pdeskwnd = (PDESKWND)pwnd;
PWINDOWPOS pwp;
CheckLock(pwnd);
VALIDATECLASSANDSIZE(pwnd, FNID_DESKTOP);
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, "WM_ICON sent to desktop window was discarded.\n") ;
return 0L ;
default:
break;
} /* switch */
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) {
_SetThreadDesktop(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)-1,
WM_PALETTECHANGED,
(DWORD)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 (gptiRit->pq->spwndAltTab) {
ThreadLockAlwaysWithPti(ptiCurrent, gptiRit->pq->spwndAltTab, &tlpwndT);
xxxSendMessage(gptiRit->pq->spwndAltTab, 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_ICON sent to desktop window was discarded.\n") ;
break;
case WM_CREATE:
/*
* Is there a desktop pattern, or bitmap name in WIN.INI?
*/
xxxSetDeskPattern((LPWSTR)-1, TRUE);
/*
* Initialize the system colors before we show the desktop window.
*/
xxxSendNotifyMessage(pwnd, WM_SYSCOLORCHANGE, 0, 0L);
hdcT = _GetDC(pwnd);
InternalPaintDesktop(pdeskwnd, hdcT, FALSE); // use "normal" HDC so SelectPalette() will work
_ReleaseDC(hdcT);
/*
* Save process and thread ids.
*/
xxxSetWindowLong(pwnd,
0,
(DWORD)PsGetCurrentThread()->Cid.UniqueProcess,
FALSE);
xxxSetWindowLong(pwnd,
4,
(DWORD)PsGetCurrentThread()->Cid.UniqueThread,
FALSE);
break;
case WM_PALETTECHANGED:
if ((ghpalWallpaper || (pwnd->head.rpdesk->dwDTFlags & DTF_NEEDSREDRAW)) &&
(HWq(pwnd) != (HWND)wParam)) {
TL tlwndShell;
TL tlwndPal;
PWND pwndShell;
PWND pwndPal;
DWORD dwFlags;
/*
* Turn of the need for a redraw. This is set in DestroyWindow
* for palette-threads.
*/
pwnd->head.rpdesk->dwDTFlags &= ~DTF_NEEDSREDRAW;
pwndPal = HMValidateHandleNoRip((HWND)wParam, TYPE_WINDOW);
ThreadLockWithPti(ptiCurrent, pwndPal, &tlwndPal);
/*
* We need to invalidate the wallpaper if the palette changed so
* it is properly redrawn with new colors.
*/
dwFlags = RDW_INVALIDATE | RDW_ERASE;
if (pwnd->head.rpdesk->pDeskInfo->spwndShell) {
pwndShell = pwnd->head.rpdesk->pDeskInfo->spwndShell;
dwFlags |= RDW_ALLCHILDREN;
} else {
pwndShell = pwnd;
dwFlags |= RDW_NOCHILDREN;
}
/*
* Redraw the desktop.
*/
ThreadLockAlwaysWithPti(ptiCurrent, pwndShell, &tlwndShell);
xxxRedrawWindow(pwndShell, NULL, NULL, dwFlags);
ThreadUnlock(&tlwndShell);
/*
* Invalidate iconic windows so their backgrounds draw with the
* new colors. To avoid recursion, don't invalidate the pwnd
* whose palette is changing.
*/
xxxInvalidateIconicWindows(PWNDDESKTOP(pwnd), pwndPal);
ThreadUnlock(&tlwndPal);
}
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;
InternalPaintDesktop(pdeskwnd, hdcT, TRUE);
return TRUE;
case WM_PAINT:
xxxBeginPaint(pwnd, (LPPAINTSTRUCT)&ps);
_EndPaint(pwnd, (LPPAINTSTRUCT)&ps);
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(
LPWSTR lpszPattern,
BOOL fCreation)
{
LPWSTR p;
int i;
UINT val;
WCHAR wszNone[20];
WCHAR wszDeskPattern[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)(LONG)-1) {
/*
* Yes! Then use that pattern;
*/
p = lpszPattern;
goto GotThePattern;
}
/*
* Else, pickup the pattern selected in WIN.INI.
*/
ServerLoadString(hModuleWin,
STR_DESKPATTERN,
wszDeskPattern,
sizeof(wszDeskPattern)/sizeof(WCHAR));
/*
* Get the "DeskPattern" string from WIN.INI's [Desktop] section.
*/
if (!UT_FastGetProfileStringW(PMAP_DESKTOP,
wszDeskPattern,
TEXT(""),
wchValue,
sizeof(wchValue)/sizeof(WCHAR)))
return FALSE;
ServerLoadString(hModuleWin,
STR_NONE,
wszNone,
sizeof(wszNone)/sizeof(WCHAR));
p = wchValue;
GotThePattern:
/*
* Was a Desk Pattern selected?
*/
if (!_wcsicmp(p, wszNone)) {
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 >= TEXT('0') && *p <= TEXT('9')))
p++;
/*
* Get the next series of digits.
*/
while (*p >= TEXT('0') && *p <= TEXT('9'))
val = val * (UINT)10 + (UINT)(*p++ - TEXT('0'));
rgBits[i] = 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)-1, 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 (hbmMem = GreCreateCompatibleBitmap(gpDispInfo->hdcBits,
CXYDESKPATTERN,
CXYDESKPATTERN)) {
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);
}
}
/***************************************************************************\
* 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;
NTSTATUS Status;
/*
* 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->dwFlags & WSF_OPENLOCK &&
Process->UniqueProcessId != 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->dwFlags & 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->dwFlags & WSF_OPENLOCK &&
Process->UniqueProcessId != 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)) {
return Status;
}
RtlZeroMemory(pdesk, sizeof(DESKTOP));
/*
* Create security descriptor
*/
Status = ObAssignSecurity(
pAccessState,
OBJECT_TO_OBJECT_HEADER(pwinsta)->SecurityDescriptor,
pdesk,
*ExDesktopObjectType);
if (!NT_SUCCESS(Status)) {
ObDereferenceObject(pdesk);
return Status;
}
/*
* Set up desktop heap. The first desktop (logon desktop) uses a
* small heap (128).
*/
if (!(pwinsta->dwFlags & WSF_NOIO) && (pwinsta->rpdeskList == NULL)) {
ulHeapSize = 128;
} else {
if (pwinsta->dwFlags & WSF_NOIO) {
ulHeapSize = gdwNOIOSectionSize;
} else {
ulHeapSize = gdwDesktopSectionSize;
}
}
/*
* Create the desktop heap.
*/
pdesk->hsectionDesktop = CreateDesktopHeap(&pdesk->hheapDesktop, ulHeapSize);
if (pdesk->hsectionDesktop == NULL) {
ObDereferenceObject(pdesk);
return STATUS_NO_MEMORY;
}
if (pwinsta->rpdeskList == NULL || (pwinsta->dwFlags & 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->hheapDesktop, sizeof(DESKTOPINFO));
pdesk->pDeskInfo = pdi;
/*
* Initialize everything
*/
if (pdi == NULL) {
RIPMSG0(RIP_ERROR, "xxxCreateDesktop: failed DeskInfo Alloc");
ObDereferenceObject(pdesk);
return STATUS_NO_MEMORY;
}
RtlZeroMemory(pdi, sizeof(*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) {
/*
* Allocate a display-info for this device.
*/
pdesk->pDispInfo = (PDISPLAYINFO)DesktopAlloc(pdesk->hheapDesktop,
sizeof(DISPLAYINFO));
pdesk->pDesktopDevmode = (PDEVMODE)UserAllocPool(Context->lpDevMode->dmSize +
Context->lpDevMode->dmDriverExtra,
TAG_DEVMODE);
if ((pdesk->pDispInfo == NULL) ||
(pdesk->pDesktopDevmode == NULL)) {
RIPMSG0(RIP_ERROR, "xxxCreateDesktop: failed DisplayInfo Alloc");
ObDereferenceObject(pdesk);
return STATUS_NO_MEMORY;
}
pdesk->pDispInfo->hDev = UserCreateHDEV(
Context->pstrDevice,
Context->lpDevMode,
&pdesk->pDispInfo->pDevInfo,
(PDEVICE_LOCK *)&pdesk->pDispInfo->pDevLock);
if (!pdesk->pDispInfo->hDev) {
ObDereferenceObject(pdesk);
return STATUS_UNSUCCESSFUL;
}
CopyRect(&pdesk->pDispInfo->rcScreen, &gpDispInfo->rcScreen);
CopyRect(&pdesk->pDispInfo->rcPrimaryScreen, &gpDispInfo->rcPrimaryScreen);
pdesk->pDispInfo->cxPixelsPerInch = gpDispInfo->cxPixelsPerInch;
pdesk->pDispInfo->cyPixelsPerInch = gpDispInfo->cyPixelsPerInch;
pdesk->pDispInfo->cPlanes = oemInfo.Planes;
pdesk->pDispInfo->cBitsPixel = oemInfo.BitCount;
RtlCopyMemory(pdesk->pDesktopDevmode,
Context->lpDevMode,
Context->lpDevMode->dmSize +
Context->lpDevMode->dmDriverExtra);
pdesk->bForceModeReset = FALSE;
} else {
pdesk->pDispInfo = gpDispInfo;
/*
* NOTE - eventually, each desktop should have it's own copy of the
* Dispinfo, and the DEVMODE will be part of the structure.
*/
pdesk->pDesktopDevmode = (PDEVMODE)UserAllocPool(gpDispInfo->pDevInfo->pCurrentDevmode->dmSize +
gpDispInfo->pDevInfo->pCurrentDevmode->dmDriverExtra,
TAG_DEVMODE);
RtlCopyMemory(pdesk->pDesktopDevmode,
gpDispInfo->pDevInfo->pCurrentDevmode,
gpDispInfo->pDevInfo->pCurrentDevmode->dmSize +
gpDispInfo->pDevInfo->pCurrentDevmode->dmDriverExtra);
pdesk->bForceModeReset = FALSE;
}
pdesk->dwDTFlags = 0;
pdi->pvDesktopBase = pdesk->hheapDesktop;
pdi->pvDesktopLimit = (PBYTE)pdesk->hheapDesktop + (ulHeapSize * 1024);
/*
* Reference the parent windowstation
*/
LockWinSta(&(pdesk->rpwinstaParent), pwinsta);
/*
* Link the desktop into the windowstation list
*/
if (pwinsta->rpdeskList == NULL) {
if (!(pwinsta->dwFlags & WSF_NOIO))
LockDesktop(&(pwinsta->rpdeskLogon), pdesk);
/*
* 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->spwndDesktopOwner->head.rpdesk), pdesk);
}
LockDesktop(&pdesk->rpdeskNext, pwinsta->rpdeskList);
LockDesktop(&pwinsta->rpdeskList, pdesk);
/*
* Mask off invalid access bits
*/
if (pAccessState->RemainingDesiredAccess & MAXIMUM_ALLOWED) {
pAccessState->RemainingDesiredAccess &= ~MAXIMUM_ALLOWED;
pAccessState->RemainingDesiredAccess |= GENERIC_ALL;
}
RtlMapGenericMask( &pAccessState->RemainingDesiredAccess, &DesktopMapping);
pAccessState->RemainingDesiredAccess &=
(DesktopMapping.GenericAll | ACCESS_SYSTEM_SECURITY);
*pObject = pdesk;
return STATUS_SUCCESS;
}
HDESK xxxCreateDesktop(
POBJECT_ATTRIBUTES ObjectAttributes,
KPROCESSOR_MODE ProbeMode,
PUNICODE_STRING pstrDevice,
LPDEVMODE lpdevmode,
DWORD dwFlags,
DWORD dwDesiredAccess)
{
HWINSTA hwinsta;
HDESK hdesk;
DESKTOP_CONTEXT Context;
PDESKTOP pdesk;
PDESKTOPINFO pdi;
PWINDOWSTATION pwinsta;
PDESKTOP pdeskTemp;
HDESK hdeskTemp;
PWND pwndDesktop;
PWND pwndMenu;
TL tlpwnd;
PTHREADINFO ptiCurrent = PtiCurrent();
BOOL fWasNull;
BOOL bSuccess;
PPROCESSINFO ppi;
PPROCESSINFO ppiSave;
NTSTATUS Status;
DWORD dwDisableHooks;
/*
* Validate the device name
* It can either be NULL or the name of a device.
* If we are creating the desktop on another device, on something
* other than the default display, the devmode is required.
*/
// if (pstrDevice && pstrDevice->Buffer) {
//
// TRACE_INIT(("xxxCreateDesktop: Creating on alternate display\n"));
//
// if (lpdevmode == NULL) {
// RIPERR0(ERROR_INVALID_PARAMETER, RIP_VERBOSE, "");
// return NULL;
// }
// }
/*
* BUGBUG For now, assume that if the DEVMODE is NULL, then we are not
* creating a new desktop, whatever the name says.
* NOTE, be craefull since we can have a NULL name + NULL devmode when
* we are called from service.c
*/
/*
* Capture directory handle and check for create access.
*/
try {
hwinsta = ObjectAttributes->RootDirectory;
} except (EXCEPTION_EXECUTE_HANDLER) {
RIPNTERR0(GetExceptionCode(), RIP_VERBOSE, "");
return NULL;
}
if (hwinsta != NULL) {
Status = ObReferenceObjectByHandle(
hwinsta,
WINSTA_CREATEDESKTOP,
*ExWindowStationObjectType,
ProbeMode,
&pwinsta,
NULL);
if (NT_SUCCESS(Status)) {
ObDereferenceObject(pwinsta);
} else {
RIPNTERR0(Status, RIP_VERBOSE, "ObReferenceObjectByHandle Failed");
return NULL;
}
}
/*
* Set up creation context
*/
Context.lpDevMode = lpdevmode;
Context.pstrDevice = pstrDevice;
Context.dwFlags = dwFlags;
/*
* Create the desktop
*/
Status = ObOpenObjectByName(
ObjectAttributes,
*ExDesktopObjectType,
ProbeMode,
NULL,
dwDesiredAccess,
&Context,
&hdesk);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_VERBOSE, "");
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)
return hdesk;
/*
* Reference the desktop to finish initialization
*/
Status = ObReferenceObjectByHandle(
hdesk,
0,
*ExDesktopObjectType,
KernelMode,
&pdesk,
NULL);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_VERBOSE, "");
ZwClose(hdesk);
return NULL;
}
pwinsta = pdesk->rpwinstaParent;
pdi = pdesk->pDeskInfo;
pdi->idShellProcess = 0;
/*
* If the desktop was not mapped in as a result of the open,
* fail.
*/
ppi = PpiCurrent();
if (GetDesktopView(ppi, pdesk) == NULL) {
/*
* Desktop mapping failed.
*/
ZwClose(hdesk);
ObDereferenceObject(pdesk);
RIPNTERR0(STATUS_ACCESS_DENIED, RIP_VERBOSE, "");
return NULL;
}
/*
* Map the desktop into CSRSS to ensure that the
* hard error handler can get access.
*/
MapDesktop(ObOpenHandle, gpepCSRSS, pdesk, 0, 1);
if (GetDesktopView(PpiFromProcess(gpepCSRSS), pdesk) == NULL) {
/*
* Desktop mapping failed.
*/
ZwClose(hdesk);
ObDereferenceObject(pdesk);
RIPNTERR0(STATUS_ACCESS_DENIED, RIP_VERBOSE, "");
return NULL;
}
/*
* Set hook flags
*/
SetDesktopHookFlag(ppi, hdesk, dwFlags & DF_ALLOWOTHERACCOUNTHOOK);
/*
* Set up to create the desktop window.
*/
fWasNull = FALSE;
if (ptiCurrent->ppi->rpdeskStartup == NULL)
fWasNull = TRUE;
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 = pwinsta->ptiDesktop->ppi;
SetDesktop(ptiCurrent, pdesk, hdesk);
/*
* 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 = xxxCreateWindowEx(
(DWORD)0,
(PLARGE_STRING)MAKEINTRESOURCE(DESKTOPCLASS),
NULL,
(WS_POPUP | WS_CLIPCHILDREN),
0,
0,
pdesk->pDispInfo->rcScreen.right - pdesk->pDispInfo->rcScreen.left,
pdesk->pDispInfo->rcScreen.bottom - pdesk->pDispInfo->rcScreen.top,
NULL,
NULL,
hModuleWin,
NULL,
VER31);
UserAssert(pdi->spwnd == NULL);
/*
* Clean things up
*/
if (pwndDesktop != NULL) {
Lock(&(pdi->spwnd), pwndDesktop);
HMChangeOwnerThread(pdi->spwnd, pwinsta->ptiDesktop);
pwndDesktop->bFullScreen = GDIFULLSCREEN;
/*
* set this windows to the fullscreen window if we don't have one yet
*/
// LATER mikeke
// this can be a problem if a desktop is created while we are in
// FullScreenCleanup()
if (gspwndFullScreen == NULL && !(pwinsta->dwFlags & WSF_NOIO)) {
Lock(&(gspwndFullScreen), pwndDesktop);
}
/*
* Link it as a child but don't use WS_CHILD style
*/
LinkWindow(pwndDesktop, NULL, &(pwinsta->spwndDesktopOwner)->spwndChild);
Lock(&pwndDesktop->spwndParent, pwinsta->spwndDesktopOwner);
Unlock(&pwndDesktop->spwndOwner);
/*
* Create shared menu window
*/
pwndMenu = xxxCreateWindowEx(
WS_EX_TOOLWINDOW | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE,
(PLARGE_STRING)MENUCLASS,
NULL,
WS_POPUP | WS_BORDER,
0,
0,
100,
100,
NULL,
NULL,
hModuleWin,
NULL,
VER40);
if (pwndMenu != NULL) {
Lock(&(pdesk->spwndMenu), pwndMenu);
HMChangeOwnerThread(pdesk->spwndMenu, pwinsta->ptiDesktop);
}
}
/*
* Fail if both windows were not created.
*/
if (pdi->spwnd == NULL || pdesk->spwndMenu == NULL) {
/*
* 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
*/
SetDesktop(ptiCurrent, pdeskTemp, hdeskTemp);
/*
* Close the desktop
*/
ZwClose(hdesk);
ObDereferenceObject(pdesk);
/*
* 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);
return NULL;
}
/*
* Restore caller's ppi
*/
PtiCurrent()->ppi = ppiSave;
/*
* 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 (pwinsta->pEventInputReady != NULL) {
KeSetEvent(pwinsta->pEventInputReady, EVENT_INCREMENT, FALSE);
ObDereferenceObject(pwinsta->pEventInputReady);
pwinsta->pEventInputReady = NULL;
}
/*
* HACK HACK:
* LATER
*
* If we have a devmode passed in, then switch desktops ...
*/
if (lpdevmode) {
TRACE_INIT(("xxxCreateDesktop: about to call switch desktop\n"));
bSuccess = xxxSwitchDesktop(pwinsta, pdesk, TRUE);
if (!bSuccess)
RIPMSG0(RIP_ERROR, "Failed to switch desktop on Create\n");
} else {
/*
* 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);
ThreadUnlock(&tlpwnd);
}
TRACE_INIT(("xxxCreateDesktop: Leaving\n"));
/*
* 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
*/
SetDesktop(ptiCurrent, pdeskTemp, hdeskTemp);
if (fWasNull)
UnlockDesktop(&ptiCurrent->ppi->rpdeskStartup);
ObDereferenceObject(pdesk);
return hdesk;
}
/***************************************************************************\
* 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;
/*
* 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.
*/
return STATUS_OBJECT_NAME_COLLISION;
} else {
Status = STATUS_OBJECT_NAME_EXISTS;
}
} else {
Status = STATUS_SUCCESS;
}
ObReferenceObject(pdesk);
*pObject = pdesk;
return Status;
}
}
/*
* Handle creation request
*/
if (Context != NULL) {
return xxxCreateDesktop2(
pContainerObject,
pAccessState,
AccessMode,
pstrRemainingName,
Context,
pObject);
}
/*
* Failure.
*/
*pObject = NULL;
return STATUS_OBJECT_NAME_NOT_FOUND;
}
/***************************************************************************\
* 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;
PDESKTOP *ppdesk;
if (pdesk->dwDTFlags & DF_DESTROYED) {
RIPMSG1(RIP_WARNING, "DestroyDesktop: Already destroyed:%#lx", 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);
UnlockDesktop(&pdesk->rpdeskNext);
}
}
/*
* Link it into the destruction list and signal the desktop thread.
*/
LockDesktop(&pdesk->rpdeskNext, pwinsta->rpdeskDestroy);
LockDesktop(&pwinsta->rpdeskDestroy, pdesk);
KeSetEvent(pwinsta->pEventDestroyDesktop, EVENT_INCREMENT, FALSE);
pdesk->dwDTFlags |= DF_DESTROYED;
return TRUE;
}
/***************************************************************************\
* FreeDesktop
*
* Called to free desktop object and section when last lock is released.
*
* History:
* 08-Dec-1993 JimA Created.
\***************************************************************************/
VOID FreeDesktop(
PVOID pobj)
{
PDESKTOP pdesk = (PDESKTOP)pobj;
NTSTATUS Status;
/*
* Mark the desktop as dying. Make sure we aren't recursing.
*/
UserAssert(!(pdesk->dwDTFlags & DF_DYING));
pdesk->dwDTFlags |= DF_DYING;
#ifdef DEBUG_DESK
{
/*
* Verify that the desktop has been cleaned out.
*/
#if 0
PPROCESSINFO ppi;
PCLS pcls, pclsClone;
#endif
PHE pheT, pheMax;
BOOL fDirty = FALSE;
#if 0
for (ppi = gppiFirst; ppi != NULL; ppi = ppi->ppiNext) {
for (pcls = ppi->pclsPrivateList; pcls != NULL; pcls = pcls->pclsNext) {
if (pcls->hheapDesktop == pdesk->hheapDesktop) {
DbgPrint("ppi %08x private class at %08x exists\n", ppi, pcls);
fDirty = TRUE;
}
for (pclsClone = pcls->pclsClone; pclsClone != NULL;
pclsClone = pclsClone->pclsNext) {
if (pclsClone->hheapDesktop == pdesk->hheapDesktop) {
DbgPrint("ppi %08x private class clone at %08x exists\n", ppi, pclsClone);
fDirty = TRUE;
}
}
}
for (pcls = ppi->pclsPublicList; pcls != NULL; pcls = pcls->pclsNext) {
if (pcls->hheapDesktop == pdesk->hheapDesktop) {
DbgPrint("ppi %08x public class at %08x exists\n", ppi, pcls);
fDirty = TRUE;
}
for (pclsClone = pcls->pclsClone; pclsClone != NULL;
pclsClone = pclsClone->pclsNext) {
if (pclsClone->hheapDesktop == pdesk->hheapDesktop) {
DbgPrint("ppi %08x public class clone at %08x exists\n", ppi, pclsClone);
fDirty = TRUE;
}
}
}
}
#endif
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 %08x exists\n", pheT->phead);
break;
}
continue;
case TYPE_MENU:
if (((PMENU)pheT->phead)->head.rpdesk == pdesk) {
DbgPrint("Menu at %08x exists\n", pheT->phead);
break;
}
continue;
case TYPE_CALLPROC:
if (((PCALLPROCDATA)pheT->phead)->head.rpdesk == pdesk) {
DbgPrint("Callproc at %08x exists\n", pheT->phead);
break;
}
continue;
case TYPE_HOOK:
if (((PHOOK)pheT->phead)->head.rpdesk == pdesk) {
DbgPrint("Hook at %08x exists\n", pheT->phead);
break;
}
continue;
default:
continue;
}
fDirty = TRUE;
}
if (fDirty) {
DbgPrint("Desktop cleanup failed\n");
DbgBreakPoint();
}
}
#endif
if (pdesk->hheapDesktop != NULL) {
Status = MmUnmapViewInSystemSpace(pdesk->hheapDesktop);
UserAssert(NT_SUCCESS(Status));
ObDereferenceObject(pdesk->hsectionDesktop);
}
/*
* 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);
UnlockWinSta(&pdesk->rpwinstaParent);
}
/***************************************************************************\
* CreateDesktopHeap
*
* Create a new desktop heap
*
* History:
* 27-Jul-1992 JimA Created.
\***************************************************************************/
HANDLE CreateDesktopHeap(
PVOID *ppvHeapBase,
ULONG ulHeapSize)
{
HANDLE hsection;
LARGE_INTEGER SectionSize;
ULONG ulViewSize;
NTSTATUS Status;
/*
* Make it into Kbytes
*/
ulHeapSize = ulHeapSize * 1024;
/*
* Create desktop heap section and map it into the kernel
*/
SectionSize.QuadPart = ulHeapSize;
Status = MmCreateSection(&hsection,
SECTION_ALL_ACCESS,
(POBJECT_ATTRIBUTES)NULL,
&SectionSize,
PAGE_EXECUTE_READWRITE,
SEC_RESERVE,
(HANDLE)NULL,
NULL);
if (!NT_SUCCESS( Status )) {
RIPNTERR0(Status, RIP_VERBOSE, "");
return NULL;
}
ulViewSize = ulHeapSize;
*ppvHeapBase = NULL;
Status = MmMapViewInSystemSpace(hsection, ppvHeapBase, &ulViewSize);
if (!NT_SUCCESS( Status )) {
RIPNTERR0(Status, RIP_VERBOSE, "");
ObDereferenceObject(hsection);
return NULL;
}
/*
* Create desktop heap.
*/
if (UserCreateHeap(hsection, *ppvHeapBase, ulHeapSize) == NULL) {
RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_VERBOSE, "");
MmUnmapViewInSystemSpace(*ppvHeapBase);
ObDereferenceObject(hsection);
*ppvHeapBase = NULL;
return NULL;
}
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;
UserAssert(pdesk != NULL);
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)
{
PSHROBJHEAD pobj;
PDESKTOPVIEW pdv;
/*
* Validate the handle
*/
pobj = HMValidateHandle(h, TYPE_GENERIC);
if (pobj == NULL)
return NULL;
/*
* 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: can not map handle %lX", h);
return NULL;
}
return (PVOID)((PBYTE)pobj - pdv->ulClientDelta);
}
/***************************************************************************\
* MapDesktop
*
* Attempts to map a desktop heap into a process.
*
* History:
* 20-Oct-1994 JimA Created.
\***************************************************************************/
VOID MapDesktop(
OB_OPEN_REASON OpenReason,
PEPROCESS Process,
PVOID pobj,
ACCESS_MASK amGranted,
ULONG cHandles)
{
PPROCESSINFO ppi;
PDESKTOP pdesk = (PDESKTOP)pobj;
NTSTATUS Status;
ULONG ulViewSize;
LARGE_INTEGER liOffset;
PDESKTOPVIEW pdvNew;
PBYTE pClientBase;
if (Process == gpepSystem)
return;
/*
* Ignore handle inheritance because MmMapViewOfSection
* cannot be called during process creation.
*/
if (OpenReason == ObInheritHandle)
return;
/*
* If there is no ppi, we can't map the desktop
*/
ppi = PpiFromProcess(Process);
if (ppi == NULL)
return;
/*
* If the desktop has already been mapped we're done.
*/
if (GetDesktopView(ppi, pdesk) != NULL)
return;
/*
* Allocate a view of the desktop
*/
pdvNew = UserAllocPoolWithQuota(sizeof(*pdvNew), TAG_PROCESSINFO);
if (pdvNew == NULL) {
RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_VERBOSE, "");
return;
}
/*
* Read/write access has been granted. Map the desktop
* memory into the client process.
*/
ulViewSize = 0;
liOffset.QuadPart = 0;
pClientBase = NULL;
Status = MmMapViewOfSection(pdesk->hsectionDesktop, Process,
&pClientBase, 0, 0, &liOffset, &ulViewSize, ViewUnmap,
SEC_NO_CHANGE, PAGE_EXECUTE_READ);
if (!NT_SUCCESS( Status )) {
#if DBG == 1
if ( Status != STATUS_NO_MEMORY &&
Status != STATUS_PROCESS_IS_TERMINATING &&
Status != STATUS_COMMITMENT_LIMIT) {
RIPMSG1(RIP_WARNING, "MapDesktop - failed to map to client process (status == %lX). Contact ChrisWil",Status);
}
#endif
RIPNTERR0(Status, RIP_VERBOSE, "");
UserFreePool(pdvNew);
return;
}
/*
* Link the view into the ppi
*/
pdvNew->pdesk = pdesk;
pdvNew->ulClientDelta = (ULONG)((PBYTE)pdesk->hheapDesktop - pClientBase);
pdvNew->pdvNext = ppi->pdvList;
ppi->pdvList = pdvNew;
}
VOID FreeView(
PEPROCESS Process,
PDESKTOP pdesk)
{
PPROCESSINFO ppi;
NTSTATUS Status;
PDESKTOPVIEW pdv;
PDESKTOPVIEW *ppdv;
ppi = PpiFromProcess(Process);
/*
* If there is no ppi, then the process is gone and nothing
* needs to be unmapped.
*/
if (ppi != NULL) {
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) {
Status = MmUnmapViewOfSection(Process,
(PBYTE)pdesk->hheapDesktop - pdv->ulClientDelta);
UserAssert(NT_SUCCESS(Status) || Status == STATUS_PROCESS_IS_TERMINATING);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING, "FreeView unmap status = 0x%#lx", Status);
}
/*
* Unlink and delete the view.
*/
for (ppdv = &ppi->pdvList; *ppdv && *ppdv != pdv;
ppdv = &(*ppdv)->pdvNext)
;
UserAssert(*ppdv);
*ppdv = pdv->pdvNext;
UserFreePool(pdv);
}
}
}
VOID UnmapDesktop(
PEPROCESS Process,
PVOID pobj,
ACCESS_MASK amGranted,
ULONG cProcessHandles,
ULONG cSystemHandles)
{
PDESKTOP pdesk = (PDESKTOP)pobj;
BOOL fReenter;
/*
* Only unmap the desktop if this is the last process handle and
* the process is not CSR.
*/
if (cProcessHandles == 1 && Process != gpepCSRSS) {
FreeView(Process, pdesk);
}
if (cSystemHandles > 2)
return;
/*
* If we do not own the resource, get it now
*/
fReenter = !ExIsResourceAcquiredExclusiveLite(gpresUser);
if (fReenter)
EnterCrit();
if (cSystemHandles == 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 (cSystemHandles == 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:%#lx", pdesk);
}
DestroyDesktop(pdesk);
}
if (fReenter)
LeaveCrit();
}
/***************************************************************************\
* UserResetDisplayDevice
*
* Called to reset the dispaly 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 UserResetDisplayDevice(
HDEV hdev)
{
TL tlpwnd;
TRACE_INIT(("UserResetDisplayDevice: about to reset the device\n"));
vEnableDisplay(hdev);
/*
* Handle early system initialization gracefully.
*/
if (grpdeskRitInput != NULL) {
ThreadLock(grpdeskRitInput->pDeskInfo->spwnd, &tlpwnd);
xxxRedrawWindow(grpdeskRitInput->pDeskInfo->spwnd,
NULL,
NULL,
RDW_INVALIDATE | RDW_ERASE | RDW_ERASENOW |
RDW_ALLCHILDREN);
gpqCursor = NULL;
InternalSetCursorPos(ptCursor.x, ptCursor.y, grpdeskRitInput);
if (gpqCursor && gpqCursor->spcurCurrent && SYSMET(MOUSEPRESENT)) {
GreSetPointer(gpDispInfo->hDev, (PCURSINFO)&(gpqCursor->spcurCurrent->xHotspot),0);
}
ThreadUnlock(&tlpwnd);
}
TRACE_INIT(("UserResetDisplayDevice: complete\n"));
}
/***************************************************************************\
* 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;
BOOL fMapped;
/*
* If the desktop was not mapped in as a result of the open,
* fail.
*/
fMapped = (GetDesktopView(ppi, pdesk) != NULL);
if (!fMapped) {
/*
* Desktop mapping failed. Status is set by MapDesktop
*/
return FALSE;
} else {
/*
* Fail if the windowstation is locked
*/
pwinsta = pdesk->rpwinstaParent;
if (pwinsta->dwFlags & WSF_OPENLOCK &&
ppi->Process->UniqueProcessId != 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->dwFlags & WSF_SHUTDOWN) ||
RtlEqualLuid(&luidCaller, &pwinsta->luidEndSession)) {
RIPERR0(ERROR_BUSY, RIP_VERBOSE, "");
/*
* Set the shut down flag
*/
*pbShutDown = TRUE;
return FALSE;
}
}
}
SetDesktopHookFlag(ppi, hdesk, dwFlags & DF_ALLOWOTHERACCOUNTHOOK);
return TRUE;
}
/***************************************************************************\
* xxxOpenDesktop (API)
*
* Open a desktop object. This is an 'xxx' function because it leaves the
* critical section while waiting for the windowstation desktop open lock
* to be available.
*
* History:
* 16-Jan-1991 JimA Created scaffold code.
\***************************************************************************/
HDESK xxxOpenDesktop(
POBJECT_ATTRIBUTES ObjA,
DWORD dwFlags,
DWORD dwDesiredAccess,
BOOL* pbShutDown)
{
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
HDESK hdesk;
PDESKTOP pdesk;
NTSTATUS Status;
/*
* Require read/write access
*/
dwDesiredAccess |= DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS;
/*
* Open the desktop
*/
Status = ObOpenObjectByName(
ObjA,
*ExDesktopObjectType,
PreviousMode,
NULL,
dwDesiredAccess,
NULL,
&hdesk);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_VERBOSE, "");
return NULL;
}
/*
* Reference the desktop
*/
ObReferenceObjectByHandle(
hdesk,
0,
NULL,
KernelMode,
&pdesk,
NULL);
if (!NT_SUCCESS(Status)) {
ZwClose(hdesk);
RIPNTERR0(Status, RIP_VERBOSE, "");
return NULL;
}
/*
* Complete the desktop open
*/
if (!OpenDesktopCompletion(pdesk, hdesk, dwFlags, pbShutDown)) {
ZwClose(hdesk);
hdesk = NULL;
}
TRACE_INIT(("xxxOpenDesktop: Leaving\n"));
ObDereferenceObject(pdesk);
return hdesk;
}
/***************************************************************************\
* xxxSwitchDesktop (API)
*
* Switch input focus to another desktop and bring it to the top of the
* desktops
*
* bCreateNew is set when a new desktop has been created on the device, and
* when we do not want to send another enable\disable
*
* History:
* 16-Jan-1991 JimA Created scaffold code.
\***************************************************************************/
BOOL xxxSwitchDesktop(
PWINDOWSTATION pwinsta,
PDESKTOP pdesk,
BOOL bCreateNew)
{
PETHREAD Thread;
PDESKTOP pdeskSave;
HDESK hdeskSave;
PWND pwndSetForeground;
TL tlpwndChild;
TL tlpwnd;
PQ pq;
BOOL bUpdateCursor = FALSE;
PLIST_ENTRY pHead, pEntry;
PTHREADINFO pti;
PTHREADINFO ptiCurrent = PtiCurrent();
if (pwinsta == NULL)
pwinsta = _GetProcessWindowStation(NULL);
/*
* Get the windowstation, and assert if this process doesn't have one.
*/
if (pwinsta == NULL) {
UserAssert(pwinsta);
return FALSE;
}
CheckCritIn();
if (pdesk == NULL) {
return FALSE;
}
UserAssert(!(pdesk->dwDTFlags & (DF_DESTROYED | DF_DESKWNDDESTROYED | DF_DYING)));
/*
* Do tracing only if compiled in.
*/
TRACE_INIT(("xxxSwitchDesktop: Entering, desktop = %ws, createdNew = %01lx\n", POBJECT_NAME(pdesk), (DWORD)bCreateNew));
if (pwinsta->rpdeskCurrent) {
TRACE_INIT((" coming from desktop = %ws\n", POBJECT_NAME(pwinsta->rpdeskCurrent)));
}
/*
* Don't allow invisible desktops to become active
*/
if (pwinsta->dwFlags & WSF_NOIO)
return FALSE;
/*
* Wait if the logon has the windowstation locked
*/
Thread = PsGetCurrentThread();
if (!IS_SYSTEM_THREAD(Thread) && pwinsta->dwFlags & WSF_SWITCHLOCK &&
pdesk != pwinsta->rpdeskLogon &&
Thread->Cid.UniqueProcess != gpidLogon)
return FALSE;
/*
* HACKHACK LATER !!!
* Where should we really switch the desktop ...
* And we need to send repaint messages to everyone...
*
*/
if (!bCreateNew &&
(pwinsta->rpdeskCurrent) &&
(pwinsta->rpdeskCurrent->pDispInfo->hDev != pdesk->pDispInfo->hDev)) {
bDisableDisplay(pwinsta->rpdeskCurrent->pDispInfo->hDev);
vEnableDisplay(pdesk->pDispInfo->hDev);
bUpdateCursor = TRUE;
}
/*
* The current desktop is now the new desktop.
*/
pwinsta->rpdeskCurrent = pdesk;
if (pdesk == grpdeskRitInput) {
return TRUE;
}
/*
* Kill any journalling that is occuring. If an app is journaling to
* the CoolSwitch window, CancelJournalling() will kill the window.
*/
if (ptiCurrent->rpdesk != NULL)
CancelJournalling();
/*
* 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 (gptiRit->pq->QF_flags & QF_INALTTAB && gptiRit->pq->spwndAltTab != NULL) {
PWND pwndT = gptiRit->pq->spwndAltTab;
TL tlpwndT;
ThreadLockWithPti(ptiCurrent, pwndT, &tlpwndT);
xxxSendMessage(pwndT, WM_CLOSE, 0, 0);
ThreadUnlock(&tlpwndT);
}
/*
* Remove all trace of previous active window.
*/
if (grpdeskRitInput != NULL) {
if (grpdeskRitInput->pDeskInfo->spwnd != NULL) {
if (gpqForeground != NULL)
Lock(&grpdeskRitInput->spwndForeground,
gpqForeground->spwndActive);
/*
* 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.
*/
pdeskSave = ptiCurrent->rpdesk;
hdeskSave = ptiCurrent->hdesk;
SetDesktop(ptiCurrent, grpdeskRitInput, NULL);
xxxSetForegroundWindow(NULL);
SetDesktop(ptiCurrent, pdeskSave, hdeskSave);
}
}
/*
* 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;
}
}
/*
* 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);
/*
* Free any spbs that are only valid for the previous desktop.
*/
FreeAllSpbs(NULL);
/*
* Lock it into the RIT thread (we could use this desktop rather than
* the global grpdeskRitInput to direct input!)
*/
SetDesktop(gptiRit, pdesk, NULL);
/*
* 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 (pwinsta->ptiDesktop->pq != pwinsta->pqDesktop) {
UserAssert(pwinsta->pqDesktop->cThreads == 0);
AllocQueue(NULL, pwinsta->pqDesktop);
pwinsta->pqDesktop->cThreads++;
AttachToQueue(pwinsta->ptiDesktop, pwinsta->pqDesktop, NULL, FALSE);
}
SetDesktop(pwinsta->ptiDesktop, pdesk, NULL);
/*
* Bring the desktop window to the top and invalidate
* everything.
*/
ThreadLockWithPti(ptiCurrent, pdesk->pDeskInfo->spwnd, &tlpwnd);
/*
* Disable DirectDraw before we bring up the desktop window, so we make
* sure that everything is repainted properly once DirectDraw is disabled.
*/
GreDisableDirectDraw(pdesk->pDispInfo->hDev, FALSE);
xxxSetWindowPos(pdesk->pDeskInfo->spwnd,
NULL,
0,
0,
0,
0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
/*
* At this point, my understanding is that the ne 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 reenable 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
*/
GreEnableDirectDraw(pdesk->pDispInfo->hDev);
/*
* 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 {
/*
* If the new foreground window is a minimized fullscreen app,
* make it fullscreen.
*/
if (pwndSetForeground->bFullScreen == FULLSCREENMIN) {
pwndSetForeground->bFullScreen = FULLSCREEN;
}
ThreadLockAlwaysWithPti(ptiCurrent, pwndSetForeground, &tlpwndChild);
xxxSetForegroundWindow(pwndSetForeground);
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) {
_PostThreadMessage(gHardErrorHandler.pti, WM_QUIT, 0, 0);
}
/*
* Notify anyone waiting for a desktop switch.
*/
if (pwinsta->pEventSwitchNotify != NULL) {
KePulseEvent(pwinsta->pEventSwitchNotify, EVENT_INCREMENT, FALSE);
}
/*
* reset the cursor when we come back from another pdev
*/
if (bUpdateCursor == TRUE) {
gpqCursor = NULL;
InternalSetCursorPos(ptCursor.x, ptCursor.y, grpdeskRitInput);
if (gpqCursor && gpqCursor->spcurCurrent && SYSMET(MOUSEPRESENT)) {
GreSetPointer(gpDispInfo->hDev,
(PCURSINFO)&(gpqCursor->spcurCurrent->xHotspot),
0);
}
}
/*
* Make sure we come back to the right mode when this is all done, because
* the device may be left in an interesting state if we were running
* DirectDraw.
*/
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, pdesk->pDispInfo->pDevInfo->szNtDeviceName);
/*
* Don't check the return code right now since there is nothing
* we can do if we can not reset the mode ...
*/
UserChangeDisplaySettings(&us,
pdesk->pDesktopDevmode,
_GetDesktopWindow(),
pdesk,
0,
NULL,
TRUE);
}
TRACE_INIT(("xxxSwitchDesktop: Leaving\n"));
return TRUE;
}
/***************************************************************************\
* SetDesktop
*
* Set desktop and desktop info in the specified pti.
*
* History:
* 23-Dec-1993 JimA Created.
\***************************************************************************/
VOID SetDesktop(
PTHREADINFO pti,
PDESKTOP pdesk,
HDESK hdesk)
{
PTEB pteb;
OBJECT_HANDLE_INFORMATION ohi;
PDESKTOP pdeskRef;
PDESKTOP pdeskOld;
PCLIENTTHREADINFO pctiOld;
TL tlpdesk;
PTHREADINFO ptiCurrent = PtiCurrent();
if (pti == NULL) {
UserAssert(pti);
return;
}
/*
* Do nothing if the thread has initialized and the desktop
* is not changing.
*/
if ((pdesk != NULL) && (pdesk == pti->rpdesk))
return;
/*
* A handle without an object pointer is bad news.
*/
UserAssert(pdesk != NULL || hdesk == NULL);
#if DBG
/*
* Catch reset of important desktops
*/
if (pti->rpdesk && pti->rpdesk->dwConsoleThreadId == (DWORD)pti->Thread->Cid.UniqueThread &&
pti->cWindows != 0) {
RIPMSG0(RIP_ERROR, "Reset of console desktop");
}
/*
* This desktop must not be destroyed
*/
if (pdesk != NULL) {
UserAssert(!(pdesk->dwDTFlags & (DF_DESTROYED | DF_DESKWNDDESTROYED | DF_DYING)));
}
#endif
/*
* 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);
pctiOld = pti->pcti;
/*
* Remove the pti from the current desktop.
*/
if (pti->rpdesk) {
UserAssert(pti->pq == NULL || pti->pq->cThreads == 1);
RemoveEntryList(&pti->PtiLink);
}
/*
* Clear hook flag
*/
pti->TIF_flags &= ~TIF_ALLOWOTHERACCOUNTHOOK;
/*
* Get granted access
*/
pti->hdesk = hdesk;
if (hdesk != NULL) {
if (NT_SUCCESS(ObReferenceObjectByHandle(hdesk,
0,
*ExDesktopObjectType,
KernelMode,
&pdeskRef,
&ohi))) {
UserAssert(pdeskRef == pdesk);
ObDereferenceObject(pdeskRef);
pti->amdesk = ohi.GrantedAccess;
if (CheckDesktopHookFlag(pti->ppi, hdesk))
pti->TIF_flags |= TIF_ALLOWOTHERACCOUNTHOOK;
} else {
pti->amdesk = 0;
}
} else {
pti->amdesk = 0;
}
LockDesktop(&pti->rpdesk, pdesk);
/*
* 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 = gpdiStatic;
}
pteb = pti->Thread->Tcb.Teb;
if (pteb) {
PDESKTOPVIEW pdv;
if (pdesk && (pdv = GetDesktopView(pti->ppi, pdesk))) {
pti->pClientInfo->pDeskInfo =
(PDESKTOPINFO)((PBYTE)pti->pDeskInfo - pdv->ulClientDelta);
pti->pClientInfo->ulClientDelta = pdv->ulClientDelta;
} else {
pti->pClientInfo->pDeskInfo = NULL;
pti->pClientInfo->ulClientDelta = 0;
/*
* Reset the cursor level to its orginal state.
*/
pti->iCursorLevel = (oemInfo.fMouse ? 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) {
pti->pcti = DesktopAlloc(pdesk->hheapDesktop, sizeof(CLIENTTHREADINFO));
}
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);
}
if (pctiOld != NULL) {
if (pctiOld != pti->pcti) {
RtlCopyMemory(pti->pcti, pctiOld, sizeof(CLIENTTHREADINFO));
}
if (pctiOld != &(pti->cti)) {
DesktopFree(pdeskOld->hheapDesktop, pctiOld);
}
} else {
RtlZeroMemory(pti->pcti, sizeof(CLIENTTHREADINFO));
}
/*
* If journalling is occuring on the new desktop, attach to
* the journal queue.
*/
if (pdesk != NULL && pti->pq != NULL &&
!(pti->TIF_flags & TIF_DONTJOURNALATTACH) &&
(pdesk->pDeskInfo->fsHooks &
(WHF_JOURNALPLAYBACK | WHF_JOURNALRECORD))) {
PTHREADINFO ptiT;
if (pdesk->pDeskInfo->asphkStart[WH_JOURNALPLAYBACK + 1] != NULL) {
ptiT = GETPTI(pdesk->pDeskInfo->asphkStart[WH_JOURNALPLAYBACK + 1]);
} else {
ptiT = GETPTI(pdesk->pDeskInfo->asphkStart[WH_JOURNALRECORD + 1]);
}
ptiT->pq->cThreads++;
AttachToQueue(pti, ptiT->pq, NULL, FALSE);
}
ThreadUnlockDesktop(ptiCurrent, &tlpdesk);
}
/***************************************************************************\
* _SetThreadDesktop (API)
*
* Associate the current thread with a desktop.
*
* History:
* 16-Jan-1991 JimA Created stub.
\***************************************************************************/
BOOL _SetThreadDesktop(
HDESK hdesk,
PDESKTOP pdesk)
{
PTHREADINFO pti;
PPROCESSINFO ppi;
PQ pqAttach;
pti = PtiCurrent();
ppi = pti->ppi;
/*
* If the handle has not been mapped in, do it now.
*/
if (pdesk != NULL) {
MapDesktop(ObOpenHandle, ppi->Process, pdesk, 0, 1);
if (GetDesktopView(ppi, pdesk) == NULL) {
return FALSE;
}
}
/*
* Check non-system thread status
*/
if (pti->Thread->ThreadsProcess != gpepSystem &&
pti->Thread->ThreadsProcess != gpepCSRSS) {
/*
* Fail if the non-system thread has any windows or thread hooks.
*/
if (pti->cWindows != 0 || pti->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 (ppi->rpdeskStartup == NULL && hdesk != NULL) {
LockDesktop(&ppi->rpdeskStartup, pdesk);
ppi->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
* DestroyQueue from getting confused and setting ptiKeyboard
* and ptiMouse to NULL when a thread detachs.
*/
if (pti->rpdesk != pdesk && pti->pq->cThreads > 1) {
pqAttach = AllocQueue(NULL, NULL);
if (pqAttach != NULL) {
pqAttach->cThreads++;
AttachToQueue(pti, pqAttach, NULL, FALSE);
} else {
RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_WARNING, "Thread could not be detached");
return FALSE;
}
}
SetDesktop(pti, pdesk, hdesk);
return TRUE;
}
/***************************************************************************\
* xxxDuplicateObject
*
* ZwDuplicateObject grabs ObpInitKillMutant so we have to leave our
* critical section.
*
* 04-24-96 GerardoB Created
\***************************************************************************/
NTSTATUS
xxxUserDuplicateObject(
IN HANDLE SourceProcessHandle,
IN HANDLE SourceHandle,
IN HANDLE TargetProcessHandle OPTIONAL,
OUT PHANDLE TargetHandle OPTIONAL,
IN ACCESS_MASK DesiredAccess,
IN ULONG HandleAttributes,
IN ULONG Options
)
{
NTSTATUS Status;
CheckCritIn();
LeaveCrit();
Status = ZwDuplicateObject(SourceProcessHandle, SourceHandle, TargetProcessHandle,
TargetHandle, DesiredAccess, HandleAttributes, Options);
EnterCrit();
return Status;
}
/***************************************************************************\
* xxxUserFindHandleForObject
*
* ObFindHandleForObject grabs ObpInitKillMutant so we have to leave our
* critical section.
*
* 04-24-96 GerardoB Created
\***************************************************************************/
BOOLEAN
xxxUserFindHandleForObject(
IN PEPROCESS Process,
IN PVOID Object OPTIONAL,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL,
OUT PHANDLE Handle
)
{
BOOL fRet;
BOOL fExclusive, fShared;
fExclusive = ExIsResourceAcquiredExclusiveLite(gpresUser);
if (!fExclusive) {
fShared = ExIsResourceAcquiredSharedLite(gpresUser);
}
if (fExclusive || fShared) {
LeaveCrit();
}
fRet = ObFindHandleForObject(Process, Object, ObjectType, HandleInformation, Handle);
if (fExclusive) {
EnterCrit();
} else if (fShared) {
EnterSharedCrit();
}
return fRet;
}
/***************************************************************************\
* 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)
{
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) {
RIPERR0(ERROR_INVALID_PARAMETER, RIP_VERBOSE, "");
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;
#if 0
DbgPrint( "USERK: [%x.%x] %s called xxxGetThreadDesktop for [%x.%x] %ws\n",
PsGetCurrentThread()->Cid.UniqueProcess,
PsGetCurrentThread()->Cid.UniqueThread,
PsGetCurrentProcess()->ImageFileName,
ppiThread->ptiMainThread->idProcess,
ppiThread->ptiMainThread->idThread,
ppiThread->W32Process->Process->ImageFileName
);
#endif
KeAttachProcess(&ppiThread->Process->Pcb);
Status = ObReferenceObjectByHandle(hdesk, 0, NULL, KernelMode,
&pobj, &ohi);
KeDetachProcess();
if (!NT_SUCCESS(Status) ||
!xxxUserFindHandleForObject(PsGetCurrentProcess(), pobj, NULL, &ohi, &hdesk)) {
hdesk = NULL;
hdesk = NULL;
}
}
if (hdesk == NULL)
RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "");
}
return hdesk;
}
/***************************************************************************\
* xxxGetInputDesktop (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 xxxGetInputDesktop(VOID)
{
HDESK hdesk;
if (xxxUserFindHandleForObject(PsGetCurrentProcess(), grpdeskRitInput, NULL, NULL, &hdesk))
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)
{
PDESKTOP pdesk;
PTHREADINFO ptiT;
PPROCESSINFO ppi;
NTSTATUS Status;
ppi = PpiCurrent();
/*
* Get a pointer to the desktop.
*/
Status = ObReferenceObjectByHandle(
hdesk,
0,
*ExDesktopObjectType,
KernelMode,
&pdesk,
NULL);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_VERBOSE, "");
return FALSE;
}
if (ppi->Process != gpepSystem && 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) {
RIPERR0(ERROR_BUSY, RIP_WARNING, "Desktop is in use by one or more threads");
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);
ppi->hdeskStartup = NULL;
}
}
/*
* Clear hook flag
*/
SetDesktopHookFlag(ppi, hdesk, FALSE);
/*
* Close the handle
*/
Status = ZwClose(hdesk);
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;
if (pdesk->dwConsoleThreadId == 0)
return;
/*
* Locate the console thread.
*/
Status = LockThreadByClientId((HANDLE)pdesk->dwConsoleThreadId, &Thread);
if (!NT_SUCCESS(Status))
return;
/*
* Post a quit message to the console.
*/
_PostThreadMessage(PtiFromThread(Thread), WM_QUIT, 0, 0);
/*
* Clear thread id so we don't post twice
*/
pdesk->dwConsoleThreadId = 0;
UnlockThread(Thread);
}
/***************************************************************************\
* CheckDesktopHookFlag
*
* Returns TRUE if the desktop handle allows other accounts
* to hook this process.
*
* History:
* 07-13-95 JimA Created.
\***************************************************************************/
BOOL CheckDesktopHookFlag(
PPROCESSINFO ppi,
HANDLE hdesk)
{
ULONG Index = OBJ_HANDLE_TO_HANDLE_INDEX(hdesk);
return (Index < ppi->bmDesktopHookFlags.SizeOfBitMap &&
RtlCheckBit(&ppi->bmDesktopHookFlags, Index));
}
/***************************************************************************\
* SetDesktopHookFlag
*
* Sets and clears the ability of a desktop handle to allow
* other accounts to hook this process.
*
* History:
* 07-13-95 JimA Created.
\***************************************************************************/
BOOL SetDesktopHookFlag(
PPROCESSINFO ppi,
HANDLE hdesk,
BOOL fSet)
{
ULONG Index = OBJ_HANDLE_TO_HANDLE_INDEX(hdesk);
PRTL_BITMAP pbm = &ppi->bmDesktopHookFlags;
ULONG Size;
PULONG Buffer;
if (fSet) {
/*
* Expand the bitmap if needed
*/
if (Index >= pbm->SizeOfBitMap) {
/*
* Size = (Index + 0x1F) & ~0x1F;
* If index = 0x20 then the new Size will also be 0x20. RtlSetBits
* asserts on ( Index + 1 <= BitMapHeader->SizeOfBitMap).
* Therefore increase size by 1
*/
Size = ((Index + 0x1F) & ~0x1F) + 1;
Buffer = UserAllocPoolWithQuota(Size / 8, TAG_PROCESSINFO);
if (Buffer == NULL)
return FALSE;
RtlZeroMemory(Buffer, Size / 8);
if (pbm->Buffer) {
RtlCopyMemory(Buffer, pbm->Buffer, pbm->SizeOfBitMap / 8);
UserFreePool(pbm->Buffer);
}
RtlInitializeBitMap(pbm, Buffer, Size);
}
RtlSetBits(pbm, Index, 1);
} else if (Index < pbm->SizeOfBitMap) {
RtlClearBits(pbm, Index, 1);
}
return TRUE;
}
/***************************************************************************\
* xxxResolveDesktop
*
* Attempts to return handles to a windowstation and desktop associated
* with the logon session.
*
* History:
* 25-Apr-1994 JimA Created.
\***************************************************************************/
HDESK xxxResolveDesktop(
HANDLE hProcess,
PUNICODE_STRING pstrDesktop,
HWINSTA *phwinsta,
BOOL fInherit,
BOOL* pbShutDown)
{
PEPROCESS Process;
PTEB pteb = NtCurrentTeb();
PPROCESSINFO ppi;
HWINSTA hwinsta;
HDESK hdesk;
PDESKTOP pdesk;
PWINDOWSTATION pwinsta;
BOOL fInteractive;
UNICODE_STRING strDesktop;
UNICODE_STRING strWinSta;
POBJECT_ATTRIBUTES ObjA = NULL;
DWORD cbObjA;
LPWSTR pszDesktop;
WCHAR awchName[sizeof(L"Service-0x0000-0000$") / sizeof(WCHAR)];
BOOL fWinStaDefaulted;
BOOL fDesktopDefaulted;
LUID luidService;
NTSTATUS Status;
HWINSTA hwinstaDup;
Status = ObReferenceObjectByHandle(hProcess,
PROCESS_QUERY_INFORMATION,
NULL,
UserMode,
&Process,
NULL);
if (!NT_SUCCESS(Status))
return NULL;
/*
* If the process already has a windowstation and a startup desktop,
* return them.
*/
hwinsta = NULL;
hwinstaDup = NULL;
hdesk = NULL;
ppi = PpiFromProcess(Process);
if (ppi != NULL && 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)) {
ZwClose(hwinsta);
hwinsta = NULL;
}
}
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_VERBOSE, "");
}
}
ObDereferenceObject(Process);
*phwinsta = hwinsta;
return hdesk;
}
/*
* Determine windowstation and desktop names.
*/
if (pstrDesktop == NULL || pstrDesktop->Length == 0) {
RtlInitUnicodeString(&strDesktop, TEXT("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 = (pszDesktop - strWinSta.Buffer) * sizeof(WCHAR);
fWinStaDefaulted = FALSE;
pteb->StaticUnicodeString.Length = 0;
RtlAppendUnicodeToString(&pteb->StaticUnicodeString, szWindowStationDirectory);
RtlAppendUnicodeToString(&pteb->StaticUnicodeString, L"\\");
RtlAppendUnicodeStringToString(&pteb->StaticUnicodeString, &strWinSta);
if (!NT_SUCCESS(Status = _UserTestForWinStaAccess(&pteb->StaticUnicodeString,TRUE))) {
RIPMSG1(RIP_WARNING,"_UserTestForWinStaAccess failed with Status %lx\n",Status);
ObDereferenceObject(Process);
*phwinsta = NULL;
return NULL;
}
}
}
/*
* 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 != NULL) {
/*
* If the windowstation name was defaulted, create a name
* based on the session.
*/
if (fWinStaDefaulted) {
//Default Window Station
RtlInitUnicodeString(&strWinSta, L"WinSta0");
pteb->StaticUnicodeString.Length = 0;
RtlAppendUnicodeToString(&pteb->StaticUnicodeString, szWindowStationDirectory);
RtlAppendUnicodeToString(&pteb->StaticUnicodeString, L"\\");
RtlAppendUnicodeStringToString(&pteb->StaticUnicodeString, &strWinSta);
fInteractive = NT_SUCCESS(_UserTestForWinStaAccess(&pteb->StaticUnicodeString,fInherit));
if (!fInteractive) {
GetProcessLuid(NULL, &luidService);
wsprintfW(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 (xxxUserFindHandleForObject(Process, NULL, *ExWindowStationObjectType,
NULL, &hwinsta)) {
/*
* If the handle belongs to another process,
* dup it into this one
*/
if (Process != PsGetCurrentProcess()) {
Status = xxxUserDuplicateObject(
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,
NULL,
KernelMode,
&pwinsta,
NULL);
if (NT_SUCCESS(Status)) {
BOOL fIO = (pwinsta->dwFlags & WSF_NOIO) ? FALSE : TRUE;
if (fIO != fInteractive) {
if ( hwinstaDup &&
NT_SUCCESS(ProtectHandle(hwinsta, FALSE))) {
ZwClose(hwinsta);
}
hwinsta = NULL;
}
ObDereferenceObject(pwinsta);
}
}
/*
* If not, open the computed windowstation.
*/
if (NT_SUCCESS(Status) && hwinsta == NULL) {
/*
* Fill in the path to the windowstation
*/
pteb->StaticUnicodeString.Length = 0;
RtlAppendUnicodeToString(&pteb->StaticUnicodeString, szWindowStationDirectory);
RtlAppendUnicodeToString(&pteb->StaticUnicodeString, L"\\");
RtlAppendUnicodeStringToString(&pteb->StaticUnicodeString, &strWinSta);
/*
* Allocate an object attributes structure in user address space.
*/
cbObjA = sizeof(*ObjA);
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
&ObjA, 0, &cbObjA, MEM_COMMIT, PAGE_READWRITE);
if (NT_SUCCESS(Status)) {
InitializeObjectAttributes( ObjA,
&pteb->StaticUnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
if (fInherit)
ObjA->Attributes |= OBJ_INHERIT;
hwinsta = _OpenWindowStation(ObjA, MAXIMUM_ALLOWED);
}
}
/*
* 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 (NT_SUCCESS(Status) && hwinsta == NULL && !fInteractive &&
fWinStaDefaulted) {
*phwinsta = xxxConnectService(
&pteb->StaticUnicodeString,
&hdesk);
/*
* Clean up and leave.
*/
if (ObjA != NULL) {
ZwFreeVirtualMemory(NtCurrentProcess(), &ObjA, &cbObjA,
MEM_RELEASE);
}
ObDereferenceObject(Process);
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 (xxxUserFindHandleForObject(Process, NULL, *ExDesktopObjectType,
NULL, &hdesk)) {
/*
* If the handle belongs to another process,
* dup it into this one
*/
if (Process != PsGetCurrentProcess()) {
HDESK hdeskDup;
Status = xxxUserDuplicateObject(
hProcess,
hdesk,
NtCurrentProcess(),
&hdeskDup,
0,
0,
DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(Status)) {
ZwClose(hdesk);
hdesk = NULL;
} else {
hdesk = hdeskDup;
}
}
/*
* Map the desktop into the process.
*/
if (hdesk != NULL && ppi != NULL) {
Status = ObReferenceObjectByHandle(hdesk,
0,
NULL,
KernelMode,
&pdesk,
NULL);
if (NT_SUCCESS(Status)) {
MapDesktop(ObOpenHandle, Process, pdesk, 0, 1);
if (GetDesktopView(ppi, pdesk) == NULL) {
Status = STATUS_NO_MEMORY;
ZwClose(hdesk);
hdesk = NULL;
}
ObDereferenceObject(pdesk);
} else {
ZwClose(hdesk);
hdesk = NULL;
}
}
}
}
/*
* If not, open the desktop.
*/
if (NT_SUCCESS(Status) && hdesk == NULL) {
RtlCopyUnicodeString(&pteb->StaticUnicodeString, &strDesktop);
if (ObjA == NULL) {
cbObjA = sizeof(*ObjA);
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
&ObjA, 0, &cbObjA, MEM_COMMIT, PAGE_READWRITE);
}
if (NT_SUCCESS(Status)) {
InitializeObjectAttributes( ObjA,
&pteb->StaticUnicodeString,
OBJ_CASE_INSENSITIVE,
hwinsta,
NULL
);
if (fInherit)
ObjA->Attributes |= OBJ_INHERIT;
hdesk = xxxOpenDesktop(ObjA, 0, MAXIMUM_ALLOWED, pbShutDown);
}
}
}
if (hdesk == NULL) {
ZwClose(hwinsta);
hwinsta = NULL;
}
}
ObDereferenceObject(Process);
if (ObjA != NULL) {
ZwFreeVirtualMemory(NtCurrentProcess(), &ObjA, &cbObjA,
MEM_RELEASE);
}
*phwinsta = hwinsta;
return hdesk;
}