mirror of https://github.com/lianthony/NT4.0
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.
4796 lines
140 KiB
4796 lines
140 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: queue.c
|
|
*
|
|
* Copyright (c) 1985-95, 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 ProcessUpdateKeyStateEvent(PQ pq, PBYTE pbKeyState);
|
|
VOID DestroyProcessesObjects(PPROCESSINFO ppi);
|
|
VOID DestroyThreadsMessages(PQ pq, PTHREADINFO pti);
|
|
void CheckProcessForeground(PTHREADINFO pti);
|
|
void ScreenSaverCheck(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);
|
|
|
|
PW32PROCESS gpwpCalcFirst = NULL;
|
|
|
|
#ifdef DEBUG
|
|
#define MSG_SENT 0
|
|
#define MSG_POST 1
|
|
#define MSG_RECV 2
|
|
#define MSG_PEEK 3
|
|
VOID TraceDdeMsg(UINT msg, HWND hwndFrom, HWND hwndTo, UINT code);
|
|
#else
|
|
#define TraceDdeMsg(m, h1, h2, c)
|
|
#endif // DEBUG
|
|
|
|
PVOID QEntryLookasideBase;
|
|
PVOID QEntryLookasideBounds;
|
|
ZONE_HEADER QEntryLookasideZone;
|
|
#if DBG
|
|
ULONG AllocQEntryHiWater;
|
|
ULONG AllocQEntryCalls;
|
|
ULONG AllocQEntrySlowCalls;
|
|
ULONG DelQEntryCalls;
|
|
ULONG DelQEntrySlowCalls;
|
|
#endif // DBG
|
|
|
|
#define QUEUE_ENTRIES 16
|
|
PQ QueueFreeList[QUEUE_ENTRIES];
|
|
|
|
#ifdef DEBUG
|
|
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
|
|
|
|
/***************************************************************************\
|
|
* xxxSetProcessInitState
|
|
*
|
|
* Set process initialization state. What state is set depends
|
|
* on whether another process is waiting on this process.
|
|
*
|
|
* 04-02-95 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
void xxxSetProcessInitState(
|
|
PEPROCESS Process,
|
|
DWORD dwFlags)
|
|
{
|
|
PW32PROCESS W32Process;
|
|
NTSTATUS Status;
|
|
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* If the W32Process structure has not been allocated, do it now.
|
|
* Leave the Process field NULL so the kernel will know to do
|
|
* a callout if the process ever calls Win32k
|
|
*/
|
|
W32Process = (PW32PROCESS)Process->Win32Process;
|
|
if (W32Process == NULL) {
|
|
LeaveCrit();
|
|
Status = PsCreateWin32Process(Process);
|
|
EnterCrit();
|
|
if (!NT_SUCCESS(Status))
|
|
return;
|
|
W32Process = (PW32PROCESS)Process->Win32Process;
|
|
|
|
if (W32Process == NULL) {
|
|
RIPMSG1(RIP_WARNING, "xxxSetProcessInitState: Process %lX died from under us", Process);
|
|
return;
|
|
}
|
|
}
|
|
|
|
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;
|
|
CalcStartCursorHide(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. InitProcessInfo() will set
|
|
* W32PF_ALLOWFOREGROUNDACTIVATE when the process initializes.
|
|
*/
|
|
gfAllowForegroundActivate = TRUE;
|
|
|
|
/*
|
|
* 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;
|
|
CalcStartCursorHide(NULL, 0);
|
|
} else if (dwFlags & STARTF_FORCEONFEEDBACK) {
|
|
CalcStartCursorHide(W32Process, 2000);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserNotifyConsoleApplication
|
|
*
|
|
* 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.
|
|
\***************************************************************************/
|
|
|
|
void UserNotifyConsoleApplication(
|
|
DWORD idProcess)
|
|
{
|
|
NTSTATUS Status;
|
|
PEPROCESS Process;
|
|
|
|
/*
|
|
* First search for this process in our process information list.
|
|
*/
|
|
LeaveCrit();
|
|
Status = LockProcessByClientId((HANDLE)idProcess, &Process);
|
|
EnterCrit();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG2(RIP_WARNING, "UserNotifyConsoleApplication: Failed with Process ID == %X, Status = %x\n",
|
|
idProcess, Status);
|
|
return;
|
|
}
|
|
|
|
xxxSetProcessInitState(Process, 0);
|
|
|
|
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.
|
|
*/
|
|
LeaveCrit();
|
|
Status = LockProcessByClientId((HANDLE)idProcess, &Process);
|
|
EnterCrit();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG2(RIP_WARNING, "UserSetConsoleProcessWindowStation: Failed with Process ID == %X, Status = %x\n",
|
|
idProcess, Status);
|
|
return;
|
|
}
|
|
|
|
Process->Win32WindowStation = hwinsta;
|
|
|
|
UnlockProcess(Process);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* UserNotifyProcessCreate
|
|
*
|
|
* 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 UserNotifyProcessCreate(
|
|
DWORD idProcess,
|
|
DWORD idParentThread,
|
|
DWORD dwData,
|
|
DWORD dwFlags)
|
|
{
|
|
PEPROCESS Process;
|
|
PETHREAD Thread;
|
|
PTHREADINFO pti;
|
|
NTSTATUS Status;
|
|
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* 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) {
|
|
LeaveCrit();
|
|
Status = LockProcessByClientId((HANDLE)idProcess, &Process);
|
|
EnterCrit();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG2(RIP_WARNING, "UserNotifyProcessCreate: Failed with Process ID == %X, Status = %x\n",
|
|
idProcess, Status);
|
|
return FALSE;
|
|
}
|
|
|
|
xxxSetProcessInitState(Process, ((dwFlags & 1) ?
|
|
STARTF_FORCEONFEEDBACK : STARTF_FORCEOFFFEEDBACK));
|
|
if (dwFlags & 0x8) {
|
|
if (Process->Win32Process)
|
|
((PW32PROCESS)Process->Win32Process)->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().
|
|
*/
|
|
|
|
LeaveCrit();
|
|
Status = LockThreadByClientId((HANDLE)idParentThread, &Thread);
|
|
EnterCrit();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG2(RIP_WARNING, "UserNotifyProcessCreate: Failed with Thread ID == %X, Status = %x\n",
|
|
idParentThread, Status);
|
|
return FALSE;
|
|
}
|
|
|
|
pti = PtiFromThread(Thread);
|
|
if (pti && (pti->TIF_flags & TIF_16BIT)) {
|
|
pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
|
|
}
|
|
|
|
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_THREADINFO);
|
|
if (pwti == NULL) {
|
|
LeaveCrit();
|
|
return FALSE;
|
|
}
|
|
INIT_PSEUDO_EVENT(&pwti->pIdleEvent);
|
|
pwti->idTask = idProcess;
|
|
pwti->pwtiNext = gpwtiFirst;
|
|
gpwtiFirst = pwti;
|
|
} else {
|
|
RESET_PSEUDO_EVENT(&pwti->pIdleEvent);
|
|
}
|
|
|
|
pwti->idWaitObject = dwData;
|
|
LeaveCrit();
|
|
Status = LockThreadByClientId((HANDLE)idParentThread, &Thread);
|
|
EnterCrit();
|
|
if (!NT_SUCCESS(Status))
|
|
return FALSE;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG2(RIP_WARNING, "UserNotifyProcessCreate: Failed with Thread ID == %X, Status = %x\n",
|
|
idParentThread, Status);
|
|
return FALSE;
|
|
}
|
|
|
|
pwti->idParentProcess = (DWORD)Thread->Cid.UniqueProcess;
|
|
UnlockThread(Thread);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* CalcStartCursorHide
|
|
*
|
|
* Calculates when to hide the startup cursor.
|
|
*
|
|
* 05-14-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
void CalcStartCursorHide(
|
|
PW32PROCESS pwp,
|
|
DWORD timeAdd)
|
|
{
|
|
DWORD timeNow;
|
|
PW32PROCESS pwpT;
|
|
PW32PROCESS *ppwpT;
|
|
|
|
timeNow = NtGetTickCount();
|
|
|
|
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 (pwpT->StartCursorHideTime <= timeNow) {
|
|
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).
|
|
*/
|
|
UpdateCursorImage();
|
|
}
|
|
|
|
|
|
#define QUERY_VALUE_BUFFER 80
|
|
|
|
/***************************************************************************\
|
|
* SetAppCompatFlags
|
|
*
|
|
*
|
|
* History:
|
|
* 03-23-92 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
void SetAppCompatFlags(
|
|
PTHREADINFO pti)
|
|
{
|
|
DWORD dwFlags = 0;
|
|
WCHAR szHex[QUERY_VALUE_BUFFER];
|
|
WCHAR szKey[80];
|
|
WCHAR *pchStart, *pchEnd;
|
|
DWORD cb;
|
|
PUNICODE_STRING pstrAppName;
|
|
|
|
/*
|
|
* If this process is WOW, every app (and every thread) has its own
|
|
* compat flags. If not WOW, then we only need to do this lookup
|
|
* once per process.
|
|
*/
|
|
if (!(pti->TIF_flags & TIF_16BIT)) {
|
|
if (LOWORD(pti->dwExpWinVer) <= VER31) {
|
|
if (pti->ppi->W32PF_Flags & W32PF_HAVECOMPATFLAGS) {
|
|
pti->dwCompatFlags = pti->ppi->dwCompatFlags;
|
|
pti->pClientInfo->dwCompatFlags = pti->dwCompatFlags;
|
|
return;
|
|
}
|
|
}
|
|
goto SACF_GotFlags; // They're zero; we're a 3.1 (32 bit) app
|
|
}
|
|
|
|
|
|
/*
|
|
* Find end of app name
|
|
*/
|
|
if (pti->pstrAppName != NULL)
|
|
pstrAppName = pti->pstrAppName;
|
|
else
|
|
pstrAppName = &pti->Thread->ThreadsProcess->Peb->ProcessParameters->ImagePathName;
|
|
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--;
|
|
}
|
|
|
|
/*
|
|
* Get a copy of the filename - make sure it fits and is zero
|
|
* terminated.
|
|
*/
|
|
cb = (pchEnd - pchStart) * sizeof(WCHAR);
|
|
if (cb >= sizeof(szKey))
|
|
cb = sizeof(szKey) - sizeof(WCHAR);
|
|
RtlCopyMemory(szKey, pchStart, cb);
|
|
szKey[(cb / sizeof(WCHAR))] = 0;
|
|
|
|
/*
|
|
* Find compatiblility flags (if not a 4.0 app)
|
|
*/
|
|
if (LOWORD(pti->dwExpWinVer) <= VER31)
|
|
if (UT_FastGetProfileStringW(PMAP_COMPAT, szKey, TEXT(""),
|
|
szHex, sizeof(szHex))) {
|
|
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);
|
|
}
|
|
|
|
SACF_GotFlags:
|
|
|
|
pti->dwCompatFlags = dwFlags;
|
|
pti->pClientInfo->dwCompatFlags = dwFlags;
|
|
|
|
pti->ppi->dwCompatFlags = dwFlags;
|
|
pti->ppi->W32PF_Flags |= W32PF_HAVECOMPATFLAGS;
|
|
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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)
|
|
{
|
|
if (pti == NULL)
|
|
pti = PtiCurrent();
|
|
|
|
return pti->dwCompatFlags;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxInitTask -- called by WOW startup for each app
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* 02-21-91 MikeHar Created.
|
|
* 02-23-92 MattFe Altered for WOW
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS xxxInitTask(
|
|
UINT dwExpWinVer,
|
|
PUNICODE_STRING pstrAppName,
|
|
DWORD hTaskWow,
|
|
DWORD dwHotkey,
|
|
DWORD idTask,
|
|
DWORD dwX,
|
|
DWORD dwY,
|
|
DWORD dwXSize,
|
|
DWORD dwYSize,
|
|
WORD wUnusedAndAvailable)
|
|
{
|
|
PTHREADINFO pti;
|
|
PTDB ptdb;
|
|
PPROCESSINFO ppi;
|
|
PWOWTHREADINFO pwti;
|
|
|
|
UNREFERENCED_PARAMETER(wUnusedAndAvailable);
|
|
|
|
pti = PtiCurrent();
|
|
ppi = pti->ppi;
|
|
|
|
/*
|
|
* Set the real name of the module. (Instead of 'NTVDM')
|
|
*/
|
|
if (pti->pstrAppName != NULL)
|
|
UserFreePool(pti->pstrAppName);
|
|
pti->pstrAppName = UserAllocPoolWithQuota(sizeof(UNICODE_STRING) +
|
|
pstrAppName->MaximumLength, TAG_TEXT);
|
|
if (pti->pstrAppName != NULL) {
|
|
pti->pstrAppName->Buffer = (PWCHAR)(pti->pstrAppName + 1);
|
|
try {
|
|
RtlCopyMemory(pti->pstrAppName->Buffer, pstrAppName->Buffer,
|
|
pstrAppName->Length);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
UserFreePool(pti->pstrAppName);
|
|
pti->pstrAppName = NULL;
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
pti->pstrAppName->MaximumLength = pstrAppName->MaximumLength;
|
|
pti->pstrAppName->Length = pstrAppName->Length;
|
|
} else
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
|
|
/*
|
|
* An app is starting!
|
|
*/
|
|
if (!(ppi->W32PF_Flags & W32PF_APPSTARTING)) {
|
|
ppi->W32PF_Flags |= W32PF_APPSTARTING;
|
|
ppi->ppiNext = gppiStarting;
|
|
gppiStarting = 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_WOW)) == NULL)
|
|
return STATUS_NO_MEMORY;
|
|
pti->ptdb = ptdb;
|
|
|
|
/*
|
|
* Set the flags to say this is a 16-bit thread - before attaching
|
|
* queues!
|
|
*/
|
|
pti->TIF_flags |= TIF_16BIT | TIF_FIRSTIDLE;
|
|
|
|
/*
|
|
* 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) {
|
|
pti->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;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
if (pwti == NULL) {
|
|
RIPMSG0(RIP_WARNING, "InitTask couldn't find WOW struct\n");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
pti->pClientInfo->dwTIFlags |= pti->TIF_flags;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
if (!FJOURNALRECORD() && !FJOURNALPLAYBACK())
|
|
ReattachThreads(FALSE);
|
|
|
|
/*
|
|
* Save away the 16 bit task handle: we use this later when calling
|
|
* wow back to close a WOW task.
|
|
*/
|
|
ptdb->hTaskWow = hTaskWow;
|
|
|
|
/*
|
|
* Setup the app start cursor for 5 second timeout.
|
|
*/
|
|
CalcStartCursorHide((PW32PROCESS)ppi, 5000);
|
|
|
|
/*
|
|
* HIWORD: != 0 if wants proportional font
|
|
* LOWORD: Expected windows version (3.00 [300], 3.10 [30A], etc)
|
|
*/
|
|
pti->dwExpWinVer = dwExpWinVer;
|
|
pti->pClientInfo->dwExpWinVer = dwExpWinVer;
|
|
|
|
/*
|
|
* 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->nEvents = 0;
|
|
ptdb->pti = pti;
|
|
ptdb->ptdbNext = NULL;
|
|
|
|
InsertTask(ppi, ptdb);
|
|
|
|
SetAppCompatFlags(pti);
|
|
|
|
/*
|
|
* 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 = pti;
|
|
ppi->pwpi->CSLockCount = -1;
|
|
|
|
EnterWowCritSect(pti, ppi->pwpi);
|
|
|
|
/*
|
|
* ensure app gets focus
|
|
*/
|
|
_ShowStartGlass(10000);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _ShowStartGlass
|
|
*
|
|
* This routine is called by WOW when first starting or when starting an
|
|
* additional WOW app.
|
|
*
|
|
* 12-07-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
void _ShowStartGlass(
|
|
DWORD dwTimeout)
|
|
{
|
|
PPROCESSINFO ppi;
|
|
|
|
/*
|
|
* If this is the first call to ShowStartGlass(), 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 = PtiCurrent()->ppi;
|
|
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.
|
|
*/
|
|
gfAllowForegroundActivate = TRUE;
|
|
ppi->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
|
|
}
|
|
ppi->W32PF_Flags |= W32PF_SHOWSTARTGLASSCALLED;
|
|
|
|
/*
|
|
* Show the start glass cursor for this much longer.
|
|
*/
|
|
CalcStartCursorHide((PW32PROCESS)ppi, dwTimeout);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxCreateThreadInfo
|
|
*
|
|
* Allocate the main thread information structure
|
|
*
|
|
* History:
|
|
* 03-18-95 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
#define STARTF_SCREENSAVER 0x80000000
|
|
|
|
ULONG ParseReserved(
|
|
WCHAR *pchReserved,
|
|
WCHAR *pchFind)
|
|
{
|
|
ULONG dw;
|
|
WCHAR *pch, *pchT, ch;
|
|
UNICODE_STRING uString;
|
|
|
|
dw = 0;
|
|
if (pchReserved != NULL && (pch = wcsstr(pchReserved, pchFind)) != NULL) {
|
|
pch += wcslen(pchFind);
|
|
|
|
pchT = pch;
|
|
while (*pchT >= '0' && *pchT <= '9')
|
|
pchT++;
|
|
|
|
ch = *pchT;
|
|
*pchT = 0;
|
|
RtlInitUnicodeString(&uString, pch);
|
|
*pchT = ch;
|
|
|
|
RtlUnicodeStringToInteger(&uString, 0, &dw);
|
|
}
|
|
|
|
return dw;
|
|
}
|
|
|
|
NTSTATUS xxxCreateThreadInfo(
|
|
PW32THREAD W32Thread)
|
|
{
|
|
PETHREAD Thread = W32Thread->Thread;
|
|
DWORD dwTIFlags = 0;
|
|
PPROCESSINFO ppi;
|
|
PTHREADINFO pti;
|
|
PEPROCESS Process = Thread->ThreadsProcess;
|
|
PUSERSTARTUPINFO pusi;
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParams;
|
|
PDESKTOP pdesk = NULL;
|
|
HDESK hdesk = NULL;
|
|
HWINSTA hwinsta;
|
|
PQ pq;
|
|
NTSTATUS Status;
|
|
BOOL fFirstThread;
|
|
PTEB pteb = NtCurrentTeb();
|
|
|
|
/*
|
|
* Although all threads now have a ETHREAD structure, server-side
|
|
* threads (RIT, Console, etc) don't have a client-server eventpair
|
|
* handle. We use this to distinguish the two cases.
|
|
*/
|
|
if (IS_SYSTEM_THREAD(Thread)) {
|
|
#ifdef FE_IME
|
|
dwTIFlags = TIF_SYSTEMTHREAD | TIF_DONTATTACHQUEUE | TIF_DISABLEIME;
|
|
#else
|
|
dwTIFlags = TIF_SYSTEMTHREAD | TIF_DONTATTACHQUEUE;
|
|
#endif
|
|
} else if (Process == gpepCSRSS) {
|
|
#ifdef FE_IME
|
|
dwTIFlags = TIF_CSRSSTHREAD | TIF_DONTATTACHQUEUE | TIF_DISABLEIME;
|
|
#else
|
|
dwTIFlags = TIF_CSRSSTHREAD | TIF_DONTATTACHQUEUE;
|
|
#endif
|
|
}
|
|
|
|
ProcessParams = (Process->Peb ? Process->Peb->ProcessParameters : NULL);
|
|
|
|
/*
|
|
* Locate the processinfo structure for the new thread.
|
|
*/
|
|
ppi = PpiCurrent();
|
|
|
|
|
|
/*
|
|
* Allocate the thread-info structure. If it's a SYSTEMTHREAD, then
|
|
* make sure we have enough space for the (pwinsta) pointer. This
|
|
* is referenced in (paint.c: DoPaint) to assure desktop/input can
|
|
* have a winsta to view.
|
|
*/
|
|
pti = (PTHREADINFO)W32Thread;
|
|
|
|
pti->TIF_flags = dwTIFlags;
|
|
pti->ptl = NULL;
|
|
pti->pmsd = NULL;
|
|
pti->Thread = (PETHREAD)Thread;
|
|
Lock(&pti->spklActive, gspklBaseLayout);
|
|
pti->pcti = &(pti->cti);
|
|
#ifdef FE_IME
|
|
pti->spwndDefaultIme = NULL;
|
|
pti->spDefaultImc = NULL;
|
|
pti->hklPrev = (HKL)0;
|
|
#endif
|
|
|
|
/*
|
|
* 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);
|
|
|
|
pti->ppi = ppi;
|
|
pti->ptiSibling = ppi->ptiList;
|
|
ppi->ptiList = pti;
|
|
ppi->cThreads++;
|
|
|
|
|
|
if (pteb != NULL)
|
|
pteb->Win32ThreadInfo = pti;
|
|
|
|
/*
|
|
* Point to the client info.
|
|
*/
|
|
if (dwTIFlags & TIF_SYSTEMTHREAD) {
|
|
pti->pClientInfo = UserAllocPoolWithQuota(sizeof(CLIENTINFO),
|
|
TAG_THREADINFO);
|
|
if (pti->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(NtCurrentTeb() != NULL);
|
|
pti->pClientInfo = ((PCLIENTINFO)((NtCurrentTeb())->Win32ClientInfo));
|
|
}
|
|
|
|
|
|
/*
|
|
* Create the input event.
|
|
*/
|
|
Status = ZwCreateEvent(&pti->hEventQueueClient,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ObReferenceObjectByHandle(pti->hEventQueueClient,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
UserMode,
|
|
&pti->pEventQueueServer,
|
|
NULL);
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ProtectHandle(pti->hEventQueueClient, TRUE);
|
|
} else if (Status == STATUS_INVALID_HANDLE) {
|
|
pti->hEventQueueClient = NULL;
|
|
}
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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 (fFirstThread) {
|
|
|
|
if (ProcessParams->WindowFlags & STARTF_USEHOTKEY) {
|
|
ppi->dwHotkey = (DWORD)ProcessParams->StandardInput;
|
|
} else {
|
|
ppi->dwHotkey = ParseReserved(ProcessParams->ShellInfo.Buffer,
|
|
L"hotkey.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Open the windowstation and desktop. If this is a system
|
|
* thread only use the desktop that might be stored in the teb.
|
|
*/
|
|
UserAssert(pti->rpdesk == NULL);
|
|
if (!(pti->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) &&
|
|
(grpwinstaList != NULL)) {
|
|
|
|
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 ErrorResponse;
|
|
|
|
LeaveCrit();
|
|
|
|
ExRaiseHardError((NTSTATUS)STATUS_DLL_INIT_FAILED_LOGOFF,
|
|
0,
|
|
0,
|
|
NULL,
|
|
OptionOk,
|
|
&ErrorResponse);
|
|
|
|
ZwTerminateProcess(NtCurrentProcess(), STATUS_DLL_INIT_FAILED);
|
|
|
|
EnterCrit();
|
|
}
|
|
else {
|
|
Status = STATUS_DLL_INIT_FAILED;
|
|
goto CreateThreadInfoFailed;
|
|
}
|
|
|
|
return STATUS_DLL_INIT_FAILED;
|
|
|
|
} else {
|
|
|
|
xxxSetProcessWindowStation(hwinsta);
|
|
|
|
/*
|
|
* Reference the desktop handle
|
|
*/
|
|
Status = ObReferenceObjectByHandle(
|
|
hdesk,
|
|
0,
|
|
*ExDesktopObjectType,
|
|
KernelMode,
|
|
&pdesk,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto CreateThreadInfoFailed;
|
|
}
|
|
|
|
/*
|
|
* The first desktop is the default for all succeeding threads.
|
|
*/
|
|
if ((ppi->hdeskStartup == NULL) &&
|
|
(Process->UniqueProcessId != gpidLogon)) {
|
|
|
|
LockDesktop(&ppi->rpdeskStartup, pdesk);
|
|
ppi->hdeskStartup = hdesk;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remember dwExpWinVer. This is used to return GetAppVer() (and
|
|
* GetExpWinVer(NULL).
|
|
*/
|
|
if (Process->Peb != NULL)
|
|
pti->dwExpWinVer = RtlGetExpWinVer(Process->Peb->ImageBaseAddress);
|
|
else
|
|
pti->dwExpWinVer = VER40;
|
|
|
|
pti->pClientInfo->dwExpWinVer = pti->dwExpWinVer;
|
|
pti->pClientInfo->dwTIFlags = pti->TIF_flags;
|
|
if (pti->spklActive) {
|
|
pti->pClientInfo->CodePage = pti->spklActive->CodePage;
|
|
} else {
|
|
pti->pClientInfo->CodePage = CP_ACP;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
if (ProcessParams && ProcessParams->WindowFlags & STARTF_SCREENSAVER) {
|
|
SetForegroundPriority(pti, TRUE);
|
|
#ifdef FE_IME
|
|
/*
|
|
* Screen saver doesn't need any IME processing.
|
|
*/
|
|
pti->TIF_flags |= (TIF_SCREENSAVER | TIF_DISABLEIME);
|
|
#else
|
|
pti->TIF_flags |= TIF_SCREENSAVER;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Set the desktop even if it is NULL to ensure that pti->pDeskInfo
|
|
* is set.
|
|
*/
|
|
SetDesktop(pti, pdesk, hdesk);
|
|
|
|
/*
|
|
* Do special processing for the first thread of a process.
|
|
*/
|
|
if (!(pti->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD))) {
|
|
|
|
if (fFirstThread) {
|
|
|
|
/*
|
|
* Setup public classes. Classes are only unregistered at process
|
|
* cleanup time. If a process exists but has only one gui thread
|
|
* that it creates and destroys, we'd continually execute through
|
|
* this cThreads == 1 code path. So make sure anything we allocate
|
|
* here only gets allocated once.
|
|
*/
|
|
if (!(ppi->W32PF_Flags & W32PF_CLASSESREGISTERED)) {
|
|
ppi->W32PF_Flags |= W32PF_CLASSESREGISTERED;
|
|
LW_RegisterWindows(FALSE);
|
|
}
|
|
|
|
/*
|
|
* If this is an application starting (ie. not some thread of
|
|
* the server context), enable the app-starting cursor.
|
|
*/
|
|
CalcStartCursorHide((PW32PROCESS)Process->Win32Process, 5000);
|
|
|
|
/*
|
|
* Open the windowstation
|
|
*/
|
|
if (grpwinstaList && ppi->rpwinsta == NULL) {
|
|
RIPERR0(ERROR_CAN_NOT_COMPLETE, RIP_WARNING, "System is not initialized\n");
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto CreateThreadInfoFailed;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* Don't register system windows until cursors and icons
|
|
* have been loaded.
|
|
*/
|
|
if ((SYSCUR(ARROW) != NULL) &&
|
|
!(ppi->W32PF_Flags & W32PF_CLASSESREGISTERED)) {
|
|
|
|
ppi->W32PF_Flags |= W32PF_CLASSESREGISTERED;
|
|
LW_RegisterWindows(pti->TIF_flags & TIF_SYSTEMTHREAD);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we have a desktop and are journalling on that desktop, use
|
|
* the journal queue, otherwise create a new queue.
|
|
*/
|
|
if (!(pti->TIF_flags & TIF_DONTATTACHQUEUE) &&
|
|
(pdesk != NULL) &&
|
|
(pdesk == grpdeskRitInput) &&
|
|
(pdesk->pDeskInfo->fsHooks &
|
|
(WHF_JOURNALPLAYBACK | WHF_JOURNALRECORD))) {
|
|
|
|
PTHREADINFO ptiT;
|
|
|
|
if (pdesk->pDeskInfo->asphkStart[WH_JOURNALPLAYBACK + 1] != NULL) {
|
|
ptiT = GETPTI(pdesk->pDeskInfo->asphkStart[WH_JOURNALPLAYBACK + 1]);
|
|
} else {
|
|
ptiT = GETPTI(pdesk->pDeskInfo->asphkStart[WH_JOURNALRECORD + 1]);
|
|
}
|
|
|
|
pti->pq = ptiT->pq;
|
|
pti->pq->cThreads++;
|
|
|
|
} else {
|
|
|
|
if ((pq = AllocQueue(NULL, NULL)) == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto CreateThreadInfoFailed;
|
|
}
|
|
|
|
/*
|
|
* Attach the Q to the THREADINFO.
|
|
*/
|
|
pti->pq = pq;
|
|
pq->ptiMouse = pq->ptiKeyboard = pti;
|
|
pq->cThreads++;
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize hung timer value
|
|
*/
|
|
|
|
SET_TIME_LAST_READ(pti);
|
|
|
|
/*
|
|
* If someone is waiting on this process propagate that info into
|
|
* the thread info
|
|
*/
|
|
if (ppi->W32PF_Flags & W32PF_WAITFORINPUTIDLE)
|
|
pti->TIF_flags |= TIF_WAITFORINPUTIDLE;
|
|
|
|
/*
|
|
* Mark the thread as initialized.
|
|
*/
|
|
pti->TIF_flags |= TIF_GUITHREADINITIALIZED;
|
|
|
|
/*
|
|
* Allow the thread to come to foreground when it is created
|
|
* if the current process is the foreground process.
|
|
* This Flag is a hack to fix Bug 28502. When we click on
|
|
* "Map Network Drive" button on the toolbar, the explorer (Bobday)
|
|
* 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( (gptiForeground) && (ppi == gptiForeground->ppi) ) {
|
|
pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
|
|
}
|
|
|
|
/*
|
|
* Call back to the client to finish initialization.
|
|
*/
|
|
if (!(dwTIFlags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD))) {
|
|
|
|
SetAppCompatFlags(pti);
|
|
|
|
Status = ClientThreadSetup();
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG0(RIP_WARNING, "ClientThreadSetup failed");
|
|
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
|
|
* will be created
|
|
*/
|
|
xxxPlayEventSound(L"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:%#lx", pdesk);
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto CreateThreadInfoFailed;
|
|
}
|
|
ObDereferenceObject(pdesk);
|
|
}
|
|
|
|
return Status;
|
|
|
|
CreateThreadInfoFailed:
|
|
if (pdesk != NULL) {
|
|
ObDereferenceObject(pdesk);
|
|
}
|
|
xxxDestroyThreadInfo();
|
|
DeleteThreadInfo(pti);
|
|
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
|
|
// other wise use global AsyncKeyState
|
|
PQ pq) // non-null == preallocated object
|
|
{
|
|
int i;
|
|
USHORT cLockCount;
|
|
|
|
if (pq == NULL) {
|
|
for (i = 0; i < QUEUE_ENTRIES; i++) {
|
|
if (QueueFreeList[i] != NULL) {
|
|
pq = QueueFreeList[i];
|
|
QueueFreeList[i] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
if (pq == NULL) {
|
|
pq = UserAllocPool(sizeof(Q), TAG_Q);
|
|
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 (!oemInfo.fMouse) {
|
|
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)
|
|
{
|
|
int i;
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* 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
|
|
|
|
for (i = 0; i < QUEUE_ENTRIES; i++) {
|
|
|
|
if (QueueFreeList[i] == NULL) {
|
|
|
|
QueueFreeList[i] = pq;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
UserFreePool(pq);
|
|
}
|
|
/***************************************************************************\
|
|
* DestroyQueue
|
|
*
|
|
*
|
|
* History:
|
|
* 05-20-91 MikeHar Created.
|
|
\***************************************************************************/
|
|
|
|
void DestroyQueue(
|
|
PQ pq,
|
|
PTHREADINFO pti)
|
|
{
|
|
PTHREADINFO ptiT;
|
|
PTHREADINFO ptiAny, ptiBestMouse, ptiBestKey;
|
|
PLIST_ENTRY pHead, pEntry;
|
|
#if DBG
|
|
USHORT cDying = 0;
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
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)) {
|
|
|
|
KdPrint(("pq %lx pq->cThreads %x cDying %x pti %lx ptiK %lx ptiM %lx\n",
|
|
pq, pq->cThreads, cDying, pti, pq->ptiKeyboard, pq->ptiMouse));
|
|
UserAssert(0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Unlock any potentially locked globals now that we know absolutely
|
|
* that this queue is going away.
|
|
*/
|
|
Unlock(&pq->spwndCapture);
|
|
Unlock(&pq->spwndFocus);
|
|
Unlock(&pq->spwndActive);
|
|
Unlock(&pq->spwndActivePrev);
|
|
Unlock(&pq->spwndLastMouseMessage);
|
|
Unlock(&pq->caret.spwnd);
|
|
LockQCursor(pq, NULL);
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* 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
|
|
|
|
/*
|
|
* If an alt-tab window is left, it needs to be destroyed. Because
|
|
* this may not be the thread that owns the window, we cannot
|
|
* directly destroy the window. Post a WM_CLOSE instead. Note that
|
|
* this situation can occur during queue attachment if more than
|
|
* one alt-tab window exists.
|
|
*/
|
|
if (pq->spwndAltTab != NULL) {
|
|
|
|
PWND pwndT = pq->spwndAltTab;
|
|
|
|
if (Lock(&pq->spwndAltTab, NULL))
|
|
_PostMessage(pwndT, WM_CLOSE, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
SetFMouseMoved();
|
|
}
|
|
|
|
if (pq->cLockCount == 0) {
|
|
FreeQueue(pq);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* DeleteThreadInfo
|
|
*
|
|
* This function is called when the _ETHREAD object 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(). Furthermore, PtiCurrent() might
|
|
* not even be a w32 thread. So only kernel calls are allowed here.
|
|
*
|
|
* 04-01-96 GerardoB Created
|
|
\**************************************************************************/
|
|
VOID DeleteThreadInfo (PTHREADINFO pti)
|
|
{
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* Events
|
|
*/
|
|
if (pti->pEventQueueServer != NULL) {
|
|
ObDereferenceObject(pti->pEventQueueServer);
|
|
}
|
|
if (pti->apEvent != NULL) {
|
|
ExFreePool(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);
|
|
}
|
|
|
|
}
|
|
if (pti->pqAttach != NULL) {
|
|
|
|
UserAssert(pti->pqAttach->cLockCount);
|
|
--(pti->pqAttach->cLockCount);
|
|
|
|
if ((pti->pqAttach->cLockCount == 0)
|
|
&& (pti->pqAttach->cThreads == 0)) {
|
|
FreeQueue(pti->pqAttach);
|
|
}
|
|
|
|
}
|
|
/*
|
|
* Unlock the desktop (pti already unlinked from ptiList)
|
|
*/
|
|
if (pti->rpdesk != NULL) {
|
|
UnlockDesktop(&pti->rpdesk);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxDestroyThreadInfo
|
|
*
|
|
* Destroys a THREADINFO created by xxxCreateThreadInfo().
|
|
*
|
|
* Note that the current pti can be locked so it might be used after this
|
|
* function returns, eventhough 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 DeleteThreadInfo
|
|
*
|
|
* 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)
|
|
{
|
|
PATTACHINFO *ppai;
|
|
PTHREADINFO pti;
|
|
PTHREADINFO *ppti;
|
|
PDESKTOP rpdesk;
|
|
PWINDOWSTATION pwinsta;
|
|
PBWL pbwl, pbwlNext;
|
|
|
|
pti = PtiCurrent();
|
|
UserAssert (pti != NULL);
|
|
|
|
/*
|
|
* Make sure that this thread is not using a client desktop. This
|
|
* should never happen because this CSR request threads are the
|
|
* only ones that use this field and the death of a request thread
|
|
* is a fatal condition.
|
|
*/
|
|
#if DBG
|
|
if (pti->TIF_flags & TIF_CSRSSTHREAD)
|
|
UserAssert(pti->pdeskClient == NULL);
|
|
#endif
|
|
|
|
/*
|
|
* Don't mess with this pti anymore.
|
|
*/
|
|
pti->TIF_flags |= TIF_DONTATTACHQUEUE;
|
|
|
|
/*
|
|
* If this thread terminated abnormally and was tracking tell
|
|
* GDI to hide the trackrect.
|
|
*/
|
|
if (pti->pmsd != NULL) {
|
|
xxxCancelTrackingForThread(pti);
|
|
}
|
|
|
|
/*
|
|
* Unlock the pmsd window.
|
|
*/
|
|
if (pti->pmsd != NULL) {
|
|
Unlock(&pti->pmsd->spwnd);
|
|
UserFreePool(pti->pmsd);
|
|
pti->pmsd = NULL;
|
|
}
|
|
|
|
/*
|
|
* First do any preparation work: windows need to be "patched" so that
|
|
* their window procs point to server only windowprocs, for example.
|
|
*/
|
|
PatchThreadWindows(pti);
|
|
|
|
/*
|
|
* Free the clipboard if owned by this thread
|
|
*/
|
|
pwinsta = _GetProcessWindowStation(NULL);
|
|
if (pwinsta && pwinsta->ptiClipLock == pti) {
|
|
xxxCloseClipboard(pwinsta);
|
|
}
|
|
|
|
/*
|
|
* Unlock all the objects stored in the menustate structure
|
|
*/
|
|
if (pti->pMenuState != NULL) {
|
|
PMENUSTATE pMenuState = pti->pMenuState;
|
|
PPOPUPMENU ppopupmenuRoot = pMenuState->pGlobalPopupMenu;
|
|
|
|
/*
|
|
* If menu mode was running on this thread
|
|
*/
|
|
if (pti == pMenuState->ptiMenuStateOwner) {
|
|
/*
|
|
* Close this menu.
|
|
*/
|
|
pMenuState->fInsideMenuLoop = FALSE;
|
|
xxxMNCloseHierarchy(ppopupmenuRoot, pMenuState);
|
|
MNEndMenuState(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) == pti));
|
|
}
|
|
} /* if (pti->pMenuState != NULL) */
|
|
|
|
/*
|
|
* Unlock all the objects stored in the sbstate structure.
|
|
*/
|
|
if (pti->pSBTrack) {
|
|
Unlock(&pti->pSBTrack->spwndSB);
|
|
Unlock(&pti->pSBTrack->spwndSBNotify);
|
|
Unlock(&pti->pSBTrack->spwndTrack);
|
|
UserFreePool(pti->pSBTrack);
|
|
pti->pSBTrack = NULL;
|
|
}
|
|
|
|
/*
|
|
* If this is the main input thread of this application, zero out
|
|
* that field.
|
|
*/
|
|
if (pti->ppi != NULL && pti->ppi->ptiMainThread == pti)
|
|
pti->ppi->ptiMainThread = NULL;
|
|
|
|
while (pti->psiiList != NULL) {
|
|
xxxDestroyThreadDDEObject(pti, pti->psiiList);
|
|
}
|
|
|
|
/*
|
|
* This thread might have some outstanding timers. Destroy them
|
|
*/
|
|
DestroyThreadsTimers(pti);
|
|
|
|
/*
|
|
* Free any windows hooks this thread has created.
|
|
*/
|
|
FreeThreadsWindowHooks();
|
|
|
|
/*
|
|
* Free any hwnd lists the thread was using
|
|
*/
|
|
for (pbwl = pbwlList; pbwl != NULL; ) {
|
|
pbwlNext = pbwl->pbwlNext;
|
|
if (pbwl->ptiOwner == pti) {
|
|
FreeHwndList(pbwl);
|
|
}
|
|
pbwl = pbwlNext;
|
|
}
|
|
|
|
/*
|
|
* Destroy all the public objects created by this thread.
|
|
*/
|
|
DestroyThreadsHotKeys();
|
|
|
|
DestroyThreadsObjects();
|
|
|
|
/*
|
|
* Check if this is the last GUI thread in the process.
|
|
*/
|
|
|
|
if ((pti->ppi) && (pti->ppi->ptiList == pti) && (pti->ptiSibling == NULL)) {
|
|
DestroyProcessesObjects(pti->ppi);
|
|
}
|
|
|
|
Unlock(&pti->spklActive);
|
|
|
|
#ifdef FE_IME
|
|
/*
|
|
* Unlock default input context.
|
|
*/
|
|
Unlock(&pti->spDefaultImc);
|
|
#endif
|
|
|
|
if (pti->pq != NULL) {
|
|
/*
|
|
* Remove this thread's cursor count from the queue.
|
|
*/
|
|
pti->pq->iCursorLevel -= pti->iCursorLevel;
|
|
|
|
/*
|
|
* Have to recalc queue ownership after this thread
|
|
* leaves if it is a member of a shared input queue.
|
|
*/
|
|
if (pti->pq->cThreads != 1) {
|
|
gpdeskRecalcQueueAttach = pti->rpdesk;
|
|
SetFMouseMoved();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remove from the process' list, also.
|
|
*/
|
|
ppti = &PpiCurrent()->ptiList;
|
|
if (*ppti != NULL) {
|
|
while (*ppti != pti && (*ppti)->ptiSibling != NULL) {
|
|
ppti = &((*ppti)->ptiSibling);
|
|
}
|
|
if (*ppti == pti) {
|
|
*ppti = pti->ptiSibling;
|
|
pti->ptiSibling = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Temporarily lock the desktop until the THREADINFO structure is
|
|
* freed. Note that locking a NULL pti->rpdesk is OK. Use a
|
|
* normal lock instead of a thread lock because the lock must
|
|
* exist past the freeing of the pti.
|
|
*/
|
|
rpdesk = NULL;
|
|
LockDesktop(&rpdesk, pti->rpdesk);
|
|
|
|
/*
|
|
* Cleanup SMS structures attached to this thread. Handles both
|
|
* pending send and receive messages. MUST make sure we do SendMsgCleanup
|
|
* AFTER window cleanup.
|
|
*/
|
|
SendMsgCleanup(pti);
|
|
|
|
|
|
/*
|
|
* Allow this thread to be swapped
|
|
*/
|
|
if (pti->cEnterCount) {
|
|
BOOLEAN bool;
|
|
|
|
RIPMSG1(RIP_WARNING, "Thread exiting with stack locked. pti:%lX\n", pti);
|
|
bool = KeSetKernelStackSwapEnable(TRUE);
|
|
pti->cEnterCount = 0;
|
|
UserAssert(!bool);
|
|
}
|
|
|
|
pti->ppi->cThreads--;
|
|
UserAssert(pti->ppi->cThreads >= 0);
|
|
|
|
/*
|
|
* If this thread is a win16 task, remove it from the scheduler.
|
|
*/
|
|
if (pti->TIF_flags & TIF_16BIT) {
|
|
DestroyTask(pti->ppi, pti);
|
|
|
|
if ((pti->ptdb) && (pti->ptdb->hTaskWow != 0))
|
|
_WOWCleanup(NULL, pti->ptdb->hTaskWow, NULL, 0);
|
|
}
|
|
|
|
if (pti->hEventQueueClient != NULL) {
|
|
ProtectHandle(pti->hEventQueueClient, FALSE);
|
|
ZwClose(pti->hEventQueueClient);
|
|
pti->hEventQueueClient = NULL;
|
|
}
|
|
|
|
|
|
if (gspwndInternalCapture != NULL) {
|
|
if (GETPTI(gspwndInternalCapture) == pti) {
|
|
Unlock(&gspwndInternalCapture);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set gptiForeground to NULL if equal to this pti before exiting
|
|
* this routine.
|
|
*/
|
|
if (gptiForeground == pti) {
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
if (rpdesk->pDeskInfo->spwndProgman)
|
|
_PostMessage(rpdesk->pDeskInfo->spwndProgman,guiActivateShellWindow, 0, 0);
|
|
|
|
/*
|
|
* Set gptiForeground to NULL because we're destroying it.
|
|
* Since gpqForeground is derived from the foreground thread
|
|
* structure, set it to NULL as well, since there now is no
|
|
* foreground thread structure.
|
|
*/
|
|
gptiForeground = NULL;
|
|
gpqForeground = NULL;
|
|
}
|
|
|
|
/*
|
|
* Make sure none of the other global thread pointers are pointing to us.
|
|
*/
|
|
if (gptiShutdownNotify == pti) {
|
|
gptiShutdownNotify = NULL;
|
|
}
|
|
if (gptiTasklist == pti) {
|
|
gptiTasklist = NULL;
|
|
}
|
|
if (gHardErrorHandler.pti == pti) {
|
|
gHardErrorHandler.pti = NULL;
|
|
}
|
|
|
|
if (pti->TIF_flags & TIF_PALETTEAWARE)
|
|
xxxFlushPalette(pti->rpdesk->pDeskInfo->spwnd);
|
|
|
|
/*
|
|
* May 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 DeleteThreadInfo
|
|
*/
|
|
if (pti->pq != NULL) {
|
|
DestroyThreadsMessages(pti->pq, pti);
|
|
(pti->pq->cLockCount)++;
|
|
DestroyQueue(pti->pq, pti);
|
|
}
|
|
|
|
if (pti->pqAttach != NULL) {
|
|
DestroyThreadsMessages(pti->pqAttach, pti);
|
|
(pti->pqAttach->cLockCount)++;
|
|
DestroyQueue(pti->pqAttach, pti);
|
|
}
|
|
|
|
/*
|
|
* Remove the pti from its pti list and reset the pointers.
|
|
*/
|
|
if (pti->rpdesk != NULL) {
|
|
RemoveEntryList(&pti->PtiLink);
|
|
InitializeListHead(&pti->PtiLink);
|
|
}
|
|
|
|
FreeMessageList(&pti->mlPost);
|
|
|
|
/*
|
|
* Free any attachinfo structures pointing to this thread
|
|
*/
|
|
ppai = &gpai;
|
|
while ((*ppai) != NULL) {
|
|
if ((*ppai)->pti1 == pti || (*ppai)->pti2 == pti) {
|
|
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(pti);
|
|
|
|
/*
|
|
* Free thread information visible from client
|
|
*/
|
|
if (pti->pcti != NULL && pti->pcti != &(pti->cti)) {
|
|
DesktopFree(rpdesk->hheapDesktop, pti->pcti);
|
|
pti->pcti = &(pti->cti);
|
|
}
|
|
|
|
/*
|
|
* Free the client info for system threads
|
|
*/
|
|
if (pti->TIF_flags & TIF_SYSTEMTHREAD && pti->pClientInfo != NULL) {
|
|
UserFreePool(pti->pClientInfo);
|
|
pti->pClientInfo = NULL;
|
|
}
|
|
|
|
/*
|
|
* Unlock the temporary desktop lock. pti->rpdesk is still locked
|
|
* and will be unlocked in DeleteThreadInfo.
|
|
*/
|
|
UnlockDesktop(&rpdesk);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* 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;
|
|
void FreeKeyState(PBYTE pKeyState);
|
|
|
|
/*
|
|
* 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:
|
|
FreeKeyState((PBYTE)pqmsg->msg.wParam);
|
|
break;
|
|
|
|
case QEVENT_ASYNCSENDMSG:
|
|
pmsg = (PASYNCSENDMSG)pqmsg->msg.wParam;
|
|
DeleteAtom((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 == (DWORD)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()
|
|
{
|
|
ULONG BlockSize;
|
|
ULONG InitialSegmentSize;
|
|
|
|
BlockSize = (sizeof(QMSG) + 7) & ~7;
|
|
InitialSegmentSize = 16 * BlockSize + sizeof(ZONE_SEGMENT_HEADER);
|
|
|
|
QEntryLookasideBase = UserAllocPool(InitialSegmentSize, TAG_LOOKASIDE);
|
|
|
|
if ( !QEntryLookasideBase ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
QEntryLookasideBounds = (PVOID)((PUCHAR)QEntryLookasideBase + InitialSegmentSize);
|
|
|
|
return ExInitializeZone(&QEntryLookasideZone,
|
|
BlockSize,
|
|
QEntryLookasideBase,
|
|
InitialSegmentSize);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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);
|
|
|
|
/*
|
|
* Attempt to get a QMSG from the zone. If this fails, then
|
|
* LocalAlloc the QMSG
|
|
*/
|
|
pqmsg = ExAllocateFromZone(&QEntryLookasideZone);
|
|
|
|
if ( !pqmsg ) {
|
|
/*
|
|
* Allocate a Q message structure.
|
|
*/
|
|
#if DBG
|
|
AllocQEntrySlowCalls++;
|
|
#endif // DBG
|
|
if ((pqmsg = (PQMSG)UserAllocPool(sizeof(QMSG), TAG_QMSG)) == NULL)
|
|
return NULL;
|
|
}
|
|
RtlZeroMemory(pqmsg, sizeof(*pqmsg));
|
|
#if DBG
|
|
AllocQEntryCalls++;
|
|
|
|
if (AllocQEntryCalls-DelQEntryCalls > AllocQEntryHiWater ) {
|
|
AllocQEntryHiWater = AllocQEntryCalls-DelQEntryCalls;
|
|
}
|
|
#endif // DBG
|
|
|
|
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);
|
|
|
|
#if DBG
|
|
DelQEntryCalls++;
|
|
#endif // DBG
|
|
|
|
/*
|
|
* 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--;
|
|
|
|
//
|
|
// If the pqmsg was from zone, then free to zone
|
|
//
|
|
if ( (PVOID)pqmsg >= QEntryLookasideBase && (PVOID)pqmsg < QEntryLookasideBounds ) {
|
|
ExFreeToZone(&QEntryLookasideZone, pqmsg);
|
|
} else {
|
|
#if DBG
|
|
DelQEntrySlowCalls++;
|
|
#endif // DBG
|
|
UserFreePool((HLOCAL)pqmsg);
|
|
}
|
|
|
|
DebugValidateMLIST(pml);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FreeQEntry
|
|
*
|
|
* Returns a qmsg to the lookaside buffer or free the memory.
|
|
*
|
|
* 10-26-93 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
void FreeQEntry(
|
|
PQMSG pqmsg)
|
|
{
|
|
#if DBG
|
|
DelQEntryCalls++;
|
|
#endif // DBG
|
|
|
|
/*
|
|
* If the pqmsg was from zone, then free to zone
|
|
*/
|
|
if ( (PVOID)pqmsg >= QEntryLookasideBase && (PVOID)pqmsg < QEntryLookasideBounds ) {
|
|
ExFreeToZone(&QEntryLookasideZone, pqmsg);
|
|
} else {
|
|
#if DBG
|
|
DelQEntrySlowCalls++;
|
|
#endif // DBG
|
|
UserFreePool((HLOCAL)pqmsg);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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.
|
|
\***************************************************************************/
|
|
|
|
PQMSG FindQMsg(
|
|
PTHREADINFO pti,
|
|
PMLIST pml,
|
|
PWND pwndFilter,
|
|
UINT msgMin,
|
|
UINT msgMax)
|
|
{
|
|
PWND pwnd;
|
|
PQMSG pqmsgRead;
|
|
UINT message;
|
|
|
|
DebugValidateMLIST(pml);
|
|
|
|
StartScan:
|
|
for (pqmsgRead = pml->pqmsgRead; pqmsgRead != NULL;
|
|
pqmsgRead = pqmsgRead->pqmsgNext) {
|
|
|
|
/*
|
|
* 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).
|
|
*/
|
|
if ((pwnd = RevalidateHwnd(pqmsgRead->msg.hwnd)) != NULL) {
|
|
if (HMIsMarkDestroy(pwnd))
|
|
pwnd = NULL;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
DelQEntry(pml, pqmsgRead);
|
|
goto StartScan;
|
|
}
|
|
|
|
/*
|
|
* Make sure this message fits both window handle and message
|
|
* filters.
|
|
*/
|
|
if (!CheckPwndFilter(pwnd, pwndFilter))
|
|
continue;
|
|
|
|
/*
|
|
* 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))
|
|
continue;
|
|
|
|
/*
|
|
* Found it.
|
|
*/
|
|
DebugValidateMLIST(pml);
|
|
return pqmsgRead;
|
|
}
|
|
|
|
DebugValidateMLIST(pml);
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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 cQuit is !=
|
|
* 0, then generate a quit!
|
|
*/
|
|
if (pti->cQuit != 0 && pti->mlPost.cMsgs == 0) {
|
|
/*
|
|
* If we're "removing" the quit, set cQuit to 0 so another one isn't
|
|
* generated.
|
|
*/
|
|
if (fRemoveMsg)
|
|
pti->cQuit = 0;
|
|
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. This routine is used for the POST MESSAGE list only!!
|
|
*
|
|
* 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);
|
|
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;
|
|
pti->ptLast = pqmsg->msg.pt;
|
|
pti->idLast = (DWORD)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)
|
|
CheckProcessForeground(pti);
|
|
|
|
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)) {
|
|
pqmsg->msg = *lpMsg;
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
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->cQuit == 0) {
|
|
pti->pcti->fsWakeBits &= ~(QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
|
|
pti->pcti->fsChangeBits &= ~QS_ALLPOSTMESSAGE;
|
|
}
|
|
|
|
return pqmsg != NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxProcessEvent
|
|
*
|
|
* 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 pti,
|
|
PQMSG pqmsg)
|
|
{
|
|
PWND pwnd;
|
|
TL tlpwndT;
|
|
PQ pq;
|
|
|
|
pq = pti->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(pti, 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) {
|
|
ThreadLockAlwaysWithPti(pti, pwnd, &tlpwndT);
|
|
xxxShowWindow(pwnd, pqmsg->msg.lParam);
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
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);
|
|
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.
|
|
*/
|
|
_ClipCursor(NULL);
|
|
xxxLockWindowUpdate2(NULL, TRUE);
|
|
|
|
/*
|
|
* Reload pq because it may have changed.
|
|
*/
|
|
pq = pti->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(pti, pwndActive = pq->spwndActive, &tlpwndT);
|
|
xxxSendMessage(pwndActive, WM_NCACTIVATE, TRUE, 0);
|
|
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.
|
|
*/
|
|
pti->TIF_flags &= ~TIF_ALLOWFOREGROUNDACTIVATE;
|
|
pti->ppi->W32PF_Flags &= ~W32PF_ALLOWFOREGROUNDACTIVATE;
|
|
}
|
|
|
|
} else {
|
|
|
|
pwnd = RevalidateHwnd((HWND)pqmsg->msg.lParam);
|
|
if (pwnd == NULL || HMIsMarkDestroy(pwnd))
|
|
break;
|
|
|
|
ThreadLockAlwaysWithPti(pti, pwnd, &tlpwndT);
|
|
|
|
/*
|
|
* If nobody is foreground, allow this app to become foreground.
|
|
*/
|
|
if (gpqForeground == NULL) {
|
|
xxxSetForegroundWindow2(pwnd, pti, 0);
|
|
} else {
|
|
if (pwnd != pq->spwndActive) {
|
|
xxxActivateThisWindow(pwnd, pqmsg->msg.wParam,
|
|
(ATW_SETFOCUS | ATW_ASYNC) |
|
|
((pqmsg->msg.message & PEM_ACTIVATE_NOZORDER) ? ATW_NOZORDER : 0));
|
|
} else {
|
|
BOOL fActive = (GETPTI(pwnd)->pq == gpqForeground);
|
|
|
|
xxxSendMessage(pwnd, WM_NCACTIVATE,
|
|
(DWORD)(fActive), 0);
|
|
|
|
/*
|
|
* 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(pti, (DWORD)pqmsg->msg.wParam);
|
|
break;
|
|
|
|
case QEVENT_CANCELMODE:
|
|
if (pq->spwndCapture != NULL) {
|
|
ThreadLockAlwaysWithPti(pti, 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(pti, 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;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _GetInputState (API)
|
|
*
|
|
* Returns the current input state for mouse buttons or keys.
|
|
*
|
|
* History:
|
|
* 11-06-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
#define QS_TEST_AND_CLEAR (QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_SENDMESSAGE)
|
|
#define QS_TEST (QS_MOUSEBUTTON | QS_KEY)
|
|
|
|
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 pti;
|
|
UINT fsChangeBits;
|
|
|
|
pti = PtiCurrentShared();
|
|
|
|
flags &= (QS_ALLINPUT | QS_ALLPOSTMESSAGE | QS_TRANSFER);
|
|
|
|
fsChangeBits = pti->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().
|
|
*/
|
|
pti->pcti->fsChangeBits &= ~flags;
|
|
|
|
/*
|
|
* Return the current change/wake-bits.
|
|
*/
|
|
return MAKELONG(fsChangeBits & flags,
|
|
(pti->pcti->fsWakeBits | pti->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,
|
|
BOOL fWaitAll,
|
|
DWORD dwMilliseconds,
|
|
DWORD dwWakeMask,
|
|
MSGWAITCALLBACK pfnNonMsg)
|
|
{
|
|
PTHREADINFO pti;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER li;
|
|
|
|
pti = PtiCurrent();
|
|
|
|
/*
|
|
* Setup the wake mask for this thread. Wait for QS_EVENT or the app won't
|
|
* get event messages like deactivate.
|
|
*/
|
|
pti->pcti->fsWakeMask = (UINT)dwWakeMask | QS_EVENT;
|
|
|
|
/*
|
|
* Stuff the event handle for the current queue at the end.
|
|
*/
|
|
apObjects[nCount] = pti->pEventQueueServer;
|
|
KeClearEvent(pti->pEventQueueServer);
|
|
|
|
/*
|
|
* 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, dwMilliseconds);
|
|
|
|
/*
|
|
* Check to see if any input came inbetween when we
|
|
* last checked and the NtClearEvent() call.
|
|
*/
|
|
if (!(pti->pcti->fsChangeBits & (UINT)dwWakeMask)) {
|
|
|
|
/*
|
|
* This app is going idle. Clear the spin count check to see
|
|
* if we need to make this process foreground again.
|
|
*/
|
|
if (pti->TIF_flags & TIF_SPINNING) {
|
|
CheckProcessForeground(pti);
|
|
}
|
|
pti->pClientInfo->cSpins = 0;
|
|
|
|
if (pti == gptiForeground &&
|
|
IsHooked(pti, WHF_FOREGROUNDIDLE)) {
|
|
xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
|
|
}
|
|
|
|
CheckForClientDeath();
|
|
|
|
/*
|
|
* Set the input idle event to wake up any threads waiting
|
|
* for this thread to go into idle state.
|
|
*/
|
|
WakeInputIdle(pti);
|
|
|
|
/*
|
|
* Wow Tasks MUST be descheduled while in the wait to allow
|
|
* other tasks in the same wow scheduler to run.
|
|
*/
|
|
if (pti->TIF_flags & TIF_16BIT) {
|
|
xxxSleepTask(FALSE, HEVENT_REMOVEME);
|
|
// caution: the wow task is no longer scheduled.
|
|
}
|
|
|
|
LeaveCrit();
|
|
|
|
Again:
|
|
Status = KeWaitForMultipleObjects(nCount + 1, apObjects,
|
|
fWaitAll ? WaitAll : WaitAny, WrUserRequest,
|
|
UserMode, FALSE,
|
|
(dwMilliseconds == INFINITE ? NULL : &li), NULL);
|
|
|
|
CheckForClientDeath();
|
|
|
|
UserAssert(NT_SUCCESS(Status));
|
|
|
|
if ((Status == STATUS_WAIT_0) &&
|
|
(pfnNonMsg != NULL)) {
|
|
/*
|
|
* Call pfnNonMsg for the first event
|
|
*/
|
|
pfnNonMsg();
|
|
goto Again;
|
|
}
|
|
|
|
EnterCrit();
|
|
if (Status == (NTSTATUS)(STATUS_WAIT_0 + nCount)) {
|
|
|
|
/*
|
|
* WOW tasks, wait to be rescheduled
|
|
*/
|
|
if (pti->TIF_flags & TIF_16BIT) {
|
|
xxxDirectedYield(DY_OLDYIELD);
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset the input idle event to block and threads waiting
|
|
* for this thread to go into idle state.
|
|
*/
|
|
SleepInputIdle(pti);
|
|
}
|
|
} else {
|
|
Status = nCount;
|
|
}
|
|
|
|
/*
|
|
* Clear fsWakeMask since we're no longer waiting on the queue.
|
|
*/
|
|
pti->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 pti;
|
|
LARGE_INTEGER li, *pli;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
BOOL fExclusive = fsWakeMask & QS_EXCLUSIVE;
|
|
|
|
if (fExclusive) {
|
|
/*
|
|
* the exclusive bit is a 'dummy' arg, turn it off to
|
|
* avoid any possible conflictions
|
|
*/
|
|
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();
|
|
|
|
pti = PtiCurrent();
|
|
|
|
while (TRUE) {
|
|
|
|
/*
|
|
* First check if the input has arrived.
|
|
*/
|
|
if (pti->pcti->fsChangeBits & fsWakeMask) {
|
|
/*
|
|
* Clear fsWakeMask since we're no longer waiting on the queue.
|
|
*/
|
|
pti->pcti->fsWakeMask = 0;
|
|
|
|
/*
|
|
* 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(pti);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Next check for SendMessages
|
|
*/
|
|
if (!fExclusive && pti->pcti->fsWakeBits & QS_SENDMESSAGE) {
|
|
xxxReceiveMessages(pti);
|
|
|
|
/*
|
|
* Restore the change bits we took out in PeekMessage()
|
|
*/
|
|
pti->pcti->fsChangeBits |= (pti->pcti->fsWakeBits & pti->fsChangeBitsRemoved);
|
|
pti->fsChangeBitsRemoved = 0;
|
|
}
|
|
|
|
/*
|
|
* Check to see if some resources need expunging.
|
|
*/
|
|
if (pti->ppi->cSysExpunge != gcSysExpunge) {
|
|
pti->ppi->cSysExpunge = gcSysExpunge;
|
|
if (pti->ppi->dwhmodLibLoadedMask & gdwSysExpungeMask)
|
|
xxxDoSysExpunge(pti);
|
|
}
|
|
|
|
/*
|
|
* OR QS_SENDMESSAGE in since ReceiveMessage() will end up
|
|
* trashing pq->fsWakeMask. Do the same for QS_SYSEXPUNGE.
|
|
*/
|
|
if (!fExclusive) {
|
|
pti->pcti->fsWakeMask = fsWakeMask | (UINT)QS_SENDMESSAGE;
|
|
} else {
|
|
pti->pcti->fsWakeMask = fsWakeMask;
|
|
}
|
|
|
|
/*
|
|
* If we have timed out then return our error to the caller.
|
|
*/
|
|
if (status == STATUS_TIMEOUT) {
|
|
RIPERR1(ERROR_TIMEOUT, RIP_WARNING, "SleepThread: The timeout has expired %lX", Timeout);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* 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();
|
|
return FALSE;
|
|
}
|
|
|
|
UserAssert(status == STATUS_SUCCESS);
|
|
|
|
KeClearEvent(pti->pEventQueueServer);
|
|
|
|
/*
|
|
* 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 (!(pti->pcti->fsChangeBits & pti->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 (pti->TIF_flags & TIF_SPINNING) {
|
|
CheckProcessForeground(pti);
|
|
}
|
|
pti->pClientInfo->cSpins = 0;
|
|
}
|
|
|
|
|
|
if (!(pti->TIF_flags & TIF_16BIT)) {
|
|
if (fInputIdle && pti == gptiForeground &&
|
|
IsHooked(pti, WHF_FOREGROUNDIDLE)) {
|
|
xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
|
|
}
|
|
|
|
CheckForClientDeath();
|
|
|
|
/*
|
|
* Set the input idle event to wake up any threads waiting
|
|
* for this thread to go into idle state.
|
|
*/
|
|
if (fInputIdle)
|
|
WakeInputIdle(pti);
|
|
|
|
xxxSleepTask(fInputIdle, NULL);
|
|
|
|
LeaveCrit();
|
|
status = KeWaitForSingleObject(pti->pEventQueueServer,
|
|
WrUserRequest, UserMode, FALSE, pli);
|
|
CheckForClientDeath();
|
|
EnterCrit();
|
|
|
|
/*
|
|
* Reset the input idle event to block and threads waiting
|
|
* for this thread to go into idle state.
|
|
*/
|
|
SleepInputIdle(pti);
|
|
|
|
/*
|
|
* pti is 16bit!
|
|
*/
|
|
} else {
|
|
if (fInputIdle)
|
|
WakeInputIdle(pti);
|
|
|
|
xxxSleepTask(fInputIdle, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* SetWakeBit
|
|
*
|
|
* Adds the specified wake bit to specified THREADINFO and wakes its
|
|
* thread up if the bit is in its fsWakeMask.
|
|
*
|
|
* History:
|
|
* 10-28-90 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
VOID SetWakeBit(
|
|
PTHREADINFO pti,
|
|
UINT wWakeBit)
|
|
{
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* TEMPORARY check for NULL pti introduced 7/27/95 - ianja
|
|
*/
|
|
if (pti == NULL) {
|
|
RIPMSG1(RIP_ERROR, "SetWakeBit(NULL, %lx)\n", wWakeBit);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Win3.1 changes ptiKeyboard and ptiMouse accordingly if we're setting
|
|
* those bits.
|
|
*/
|
|
if (wWakeBit & QS_MOUSE)
|
|
pti->pq->ptiMouse = pti;
|
|
|
|
if (wWakeBit & QS_KEY)
|
|
pti->pq->ptiKeyboard = pti;
|
|
|
|
/*
|
|
* 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 (pti->TIF_flags & TIF_SCREENSAVER)
|
|
ScreenSaverCheck(pti);
|
|
|
|
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.
|
|
*/
|
|
fsMask = CalcWakeMask(message, message) & (QS_MOUSE | QS_KEY);
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* Only clear the wake bits, not the change bits as well!
|
|
*/
|
|
pti->pcti->fsWakeBits &= ~wWakeBit;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* PqFromThreadId
|
|
*
|
|
* 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((HANDLE)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 (pti->Thread->Cid.UniqueThread != (HANDLE)dwThreadId) {
|
|
pti = NULL;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
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,
|
|
DWORD wParam,
|
|
LONG 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 = ptCursor;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* StoreQMessage
|
|
*
|
|
*
|
|
* History:
|
|
* 02-27-91 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
void StoreQMessage(
|
|
PQMSG pqmsg,
|
|
PWND pwnd,
|
|
UINT message,
|
|
DWORD wParam,
|
|
LONG lParam,
|
|
DWORD dwQEvent,
|
|
DWORD dwExtraInfo)
|
|
{
|
|
CheckCritIn();
|
|
|
|
pqmsg->msg.hwnd = HW(pwnd);
|
|
pqmsg->msg.message = message;
|
|
pqmsg->msg.wParam = wParam;
|
|
pqmsg->msg.lParam = lParam;
|
|
pqmsg->msg.time = NtGetTickCount();
|
|
pqmsg->msg.pt = ptCursor;
|
|
pqmsg->dwQEvent = dwQEvent;
|
|
pqmsg->ExtraInfo = dwExtraInfo;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* InitProcessInfo
|
|
*
|
|
* 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.
|
|
\***************************************************************************/
|
|
|
|
BOOL InitProcessInfo(
|
|
PW32PROCESS pwp)
|
|
{
|
|
PPROCESSINFO ppi = (PPROCESSINFO)pwp;
|
|
NTSTATUS Status;
|
|
|
|
CheckCritIn();
|
|
|
|
/*
|
|
* If initialization has already been done, leave.
|
|
*/
|
|
if (ppi->W32PF_Flags & W32PF_PROCESSCONNECTED)
|
|
return TRUE;
|
|
ppi->W32PF_Flags |= W32PF_PROCESSCONNECTED;
|
|
|
|
/*
|
|
* Mark this app as "starting" - it will be starting until its first
|
|
* window activates.
|
|
*/
|
|
xxxSetProcessInitState(pwp->Process, STARTF_FORCEOFFFEEDBACK);
|
|
ppi->W32PF_Flags |= W32PF_APPSTARTING;
|
|
ppi->ppiNext = gppiStarting;
|
|
gppiStarting = ppi;
|
|
|
|
/*
|
|
* Allow this process to come to the foreground when it does its
|
|
* first activation.
|
|
*/
|
|
if (gfAllowForegroundActivate)
|
|
ppi->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* 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.
|
|
*
|
|
* 04/08/96 GerardoB Added header
|
|
\***************************************************************************/
|
|
BOOL DestroyProcessInfo(
|
|
PW32PROCESS pwp)
|
|
{
|
|
PPROCESSINFO ppi = (PPROCESSINFO)pwp;
|
|
PDESKTOPVIEW pdv, pdvNext;
|
|
BOOL fHadThreads;
|
|
|
|
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.
|
|
*/
|
|
if (pwp->W32PF_Flags & W32PF_STARTGLASS) {
|
|
pwp->W32PF_Flags &= ~W32PF_STARTGLASS;
|
|
CalcStartCursorHide(NULL, 0);
|
|
}
|
|
|
|
/*
|
|
* If the process never called Win32k, we're done.
|
|
*/
|
|
if (!(pwp->W32PF_Flags & W32PF_PROCESSCONNECTED)) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* 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 (ppi->rpdeskStartup != 0 &&
|
|
((ULONG)ppi->Process->UniqueProcessId == ppi->rpdeskStartup->pDeskInfo->idShellProcess)) {
|
|
|
|
PWND spwndLogonNotify = ppi->rpwinsta->spwndLogonNotify;
|
|
|
|
/*
|
|
* The shell process will get killed and it's better to set this
|
|
* in the desktop info.
|
|
*/
|
|
ppi->rpdeskStartup->pDeskInfo->idShellProcess = 0;
|
|
|
|
/*
|
|
* If we're not logging off, notify winlogon
|
|
*/
|
|
if ((spwndLogonNotify != NULL)
|
|
&& !(ppi->rpwinsta->dwFlags & WSF_OPENLOCK)) {
|
|
|
|
PTHREADINFO pti = GETPTI(spwndLogonNotify);
|
|
PQMSG pqmsg;
|
|
|
|
if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
|
|
StoreQMessage(pqmsg, spwndLogonNotify, WM_LOGONNOTIFY,
|
|
LOGON_RESTARTSHELL, ppi->Process->ExitStatus, 0, 0);
|
|
SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ppi->cThreads)
|
|
RIPMSG1(RIP_ERROR, "Disconnect with %d threads remaining\n", ppi->cThreads);
|
|
|
|
/*
|
|
* If the app is still starting, remove it from the startup list
|
|
*/
|
|
if (ppi->W32PF_Flags & W32PF_APPSTARTING) {
|
|
PPROCESSINFO *pppi;
|
|
|
|
for (pppi = &gppiStarting; pppi != NULL; pppi = &(*pppi)->ppiNext) {
|
|
if (*pppi == ppi) {
|
|
*pppi = ppi->ppiNext;
|
|
break;
|
|
}
|
|
}
|
|
ppi->W32PF_Flags &= ~W32PF_APPSTARTING;
|
|
}
|
|
|
|
/*
|
|
* If any threads ever connected, there may be DCs, classes,
|
|
* cursors, etc. still lying around. If not 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 we get here and pti is NULL, that means there never were
|
|
* any threads with THREADINFOs, or else this process structure
|
|
* would be gone already! If there never where threads with
|
|
* THREADINFOs, then we don't need to delete all these gui
|
|
* objects below... side affect of this is that when
|
|
* calling these routines, PtiCurrent() will always work (will
|
|
* return != NULL).
|
|
*/
|
|
DestroyProcessesClasses(ppi);
|
|
#if DBG
|
|
{
|
|
PHE pheT, pheMax;
|
|
extern CONST BYTE gabObjectCreateFlags[];
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
if ((gabObjectCreateFlags[pheT->bType] & OCF_PROCESSOWNED) &&
|
|
(PPROCESSINFO)pheT->pOwner == ppi) {
|
|
UserAssert(FALSE);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
UnlockWinSta(&ppi->rpwinsta);
|
|
UnlockDesktop(&ppi->rpdeskStartup);
|
|
|
|
/*
|
|
* 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;
|
|
PWOWPROCESSINFO *ppwpi;
|
|
|
|
ObDereferenceObject(pwpi->pEventWowExec);
|
|
|
|
for (ppwpi = &gpwpiFirstWow; *ppwpi != NULL; ppwpi = &((*ppwpi)->pwpiNext)) {
|
|
if (*ppwpi == pwpi) {
|
|
*ppwpi = pwpi->pwpiNext;
|
|
break;
|
|
}
|
|
}
|
|
|
|
UserFreePool(pwpi);
|
|
ppi->pwpi = NULL;
|
|
}
|
|
|
|
/*
|
|
* Delete desktop hook attribute bitmap
|
|
*/
|
|
if (ppi->bmDesktopHookFlags.Buffer)
|
|
UserFreePool(ppi->bmDesktopHookFlags.Buffer);
|
|
|
|
/*
|
|
* Delete desktop views. System will do unmapping.
|
|
*/
|
|
pdv = ppi->pdvList;
|
|
while (pdv) {
|
|
pdvNext = pdv->pdvNext;
|
|
UserFreePool(pdv);
|
|
pdv = pdvNext;
|
|
}
|
|
|
|
return fHadThreads;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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 pti;
|
|
|
|
pti = PtiCurrent();
|
|
|
|
/*
|
|
* Refresh the client side event handle (in case the TEB was trashed)
|
|
*/
|
|
pti->pClientInfo->hEventQueueClient = pti->hEventQueueClient;
|
|
|
|
/*
|
|
* If the wake mask is already satisfied, return -1 to signify
|
|
* there's no need to wait.
|
|
*/
|
|
if (pti->pcti->fsChangeBits & (UINT)dwWakeMask) {
|
|
return (HANDLE)-1;
|
|
}
|
|
|
|
KeClearEvent(pti->pEventQueueServer);
|
|
|
|
/*
|
|
* If an idle hook is set, call it.
|
|
*/
|
|
if (pti == gptiForeground &&
|
|
IsHooked(pti, WHF_FOREGROUNDIDLE)) {
|
|
xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
|
|
}
|
|
|
|
CheckForClientDeath();
|
|
|
|
/*
|
|
* What is the criteria for an "idle process"?
|
|
* Answer: The first thread that calls WakeInputIdle, 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)) {
|
|
pti->ppi->ptiMainThread = pti;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
WakeInputIdle(pti);
|
|
|
|
/*
|
|
* Setup the wake mask for this thread. Wait for QS_EVENT or the app won't
|
|
* get event messages like deactivate.
|
|
*/
|
|
pti->pcti->fsWakeMask = (UINT)dwWakeMask | QS_EVENT;
|
|
|
|
/*
|
|
* This app is going idle. Clear the spin count check to see
|
|
* if we need to make this process foreground again.
|
|
*/
|
|
pti->pClientInfo->cSpins = 0;
|
|
if (pti->TIF_flags & TIF_SPINNING) {
|
|
CheckProcessForeground(pti);
|
|
}
|
|
|
|
return pti->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(
|
|
DWORD 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 == (DWORD)ptiCurrent->Thread->Cid.UniqueProcess &&
|
|
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\n");
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* If here, then it is not the shared WOW process
|
|
*/
|
|
|
|
/*
|
|
* Don't wait for the system process.
|
|
*/
|
|
if (ptiCurrent->Thread->ThreadsProcess == gpepSystem)
|
|
return (DWORD)-1;
|
|
|
|
/*
|
|
* If the app is waiting for itself to go idle, error.
|
|
*/
|
|
if (ptiCurrent->Thread->Cid.UniqueProcess == (HANDLE)idProcess &&
|
|
ptiCurrent == ptiCurrent->ppi->ptiMainThread) {
|
|
RIPMSG0(RIP_WARNING, "WaitForInputIdle waiting on self\n");
|
|
return (DWORD)-1;
|
|
}
|
|
|
|
/*
|
|
* Now find the ppi structure for this process.
|
|
*/
|
|
LeaveCrit();
|
|
Status = LockProcessByClientId((HANDLE)idProcess, &Process);
|
|
EnterCrit();
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
return (DWORD)-1;
|
|
|
|
if (Process->ExitProcessCalled) {
|
|
UnlockProcess(Process);
|
|
return (DWORD)-1;
|
|
}
|
|
|
|
W32Process = (PW32PROCESS)Process->Win32Process;
|
|
|
|
/*
|
|
* Couldn't find that process info structure.... return error.
|
|
*/
|
|
if (W32Process == NULL) {
|
|
RIPMSG0(RIP_WARNING, "WaitForInputIdle process not GUI process\n");
|
|
UnlockProcess(Process);
|
|
return (DWORD)-1;
|
|
}
|
|
|
|
|
|
ppi = (PPROCESSINFO)W32Process;
|
|
|
|
/*
|
|
* If this is a console application, don't wait on it.
|
|
*/
|
|
if (W32Process->W32PF_Flags & W32PF_CONSOLEAPPLICATION) {
|
|
RIPMSG0(RIP_WARNING, "WaitForInputIdle process is console process\n");
|
|
UnlockProcess(Process);
|
|
return (DWORD)-1;
|
|
}
|
|
|
|
/*
|
|
* Wait on this event for the passed in time limit.
|
|
*/
|
|
CheckForClientDeath();
|
|
|
|
/*
|
|
* We have to wait mark the Process as one which others are waiting on
|
|
*/
|
|
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.
|
|
*/
|
|
ThreadLockObject(ptiCurrent, Process, &tlProcess);
|
|
UnlockProcess(Process);
|
|
|
|
dwResult = WaitOnPseudoEvent(&W32Process->InputIdleEvent, dwMilliseconds);
|
|
if (dwResult == STATUS_ABANDONED) {
|
|
dwResult = xxxPollAndWaitForSingleObject(W32Process->InputIdleEvent,
|
|
Process,
|
|
dwMilliseconds);
|
|
}
|
|
|
|
/*
|
|
* Check to see if the process died while we were waiting. Although
|
|
* we have the process locked, there is no guarantee that the Win32
|
|
* data structures still exist. Process->Win32Process is set to
|
|
* NULL during process cleanup.
|
|
*/
|
|
W32Process = (PW32PROCESS)Process->Win32Process;
|
|
if (W32Process != NULL) {
|
|
/*
|
|
* Clear all thread TIF_WAIT bits from the process.
|
|
*/
|
|
ppi = (PPROCESSINFO)W32Process;
|
|
ppi->W32PF_Flags &= ~W32PF_WAITFORINPUTIDLE;
|
|
for (pti = ppi->ptiList; pti != NULL; pti = pti->ptiSibling) {
|
|
pti->TIF_flags &= ~TIF_WAITFORINPUTIDLE;
|
|
}
|
|
}
|
|
|
|
ThreadUnlockObject(ptiCurrent);
|
|
|
|
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;
|
|
PTHREADINFO ptiCurrent;
|
|
UINT cEvent = 2;
|
|
NTSTATUS Status = -1;
|
|
LARGE_INTEGER li;
|
|
TL tlEvent;
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
if (ptiCurrent->apEvent == NULL) {
|
|
ptiCurrent->apEvent = ExAllocatePoolWithTag(NonPagedPool,
|
|
POLL_EVENT_CNT * sizeof(PKEVENT), TAG_THREADINFO);
|
|
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(ptiCurrent, 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++;
|
|
}
|
|
|
|
KeClearEvent(ptiCurrent->pEventQueueServer);
|
|
ptiCurrent->pcti->fsWakeMask = 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.
|
|
}
|
|
|
|
while (TRUE) {
|
|
if (dwMilliseconds > INTERMEDIATE_TIMEOUT) {
|
|
dwIntermediateMilliseconds = INTERMEDIATE_TIMEOUT;
|
|
|
|
/*
|
|
* If we are not waiting an infinite amount of time then subtract
|
|
* the intermediate wait from the total time left to wait.
|
|
*/
|
|
if (dwMilliseconds != INFINITE) {
|
|
dwMilliseconds -= INTERMEDIATE_TIMEOUT;
|
|
}
|
|
} 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(ptiCurrent); // 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, NULL,
|
|
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);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* QueryInformationThread
|
|
*
|
|
* Returns information about a thread.
|
|
*
|
|
* History:
|
|
* 03-01-95 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS QueryInformationThread(
|
|
IN HANDLE hThread,
|
|
IN USERTHREADINFOCLASS ThreadInfoClass,
|
|
OUT PVOID ThreadInformation,
|
|
IN ULONG ThreadInformationLength,
|
|
OUT PULONG ReturnLength OPTIONAL)
|
|
{
|
|
PUSERTHREAD_SHUTDOWN_INFORMATION pShutdown;
|
|
PUSERTHREAD_WOW_INFORMATION pWow;
|
|
ANSI_STRING strAppName;
|
|
UNICODE_STRING strAppNameU;
|
|
PETHREAD Thread;
|
|
PTHREADINFO pti;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG LocalReturnLength = 0;
|
|
|
|
/*
|
|
* Only allow CSRSS to make this call
|
|
*/
|
|
if (PsGetCurrentProcess() != gpepCSRSS)
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
Status = ObReferenceObjectByHandle(hThread,
|
|
THREAD_QUERY_INFORMATION,
|
|
NULL,
|
|
UserMode,
|
|
&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
try {
|
|
pti = PtiFromThread(Thread);
|
|
|
|
switch (ThreadInfoClass) {
|
|
case UserThreadShutdownInformation:
|
|
LocalReturnLength = sizeof(USERTHREAD_SHUTDOWN_INFORMATION);
|
|
if (ThreadInformationLength != sizeof(USERTHREAD_SHUTDOWN_INFORMATION)) {
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
pShutdown = ThreadInformation;
|
|
RtlZeroMemory(pShutdown, sizeof(USERTHREAD_SHUTDOWN_INFORMATION));
|
|
|
|
/*
|
|
* 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->dwFlags & 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 (Thread->Cid.UniqueProcess == 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.
|
|
*/
|
|
if ((pShutdown->dwFlags & USER_THREAD_GUI) &&
|
|
pShutdown->StatusShutdown == 0) {
|
|
xxxSwitchDesktop(pti->rpdesk->rpwinstaParent, pti->rpdesk, FALSE);
|
|
}
|
|
break;
|
|
|
|
case UserThreadFlags:
|
|
LocalReturnLength = sizeof(DWORD);
|
|
if (pti == NULL)
|
|
Status = STATUS_INVALID_HANDLE;
|
|
else if (ThreadInformationLength != sizeof(DWORD))
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
else
|
|
*(LPDWORD)ThreadInformation = pti->TIF_flags;
|
|
break;
|
|
|
|
case UserThreadTaskName:
|
|
if (pti == NULL) {
|
|
*(LPWSTR)ThreadInformation = 0;
|
|
LocalReturnLength = 0;
|
|
break;
|
|
}
|
|
if (pti->pstrAppName != NULL) {
|
|
LocalReturnLength = min(pti->pstrAppName->Length + sizeof(WCHAR),
|
|
ThreadInformationLength);
|
|
RtlCopyMemory(ThreadInformation, pti->pstrAppName->Buffer,
|
|
LocalReturnLength);
|
|
} else {
|
|
RtlInitAnsiString(&strAppName, pti->Thread->ThreadsProcess->ImageFileName);
|
|
if (ThreadInformationLength < sizeof(WCHAR))
|
|
LocalReturnLength = (strAppName.Length + 1) * sizeof(WCHAR);
|
|
else {
|
|
strAppNameU.Buffer = (PWCHAR)ThreadInformation;
|
|
strAppNameU.MaximumLength = (SHORT)ThreadInformationLength -
|
|
sizeof(WCHAR);
|
|
Status = RtlAnsiStringToUnicodeString(&strAppNameU, &strAppName,
|
|
FALSE);
|
|
if (NT_SUCCESS(Status))
|
|
LocalReturnLength = strAppNameU.Length + sizeof(WCHAR);
|
|
}
|
|
}
|
|
((LPWSTR)ThreadInformation)[(LocalReturnLength / sizeof(WCHAR)) - 1] = 0;
|
|
break;
|
|
|
|
case UserThreadWOWInformation:
|
|
LocalReturnLength = sizeof(USERTHREAD_WOW_INFORMATION);
|
|
if (ThreadInformationLength != sizeof(USERTHREAD_WOW_INFORMATION)) {
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
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 = (PVOID)pti->ppi->pwpi->lpfnWowExitTask;
|
|
if (pti->ptdb)
|
|
pWow->hTaskWow = pti->ptdb->hTaskWow;
|
|
else
|
|
pWow->hTaskWow = 0;
|
|
}
|
|
break;
|
|
|
|
case UserThreadHungStatus:
|
|
LocalReturnLength = sizeof(DWORD);
|
|
if (ThreadInformationLength < sizeof(DWORD)) {
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Return hung status.
|
|
*/
|
|
if (pti)
|
|
*(PDWORD)ThreadInformation =
|
|
(DWORD) FHungApp(pti, (DWORD)*(PDWORD)ThreadInformation);
|
|
else
|
|
*(PDWORD)ThreadInformation = FALSE;
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
break;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(ReturnLength) ) {
|
|
*ReturnLength = LocalReturnLength;
|
|
}
|
|
|
|
UnlockThread(Thread);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
UnlockThread(Thread);
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SetInformationThread
|
|
*
|
|
* Sets information about a thread.
|
|
*
|
|
* History:
|
|
* 03-01-95 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS SetInformationThread(
|
|
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;
|
|
PDESKTOP pdeskClient;
|
|
|
|
/*
|
|
* Only allow CSRSS to make this call
|
|
*/
|
|
if (PsGetCurrentProcess() != gpepCSRSS)
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
Status = ObReferenceObjectByHandle(hThread,
|
|
THREAD_SET_INFORMATION,
|
|
NULL,
|
|
UserMode,
|
|
&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
try {
|
|
pti = PtiFromThread(Thread);
|
|
|
|
switch (ThreadInfoClass) {
|
|
case UserThreadFlags:
|
|
if (pti == NULL)
|
|
Status = STATUS_INVALID_HANDLE;
|
|
else if (ThreadInformationLength != sizeof(USERTHREAD_FLAGS))
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
else {
|
|
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:
|
|
if (ThreadInformationLength != sizeof(ULONG)) {
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
Status = InitiateShutdown(Thread, (PULONG)ThreadInformation);
|
|
break;
|
|
|
|
case UserThreadEndShutdown:
|
|
if (ThreadInformationLength != sizeof(NTSTATUS)) {
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
Status = EndShutdown(Thread, *(NTSTATUS *)ThreadInformation);
|
|
break;
|
|
|
|
case UserThreadUseDesktop:
|
|
if (ThreadInformationLength != sizeof(HANDLE)) {
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
if (pti == NULL) {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
break;
|
|
}
|
|
hClientThread = *(PHANDLE)ThreadInformation;
|
|
if (hClientThread == NULL) {
|
|
_SetThreadDesktop(NULL, NULL);
|
|
pdeskClient = NULL;
|
|
} else {
|
|
Status = ObReferenceObjectByHandle(hClientThread,
|
|
THREAD_QUERY_INFORMATION,
|
|
NULL,
|
|
UserMode,
|
|
&ThreadClient,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
ptiT = PtiFromThread(ThreadClient);
|
|
|
|
if (ptiT == NULL || ptiT->rpdesk == NULL)
|
|
Status = STATUS_INVALID_HANDLE;
|
|
else {
|
|
if (_SetThreadDesktop(NULL, ptiT->rpdesk))
|
|
pdeskClient = ptiT->rpdesk;
|
|
else
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
ObDereferenceObject(ThreadClient);
|
|
}
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (pdeskClient != NULL) {
|
|
/*
|
|
* Make sure this desktop won't go away
|
|
*/
|
|
if (pti->pdeskClient == NULL) {
|
|
Status = ObReferenceObjectByPointer(pdeskClient,
|
|
MAXIMUM_ALLOWED,
|
|
*ExDesktopObjectType,
|
|
KernelMode);
|
|
if (NT_SUCCESS(Status)) {
|
|
pti->pdeskClient = pdeskClient;
|
|
pti->cDeskClient++;
|
|
}
|
|
} else if (pti->pdeskClient == pdeskClient) {
|
|
pti->cDeskClient++;
|
|
} else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
RIPMSG0(RIP_ERROR, "SetInformationThread: pti->pdeskClient != NULL");
|
|
}
|
|
} else {
|
|
/*
|
|
* Don't need to hold this desktop any longer
|
|
*/
|
|
if (pti->pdeskClient != NULL) {
|
|
UserAssert(pti->cDeskClient > 0);
|
|
if (--pti->cDeskClient == 0) {
|
|
ObDereferenceObject(pti->pdeskClient);
|
|
pti->pdeskClient = NULL;
|
|
}
|
|
} else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
RIPMSG0(RIP_ERROR, "SetInformationThread: pti->pdeskClient == NULL");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UserThreadUseActiveDesktop:
|
|
if (pti == NULL || grpdeskRitInput == NULL) {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
break;
|
|
}
|
|
if (_SetThreadDesktop(NULL, grpdeskRitInput)) {
|
|
|
|
/*
|
|
* Make sure this desktop won't go away
|
|
*/
|
|
if (pti->pdeskClient == NULL) {
|
|
Status = ObReferenceObjectByPointer(grpdeskRitInput,
|
|
MAXIMUM_ALLOWED,
|
|
*ExDesktopObjectType,
|
|
KernelMode);
|
|
if (NT_SUCCESS(Status)) {
|
|
pti->pdeskClient = grpdeskRitInput;
|
|
pti->cDeskClient++;
|
|
}
|
|
} else if (pti->pdeskClient == grpdeskRitInput) {
|
|
pti->cDeskClient++;
|
|
} else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
RIPMSG0(RIP_ERROR, "SetInformationThread: pti->pdeskClient != NULL");
|
|
}
|
|
} else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
break;
|
|
|
|
case UserThreadCsrApiPort:
|
|
|
|
/*
|
|
* Only CSR can call this
|
|
*/
|
|
if (Thread->ThreadsProcess != gpepCSRSS) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
|
|
if (ThreadInformationLength != sizeof(HANDLE))
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
else {
|
|
|
|
/*
|
|
* Only set it once.
|
|
*/
|
|
if (CsrApiPort != NULL)
|
|
break;
|
|
|
|
CsrPortHandle = *(PHANDLE)ThreadInformation;
|
|
Status = ObReferenceObjectByHandle(
|
|
CsrPortHandle,
|
|
0,
|
|
NULL,
|
|
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;
|
|
break;
|
|
}
|
|
|
|
UnlockThread(Thread);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
UnlockThread(Thread);
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ConsoleControl
|
|
*
|
|
* Performs special control operations for console.
|
|
*
|
|
* History:
|
|
* 03-01-95 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS ConsoleControl(
|
|
IN CONSOLECONTROL ConsoleControl,
|
|
IN PVOID ConsoleInformation,
|
|
IN ULONG ConsoleInformationLength)
|
|
{
|
|
PCONSOLEDESKTOPCONSOLETHREAD pDesktopConsole;
|
|
PCONSOLEWINDOWSTATIONPROCESS pConsoleWindowStationInfo;
|
|
PDESKTOP pdesk;
|
|
DWORD dwThreadIdOld;
|
|
BOOL fSuccess;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Only allow CSRSS to make this call
|
|
*/
|
|
if (PsGetCurrentProcess() != gpepCSRSS)
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
switch (ConsoleControl) {
|
|
case ConsoleDesktopConsoleThread:
|
|
if (ConsoleInformationLength != sizeof(CONSOLEDESKTOPCONSOLETHREAD))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
pDesktopConsole = (PCONSOLEDESKTOPCONSOLETHREAD)ConsoleInformation;
|
|
|
|
Status = ObReferenceObjectByHandle(
|
|
pDesktopConsole->hdesk,
|
|
0,
|
|
*ExDesktopObjectType,
|
|
UserMode,
|
|
&pdesk,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
dwThreadIdOld = pdesk->dwConsoleThreadId;
|
|
|
|
if (pDesktopConsole->dwThreadId != (DWORD)-1) {
|
|
pdesk->dwConsoleThreadId =
|
|
pDesktopConsole->dwThreadId;
|
|
}
|
|
|
|
pDesktopConsole->dwThreadId = dwThreadIdOld;
|
|
ObDereferenceObject(pdesk);
|
|
break;
|
|
|
|
case ConsoleClassAtom:
|
|
if (ConsoleInformationLength != sizeof(ATOM))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
gatomConsoleClass = *(ATOM *)ConsoleInformation;
|
|
break;
|
|
|
|
case ConsolePermanentFont:
|
|
fSuccess = (BOOL)xxxAddFontResourceW((LPWSTR)ConsoleInformation,
|
|
AFRW_ADD_LOCAL_FONT);
|
|
if (!fSuccess)
|
|
return STATUS_UNSUCCESSFUL;
|
|
break;
|
|
|
|
case ConsoleNotifyConsoleApplication:
|
|
if (ConsoleInformationLength != sizeof(DWORD))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
UserNotifyConsoleApplication(*(LPDWORD)ConsoleInformation);
|
|
break;
|
|
|
|
case ConsoleSetVDMCursorBounds:
|
|
if ((ConsoleInformation != NULL) &&
|
|
(ConsoleInformationLength != sizeof(RECT)))
|
|
{
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
SetVDMCursorBounds(ConsoleInformation);
|
|
break;
|
|
|
|
case ConsolePublicPalette:
|
|
if (ConsoleInformationLength != sizeof(HPALETTE))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
GreSetPaletteOwner(*(HPALETTE *)ConsoleInformation, OBJECT_OWNER_PUBLIC);
|
|
break;
|
|
|
|
case ConsoleWindowStationProcess:
|
|
if (ConsoleInformationLength != sizeof(CONSOLEWINDOWSTATIONPROCESS))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
pConsoleWindowStationInfo = (PCONSOLEWINDOWSTATIONPROCESS)ConsoleInformation;
|
|
UserSetConsoleProcessWindowStation(pConsoleWindowStationInfo->dwProcessId,
|
|
pConsoleWindowStationInfo->hwinsta);
|
|
break;
|
|
|
|
default:
|
|
RIPMSG0(RIP_ERROR, "ConsoleControl - invalid control class\n");
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|