mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1503 lines
45 KiB
1503 lines
45 KiB
/**************************** Module Header ********************************\
|
|
* Module Name: exitwin.c
|
|
*
|
|
* Copyright 1985-92, Microsoft Corporation
|
|
*
|
|
* NT: Logoff user
|
|
* DOS: Exit windows
|
|
*
|
|
* History:
|
|
* 07-23-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define BEGIN_LPC_RECV(API) \
|
|
P##API##MSG a = (P##API##MSG)&m->u.ApiMessageData; \
|
|
PCSR_THREAD pcsrt; \
|
|
PTEB Teb = NtCurrentTeb(); \
|
|
NTSTATUS Status = STATUS_SUCCESS; \
|
|
UNREFERENCED_PARAMETER(ReplyStatus); \
|
|
\
|
|
EnterCrit(); \
|
|
Teb->LastErrorValue = 0; \
|
|
pcsrt = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
|
|
#define END_LPC_RECV() \
|
|
a->dwLastError = Teb->LastErrorValue; \
|
|
LeaveCrit(); \
|
|
return Status;
|
|
|
|
/*
|
|
* Commands returned from MySendEndSessionMessages()
|
|
*/
|
|
#define CMDEND_APPSAYSOK 1
|
|
#define CMDEND_APPSAYSNOTOK 2
|
|
#define CMDEND_USERSAYSKILL 3
|
|
#define CMDEND_USERSAYSCANCEL 4
|
|
#define CMDEND_NOWINDOW 5
|
|
|
|
#define CCHMSGMAX 256
|
|
#define CCHBODYMAX 512
|
|
|
|
#define CSR_THREAD_SHUTDOWNSKIP 0x00000008
|
|
|
|
DWORD SendShutdownMessages(HWND hwndDesktop, PCSR_THREAD pcsrt, DWORD dwClientFlags);
|
|
BOOL WowExitTask(PCSR_THREAD pcsrt);
|
|
DWORD MySendEndSessionMessages(HWND hwnd, PCSR_THREAD pcsrt, BOOL fEndTask, DWORD dwClientFlags);
|
|
NTSTATUS UserClientShutdown(PCSR_PROCESS pcsrp, ULONG dwFlags, BOOLEAN fFirstPass);
|
|
BOOL BoostHardError(DWORD dwProcessId, BOOL fForce);
|
|
int DoEndTaskDialog(WCHAR* pszTitle, HANDLE h, UINT type, int cSeconds);
|
|
|
|
/***************************************************************************\
|
|
* _ExitWindowsEx
|
|
*
|
|
* Determines whether shutdown is allowed, and if so calls CSR to start
|
|
* shutting down processes. If this succeeds all the way through, tell winlogon
|
|
* so it'll either logoff or reboot the system. Shuts down the processes in
|
|
* the caller's sid.
|
|
*
|
|
* History
|
|
* 07-23-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
DWORD gdwFlags = 0;
|
|
int gcInternalDoEndTaskDialog = 0;
|
|
|
|
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;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserClientShutdown
|
|
*
|
|
* This gets called from CSR. If we recognize the application (i.e., it has a
|
|
* top level window), then send queryend/end session messages to it. Otherwise
|
|
* say we don't recognize it.
|
|
*
|
|
* 07-23-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
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;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FindWindowFromThread
|
|
*
|
|
* This is a callback function passed to EnumThreadWindows by SendShutdownMessages.
|
|
* to find a top level window owned by a given thread
|
|
*
|
|
* 07/18/96 GerardoB Created
|
|
\***************************************************************************/
|
|
BOOL CALLBACK FindWindowFromThread (HWND hwnd, LPARAM lParam)
|
|
{
|
|
*((HWND *)lParam) = hwnd;
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SendShutdownMessages
|
|
*
|
|
* This gets called to actually send the queryend / end session messages.
|
|
*
|
|
* 07-25-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
DWORD SendShutdownMessages(
|
|
HWND hwndDesktop,
|
|
PCSR_THREAD pcsrt,
|
|
DWORD dwClientFlags)
|
|
{
|
|
HWND hwnd;
|
|
DWORD cmd;
|
|
|
|
/*
|
|
* Find a top-level window owned by the thread.
|
|
*/
|
|
hwnd = NULL;
|
|
EnumThreadWindows((DWORD)pcsrt->ClientId.UniqueThread,
|
|
&FindWindowFromThread, (LPARAM)&hwnd);
|
|
if (!hwnd)
|
|
return CMDEND_NOWINDOW;
|
|
/*
|
|
* This'll send WM_QUERYENDSESSION / WM_ENDSESSION messages to all
|
|
* the windows of this hwnd's thread.
|
|
*/
|
|
cmd = MySendEndSessionMessages(hwnd, pcsrt, FALSE, dwClientFlags);
|
|
|
|
switch (cmd) {
|
|
case CMDEND_APPSAYSOK:
|
|
/*
|
|
* This thread says ok... continue on to the next thread.
|
|
*/
|
|
break;
|
|
|
|
case CMDEND_USERSAYSKILL:
|
|
/*
|
|
* The user hit the "end-task" button on the hung app dialog.
|
|
* If this is a wow app, kill just this app and continue to
|
|
* the next wow app.
|
|
*/
|
|
if (!(pcsrt->Flags & CSR_THREAD_DESTROYED)) {
|
|
if (WowExitTask(pcsrt))
|
|
break;
|
|
}
|
|
|
|
/* otherwise fall through */
|
|
|
|
case CMDEND_USERSAYSCANCEL:
|
|
case CMDEND_APPSAYSNOTOK:
|
|
/*
|
|
* Exit out of here... either the user wants to kill or cancel,
|
|
* or the app says no.
|
|
*/
|
|
return cmd;
|
|
}
|
|
|
|
return CMDEND_APPSAYSOK;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MySendEndSessionMessages
|
|
*
|
|
* Tell the app to go away.
|
|
*
|
|
* 07-25-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DoEndTaskDialog
|
|
*
|
|
* Create a dialog notifying the user that the app is not responding and
|
|
* wait for a user responce. This function also exits if the shutdown is
|
|
* cancelled
|
|
*
|
|
* 29-Oct-1992 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
typedef struct _ENDDLGPARAMS {
|
|
TCHAR* pszTitle;
|
|
HANDLE h;
|
|
UINT type;
|
|
int cSeconds;
|
|
} ENDDLGPARAMS;
|
|
|
|
/***************************************************************************\
|
|
* EndTaskDlgProc
|
|
*
|
|
* This is the dialog procedure for the dialog that comes up when an app is
|
|
* not responding.
|
|
*
|
|
* 07-23-92 ScottLu Rewrote it, but used same dialog template.
|
|
* 04-28-92 JonPa Created.
|
|
\***************************************************************************/
|
|
|
|
LONG APIENTRY EndTaskDlgProc(
|
|
HWND hwndDlg,
|
|
UINT msg,
|
|
UINT wParam,
|
|
LONG lParam)
|
|
{
|
|
ENDDLGPARAMS* pedp;
|
|
LARGE_INTEGER li;
|
|
WCHAR achFormat[CCHBODYMAX];
|
|
WCHAR achText[CCHBODYMAX];
|
|
DWORD dwData;
|
|
USERTHREAD_FLAGS Flags;
|
|
|
|
switch (msg) {
|
|
case WM_INITDIALOG:
|
|
pedp = (ENDDLGPARAMS*)lParam;
|
|
|
|
/*
|
|
* Save this for later revalidation.
|
|
*/
|
|
SetWindowLong(hwndDlg, GWL_USERDATA, (DWORD)pedp->h);
|
|
SetWindowLong(hwndDlg, DWL_USER, (DWORD)pedp->type);
|
|
|
|
SetWindowText(hwndDlg, pedp->pszTitle);
|
|
|
|
/*
|
|
* Update text that says how long we'll wait.
|
|
*/
|
|
GetDlgItemText(hwndDlg, IDIGNORE, achFormat, CCHBODYMAX);
|
|
wsprintf(achText, achFormat, pedp->cSeconds);
|
|
SetDlgItemText(hwndDlg, IDIGNORE, achText);
|
|
|
|
/*
|
|
* Make this dialog top most and foreground.
|
|
*/
|
|
Flags.dwFlags = TIF_ALLOWFOREGROUNDACTIVATE;
|
|
Flags.dwMask = TIF_ALLOWFOREGROUNDACTIVATE;
|
|
NtUserSetInformationThread(NtCurrentThread(), UserThreadFlags,
|
|
&Flags, sizeof(Flags));
|
|
SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE);
|
|
|
|
/*
|
|
* Set this timer so every 1/2 a second we can see if this app
|
|
* has gone away.
|
|
*/
|
|
SetTimer(hwndDlg, 5, 500, NULL);
|
|
return TRUE;
|
|
|
|
case WM_TIMER:
|
|
/*
|
|
* If shutdown has been cancelled, bring down the dialog.
|
|
*/
|
|
li.QuadPart = 0;
|
|
if (NtWaitForSingleObject(heventCancel, FALSE, &li) == 0) {
|
|
EndDialog(hwndDlg, IDCANCEL);
|
|
break;
|
|
}
|
|
|
|
dwData = GetWindowLong(hwndDlg, GWL_USERDATA);
|
|
if (GetWindowLong(hwndDlg, DWL_USER) == TYPE_CONSOLE_ID) {
|
|
|
|
/*
|
|
* If it's the console calling us, check if the thread or process
|
|
* handle is still valid. If not, bring down the dialog.
|
|
*/
|
|
if (WaitForSingleObject((HANDLE)dwData, 0) != 0)
|
|
break;
|
|
} else if (!(((PCSR_THREAD)dwData)->Flags & CSR_THREAD_DESTROYED)) {
|
|
|
|
/*
|
|
* If the thread is marked as destroyed, bring down the dialog.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* This'll cause the dialog to go away and the wait for this app to
|
|
* close to return.
|
|
*/
|
|
EndDialog(hwndDlg, IDRETRY);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
/*
|
|
* Assume WM_CLOSE means cancel shutdown
|
|
*/
|
|
wParam = IDCANCEL;
|
|
/*
|
|
* falls through...
|
|
*/
|
|
|
|
case WM_COMMAND:
|
|
EndDialog(hwndDlg, LOWORD(wParam));
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _EndTask
|
|
*
|
|
* This routine is called from the task manager to end an application - for
|
|
* gui apps, either a win32 app or a win16 app. Note: Multiple console
|
|
* processes can live in a single console window. We'll pass these requests
|
|
* for destruction to console.
|
|
*
|
|
* 07-25-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
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;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* WowExitTask
|
|
*
|
|
* Calls wow back to make sure a specific task has exited. Returns
|
|
* TRUE if the thread is a WOW task, FALSE if not.
|
|
*
|
|
* 08-02-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
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;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InternalWaitCancel
|
|
*
|
|
* Console calls this to wait for objects or shutdown to be cancelled
|
|
*
|
|
* 29-Oct-1992 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
DWORD InternalWaitCancel(
|
|
HANDLE handle,
|
|
DWORD dwMilliseconds)
|
|
{
|
|
HANDLE ahandle[2];
|
|
|
|
ahandle[0] = handle;
|
|
ahandle[1] = heventCancel;
|
|
|
|
return WaitForMultipleObjects(2, ahandle, FALSE, dwMilliseconds);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InternalDoEndTaskDialog
|
|
*
|
|
* Console calls this to put up a cancelable dialog.
|
|
*
|
|
* 29-Oct-1992 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
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;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InternalCreateCallbackThread
|
|
*
|
|
* This routine creates a remote thread in the context of a given process.
|
|
* It is used to call the console control routine, as well as ExitProcess when
|
|
* forcing an exit. Returns a thread handle.
|
|
*
|
|
* 07-28-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
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;
|
|
}
|
|
|
|
ULONG
|
|
SrvExitWindowsEx(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus)
|
|
{
|
|
BEGIN_LPC_RECV(EXITWINDOWSEX);
|
|
|
|
Status = _ExitWindowsEx(pcsrt, a->uFlags, a->dwReserved);
|
|
a->fSuccess = NT_SUCCESS(Status);
|
|
|
|
END_LPC_RECV();
|
|
}
|
|
|
|
ULONG
|
|
SrvEndTask(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus)
|
|
{
|
|
BEGIN_LPC_RECV(ENDTASK);
|
|
|
|
a->fSuccess = _EndTask(
|
|
a->hwnd,
|
|
a->fShutdown,
|
|
a->fForce);
|
|
|
|
END_LPC_RECV();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* IsPrivileged
|
|
*
|
|
* Check to see if the client has the specified privileges
|
|
*
|
|
* History:
|
|
* 01-02-91 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
BOOL IsPrivileged(
|
|
PPRIVILEGE_SET ppSet)
|
|
{
|
|
HANDLE hToken;
|
|
NTSTATUS Status;
|
|
BOOLEAN bResult = FALSE;
|
|
UNICODE_STRING strSubSystem;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
if (!CsrImpersonateClient(NULL))
|
|
return FALSE;
|
|
|
|
/*
|
|
* Open the client's token
|
|
*/
|
|
RtlInitUnicodeString(&strSubSystem, L"USER32");
|
|
if (NT_SUCCESS(Status = NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY,
|
|
(BOOLEAN)TRUE, &hToken))) {
|
|
|
|
/*
|
|
* Perform the check
|
|
*/
|
|
Status = NtPrivilegeCheck(hToken, ppSet, &bResult);
|
|
NtPrivilegeObjectAuditAlarm(&strSubSystem, NULL, hToken,
|
|
0, ppSet, bResult);
|
|
NtClose(hToken);
|
|
if (!bResult) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
}
|
|
}
|
|
CsrRevertToSelf();
|
|
if (!NT_SUCCESS(Status))
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
|
|
/*
|
|
* Return result of privilege check
|
|
*/
|
|
return (BOOL)(bResult && NT_SUCCESS(Status));
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _RegisterServicesProcess
|
|
*
|
|
* Register the services process.
|
|
*
|
|
* History:
|
|
* 05-05-95 BradG Created.
|
|
\***************************************************************************/
|
|
|
|
ULONG
|
|
SrvRegisterServicesProcess(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus)
|
|
{
|
|
PRIVILEGE_SET psTcb = { 1, PRIVILEGE_SET_ALL_NECESSARY,
|
|
{ SE_TCB_PRIVILEGE, 0 }
|
|
};
|
|
|
|
BEGIN_LPC_RECV(REGISTERSERVICESPROCESS);
|
|
|
|
/*
|
|
* Allow only one services process and then only if it has TCB
|
|
* privilege.
|
|
*/
|
|
if (gdwServicesProcessId != 0 || !IsPrivileged(&psTcb)) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
a->fSuccess = FALSE;
|
|
} else {
|
|
gdwServicesProcessId = a->dwProcessId;
|
|
a->fSuccess = TRUE;
|
|
}
|
|
|
|
END_LPC_RECV();
|
|
}
|