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

6869 lines
221 KiB

/****************************** Module Header ******************************\
* Module Name: queue.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* This module contains the low-level code for working with the Q structure.
*
* History:
* 12-02-90 DavidPe Created.
* 02-06-91 IanJa HWND revalidation added
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
VOID DestroyProcessesObjects(PPROCESSINFO ppi);
VOID DestroyThreadsMessages(PQ pq, PTHREADINFO pti);
NTSTATUS CheckProcessForeground(PTHREADINFO pti);
DWORD xxxPollAndWaitForSingleObject(PKEVENT pEvent, PVOID pExecObject,
DWORD dwMilliseconds);
NTSTATUS InitiateShutdown(PETHREAD Thread, PULONG lpdwFlags);
NTSTATUS EndShutdown(PETHREAD Thread, NTSTATUS StatusShutdown);
VOID SetVDMCursorBounds(LPRECT lprc);
NTSTATUS InitQEntryLookaside(VOID);
VOID SetAppStarting(PPROCESSINFO ppi);
#if defined(_WIN64)
/*
* For Win64 ask winlogon to play sounds for accessibility events
* (IA64 machines do not have internal speakers so we have to go
* thru the sound card). Post a message to winlogon with an lParam
* whose high word is ACCESS_SOUND_RANGE and low word is the index
* of the sound to make. If a new RITSOUND_xx macro is added then
* winlogon has to be updated too.
*/
#define ACCESS_SOUND_RANGE 1
#endif
#pragma alloc_text(INIT, InitQEntryLookaside)
PW32PROCESS gpwpCalcFirst;
PPAGED_LOOKASIDE_LIST QLookaside;
PPAGED_LOOKASIDE_LIST QEntryLookaside;
#if DBG
VOID DebugValidateMLIST(
PMLIST pml)
{
int c;
PQMSG pqmsg;
/*
* Check that the message list is properly terminated.
*/
UserAssert(!pml->pqmsgRead || !pml->pqmsgRead->pqmsgPrev);
UserAssert(!pml->pqmsgWriteLast || !pml->pqmsgWriteLast->pqmsgNext);
/*
* Check that there aren't loops in the Next list.
*/
c = pml->cMsgs;
UserAssert(c >= 0);
pqmsg = pml->pqmsgRead;
while (--c >= 0) {
UserAssert(pqmsg);
if (c == 0) {
UserAssert(pqmsg == pml->pqmsgWriteLast);
}
pqmsg = pqmsg->pqmsgNext;
}
UserAssert(!pqmsg);
/*
* Check that there aren't loops in the Prev list.
*/
c = pml->cMsgs;
pqmsg = pml->pqmsgWriteLast;
while (--c >= 0) {
UserAssert(pqmsg);
if (c == 0) {
UserAssert(pqmsg == pml->pqmsgRead);
}
pqmsg = pqmsg->pqmsgPrev;
}
UserAssert(!pqmsg);
}
VOID DebugValidateMLISTandQMSG(
PMLIST pml,
PQMSG pqmsg)
{
PQMSG pqmsgT;
DebugValidateMLIST(pml);
for (pqmsgT = pml->pqmsgRead; pqmsgT; pqmsgT = pqmsgT->pqmsgNext) {
if (pqmsgT == pqmsg) {
return;
}
}
UserAssert(pqmsgT == pqmsg);
}
#else
#define DebugValidateMLIST(pml)
#define DebugValidateMLISTandQMSG(pml, pqmsg)
#endif
VOID
_AllowForegroundActivation(
VOID)
{
SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
TAGMSG0(DBGTAG_FOREGROUND, "AllowSetForegroundWindows set PUDF.");
}
/***************************************************************************\
* xxxSetProcessInitState
*
* Set process initialization state. What state is set depends
* on whether another process is waiting on this process.
*
* 04-02-95 JimA Created.
\***************************************************************************/
BOOL xxxSetProcessInitState(
PEPROCESS Process,
DWORD dwFlags)
{
PW32PROCESS W32Process;
NTSTATUS Status;
CheckCritIn();
UserAssert(IsWinEventNotifyDeferredOK());
/*
* If the W32Process structure has not been allocated, do it now.
*/
W32Process = (PW32PROCESS)PsGetProcessWin32Process(Process);
if (W32Process == NULL) {
Status = AllocateW32Process(Process);
if (!NT_SUCCESS(Status)) {
return FALSE;
}
W32Process = (PW32PROCESS)PsGetProcessWin32Process(Process);
#if DBG
/*
* The above AllocateW32Process(Process, FALSE) won't set the
* W32PF_PROCESSCONNECTED flag (and if it wasn't previously set),
* make sure we're not on the gppiStarting list, because if we are,
* we will not be removed without the W32PF_PROCESSCONNECTED bit.
*/
if ((W32Process->W32PF_Flags & W32PF_PROCESSCONNECTED) == 0) {
UserAssert((W32Process->W32PF_Flags & W32PF_APPSTARTING) == 0);
}
#endif
}
/*
* Defer WinEvent notifications, because the thread isn't initialized yet.
*/
DeferWinEventNotify();
if (dwFlags == 0) {
if (!(W32Process->W32PF_Flags & W32PF_WOW)) {
/*
* Check to see if the startglass is on, and if so turn it off and update.
*/
if (W32Process->W32PF_Flags & W32PF_STARTGLASS) {
W32Process->W32PF_Flags &= ~W32PF_STARTGLASS;
zzzCalcStartCursorHide(NULL, 0);
}
/*
* Found it. Set the console bit and reset the wait event so any sleepers
* wake up.
*/
W32Process->W32PF_Flags |= W32PF_CONSOLEAPPLICATION;
SET_PSEUDO_EVENT(&W32Process->InputIdleEvent);
}
} else if (!(W32Process->W32PF_Flags & W32PF_INITIALIZED)) {
W32Process->W32PF_Flags |= W32PF_INITIALIZED;
/*
* Set global state to allow the new process to become
* foreground. xxxInitProcessInfo() will set
* W32PF_ALLOWFOREGROUNDACTIVATE when the process initializes.
*/
SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
TAGMSG1(DBGTAG_FOREGROUND, "xxxSetProcessInitState set PUDF. %#p", W32Process);
/*
* If this is the win32 server process, force off start glass feedback
*/
if (Process == gpepCSRSS) {
dwFlags |= STARTF_FORCEOFFFEEDBACK;
}
/*
* Show the app start cursor for 2 seconds if it was requested from
* the application.
*/
if (dwFlags & STARTF_FORCEOFFFEEDBACK) {
W32Process->W32PF_Flags |= W32PF_FORCEOFFFEEDBACK;
zzzCalcStartCursorHide(NULL, 0);
} else if (dwFlags & STARTF_FORCEONFEEDBACK) {
zzzCalcStartCursorHide(W32Process, 2000);
}
}
/*
* Have to defer without processing, because we don't have a ptiCurrent yet
*/
EndDeferWinEventNotifyWithoutProcessing();
return TRUE;
}
/***************************************************************************\
* CheckAllowForeground
*
* Bug 273518 - joejo
*
* Removed this loop from xxxInitProcessInfo to allow code shareing between
* that function and xxxUserNotifyConsoleApplication. This will allow console
* windows to set foreground correctly on new process' it launches, as opposed
* it just forcing foreground.
\***************************************************************************/
BOOL CheckAllowForeground(
PEPROCESS pep)
{
BOOL fCreator = TRUE;
HANDLE hpid = PsGetProcessInheritedFromUniqueProcessId(pep);
LUID luid;
PACCESS_TOKEN pat;
PEPROCESS pepParent;
PPROCESSINFO ppiParent;
UINT uAncestors = 0;
BOOL fAllowForeground = FALSE;
NTSTATUS Status;
do {
/*
* Get the ppi for the parent process.
*/
Status = LockProcessByClientId(hpid, &pepParent);
if (!NT_SUCCESS(Status)) {
/*
* Bug 294193 - joejo
*
* If this is a process that was created after it'a creator was
* destroyed, then lets attempt to give it foreground. This is a
* typical scenario when a stub exe trys to create another process
* in it's place.
*/
CheckForegroundActivateRight:
if (HasForegroundActivateRight(PsGetProcessInheritedFromUniqueProcessId(pep))) {
fAllowForeground = TRUE;
}
break;
}
ppiParent = PpiFromProcess(pepParent);
if (ppiParent == NULL) {
UnlockProcess(pepParent);
goto CheckForegroundActivateRight;
}
/*
* If we're walking the parent chain,
* stop when we get to the shell or to a process that
* is not running on the IO winsta
*/
if (!fCreator
&& (IsShellProcess(ppiParent)
|| ((ppiParent->rpwinsta != NULL)
&& (ppiParent->rpwinsta->dwWSF_Flags & WSF_NOIO)))) {
UnlockProcess(pepParent);
break;
}
fAllowForeground = CanForceForeground(ppiParent FG_HOOKLOCK_PARAM(NULL));
if (!fAllowForeground) {
/*
* Bug 285639 - joejo
*
* If the first thread of the parent process has allow set foreground
* than we allow the setting of the foreground.
*/
if (ppiParent->ptiList != NULL
&& (ppiParent->ptiList->TIF_flags & TIF_ALLOWFOREGROUNDACTIVATE)) {
fAllowForeground = TRUE;
}
if (!fAllowForeground) {
/*
* Let's try an ancestor (this might be a worker process).
*/
hpid = PsGetProcessInheritedFromUniqueProcessId(pepParent);
/*
* If this is launched by a system process, let it come to
* the foreground (i.e. CSRSS launching an OLE server).
*/
if (fCreator) {
fCreator = FALSE;
pat = PsReferencePrimaryToken(pepParent);
if (pat != NULL) {
Status = SeQueryAuthenticationIdToken(pat, &luid);
if (NT_SUCCESS(Status)) {
fAllowForeground = RtlEqualLuid(&luid, &luidSystem);
/*
* If it is a system process, give it the
* permanent right so we won't have to check
* its luid again
*/
if (fAllowForeground) {
ppiParent->W32PF_Flags |= W32PF_ALLOWSETFOREGROUND;
}
}
ObDereferenceObject(pat);
}
}
}
}
UnlockProcess(pepParent);
/*
* InheritedFromUniqueProcessId cannot be quite trusted because
* process ids get reused very often. So we just check few levels up
*/
} while (!fAllowForeground && (uAncestors++ < 5));
return fAllowForeground || GiveUpForeground();
}
/***************************************************************************\
* xxxUserNotifyConsoleApplication
*
* This is called by the console init code - it tells us that the starting
* application is a console application. We want to know this for various
* reasons, one being that WinExec() doesn't wait on a starting console
* application.
*
* 09-18-91 ScottLu Created.
* 01-12-99 JoeJo Bug 273518
\***************************************************************************/
VOID xxxUserNotifyConsoleApplication(
PCONSOLE_PROCESS_INFO pcpi)
{
NTSTATUS Status;
PEPROCESS Process;
BOOL retval;
/*
* First search for this process in our process information list.
*/
Status = LockProcessByClientId(LongToHandle(pcpi->dwProcessID), &Process);
if (!NT_SUCCESS(Status)) {
RIPMSGF2(RIP_WARNING,
"Failed with Process ID == 0x%x, Status = 0x%x",
pcpi->dwProcessID,
Status);
return;
}
retval = xxxSetProcessInitState(Process, 0);
/*
* Bug 273518 - joejo
*
* This will allow console windows to set foreground correctly on new
* process' it launches, as opposed it just forcing foreground.
*/
if (retval) {
if (pcpi->dwFlags & CPI_NEWPROCESSWINDOW) {
PPROCESSINFO ppiCurrent = PpiCurrent();
if (CheckAllowForeground(Process)) {
if (!(ppiCurrent->W32PF_Flags & W32PF_APPSTARTING)) {
SetAppStarting(ppiCurrent);
}
SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
TAGMSG0(DBGTAG_FOREGROUND, "xxxUserNotifyConsoleApplication set PUDF");
ppiCurrent->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
}
TAGMSG3(DBGTAG_FOREGROUND, "xxxUserNotifyConsoleApplication %s W32PF %#p-%#p",
((ppiCurrent->W32PF_Flags & W32PF_ALLOWFOREGROUNDACTIVATE) ? "set" : "NOT"),
ppiCurrent, PpiFromProcess(Process));
}
} else {
RIPMSG1(RIP_WARNING, "xxxUserNotifyConsoleApplication - SetProcessInitState failed on %#p", Process);
}
UnlockProcess(Process);
}
/***************************************************************************\
* UserSetConsoleProcessWindowStation
*
* This is called by the console init code - it tells us that the starting
* application is a console application and which window station they are
* associated with. The window station pointer is stored in the EPROCESS for
* the Global atom calls to find the correct global atom table when called from
* a console application
*
\***************************************************************************/
VOID UserSetConsoleProcessWindowStation(
DWORD idProcess,
HWINSTA hwinsta)
{
NTSTATUS Status;
PEPROCESS Process;
/*
* First search for this process in our process information list.
*/
Status = LockProcessByClientId(LongToHandle(idProcess), &Process);
if (!NT_SUCCESS(Status)) {
RIPMSGF2(RIP_WARNING, "Failed with Process ID == 0x%x, Status = 0x%x",
idProcess,
Status);
return;
}
PsSetProcessWindowStation(Process, hwinsta);
UnlockProcess(Process);
}
/***************************************************************************\
* xxxUserNotifyProcessCreate
*
* This is a special notification that we get from the base while process data
* structures are being created, but before the process has started. We use
* this notification for startup synchronization matters (winexec, startup
* activation, type ahead, etc).
*
* This notification is called on the server thread for the client thread
* starting the process.
*
* 09-09-91 ScottLu Created.
\***************************************************************************/
BOOL xxxUserNotifyProcessCreate(
DWORD idProcess,
DWORD idParentThread,
ULONG_PTR dwData,
DWORD dwFlags)
{
PEPROCESS Process;
PETHREAD Thread;
PTHREADINFO pti;
NTSTATUS Status;
BOOL retval;
CheckCritIn();
GiveForegroundActivateRight(LongToHandle(idProcess));
/*
* 0x1 bit means give feedback (app start cursor).
* 0x2 bit means this is a gui app (meaning, call CreateProcessInfo()
* so we get app start synchronization (WaitForInputIdle()).
* 0x8 bit means this process is a WOW process, set W32PF_WOW. 0x1
* and 0x2 bits will also be set.
* 0x4 value means this is really a shared WOW task starting
*/
/*
* If we want feedback, we need to create a process info structure,
* so do it: it will be properly cleaned up.
*/
if ((dwFlags & 0xb) != 0) {
Status = LockProcessByClientId(LongToHandle(idProcess), &Process);
if (!NT_SUCCESS(Status)) {
RIPMSGF2(RIP_WARNING,
"Failed with Process ID == 0x%x, Status = 0x%x",
idProcess,
Status);
return FALSE;
}
retval = xxxSetProcessInitState(Process, ((dwFlags & 1) ? STARTF_FORCEONFEEDBACK : STARTF_FORCEOFFFEEDBACK));
if (!retval) {
RIPMSG1(RIP_WARNING, "xxxUserNotifyProcessCreate - SetProcessInitState failed on %#p", Process);
}
if (dwFlags & 0x8) {
PPROCESSINFO ppi;
ppi = PsGetProcessWin32Process(Process);
if (ppi != NULL)
ppi->W32PF_Flags |= W32PF_WOW;
}
UnlockProcess(Process);
/*
* Find out who is starting this app. If it is a 16 bit app, allow
* it to bring itself back to the foreground if it calls
* SetActiveWindow() or SetFocus(). This is because this could be
* related to OLE to DDE activation. Notes has a case where after it
* lauches pbrush to edit an embedded bitmap, it brings up a message
* box on top if the bitmap is read only. This message box won't appear
* foreground unless we allow it to. This usually isn't a problem
* because most apps don't bring up windows on top of editors
* like this. 32 bit apps will call SetForegroundWindow().
*/
Status = LockThreadByClientId(LongToHandle(idParentThread), &Thread);
if (!NT_SUCCESS(Status)) {
RIPMSGF2(RIP_WARNING,
"Failed with Thread ID == 0x%x, Status = 0x%x",
idParentThread,
Status);
return FALSE;
}
pti = PtiFromThread(Thread);
if (pti && (pti->TIF_flags & TIF_16BIT)) {
pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
TAGMSG1(DBGTAG_FOREGROUND,
"xxxUserNotifyProcessCreate set TIF on pti 0x%p",
pti);
}
UnlockThread(Thread);
} else if (dwFlags == 4) {
/*
* A WOW task is starting up. Create the WOW per thread info
* structure here in case someone calls WaitForInputIdle
* before the thread is created.
*/
PWOWTHREADINFO pwti;
/*
* Look for a matching thread in the WOW thread info list.
*/
for (pwti = gpwtiFirst; pwti != NULL; pwti = pwti->pwtiNext) {
if (pwti->idTask == idProcess) {
break;
}
}
/*
* If we didn't find one, allocate a new one and add it to
* the head of the list.
*/
if (pwti == NULL) {
pwti = (PWOWTHREADINFO)UserAllocPoolWithQuota(
sizeof(WOWTHREADINFO), TAG_WOWTHREADINFO);
if (pwti == NULL) {
return FALSE;
}
INIT_PSEUDO_EVENT(&pwti->pIdleEvent);
pwti->idTask = idProcess;
pwti->pwtiNext = gpwtiFirst;
gpwtiFirst = pwti;
} else {
RESET_PSEUDO_EVENT(&pwti->pIdleEvent);
}
pwti->idWaitObject = dwData;
Status = LockThreadByClientId(LongToHandle(idParentThread), &Thread);
if (!NT_SUCCESS(Status)) {
RIPMSGF2(RIP_WARNING,
"Failed with Thread ID == 0x%x, Status = 0x%x",
idParentThread,
Status);
return FALSE;
}
pwti->idParentProcess = HandleToUlong(PsGetThreadProcessId(Thread));
UnlockThread(Thread);
}
return TRUE;
}
/***************************************************************************\
* zzzCalcStartCursorHide
*
* Calculates when to hide the startup cursor.
*
* 05-14-92 ScottLu Created.
\***************************************************************************/
VOID zzzCalcStartCursorHide(
PW32PROCESS pwp,
DWORD timeAdd)
{
DWORD timeNow = NtGetTickCount();
PW32PROCESS pwpT;
PW32PROCESS *ppwpT;
if (pwp != NULL) {
/*
* We were passed in a timeout. Recalculate when we timeout and add
* the pwp to the starting list.
*/
if (!(pwp->W32PF_Flags & W32PF_STARTGLASS)) {
/*
* Add it to the list only if it is not already in the list.
*/
for (pwpT = gpwpCalcFirst; pwpT != NULL; pwpT = pwpT->NextStart) {
if (pwpT == pwp) {
break;
}
}
if (pwpT != pwp) {
pwp->NextStart = gpwpCalcFirst;
gpwpCalcFirst = pwp;
}
}
pwp->StartCursorHideTime = timeAdd + timeNow;
pwp->W32PF_Flags |= W32PF_STARTGLASS;
}
gtimeStartCursorHide = 0;
for (ppwpT = &gpwpCalcFirst; (pwpT = *ppwpT) != NULL;) {
/*
* If the app isn't starting or feedback is forced off, remove
* it from the list so we don't look at it again.
*/
if (!(pwpT->W32PF_Flags & W32PF_STARTGLASS) ||
(pwpT->W32PF_Flags & W32PF_FORCEOFFFEEDBACK)) {
*ppwpT = pwpT->NextStart;
continue;
}
/*
* Find the greatest hide cursor timeout value.
*/
if (gtimeStartCursorHide < pwpT->StartCursorHideTime) {
gtimeStartCursorHide = pwpT->StartCursorHideTime;
}
/*
* If this app has timed out, it isn't starting anymore! Remove it
* from the list.
*/
if (ComputeTickDelta(timeNow, pwpT->StartCursorHideTime) > 0) {
pwpT->W32PF_Flags &= ~W32PF_STARTGLASS;
*ppwpT = pwpT->NextStart;
continue;
}
/*
* Step to the next pwp in the list.
*/
ppwpT = &pwpT->NextStart;
}
/*
* If the hide time is still less than the current time, then turn off
* the app starting cursor.
*/
if (gtimeStartCursorHide <= timeNow) {
gtimeStartCursorHide = 0;
}
/*
* Update the cursor image with the new info (doesn't do anything unless
* the cursor is really changing).
*/
zzzUpdateCursorImage();
}
#define QUERY_VALUE_BUFFER 80
/*
* Install hack.
*
* We have a hack inherited from Chicago that allows the shell to
* clean up registry information after a setup program runs. A
* setup program is defined as an app with one of a list of names.
*/
PUNICODE_STRING gpastrSetupExe; // These are initialized in the routine
int giSetupExe; // CreateSetupNameArray in setup.c
/***************************************************************************\
* SetAppImeCompatFlags - NOTE pstrModName->Buffer must be zero terminated.
*
*
* History:
* 07-17-97 DaveHart Split from SetAppCompatFlags -- misleadingly it also
* returns a BOOL indicating whether the filename is
* recognized as a setup program. Used by SetAppCompatFlags
* for 32-bit apps and zzzInitTask for 16-bit ones.
\***************************************************************************/
BOOL SetAppImeCompatFlags(
PTHREADINFO pti,
PUNICODE_STRING pstrModName,
PUNICODE_STRING pstrBaseFileName)
{
DWORD dwImeFlags = 0;
WCHAR szHex[QUERY_VALUE_BUFFER];
WORD wPrimaryLangID;
LCID lcid;
int iSetup;
BOOL fSetup = FALSE;
int iAppName;
int cAppNames;
PUNICODE_STRING rgpstrAppNames[2];
UNICODE_STRING strHex;
/*
* Because can't access pClientInfo of another process
*/
UserAssert(pti->ppi == PpiCurrent());
/*
* Because it is used as a zero-terminated profile key name.
*/
UserAssert(pstrModName->Buffer[pstrModName->Length / sizeof(WCHAR)] == 0);
if (FastGetProfileStringW(
NULL,
PMAP_IMECOMPAT,
pstrModName->Buffer,
NULL,
szHex,
ARRAY_SIZE(szHex),
0)) {
/*
* Found some flags. Attempt to convert the hex string
* into numeric value. Specify base 0, so
* RtlUnicodeStringToInteger will handle the 0x format.
*/
RtlInitUnicodeString(&strHex, szHex);
RtlUnicodeStringToInteger(&strHex, 0, (PULONG)&dwImeFlags);
}
/*
* If current layout is not IME layout, we don't need to get
* compatible flags for IME. But now, we don't have any scheme
* to get this flags when the keyboard layout is switched. Then
* we get it here, even this flags are not nessesary for non-IME
* keyboard layouts.
*/
ZwQueryDefaultLocale(FALSE, &lcid);
wPrimaryLangID = PRIMARYLANGID(lcid);
if ((wPrimaryLangID == LANG_KOREAN || wPrimaryLangID == LANG_JAPANESE) &&
(LOWORD(pti->dwExpWinVer) <= VER31)) {
/*
* IME compatibility flags are needed even if it's a 32 bit app.
*/
pti->ppi->dwImeCompatFlags = dwImeFlags;
} else {
pti->ppi->dwImeCompatFlags = dwImeFlags & (IMECOMPAT_NOFINALIZECOMPSTR | IMECOMPAT_HYDRACLIENT);
if (dwImeFlags & IMECOMPAT_NOFINALIZECOMPSTR) {
RIPMSG1(RIP_WARNING, "IMECOMPAT_NOFINALIZECOMPSTR is set to ppi=%#p", pti->ppi);
}
if (dwImeFlags & IMECOMPAT_HYDRACLIENT) {
RIPMSG1(RIP_WARNING, "IMECOMPAT_HYDRACLIENT is set to ppi=%#p", pti->ppi);
}
}
if (gpastrSetupExe == NULL) {
return fSetup;
}
rgpstrAppNames[0] = pstrModName;
cAppNames = 1;
if (pstrBaseFileName) {
rgpstrAppNames[1] = pstrBaseFileName;
cAppNames = 2;
}
for (iAppName = 0; iAppName < cAppNames && !fSetup; iAppName++) {
iSetup = 0;
while (iSetup < giSetupExe) {
if (RtlCompareUnicodeString(rgpstrAppNames[iAppName], &(gpastrSetupExe[iSetup]), TRUE) == 0) {
fSetup = TRUE;
break;
}
iSetup++;
}
}
return fSetup;
}
/***************************************************************************\
* SetAppCompatFlags
*
*
* History:
* 03-23-92 JimA Created.
* 07-17-97 FritzS add return for fSetup -- returns TRUE if app is a setup app.
* 09-03-97 DaveHart Split out IME, WOW doesn't use this function anymore.
* 07-14-98 MCostea Add Compatibility2 flags
* 01-21-99 MCostea Add DesiredOSVersion
\***************************************************************************/
BOOL SetAppCompatFlags(
PTHREADINFO pti)
{
DWORD dwFlags = 0;
DWORD dwFlags2 = 0;
WCHAR szHex[QUERY_VALUE_BUFFER];
WCHAR szKey[90];
WCHAR *pchStart, *pchEnd;
DWORD cb;
PUNICODE_STRING pstrAppName;
UNICODE_STRING strKey;
UNICODE_STRING strImageName;
/*
* Because can't access pClientInfo of another process.
*/
UserAssert(pti->ppi == PpiCurrent());
UserAssert(pti->ppi->ptiList);
UserAssert(!(pti->TIF_flags & TIF_16BIT));
/*
* We assume here that pti was just inserted in at the head of ptiList
*/
UserAssert(pti == pti->ppi->ptiList);
try {
PPEB ppeb = PsGetProcessPeb(PsGetThreadProcess(pti->pEThread));
struct _RTL_USER_PROCESS_PARAMETERS *ProcessParameters;
if (pti->ptiSibling) {
pti->pClientInfo->dwCompatFlags = pti->dwCompatFlags = pti->ptiSibling->dwCompatFlags;
pti->pClientInfo->dwCompatFlags2 = pti->dwCompatFlags2 = pti->ptiSibling->dwCompatFlags2;
return FALSE;
}
/*
* Find end of app name
*/
if (pti->pstrAppName != NULL) {
pstrAppName = pti->pstrAppName;
} else {
ProbeForRead(ppeb, sizeof(PEB), sizeof(BYTE));
ProcessParameters = ppeb->ProcessParameters;
ProbeForRead(ProcessParameters, sizeof(*ProcessParameters), sizeof(BYTE));
strImageName = ProbeAndReadUnicodeString(&ProcessParameters->ImagePathName);
ProbeForReadUnicodeStringBuffer(strImageName);
pstrAppName = &strImageName;
}
pchStart = pchEnd = pstrAppName->Buffer +
(pstrAppName->Length / sizeof(WCHAR));
/*
* Locate start of extension
*/
while (TRUE) {
if (pchEnd == pstrAppName->Buffer) {
pchEnd = pchStart;
break;
}
if (*pchEnd == TEXT('.')) {
break;
}
pchEnd--;
}
/*
* Locate start of filename
*/
pchStart = pchEnd;
while (pchStart != pstrAppName->Buffer) {
if (*pchStart == TEXT('\\') || *pchStart == TEXT(':')) {
pchStart++;
break;
}
pchStart--;
}
#define MODULESUFFIXSIZE (8 * sizeof(WCHAR))
#define MAXMODULENAMELEN (sizeof(szKey) - MODULESUFFIXSIZE)
/*
* Get a copy of the filename
* Allow extra spaces for the 'ImageSubsystemMajorVersionMinorVersion'
* i.e. 3.5 that will get appended at the end of the module name
*/
cb = (DWORD)(pchEnd - pchStart) * sizeof(WCHAR);
if (cb >= MAXMODULENAMELEN)
cb = MAXMODULENAMELEN - sizeof(WCHAR);
RtlCopyMemory(szKey, pchStart, cb);
/*
* Get the compat2 flags from the PEB. The appcompat infrastructure
* gets the flags from the shim database.
*/
pti->dwCompatFlags2 = ppeb->AppCompatFlagsUser.LowPart;
pti->pClientInfo->dwCompatFlags2 = pti->dwCompatFlags2;
} except (W32ExceptionHandler(FALSE, RIP_ERROR)) {
return FALSE;
}
szKey[(cb / sizeof(WCHAR))] = 0;
#undef MAXMODULENAMELEN
if (FastGetProfileStringW(
NULL,
PMAP_COMPAT32,
szKey,
NULL,
szHex,
ARRAY_SIZE(szHex),
0)) {
UNICODE_STRING strHex;
/*
* Found some flags. Attempt to convert the hex string
* into numeric value. Specify base 0, so
* RtlUnicodeStringToInteger will handle the 0x format
*/
RtlInitUnicodeString(&strHex, szHex);
RtlUnicodeStringToInteger(&strHex, 0, (PULONG)&dwFlags);
}
try {
pti->pClientInfo->dwCompatFlags = dwFlags;
} except (W32ExceptionHandler(FALSE, RIP_ERROR)) {
return FALSE;
}
pti->dwCompatFlags = dwFlags;
/*
* Restore the string
*/
szKey[(cb / sizeof(WCHAR))] = 0;
RtlInitUnicodeString(&strKey, szKey);
return SetAppImeCompatFlags(pti, &strKey, NULL);
}
/***************************************************************************\
* GetAppCompatFlags
*
* Compatibility flags for < Win 3.1 apps running on 3.1
*
* History:
* 04-??-92 ScottLu Created.
* 05-04-92 DarrinM Moved to USERRTL.DLL.
\***************************************************************************/
DWORD GetAppCompatFlags(
PTHREADINFO pti)
{
/*
* GRE calls this with pti == NULL.
*/
if (pti == NULL) {
pti = PtiCurrentShared();
}
return pti->dwCompatFlags;
}
/***************************************************************************\
* GetAppCompatFlags2
*
* Compatibility flags for < wVer apps
*
* History:
* 07-01-98 MCostea Created.
\***************************************************************************/
DWORD GetAppCompatFlags2(
WORD wVer)
{
return GetAppCompatFlags2ForPti(PtiCurrentShared(), wVer);
}
DWORD GetAppImeCompatFlags(
PTHREADINFO pti)
{
if (pti == NULL) {
pti = PtiCurrentShared();
}
return pti->ppi->dwImeCompatFlags;
}
/***************************************************************************\
* CheckAppStarting
*
* This is a timer proc (see SetAppStarting) which removes ppi's from the
* starting list once their initialization time has expired.
*
* History:
* 08/26/97 GerardoB Created
\***************************************************************************/
VOID CheckAppStarting(
PWND pwnd,
UINT message,
UINT_PTR nID,
LPARAM lParam)
{
LARGE_INTEGER liStartingTimeout;
PPROCESSINFO *pppi = &gppiStarting;
KeQuerySystemTime(&liStartingTimeout); /* 1 unit == 100ns */
liStartingTimeout.QuadPart -= (LONGLONG)(CMSAPPSTARTINGTIMEOUT * 10000);
while (*pppi != NULL) {
if (liStartingTimeout.QuadPart > PsGetProcessCreateTimeQuadPart((*pppi)->Process)) {
(*pppi)->W32PF_Flags &= ~(W32PF_APPSTARTING | W32PF_ALLOWFOREGROUNDACTIVATE);
TAGMSG1(DBGTAG_FOREGROUND, "CheckAppStarting clear W32PF %#p", *pppi);
*pppi = (*pppi)->ppiNext;
} else {
pppi = &(*pppi)->ppiNext;
}
}
TAGMSG0(DBGTAG_FOREGROUND, "Removing all entries from ghCanActivateForegroundPIDs array");
RtlZeroMemory(ghCanActivateForegroundPIDs, sizeof(ghCanActivateForegroundPIDs));
UNREFERENCED_PARAMETER(pwnd);
UNREFERENCED_PARAMETER(message);
UNREFERENCED_PARAMETER(nID);
UNREFERENCED_PARAMETER(lParam);
}
/***************************************************************************\
* SetAppStarting
*
* Add a process to the starting list and mark it as such. The process will
* remain in the list until it activates a window, our timer goes off or the
* process goes away, whichever happens first.
*
* History:
* 08/26/97 GerardoB Created
\***************************************************************************/
VOID SetAppStarting(
PPROCESSINFO ppi)
{
static UINT_PTR guAppStartingId = 0;
/*
* This ppi had better not be in the list already, or we will be creating
* a loop (as seen in stress).
*/
UserAssert((ppi->W32PF_Flags & W32PF_APPSTARTING) == 0);
/*
* If we add this to the gppiStartingList without this bit set, we will
* skip removing it from the list in DestroyProcessInfo(), but continue
* to free it in FreeW32Process called by W32pProcessCallout.
*/
UserAssert((ppi->W32PF_Flags & W32PF_PROCESSCONNECTED));
ppi->W32PF_Flags |= W32PF_APPSTARTING;
ppi->ppiNext = gppiStarting;
gppiStarting = ppi;
/*
* Some system processes are initialized before the RIT has setup the master
* timer, so check for it.
*/
if (gptmrMaster != NULL) {
guAppStartingId = InternalSetTimer(NULL, guAppStartingId,
CMSAPPSTARTINGTIMEOUT + CMSHUNGAPPTIMEOUT,
CheckAppStarting, TMRF_RIT | TMRF_ONESHOT);
}
}
/***************************************************************************\
* ClearAppStarting
*
* Remove a process from the app starting list and clear the W32PF_APPSTARTING
* flag. No major action here, just a centralized place to take care of this.
*
* History:
* 08/26/97 GerardoB Created
\***************************************************************************/
VOID ClearAppStarting(
PPROCESSINFO ppi)
{
REMOVE_FROM_LIST(PROCESSINFO, gppiStarting, ppi, ppiNext);
ppi->W32PF_Flags &= ~W32PF_APPSTARTING;
}
/***************************************************************************\
* zzzInitTask -- called by WOW startup for each app
*
*
* History:
* 02-21-91 MikeHar Created.
* 02-23-92 MattFe Altered for WOW
* 09-03-97 DaveHart WOW supplies compat flags, we tell it about setup apps.
\***************************************************************************/
NTSTATUS zzzInitTask(
UINT dwExpWinVer,
DWORD dwAppCompatFlags,
DWORD dwUserWOWCompatFlags,
PUNICODE_STRING pstrModName,
PUNICODE_STRING pstrBaseFileName,
DWORD hTaskWow,
DWORD dwHotkey,
DWORD idTask,
DWORD dwX,
DWORD dwY,
DWORD dwXSize,
DWORD dwYSize)
{
PTHREADINFO ptiCurrent;
PTDB ptdb;
PPROCESSINFO ppi;
PWOWTHREADINFO pwti;
ULONG ProcessInfo;
NTSTATUS Status;
ptiCurrent = PtiCurrent();
ppi = ptiCurrent->ppi;
/*
* Set the real name of the module. (Instead of 'NTVDM')
* We've already probed pstrModName->Buffer for Length+sizeof(WCHAR) so
* we can copy the UNICODE_NULL terminator as well.
*/
if (ptiCurrent->pstrAppName != NULL) {
UserFreePool(ptiCurrent->pstrAppName);
}
ptiCurrent->pstrAppName = NULL;
//
// Check the Target Process to see if this is a Wx86 process
//
if (ptiCurrent->ptdb) {
/*
* Shouldn't be called more than once on a thread.
*/
return STATUS_ACCESS_DENIED;
}
Status = ZwQueryInformationProcess(NtCurrentProcess(),
ProcessWx86Information,
&ProcessInfo,
sizeof(ProcessInfo),
NULL);
if (!NT_SUCCESS(Status) || ProcessInfo == 0) {
return STATUS_ACCESS_DENIED;
}
ptiCurrent->pstrAppName = UserAllocPoolWithQuota(sizeof(UNICODE_STRING) +
pstrModName->Length + sizeof(WCHAR), TAG_TEXT);
if (ptiCurrent->pstrAppName != NULL) {
ptiCurrent->pstrAppName->Buffer = (PWCHAR)(ptiCurrent->pstrAppName + 1);
try {
RtlCopyMemory(ptiCurrent->pstrAppName->Buffer, pstrModName->Buffer,
pstrModName->Length);
ptiCurrent->pstrAppName->Buffer[pstrModName->Length / sizeof(WCHAR)] = 0;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
UserFreePool(ptiCurrent->pstrAppName);
ptiCurrent->pstrAppName = NULL;
return STATUS_OBJECT_NAME_INVALID;
}
ptiCurrent->pstrAppName->MaximumLength = pstrModName->Length + sizeof(WCHAR);
ptiCurrent->pstrAppName->Length = pstrModName->Length;
} else {
return STATUS_OBJECT_NAME_INVALID;
}
/*
* An app is starting!
*/
if (!(ppi->W32PF_Flags & W32PF_APPSTARTING)) {
SetAppStarting(ppi);
}
/*
* We never want to use the ShowWindow defaulting mechanism for WOW
* apps. If STARTF_USESHOWWINDOW was set in the client-side
* STARTUPINFO structure, WOW has already picked it up and used
* it for the first (command-line) app.
*/
ppi->usi.dwFlags &= ~STARTF_USESHOWWINDOW;
/*
* If WOW passed us a hotkey for this app, save it for CreateWindow's use.
*/
if (dwHotkey != 0) {
ppi->dwHotkey = dwHotkey;
}
/*
* If WOW passed us a non-default window position use it, otherwise clear it.
*/
ppi->usi.cb = sizeof(ppi->usi);
if (dwX == CW_USEDEFAULT || dwX == CW2_USEDEFAULT) {
ppi->usi.dwFlags &= ~STARTF_USEPOSITION;
} else {
ppi->usi.dwFlags |= STARTF_USEPOSITION;
ppi->usi.dwX = dwX;
ppi->usi.dwY = dwY;
}
/*
* If WOW passed us a non-default window size use it, otherwise clear it.
*/
if (dwXSize == CW_USEDEFAULT || dwXSize == CW2_USEDEFAULT) {
ppi->usi.dwFlags &= ~STARTF_USESIZE;
} else {
ppi->usi.dwFlags |= STARTF_USESIZE;
ppi->usi.dwXSize = dwXSize;
ppi->usi.dwYSize = dwYSize;
}
/*
* Alloc and Link in new task into the task list
*/
if ((ptdb = (PTDB)UserAllocPoolWithQuota(sizeof(TDB), TAG_WOWTDB)) == NULL)
return STATUS_NO_MEMORY;
RtlZeroMemory(ptdb, sizeof(TDB));
ptiCurrent->ptdb = ptdb;
/*
* Save away the 16 bit task handle: we use this later when calling
* wow back to close a WOW task.
*/
ptdb->hTaskWow = LOWORD(hTaskWow);
try {
ptiCurrent->pClientInfo->dwCompatFlags = dwAppCompatFlags;
UserAssert(ptiCurrent->ppi->ptiList);
dwUserWOWCompatFlags &= COMPATFLAGS2_FORWOW;
ptiCurrent->pClientInfo->dwCompatFlags2 = dwUserWOWCompatFlags;
/*
* HIWORD: != 0 if wants proportional font
* LOWORD: Expected windows version (3.00 [300], 3.10 [30A], etc)
*/
ptiCurrent->pClientInfo->dwExpWinVer = dwExpWinVer;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
}
ptiCurrent->dwCompatFlags = dwAppCompatFlags;
ptiCurrent->dwCompatFlags2 = dwUserWOWCompatFlags;
ptiCurrent->dwExpWinVer = dwExpWinVer;
/*
* We haven't captured pstrBaseFileName's buffer, we
* may fault touching it in SetAppImeCompatFlags. If
* so the IME flags have been set already and we
* can safely assume it's not a setup app.
*/
try {
if (SetAppImeCompatFlags(ptiCurrent, ptiCurrent->pstrAppName,
pstrBaseFileName)) {
/*
* Flag task as a setup app.
*/
ptdb->TDB_Flags = TDBF_SETUP;
ppi->W32PF_Flags |= W32PF_SETUPAPP;
}
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
}
/*
* Set the flags to say this is a TIF_MEOW queues.
*/
if (hTaskWow & HTW_ISMEOW) {
ptiCurrent->TIF_flags |= TIF_MEOW;
}
ptiCurrent->TIF_flags |= TIF_16BIT | TIF_FIRSTIDLE;
/*
* Set the flags to say this is a 16-bit thread before attaching
* queues.
*/
/*
* If this task is running in the shared WOW VDM, we handle
* WaitForInputIdle a little differently than separate WOW
* VDMs. This is because CreateProcess returns a real process
* handle when you start a separate WOW VDM, so the "normal"
* WaitForInputIdle works. For the shared WOW VDM, CreateProcess
* returns an event handle.
*/
ptdb->pwti = NULL;
if (idTask) {
ptiCurrent->TIF_flags |= TIF_SHAREDWOW;
/*
* Look for a matching thread in the WOW thread info list.
*/
if (idTask != (DWORD)-1) {
for (pwti = gpwtiFirst; pwti != NULL; pwti = pwti->pwtiNext) {
if (pwti->idTask == idTask) {
ptdb->pwti = pwti;
break;
}
}
#if DBG
if (pwti == NULL) {
RIPMSG0(RIP_WARNING, "InitTask couldn't find WOW struct\n");
}
#endif
}
}
try {
ptiCurrent->pClientInfo->dwTIFlags |= ptiCurrent->TIF_flags;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
}
/*
* We need this thread to share the queue of other win16 apps.
* If we're journalling, all apps are sharing a queue, so we wouldn't
* want to interrupt that - so only cause queue recalculation
* if we aren't journalling.
* ptdb may be freed by DestroyTask during a callback, so defer WinEvent
* notifications until we don't need ptdb any more.
*/
DeferWinEventNotify();
if (!FJOURNALRECORD() && !FJOURNALPLAYBACK()) {
zzzReattachThreads(FALSE);
}
/*
* Setup the app start cursor for 5 second timeout.
*/
zzzCalcStartCursorHide((PW32PROCESS)ppi, 5000);
/*
* Mark this guy and add him to the global task list so he can run.
*/
#define NORMAL_PRIORITY_TASK 10
/*
* To be Compatible it super important that the new task run immediately
* Set its priority accordingly. No other task should ever be set to
* CREATION priority
*/
ptdb->nPriority = NORMAL_PRIORITY_TASK;
ptdb->pti = ptiCurrent;
InsertTask(ppi, ptdb);
zzzEndDeferWinEventNotify();
/*
* Force this new task to be the active task (WOW will ensure the
* currently running task does a Yield which will put it into the
* non preemptive scheduler.
*/
ppi->pwpi->ptiScheduled = ptiCurrent;
ppi->pwpi->CSLockCount = -1;
EnterWowCritSect(ptiCurrent, ppi->pwpi);
/*
* Ensure app gets focus.
*/
zzzShowStartGlass(10000);
return STATUS_SUCCESS;
}
/***************************************************************************\
* zzzShowStartGlass
*
* This routine is called by WOW when first starting or when starting an
* additional WOW app.
*
* 12-07-92 ScottLu Created.
\***************************************************************************/
VOID zzzShowStartGlass(
DWORD dwTimeout)
{
PPROCESSINFO ppi;
/*
* If this is the first call to zzzShowStartGlass(), then the
* W32PF_ALLOWFOREGROUNDACTIVATE bit has already been set in the process
* info - we don't want to set it again because it may have been
* purposefully cleared when the user hit a key or mouse clicked.
*/
ppi = PpiCurrent();
if (ppi->W32PF_Flags & W32PF_SHOWSTARTGLASSCALLED) {
/*
* Allow this wow app to come to the foreground. This'll be cancelled
* if the user mouse clicks or hits any keys.
*/
SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
TAGMSG0(DBGTAG_FOREGROUND, "zzzShowStartGlass set PUDF");
ppi->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
TAGMSG1(DBGTAG_FOREGROUND, "zzzShowStartGlass set W32PF %#p", ppi);
}
ppi->W32PF_Flags |= W32PF_SHOWSTARTGLASSCALLED;
/*
* Show the start glass cursor for this much longer.
*/
zzzCalcStartCursorHide((PW32PROCESS)ppi, dwTimeout);
}
/***************************************************************************\
* GetJournallingQueue
*
* 03/21/97 GerardoB Created
\***************************************************************************/
PQ GetJournallingQueue(
PTHREADINFO pti)
{
PHOOK phook;
/*
* Fail if we cannot journal this thread.
*/
if ((pti->TIF_flags & TIF_DONTJOURNALATTACH) || pti->rpdesk == NULL) {
return NULL;
}
/*
* Get the journalling hook if any.
*/
phook = PhkFirstGlobalValid(pti, WH_JOURNALPLAYBACK);
if (phook == NULL) {
phook = PhkFirstGlobalValid(pti, WH_JOURNALRECORD);
}
/*
* Validate fsHooks bits.
*/
UserAssert((phook == NULL)
^ IsHooked(pti, (WHF_FROM_WH(WH_JOURNALPLAYBACK) | WHF_FROM_WH(WH_JOURNALRECORD))));
/*
* Return the queue if we found a journalling hook.
*/
return ((phook == NULL) ? NULL : GETPTI(phook)->pq);
}
/***************************************************************************\
* ClearQueueServerEvent
*
* This function should be called when a thread needs to wait for some kind of
* input. This clears pEventQueueServer which means we won't return from the
* wait until new input of the required type arrives. Setting the wake mask
* controls what input will wake us up. WOW apps skip this since their
* scheduler controls when they wake up.
*
* History:
* 09/12/97 GerardoB Created
\***************************************************************************/
VOID ClearQueueServerEvent(
WORD wWakeMask)
{
PTHREADINFO ptiCurrent = PtiCurrent();
UserAssert(wWakeMask != 0);
ptiCurrent->pcti->fsWakeMask = wWakeMask;
KeClearEvent(ptiCurrent->pEventQueueServer);
}
ULONG ParseReserved(
WCHAR *cczpReserved,
WCHAR *pchFind)
{
ULONG dw;
WCHAR *cczpch, *cczpchT, ch;
UNICODE_STRING cczuString;
dw = 0;
try {
if (cczpReserved != NULL && (cczpch = wcsstr(cczpReserved, pchFind)) != NULL) {
cczpch += wcslen(pchFind);
cczpchT = cczpch;
while (*cczpchT >= '0' && *cczpchT <= '9')
cczpchT++;
ch = *cczpchT;
*cczpchT = 0;
RtlInitUnicodeString(&cczuString, cczpch);
*cczpchT = ch;
RtlUnicodeStringToInteger(&cczuString, 0, &dw);
}
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
return 0;
}
return dw;
}
/*
* Structure USER_PROCESS_PARAMETERS is used to capture all the fields that
* we touch in RTL_USER_PROCESS_PARAMETERS (ppeb->ProcessParameters) since the
* PEB can be trashed from the client side.
*/
typedef struct tagUSER_PROCESS_PARAMETERS {
HANDLE StandardInput;
HANDLE StandardOutput;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING DesktopInfo; // ProcessParameters
UNICODE_STRING ShellInfo; // ProcessParameters
} USER_PROCESS_PARAMETERS, *PUSER_PROCESS_PARAMETERS;
/***************************************************************************\
* xxxCreateThreadInfo
*
* Allocate the main thread information structure
*
* History:
* 03-18-95 JimA Created.
* 04-18-01 Mohamed Modified error recovery wrt hEventQueueClient.
\***************************************************************************/
NTSTATUS xxxCreateThreadInfo(
PETHREAD pEThread)
{
DWORD dwTIFlags = 0;
PPROCESSINFO ppi;
PTHREADINFO ptiCurrent;
PEPROCESS pEProcess = PsGetThreadProcess(pEThread);
PUSERSTARTUPINFO pusi;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PUSER_PROCESS_PARAMETERS ProcessParams = NULL;
USER_PROCESS_PARAMETERS ProcessParamsData;
PDESKTOP pdesk = NULL;
HDESK hdesk = NULL;
HWINSTA hwinsta;
PQ pq;
NTSTATUS Status;
BOOL fFirstThread;
PTEB pteb = NtCurrentTeb();
TL tlpdesk, tlPool;
PPEB ppeb;
PVOID pTmpPool = NULL;
CheckCritIn();
UserAssert(IsWinEventNotifyDeferredOK());
ValidateProcessSessionId(pEProcess);
/*
* If CleanupResources was called for the last GUI thread then
* we should not allow any more GUI threads
*/
if (gbCleanedUpResources) {
RIPMSG0(RIP_ERROR, "No more GUI threads should be created");
return STATUS_PROCESS_IS_TERMINATING;
}
/*
* Increment the number of GUI threads in the session
*/
gdwGuiThreads++;
if (pEProcess == gpepCSRSS) {
dwTIFlags = TIF_CSRSSTHREAD | TIF_DONTATTACHQUEUE | TIF_DISABLEIME;
}
ptiCurrent = (PTHREADINFO)PsGetThreadWin32Thread(pEThread);
ProcessParamsData.DesktopInfo.Buffer = NULL;
ppeb = PsGetProcessPeb(pEProcess);
try {
/*
* NOTE: We allocate memory for the DesktopInfo.Buffer and free it
* later. For ShellInfo we do not since ParseReserved handles user-mode
* pointers.
*/
if (ppeb != NULL) {
ProbeForRead(ppeb, sizeof(PEB), sizeof(BYTE));
ProcessParameters = ppeb->ProcessParameters;
ProcessParams = &ProcessParamsData;
ProbeForRead(ProcessParameters, sizeof(RTL_USER_PROCESS_PARAMETERS), sizeof(BYTE));
ProcessParamsData.StandardInput = ProcessParameters->StandardInput;
ProcessParamsData.StandardOutput = ProcessParameters->StandardOutput;
ProcessParamsData.StartingX = ProcessParameters->StartingX;
ProcessParamsData.StartingY = ProcessParameters->StartingY;
ProcessParamsData.CountX = ProcessParameters->CountX;
ProcessParamsData.CountY = ProcessParameters->CountY;
ProcessParamsData.WindowFlags = ProcessParameters->WindowFlags;
ProcessParamsData.ShowWindowFlags = ProcessParameters->ShowWindowFlags;
ProcessParamsData.DesktopInfo = ProbeAndReadUnicodeString(&ProcessParameters->DesktopInfo);
if (ProcessParamsData.DesktopInfo.Length > 0) {
PWSTR pszCapture = ProcessParamsData.DesktopInfo.Buffer;
ProbeForReadUnicodeStringBuffer(ProcessParamsData.DesktopInfo);
/*
* The pool pointer is stored in pTmpPool as well as in
* DesktopInfo.Buffer. The reason is that in case of corrupt
* user mode DesktopInfo an exception is raised which bails
* out of this try-except block without properly allocating
* the new pool. Therefore, pTmpPool is used in the check
* before freeing this pool.
*/
pTmpPool = UserAllocPoolWithQuota(ProcessParamsData.DesktopInfo.Length, TAG_TEXT2);
ProcessParamsData.DesktopInfo.Buffer = pTmpPool;
if (ProcessParamsData.DesktopInfo.Buffer) {
ThreadLockPool(ptiCurrent, ProcessParamsData.DesktopInfo.Buffer, &tlPool);
RtlCopyMemory(ProcessParamsData.DesktopInfo.Buffer, pszCapture, ProcessParamsData.DesktopInfo.Length);
} else {
ExRaiseStatus(STATUS_NO_MEMORY);
}
} else {
ProcessParamsData.DesktopInfo.Buffer = NULL;
}
ProcessParamsData.ShellInfo = ProbeAndReadUnicodeString(&ProcessParameters->ShellInfo);
ProbeForReadUnicodeStringBuffer(ProcessParamsData.ShellInfo);
}
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
Status = GetExceptionCode();
goto CreateThreadInfoFailed;
}
/*
* Locate the processinfo structure for the new thread.
*/
ppi = PpiCurrent();
#ifdef _WIN64
/*
* If the process is marked as an emulated 32bit app mark the thread as
* an emulated 32bit thread. This is to be consistent with the way WOW16
* marks threads.
*/
if (ppi->W32PF_Flags & W32PF_WOW64) {
dwTIFlags |= TIF_WOW64;
}
#endif
/*
* For Winlogon, only the first thread can have IME processing.
*/
if (gpidLogon == PsGetThreadProcessId(pEThread)) {
if (ppi->ptiList != NULL) {
dwTIFlags |= TIF_DISABLEIME;
RIPMSG1(RIP_VERBOSE, "WinLogon, second or other thread. pti=%#p", PsGetThreadWin32Thread(pEThread));
}
}
ptiCurrent->TIF_flags = dwTIFlags;
Lock(&ptiCurrent->spklActive, gspklBaseLayout);
ptiCurrent->pcti = &(ptiCurrent->cti);
/*
* Check if no IME processing for all threads
* in the same process.
*/
if (ppi->W32PF_Flags & W32PF_DISABLEIME) {
ptiCurrent->TIF_flags |= TIF_DISABLEIME;
}
/*
* Hook up this queue to this process info structure, increment
* the count of threads using this process info structure. Set up
* the ppi before calling SetForegroundPriority().
*/
UserAssert(ppi != NULL);
ptiCurrent->ppi = ppi;
ptiCurrent->ptiSibling = ppi->ptiList;
ppi->ptiList = ptiCurrent;
ppi->cThreads++;
if (pteb != NULL) {
try {
pteb->Win32ThreadInfo = ptiCurrent;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
Status = GetExceptionCode();
goto CreateThreadInfoFailed;
}
}
/*
* Point to the client info.
*/
if (dwTIFlags & TIF_SYSTEMTHREAD) {
ptiCurrent->pClientInfo = UserAllocPoolWithQuota(sizeof(CLIENTINFO),
TAG_CLIENTTHREADINFO);
if (ptiCurrent->pClientInfo == NULL) {
Status = STATUS_NO_MEMORY;
goto CreateThreadInfoFailed;
}
} else {
/*
* If this is not a system thread then grab the user mode client
* info elsewhere we use the GetClientInfo macro which looks here.
*/
UserAssert(pteb != NULL);
try {
ptiCurrent->pClientInfo = ((PCLIENTINFO)((pteb)->Win32ClientInfo));
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
Status = GetExceptionCode();
goto CreateThreadInfoFailed;
}
/*
* Set the restricted flag in the thread flags if this is a secure
* process.
*/
if (((PW32PROCESS)ppi)->W32PF_Flags & W32PF_RESTRICTED) {
ptiCurrent->TIF_flags |= TIF_RESTRICTED;
}
}
/*
* Create the input event.
*/
Status = ZwCreateEvent(&ptiCurrent->hEventQueueClient,
EVENT_ALL_ACCESS,
NULL,
SynchronizationEvent,
FALSE);
if (NT_SUCCESS(Status)) {
Status = ObReferenceObjectByHandle(ptiCurrent->hEventQueueClient,
EVENT_ALL_ACCESS,
*ExEventObjectType,
UserMode,
&ptiCurrent->pEventQueueServer,
NULL);
if (NT_SUCCESS(Status)) {
Status = ProtectHandle(ptiCurrent->hEventQueueClient, *ExEventObjectType, TRUE);
} else if (Status != STATUS_INVALID_HANDLE) {
ObCloseHandle(ptiCurrent->hEventQueueClient, UserMode);
}
}
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING, "xxxCreateThreadInfo: failed a handle routine for hEventQueueClient handle, status=%08x", Status);
ptiCurrent->hEventQueueClient = NULL;
goto CreateThreadInfoFailed;
}
/*
* Mark the process as having threads that need cleanup. See
* DestroyProcessesObjects().
*/
fFirstThread = !(ppi->W32PF_Flags & W32PF_THREADCONNECTED);
ppi->W32PF_Flags |= W32PF_THREADCONNECTED;
/*
* If we haven't copied over our startup info yet, do it now.
* Don't bother copying the info if we aren't going to use it.
*/
if (ProcessParams) {
pusi = &ppi->usi;
if ((pusi->cb == 0) && (ProcessParams->WindowFlags != 0)) {
pusi->cb = sizeof(USERSTARTUPINFO);
pusi->dwX = ProcessParams->StartingX;
pusi->dwY = ProcessParams->StartingY;
pusi->dwXSize = ProcessParams->CountX;
pusi->dwYSize = ProcessParams->CountY;
pusi->dwFlags = ProcessParams->WindowFlags;
pusi->wShowWindow = (WORD)ProcessParams->ShowWindowFlags;
}
if (fFirstThread) {
/*
* Set up the hot key, if there is one.
*
* If the STARTF_USEHOTKEY flag is given in the startup info, then
* the hStdInput is the hotkey (new from Chicago). Otherwise, parse
* it out in string format from the lpReserved string.
*/
if (ProcessParams->WindowFlags & STARTF_USEHOTKEY) {
ppi->dwHotkey = HandleToUlong(ProcessParams->StandardInput);
} else {
if (ProcessParams->ShellInfo.Length > 0) {
ppi->dwHotkey = ParseReserved(ProcessParams->ShellInfo.Buffer,
L"hotkey.");
} else {
ppi->dwHotkey = 0;
}
}
/*
* Copy the monitor handle, if there is one.
*/
UserAssert(!ppi->hMonitor);
if (ProcessParams->WindowFlags & STARTF_HASSHELLDATA) {
HMONITOR hMonitor;
hMonitor = (HMONITOR)ProcessParams->StandardOutput;
if (ValidateHmonitor(hMonitor)) {
ppi->hMonitor = hMonitor;
}
}
}
}
if ((pq = AllocQueue(NULL, NULL)) == NULL) {
Status = STATUS_NO_MEMORY;
goto CreateThreadInfoFailed;
}
/*
* Attach the Q to the THREADINFO.
*/
ptiCurrent->pq = pq;
pq->ptiMouse = pq->ptiKeyboard = ptiCurrent;
pq->cThreads++;
/*
* Open the windowstation and desktop. If this is a system
* thread only use the desktop that might be stored in the teb.
*/
UserAssert(ptiCurrent->rpdesk == NULL);
if (!(ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) &&
grpWinStaList) {
BOOL bShutDown = FALSE;
hdesk = xxxResolveDesktop(
NtCurrentProcess(),
&ProcessParams->DesktopInfo,
&hwinsta, (ProcessParams->WindowFlags & STARTF_DESKTOPINHERIT),
&bShutDown);
if (hdesk == NULL) {
if (bShutDown) {
/*
* Trying to create a new process during logoff.
*/
ULONG_PTR adwParameters[5] = {0, 0, 0, 0, MB_DEFAULT_DESKTOP_ONLY};
ULONG ErrorResponse;
LeaveCrit();
ExRaiseHardError((NTSTATUS)STATUS_DLL_INIT_FAILED_LOGOFF,
ARRAY_SIZE(adwParameters),
0,
adwParameters,
OptionOkNoWait,
&ErrorResponse);
ZwTerminateProcess(NtCurrentProcess(), STATUS_DLL_INIT_FAILED);
EnterCrit();
}
Status = STATUS_DLL_INIT_FAILED;
goto CreateThreadInfoFailed;
} else {
Status = _SetProcessWindowStation(hwinsta, UserMode);
if (!NT_SUCCESS(Status)) {
goto CreateThreadInfoFailed;
}
/*
* Reference the desktop handle
*/
Status = ObReferenceObjectByHandle(hdesk,
0,
*ExDesktopObjectType,
KernelMode,
&pdesk,
NULL);
if (!NT_SUCCESS(Status)) {
UserAssert(pdesk == NULL);
goto CreateThreadInfoFailed;
}
ThreadLockDesktop(ptiCurrent, pdesk, &tlpdesk, LDLT_FN_CREATETHREADINFO);
ObDereferenceObject(pdesk);
/*
* Map the desktop into the current process.
*/
{
WIN32_OPENMETHOD_PARAMETERS OpenParams;
OpenParams.OpenReason = ObOpenHandle;
OpenParams.Process = PsGetCurrentProcess();
OpenParams.Object = pdesk;
OpenParams.GrantedAccess = 0;
OpenParams.HandleCount = 1;
if (!NT_SUCCESS(MapDesktop(&OpenParams))) {
RIPMSGF2(RIP_WARNING,
"Could't map pdesk %p in ppi %p",
pdesk,
PpiCurrent());
Status = STATUS_NO_MEMORY;
goto CreateThreadInfoFailed;
}
}
/*
* The first desktop is the default for all succeeding threads.
*/
if (ppi->hdeskStartup == NULL &&
PsGetProcessId(pEProcess) != gpidLogon) {
LockDesktop(&ppi->rpdeskStartup, pdesk, LDL_PPI_DESKSTARTUP2, (ULONG_PTR)ppi);
ppi->hdeskStartup = hdesk;
}
}
}
/*
* Remember dwExpWinVer. This is used to return GetAppVer() (and
* GetExpWinVer(NULL)).
*/
if (PsGetProcessPeb(pEProcess) != NULL) {
ptiCurrent->dwExpWinVer = RtlGetExpWinVer(PsGetProcessSectionBaseAddress(pEProcess));
} else {
ptiCurrent->dwExpWinVer = VER40;
}
INITCLIENTINFO(ptiCurrent);
/*
* Set the desktop even if it is NULL to ensure that ptiCurrent->pDeskInfo
* is set.
* NOTE: This adds the pti to the desktop's PtiList, but we don't yet have
* a pti->pq. zzzRecalcThreadAttachment loops through this PtiList expects
* a pq, so we must not leave the critsect until we have a queue.
* zzzSetDesktop only zzz leaves the critsect if there is a pti->pq, so we
* can BEGINATOMICCHECK to ensure this, and make sure we allocate the queue
* before we leave the critical section.
*/
BEGINATOMICCHECK();
if (zzzSetDesktop(ptiCurrent, pdesk, hdesk) == FALSE) {
EXITATOMICCHECK();
goto CreateThreadInfoFailed;
}
ENDATOMICCHECK();
/*
* If we have a desktop and are journalling on that desktop, use
* the journal queue, otherwise create a new queue.
*/
if (pdesk == grpdeskRitInput) {
UserAssert((pdesk == NULL) || (ptiCurrent->pDeskInfo == pdesk->pDeskInfo));
UserAssert(ptiCurrent->rpdesk == pdesk);
pq = GetJournallingQueue(ptiCurrent);
if (pq != NULL && pq != ptiCurrent->pq) {
DestroyThreadsMessages(ptiCurrent->pq, ptiCurrent);
zzzDestroyQueue(ptiCurrent->pq, ptiCurrent);
ptiCurrent->pq = pq;
pq->cThreads++;
}
}
/*
* Remember that this is a screen saver. That way we can set its
* priority appropriately when it is idle or when it needs to go
* away. At first we set it to normal priority, then we set the
* TIF_IDLESCREENSAVER bit so that when it activates it will get
* lowered in priority.
*/
if (ProcessParams && ProcessParams->WindowFlags & STARTF_SCREENSAVER) {
if (fFirstThread) {
UserAssert(gppiScreenSaver == NULL);
/*
* Make sure the parent's process is WinLogon, since only
* WinLogon is allowed to use the STARTF_SCREENSAVER flag.
*/
if (gpidLogon == 0 || PsGetProcessInheritedFromUniqueProcessId(pEProcess) != gpidLogon) {
RIPMSG0(RIP_WARNING,"Only the Logon process can launch a screen saver.");
ProcessParams->WindowFlags &= ~STARTF_SCREENSAVER;
goto NotAScreenSaver;
}
gppiScreenSaver = ppi;
gptSSCursor = gpsi->ptCursor;
ppi->W32PF_Flags |= W32PF_SCREENSAVER;
} else {
UserAssert(ppi->W32PF_Flags & W32PF_SCREENSAVER);
}
SetForegroundPriority(ptiCurrent, TRUE);
if (fFirstThread) {
ppi->W32PF_Flags |= W32PF_IDLESCREENSAVER;
}
/*
* Screen saver doesn't need any IME processing.
*/
ptiCurrent->TIF_flags |= TIF_DISABLEIME;
}
NotAScreenSaver:
/*
* Do special processing for the first thread of a process.
*/
if (!(ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD))) {
#ifndef LAZY_CLASS_INIT
/*
* I changed the code a while ago to unregister classes when the last
* GUI thread is destroyed. Simply, there was too much stuff getting
* unlocked and destroyed to guarantee that it would work on a non-GUI
* thread. So if a process destroys its last GUI thread and then makes
* a thread GUI later, we need to re-register the classes.
*/
if (!(ppi->W32PF_Flags & W32PF_CLASSESREGISTERED)) {
if (!LW_RegisterWindows()) {
RIPMSG0(RIP_WARNING, "xxxCreateThreadInfo: LW_RegisterWindows failed");
Status = STATUS_UNSUCCESSFUL;
goto CreateThreadInfoFailed;
}
}
#endif
if (fFirstThread) {
/*
* If this is an application starting (ie. not some thread of
* the server context), enable the app-starting cursor.
*/
DeferWinEventNotify();
zzzCalcStartCursorHide((PW32PROCESS)PsGetProcessWin32Process(pEProcess), 5000);
EndDeferWinEventNotifyWithoutProcessing();
/*
* Open the windowstation.
*/
if (grpWinStaList && ppi->rpwinsta == NULL) {
RIPERR0(ERROR_CAN_NOT_COMPLETE,
RIP_WARNING,
"System is not initialized");
Status = STATUS_UNSUCCESSFUL;
goto CreateThreadInfoFailed;
}
}
#ifndef LAZY_CLASS_INIT
} else {
/*
* Don't register system windows until cursors and icons have been
* loaded.
*/
if ((SYSCUR(ARROW) != NULL) &&
!(ppi->W32PF_Flags & W32PF_CLASSESREGISTERED)) {
if (!LW_RegisterWindows()) {
RIPMSG0(RIP_WARNING, "xxxCreateThreadInfo: LW_RegisterWindows failed");
Status = STATUS_UNSUCCESSFUL;
goto CreateThreadInfoFailed;
}
}
#endif
}
/*
* Initialize hung timer value.
*/
SET_TIME_LAST_READ(ptiCurrent);
/*
* If someone is waiting on this process propagate that info into the
* thread info.
*/
if (ppi->W32PF_Flags & W32PF_WAITFORINPUTIDLE) {
ptiCurrent->TIF_flags |= TIF_WAITFORINPUTIDLE;
}
/*
* Mark the thread as initialized.
*/
ptiCurrent->TIF_flags |= TIF_GUITHREADINITIALIZED;
/*
* Allow the thread to come to foreground when it is created if the
* current process is the foreground process or the last input owner.
* This Flag is a hack to fix Bug 28502. When we click on "Map Network
* Drive" button on the toolbar, explorer creates another thread to
* create the dialog box. This will create the dialog in the background.
* We are adding this fix at the request of the Shell team so that this
* dialog comes up as foreground.
*
* If the process already has the foreground right, we don't give it to
* this thread (it doesn't need it). We do this to narrow the number of
* ways this process can force the foreground. Also, if the process is
* starting, it already has the right unless the user has canceled it --
* in which case we don't want to give it back.
*/
if (!(ppi->W32PF_Flags & (W32PF_ALLOWFOREGROUNDACTIVATE | W32PF_APPSTARTING))) {
if (((gptiForeground != NULL) && (ppi == gptiForeground->ppi))
|| ((glinp.ptiLastWoken != NULL) && (ppi == glinp.ptiLastWoken->ppi))) {
ptiCurrent->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
TAGMSG1(DBGTAG_FOREGROUND, "xxxCreateThreadInfo set TIF %#p", ptiCurrent);
}
}
if (IS_IME_ENABLED()) {
/*
* Create per-thread default input context
*/
CreateInputContext(0);
}
/*
* Call back to the client to finish initialization.
*/
if (!(dwTIFlags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD))) {
if (SetAppCompatFlags(ptiCurrent)) {
/*
* Flag this process as a setup app.
*/
ppi->W32PF_Flags |= W32PF_SETUPAPP;
}
Status = xxxClientThreadSetup();
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING, "ClientThreadSetup failed with NTSTATUS %lx", Status);
goto CreateThreadInfoFailed;
}
}
if ((NT_SUCCESS(Status) && fFirstThread) &&
!(ppi->W32PF_Flags & W32PF_CONSOLEAPPLICATION)) {
/*
* Don't play the sound for console processes since we will play it
* when the console window is created.
*/
PlayEventSound(USER_SOUND_OPEN);
}
/*
* Release desktop.
* Some other thread might have been waiting to destroy this desktop
* when xxxResolveDestktop got a handle to it. So let's double
* check this now that we have called back several times after getting
* the handle back.
*/
if (pdesk != NULL) {
if (pdesk->dwDTFlags & DF_DESTROYED) {
RIPMSG1(RIP_WARNING, "xxxCreateThreadInfo: pdesk destroyed:%#p", pdesk);
Status = STATUS_UNSUCCESSFUL;
goto CreateThreadInfoFailed;
}
ThreadUnlockDesktop(ptiCurrent, &tlpdesk, LDUT_FN_CREATETHREADINFO1);
}
if (pTmpPool != NULL) {
ThreadUnlockAndFreePool(ptiCurrent, &tlPool);
}
UserAssert(NT_SUCCESS(Status));
return Status;
Error:
CreateThreadInfoFailed:
RIPMSG3(RIP_WARNING, "xxxCreateThreadInfo failed: pti %#p pdesk %#p status 0x%x", ptiCurrent, pdesk, Status);
if (pdesk != NULL) {
ThreadUnlockDesktop(ptiCurrent, &tlpdesk, LDUT_FN_CREATETHREADINFO2);
}
if (pTmpPool != NULL) {
ThreadUnlockAndFreePool(ptiCurrent, &tlPool);
}
xxxDestroyThreadInfo();
return Status;
}
/***************************************************************************\
* AllocQueue
*
* Allocates the memory for a TI structure and initializes its fields.
* Each Win32 queue has it's own TI while all Win16 threads share the same
* TI.
*
* History:
* 02-21-91 MikeHar Created.
\***************************************************************************/
PQ AllocQueue(
PTHREADINFO ptiKeyState, // If non-Null then use this key state.
// Otherwise use global AsyncKeyState.
PQ pq) // Non-NULL == preallocated object.
{
USHORT cLockCount;
if (pq == NULL) {
pq = ExAllocateFromPagedLookasideList(QLookaside);
if (pq == NULL) {
return NULL;
}
cLockCount = 0;
} else {
DebugValidateMLIST(&pq->mlInput);
/*
* Preserve lock count.
*/
cLockCount = pq->cLockCount;
}
RtlZeroMemory(pq, sizeof(Q));
pq->cLockCount = cLockCount;
/*
* This is a new queue; we need to update its key state table before
* the first input event is put in the queue.
* We do this by copying the current keystate table and NULLing the recent
* down state table. If a key is really down it will be updated when
* we get it repeats.
*
* He is the old way that did not work because if the first key was say an
* alt key the Async table would be updated, then the UpdateKeyState
* message and it would look like the alt key was PREVIOUSLY down.
*
* The queue will get updated when it first reads input: to allow the
* app to query the key state before it calls GetMessage, set its initial
* key state to the asynchronous key state.
*/
if (ptiKeyState) {
RtlCopyMemory(pq->afKeyState, ptiKeyState->pq->afKeyState, CBKEYSTATE);
} else {
RtlCopyMemory(pq->afKeyState, gafAsyncKeyState, CBKEYSTATE);
}
/*
* If there isn't a mouse set iCursorLevel to -1 so the
* mouse cursor won't be visible on the screen.
*/
if (!TEST_GTERMF(GTERMF_MOUSE)) {
pq->iCursorLevel--;
}
/*
* While the thread is starting up ... it has the wait cursor.
*/
LockQCursor(pq, SYSCUR(WAIT));
DebugValidateMLIST(&pq->mlInput);
return pq;
}
/***************************************************************************\
* FreeQueue
*
* 04-04-96 GerardoB Created.
\***************************************************************************/
VOID FreeQueue(
PQ pq)
{
#if DBG
/*
* Turn off the flag indicating that this queue is in destruction.
* We do this in either case that we are putting this into the free
* list, or truly destroying the handle. We use this to try and
* track cases where someone tries to lock elements into the queue
* structure while it's going through destuction.
*/
pq->QF_flags &= ~QF_INDESTROY;
#endif
UserAssert(pq != gpqForeground);
UserAssert(pq != gpqForegroundPrev);
UserAssert(pq != gpqCursor);
ExFreeToPagedLookasideList(QLookaside, pq);
}
/***************************************************************************\
* FreeCachedQueues
*
* 14-Jan-98 CLupu Created.
\***************************************************************************/
VOID FreeCachedQueues(
VOID)
{
if (QLookaside != NULL) {
ExDeletePagedLookasideList(QLookaside);
UserFreePool(QLookaside);
QLookaside = NULL;
}
}
/***************************************************************************\
* zzzDestroyQueue
*
*
* History:
* 05-20-91 MikeHar Created.
\***************************************************************************/
VOID zzzDestroyQueue(
PQ pq,
PTHREADINFO pti)
{
PTHREADINFO ptiT;
PTHREADINFO ptiAny, ptiBestMouse, ptiBestKey;
PLIST_ENTRY pHead, pEntry;
#if DBG
USHORT cDying = 0;
#endif
BOOL fSetFMouseMoved = FALSE;
DebugValidateMLIST(&pq->mlInput);
UserAssert(pq->cThreads);
pq->cThreads--;
if (pq->cThreads != 0) {
/*
* Since we aren't going to destroy this queue, make sure
* it isn't pointing to the THREADINFO that's going away.
*/
if (pq->ptiSysLock == pti) {
CheckSysLock(6, pq, NULL);
pq->ptiSysLock = NULL;
}
if ((pq->ptiKeyboard == pti) || (pq->ptiMouse == pti)) {
/*
* Run through THREADINFOs looking for one pointing to pq.
*/
ptiAny = NULL;
ptiBestMouse = NULL;
ptiBestKey = NULL;
pHead = &pti->rpdesk->PtiList;
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
/*
* Skip threads that are going away or belong to a
* different queue.
*/
if ((ptiT->TIF_flags & TIF_INCLEANUP) || (ptiT->pq != pq)) {
#if DBG
if (ptiT->pq == pq && (ptiT->TIF_flags & TIF_INCLEANUP)) {
cDying++;
}
#endif
continue;
}
ptiAny = ptiT;
if (pti->pcti->fsWakeBits & QS_MOUSE) {
if (ptiT->pcti->fsWakeMask & QS_MOUSE)
ptiBestMouse = ptiT;
}
if (pti->pcti->fsWakeBits & QS_KEY) {
if (ptiT->pcti->fsWakeMask & QS_KEY)
ptiBestKey = ptiT;
}
#ifdef GENERIC_INPUT
if (pti->pcti->fsWakeMask & QS_RAWINPUT) {
if (ptiT->pcti->fsWakeMask & QS_RAWINPUT) {
/* For now, let's use keyboard focus to route the raw input */
ptiBestKey = ptiT;
}
}
#endif
}
if (ptiBestMouse == NULL)
ptiBestMouse = ptiAny;
if (ptiBestKey == NULL)
ptiBestKey = ptiAny;
/*
* Transfer any wake-bits to this new queue. This
* is a common problem for QS_MOUSEMOVE which doesn't
* get set on coalesced WM_MOUSEMOVE events, so we
* need to make sure the new thread tries to process
* any input waiting in the queue.
*/
if (ptiBestMouse != NULL)
SetWakeBit(ptiBestMouse, pti->pcti->fsWakeBits & QS_MOUSE);
if (ptiBestKey != NULL) {
SetWakeBit(ptiBestKey, pti->pcti->fsWakeBits & QS_KEY);
#ifdef GENERIC_INPUT
SetWakeBit(ptiBestKey, pti->pcti->fsWakeBits & QS_RAWINPUT);
#endif
}
if (pq->ptiKeyboard == pti)
pq->ptiKeyboard = ptiBestKey;
if (pq->ptiMouse == pti)
pq->ptiMouse = ptiBestMouse;
#if DBG
/*
* Bad things happen if ptiKeyboard or ptiMouse are NULL
*/
if (pq->cThreads != cDying && (pq->ptiKeyboard == NULL || pq->ptiMouse == NULL)) {
RIPMSG6(RIP_ERROR,
"pq %#p pq->cThreads %x cDying %x pti %#p ptiK %#p ptiM %#p",
pq, pq->cThreads, cDying, pti, pq->ptiKeyboard, pq->ptiMouse);
}
#endif
}
return;
}
/*
* Unlock any potentially locked globals now that we know absolutely
* that this queue is going away.
*/
UnlockCaptureWindow(pq);
Unlock(&pq->spwndFocus);
Unlock(&pq->spwndActive);
Unlock(&pq->spwndActivePrev);
Unlock(&pq->caret.spwnd);
LockQCursor(pq, NULL);
#if DBG
/*
* Mark this queue as being in the destruction process. This is
* cleared in FreeQueue() once we have determined it's safe to
* place in the free-list, or destroy the handle. We use this
* to track cases where someone will lock a cursor into the queue
* while it's in the middle of being destroyed.
*/
pq->QF_flags |= QF_INDESTROY;
#endif
/*
* Free everything else that was allocated/created by AllocQueue.
*/
FreeMessageList(&pq->mlInput);
/*
* If this queue is in the foreground, set gpqForeground
* to NULL so no input is routed. At some point we'll want
* to do slightly more clever assignment of gpqForeground here.
*/
if (gpqForeground == pq) {
gpqForeground = NULL;
}
if (gpqForegroundPrev == pq) {
gpqForegroundPrev = NULL;
}
if (gpqCursor == pq) {
gpqCursor = NULL;
fSetFMouseMoved = TRUE;
}
if (pq->cLockCount == 0) {
FreeQueue(pq);
}
if (fSetFMouseMoved) {
zzzSetFMouseMoved();
}
}
/**************************************************************************\
* UserDeleteW32Thread
*
* This function is called when the W32THREAD reference count goes
* down to zero. So everything left around by xxxDestroyThreadInfo
* must be cleaned up here.
*
* SO VERY IMPORTANT:
* Note that this call is not in the context of the pti being cleaned up,
* in other words, pti != PtiCurrent(). So only kernel calls are allowed here.
*
* 04-01-96 GerardoB Created
\**************************************************************************/
VOID UserDeleteW32Thread(
PW32THREAD pW32Thread)
{
PTHREADINFO pti = (PTHREADINFO)pW32Thread;
BEGIN_REENTERCRIT();
/*
* Make sure the ref count didn't get bumped up while we were waiting.
*/
if (pW32Thread->RefCount == 0) {
/*
* Events
*/
if (pti->pEventQueueServer != NULL) {
ObDereferenceObject(pti->pEventQueueServer);
}
if (pti->apEvent != NULL) {
UserFreePool(pti->apEvent);
}
/*
* App name.
*/
if (pti->pstrAppName != NULL) {
UserFreePool(pti->pstrAppName);
}
/*
* Unlock the queues and free them if no one is using them (the
* queues were already destroyed in DestroyThreadInfo).
*/
if (pti->pq != NULL) {
UserAssert(pti->pq->cLockCount);
--(pti->pq->cLockCount);
if (pti->pq->cLockCount == 0 && pti->pq->cThreads == 0) {
FreeQueue(pti->pq);
}
}
/*
* zzzReattachThreads shouldn't call back while using pqAttach
*/
UserAssert(pti->pqAttach == NULL);
/*
* Unlock the desktop (pti already unlinked from ptiList)
*/
if (pti->rpdesk != NULL) {
UnlockDesktop(&pti->rpdesk, LDU_PTI_DESK, (ULONG_PTR)pti);
}
/*
* Remove the pointer to this W32Thread and free the associated memory.
*/
PsSetThreadWin32Thread(pW32Thread->pEThread, NULL, pW32Thread);
Win32FreePool(pW32Thread);
}
END_REENTERCRIT();
}
/**************************************************************************\
* UserDeleteW32Process
*
* This function is called when the W32PROCESS reference count goes
* down to zero. So everything left around by DestroyProcessInfo
* must be cleaned up here.
*
* SO VERY IMPORTANT:
* Note that this call may not be in the context of the ppi being cleaned up,
* in other words, ppi != PpiCurrent(). So only kernel calls are allowed here.
*
* 04-01-96 GerardoB Created
\**************************************************************************/
VOID UserDeleteW32Process(
PW32PROCESS pW32Process)
{
PPROCESSINFO ppi = (PPROCESSINFO)pW32Process;
BEGIN_REENTERCRIT();
/*
* Make sure the ref count didn't get bumped up while we were waiting.
*/
if (pW32Process->RefCount == 0) {
UserAssert(ppi->ptiMainThread == NULL && ppi->ptiList == NULL);
/*
* Grab the handle flags lock. We can't call into the object manager when
* we have this or we might deadlock.
*/
EnterHandleFlagsCrit();
/*
* Delete handle flags attribute bitmap
*/
if (ppi->bmHandleFlags.Buffer) {
UserFreePool(ppi->bmHandleFlags.Buffer);
RtlInitializeBitMap(&ppi->bmHandleFlags, NULL, 0);
}
/*
* Remove the pointer to this W32Process and free the associated memory.
*/
PsSetProcessWin32Process(pW32Process->Process, NULL, pW32Process);
Win32FreePool(pW32Process);
/*
* Release the handle flags lock.
*/
LeaveHandleFlagsCrit();
}
END_REENTERCRIT();
}
/***************************************************************************\
* FLastGuiThread
*
* Check if this is the last GUI thread in the process.
\***************************************************************************/
__inline BOOL FLastGuiThread(
PTHREADINFO pti)
{
return (pti->ppi != NULL &&
pti->ppi->ptiList == pti &&
pti->ptiSibling == NULL);
}
/***************************************************************************\
* xxxDestroyThreadInfo
*
* Destroys a THREADINFO created by xxxCreateThreadInfo().
*
* Note that the current pti can be locked so it might be used after this
* function returns, even though the thread execution has ended.
*
* We want to stop any activity on this thread so we clean up any USER stuff
* like messages, clipboard, queue, etc and specially anything that assumes
* to be running on a Win32 thread and client side stuff. The final cleanup
* will take place in UserDeleteW32Thread.
*
* This function must not go into the user mode because the ntos data
* structures may no longer support it and it may bluescreen the system.
*
* Make all callbacks before the thread objects are destroyed. If you callback
* afterwards, new objects might be created and won't be cleaned up.
*
* History:
* 02-15-91 DarrinM Created.
* 02-27-91 mikeke Made it work
* 02-27-91 Mikehar Removed queue from the global list
\***************************************************************************/
VOID xxxDestroyThreadInfo(
VOID)
{
PTHREADINFO ptiCurrent = PtiCurrent(), *ppti;
PTEB pteb = NtCurrentTeb();
UserAssert(ptiCurrent != NULL);
UserAssert(IsWinEventNotifyDeferredOK());
/*
* We must NULL out the Win32ThreadInfo pointer. Otherwise, a thread
* that is here due to failing to convert to GUI can call a USER
* function, at which point they'll access this (now bogus) pointer.
*/
if (pteb != NULL) {
try {
pteb->Win32ThreadInfo = NULL;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
/*
* Do nothing. Worse case scenario, the app crashes later,
* but that's the full extent of what we were trying to avoid.
*/
}
}
/*
* If this thread is blocking input, stop it.
*/
if (gptiBlockInput == ptiCurrent) {
gptiBlockInput = NULL;
}
/*
* Don't mess with this ptiCurrent anymore.
*/
ptiCurrent->TIF_flags |= (TIF_DONTATTACHQUEUE | TIF_INCLEANUP);
/*
* First do any preparation work: Windows need to be "patched" so that
* their window procs point to server only windowprocs, for example.
*/
PatchThreadWindows(ptiCurrent);
/*
* If this thread terminated abnormally and was tracking tell
* GDI to hide the trackrect.
*/
if (ptiCurrent->pmsd != NULL) {
xxxCancelTrackingForThread(ptiCurrent);
}
/*
* Unlock the pmsd window.
*/
if (ptiCurrent->pmsd != NULL) {
Unlock(&ptiCurrent->pmsd->spwnd);
UserFreePool(ptiCurrent->pmsd);
ptiCurrent->pmsd = NULL;
}
/*
* Free the clipboard if owned by this thread.
*/
{
PWINDOWSTATION pwinsta;
pwinsta = _GetProcessWindowStation(NULL);
if (pwinsta != NULL) {
if (pwinsta->ptiClipLock == ptiCurrent) {
xxxCloseClipboard(pwinsta);
}
if (pwinsta->ptiDrawingClipboard == ptiCurrent) {
pwinsta->ptiDrawingClipboard = NULL;
}
}
}
/*
* Unlock all the objects stored in the menustate structure.
*/
while (ptiCurrent->pMenuState != NULL) {
PMENUSTATE pMenuState;
PPOPUPMENU ppopupmenuRoot;
pMenuState = ptiCurrent->pMenuState;
ppopupmenuRoot = pMenuState->pGlobalPopupMenu;
/*
* If menu mode was running on this thread
*/
if (ptiCurrent == pMenuState->ptiMenuStateOwner) {
/*
* The menu's going away, so anyone who's locked it
* is SOL anyway. Windows NT Bug #375467.
*/
pMenuState->dwLockCount = 0;
/*
* Close this menu.
*/
if (pMenuState->fModelessMenu) {
xxxEndMenuLoop(pMenuState, ppopupmenuRoot);
xxxMNEndMenuState(TRUE);
} else {
pMenuState->fInsideMenuLoop = FALSE;
ptiCurrent->pq->QF_flags &= ~QF_CAPTURELOCKED;
xxxMNCloseHierarchy(ppopupmenuRoot, pMenuState);
xxxMNEndMenuState(ppopupmenuRoot->fIsMenuBar || ppopupmenuRoot->fDestroyed);
}
} else {
/*
* Menu mode is running on another thread. This thread
* must own spwndNotify which is going away soon.
*
* When spwndNotify is destroyed, we will clean up pMenuState
* from this pti. So do nothing now as we'll need this
* pMenuState at that time.
*/
UserAssert((ppopupmenuRoot->spwndNotify != NULL)
&& (GETPTI(ppopupmenuRoot->spwndNotify) == ptiCurrent));
/*
* Nested menus are not supposed to involve multiple threads.
*/
UserAssert(pMenuState->pmnsPrev == NULL);
break;
}
}
/*
* Unlock all the objects stored in the sbstate structure.
*/
if (ptiCurrent->pSBTrack) {
Unlock(&ptiCurrent->pSBTrack->spwndSB);
Unlock(&ptiCurrent->pSBTrack->spwndSBNotify);
Unlock(&ptiCurrent->pSBTrack->spwndTrack);
UserFreePool(ptiCurrent->pSBTrack);
ptiCurrent->pSBTrack = NULL;
}
/*
* If this is the main input thread of this application, zero out
* that field.
*/
if (ptiCurrent->ppi != NULL && ptiCurrent->ppi->ptiMainThread == ptiCurrent) {
ptiCurrent->ppi->ptiMainThread = NULL;
}
while (ptiCurrent->psiiList != NULL) {
xxxDestroyThreadDDEObject(ptiCurrent, ptiCurrent->psiiList);
}
if (ptiCurrent->TIF_flags & TIF_PALETTEAWARE) {
PWND pwnd;
TL tlpwnd;
UserAssert(ptiCurrent->rpdesk != NULL);
pwnd = ptiCurrent->rpdesk->pDeskInfo->spwnd;
if (pwnd) {
ThreadLock(pwnd, &tlpwnd);
xxxFlushPalette(pwnd);
ThreadUnlock(&tlpwnd);
}
}
/*
* If this is the last GUI thread for the process that made a temporary
* (fullscreen) mode change, restore the mode to what's in the registry.
*/
if (FLastGuiThread(ptiCurrent)) {
if ((gppiFullscreen == ptiCurrent->ppi) && !gbMDEVDisabled) {
LONG Status = xxxUserChangeDisplaySettings(NULL, NULL, NULL, 0, 0, KernelMode);
if (gppiFullscreen == ptiCurrent->ppi) {
RIPMSG1(RIP_WARNING, "xxxUserChangeDisplaySettings failed with status 0x%x", Status);
}
}
}
#ifdef GENERIC_INPUT
/*
* If the raw input is specified in this process,
* destroy thread-related objects and requests.
* This has to be done before the window reparenting.
*/
if (ptiCurrent->ppi && ptiCurrent->ppi->pHidTable) {
DestroyThreadHidObjects(ptiCurrent);
}
#endif
/**************************************************************************\
*
* CLEANING THREAD OBJECTS. AVOID CALLING BACK AFTER THIS POINT.
*
* New objects might be created while calling back and won't be cleaned up.
*
\**************************************************************************/
/*
* This thread might have some outstanding timers. Destroy them.
*/
DestroyThreadsTimers(ptiCurrent);
/*
* Free any windows hooks this thread has created.
*/
FreeThreadsWindowHooks();
/*
* Cleanup any switch window info for any window switch window
* belonging to this thread.
*/
RemoveThreadSwitchWindowInfo(ptiCurrent);
/*
* Free any hwnd lists the thread was using
*/
{
PBWL pbwl, pbwlNext;
for (pbwl = gpbwlList; pbwl != NULL;) {
pbwlNext = pbwl->pbwlNext;
if (pbwl->ptiOwner == ptiCurrent) {
FreeHwndList(pbwl);
}
pbwl = pbwlNext;
}
}
/*
* Destroy all the public objects created by this thread.
*/
DestroyThreadsHotKeys();
DestroyThreadsObjects();
/*
* Free any synchronous Notifies pending for this thread and
* free any Win Event Hooks this thread created.
*/
FreeThreadsWinEvents(ptiCurrent);
/*
* Unlock the keyboard layouts here.
*/
Unlock(&ptiCurrent->spklActive);
/*
* Cleanup the global resources if this is the last GUI thread for this
* session.
*/
if (gdwGuiThreads == 1) {
CleanupResources();
}
if (FLastGuiThread(ptiCurrent)) {
/*
* Check if this was a setup app.
*/
if (ptiCurrent->ppi->W32PF_Flags & W32PF_SETUPAPP) {
PDESKTOPINFO pdeskinfo = GETDESKINFO(ptiCurrent);
if (pdeskinfo->spwndShell) {
_PostMessage(pdeskinfo->spwndShell, DTM_SETUPAPPRAN, 0, 0);
}
}
DestroyProcessesClasses(ptiCurrent->ppi);
ptiCurrent->ppi->W32PF_Flags &= ~(W32PF_CLASSESREGISTERED);
try {
LPBOOL lpClassesRegistered = ptiCurrent->pClientInfo->lpClassesRegistered;
if (lpClassesRegistered) {
ProbeForWrite(lpClassesRegistered, sizeof(BOOL), sizeof(DWORD));
*lpClassesRegistered = FALSE;
}
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
}
DestroyProcessesObjects(ptiCurrent->ppi);
}
/*
* Unlock default input context.
*/
Unlock(&ptiCurrent->spDefaultImc);
if (ptiCurrent->pq != NULL) {
/*
* Remove this thread's cursor count from the queue.
*/
ptiCurrent->pq->iCursorLevel -= ptiCurrent->iCursorLevel;
/*
* Have to recalc queue ownership after this thread leaves if it is
* a member of a shared input queue.
*/
if (ptiCurrent->pq->cThreads != 1) {
gpdeskRecalcQueueAttach = ptiCurrent->rpdesk;
/*
* Because we are in thread cleanup, we won't callback due to
* WinEvents (zzzSetFMouseMoved calls zzzUpdateCursorImage).
*/
UserAssert(ptiCurrent->TIF_flags & TIF_INCLEANUP);
UserAssert(gbExitInProgress == FALSE);
zzzSetFMouseMoved();
}
}
/*
* Remove from the process' list, also.
*/
ppti = &PpiCurrent()->ptiList;
if (*ppti != NULL) {
while (*ppti != ptiCurrent && (*ppti)->ptiSibling != NULL) {
ppti = &((*ppti)->ptiSibling);
}
if (*ppti == ptiCurrent) {
*ppti = ptiCurrent->ptiSibling;
ptiCurrent->ptiSibling = NULL;
}
}
{
PDESKTOP rpdesk;
PATTACHINFO *ppai;
/*
* Temporarily lock the desktop until the THREADINFO structure is
* freed. Note that locking a NULL ptiCurrent->rpdesk is OK. Use a
* normal lock instead of a thread lock because the lock must
* exist past the freeing of the ptiCurrent.
*/
rpdesk = NULL;
LockDesktop(&rpdesk, ptiCurrent->rpdesk, LDL_FN_DESTROYTHREADINFO, (ULONG_PTR)PtiCurrent());
/*
* Cleanup SMS structures attached to this thread. Handles both
* pending send and receive messages. MUST make sure we do
* SendMsgCleanup AFTER window cleanup.
*/
SendMsgCleanup(ptiCurrent);
/*
* Allow this thread to be swapped
*/
if (ptiCurrent->cEnterCount) {
BOOLEAN bool;
RIPMSG1(RIP_WARNING,
"Thread exiting with stack locked. pti: 0x%p",
ptiCurrent);
bool = KeSetKernelStackSwapEnable(TRUE);
ptiCurrent->cEnterCount = 0;
UserAssert(!bool);
}
if (ptiCurrent->ppi != NULL) {
ptiCurrent->ppi->cThreads--;
UserAssert(ptiCurrent->ppi->cThreads >= 0);
}
/*
* If this thread is a win16 task, remove it from the scheduler.
*/
if (ptiCurrent->TIF_flags & TIF_16BIT) {
if ((ptiCurrent->ptdb) && (ptiCurrent->ptdb->hTaskWow != 0)) {
_WOWCleanup(NULL, ptiCurrent->ptdb->hTaskWow);
}
DestroyTask(ptiCurrent->ppi, ptiCurrent);
}
if (ptiCurrent->hEventQueueClient != NULL) {
NTSTATUS Status;
Status = ProtectHandle(ptiCurrent->hEventQueueClient, *ExEventObjectType, FALSE);
if (NT_SUCCESS(Status)) {
ObCloseHandle(ptiCurrent->hEventQueueClient, UserMode);
} else {
RIPMSG1(RIP_WARNING, "xxxDestroyThreadInfo: failed to unprotect the hEventQueueClient handle, status=%08x", Status);
}
ptiCurrent->hEventQueueClient = NULL;
}
if (gspwndInternalCapture != NULL) {
if (GETPTI(gspwndInternalCapture) == ptiCurrent) {
Unlock(&gspwndInternalCapture);
}
}
/*
* Set gptiForeground to NULL if equal to this pti before exiting
* this routine.
*/
if (gptiForeground == ptiCurrent) {
/*
* Post these (WEF_ASYNC), since we can't make callbacks from here.
*/
xxxWindowEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT, INDEXID_CONTAINER, WEF_ASYNC);
xxxWindowEvent(EVENT_SYSTEM_FOREGROUND, NULL, OBJID_WINDOW, INDEXID_CONTAINER, WEF_ASYNC);
/*
* Call the Shell to ask it to activate its main window. This
* will be accomplished with a PostMessage() to itself, so the
* actual activation will take place later.
*/
UserAssert(rpdesk != NULL);
if (rpdesk->pDeskInfo->spwndProgman) {
_PostMessage(rpdesk->pDeskInfo->spwndProgman, guiActivateShellWindow, 0, 0);
}
/*
* Set gptiForeground to NULL because we're destroying it.
*/
SetForegroundThread(NULL);
/*
* If this thread is attached to gpqForeground AND it's the
* last thread in the queue, then zzzDestroyQueue will NULL out
* qpqForeground. Due to journalling attaching, gptiForegrouund
* is not always attached to gpqForeground. This is one reason
* why we no longer NULL out gpqForeground as stated in the old
* comment. The other reason is that there might be other threads
* in the foreground queue so there is no need to zap it. This was
* messing up MsTest (now called VisualTest)
* This is the old comment:
* "Since gpqForeground is derived from the foreground thread
* structure, set it to NULL as well, since there now is no
* foreground thread structure"
*
* qpqForeground = NULL;
*/
}
/*
* If this thread got the last input event, pass ownership to another
* thread in this process or to the foreground thread.
*/
if (ptiCurrent == glinp.ptiLastWoken) {
UserAssert(PpiCurrent() == ptiCurrent->ppi);
if (ptiCurrent->ppi->ptiList != NULL) {
UserAssert (ptiCurrent != ptiCurrent->ppi->ptiList);
glinp.ptiLastWoken = ptiCurrent->ppi->ptiList;
} else {
glinp.ptiLastWoken = gptiForeground;
}
}
/*
* Make sure none of the other global thread pointers are pointing to us.
*/
if (gptiShutdownNotify == ptiCurrent) {
gptiShutdownNotify = NULL;
}
if (gptiTasklist == ptiCurrent) {
gptiTasklist = NULL;
}
if (gHardErrorHandler.pti == ptiCurrent) {
gHardErrorHandler.pti = NULL;
}
/*
* Might be called from xxxCreateThreadInfo before the queue is created
* so check for NULL queue. Lock the queues since this pti might be
* locked. They will be unlocked in UserDeleteW32Thread
*/
if (ptiCurrent->pq != NULL) {
UserAssert(ptiCurrent->pq != ptiCurrent->pqAttach);
DestroyThreadsMessages(ptiCurrent->pq, ptiCurrent);
(ptiCurrent->pq->cLockCount)++;
zzzDestroyQueue(ptiCurrent->pq, ptiCurrent);
}
/*
* zzzReattachThreads shouldn't call back while using pqAttach
*/
UserAssert(ptiCurrent->pqAttach == NULL);
/*
* Remove the pti from its pti list and reset the pointers.
*/
if (ptiCurrent->rpdesk != NULL) {
RemoveEntryList(&ptiCurrent->PtiLink);
InitializeListHead(&ptiCurrent->PtiLink);
}
FreeMessageList(&ptiCurrent->mlPost);
/*
* Free any attachinfo structures pointing to this thread
*/
ppai = &gpai;
while ((*ppai) != NULL) {
if ((*ppai)->pti1 == ptiCurrent || (*ppai)->pti2 == ptiCurrent) {
PATTACHINFO paiKill = *ppai;
*ppai = (*ppai)->paiNext;
UserFreePool((HLOCAL)paiKill);
} else {
ppai = &(*ppai)->paiNext;
}
}
/*
* Change ownership of any objects that didn't get freed (because they
* are locked or we have a bug and the object didn't get destroyed).
*/
MarkThreadsObjects(ptiCurrent);
/*
* Free thread information visible from client
*/
if (rpdesk && ptiCurrent->pcti != NULL && ptiCurrent->pcti != &(ptiCurrent->cti)) {
DesktopFree(rpdesk, ptiCurrent->pcti);
ptiCurrent->pcti = &(ptiCurrent->cti);
}
/*
* Free the client info for system threads.
*/
if (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD && ptiCurrent->pClientInfo != NULL) {
UserFreePool(ptiCurrent->pClientInfo);
ptiCurrent->pClientInfo = NULL;
}
/*
* Unlock the temporary desktop lock. ptiCurrent->rpdesk is still locked
* and will be unlocked in UserDeleteW32Thread.
*/
UnlockDesktop(&rpdesk, LDU_FN_DESTROYTHREADINFO, (ULONG_PTR)PtiCurrent());
}
/*
* One more thread died.
*/
gdwGuiThreads--;
}
/***************************************************************************\
* CleanEventMessage
*
* This routine takes a message and destroys and event message related pieces,
* which may be allocated.
*
* 12-10-92 ScottLu Created.
\***************************************************************************/
VOID CleanEventMessage(
PQMSG pqmsg)
{
PASYNCSENDMSG pmsg;
/*
* Certain special messages on the INPUT queue have associated
* bits of memory that need to be freed.
*/
switch (pqmsg->dwQEvent) {
case QEVENT_SETWINDOWPOS:
UserFreePool((PSMWP)pqmsg->msg.wParam);
break;
case QEVENT_UPDATEKEYSTATE:
UserFreePool((PBYTE)pqmsg->msg.wParam);
break;
case QEVENT_NOTIFYWINEVENT:
DestroyNotify((PNOTIFY)pqmsg->msg.lParam);
break;
case QEVENT_ASYNCSENDMSG:
pmsg = (PASYNCSENDMSG)pqmsg->msg.wParam;
UserDeleteAtom((ATOM)pmsg->lParam);
UserFreePool(pmsg);
break;
}
}
/***************************************************************************\
* FreeMessageList
*
* History:
* 02-27-91 mikeke Created.
* 11-03-92 scottlu Changed to work with MLIST structure.
\***************************************************************************/
VOID FreeMessageList(
PMLIST pml)
{
PQMSG pqmsg;
DebugValidateMLIST(pml);
while ((pqmsg = pml->pqmsgRead) != NULL) {
CleanEventMessage(pqmsg);
DelQEntry(pml, pqmsg);
}
DebugValidateMLIST(pml);
}
/***************************************************************************\
* DestroyThreadsMessages
*
* History:
* 02-21-96 jerrysh Created.
\***************************************************************************/
VOID DestroyThreadsMessages(
PQ pq,
PTHREADINFO pti)
{
PQMSG pqmsg;
PQMSG pqmsgNext;
DebugValidateMLIST(&pq->mlInput);
pqmsg = pq->mlInput.pqmsgRead;
while (pqmsg != NULL) {
pqmsgNext = pqmsg->pqmsgNext;
if (pqmsg->pti == pti) {
/*
* Make sure we don't leave any bogus references to this message
* lying around.
*/
if (pq->idSysPeek == (ULONG_PTR)pqmsg) {
CheckPtiSysPeek(8, pq, 0);
pq->idSysPeek = 0;
}
CleanEventMessage(pqmsg);
DelQEntry(&pq->mlInput, pqmsg);
}
pqmsg = pqmsgNext;
}
DebugValidateMLIST(&pq->mlInput);
}
/***************************************************************************\
* InitQEntryLookaside
*
* Initializes the Q entry lookaside list. This improves Q entry locality
* by keeping Q entries in a single page
*
* 09-09-93 Markl Created.
\***************************************************************************/
NTSTATUS
InitQEntryLookaside(
VOID)
{
QEntryLookaside = Win32AllocPoolNonPagedNS(sizeof(PAGED_LOOKASIDE_LIST),
TAG_LOOKASIDE);
if (QEntryLookaside == NULL) {
return STATUS_NO_MEMORY;
}
ExInitializePagedLookasideList(QEntryLookaside,
NULL,
NULL,
gSessionPoolMask,
sizeof(QMSG),
TAG_QMSG,
16);
QLookaside = Win32AllocPoolNonPagedNS(sizeof(PAGED_LOOKASIDE_LIST),
TAG_LOOKASIDE);
if (QLookaside == NULL) {
return STATUS_NO_MEMORY;
}
ExInitializePagedLookasideList(QLookaside,
NULL,
NULL,
gSessionPoolMask,
sizeof(Q),
TAG_Q,
16);
return STATUS_SUCCESS;
}
/***************************************************************************\
* AllocQEntry
*
* Allocates a message on a message list. DelQEntry deletes a message
* on a message list.
*
* 10-22-92 ScottLu Created.
\***************************************************************************/
PQMSG AllocQEntry(
PMLIST pml)
{
PQMSG pqmsg;
DebugValidateMLIST(pml);
if (pml->cMsgs >= gUserPostMessageLimit) {
RIPERR3(ERROR_NOT_ENOUGH_QUOTA, RIP_VERBOSE, "AllocQEntry: # of post messages exceeds the limit(%d) in pti=%#p, pml=%#p",
gUserPostMessageLimit, W32GetCurrentThread(), pml);
return NULL;
}
/*
* Allocate a Q message structure.
*/
if ((pqmsg = ExAllocateFromPagedLookasideList(QEntryLookaside)) == NULL) {
RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_VERBOSE, "AllocQEntry: allocation failed, not enough memory");
return NULL;
}
RtlZeroMemory(pqmsg, sizeof(*pqmsg));
if (pml->pqmsgWriteLast != NULL) {
pml->pqmsgWriteLast->pqmsgNext = pqmsg;
pqmsg->pqmsgPrev = pml->pqmsgWriteLast;
pml->pqmsgWriteLast = pqmsg;
} else {
pml->pqmsgWriteLast = pml->pqmsgRead = pqmsg;
}
pml->cMsgs++;
DebugValidateMLISTandQMSG(pml, pqmsg);
return pqmsg;
}
/***************************************************************************\
* DelQEntry
*
* Simply removes a message from a message queue list.
*
* 10-20-92 ScottLu Created.
\***************************************************************************/
VOID DelQEntry(
PMLIST pml,
PQMSG pqmsg)
{
DebugValidateMLISTandQMSG(pml, pqmsg);
UserAssert((int)pml->cMsgs > 0);
UserAssert(pml->pqmsgRead);
UserAssert(pml->pqmsgWriteLast);
/*
* Unlink this pqmsg from the message list.
*/
if (pqmsg->pqmsgPrev != NULL)
pqmsg->pqmsgPrev->pqmsgNext = pqmsg->pqmsgNext;
if (pqmsg->pqmsgNext != NULL)
pqmsg->pqmsgNext->pqmsgPrev = pqmsg->pqmsgPrev;
/*
* Update the read/write pointers if necessary.
*/
if (pml->pqmsgRead == pqmsg)
pml->pqmsgRead = pqmsg->pqmsgNext;
if (pml->pqmsgWriteLast == pqmsg)
pml->pqmsgWriteLast = pqmsg->pqmsgPrev;
/*
* Adjust the message count and free the message structure.
*/
pml->cMsgs--;
ExFreeToPagedLookasideList(QEntryLookaside, pqmsg);
DebugValidateMLIST(pml);
}
/***************************************************************************\
* CheckRemoveHotkeyBit
*
* We have a special bit for the WM_HOTKEY message - QS_HOTKEY. When there
* is a WM_HOTKEY message in the queue, that bit is on. When there isn't,
* that bit is off. This checks for more than one hot key, because the one
* is about to be deleted. If there is only one, the hot key bits are cleared.
*
* 11-12-92 ScottLu Created.
\***************************************************************************/
VOID CheckRemoveHotkeyBit(
PTHREADINFO pti,
PMLIST pml)
{
PQMSG pqmsg;
DWORD cHotkeys;
/*
* Remove the QS_HOTKEY bit if there is only one WM_HOTKEY message
* in this message list.
*/
cHotkeys = 0;
for (pqmsg = pml->pqmsgRead; pqmsg != NULL; pqmsg = pqmsg->pqmsgNext) {
if (pqmsg->msg.message == WM_HOTKEY)
cHotkeys++;
}
/*
* If there is 1 or fewer hot keys, remove the hotkey bits.
*/
if (cHotkeys <= 1) {
pti->pcti->fsWakeBits &= ~QS_HOTKEY;
pti->pcti->fsChangeBits &= ~QS_HOTKEY;
}
}
/***************************************************************************\
* FindQMsg
*
* Finds a qmsg that fits the filters by looping through the message list.
*
* 10-20-92 ScottLu Created.
* 06-06-97 CLupu added processing for WM_DDE_ACK messages
\***************************************************************************/
PQMSG FindQMsg(
PTHREADINFO pti,
PMLIST pml,
PWND pwndFilter,
UINT msgMin,
UINT msgMax,
BOOL bProcessAck)
{
PWND pwnd;
PQMSG pqmsgRead;
PQMSG pqmsgRet = NULL;
UINT message;
DebugValidateMLIST(pml);
pqmsgRead = pml->pqmsgRead;
while (pqmsgRead != NULL) {
/*
* Make sure this window is valid and doesn't have the destroy
* bit set (don't want to send it to any client side window procs
* if destroy window has been called on it).
*/
pwnd = RevalidateHwnd(pqmsgRead->msg.hwnd);
if (pwnd == NULL && pqmsgRead->msg.hwnd != NULL) {
/*
* If we're removing a WM_HOTKEY message, we may need to
* clear the QS_HOTKEY bit, since we have a special bit
* for that message.
*/
if (pqmsgRead->msg.message == WM_HOTKEY) {
CheckRemoveHotkeyBit(pti, pml);
}
/*
* If the current thread's queue is locked waiting for this message,
* we have to unlock it because we're eating the message. If there's
* no more input/messages for this thread, the thread is going to
* sleep; hence there might not be a next Get/PeekMessage call to
* unlock the queue (ie, updating pti->idLast is not enough);
* so we must unlock it now.
* Win95 doesn't have this problem because their FindQMsg doesn't
* eat messages; they call ReadPostMessage from FreeWindow
* to take care of this scenario (== message for a destroyed window).
* We could also do this if we have some problems with this fix.
*/
if ((pti->pq->idSysLock == (ULONG_PTR)pqmsgRead)
&& (pti->pq->ptiSysLock == pti)) {
/* CheckSysLock(What number?, pti->pq, NULL); */
RIPMSG2(RIP_VERBOSE, "FindQMsg: Unlocking queue:%#p. Msg:%#lx",
pti->pq, pqmsgRead->msg.message);
pti->pq->ptiSysLock = NULL;
}
DelQEntry(pml, pqmsgRead);
goto nextMsgFromPml;
}
/*
* Process the WM_DDE_ACK messages if bProcessAck is set.
*/
if (bProcessAck && (PtoH(pwndFilter) == pqmsgRead->msg.hwnd) &&
(pqmsgRead->msg.message == (WM_DDE_ACK | MSGFLAG_DDE_MID_THUNK))) {
PXSTATE pxs;
pxs = (PXSTATE)HMValidateHandleNoRip((HANDLE)pqmsgRead->msg.lParam, TYPE_DDEXACT);
if (pxs != NULL && (pxs->flags & XS_FREEPXS)) {
FreeDdeXact(pxs);
DelQEntry(pml, pqmsgRead);
goto nextMsgFromPml;
}
}
/*
* Make sure this message fits both window handle and message
* filters.
*/
if (!CheckPwndFilter(pwnd, pwndFilter))
goto nextMsg;
/*
* If this is a fixed up dde message, then turn it into a normal
* dde message for the sake of message filtering.
*/
message = pqmsgRead->msg.message;
if (CheckMsgFilter(message,
(WM_DDE_FIRST + 1) | MSGFLAG_DDE_MID_THUNK,
WM_DDE_LAST | MSGFLAG_DDE_MID_THUNK)) {
message = message & ~MSGFLAG_DDE_MID_THUNK;
}
if (!CheckMsgFilter(message, msgMin, msgMax))
goto nextMsg;
/*
* Found it. If bProcessAck is set, remember this pointer and go on
* till we finish walking the list to process all WM_DDE_ACK messages.
*/
if (!bProcessAck) {
DebugValidateMLIST(pml);
return pqmsgRead;
}
if (pqmsgRet == NULL) {
pqmsgRet = pqmsgRead;
}
nextMsg:
pqmsgRead = pqmsgRead->pqmsgNext;
continue;
nextMsgFromPml:
pqmsgRead = pml->pqmsgRead;
continue;
}
DebugValidateMLIST(pml);
return pqmsgRet;
}
/***************************************************************************\
* CheckQuitMessage
*
* Checks to see if a WM_QUIT message should be generated.
*
* 11-06-92 ScottLu Created.
\***************************************************************************/
BOOL CheckQuitMessage(
PTHREADINFO pti,
LPMSG lpMsg,
BOOL fRemoveMsg)
{
/*
* If there are no more posted messages in the queue and the app
* has already called PostQuitMessage, then generate a quit.
*/
if ((pti->TIF_flags & TIF_QUITPOSTED) && pti->mlPost.cMsgs == 0) {
/*
* If we're "removing" the quit, clear TIF_QUITPOSTED so another one
* isn't generated.
*/
if (fRemoveMsg) {
pti->TIF_flags &= ~TIF_QUITPOSTED;
}
StoreMessage(lpMsg, NULL, WM_QUIT, (DWORD)pti->exitCode, 0, 0);
return TRUE;
}
return FALSE;
}
/***************************************************************************\
* ReadPostMessage
*
* If queue is not empty, read message satisfying filter conditions from
* this queue to *lpMsg.
*
* 10-19-92 ScottLu Created.
\***************************************************************************/
BOOL xxxReadPostMessage(
PTHREADINFO pti,
LPMSG lpMsg,
PWND pwndFilter,
UINT msgMin,
UINT msgMax,
BOOL fRemoveMsg)
{
PQMSG pqmsg;
PMLIST pmlPost;
/*
* Check to see if it is time to generate a quit message.
*/
if (CheckQuitMessage(pti, lpMsg, fRemoveMsg)) {
return TRUE;
}
/*
* Loop through the messages in this list looking for the one that
* fits the passed in filters.
*/
pmlPost = &pti->mlPost;
pqmsg = FindQMsg(pti, pmlPost, pwndFilter, msgMin, msgMax, FALSE);
if (pqmsg == NULL) {
/*
* Check again for quit... FindQMsg deletes some messages
* in some instances, so we may match the conditions
* for quit generation here.
*/
if (CheckQuitMessage(pti, lpMsg, fRemoveMsg)) {
return TRUE;
}
} else {
/*
* Update the thread info fields with the info from this qmsg.
*/
pti->timeLast = pqmsg->msg.time;
if (!RtlEqualMemory(&pti->ptLast, &pqmsg->msg.pt, sizeof(POINT))) {
pti->TIF_flags |= TIF_MSGPOSCHANGED;
}
pti->ptLast = pqmsg->msg.pt;
pti->idLast = (ULONG_PTR)pqmsg;
pti->pq->ExtraInfo = pqmsg->ExtraInfo;
/*
* Are we supposed to yank out the message? If not, stick some
* random id into idLast so we don't unlock the input queue until we
* pull this message from the queue.
*/
*lpMsg = pqmsg->msg;
if (!fRemoveMsg) {
pti->idLast = 1;
} else {
/*
* If we're removing a WM_HOTKEY message, we may need to
* clear the QS_HOTKEY bit, since we have a special bit
* for that message.
*/
if (pmlPost->pqmsgRead->msg.message == WM_HOTKEY) {
CheckRemoveHotkeyBit(pti, pmlPost);
}
/*
* Since we're removing an event from the queue, we
* need to check priority. This resets the TIF_SPINNING
* since we're no longer spinning.
*/
if (pti->TIF_flags & TIF_SPINNING) {
if (!NT_SUCCESS(CheckProcessForeground(pti))) {
return FALSE;
}
}
DelQEntry(pmlPost, pqmsg);
}
/*
* See if this is a dde message that needs to be fixed up.
*/
if (CheckMsgFilter(lpMsg->message,
(WM_DDE_FIRST + 1) | MSGFLAG_DDE_MID_THUNK,
WM_DDE_LAST | MSGFLAG_DDE_MID_THUNK)) {
/*
* Fixup the message value.
*/
lpMsg->message &= (UINT)~MSGFLAG_DDE_MID_THUNK;
/*
* Call back the client to allocate the dde data for this message.
*/
xxxDDETrackGetMessageHook(lpMsg);
/*
* Copy these values back into the queue if this message hasn't
* been removed from the queue. Need to search through the
* queue again because the pqmsg may have been removed when
* we left the critical section above.
*/
if (!fRemoveMsg) {
if (pqmsg == FindQMsg(pti, pmlPost, pwndFilter, msgMin, msgMax, FALSE)) {
pqmsg->msg = *lpMsg;
}
}
}
#if DBG
else if (CheckMsgFilter(lpMsg->message, WM_DDE_FIRST, WM_DDE_LAST)) {
if (fRemoveMsg) {
TraceDdeMsg(lpMsg->message, (HWND)lpMsg->wParam, lpMsg->hwnd, MSG_RECV);
} else {
TraceDdeMsg(lpMsg->message, (HWND)lpMsg->wParam, lpMsg->hwnd, MSG_PEEK);
}
}
#endif
}
/*
* If there are no posted messages available, clear the post message
* bit so we don't go looking for them again.
*/
if (pmlPost->cMsgs == 0 && !(pti->TIF_flags & TIF_QUITPOSTED)) {
pti->pcti->fsWakeBits &= ~(QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
pti->pcti->fsChangeBits &= ~QS_ALLPOSTMESSAGE;
}
return pqmsg != NULL;
}
#ifdef HUNGAPP_GHOSTING
/***************************************************************************\
* xxxProcessHungThreadEvent
*
* We check when a thread gets unhung when it reads the posted queue message.
*
* 6-10-99 vadimg created
\***************************************************************************/
VOID xxxProcessHungThreadEvent(
PWND pwnd)
{
PTHREADINFO ptiCurrent = PtiCurrent();
PWND pwndGhost;
HWND hwnd, hwndGhost;
TL tlpwndT1, tlpwndT2;
BOOL fUnlockGhost = FALSE;
CheckLock(pwnd);
/*
* The app processed this queue message, so update time last read
* used for hung app calculations.
*/
SET_TIME_LAST_READ(ptiCurrent);
pwndGhost = FindGhost(pwnd);
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT1);
if (pwndGhost != NULL) {
ThreadLockAlwaysWithPti(ptiCurrent, pwndGhost, &tlpwndT2);
fUnlockGhost = TRUE;
/*
* Try to set the state of the hung window to the current state of
* the ghost window.
*/
if (TestWF(pwndGhost, WFMAXIMIZED)) {
xxxMinMaximize(pwnd, SW_MAXIMIZE, MINMAX_KEEPHIDDEN);
} else if (TestWF(pwndGhost, WFMINIMIZED)) {
xxxMinMaximize(pwnd, SW_SHOWMINNOACTIVE, MINMAX_KEEPHIDDEN);
} else {
DWORD dwFlags;
PTHREADINFO pti = GETPTI(pwndGhost);
/*
* If the ghost is the active foreground window, allow this
* activation to bring the hung window to the foreground.
*/
if (pti->pq == gpqForeground && pti->pq->spwndActive == pwndGhost) {
dwFlags = 0;
GETPTI(pwnd)->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
} else {
dwFlags = SWP_NOACTIVATE;
}
/*
* Unless the user explicitly moved or sized the ghost window,
* Don't copy it's location and size to the unhung window.
* See bug#s 415519 and 413418
*/
if (!GhostSizedOrMoved(pwnd)) {
dwFlags |= (SWP_NOMOVE | SWP_NOSIZE) ;
}
/*
* This will appropriately zorder, activate, and position the
* hung window.
*/
xxxSetWindowPos(pwnd, pwndGhost,
pwndGhost->rcWindow.left, pwndGhost->rcWindow.top,
pwndGhost->rcWindow.right - pwndGhost->rcWindow.left,
pwndGhost->rcWindow.bottom - pwndGhost->rcWindow.top,
dwFlags);
}
}
/*
* Toggle the visible bit of the hung window and remove the ghost window
* corresponding to this previously hung window.
*/
if (TestWF(pwnd, WEFGHOSTMAKEVISIBLE)) {
SetVisible(pwnd, SV_SET);
}
RemoveGhost(pwnd);
/*
* Make the shell aware again of the hung window.
*/
hwnd = PtoHq(pwnd);
hwndGhost = PtoH(pwndGhost);
PostShellHookMessages(HSHELL_WINDOWREPLACING, (LPARAM)hwnd);
PostShellHookMessages(HSHELL_WINDOWREPLACED, (LPARAM)hwndGhost);
xxxCallHook(HSHELL_WINDOWREPLACED, (WPARAM)hwndGhost, (LPARAM)hwnd, WH_SHELL);
/*
* Completely invalidate the hung window, since it became visible again.
*/
if (TestWF(pwnd, WEFGHOSTMAKEVISIBLE)) {
xxxRedrawWindow(pwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE |
RDW_ALLCHILDREN | RDW_FRAME);
}
if (fUnlockGhost) {
ThreadUnlock(&tlpwndT2);
}
ThreadUnlock(&tlpwndT1);
}
#else // HUNGAPP_GHOSTING
VOID xxxProcessHungThreadEvent(
PWND pwnd)
{
CheckLock(pwnd);
if (TestWF(pwnd, WFVISIBLE)) {
RIPMSG0(RIP_WARNING, "xxxProcessHungThreadEvent: window is already visible");
} else {
SetVisible(pwnd, SV_SET);
if (TestWF(pwnd, WFMINIMIZED)) {
RIPMSG0(RIP_WARNING, "xxxProcessHungThreadEvent: window is already minmized");
} else {
xxxMinMaximize(pwnd, SW_SHOWMINNOACTIVE, MINMAX_KEEPHIDDEN);
}
}
}
#endif
#ifndef _WIN64
BEEPPROC pfnBP[] = {
UpSiren,
DownSiren,
LowBeep,
HighBeep,
KeyClick};
#endif
/***************************************************************************\
* xxxProcessEventMessage
*
* This handles our processing for 'event' messages. We return a BOOL here
* telling the system whether or not to continue processing messages.
*
* History:
* 06-17-91 DavidPe Created.
\***************************************************************************/
VOID xxxProcessEventMessage(
PTHREADINFO ptiCurrent,
PQMSG pqmsg)
{
PWND pwnd;
TL tlpwndT;
TL tlMsg;
PQ pq;
UserAssert(IsWinEventNotifyDeferredOK());
UserAssert(ptiCurrent == PtiCurrent());
ThreadLockPoolCleanup(ptiCurrent, pqmsg, &tlMsg, CleanEventMessage);
pq = ptiCurrent->pq;
switch (pqmsg->dwQEvent) {
case QEVENT_DESTROYWINDOW:
/*
* These events are posted from xxxDW_DestroyOwnedWindows
* for owned windows that are not owned by the owner
* window thread.
*/
pwnd = RevalidateHwnd((HWND)pqmsg->msg.wParam);
if (pwnd != NULL) {
if (!TestWF(pwnd, WFCHILD)) {
xxxDestroyWindow(pwnd);
} else {
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
xxxFreeWindow(pwnd, &tlpwndT);
}
}
break;
case QEVENT_SHOWWINDOW:
/*
* These events are mainly used from within CascadeChildWindows()
* and TileChildWindows() so that taskmgr doesn't hang while calling
* these apis if it is trying to tile or cascade a hung application.
*/
/* The HIWORD of lParam now has the preserved state of gfAnimate at the
* time of the call.
*/
pwnd = RevalidateHwnd((HWND)pqmsg->msg.wParam);
if (pwnd != NULL && !TestWF(pwnd, WFINDESTROY)) {
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
xxxShowWindow(pwnd, (DWORD)pqmsg->msg.lParam);
/*
* If this is coming from an async SetWindowPlacement, update the
* check point settings if the window is minimized.
*/
if ((pqmsg->msg.message & WPF_ASYNCWINDOWPLACEMENT)
&& TestWF(pwnd, WFMINIMIZED)) {
WPUpdateCheckPointSettings(pwnd, (UINT)pqmsg->msg.message);
}
ThreadUnlock(&tlpwndT);
}
break;
case QEVENT_NOTIFYWINEVENT:
UserAssert(((PNOTIFY)pqmsg->msg.lParam)->dwWEFlags & WEF_POSTED);
UserAssert(((PNOTIFY)pqmsg->msg.lParam)->dwWEFlags & WEF_ASYNC);
xxxProcessNotifyWinEvent((PNOTIFY)pqmsg->msg.lParam);
break;
case QEVENT_SETWINDOWPOS:
/*
* QEVENT_SETWINDOWPOS events are generated when a thread calls
* SetWindowPos with a list of windows owned by threads other than
* itself. This way all WINDOWPOSing on a window is done the thread
* that owns (created) the window and we don't have any of those
* nasty inter-thread synchronization problems.
*/
xxxProcessSetWindowPosEvent((PSMWP)pqmsg->msg.wParam);
break;
case QEVENT_UPDATEKEYSTATE:
/*
* Update the local key state with the state from those
* keys that have changed since the last time key state
* was synchronized.
*/
ProcessUpdateKeyStateEvent(pq, (PBYTE)pqmsg->msg.wParam, (PBYTE)pqmsg->msg.wParam + CBKEYSTATE);
break;
case QEVENT_ACTIVATE:
{
if (pqmsg->msg.lParam == 0) {
/*
* Clear any visible tracking going on in system. We
* only bother to do this if lParam == 0 since
* xxxSetForegroundWindow2() deals with this in the
* other case.
*/
xxxCancelTracking();
/*
* Remove the clip cursor rectangle - it is a global mode that
* gets removed when switching. Also remove any LockWindowUpdate()
* that's still around.
*/
zzzClipCursor(NULL);
LockWindowUpdate2(NULL, TRUE);
/*
* Reload pq because it may have changed.
*/
pq = ptiCurrent->pq;
/*
* If this event didn't originate from an initializing app
* coming to the foreground [wParam == 0] then go ahead
* and check if there's already an active window and if so make
* it visually active. Also make sure we're still the foreground
* queue.
*/
if ((pqmsg->msg.wParam != 0) && (pq->spwndActive != NULL) &&
(pq == gpqForeground)) {
PWND pwndActive;
ThreadLockAlwaysWithPti(ptiCurrent, pwndActive = pq->spwndActive, &tlpwndT);
xxxSendMessage(pwndActive, WM_NCACTIVATE, TRUE, 0);
xxxUpdateTray(pwndActive);
xxxSetWindowPos(pwndActive, PWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
ThreadUnlock(&tlpwndT);
} else if (pq != gpqForeground) {
/*
* If we're not being activated, make sure we don't become foreground.
*/
ptiCurrent->TIF_flags &= ~TIF_ALLOWFOREGROUNDACTIVATE;
TAGMSG1(DBGTAG_FOREGROUND, "xxxProcessEventMessage clear TIF %#p", ptiCurrent);
ptiCurrent->ppi->W32PF_Flags &= ~W32PF_ALLOWFOREGROUNDACTIVATE;
TAGMSG1(DBGTAG_FOREGROUND, "xxxProcessEventMessage clear W32PF %#p", ptiCurrent->ppi);
}
} else {
pwnd = RevalidateHwnd((HWND)pqmsg->msg.lParam);
if (pwnd == NULL)
break;
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
/*
* If nobody is foreground, allow this app to become foreground.
*/
if (gpqForeground == NULL) {
xxxSetForegroundWindow2(pwnd, ptiCurrent, 0);
} else {
if (pwnd != pq->spwndActive) {
if (xxxActivateThisWindow(pwnd, (UINT)pqmsg->msg.wParam,
(ATW_SETFOCUS | ATW_ASYNC) |
((pqmsg->msg.message & PEM_ACTIVATE_NOZORDER) ? ATW_NOZORDER : 0))) {
/*
* This event was posted by SetForegroundWindow2
* (i.e. pqmsg->msg.lParam != 0) so make sure
* mouse is on this window.
*/
if (TestUP(ACTIVEWINDOWTRACKING)) {
zzzActiveCursorTracking(pwnd);
}
}
} else {
BOOL fActive = (GETPTI(pwnd)->pq == gpqForeground);
xxxSendMessage(pwnd, WM_NCACTIVATE,
(DWORD)(fActive), 0);
if (fActive) {
xxxUpdateTray(pwnd);
}
/*
* Only bring the window to the top if it is becoming active.
*/
if (fActive && !(pqmsg->msg.message & PEM_ACTIVATE_NOZORDER))
xxxSetWindowPos(pwnd, PWND_TOP, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE);
}
}
/*
* Check here to see if the window needs to be restored. This is a
* hack so that we're compatible with what msmail expects out of
* win3.1 alt-tab. msmail expects to always be active when it gets
* asked to be restored. This will ensure that during alt-tab
* activate.
*/
if (pqmsg->msg.message & PEM_ACTIVATE_RESTORE) {
if (TestWF(pwnd, WFMINIMIZED)) {
_PostMessage(pwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
}
}
ThreadUnlock(&tlpwndT);
}
}
break;
case QEVENT_DEACTIVATE:
xxxDeactivate(ptiCurrent, (DWORD)pqmsg->msg.wParam);
break;
case QEVENT_CANCELMODE:
if (pq->spwndCapture != NULL) {
ThreadLockAlwaysWithPti(ptiCurrent, pq->spwndCapture, &tlpwndT);
xxxSendMessage(pq->spwndCapture, WM_CANCELMODE, 0, 0);
ThreadUnlock(&tlpwndT);
/*
* Set QS_MOUSEMOVE so any sleeping modal loops,
* like the move/size code, will wake up and figure
* out that it should abort.
*/
SetWakeBit(ptiCurrent, QS_MOUSEMOVE);
}
break;
case QEVENT_POSTMESSAGE:
/*
* This event is used in situations where we need to ensure that posted
* messages are processed after previous QEVENT's. Normally, posting a
* queue event and then calling postmessage will result in the posted
* message being seen first by the app (because posted messages are
* processed before input.) Instead we will post a QEVENT_POSTMESSAGE
* instead of doing a postmessage directly, which will result in the
* correct ordering of messages.
*
*/
if (pwnd = RevalidateHwnd((HWND)pqmsg->msg.hwnd)) {
_PostMessage(pwnd,pqmsg->msg.message,pqmsg->msg.wParam,pqmsg->msg.lParam);
}
break;
case QEVENT_ASYNCSENDMSG:
xxxProcessAsyncSendMessage((PASYNCSENDMSG)pqmsg->msg.wParam);
break;
case QEVENT_HUNGTHREAD:
pwnd = RevalidateHwnd((HWND)pqmsg->msg.hwnd);
if (pwnd != NULL) {
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
xxxProcessHungThreadEvent(pwnd);
ThreadUnlock(&tlpwndT);
}
break;
case QEVENT_CANCELMOUSEMOVETRK: {
/*
* hwnd: hwndTrack. message: dwDTFlags.
* wParam: htEx. lParam: dwDTCancel
*/
PDESKTOP pdesk = ptiCurrent->rpdesk;
pwnd = RevalidateHwnd((HWND)pqmsg->msg.hwnd);
/*
* Let's check that the app didn't manage to restart mouse leave
* tracking before we had a chance to cancel it.
*/
UserAssert(!(pqmsg->msg.message & DF_TRACKMOUSELEAVE)
|| !(pdesk->dwDTFlags & DF_TRACKMOUSELEAVE)
|| (PtoHq(pdesk->spwndTrack) != pqmsg->msg.hwnd)
|| !((pdesk->htEx == HTCLIENT) ^ ((int)pqmsg->msg.wParam == HTCLIENT)));
/*
* If we're back tracking at the same spot, bail
*/
if ((pdesk->dwDTFlags & DF_MOUSEMOVETRK)
&& (PtoHq(pdesk->spwndTrack) == pqmsg->msg.hwnd)
&& (pdesk->htEx == (int)pqmsg->msg.wParam)) {
/*
* If we're tracking mouse leave,
*/
break;
}
/*
* Don't nuke the tooltip if it has been reactivated.
*/
if (pdesk->dwDTFlags & DF_TOOLTIPACTIVE) {
pqmsg->msg.lParam &= ~DF_TOOLTIP;
}
/*
* Cancel tracking if the window is still around
*/
if (pwnd != NULL) {
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
xxxCancelMouseMoveTracking(pqmsg->msg.message, pwnd,
(int)pqmsg->msg.wParam,
(DWORD)pqmsg->msg.lParam);
ThreadUnlock(&tlpwndT);
} else if ((pqmsg->msg.lParam & DF_TOOLTIP)
&& (pqmsg->msg.message & DF_TOOLTIPSHOWING)) {
/*
* The window is gone and so must be tracking.
* Just take care of the tooltip which is still showing.
*/
pwnd = pdesk->spwndTooltip;
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
xxxResetTooltip((PTOOLTIPWND)pwnd);
ThreadUnlock(&tlpwndT);
}
}
break;
case QEVENT_RITACCESSIBILITY:
if (IsHooked(ptiCurrent, WHF_SHELL)) {
xxxCallHook((UINT)pqmsg->msg.wParam,
(WPARAM)pqmsg->msg.lParam,
(LPARAM)0,
WH_SHELL);
}
PostShellHookMessages((UINT)pqmsg->msg.wParam, pqmsg->msg.lParam);
break;
case QEVENT_RITSOUND:
/*
* This should only happen on the desktop thread.
*/
#ifndef _WIN64
switch(pqmsg->msg.message) {
case RITSOUND_UPSIREN:
case RITSOUND_DOWNSIREN:
case RITSOUND_LOWBEEP:
case RITSOUND_HIGHBEEP:
case RITSOUND_KEYCLICK:
(pfnBP[pqmsg->msg.message])();
break;
case RITSOUND_DOBEEP:
switch(pqmsg->msg.wParam) {
case RITSOUND_UPSIREN:
case RITSOUND_DOWNSIREN:
case RITSOUND_LOWBEEP:
case RITSOUND_HIGHBEEP:
case RITSOUND_KEYCLICK:
DoBeep(pfnBP[pqmsg->msg.wParam], (DWORD)pqmsg->msg.lParam);
}
break;
}
#else
{
UINT uCount;
UINT uSound;
/*
* The above code uses UserBeep() - which doesn't do anything
* useful on Win64. (IA64 spec doesn't include a h/w speaker.)
*
* Instead, we post a message to WinLogon to ask it to play a sound
* for us using the PlaySound.
*/
/*
* RITSOUND_DOBEEP is used to repeat a sound n times. It is currently
* only used to repeat UPSIREN 2 or 3 times, for stickykeys, when the
* right shift is held down for 12 and 16 seconds.
*/
if (pqmsg->msg.message == RITSOUND_DOBEEP) {
uSound = (UINT) pqmsg->msg.wParam;
uCount = (UINT) pqmsg->msg.lParam;
if (uCount > 5) {
uCount = 5;
}
} else {
uSound = pqmsg->msg.message;
uCount = 1;
}
if (gspwndLogonNotify != NULL) {
while(uCount--) {
_PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY,
LOGON_PLAYEVENTSOUND, MAKELPARAM(uSound, ACCESS_SOUND_RANGE));
}
}
/*
* Since PlaySound doesn't currently flash the window if SoundSentry
* is active, we have to do it ourselces here.
* (On 32-bit, UserBeep() takes care of doing that.)
*/
_UserSoundSentryWorker();
}
#endif
break;
case QEVENT_APPCOMMAND: {
/*
* qevent app commands so we can post a wm_appcommand to the queue
*/
THREADINFO *ptiWindowOwner;
int cmd;
UINT keystate;
/*
* Check the appcommand's are within reasonable ranges. If they aren't
* then we have an internal consistency error since xxxKeyEvent should
* have generated correct ones for us.
*/
UserAssert(pqmsg->msg.lParam >= VK_APPCOMMAND_FIRST &&
pqmsg->msg.lParam <= VK_APPCOMMAND_LAST);
/*
* We need to work out which window to send to here. Using the same
* rules as from xxxScanSysQueue:
* Assign the input to the focus window. If there is no focus
* window, assign it to the active window as a SYS message.
*/
pwnd = ptiCurrent->pq->spwndFocus;
if (!pwnd) {
pwnd = ptiCurrent->pq->spwndActive;
if (!pwnd) {
/*
* At the moment we will just eat the message since we can't find a foreground q
* This follows the method that any other app (eg hidserv) would mimic to
* find the window to send to.
*/
break;
}
}
/*
* We don't want to block on another thread since the xxxSendMessage is a synchronous call
* so we post the message to the queue of the window owner thread
*/
ptiWindowOwner = GETPTI(pwnd);
if (ptiCurrent != ptiWindowOwner) {
/*
* Post the event message to the window who should get it
*/
PostEventMessage(ptiWindowOwner, ptiWindowOwner->pq, QEVENT_APPCOMMAND,
NULL, 0, (WPARAM)0, pqmsg->msg.lParam);
/*
* Break out of this since we've now posted the message to a
* different q - we don't want to deal with it here.
*/
break;
}
cmd = APPCOMMAND_FIRST + ((UINT)pqmsg->msg.lParam - VK_APPCOMMAND_FIRST);
keystate = GetMouseKeyFlags(ptiWindowOwner->pq);
pqmsg->msg.lParam = MAKELPARAM(keystate, cmd);
/*
* Generate a WM_APPCOMMAND message from the keyboard keys.
*/
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
xxxSendMessage(pwnd, WM_APPCOMMAND, (WPARAM)HWq(pwnd), pqmsg->msg.lParam);
ThreadUnlock(&tlpwndT);
break;
}
default:
RIPMSG1(RIP_ERROR,
"xxxProcessEventMessage: Bad dwQEvent: 0x%x",
pqmsg->dwQEvent);
break;
}
ThreadUnlockPoolCleanup(ptiCurrent, &tlMsg);
}
#define QS_TEST_AND_CLEAR (QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_SENDMESSAGE)
#define QS_TEST (QS_MOUSEBUTTON | QS_KEY)
/***************************************************************************\
* _GetInputState (API)
*
* Returns the current input state for mouse buttons or keys.
*
* History:
* 11-06-90 DavidPe Created.
\***************************************************************************/
BOOL _GetInputState(
VOID)
{
if (LOWORD(_GetQueueStatus(QS_TEST_AND_CLEAR)) & QS_TEST) {
return TRUE;
} else {
return FALSE;
}
}
#undef QS_TEST_AND_CLEAR
#undef QS_TEST
/***************************************************************************\
* _GetQueueStatus (API)
*
* Returns the changebits in the lo-word and wakebits in
* the hi-word for the current queue.
*
* History:
* 12-17-90 DavidPe Created.
\***************************************************************************/
DWORD _GetQueueStatus(
UINT flags)
{
PTHREADINFO ptiCurrent;
UINT fsChangeBits;
ptiCurrent = PtiCurrentShared();
flags &= (QS_ALLINPUT | QS_ALLPOSTMESSAGE | QS_TRANSFER);
fsChangeBits = ptiCurrent->pcti->fsChangeBits;
/*
* Clear out the change bits the app is looking at
* so it'll know what changed since it's last call
* to GetQueueStatus().
*/
ptiCurrent->pcti->fsChangeBits &= ~flags;
/*
* Return the current change/wake-bits.
*/
return MAKELONG(fsChangeBits & flags,
(ptiCurrent->pcti->fsWakeBits | ptiCurrent->pcti->fsWakeBitsJournal) & flags);
}
/***************************************************************************\
* xxxMsgWaitForMultipleObjects (API)
*
* Blocks until an 'event' satisifying dwWakeMask occurs for the
* current thread as well as all other objects specified by the other
* parameters which are the same as the base call WaitForMultipleObjects().
*
* pfnNonMsg indicates that pHandles is big enough for nCount+1 handles
* (empty slot at end, and to call pfnNonMsg for non message events).
*
* History:
* 12-17-90 DavidPe Created.
\***************************************************************************/
#ifdef LOCK_MOUSE_CODE
#pragma alloc_text(MOUSE, xxxMsgWaitForMultipleObjects)
#endif
DWORD xxxMsgWaitForMultipleObjects(
DWORD nCount,
PVOID *apObjects,
MSGWAITCALLBACK pfnNonMsg,
PKWAIT_BLOCK WaitBlockArray)
{
PTHREADINFO ptiCurrent = PtiCurrent();
NTSTATUS Status;
ptiCurrent = PtiCurrent();
UserAssert(IsWinEventNotifyDeferredOK());
/*
* Setup the wake mask for this thread. Wait for QS_EVENT or the app won't
* get event messages like deactivate.
*/
ClearQueueServerEvent(QS_ALLINPUT | QS_EVENT);
/*
* Stuff the event handle for the current queue at the end.
*/
apObjects[nCount] = ptiCurrent->pEventQueueServer;
/*
* Check to see if any input came inbetween when we
* last checked and the NtClearEvent() call.
*/
if (!(ptiCurrent->pcti->fsChangeBits & QS_ALLINPUT)) {
/*
* This app is going idle. Clear the spin count check to see
* if we need to make this process foreground again.
*/
if (ptiCurrent->TIF_flags & TIF_SPINNING) {
if (!NT_SUCCESS(Status = CheckProcessForeground(ptiCurrent))) {
return Status;
}
}
try {
ptiCurrent->pClientInfo->cSpins = 0;
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
return GetExceptionCode();
}
if (ptiCurrent == gptiForeground &&
IsHooked(ptiCurrent, WHF_FOREGROUNDIDLE)) {
xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
}
/*
* Set the input idle event to wake up any threads waiting
* for this thread to go into idle state.
*/
zzzWakeInputIdle(ptiCurrent);
Again:
LeaveCrit();
Status = KeWaitForMultipleObjects(nCount + 1, apObjects,
WaitAny, WrUserRequest,
UserMode, FALSE,
NULL, WaitBlockArray);
EnterCrit();
UserAssert(NT_SUCCESS(Status));
if (Status == STATUS_WAIT_0 && pfnNonMsg != NULL) {
/*
* Call pfnNonMsg for the first event
*/
pfnNonMsg(DEVICE_TYPE_MOUSE);
/*
* Setup again the wake mask for this thread.
* Wait for QS_EVENT or the app won't
* get event messages like deactivate.
*/
ptiCurrent->pcti->fsWakeMask = QS_ALLINPUT | QS_EVENT;
goto Again;
}
if (Status == (NTSTATUS)(STATUS_WAIT_0 + nCount)) {
/*
* Reset the input idle event to block and threads waiting
* for this thread to go into idle state.
*/
SleepInputIdle(ptiCurrent);
}
} else {
Status = nCount;
}
/*
* Clear fsWakeMask since we're no longer waiting on the queue.
*/
ptiCurrent->pcti->fsWakeMask = 0;
return (DWORD)Status;
}
/***************************************************************************\
* xxxSleepThread
*
* Blocks until an 'event' satisifying fsWakeMask occurs for the
* current thread.
*
* History:
* 10-28-90 DavidPe Created.
\***************************************************************************/
BOOL xxxSleepThread(
UINT fsWakeMask,
DWORD Timeout,
BOOL fInputIdle)
{
PTHREADINFO ptiCurrent;
LARGE_INTEGER li, *pli;
NTSTATUS status = STATUS_SUCCESS;
BOOL fExclusive = fsWakeMask & QS_EXCLUSIVE;
WORD fsWakeMaskSaved;
BOOL fRet = FALSE;
UserAssert(IsWinEventNotifyDeferredOK());
if (fExclusive) {
/*
* The exclusive bit is a 'dummy' arg, turn it off to
* avoid any possible conflicts.
*/
fsWakeMask = fsWakeMask & ~QS_EXCLUSIVE;
}
if (Timeout) {
/*
* Convert dwMilliseconds to a relative-time (i.e. negative)
* LARGE_INTEGER. NT Base calls take time values in 100 nanosecond
* units.
*/
li.QuadPart = Int32x32To64(-10000, Timeout);
pli = &li;
} else {
pli = NULL;
}
CheckCritIn();
ptiCurrent = PtiCurrent();
fsWakeMaskSaved = ptiCurrent->pcti->fsWakeMask;
while (TRUE) {
/*
* First check if the input has arrived.
*/
if (ptiCurrent->pcti->fsChangeBits & fsWakeMask) {
fRet = TRUE;
GetOutFromHere:
/*
* Restore the wake mask to what it was before we went to sleep
* to allow possible callbacks before KeWait... but after the mask
* has been set and also APCs from KeWait... to still be able to
* wake up. Simply clearing the mask here if we're in such a
* callback or in an APC means that the thread will never wake up.
*/
ptiCurrent->pcti->fsWakeMask = fsWakeMaskSaved;
if (fRet) {
/*
* Update timeLastRead - it is used for hung app calculations.
* If the thread is waking up to process input, it isn't hung!
*/
SET_TIME_LAST_READ(ptiCurrent);
}
return fRet;
}
/*
* Next check for SendMessages
*/
if (!fExclusive && ptiCurrent->pcti->fsWakeBits & QS_SENDMESSAGE) {
xxxReceiveMessages(ptiCurrent);
/*
* Restore the change bits we took out in PeekMessage()
*/
ptiCurrent->pcti->fsChangeBits |= (ptiCurrent->pcti->fsWakeBits & ptiCurrent->fsChangeBitsRemoved);
ptiCurrent->fsChangeBitsRemoved = 0;
}
/*
* Check to see if some resources need expunging.
* This will unload hook DLLs, including WinEvent ones.
*/
if (ptiCurrent->ppi->cSysExpunge != gcSysExpunge) {
ptiCurrent->ppi->cSysExpunge = gcSysExpunge;
if (ptiCurrent->ppi->dwhmodLibLoadedMask & gdwSysExpungeMask)
xxxDoSysExpunge(ptiCurrent);
}
/*
* OR QS_SENDMESSAGE in since ReceiveMessage() will end up
* trashing pq->fsWakeMask. Do the same for QS_SYSEXPUNGE.
*/
ClearQueueServerEvent((WORD)(fsWakeMask | (fExclusive ? 0 : QS_SENDMESSAGE)));
/*
* If we have timed out then return our error to the caller.
*/
if (status == STATUS_TIMEOUT) {
RIPERR1(ERROR_TIMEOUT, RIP_VERBOSE, "SleepThread: The timeout has expired %lX", Timeout);
UserAssert(fRet == FALSE);
goto GetOutFromHere;
}
/*
* Because we do a non-alertable wait, we know that a status
* of STATUS_USER_APC means that the thread was terminated.
* If we have terminated, get back to user mode.
*/
if (status == STATUS_USER_APC) {
#if DBG
if (ptiCurrent == gptiRit || ptiCurrent == gTermIO.ptiDesktop) {
/*
* If STATUS_USER_APC is caught in the system threads,
* that most likely means the thread was terminated by
* someone inadvertently.
*/
RIPMSG1(RIP_WARNING, "xxxSleepThread: STATUS_USER_APC caught in the system thread pti=%p", ptiCurrent);
}
#endif
ClientDeliverUserApc();
UserAssert(fRet == FALSE);
goto GetOutFromHere;
}
/*
* If this is the power state callout thread, we might need to bail
* out early.
*/
if (gPowerState.pEvent == ptiCurrent->pEventQueueServer) {
if (gPowerState.fCritical) {
UserAssert(fRet == FALSE);
goto GetOutFromHere;
}
}
UserAssert(status == STATUS_SUCCESS);
/*
* Check to see if any input came inbetween when we
* last checked and the NtClearEvent() call.
*
* We call NtWaitForSingleObject() rather than
* WaitForSingleObject() so we can set fAlertable
* to TRUE and thus allow timer APCs to be processed.
*/
if (!(ptiCurrent->pcti->fsChangeBits & ptiCurrent->pcti->fsWakeMask)) {
/*
* This app is going idle. Clear the spin count check to see
* if we need to make this process foreground again.
*/
if (fInputIdle) {
if (ptiCurrent->TIF_flags & TIF_SPINNING) {
if (!NT_SUCCESS(CheckProcessForeground(ptiCurrent))) {
goto GetOutFromHere;
}
}
try {
ptiCurrent->pClientInfo->cSpins = 0;
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
goto GetOutFromHere;
}
}
if (!(ptiCurrent->TIF_flags & TIF_16BIT)) {
if (fInputIdle && ptiCurrent == gptiForeground &&
IsHooked(ptiCurrent, WHF_FOREGROUNDIDLE)) {
xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
}
/*
* Set the input idle event to wake up any threads waiting
* for this thread to go into idle state.
*/
if (fInputIdle) {
zzzWakeInputIdle(ptiCurrent);
}
xxxSleepTask(fInputIdle, NULL);
LeaveCrit();
status = KeWaitForSingleObject(ptiCurrent->pEventQueueServer,
WrUserRequest, UserMode, FALSE, pli);
EnterCrit();
/*
* Reset the input idle event to block and threads waiting
* for this thread to go into idle state.
*/
SleepInputIdle(ptiCurrent);
/*
* ptiCurrent is 16bit!
*/
} else {
if (fInputIdle) {
zzzWakeInputIdle(ptiCurrent);
}
xxxSleepTask(fInputIdle, NULL);
}
}
}
}
/***************************************************************************\
* SetWakeBit
*
* Adds the specified wake bit to specified THREADINFO and wakes its thread
* up if the bit is in its fsWakeMask.
*
* Nothing will happen in the system unless we come to this function, so be
* fast and small.
*
* History:
* 10-28-90 DavidPe Created.
\***************************************************************************/
VOID SetWakeBit(
PTHREADINFO pti,
UINT wWakeBit)
{
CheckCritIn();
UserAssert(pti);
/*
* Win3.1 changes ptiKeyboard and ptiMouse accordingly if we're setting
* those bits.
*/
if (wWakeBit & QS_MOUSE) {
pti->pq->ptiMouse = pti;
}
#ifdef GENERIC_INPUT
if (wWakeBit & (QS_KEY | QS_RAWINPUT)) {
pti->pq->ptiKeyboard = pti;
}
#else
if (wWakeBit & QS_KEY) {
pti->pq->ptiKeyboard = pti;
}
#endif
/*
* OR in these bits - these bits represent what input this app has
* (fsWakeBits), or what input has arrived since that last look
* (fsChangeBits).
*/
pti->pcti->fsWakeBits |= wWakeBit;
pti->pcti->fsChangeBits |= wWakeBit;
/*
* Before waking, do screen saver check to see if it should
* go away.
*/
if ((wWakeBit & QS_INPUT)
&& (pti->ppi->W32PF_Flags & W32PF_IDLESCREENSAVER)) {
if ((wWakeBit & QS_MOUSEMOVE)
&& (gpsi->ptCursor.x == gptSSCursor.x)
&& (gpsi->ptCursor.y == gptSSCursor.y)) {
goto SkipScreenSaverStuff;
}
/*
* Our idle screen saver needs to be given a priority boost so that it
* can process input.
*/
pti->ppi->W32PF_Flags &= ~W32PF_IDLESCREENSAVER;
SetForegroundPriority(pti, TRUE);
}
SkipScreenSaverStuff:
if (wWakeBit & pti->pcti->fsWakeMask) {
/*
* Wake the Thread
*/
if (pti->TIF_flags & TIF_16BIT) {
pti->ptdb->nEvents++;
gpsi->nEvents++;
WakeWowTask(pti);
} else {
KeSetEvent(pti->pEventQueueServer, 2, FALSE);
}
}
}
/***************************************************************************\
* TransferWakeBit
*
* We have a mesasge from the system queue. If out input bit for this
* message isn't set, set ours and clear the guy whose bit was set
* because of this message.
*
* 10-22-92 ScottLu Created.
\***************************************************************************/
VOID TransferWakeBit(
PTHREADINFO pti,
UINT message)
{
PTHREADINFO ptiT;
UINT fsMask;
/*
* Calculate the mask from the message range. Only interested
* in hardware input here: mouse and keys.
*/
#ifdef GENERIC_INPUT
fsMask = CalcWakeMask(message, message, 0) & QS_INPUT;
#else
fsMask = CalcWakeMask(message, message, 0) & (QS_MOUSE | QS_KEY);
#endif
/*
* If it is set in this thread's wakebits, nothing to do.
* Otherwise transfer them from the owner to this thread.
*/
if (!(pti->pcti->fsWakeBits & fsMask)) {
/*
* Either mouse or key is set (not both). Remove this bit
* from the thread that currently owns it, and change mouse /
* key ownership to this thread.
*/
if (fsMask & QS_KEY) {
ptiT = pti->pq->ptiKeyboard;
pti->pq->ptiKeyboard = pti;
} else {
ptiT = pti->pq->ptiMouse;
pti->pq->ptiMouse = pti;
}
ptiT->pcti->fsWakeBits &= ~fsMask;
/*
* Transfer them to this thread (certainly this may be the
* same thread for win32 threads not sharing queues).
*/
pti->pcti->fsWakeBits |= fsMask;
pti->pcti->fsChangeBits |= fsMask;
}
}
/***************************************************************************\
* ClearWakeBit
*
* Clears wake bits. If fSysCheck is TRUE, this clears the input bits only
* if no messages are in the input queue. Otherwise, it clears input bits
* unconditionally.
*
* 11-05-92 ScottLu Created.
\***************************************************************************/
VOID ClearWakeBit(
PTHREADINFO pti,
UINT wWakeBit,
BOOL fSysCheck)
{
/*
* If fSysCheck is TRUE, clear bits only if we are not doing journal
* playback and there are no more messages in the queue. fSysCheck
* is TRUE if clearing because of no more input. FALSE if just
* transfering input ownership from one thread to another.
*/
if (fSysCheck) {
if (pti->pq->mlInput.cMsgs != 0 || FJOURNALPLAYBACK()) {
return;
}
if (pti->pq->QF_flags & QF_MOUSEMOVED) {
wWakeBit &= ~QS_MOUSEMOVE;
}
}
/*
* Only clear the wake bits, not the change bits as well!
*/
pti->pcti->fsWakeBits &= ~wWakeBit;
}
/***************************************************************************\
* PtiFromThreadId
*
* Returns the THREADINFO for the specified thread or NULL if thread
* doesn't exist or doesn't have a THREADINFO.
*
* History:
* 01-30-91 DavidPe Created.
\***************************************************************************/
PTHREADINFO PtiFromThreadId(
DWORD dwThreadId)
{
PETHREAD pEThread;
PTHREADINFO pti;
if (!NT_SUCCESS(LockThreadByClientId(LongToHandle(dwThreadId), &pEThread))) {
return NULL;
}
/*
* If the thread is not terminating, look up the pti. This is
* needed because the value returned by PtiFromThread() is
* undefined if the thread is terminating. See PspExitThread in
* ntos\ps\psdelete.c.
*/
if (!PsIsThreadTerminating(pEThread)) {
pti = PtiFromThread(pEThread);
} else {
pti = NULL;
}
/*
* Do a sanity check on the pti to make sure it's really valid.
*/
if (pti != NULL) {
try {
if (GETPTIID(pti) != LongToHandle(dwThreadId)) {
pti = NULL;
} else if (!(pti->TIF_flags & TIF_GUITHREADINITIALIZED)) {
RIPMSG1(RIP_WARNING, "PtiFromThreadId: pti %#p not initialized", pti);
pti = NULL;
} else if (pti->TIF_flags & TIF_INCLEANUP) {
RIPMSG1(RIP_WARNING, "PtiFromThreadId: pti %#p in cleanup", pti);
pti = NULL;
}
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
pti = NULL;
}
}
UnlockThread(pEThread);
return pti;
}
/***************************************************************************\
* StoreMessage
*
*
*
* History:
* 10-31-90 DarrinM Ported from Win 3.0 sources.
\***************************************************************************/
VOID StoreMessage(
LPMSG pmsg,
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam,
DWORD time)
{
CheckCritIn();
pmsg->hwnd = HW(pwnd);
pmsg->message = message;
pmsg->wParam = wParam;
pmsg->lParam = lParam;
pmsg->time = (time != 0 ? time : NtGetTickCount());
pmsg->pt = gpsi->ptCursor;
}
/***************************************************************************\
* StoreQMessage
*
* If 'time' is 0 grab the current time, if not, it means that this message
* is for an input event and eventually the mouse/keyboard hooks want to see
* the right time stamps.
*
* History:
* 02-27-91 DavidPe Created.
* 06-15-96 CLupu Add 'time' parameter
\***************************************************************************/
VOID StoreQMessage(
PQMSG pqmsg,
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam,
DWORD time,
DWORD dwQEvent,
ULONG_PTR dwExtraInfo)
{
CheckCritIn();
pqmsg->msg.hwnd = HW(pwnd);
pqmsg->msg.message = message;
pqmsg->msg.wParam = wParam;
pqmsg->msg.lParam = lParam;
pqmsg->msg.time = (time == 0) ? NtGetTickCount() : time;
#ifdef REDIRECTION
if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) {
pqmsg->msg.pt.x = LOWORD(lParam);
pqmsg->msg.pt.y = HIWORD(lParam);
} else {
pqmsg->msg.pt = gpsi->ptCursor;
}
#else
pqmsg->msg.pt = gpsi->ptCursor;
#endif
pqmsg->dwQEvent = dwQEvent;
pqmsg->ExtraInfo = dwExtraInfo;
}
/***************************************************************************\
* xxxInitProcessInfo
*
* This initializes the process info. Usually gets created before the
* CreateProcess() call returns (so we can synchronize with the starting
* process in several different ways).
*
* 09-18-91 ScottLu Created.
\***************************************************************************/
NTSTATUS xxxInitProcessInfo(
PW32PROCESS pwp)
{
PPROCESSINFO ppi = (PPROCESSINFO)pwp;
NTSTATUS Status;
CheckCritIn();
/*
* Check if we need to initialize the process.
*/
if (pwp->W32PF_Flags & W32PF_PROCESSCONNECTED) {
return STATUS_ALREADY_WIN32;
}
pwp->W32PF_Flags |= W32PF_PROCESSCONNECTED;
#if defined(_WIN64)
/* Tag as emulated 32bit. Flag is copied to be consistent with
* the way WOW16 apps are tagged for win32k.
*/
if (PsGetProcessWow64Process(pwp->Process)) {
pwp->W32PF_Flags |= W32PF_WOW64;
}
#endif
#ifdef GENERIC_INPUT
UserAssert(ppi->pHidTable == NULL);
#endif
/*
* Mark this app as "starting" - it will be starting until its first
* window activates.
*/
UserVerify(xxxSetProcessInitState(pwp->Process, STARTF_FORCEOFFFEEDBACK));
/*
* link it into the starting processes list
*/
SetAppStarting(ppi);
/*
* link it into the global processes list
*/
ppi->ppiNextRunning = gppiList;
gppiList = ppi;
/*
* If foreground activation has not been canceled and the parent process
* (or an ancestor) can force a foreground change, then allow this process
* to come to the foreground when it does its first activation.
*
* Bug 273518 - joejo
*
* This will allow console windows to set foreground correctly on new
* process' it launches, as opposed it just forcing foreground.
*/
if (TEST_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE) && CheckAllowForeground(pwp->Process)) {
ppi->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
}
TAGMSG2(DBGTAG_FOREGROUND, "xxxInitProcessInfo %s W32PF %#p",
((ppi->W32PF_Flags & W32PF_ALLOWFOREGROUNDACTIVATE) ? "set" : "NOT"),
ppi);
/*
* Get the logon session id. This is used to determine which
* windowstation to connect to and to identify attempts to
* call hooks across security contexts.
*/
Status = GetProcessLuid(NULL, &ppi->luidSession);
UserAssert(NT_SUCCESS(Status));
/*
* Ensure that we're in sync with the expunge count
*/
ppi->cSysExpunge = gcSysExpunge;
/*
* Don't perform any LPK callbacks until GDI notifies
* us that the LPK(s) are loaded and initialized.
*/
ppi->dwLpkEntryPoints = 0;
return STATUS_SUCCESS;
}
/***************************************************************************\
* DestroyProcessInfo
*
* This function is executed when the last thread of a process goes
* away.
*
* SO VERY IMPORTANT:
* Note that the last thread of the process might not be a w32 thread. So do
* not make any calls here that assume a w32 pti. Do avoid any function calling
* PtiCurrent() as it probably assumes it is on a nice w32 thread.
*
* Also note that if the process is locked, the ppi is not going away; this
* simply means that execution on this process has ended. So make sure to clean
* up in a way that the ppi data is still valid (for example, if you free a
* pointer, set it to NULL).
*
* zzz Note: Not a zzz routine although it calls zzzCalcStartCursorHide() -
* Since we can't make callbacks on a non-GUI thread, we use
* DeferWinEventNotify() & EndDeferWinEventNotifyWithoutProcessing()
* to prevent callbacks.
*
* 04/08/96 GerardoB Added header
\***************************************************************************/
BOOL DestroyProcessInfo(
PW32PROCESS pwp)
{
PPROCESSINFO ppi = (PPROCESSINFO)pwp;
PDESKTOPVIEW pdv, pdvNext;
BOOL fHadThreads;
PPUBOBJ ppo;
CheckCritIn();
/*
* Free up input idle event if it exists - wake everyone waiting on it
* first. This object will get created sometimes even for non-windows
* processes (usually for WinExec(), which calls WaitForInputIdle()).
*/
CLOSE_PSEUDO_EVENT(&pwp->InputIdleEvent);
/*
* Check to see if the startglass is on, and if so turn it off and update.
* DeferWinEventNotify to because we cannot process notifications for this
* thread now (we may have no PtiCurrent, see comment above)
*/
BEGINATOMICCHECK();
DeferWinEventNotify();
if (pwp->W32PF_Flags & W32PF_STARTGLASS) {
pwp->W32PF_Flags &= ~W32PF_STARTGLASS;
zzzCalcStartCursorHide(NULL, 0);
}
/*
* This is bookkeeping - restore original notification deferral but without
* attempting to process any deferred notifications because we have no pti.
*/
EndDeferWinEventNotifyWithoutProcessing();
ENDATOMICCHECK();
/*
* If the process never called Win32k, we're done.
*/
if (!(pwp->W32PF_Flags & W32PF_PROCESSCONNECTED)) {
return FALSE;
}
#ifdef GENERIC_INPUT
/*
* Cleanup the HID devices this process has requested.
*/
if (ppi->pHidTable) {
DestroyProcessHidRequests(ppi);
}
#endif
/*
* Play the Process Close sound for non-console processes
* running on the I/O windowstation.
*/
if ((ppi->W32PF_Flags & W32PF_IOWINSTA) &&
!(ppi->W32PF_Flags & W32PF_CONSOLEAPPLICATION) &&
(gspwndLogonNotify != NULL) &&
!(ppi->rpwinsta->dwWSF_Flags & WSF_OPENLOCK)) {
PTHREADINFO pti = GETPTI(gspwndLogonNotify);
PQMSG pqmsg;
if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
StoreQMessage(pqmsg, gspwndLogonNotify, WM_LOGONNOTIFY,
LOGON_PLAYEVENTSOUND, USER_SOUND_CLOSE, 0, 0, 0);
SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
}
}
/*
* Be like WIN95.
* If this is the shell process, then send a LOGON_RESTARTSHELL
* notification to the winlogon process (only if not logging off)
*/
if (IsShellProcess(ppi)) {
/*
* The shell process will get killed and it's better to set this
* in the desktop info.
*/
ppi->rpdeskStartup->pDeskInfo->ppiShellProcess = NULL;
/*
* If we're not logging off, notify winlogon
*/
if ((gspwndLogonNotify != NULL) &&
!(ppi->rpwinsta->dwWSF_Flags & WSF_OPENLOCK)) {
PTHREADINFO pti = GETPTI(gspwndLogonNotify);
PQMSG pqmsg;
if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
StoreQMessage(pqmsg, gspwndLogonNotify, WM_LOGONNOTIFY,
LOGON_RESTARTSHELL, PsGetProcessExitStatus(ppi->Process), 0, 0, 0);
SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
}
}
}
if (ppi->cThreads) {
RIPMSG1(RIP_ERROR,
"Disconnect with 0x%x threads remaining",
ppi->cThreads);
}
/*
* If the app is still starting, remove it from the startup list
*/
if (ppi->W32PF_Flags & W32PF_APPSTARTING) {
/*
* Bug 294193 - joejo
*
* Handle case when creator process exits before the child
* process makes it to CheckAllowForeground code. This is typical with
* stub EXEs that do nothing but create other processes.
*/
GiveForegroundActivateRight(PsGetProcessId(ppi->Process));
ClearAppStarting(ppi);
}
/*
* remove it from the global list
*/
REMOVE_FROM_LIST(PROCESSINFO, gppiList, ppi, ppiNextRunning);
/*
* If any threads ever connected, there may be DCs, classes,
* cursors, etc. still lying around. If no threads connected
* (which is the case for console apps), skip all of this cleanup.
*/
fHadThreads = ppi->W32PF_Flags & W32PF_THREADCONNECTED;
if (fHadThreads) {
/*
* When a process dies we need to make sure any DCE's it owns
* and have not been deleted are cleanup up. The clean up
* earlier may have failed if the DC was busy in GDI.
*/
if (ppi->W32PF_Flags & W32PF_OWNDCCLEANUP) {
DelayedDestroyCacheDC();
}
#if DBG
{
PHE pheT, pheMax;
/*
* Loop through the table destroying all objects created by the current
* process. All objects will get destroyed in their proper order simply
* because of the object locking.
*/
pheMax = &gSharedInfo.aheList[giheLast];
for (pheT = gSharedInfo.aheList; pheT <= pheMax; pheT++) {
/*
* We should have no process objects left for this process.
*/
UserAssertMsg0(
pheT->bFlags & HANDLEF_DESTROY ||
!(gahti[pheT->bType].bObjectCreateFlags & OCF_PROCESSOWNED) ||
(PPROCESSINFO)pheT->pOwner != ppi,
"We should have no process objects left for this process!");
}
}
#endif
}
if (pwp->UserHandleCount) {
RIPMSG1(RIP_ERROR,
"Disconnect with 0x%x User handle objects remaining",
pwp->UserHandleCount);
}
/*
* check if we need to zap PID's for DDE objects
*/
for (ppo = gpPublicObjectList;
ppo != NULL;
ppo = ppo->next) {
if (ppo->pid == pwp->W32Pid) {
ppo->pid = OBJECT_OWNER_PUBLIC;
}
}
if (gppiScreenSaver == ppi) {
UserAssert(ppi->W32PF_Flags & W32PF_SCREENSAVER);
gppiScreenSaver = NULL;
}
if (gppiForegroundOld == ppi) {
gppiForegroundOld = NULL;
}
if (gppiUserApiHook == ppi) {
_UnregisterUserApiHook();
}
UnlockWinSta(&ppi->rpwinsta);
UnlockDesktop(&ppi->rpdeskStartup, LDU_PPI_DESKSTARTUP3, (ULONG_PTR)ppi);
/*
* Close the startup desktop handle now if it's still around. If we wait
* until handle table cleanup time we could potentially deadlock.
*/
if (ppi->hdeskStartup) {
UserVerify(NT_SUCCESS(CloseProtectedHandle(ppi->hdeskStartup)));
ppi->hdeskStartup = NULL;
}
/*
* Mark the process as terminated so access checks will work.
*/
ppi->W32PF_Flags |= W32PF_TERMINATED;
/*
* Cleanup wow process info struct, if any
*/
if (ppi->pwpi) {
PWOWPROCESSINFO pwpi = ppi->pwpi;
ObDereferenceObject(pwpi->pEventWowExec);
REMOVE_FROM_LIST(WOWPROCESSINFO, gpwpiFirstWow, pwpi, pwpiNext);
UserFreePool(pwpi);
ppi->pwpi = NULL;
}
/*
* Delete desktop views. System will do unmapping.
*/
pdv = ppi->pdvList;
while (pdv) {
pdvNext = pdv->pdvNext;
UserFreePool(pdv);
pdv = pdvNext;
}
ppi->pdvList = NULL;
/*
* Clear the SendInput/Journalling hook caller ppi
*/
if (ppi == gppiInputProvider) {
gppiInputProvider = NULL;
}
/*
* If this ppi locked SetForegroundWindow, clean up
*/
if (ppi == gppiLockSFW) {
gppiLockSFW = NULL;
}
return fHadThreads;
}
/***************************************************************************\
* ClearWakeMask
*
\***************************************************************************/
VOID ClearWakeMask(
VOID)
{
PtiCurrent()->pcti->fsWakeMask = 0;
}
/***************************************************************************\
* xxxGetInputEvent
*
* Returns a duplicated event-handle that the client process can use to
* wait on input events.
*
* History:
* 05-02-91 DavidPe Created.
\***************************************************************************/
HANDLE xxxGetInputEvent(
DWORD dwWakeMask)
{
PTHREADINFO ptiCurrent;
WORD wFlags = HIWORD(dwWakeMask);
UserAssert(IsWinEventNotifyDeferredOK());
ptiCurrent = PtiCurrent();
/*
* If our wait condition is satisfied, signal the event and return.
* (Since the wake mask could have been anything at the time the input
* arrived, the event might not be signaled)
*/
if (GetInputBits(ptiCurrent->pcti, LOWORD(dwWakeMask), (wFlags & MWMO_INPUTAVAILABLE))) {
KeSetEvent(ptiCurrent->pEventQueueServer, 2, FALSE);
return ptiCurrent->hEventQueueClient;
}
/*
* If an idle hook is set, call it.
*/
if (ptiCurrent == gptiForeground &&
IsHooked(ptiCurrent, WHF_FOREGROUNDIDLE)) {
xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
}
/*
* What is the criteria for an "idle process"?
* Answer: The first thread that calls zzzWakeInputIdle, or SleepInputIdle or...
* Any thread that calls xxxGetInputEvent with any of the following
* bits set in its wakemask: (sanfords)
*/
if (dwWakeMask & (QS_POSTMESSAGE | QS_INPUT)) {
ptiCurrent->ppi->ptiMainThread = ptiCurrent;
}
/*
* When we return, this app is going to sleep. Since it is in its
* idle mode when it goes to sleep, wake any apps waiting for this
* app to go idle.
*/
zzzWakeInputIdle(ptiCurrent);
/*
* Setup the wake mask for this thread. Wait for QS_EVENT or the app won't
* get event messages like deactivate.
*/
ClearQueueServerEvent((WORD)(dwWakeMask | QS_EVENT));
/*
* This app is going idle. Clear the spin count check to see
* if we need to make this process foreground again.
*/
try {
ptiCurrent->pClientInfo->cSpins = 0;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
return NULL;
}
if (ptiCurrent->TIF_flags & TIF_SPINNING) {
if (!NT_SUCCESS(CheckProcessForeground(ptiCurrent))) {
return NULL;
}
}
UserAssert(ptiCurrent->pcti->fsWakeMask != 0);
return ptiCurrent->hEventQueueClient;
}
/***************************************************************************\
* xxxWaitForInputIdle
*
* This routine waits on a particular input queue for "input idle", meaning
* it waits till that queue has no input to process.
*
* 09-13-91 ScottLu Created.
\***************************************************************************/
DWORD xxxWaitForInputIdle(
ULONG_PTR idProcess,
DWORD dwMilliseconds,
BOOL fSharedWow)
{
PTHREADINFO ptiCurrent;
PTHREADINFO pti;
PEPROCESS Process;
PW32PROCESS W32Process;
PPROCESSINFO ppi;
DWORD dwResult;
NTSTATUS Status;
TL tlProcess;
ptiCurrent = PtiCurrent();
/*
* If fSharedWow is set, the client passed in a fake process
* handle which CreateProcess returns for Win16 apps started
* in the shared WOW VDM.
*
* CreateProcess returns a real process handle when you start
* a Win16 app in a separate WOW VDM.
*/
if (fSharedWow) { // Waiting for a WOW task to go idle.
PWOWTHREADINFO pwti;
/*
* Look for a matching thread in the WOW thread info list.
*/
for (pwti = gpwtiFirst; pwti != NULL; pwti = pwti->pwtiNext) {
if (pwti->idParentProcess == HandleToUlong(PsGetThreadProcessId(ptiCurrent->pEThread)) &&
pwti->idWaitObject == idProcess) {
break;
}
}
/*
* If we couldn't find the right thread, bail out.
*/
if (pwti == NULL) {
RIPMSG0(RIP_WARNING, "WaitForInputIdle couldn't find 16-bit task");
return (DWORD)-1;
}
/*
* Now wait for it to go idle and return.
*/
dwResult = WaitOnPseudoEvent(&pwti->pIdleEvent, dwMilliseconds);
if (dwResult == STATUS_ABANDONED) {
dwResult = xxxPollAndWaitForSingleObject(pwti->pIdleEvent,
NULL,
dwMilliseconds);
}
return dwResult;
}
/*
* We shouldn't get here for system threads.
*/
UserAssert(!(ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD));
/*
* If the app is waiting for itself to go idle, error.
*/
if (PsGetThreadProcessId(ptiCurrent->pEThread) == (HANDLE)idProcess &&
ptiCurrent == ptiCurrent->ppi->ptiMainThread) {
RIPMSG0(RIP_WARNING, "WaitForInputIdle waiting on self");
return (DWORD)-1;
}
/*
* Now find the ppi structure for this process.
*/
Status = LockProcessByClientId((HANDLE)idProcess, &Process);
if (!NT_SUCCESS(Status))
return (DWORD)-1;
if (PsGetProcessExitProcessCalled(Process)) {
UnlockProcess(Process);
return (DWORD)-1;
}
W32Process = (PW32PROCESS)PsGetProcessWin32Process(Process);
/*
* If we can't find that process info structure, return error.
* Or, if this is a console application, don't wait on it.
*/
if (W32Process == NULL || W32Process->W32PF_Flags & W32PF_CONSOLEAPPLICATION) {
UnlockProcess(Process);
return (DWORD)-1;
}
/*
* We have to wait mark the Process as one which others are waiting on
*/
ppi = (PPROCESSINFO)W32Process;
ppi->W32PF_Flags |= W32PF_WAITFORINPUTIDLE;
for (pti = ppi->ptiList; pti != NULL; pti = pti->ptiSibling) {
pti->TIF_flags |= TIF_WAITFORINPUTIDLE;
}
/*
* Thread lock the process to ensure that it will be dereferenced
* if the thread exits.
*/
LockW32Process(W32Process, &tlProcess);
UnlockProcess(Process);
dwResult = WaitOnPseudoEvent(&W32Process->InputIdleEvent, dwMilliseconds);
if (dwResult == STATUS_ABANDONED) {
dwResult = xxxPollAndWaitForSingleObject(W32Process->InputIdleEvent,
Process,
dwMilliseconds);
}
/*
* Clear all thread TIF_WAIT bits from the process.
*/
ppi->W32PF_Flags &= ~W32PF_WAITFORINPUTIDLE;
for (pti = ppi->ptiList; pti != NULL; pti = pti->ptiSibling) {
pti->TIF_flags &= ~TIF_WAITFORINPUTIDLE;
}
UnlockW32Process(&tlProcess);
return dwResult;
}
#define INTERMEDIATE_TIMEOUT (500) // 1/2 second
/***************************************************************************\
* xxxPollAndWaitForSingleObject
*
* Sometimes we have to wait on an event but still want to periodically
* wake up and see if the client process has been terminated.
*
* dwMilliseconds is initially the total amount of time to wait and after
* each intermediate wait reflects the amount of time left to wait.
* -1 means wait indefinitely.
*
* 02-Jul-1993 johnc Created.
\***************************************************************************/
// LATER!!! can we get rid of the Polling idea and wait additionally on
// LATER!!! the hEventServer and set that when a thread dies
DWORD xxxPollAndWaitForSingleObject(
PKEVENT pEvent,
PVOID pExecObject,
DWORD dwMilliseconds)
{
DWORD dwIntermediateMilliseconds, dwStartTickCount;
PTHREADINFO ptiCurrent;
UINT cEvent = 2;
NTSTATUS Status = -1;
LARGE_INTEGER li;
TL tlEvent;
ptiCurrent = PtiCurrent();
if (ptiCurrent->apEvent == NULL) {
ptiCurrent->apEvent = UserAllocPoolNonPaged(POLL_EVENT_CNT * sizeof(PKEVENT), TAG_EVENT);
if (ptiCurrent->apEvent == NULL)
return (DWORD)-1;
}
/*
* Refcount the event to ensure that it won't go
* away during the wait. By using a thread lock, the
* event will be dereferenced if the thread exits
* during a callback. The process pointer has already been
* locked.
*/
ThreadLockObject(pEvent, &tlEvent);
/*
* If a process was passed in, wait on it too. No need
* to reference this because the caller has it referenced.
*/
if (pExecObject) {
cEvent++;
}
/*
* We want to wake if there're sent messages pending
*/
ClearQueueServerEvent(QS_SENDMESSAGE);
/*
* Wow Tasks MUST be descheduled while in the wait to allow
* other tasks in the same wow scheduler to run.
*
* For example, 16 bit app A calls WaitForInputIdle on 32 bit app B.
* App B starts up and tries to send a message to 16 bit app C. App C
* will never be able to process the message unless app A yields
* control to it, so app B will never go idle.
*/
if (ptiCurrent->TIF_flags & TIF_16BIT) {
xxxSleepTask(FALSE, HEVENT_REMOVEME);
// caution: the wow task is no longer scheduled.
}
dwStartTickCount = NtGetTickCount();
while (TRUE) {
if (dwMilliseconds > INTERMEDIATE_TIMEOUT) {
dwIntermediateMilliseconds = INTERMEDIATE_TIMEOUT;
/*
* If we are not waiting an infinite amount of time then subtract
* the last loop duration from time left to wait.
*/
if (dwMilliseconds != INFINITE) {
DWORD dwNewTickCount = NtGetTickCount();
DWORD dwDelta = ComputePastTickDelta(dwNewTickCount, dwStartTickCount);
dwStartTickCount = dwNewTickCount;
if (dwDelta < dwMilliseconds) {
dwMilliseconds -= dwDelta;
} else {
dwMilliseconds = 0;
}
}
} else {
dwIntermediateMilliseconds = dwMilliseconds;
dwMilliseconds = 0;
}
/*
* Convert dwMilliseconds to a relative-time(i.e. negative) LARGE_INTEGER.
* NT Base calls take time values in 100 nanosecond units.
*/
if (dwIntermediateMilliseconds != INFINITE)
li.QuadPart = Int32x32To64(-10000, dwIntermediateMilliseconds);
/*
* Load events into the wait array. Do this every time
* through the loop in case of recursion.
*/
ptiCurrent->apEvent[IEV_IDLE] = pEvent;
ptiCurrent->apEvent[IEV_INPUT] = ptiCurrent->pEventQueueServer;
ptiCurrent->apEvent[IEV_EXEC] = pExecObject;
LeaveCrit();
Status = KeWaitForMultipleObjects(cEvent,
&ptiCurrent->apEvent[IEV_IDLE],
WaitAny,
WrUserRequest,
UserMode,
FALSE,
(dwIntermediateMilliseconds == INFINITE ?
NULL : &li),
NULL);
EnterCrit();
if (!NT_SUCCESS(Status)) {
Status = -1;
} else {
/*
* Because we do a non-alertable wait, we know that a status
* of STATUS_USER_APC means that the thread was terminated.
* If we have terminated, get back to user mode
*/
if (Status == STATUS_USER_APC) {
ClientDeliverUserApc();
Status = -1;
}
}
if (ptiCurrent->pcti->fsChangeBits & QS_SENDMESSAGE) {
/*
* Wow Tasks MUST wait to be rescheduled in the wow non-premptive
* scheduler before doing anything which might invoke client code.
*/
if (ptiCurrent->TIF_flags & TIF_16BIT) {
xxxDirectedYield(DY_OLDYIELD);
}
xxxReceiveMessages(ptiCurrent);
if (ptiCurrent->TIF_flags & TIF_16BIT) {
xxxSleepTask(FALSE, HEVENT_REMOVEME);
// caution: the wow task is no longer scheduled.
}
}
/*
* If we returned from the wait for some other reason than a timeout
* or to receive messages we are done. If it is a timeout we are
* only done waiting if the overall time is zero.
*/
if (Status != STATUS_TIMEOUT && Status != 1)
break;
if (dwMilliseconds == 0) {
/*
* Fix up the return if the last poll was interupted by a message
*/
if (Status == 1)
Status = WAIT_TIMEOUT;
break;
}
}
/*
* reschedule the 16 bit app
*/
if (ptiCurrent->TIF_flags & TIF_16BIT) {
xxxDirectedYield(DY_OLDYIELD);
}
/*
* Unlock the events.
*/
ThreadUnlockObject(&tlEvent);
return Status;
}
/***************************************************************************\
* WaitOnPseudoEvent
*
* Similar semantics to WaitForSingleObject() but works with pseudo events.
* Could fail if creation on the fly fails.
* Returns STATUS_ABANDONED_WAIT if caller needs to wait on the event and event is
* created and ready to be waited on.
*
* This assumes the event was created with fManualReset=TRUE, fInitState=FALSE
*
* 10/28/93 SanfordS Created
\***************************************************************************/
DWORD WaitOnPseudoEvent(
HANDLE *phE,
DWORD dwMilliseconds)
{
HANDLE hEvent;
NTSTATUS Status;
CheckCritIn();
if (*phE == PSEUDO_EVENT_OFF) {
if (!NT_SUCCESS(ZwCreateEvent(&hEvent, EVENT_ALL_ACCESS, NULL,
NotificationEvent, FALSE))) {
UserAssert(!"Could not create event on the fly.");
if (dwMilliseconds != INFINITE) {
return STATUS_TIMEOUT;
} else {
return (DWORD)-1;
}
}
Status = ObReferenceObjectByHandle(hEvent, EVENT_ALL_ACCESS, *ExEventObjectType,
KernelMode, phE, NULL);
ZwClose(hEvent);
if (!NT_SUCCESS(Status))
return (DWORD)-1;
} else if (*phE == PSEUDO_EVENT_ON) {
return STATUS_WAIT_0;
}
return(STATUS_ABANDONED);
}
/***************************************************************************\
* xxxSetCsrssThreadDesktop
*
* Set/clear and lock/unlock a desktop for a csrss thread
* When setting a desktop, ppdeskRestore must be valid and will receive
* the old (previous) desktop, if any; the caller is expected to restore
* this pdesk when done.
*
* When restoring a desktop, ppdeskRestore must be NULL. pdesk must have been
* previously returned by this same function (in *ppdeskRestore).
*
* History:
* 02-18-97 GerardoB Extracted from SetInformationThread
\***************************************************************************/
NTSTATUS xxxSetCsrssThreadDesktop(PDESKTOP pdesk, PDESKRESTOREDATA pdrdRestore)
{
PTHREADINFO ptiCurrent = PtiCurrent();
NTSTATUS Status = STATUS_SUCCESS;
MSG msg;
/*
* Only csr should come here
*/
UserAssert(ISCSRSS());
UserAssert(pdrdRestore);
UserAssert(pdrdRestore->pdeskRestore == NULL);
#if 0
/*
* If we're in clean up, csrss worker threads should not be messing around
*/
if (gdwHydraHint & HH_INITIATEWIN32KCLEANUP) {
FRE_RIPMSG0(RIP_ERROR, "xxxSetCsrssThreadDesktop: HH_INITIATEWIN32KCLEANUP is set");
}
#endif
if (pdesk->dwDTFlags & DF_DESTROYED) {
RIPMSG1(RIP_WARNING, "xxxSetCsrssThreadDesktop: pdesk %#p destroyed",
pdesk);
return STATUS_UNSUCCESSFUL;
}
/*
* Lock the current desktop (set operation). Also, create and save a
* handle to the new desktop.
*/
pdrdRestore->pdeskRestore = ptiCurrent->rpdesk;
if (pdrdRestore->pdeskRestore != NULL) {
Status = ObReferenceObjectByPointer(pdrdRestore->pdeskRestore,
MAXIMUM_ALLOWED,
*ExDesktopObjectType,
KernelMode);
if (!NT_SUCCESS(Status)) {
pdrdRestore->pdeskRestore = NULL;
pdrdRestore->hdeskNew = NULL;
return Status;
}
LogDesktop(pdrdRestore->pdeskRestore, LD_REF_FN_SETCSRSSTHREADDESKTOP, TRUE, (ULONG_PTR)PtiCurrent());
}
Status = ObOpenObjectByPointer(
pdesk,
0,
NULL,
EVENT_ALL_ACCESS,
NULL,
KernelMode,
&(pdrdRestore->hdeskNew));
if (!NT_SUCCESS(Status)) {
RIPNTERR2(Status, RIP_WARNING, "SetCsrssThreadDesktop, can't open handle, pdesk %#p. Status: %#x", pdesk, Status);
if (pdrdRestore->pdeskRestore) {
LogDesktop(pdrdRestore->pdeskRestore, LD_DEREF_FN_SETCSRSSTHREADDESKTOP1, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdrdRestore->pdeskRestore);
pdrdRestore->pdeskRestore = NULL;
}
pdrdRestore->hdeskNew = NULL;
return Status;
}
/*
* Set the new desktop, if switching
*/
if (pdesk != ptiCurrent->rpdesk) {
/*
* Process any remaining messages before we leave the desktop
*/
if (ptiCurrent->rpdesk) {
while (xxxPeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
xxxDispatchMessage(&msg);
}
if (!xxxSetThreadDesktop(NULL, pdesk)) {
RIPMSG1(RIP_WARNING, "xxxSetCsrssThreadDesktop: xxxSetThreadDesktop(%#p) failed", pdesk);
Status = STATUS_INVALID_HANDLE;
/*
* We're failing so deref if needed.
*/
if (pdrdRestore->pdeskRestore != NULL) {
LogDesktop(pdrdRestore->pdeskRestore, LD_DEREF_FN_SETCSRSSTHREADDESKTOP1, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdrdRestore->pdeskRestore);
pdrdRestore->pdeskRestore = NULL;
}
CloseProtectedHandle(pdrdRestore->hdeskNew);
pdrdRestore->hdeskNew = NULL;
}
}
UserAssert(NT_SUCCESS(Status));
return Status;
}
NTSTATUS xxxRestoreCsrssThreadDesktop(PDESKRESTOREDATA pdrdRestore)
{
PTHREADINFO ptiCurrent = PtiCurrent();
NTSTATUS Status = STATUS_SUCCESS;
MSG msg;
/*
* Only csr should come here
*/
UserAssert(ISCSRSS());
UserAssert(pdrdRestore);
/*
* Set the new desktop, if switching
*/
if (pdrdRestore->pdeskRestore != ptiCurrent->rpdesk) {
/*
* Process any remaining messages before we leave the desktop
*/
if (ptiCurrent->rpdesk) {
while (xxxPeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
xxxDispatchMessage(&msg);
}
if (!xxxSetThreadDesktop(NULL, pdrdRestore->pdeskRestore)) {
FRE_RIPMSG1(RIP_ERROR, "xxxRestoreCsrssThreadDesktop: xxxRestoreThreadDesktop(%#p) failed", pdrdRestore->pdeskRestore);
Status = STATUS_INVALID_HANDLE;
}
}
/*
* Dereference the desktop, even if failing the call.
*/
if (pdrdRestore->pdeskRestore != NULL) {
LogDesktop(pdrdRestore->pdeskRestore, LD_DEREF_FN_SETCSRSSTHREADDESKTOP2, FALSE, 0);
ObDereferenceObject(pdrdRestore->pdeskRestore);
pdrdRestore->pdeskRestore = NULL;
}
if (pdrdRestore->hdeskNew) {
CloseProtectedHandle(pdrdRestore->hdeskNew);
UserAssert(NT_SUCCESS(Status));
pdrdRestore->hdeskNew = NULL;
}
return Status;
}
/***************************************************************************\
* GetTaskName
*
* Gets the application name from a thread.
\***************************************************************************/
ULONG GetTaskName(
PTHREADINFO pti,
PWSTR Buffer,
ULONG BufferLength)
{
ANSI_STRING strAppName;
UNICODE_STRING strAppNameU;
NTSTATUS Status;
ULONG NameLength = 0;
if (pti == NULL) {
*Buffer = 0;
return 0;
}
if (pti->pstrAppName != NULL) {
NameLength = min(pti->pstrAppName->Length + sizeof(WCHAR), BufferLength);
RtlCopyMemory(Buffer, pti->pstrAppName->Buffer, NameLength);
} else {
RtlInitAnsiString(&strAppName, PsGetProcessImageFileName(PsGetThreadProcess(pti->pEThread)));
if (BufferLength < sizeof(WCHAR)) {
NameLength = (strAppName.Length + 1) * sizeof(WCHAR);
} else {
strAppNameU.Buffer = Buffer;
strAppNameU.MaximumLength = (SHORT)BufferLength - sizeof(WCHAR);
Status = RtlAnsiStringToUnicodeString(&strAppNameU, &strAppName,
FALSE);
if (NT_SUCCESS(Status))
NameLength = strAppNameU.Length + sizeof(WCHAR);
}
}
Buffer[(NameLength / sizeof(WCHAR)) - 1] = 0;
return NameLength;
}
/***************************************************************************\
* QueryInformationThread
*
* Returns information about a thread.
*
* History:
* 03-01-95 JimA Created.
\***************************************************************************/
NTSTATUS xxxQueryInformationThread(
IN HANDLE hThread,
IN USERTHREADINFOCLASS ThreadInfoClass,
OUT PVOID ThreadInformation,
IN ULONG ThreadInformationLength,
OUT PULONG ReturnLength OPTIONAL)
{
PUSERTHREAD_SHUTDOWN_INFORMATION pShutdown;
PUSERTHREAD_WOW_INFORMATION pWow;
PETHREAD Thread;
PTHREADINFO pti;
NTSTATUS Status = STATUS_SUCCESS;
ULONG LocalReturnLength = 0;
DWORD dwClientFlags;
/*
* Only allow CSRSS to make this call
*/
UserAssert(ISCSRSS());
Status = ObReferenceObjectByHandle(hThread,
THREAD_QUERY_INFORMATION,
*PsThreadType,
UserMode,
&Thread,
NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
pti = PtiFromThread(Thread);
switch (ThreadInfoClass) {
case UserThreadShutdownInformation:
LocalReturnLength = sizeof(USERTHREAD_SHUTDOWN_INFORMATION);
UserAssert(ThreadInformationLength == sizeof(USERTHREAD_SHUTDOWN_INFORMATION));
pShutdown = ThreadInformation;
/*
* Read the client flags and zero out the structure,
* except for pdeskRestore (which is supposed
* to be the last field)
*/
dwClientFlags = pShutdown->dwFlags;
UserAssert(FIELD_OFFSET(USERTHREAD_SHUTDOWN_INFORMATION, drdRestore)
== (sizeof(USERTHREAD_SHUTDOWN_INFORMATION) - sizeof(DESKRESTOREDATA)));
RtlZeroMemory(pShutdown,
sizeof(USERTHREAD_SHUTDOWN_INFORMATION) - sizeof(DESKRESTOREDATA));
/*
* Return the desktop window handle if the thread
* has a desktop and the desktop is on a visible
* windowstation.
*/
if (pti != NULL && pti->rpdesk != NULL &&
!(pti->rpdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO))
pShutdown->hwndDesktop = HW(pti->rpdesk->pDeskInfo->spwnd);
/*
* Return shutdown status. Zero indicates that the thread
* has windows and can be shut down in the normal manner.
*/
if (PsGetThreadProcessId(Thread) == gpidLogon) {
/*
* Do not shutdown the logon process.
*/
pShutdown->StatusShutdown = SHUTDOWN_KNOWN_PROCESS;
} else if (pti == NULL || pti->rpdesk == NULL) {
/*
* The thread either is not a gui thread or it doesn't
* have a desktop. Make console do the shutdown.
*/
pShutdown->StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS;
}
/*
* Return flags
*/
if (pti != NULL && pti->cWindows != 0)
pShutdown->dwFlags |= USER_THREAD_GUI;
/*
* If we return the desktop window handle and the
* app should be shut down, switch to the desktop
* and assign it to the shutdown worker thread.
*/
if ((pShutdown->dwFlags & USER_THREAD_GUI) &&
pShutdown->StatusShutdown == 0) {
/*
* The current csrss thread is going to
* make activation calls, send messages,
* switch video modes, etc so we need to
* assign it to a dekstop
*/
PTHREADINFO ptiCurrent = PtiCurrent();
UserAssert(pti->rpdesk != NULL);
if (ptiCurrent->rpdesk != pti->rpdesk) {
/*
* If this thread already has a desktop,
* restore the old one first.
* This might happen when threads of the same
* process are attached to different desktops.
*/
if (ptiCurrent->rpdesk != NULL) {
Status = xxxRestoreCsrssThreadDesktop(&pShutdown->drdRestore);
UserAssert(pti == PtiFromThread(Thread));
}
if (NT_SUCCESS(Status)) {
Status = xxxSetCsrssThreadDesktop(pti->rpdesk, &pShutdown->drdRestore);
UserAssert(pti == PtiFromThread(Thread));
}
}
/*
* If we're forcing a shutdown, then there is no need to switch
* since we won't send any messages or bring up the EndTask dialog
* (We still want to have a proper rpdesk since BoostHardError might
* call PostThreadMessage)
*/
if (!(dwClientFlags & WMCS_NORETRY)) {
if (NT_SUCCESS(Status)) {
xxxSwitchDesktop(pti->rpdesk->rpwinstaParent, pti->rpdesk, SDF_SLOVERRIDE);
UserAssert(pti == PtiFromThread(Thread));
}
}
}
break;
case UserThreadFlags:
LocalReturnLength = sizeof(DWORD);
if (pti == NULL) {
Status = STATUS_INVALID_HANDLE;
} else {
UserAssert(ThreadInformationLength == sizeof(DWORD));
*(LPDWORD)ThreadInformation = pti->TIF_flags;
}
break;
case UserThreadTaskName:
LocalReturnLength = GetTaskName(pti, ThreadInformation, ThreadInformationLength);
break;
case UserThreadWOWInformation:
LocalReturnLength = sizeof(USERTHREAD_WOW_INFORMATION);
UserAssert(ThreadInformationLength == sizeof(USERTHREAD_WOW_INFORMATION));
pWow = ThreadInformation;
RtlZeroMemory(pWow, sizeof(USERTHREAD_WOW_INFORMATION));
/*
* If the thread is 16-bit, Status = the exit task function
* and task id.
*/
if (pti && pti->TIF_flags & TIF_16BIT) {
pWow->lpfnWowExitTask = pti->ppi->pwpi->lpfnWowExitTask;
if (pti->ptdb) {
pWow->hTaskWow = pti->ptdb->hTaskWow;
} else {
pWow->hTaskWow = 0;
}
}
break;
case UserThreadHungStatus:
LocalReturnLength = sizeof(DWORD);
UserAssert(ThreadInformationLength >= sizeof(DWORD));
/*
* Return hung status.
*/
if (pti) {
*(PDWORD)ThreadInformation =
(DWORD)FHungApp(pti, (DWORD)*(PDWORD)ThreadInformation);
} else {
*(PDWORD)ThreadInformation = FALSE;
}
break;
default:
Status = STATUS_INVALID_INFO_CLASS;
RIPMSG1(RIP_ERROR, "Invalid ThreadInfoClass 0x%x", ThreadInfoClass);
break;
}
if (ARGUMENT_PRESENT(ReturnLength)) {
*ReturnLength = LocalReturnLength;
}
UnlockThread(Thread);
return Status;
}
/***************************************************************************\
* xxxSetInformationThread
*
* Sets information about a thread.
*
* History:
* 03-01-95 JimA Created.
\***************************************************************************/
NTSTATUS xxxSetInformationThread(
IN HANDLE hThread,
IN USERTHREADINFOCLASS ThreadInfoClass,
IN PVOID ThreadInformation,
IN ULONG ThreadInformationLength)
{
PUSERTHREAD_FLAGS pFlags;
HANDLE hClientThread;
DWORD dwOldFlags;
PTHREADINFO ptiT;
NTSTATUS Status = STATUS_SUCCESS;
PETHREAD Thread;
PETHREAD ThreadClient;
PTHREADINFO pti;
HANDLE CsrPortHandle;
UNREFERENCED_PARAMETER(ThreadInformationLength);
/*
* Only allow CSRSS to make this call
*/
UserAssert(ISCSRSS());
Status = ObReferenceObjectByHandle(hThread,
THREAD_SET_INFORMATION,
*PsThreadType,
UserMode,
&Thread,
NULL);
if (!NT_SUCCESS(Status))
return Status;
pti = PtiFromThread(Thread);
switch (ThreadInfoClass) {
case UserThreadFlags:
if (pti == NULL) {
Status = STATUS_INVALID_HANDLE;
} else {
UserAssert(ThreadInformationLength == sizeof(USERTHREAD_FLAGS));
pFlags = ThreadInformation;
dwOldFlags = pti->TIF_flags;
pti->TIF_flags ^= ((dwOldFlags ^ pFlags->dwFlags) & pFlags->dwMask);
}
break;
case UserThreadHungStatus:
if (pti == NULL) {
Status = STATUS_INVALID_HANDLE;
} else {
/*
* No arguments, simple set the last time read.
*/
SET_TIME_LAST_READ(pti);
}
break;
case UserThreadInitiateShutdown:
UserAssert(ThreadInformationLength == sizeof(ULONG));
Status = InitiateShutdown(Thread, (PULONG)ThreadInformation);
break;
case UserThreadEndShutdown:
UserAssert(ThreadInformationLength == sizeof(NTSTATUS));
Status = EndShutdown(Thread, *(NTSTATUS *)ThreadInformation);
break;
case UserThreadUseDesktop:
UserAssert(ThreadInformationLength == sizeof(USERTHREAD_USEDESKTOPINFO));
if (pti == NULL) {
Status = STATUS_INVALID_HANDLE;
break;
}
/*
* If the caller provides a thread handle, then we use that
* thread's pdesk and return the pdesk currently used
* by the caller (set operation). Otherwise,
* we use the pdesk provided by the caller (restore operation).
*/
hClientThread = ((PUSERTHREAD_USEDESKTOPINFO)ThreadInformation)->hThread;
if (hClientThread != NULL) {
Status = ObReferenceObjectByHandle(hClientThread,
THREAD_QUERY_INFORMATION,
*PsThreadType,
UserMode,
&ThreadClient,
NULL);
if (!NT_SUCCESS(Status))
break;
ptiT = PtiFromThread(ThreadClient);
if ((ptiT == NULL) || (ptiT->rpdesk == NULL)) {
Status = STATUS_INVALID_HANDLE;
goto DerefClientThread;
}
Status = xxxSetCsrssThreadDesktop(ptiT->rpdesk, &((PUSERTHREAD_USEDESKTOPINFO)ThreadInformation)->drdRestore);
} else {
Status = xxxRestoreCsrssThreadDesktop(&((PUSERTHREAD_USEDESKTOPINFO)ThreadInformation)->drdRestore);
}
if (hClientThread != NULL) {
DerefClientThread:
ObDereferenceObject(ThreadClient);
}
break;
case UserThreadUseActiveDesktop:
UserAssert(ThreadInformationLength == sizeof(USERTHREAD_USEDESKTOPINFO));
if (pti == NULL || grpdeskRitInput == NULL) {
Status = STATUS_INVALID_HANDLE;
break;
}
Status = xxxSetCsrssThreadDesktop(grpdeskRitInput,
&((PUSERTHREAD_USEDESKTOPINFO)ThreadInformation)->drdRestore);
break;
case UserThreadCsrApiPort:
/*
* Only CSR can call this
*/
if (PsGetThreadProcess(Thread) != gpepCSRSS) {
Status = STATUS_ACCESS_DENIED;
break;
}
UserAssert(ThreadInformationLength == sizeof(HANDLE));
/*
* Only set it once.
*/
if (CsrApiPort != NULL)
break;
CsrPortHandle = *(PHANDLE)ThreadInformation;
Status = ObReferenceObjectByHandle(
CsrPortHandle,
0,
NULL, //*LpcPortObjectType,
UserMode,
&CsrApiPort,
NULL);
if (!NT_SUCCESS(Status)) {
CsrApiPort = NULL;
RIPMSG1(RIP_WARNING,
"CSR port reference failed, Status=%#lx",
Status);
}
break;
default:
Status = STATUS_INVALID_INFO_CLASS;
RIPMSG1(RIP_ERROR, "Invalid ThreadInfoClass 0x%x", ThreadInfoClass);
break;
}
UnlockThread(Thread);
return Status;
}
/***************************************************************************\
* _GetProcessDefaultLayout (API)
*
* Retreives the default layout information about a process.
*
* History:
* 23-01-98 SamerA Created.
\***************************************************************************/
BOOL _GetProcessDefaultLayout(
OUT DWORD *pdwDefaultLayout)
{
BOOL fSuccess = FALSE;
/*
* Do not allow CSRSS to make this call. This call might happen due to
* the inheritence code. See xxxCreateWindowEx(...)
*/
if (ISCSRSS()) {
UserSetLastError(ERROR_INVALID_ACCESS);
goto api_error;
}
try {
ProbeForWriteUlong(pdwDefaultLayout);
*pdwDefaultLayout = PpiCurrent()->dwLayout;
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
goto api_error;
}
fSuccess = TRUE;
api_error:
return fSuccess;
}
/***************************************************************************\
* _SetProcessDefaultLayout (API)
*
* Sets the default layout information about a process.
*
* History:
* 23-01-98 SamerA Created.
\***************************************************************************/
BOOL _SetProcessDefaultLayout(
IN DWORD dwDefaultLayout)
{
/*
* Do not allow CSRSS to make this call
*/
UserAssert(PsGetCurrentProcess() != gpepCSRSS);
/*
* Validate dwDefaultLayout
*/
if (dwDefaultLayout & ~LAYOUT_ORIENTATIONMASK)
{
RIPERR1(ERROR_INVALID_PARAMETER,
RIP_WARNING,
"Calling SetProcessDefaultLayout with invalid layout = %lX",
dwDefaultLayout);
return FALSE;
}
/*
* Update the process default layout param
*/
PpiCurrent()->dwLayout = dwDefaultLayout;
return TRUE;
}
/***************************************************************************\
* SetInformationProcess
*
* Sets information about a process.
*
* History:
* 09-27-96 GerardoB Created.
\***************************************************************************/
NTSTATUS SetInformationProcess(
IN HANDLE hProcess,
IN USERPROCESSINFOCLASS ProcessInfoClass,
IN PVOID ProcessInformation,
IN ULONG ProcessInformationLength)
{
PUSERPROCESS_FLAGS pFlags;
DWORD dwOldFlags;
NTSTATUS Status = STATUS_SUCCESS;
PEPROCESS Process;
PPROCESSINFO ppi;
UNREFERENCED_PARAMETER(ProcessInformationLength);
UserAssert(ISCSRSS());
Status = ObReferenceObjectByHandle(hProcess,
PROCESS_SET_INFORMATION,
*PsProcessType,
UserMode,
&Process,
NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
ppi = PpiFromProcess(Process);
switch (ProcessInfoClass) {
case UserProcessFlags:
if (ppi == NULL) {
Status = STATUS_INVALID_HANDLE;
} else {
UserAssert(ProcessInformationLength == sizeof(USERPROCESS_FLAGS));
pFlags = ProcessInformation;
dwOldFlags = ppi->W32PF_Flags;
ppi->W32PF_Flags ^= ((dwOldFlags ^ pFlags->dwFlags) & pFlags->dwMask);
}
break;
default:
Status = STATUS_INVALID_INFO_CLASS;
UserAssert(FALSE);
break;
}
UnlockProcess(Process);
return Status;
}
/***************************************************************************\
* xxxSetConsoleCaretInfo
*
* Store information about the console's homegrown caret and notify any
* interested apps that it changed. We need this for accessibility.
*
* History:
* 26-May-1999 JerrySh Created.
\***************************************************************************/
VOID xxxSetConsoleCaretInfo(
PCONSOLE_CARET_INFO pcci)
{
PWND pwnd;
TL tlpwnd;
pwnd = ValidateHwnd(pcci->hwnd);
if (pwnd && pwnd->head.rpdesk) {
pwnd->head.rpdesk->cciConsole = *pcci;
ThreadLock(pwnd, &tlpwnd);
xxxWindowEvent(EVENT_OBJECT_LOCATIONCHANGE, pwnd, OBJID_CARET, INDEXID_CONTAINER, WEF_ASYNC);
ThreadUnlock(&tlpwnd);
}
}
/***************************************************************************\
* xxxConsoleControl
*
* Performs special control operations for console.
*
* History:
* 03-01-95 JimA Created.
\***************************************************************************/
NTSTATUS xxxConsoleControl(
IN CONSOLECONTROL ConsoleControl,
IN PVOID ConsoleInformation,
IN ULONG ConsoleInformationLength)
{
PCONSOLEDESKTOPCONSOLETHREAD pDesktopConsole;
PCONSOLEWINDOWSTATIONPROCESS pConsoleWindowStationInfo;
PDESKTOP pdesk;
DWORD dwThreadIdOld;
NTSTATUS Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ConsoleInformationLength);
UserAssert(ISCSRSS());
switch (ConsoleControl) {
case ConsoleDesktopConsoleThread:
UserAssert(ConsoleInformationLength == sizeof(CONSOLEDESKTOPCONSOLETHREAD));
pDesktopConsole = (PCONSOLEDESKTOPCONSOLETHREAD)ConsoleInformation;
Status = ObReferenceObjectByHandle(
pDesktopConsole->hdesk,
0,
*ExDesktopObjectType,
UserMode,
&pdesk,
NULL);
if (!NT_SUCCESS(Status))
return Status;
LogDesktop(pdesk, LD_REF_FN_CONSOLECONTROL1, TRUE, (ULONG_PTR)PtiCurrent());
dwThreadIdOld = pdesk->dwConsoleThreadId;
if (pDesktopConsole->dwThreadId != (DWORD)-1) {
pdesk->dwConsoleThreadId =
pDesktopConsole->dwThreadId;
/*
* Make sure that the console input thread is not starting while
* shutting down.
*/
if ((pDesktopConsole->dwThreadId != 0) && (gdwHydraHint & HH_INITIATEWIN32KCLEANUP)) {
FRE_RIPMSG1(RIP_ERROR, "xxxConsoleControl: Console input thread starting during shutdown. dwThreadId: %lx",
pDesktopConsole->dwThreadId);
}
}
pDesktopConsole->dwThreadId = dwThreadIdOld;
LogDesktop(pdesk, LD_DEREF_FN_CONSOLECONTROL1, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdesk);
break;
case ConsoleClassAtom:
UserAssert(ConsoleInformationLength == sizeof(ATOM));
gatomConsoleClass = *(ATOM *)ConsoleInformation;
break;
case ConsoleNotifyConsoleApplication:
/*
* Bug 273518 - joejo
*
* Adding optimization to bug fix
*/
UserAssert(ConsoleInformationLength == sizeof(CONSOLE_PROCESS_INFO));
xxxUserNotifyConsoleApplication((PCONSOLE_PROCESS_INFO)ConsoleInformation);
break;
case ConsoleSetVDMCursorBounds:
UserAssert((ConsoleInformation == NULL) ||
(ConsoleInformationLength == sizeof(RECT)));
SetVDMCursorBounds(ConsoleInformation);
break;
case ConsolePublicPalette:
UserAssert(ConsoleInformationLength == sizeof(HPALETTE));
GreSetPaletteOwner(*(HPALETTE *)ConsoleInformation, OBJECT_OWNER_PUBLIC);
break;
case ConsoleWindowStationProcess:
UserAssert(ConsoleInformationLength == sizeof(CONSOLEWINDOWSTATIONPROCESS));
pConsoleWindowStationInfo = (PCONSOLEWINDOWSTATIONPROCESS)ConsoleInformation;
UserSetConsoleProcessWindowStation(pConsoleWindowStationInfo->dwProcessId,
pConsoleWindowStationInfo->hwinsta);
break;
#if defined(FE_IME)
/*
* For console IME issue
*
* Console IME do register thread ID in DESKTOP info.
* So should be per desktop.
*/
case ConsoleRegisterConsoleIME:
{
PCONSOLE_REGISTER_CONSOLEIME RegConIMEInfo;
DWORD dwConsoleIMEThreadIdOld;
UserAssert(ConsoleInformationLength == sizeof(CONSOLE_REGISTER_CONSOLEIME));
RegConIMEInfo = (PCONSOLE_REGISTER_CONSOLEIME)ConsoleInformation;
RegConIMEInfo->dwConsoleInputThreadId = 0;
Status = ObReferenceObjectByHandle(
RegConIMEInfo->hdesk,
0,
*ExDesktopObjectType,
UserMode,
&pdesk,
NULL);
if (!NT_SUCCESS(Status))
return Status;
LogDesktop(pdesk, LD_REF_FN_CONSOLECONTROL2, TRUE, (ULONG_PTR)PtiCurrent());
Status = STATUS_SUCCESS;
if (pdesk->dwConsoleThreadId)
{
/*
* Exists console input thread
*/
RegConIMEInfo->dwConsoleInputThreadId = pdesk->dwConsoleThreadId;
dwConsoleIMEThreadIdOld = pdesk->dwConsoleIMEThreadId;
if (RegConIMEInfo->dwAction != REGCONIME_QUERY) {
PTHREADINFO ptiConsoleIME;
if ((ptiConsoleIME = PtiFromThreadId(RegConIMEInfo->dwThreadId)) != NULL) {
if ((RegConIMEInfo->dwAction == REGCONIME_REGISTER) &&
!(ptiConsoleIME->TIF_flags & TIF_DONTATTACHQUEUE)) {
/*
* Register
*/
ptiConsoleIME->TIF_flags |= TIF_DONTATTACHQUEUE;
pdesk->dwConsoleIMEThreadId = RegConIMEInfo->dwThreadId;
} else if ((RegConIMEInfo->dwAction == REGCONIME_UNREGISTER) &&
(ptiConsoleIME->TIF_flags & TIF_DONTATTACHQUEUE)) {
/*
* Unregister
*/
ptiConsoleIME->TIF_flags &= ~TIF_DONTATTACHQUEUE;
pdesk->dwConsoleIMEThreadId = 0;
} else if (RegConIMEInfo->dwAction == REGCONIME_TERMINATE) {
/*
* Terminate console IME (Logoff/Shutdown)
*/
pdesk->dwConsoleIMEThreadId = 0;
}
} else if (RegConIMEInfo->dwAction == REGCONIME_TERMINATE) {
/*
* Abnormal end console IME
*/
pdesk->dwConsoleIMEThreadId = 0;
} else {
Status = STATUS_ACCESS_DENIED;
}
}
RegConIMEInfo->dwThreadId = dwConsoleIMEThreadIdOld;
}
LogDesktop(pdesk, LD_DEREF_FN_CONSOLECONTROL2, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdesk);
return Status;
}
break;
#endif
case ConsoleFullscreenSwitch:
UserAssert(ConsoleInformationLength == sizeof(CONSOLE_FULLSCREEN_SWITCH));
xxxbFullscreenSwitch(((PCONSOLE_FULLSCREEN_SWITCH)ConsoleInformation)->bFullscreenSwitch,
((PCONSOLE_FULLSCREEN_SWITCH)ConsoleInformation)->hwnd);
break;
case ConsoleSetCaretInfo:
UserAssert(ConsoleInformationLength == sizeof(CONSOLE_CARET_INFO));
xxxSetConsoleCaretInfo((PCONSOLE_CARET_INFO)ConsoleInformation);
break;
default:
RIPMSGF1(RIP_ERROR, "Invalid control class: 0x%x", ConsoleControl);
return STATUS_INVALID_INFO_CLASS;
}
return STATUS_SUCCESS;
}