BOOL WowExitTask( PCSR_THREAD pcsrt) { HANDLE ahandle[2]; USERTHREAD_WOW_INFORMATION WowInfo; NTSTATUS Status; ahandle[1] = heventCancel; /* * Query task id and exit function. */ LeaveCrit(); Status = NtUserQueryInformationThread(pcsrt->ThreadHandle, UserThreadWOWInformation, &WowInfo, sizeof(WowInfo), NULL); EnterCrit(); if (!NT_SUCCESS(Status)) return FALSE; /* * If no task id was returned, it is not a WOW task */ if (WowInfo.hTaskWow == 0) return FALSE; /* * The created thread needs to be able to reenter user because this * call will grab the CSR critical section and whoever has that * may need to grab the USER critical section before it can * release it. */ LeaveCrit(); /* * Try to make it exit itself. This will work most of the time. * If this doesn't work, terminate this process. */ ahandle[0] = InternalCreateCallbackThread(pcsrt->Process->ProcessHandle, (DWORD)WowInfo.lpfnWowExitTask, (DWORD)WowInfo.hTaskWow); if (ahandle[0] == NULL) { NtTerminateProcess(pcsrt->Process->ProcessHandle, 0); pcsrt->Process->Flags |= CSR_PROCESS_TERMINATED; goto Exit; } WaitForMultipleObjects(2, ahandle, FALSE, INFINITE); NtClose(ahandle[0]); Exit: EnterCrit(); return TRUE; }
int DoEndTaskDialog( WCHAR* pszTitle, HANDLE h, UINT type, int cSeconds) { int result; ENDDLGPARAMS edp; if (gfAutoEndTask) return IDABORT; edp.pszTitle = pszTitle; edp.h = h; edp.type = type; edp.cSeconds = cSeconds; LeaveCrit(); result = DialogBoxParam(hModuleWin, MAKEINTRESOURCE(IDD_ENDTASK), NULL, EndTaskDlgProc, (DWORD)(&edp)); EnterCrit(); return result; }
void PH_DumpInternalReal(CommonBuffer * common, ThreadBuffer * thread, BOOL toDisk) { // Same as before, just unrolled to prevent doing this outer level if check a bunch of times inside the loop // This is used by the Buddhabrot render while (thread->left < thread->total) { int count; int level; double a; double b; double c; TD_GET(int, count); TD_GET(int, level); TD_GET(double, a); TD_GET(double, b); TD_GET(double, c); #ifdef USE_CRIT_SECTION EnterCrit(); #endif while (count > 0) { unsigned long long xy; TD_GET(unsigned long long, xy); #ifdef USE_CRIT_SECTION common->m_levelsData[xy]++; common->m_levelsPlotReal[xy] += a; common->m_levelsPlotImaginary[xy] += b; #else for (;;) { LONGLONG levelsData = InterlockedExchangeNoFence64((LONGLONG*)&common->m_levelsData[xy], OWNED_VALUE); if (levelsData != OWNED_VALUE) { levelsData++; common->m_levelsPlotReal[xy] += a; common->m_levelsPlotImaginary[xy] += b; InterlockedExchangeNoFence64((LONGLONG*)&common->m_levelsData[xy], levelsData); break; } } #endif count--; } #ifdef USE_CRIT_SECTION LeaveCrit(); #endif } }
void PH_DumpInternalLevel(CommonBuffer * common, ThreadBuffer * thread, BOOL toDisk) { // And same as the other three if clauses, but only store level data. This is for a simple B/W // Mandelbrot render while (thread->left < thread->total) { int count; int level; double a; double b; double c; TD_GET(int, count); TD_GET(int, level); TD_GET(double, a); TD_GET(double, b); TD_GET(double, c); #ifdef USE_CRIT_SECTION EnterCrit(); #endif while (count > 0) { unsigned long long xy; TD_GET(unsigned long long, xy); #ifdef USE_CRIT_SECTION common->m_levelsData[xy]++; #else // This is different than the other clauses. No need to 'own' a // pixel here, we can just increment the value directly using // InterlockedIncrement since we won't be touching anything else InterlockedIncrementNoFence((ULONGLONG*)&common->m_levelsData[xy]); #endif count--; } #ifdef USE_CRIT_SECTION LeaveCrit(); #endif } }
int InternalDoEndTaskDialog( TCHAR* pszTitle, HANDLE h, int cSeconds) { int iRet; EnterCrit(); gcInternalDoEndTaskDialog++; try { iRet = DoEndTaskDialog(pszTitle, h, TYPE_CONSOLE_ID, cSeconds); } finally { gcInternalDoEndTaskDialog--; } LeaveCrit(); return iRet; }
VOID UserRedrawDesktop(VOID) { TL tlpwnd; PWND pwndDesk; EnterCrit(); pwndDesk = PtiCurrent()->rpdesk->pDeskInfo->spwnd; ThreadLockAlways(pwndDesk, &tlpwnd); xxxInternalInvalidate(pwndDesk, HRGN_FULL, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN); ThreadUnlock(&tlpwnd); LeaveCrit(); }
NTSTATUS UserServerDllInitialization( PCSR_SERVER_DLL psrvdll) { CLIENT_ID ClientId; DWORD cbAllocated; NTSTATUS Status; int i; /* * Initialize a critical section structure that will be used to protect * all of the User Server's critical sections (except a few special * cases like the RIT -- see below). */ RtlInitializeCriticalSection(&gcsUserSrv); EnterCrit(); // synchronize heap calls /* * Remember WINSRV.DLL's hmodule so we can grab resources from it later. */ hModuleWin = psrvdll->ModuleHandle; psrvdll->ApiNumberBase = USERK_FIRST_API_NUMBER; psrvdll->MaxApiNumber = UserpMaxApiNumber; psrvdll->ApiDispatchTable = UserServerApiDispatchTable; psrvdll->ApiServerValidTable = UserServerApiServerValidTable; #if DBG psrvdll->ApiNameTable = UserServerApiNameTable; #else psrvdll->ApiNameTable = NULL; #endif psrvdll->PerProcessDataLength = CHANDLES * sizeof(HANDLE); psrvdll->PerThreadDataLength = 0; psrvdll->ConnectRoutine = UserClientConnect; psrvdll->DisconnectRoutine = UserClientDisconnect; psrvdll->HardErrorRoutine = UserHardError; psrvdll->ShutdownProcessRoutine = UserClientShutdown; /* * Create these events used by shutdown */ NtCreateEvent(&heventCancel, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); NtCreateEvent(&heventCancelled, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE); /* * Tell the base what user address to call when it is creating a process * (but before the process starts running). */ BaseSetProcessCreateNotify(NtUserNotifyProcessCreate); /* * Set up translation tables. */ InitOemXlateTables(); /* * Get timeout values from registry */ GetTimeouts(); /* * Load some strings. */ pszaSUCCESS = (LPSTR)RtlLoadStringOrError(hModuleWin, STR_SUCCESS, NULL, &cbAllocated, TRUE); pszaSYSTEM_INFORMATION = (LPSTR)RtlLoadStringOrError(hModuleWin, STR_SYSTEM_INFORMATION, NULL, &cbAllocated, TRUE); pszaSYSTEM_WARNING = (LPSTR)RtlLoadStringOrError(hModuleWin, STR_SYSTEM_WARNING, NULL, &cbAllocated, TRUE); pszaSYSTEM_ERROR = (LPSTR)RtlLoadStringOrError(hModuleWin, STR_SYSTEM_ERROR, NULL, &cbAllocated, TRUE); /* * Add marlett font and make it permanent */ i = GdiAddFontResourceW(L"marlett.ttf", AFRW_ADD_LOCAL_FONT); /* * Initialize USER */ { HANDLE hModBase; LeaveCrit(); hModBase = GetModuleHandle(TEXT("kernel32")); EnterCrit(); UserAssert(hModBase); gpfnAttachRoutine = GetProcAddress(hModBase,"BaseAttachCompleteThunk"); UserAssert(gpfnAttachRoutine); Status = NtUserInitialize(USERCURRENTVERSION, gpfnAttachRoutine); if (!NT_SUCCESS(Status)) { goto ExitUserInit; } } /* * Start registry notification thread */ Status = RtlCreateUserThread(NtCurrentProcess(), NULL, FALSE, 0, 0, 4*0x1000, (PUSER_THREAD_START_ROUTINE)NotificationThread, NULL, &hThreadNotification, &ClientId); CsrAddStaticServerThread(hThreadNotification, &ClientId, 0); ExitUserInit: LeaveCrit(); return Status; }
BOOL xxxActivateDebugger( UINT fsModifiers) { ULONG ArgLength; USER_API_MSG m; PACTIVATEDEBUGGERMSG a = &m.u.ActivateDebugger; PEPROCESS Process; HANDLE hDebugPort; NTSTATUS Status; if (fsModifiers & MOD_CONTROL) { #ifdef DEBUG if (RipOutput(0, RIP_WARNING, "User debugger", 0, "Debug prompt", NULL)) { DbgBreakPoint(); } #endif return FALSE; } else if (fsModifiers & MOD_SHIFT) { /* * Bail out if the process is not being debugged. */ if (gpepCSRSS->DebugPort == NULL) return FALSE; a->ClientId.UniqueProcess = gpepCSRSS->UniqueProcessId; } else { if ((gpqForeground == NULL) || (gpqForeground->ptiKeyboard == NULL)) return FALSE; a->ClientId = gpqForeground->ptiKeyboard->Thread->Cid; /* * Bail out if the process is not being debugged. */ if (!NT_SUCCESS(LockProcessByClientId(a->ClientId.UniqueProcess, &Process))) return FALSE; hDebugPort = Process->DebugPort; UnlockProcess(Process); if (hDebugPort == NULL) return FALSE; } /* * Send the datagram to CSR */ if (CsrApiPort != NULL) { ArgLength = sizeof(*a); ArgLength |= (ArgLength << 16); ArgLength += ((sizeof( CSR_API_MSG ) - sizeof( m.u )) << 16) | (FIELD_OFFSET( CSR_API_MSG, u ) - sizeof( m.h )); m.h.u1.Length = ArgLength; m.h.u2.ZeroInit = 0; m.CaptureBuffer = NULL; m.ApiNumber = CSR_MAKE_API_NUMBER( USERSRV_SERVERDLL_INDEX, UserpActivateDebugger); LeaveCrit(); Status = LpcRequestPort(CsrApiPort, (PPORT_MESSAGE)&m); EnterCrit(); UserAssert(NT_SUCCESS(Status)); } /* * Don't eat this event unless we are breaking into CSR! Since we have * choosen an arbitrary hot key like F12 for the debug key, we need to * pass on the key to the application, or apps that want this key would * never see it. If we had an api for installing a debug hot key * (export or MOD_DEBUG flag to RegisterHotKey()), then it would be ok * to eat because the user selected the hot key. But it is not ok to * eat it as long as we've picked an arbitrary hot key. scottlu. */ if (fsModifiers & MOD_SHIFT) return TRUE; else return FALSE; }
NTSTATUS EndShutdown( PETHREAD Thread, NTSTATUS StatusShutdown) { PWINDOWSTATION pwinsta = gpwinstaLogoff; PDESKTOP pdesk; LUID luidCaller; UserAssert(gpwinstaLogoff); gpwinstaLogoff = NULL; dwThreadEndSession = 0; pwinsta->dwFlags &= ~WSF_SHUTDOWN; if (!NT_SUCCESS(GetProcessLuid(Thread, &luidCaller))) { luidCaller = RtlConvertUlongToLuid(0); // null luid } if (!NT_SUCCESS(StatusShutdown)) { /* * We need to notify the process that called ExitWindows that * the logoff was aborted. */ if (gptiShutdownNotify) { _PostThreadMessage(gptiShutdownNotify, WM_ENDSESSION, FALSE, 0); gptiShutdownNotify = NULL; } /* * Reset the windowstation lock flags so apps can start * again. */ pwinsta->dwFlags = (pwinsta->dwFlags & ~WSF_OPENLOCK) | gdwLocks; return STATUS_SUCCESS; } gptiShutdownNotify = NULL; /* * If logoff is occuring for the user set by winlogon, perform * the normal logoff cleanup. Otherwise, clear the open lock * and continue. */ if (((pwinsta->luidUser.LowPart != 0) || (pwinsta->luidUser.HighPart != 0)) && RtlEqualLuid(&pwinsta->luidUser, &luidCaller)) { /* * Save the current user's NumLock state */ if (FastOpenProfileUserMapping()) { RegisterPerUserKeyboardIndicators(); FastCloseProfileUserMapping(); } /* * Zero out the free blocks in all desktop heaps. */ for (pdesk = pwinsta->rpdeskList; pdesk != NULL; pdesk = pdesk->rpdeskNext) { RtlZeroHeap(pdesk->hheapDesktop, 0); } /* * Logoff/shutdown was successful. In case this is a logoff, remove * everything from the clipboard so the next logged on user can't get * at this stuff. */ ForceEmptyClipboard(pwinsta); /* * Destroy all non-pinned atoms in the global atom table. User can't * create pinned atoms. Currently only the OLE atoms are pinned. */ RtlEmptyAtomTable(pwinsta->pGlobalAtomTable, FALSE); // this code path is hit only on logoff and also on shutdown // We do not want to unload fonts twice when we attempt shutdown // so we mark that the fonts have been unloaded at a logoff time if (bFontsAreLoaded) { LeaveCrit(); GreRemoveAllButPermanentFonts(); EnterCrit(); bFontsAreLoaded = FALSE; } } else { pwinsta->dwFlags &= ~WSF_OPENLOCK; } /* * Tell winlogon that we successfully shutdown/logged off. */ NotifyLogon(pwinsta, &luidCaller, gdwShutdownFlags); return STATUS_SUCCESS; }
void PH_DumpInternalRealImOther(CommonBuffer * common, ThreadBuffer * thread, BOOL toDisk) { // While there's data while (thread->left < thread->total) { int count; int level; double a; double b; double c; // Get the value to apply TD_GET(int, count); TD_GET(int, level); TD_GET(double, a); TD_GET(double, b); TD_GET(double, c); #ifdef USE_CRIT_SECTION EnterCrit(); #endif // And for each point to apply it to while (count > 0) { // Get the index of the point in question unsigned long long xy; TD_GET(unsigned long long, xy); // Note: in each of these we're adding the final exit point of the point trail to each point along the point trail // For Mandelbrot renders, this just means we're storing RGB values // For Buddhabrot renders this lets us determine which 'direction' each of those points averages // out to, for coloring purposes. // In either case the level data lets us know how often it was 'hit', for brightness purposes. #ifdef USE_CRIT_SECTION // The critical section version is easy, just enter the critical section // And update the memory common->m_levelsData[xy]++; common->m_levelsPlotReal[xy] += a; common->m_levelsPlotImaginary[xy] += b; common->m_levelsPlotOther[xy] += c; #else // The interlocked version is quicker, but odder for (;;) { // Set the levels data to the OWNED_VALUE sentinel value LONGLONG levelsData = InterlockedExchangeNoFence64((LONGLONG*)&common->m_levelsData[xy], OWNED_VALUE); // Was the value that was already in memory not owned by another thread if (levelsData != OWNED_VALUE) { // It was! Go ahead and add one to the number of levels, and update // the other values as necessary levelsData++; common->m_levelsPlotReal[xy] += a; common->m_levelsPlotImaginary[xy] += b; common->m_levelsPlotOther[xy] += c; // And we're done, so put the correct value back in memory, we no longer "own" // this pixel now InterlockedExchangeNoFence64((LONGLONG*)&common->m_levelsData[xy], levelsData); break; } } #endif count--; } #ifdef USE_CRIT_SECTION LeaveCrit(); #endif } }
NTSTATUS _ExitWindowsEx( PCSR_THREAD pcsrt, UINT dwFlags, DWORD dwReserved) { BOOL fDoEndSession = FALSE; LUID luidCaller; NTSTATUS Status = STATUS_SUCCESS; UNREFERENCED_PARAMETER(dwReserved); if ((dwFlags & EWX_REBOOT) || (dwFlags & EWX_POWEROFF)) { dwFlags |= EWX_SHUTDOWN; } /* * Find out the callers sid. Only want to shutdown processes in the * callers sid. */ if (!CsrImpersonateClient(NULL)) { return STATUS_BAD_IMPERSONATION_LEVEL; } Status = CsrGetProcessLuid(NULL, &luidCaller); if (!NT_SUCCESS(Status)) { CsrRevertToSelf(); return Status; } try { while (1) { LARGE_INTEGER li; LeaveCrit(); Status = NtUserSetInformationThread( pcsrt->ThreadHandle, UserThreadInitiateShutdown, &dwFlags, sizeof(dwFlags)); EnterCrit(); switch (Status) { case STATUS_PENDING: /* * The logoff/shutdown is in progress and nothing * more needs to be done. */ goto fastexit; case STATUS_RETRY: /* * Another logoff/shutdown is in progress and we need * to cancel it so we can do an override. * * if someone else is trying to cancel shutdown, exit */ li.QuadPart = 0; if (NtWaitForSingleObject(heventCancel, FALSE, &li) == 0) { Status = STATUS_PENDING; goto fastexit; } /* * Cancel the old shutdown */ NtClearEvent(heventCancelled); NtSetEvent(heventCancel, NULL); /* * Wait for the other guy to be cancelled */ LeaveCrit(); NtWaitForSingleObject(heventCancelled, FALSE, NULL); EnterCrit(); /* * This signals that we are no longer trying to cancel a * shutdown */ NtClearEvent(heventCancel); continue; case STATUS_CANT_WAIT: /* * There is no notify window and the calling thread has * windows that prevent this request from succeeding. * The client handles this by starting another thread * to recall ExitWindowsEx. */ goto fastexit; default: if (!NT_SUCCESS(Status)) goto fastexit; } break; } gdwFlags = dwFlags; dwThreadEndSession = (DWORD)pcsrt->ClientId.UniqueThread; fDoEndSession = TRUE; /* * Sometimes the console calls the dialog box when not in shutdown * if now is one of those times cancel the dialog box. */ while (gcInternalDoEndTaskDialog > 0) { LARGE_INTEGER li; NtPulseEvent(heventCancel, NULL); LeaveCrit(); li.QuadPart = (LONGLONG)-10000 * CMSSLEEP; NtDelayExecution(FALSE, &li); EnterCrit(); } /* * Call csr to loop through the processes shutting them down. */ LeaveCrit(); Status = CsrShutdownProcesses(&luidCaller, dwFlags); NtUserSetInformationThread( pcsrt->ThreadHandle, UserThreadEndShutdown, &Status, sizeof(Status)); EnterCrit(); fastexit:; } finally { /* * Only turn off dwThreadEndSession if this is the * thread doing shutdown. */ if (fDoEndSession) { dwThreadEndSession = 0; NtSetEvent(heventCancelled, NULL); } } CsrRevertToSelf(); return Status; }
DWORD MySendEndSessionMessages( HWND hwnd, PCSR_THREAD pcsrt, BOOL fEndTask, DWORD dwClientFlags) { HWND hwndOwner; LARGE_INTEGER li; DWORD dwRet; int cLoops; int cSeconds; WCHAR achName[CCHBODYMAX]; BOOL fPostedClose; BOOL fDialogFirst; DWORD dwFlags; DWORD dwHungApp; HANDLE hNull = NULL; NTSTATUS Status; /* * We've got a random top level window for this application. Find the * root owner, because that's who we want to send the WM_CLOSE to. */ while ((hwndOwner = GetWindow(hwnd, GW_OWNER)) != NULL) hwnd = hwndOwner; /* * We expect this application to process this shutdown request, * so make it the foreground window so it has foreground priority. * This won't leave the critical section. */ SetForegroundWindow(hwnd); /* * Send the WM_CLIENTSHUTDOWN message for end-session. When the app * receives this, it'll then get WM_QUERYENDSESSION and WM_ENDSESSION * messages. */ if (!fEndTask) { USERTHREAD_FLAGS Flags; Flags.dwFlags = 0; Flags.dwMask = (TIF_SHUTDOWNCOMPLETE | TIF_ALLOWSHUTDOWN); LeaveCrit(); Status = NtUserSetInformationThread(pcsrt->ThreadHandle, UserThreadFlags, &Flags, sizeof(Flags)); EnterCrit(); if (!NT_SUCCESS(Status)) return CMDEND_APPSAYSOK; SendNotifyMessage(hwnd, WM_CLIENTSHUTDOWN, dwClientFlags, 0); } /* * If the main window is disabled, bring up the end-task window first, * right away, only if this the WM_CLOSE case. */ fDialogFirst = FALSE; if (fEndTask && (GetWindowLong(hwnd, GWL_STYLE) & WS_DISABLED)) fDialogFirst = TRUE; fPostedClose = FALSE; while (TRUE) { if (fEndTask) { cLoops = (CMSHUNGAPPTIMEOUT / CMSSLEEP); cSeconds = (CMSHUNGAPPTIMEOUT / 1000); } else { cLoops = (CMSWAITTOKILLTIMEOUT / CMSSLEEP); cSeconds = (CMSWAITTOKILLTIMEOUT / 1000); } /* * If end-task and not shutdown, must give this app a WM_CLOSE * message. Can't do this if it has a dialog up because it is in * the wrong processing loop. We detect this by seeing if the window * is disabled - if it is, we don't send it a WM_CLOSE and instead * bring up the end task dialog right away (this is exactly compatible * with win3.1 taskmgr.exe). */ if (fEndTask) { if (!fPostedClose && IsWindow(hwnd) && !(GetWindowLong(hwnd, GWL_STYLE) & WS_DISABLED)) { PostMessage(hwnd, WM_CLOSE, 0, 0L); fPostedClose = TRUE; } } /* * Every so often wake up to see if the app is hung, and if not go * back to sleep until we've run through our timeout. */ while (cLoops--) { /* * If a WM_QUERY/ENDSESSION has been answered to, return. */ if (!fEndTask) { LeaveCrit(); NtUserQueryInformationThread(pcsrt->ThreadHandle, UserThreadFlags, &dwFlags, sizeof(DWORD), NULL); EnterCrit(); if (dwFlags & TIF_SHUTDOWNCOMPLETE) { if (dwFlags & TIF_ALLOWSHUTDOWN) return CMDEND_APPSAYSOK; return CMDEND_APPSAYSNOTOK; } } /* * If the thread is gone, we're done. */ if (pcsrt->Flags & CSR_THREAD_DESTROYED) { return CMDEND_APPSAYSOK; } /* * If the dialog should be brought up first (because the window * was initially disabled), do it. */ if (fDialogFirst) { fDialogFirst = FALSE; break; } /* * if we we're externally cancelled get out */ li.QuadPart = 0; if (NtWaitForSingleObject(heventCancel, FALSE, &li) == 0) { /* * !!! JimA - We may want to call the kernel to * set TIF_SHUTDOWNCOMPLETE in this case. */ return CMDEND_USERSAYSCANCEL; } /* * If hung, bring up the endtask dialog right away. */ dwHungApp = (fEndTask ? CMSHUNGAPPTIMEOUT : CMSWAITTOKILLTIMEOUT); LeaveCrit(); Status = NtUserQueryInformationThread(pcsrt->ThreadHandle, UserThreadHungStatus, &dwHungApp, sizeof(dwHungApp), NULL); EnterCrit(); if (!NT_SUCCESS(Status) || dwHungApp == TRUE) break; /* * Sleep for a second. */ LeaveCrit(); li.QuadPart = (LONGLONG)-10000 * CMSSLEEP; NtDelayExecution(FALSE, &li); EnterCrit(); } achName[0] = 0; if (IsWindow(hwnd)) { GetWindowText(hwnd, achName, CCHMSGMAX); } /* * If there's a hard error, put it on top. */ BoostHardError((DWORD)pcsrt->ClientId.UniqueProcess, FALSE); if (achName[0] == 0) { /* * If the thread is gone, we're done. */ if (pcsrt->Flags & CSR_THREAD_DESTROYED) { return CMDEND_APPSAYSOK; } /* * pti is valid right now. Use the name in the pti. */ LeaveCrit(); NtUserQueryInformationThread(pcsrt->ThreadHandle, UserThreadTaskName, achName, CCHMSGMAX * sizeof(WCHAR), NULL); EnterCrit(); } /* * Set this thread to use the desktop of the * thread being shutdown. */ if (NT_SUCCESS(NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &pcsrt->ThreadHandle, sizeof(HANDLE)))) { /* * Bring up the dialog */ dwRet = DoEndTaskDialog(achName, pcsrt, TYPE_THREADINFO, cSeconds); /* * Release the desktop that was used. */ NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &hNull, sizeof(HANDLE)); } else { /* * We were unable to get the thread's desktop. All we * can do is kill the task. */ dwRet = IDABORT; } switch(dwRet) { case IDCANCEL: /* * Cancel the shutdown process... Get out of here. Signify that * we're cancelling the shutdown request. * * !!! JimA - We may want to call the kernel to * set TIF_SHUTDOWNCOMPLETE in this case. */ return CMDEND_USERSAYSCANCEL; break; case IDABORT: /* * End this guy's task... */ BoostHardError((DWORD)pcsrt->ClientId.UniqueProcess, TRUE); /* * !!! JimA - We may want to call the kernel to * set TIF_SHUTDOWNCOMPLETE in this case. */ return CMDEND_USERSAYSKILL; break; case IDRETRY: /* * Just continue to wait. Reset this app so it doesn't think it's * hung. This'll cause us to wait again. */ if (!(pcsrt->Flags & CSR_THREAD_DESTROYED)) { LeaveCrit(); NtUserSetInformationThread(pcsrt->ThreadHandle, UserThreadHungStatus, NULL, 0); EnterCrit(); } fPostedClose = FALSE; break; } } }
NTSTATUS UserClientShutdown( PCSR_PROCESS pcsrp, ULONG dwFlags, BOOLEAN fFirstPass) { PLIST_ENTRY ListHead, ListNext; PCSR_PROCESS Process; PCSR_THREAD Thread; USERTHREAD_SHUTDOWN_INFORMATION ShutdownInfo; BOOL fNoRetry; DWORD cmd, dwClientFlags; NTSTATUS Status; UINT cThreads; BOOL fSendEndSession = FALSE; /* * If this is a logoff and the process does not belong to * the account doing the logoff and is not LocalSystem, * do not send end-session messages. Console will notify * the app of the logoff. */ if (!(dwFlags & EWX_SHUTDOWN) && (pcsrp->ShutdownFlags & SHUTDOWN_OTHERCONTEXT)) return SHUTDOWN_UNKNOWN_PROCESS; /* * Calculate whether to allow exit and force-exit this process before * we unlock pcsrp. */ fNoRetry = (pcsrp->ShutdownFlags & SHUTDOWN_NORETRY) || (dwFlags & EWX_FORCE); /* * Setup flags for WM_CLIENTSHUTDOWN * -Assume the process is going to OK the WM_QUERYENDSESSION (WMCS_EXIT) * -NT's shutdown always starts with a logoff. * -Shutdown or logoff? (WMCS_SHUTDOWN) * -is this process in the context being logged off? (WMCS_CONTEXTLOGOFF) */ dwClientFlags = WMCS_EXIT | WMCS_LOGOFF; if (dwFlags & EWX_SHUTDOWN) { /* * Apps will never see this set because shutdown starts with a logoff; * later the actual shutdown uses EWX_FORCE so WM_CLIENTSHUTDOWN is not sent. * If we need apps to see this when we're about to shutdown, then we should * check for (dwFlags & (EWX_WINLOGON_OLD_REBOOT | EWX_WINLOGON_OLD_SHUTDOWN)) * (EWX_WINLOGON_OLD_* corresponds to the original request) */ dwClientFlags |= WMCS_SHUTDOWN; } if (!(pcsrp->ShutdownFlags & (SHUTDOWN_SYSTEMCONTEXT | SHUTDOWN_OTHERCONTEXT))) { dwClientFlags |= WMCS_CONTEXTLOGOFF; } /* * Lock the process while we walk the thread list. We know * that the process is valid and therefore do not need to * check the return status. */ CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process); EnterCrit(); ShutdownInfo.StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS; /* * Go through the thread list and mark them as not * shutdown yet. */ ListHead = &pcsrp->ThreadList; ListNext = ListHead->Flink; while (ListNext != ListHead) { Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link ); Thread->Flags &= ~CSR_THREAD_SHUTDOWNSKIP; ListNext = ListNext->Flink; } /* * Perform the proper shutdown operation on each thread. Keep * a count of the number of gui threads found. */ cThreads = 0; while (TRUE) { ListNext = ListHead->Flink; while (ListNext != ListHead) { Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link ); /* * Skip the thread doing the shutdown. Assume that it's * ready. */ if (Thread->ClientId.UniqueThread == (HANDLE)dwThreadEndSession) Thread->Flags |= CSR_THREAD_SHUTDOWNSKIP; if (!(Thread->Flags & (CSR_THREAD_DESTROYED | CSR_THREAD_SHUTDOWNSKIP))) { break; } ListNext = ListNext->Flink; } if (ListNext == ListHead) break; Thread->Flags |= CSR_THREAD_SHUTDOWNSKIP; LeaveCrit(); Status = NtUserQueryInformationThread(Thread->ThreadHandle, UserThreadShutdownInformation, &ShutdownInfo, sizeof(ShutdownInfo), NULL); EnterCrit(); if (!NT_SUCCESS(Status)) continue; if (ShutdownInfo.StatusShutdown == SHUTDOWN_UNKNOWN_PROCESS) continue; if (ShutdownInfo.StatusShutdown == SHUTDOWN_KNOWN_PROCESS) { CsrUnlockProcess(Process); LeaveCrit(); CsrDereferenceProcess(pcsrp); return SHUTDOWN_KNOWN_PROCESS; } /* * If this process is not in the account being logged off and it * is not on the windowstation being logged off, don't send * the end session messages. */ if (!(dwClientFlags & WMCS_CONTEXTLOGOFF) && (ShutdownInfo.hwndDesktop == NULL)) { /* * This process is not in the context being logged off. Do * not terminate it and let console send an event to the process. */ ShutdownInfo.StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS; continue; } /* * Shut down this process. */ cThreads++; if (fNoRetry || !(ShutdownInfo.dwFlags & USER_THREAD_GUI)) { /* * Dispose of any hard errors. */ BoostHardError((DWORD)Thread->ClientId.UniqueProcess, TRUE); } else { CsrReferenceThread(Thread); CsrUnlockProcess(Process); /* * There are problems in changing shutdown to send all the * QUERYENDSESSIONs at once before doing any ENDSESSIONs, like * Windows does. The whole machine needs to be modal if you do this. * If it isn't modal, then you have this problem. Imagine app 1 and 2. * 1 gets the queryendsession, no problem. 2 gets it and brings up a * dialog. Now being a simple user, you decide you need to change the * document in app 1. Now you switch back to app 2, hit ok, and * everything goes away - including app 1 without saving its changes. * Also, apps expect that once they've received the QUERYENDSESSION, * they are not going to get anything else of any particular interest * (unless it is a WM_ENDSESSION with FALSE) We had bugs pre 511 where * apps were blowing up because of this. * If this change is made, the entire system must be modal * while this is going on. - ScottLu 6/30/94 */ cmd = SendShutdownMessages(ShutdownInfo.hwndDesktop, Thread, dwClientFlags | WMCS_QUERYEND); CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process); CsrDereferenceThread(Thread); /* * If shutdown has been cancelled, let csr know about it. */ switch (cmd) { case CMDEND_USERSAYSCANCEL: case CMDEND_APPSAYSNOTOK: /* * Only allow cancelling if this is not a forced shutdown (if * !fNoRetry) */ if (!fNoRetry) { dwClientFlags &= ~WMCS_EXIT; } /* * Fall through. */ case CMDEND_APPSAYSOK: fSendEndSession = TRUE; break; case CMDEND_USERSAYSKILL: break; case CMDEND_NOWINDOW: /* * Did this process have a window? * If this is the second pass we terminate the process even if it did * not have any windows in case the app was just starting up. * WOW hits this often when because it takes so long to start up. * Logon (with WOW auto-starting) then logoff WOW won't die but will * lock some files open so you can't logon next time. */ if (fFirstPass) { cThreads--; } break; } } } /* * If end session message need to be sent, do it now. */ if (fSendEndSession) { /* * Go through the thread list and mark them as not * shutdown yet. */ ListNext = ListHead->Flink; while (ListNext != ListHead) { Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link ); Thread->Flags &= ~CSR_THREAD_SHUTDOWNSKIP; ListNext = ListNext->Flink; } /* * Perform the proper shutdown operation on each thread. */ while (TRUE) { ListHead = &pcsrp->ThreadList; ListNext = ListHead->Flink; while (ListNext != ListHead) { Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link ); if (!(Thread->Flags & (CSR_THREAD_DESTROYED | CSR_THREAD_SHUTDOWNSKIP))) { break; } ListNext = ListNext->Flink; } if (ListNext == ListHead) break; Thread->Flags |= CSR_THREAD_SHUTDOWNSKIP; LeaveCrit(); Status = NtUserQueryInformationThread(Thread->ThreadHandle, UserThreadShutdownInformation, &ShutdownInfo, sizeof(ShutdownInfo), NULL); EnterCrit(); if (!NT_SUCCESS(Status)) continue; if (ShutdownInfo.StatusShutdown == SHUTDOWN_UNKNOWN_PROCESS || !(ShutdownInfo.dwFlags & USER_THREAD_GUI)) continue; /* * Send the end session messages to the thread. */ CsrReferenceThread(Thread); CsrUnlockProcess(Process); /* * If the user says kill it, the user wants it to go away now * no matter what. If the user didn't say kill, then call again * because we need to send WM_ENDSESSION messages. */ SendShutdownMessages(ShutdownInfo.hwndDesktop, Thread, dwClientFlags); CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process); CsrDereferenceThread(Thread); } } CsrUnlockProcess(Process); if (!fNoRetry && !(dwClientFlags & WMCS_EXIT)) { LeaveCrit(); CsrDereferenceProcess(pcsrp); return SHUTDOWN_CANCEL; } /* * Set the final shutdown status according to the number of gui * threads found. If the count is zero, we have an unknown process. */ if (cThreads == 0) ShutdownInfo.StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS; else ShutdownInfo.StatusShutdown = SHUTDOWN_KNOWN_PROCESS; if (ShutdownInfo.StatusShutdown == SHUTDOWN_UNKNOWN_PROCESS || !(dwClientFlags & WMCS_CONTEXTLOGOFF)) { /* * This process is not in the context being logged off. Do * not terminate it and let console send an event to the process. */ LeaveCrit(); return SHUTDOWN_UNKNOWN_PROCESS; } /* * Calling ExitProcess() in the app's context will not always work * because the app may have .dll termination deadlocks: so the thread * will hang with the rest of the process. To ensure apps go away, * we terminate the process with NtTerminateProcess(). * * Pass this special value, DBG_TERMINATE_PROCESS, which tells * NtTerminateProcess() to return failure if it can't terminate the * process because the app is being debugged. */ NtTerminateProcess(pcsrp->ProcessHandle, DBG_TERMINATE_PROCESS); pcsrp->Flags |= CSR_PROCESS_TERMINATED; /* * Let csr know we know about this process - meaning it was our * responsibility to shut it down. */ LeaveCrit(); /* * Now that we're done with the process handle, derefence the csr * process structure. */ CsrDereferenceProcess(pcsrp); return SHUTDOWN_KNOWN_PROCESS; }
HANDLE InternalCreateCallbackThread( HANDLE hProcess, DWORD lpfn, DWORD dwData) { LONG BasePriority; HANDLE hThread, hToken; PTOKEN_DEFAULT_DACL lpDaclDefault; TOKEN_DEFAULT_DACL daclDefault; ULONG cbDacl; SECURITY_ATTRIBUTES attrThread; SECURITY_DESCRIPTOR sd; DWORD idThread; NTSTATUS Status; hThread = NULL; Status = NtOpenProcessToken(hProcess, TOKEN_QUERY, &hToken); if (!NT_SUCCESS(Status)) { KdPrint(("NtOpenProcessToken failed, status = %x\n", Status)); return NULL; } cbDacl = 0; NtQueryInformationToken(hToken, TokenDefaultDacl, &daclDefault, sizeof(daclDefault), &cbDacl); EnterCrit(); // to synchronize heap lpDaclDefault = (PTOKEN_DEFAULT_DACL)LocalAlloc(LMEM_FIXED, cbDacl); LeaveCrit(); if (lpDaclDefault == NULL) { KdPrint(("LocalAlloc failed for lpDaclDefault")); goto closeexit; } Status = NtQueryInformationToken(hToken, TokenDefaultDacl, lpDaclDefault, cbDacl, &cbDacl); if (!NT_SUCCESS(Status)) { KdPrint(("NtQueryInformationToken failed, status = %x\n", Status)); goto freeexit; } if (!NT_SUCCESS(RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION1))) { UserAssert(FALSE); goto freeexit; } RtlSetDaclSecurityDescriptor(&sd, TRUE, lpDaclDefault->DefaultDacl, TRUE); attrThread.nLength = sizeof(attrThread); attrThread.lpSecurityDescriptor = &sd; attrThread.bInheritHandle = FALSE; GetLastError(); hThread = CreateRemoteThread(hProcess, &attrThread, 0L, (LPTHREAD_START_ROUTINE)lpfn, (LPVOID)dwData, 0, &idThread); if (hThread != NULL) { BasePriority = THREAD_PRIORITY_HIGHEST; NtSetInformationThread(hThread, ThreadBasePriority, &BasePriority, sizeof(LONG)); } freeexit: EnterCrit(); // to synchronize heap LocalFree((HANDLE)lpDaclDefault); LeaveCrit(); closeexit: NtClose(hToken); return hThread; }
BOOL _EndTask( HWND hwnd, BOOL fShutdown, BOOL fMeanKill) { PCSR_THREAD pcsrt = CSR_SERVER_QUERYCLIENTTHREAD(); PCSR_THREAD pcsrtKill; DWORD dwThreadId; DWORD dwProcessId; LPWSTR lpszMsg; BOOL fAllocated; DWORD dwCmd; /* * Note: fShutdown isn't used for anything in this routine! * They are still there because I haven't removed them: the old endtask * code relied on them. */ UNREFERENCED_PARAMETER(fShutdown); /* * Get the process and thread that owns hwnd. */ dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcessId); if (dwThreadId == 0) return TRUE; /* * If this is a console window, then just send the close message to * it, and let console clean up the processes in it. */ if ((HANDLE)GetWindowLong(hwnd, GWL_HINSTANCE) == hModuleWin) { PostMessage(hwnd, WM_CLOSE, 0, 0); return TRUE; } /* * Find the CSR_THREAD for the window. */ LeaveCrit(); CsrLockThreadByClientId((HANDLE)dwThreadId, &pcsrtKill); EnterCrit(); if (pcsrtKill == NULL) return TRUE; CsrReferenceThread(pcsrtKill); CsrUnlockThread(pcsrtKill); /* * If this is a WOW app, then shutdown just this wow application. */ if (!fMeanKill) { /* * Find out what to do now - did the user cancel or the app cancel, * etc? Only allow cancelling if we are not forcing the app to * exit. */ dwCmd = MySendEndSessionMessages(hwnd, pcsrtKill, TRUE, 0); switch (dwCmd) { case CMDEND_APPSAYSNOTOK: /* * App says not ok - this'll let taskman bring up the "are you sure?" * dialog to the user. */ CsrDereferenceThread(pcsrtKill); return FALSE; case CMDEND_USERSAYSCANCEL: /* * User hit cancel on the timeout dialog - so the user really meant * it. Let taskman know everything is ok by returning TRUE. */ CsrDereferenceThread(pcsrtKill); return TRUE; } } /* * Kill the application now. If the thread has not been destroyed, * nuke the task. If WowExitTask returns that the thread is not * a WOW task, terminate the process. */ if (!(pcsrtKill->Flags & CSR_THREAD_DESTROYED) && !WowExitTask(pcsrtKill)) { /* * Calling ExitProcess() in the app's context will not always work * because the app may have .dll termination deadlocks: so the thread * will hang with the rest of the process. To ensure apps go away, * we terminate the process with NtTerminateProcess(). * * Pass this special value, DBG_TERMINATE_PROCESS, which tells * NtTerminateProcess() to return failure if it can't terminate the * process because the app is being debugged. */ if (!NT_SUCCESS(NtTerminateProcess(pcsrtKill->Process->ProcessHandle, DBG_TERMINATE_PROCESS))) { /* * If the app is being debugged, don't close it - because that can * cause a hang to the NtTerminateProcess() call. */ lpszMsg = ServerLoadString(hModuleWin, STR_APPDEBUGGED, NULL, &fAllocated); if (lpszMsg) { if (NT_SUCCESS(NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &pcsrt->ThreadHandle, sizeof(HANDLE)))) { HANDLE hNull = NULL; LeaveCrit(); MessageBoxEx(NULL, lpszMsg, NULL, MB_OK | MB_SETFOREGROUND, 0); EnterCrit(); NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop, &hNull, sizeof(HANDLE)); } LocalFree(lpszMsg); } } else { pcsrtKill->Process->Flags |= CSR_PROCESS_TERMINATED; } } CsrDereferenceThread(pcsrtKill); return TRUE; }