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