reactos/win32ss/user/winsrv/usersrv/shutdown.c

879 lines
27 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS User API Server DLL
* FILE: win32ss/user/winsrv/usersrv/shutdown.c
* PURPOSE: Logout/shutdown
* PROGRAMMERS:
*/
/* INCLUDES *******************************************************************/
#include "usersrv.h"
#include <commctrl.h>
#include <psapi.h>
#include "resource.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
// Those flags (that are used for CsrProcess->ShutdownFlags) are named
// in accordance to the only public one: SHUTDOWN_NORETRY used for the
// SetProcessShutdownParameters API.
#if !defined(SHUTDOWN_SYSTEMCONTEXT) && !defined(SHUTDOWN_OTHERCONTEXT)
#define SHUTDOWN_SYSTEMCONTEXT CsrShutdownSystem
#define SHUTDOWN_OTHERCONTEXT CsrShutdownOther
#endif
// The DPRINTs that need to really be removed as soon as everything works.
#define MY_DPRINT DPRINT1
#define MY_DPRINT2 DPRINT
typedef struct tagNOTIFY_CONTEXT
{
UINT Msg;
WPARAM wParam;
LPARAM lParam;
// HDESK Desktop;
// HDESK OldDesktop;
DWORD StartTime;
DWORD QueryResult;
HWND Dlg;
DWORD EndNowResult;
BOOL ShowUI;
HANDLE UIThread;
HWND WndClient;
PSHUTDOWN_SETTINGS ShutdownSettings;
} NOTIFY_CONTEXT, *PNOTIFY_CONTEXT;
#define QUERY_RESULT_ABORT 0
#define QUERY_RESULT_CONTINUE 1
#define QUERY_RESULT_TIMEOUT 2
#define QUERY_RESULT_ERROR 3
#define QUERY_RESULT_FORCE 4
typedef void (WINAPI *INITCOMMONCONTROLS_PROC)(void);
typedef struct tagMESSAGE_CONTEXT
{
HWND Wnd;
UINT Msg;
WPARAM wParam;
LPARAM lParam;
DWORD Timeout;
} MESSAGE_CONTEXT, *PMESSAGE_CONTEXT;
/* FUNCTIONS ******************************************************************/
static HMODULE hComCtl32Lib = NULL;
static VOID
CallInitCommonControls(VOID)
{
static BOOL Initialized = FALSE;
INITCOMMONCONTROLS_PROC InitProc;
if (Initialized) return;
hComCtl32Lib = LoadLibraryW(L"COMCTL32.DLL");
if (hComCtl32Lib == NULL) return;
InitProc = (INITCOMMONCONTROLS_PROC)GetProcAddress(hComCtl32Lib, "InitCommonControls");
if (InitProc == NULL) return;
(*InitProc)();
Initialized = TRUE;
}
static VOID FASTCALL
UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext)
{
DWORD Passed;
Passed = GetTickCount() - NotifyContext->StartTime;
Passed -= NotifyContext->ShutdownSettings->HungAppTimeout;
if (NotifyContext->ShutdownSettings->WaitToKillAppTimeout < Passed)
{
Passed = NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
}
SendMessageW(ProgressBar, PBM_SETPOS, Passed / 2, 0);
}
static INT_PTR CALLBACK
EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
INT_PTR Result;
PNOTIFY_CONTEXT NotifyContext;
HWND ProgressBar;
DWORD TitleLength;
int Len;
LPWSTR Title;
switch(Msg)
{
case WM_INITDIALOG:
NotifyContext = (PNOTIFY_CONTEXT)lParam;
NotifyContext->EndNowResult = QUERY_RESULT_ABORT;
SetWindowLongPtrW(Dlg, DWLP_USER, (LONG_PTR)lParam);
TitleLength = SendMessageW(NotifyContext->WndClient, WM_GETTEXTLENGTH,
0, 0) +
GetWindowTextLengthW(Dlg);
Title = HeapAlloc(UserServerHeap, 0, (TitleLength + 1) * sizeof(WCHAR));
if (Title)
{
Len = GetWindowTextW(Dlg, Title, TitleLength + 1);
SendMessageW(NotifyContext->WndClient, WM_GETTEXT,
TitleLength + 1 - Len, (LPARAM)(Title + Len));
SetWindowTextW(Dlg, Title);
HeapFree(UserServerHeap, 0, Title);
}
ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS);
SendMessageW(ProgressBar, PBM_SETRANGE32, 0,
NotifyContext->ShutdownSettings->WaitToKillAppTimeout / 2);
UpdateProgressBar(ProgressBar, NotifyContext);
SetTimer(Dlg, 0, 200, NULL);
Result = FALSE;
break;
case WM_TIMER:
NotifyContext = (PNOTIFY_CONTEXT)GetWindowLongPtrW(Dlg, DWLP_USER);
ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS);
UpdateProgressBar(ProgressBar, NotifyContext);
Result = TRUE;
break;
case WM_COMMAND:
if (BN_CLICKED == HIWORD(wParam) && IDC_END_NOW == LOWORD(wParam))
{
NotifyContext = (PNOTIFY_CONTEXT)GetWindowLongPtrW(Dlg, DWLP_USER);
NotifyContext->EndNowResult = QUERY_RESULT_FORCE;
MY_DPRINT("Closing progress dlg by hand\n");
SendMessageW(Dlg, WM_CLOSE, 0, 0);
Result = TRUE;
}
else
{
Result = FALSE;
}
break;
case WM_CLOSE:
MY_DPRINT("WM_CLOSE\n");
DestroyWindow(Dlg);
Result = TRUE;
break;
case WM_DESTROY:
MY_DPRINT("WM_DESTROY\n");
NotifyContext = (PNOTIFY_CONTEXT)GetWindowLongPtrW(Dlg, DWLP_USER);
NotifyContext->Dlg = NULL;
KillTimer(Dlg, 0);
PostQuitMessage(NotifyContext->EndNowResult);
Result = TRUE;
break;
default:
Result = FALSE;
break;
}
return Result;
}
static DWORD WINAPI
EndNowThreadProc(LPVOID Parameter)
{
PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT)Parameter;
MSG Msg;
#if 0
SetThreadDesktop(NotifyContext->Desktop);
SwitchDesktop(NotifyContext->Desktop);
#else
/* For now show the end task dialog in the active desktop */
NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseActiveDesktop,
NULL,
0);
#endif
CallInitCommonControls();
NotifyContext->Dlg = CreateDialogParam(UserServerDllInstance,
MAKEINTRESOURCE(IDD_END_NOW), NULL,
EndNowDlgProc, (LPARAM)NotifyContext);
if (NotifyContext->Dlg == NULL)
return 0;
ShowWindow(NotifyContext->Dlg, SW_SHOWNORMAL);
while (GetMessageW(&Msg, NULL, 0, 0))
{
if (!IsDialogMessage(NotifyContext->Dlg, &Msg))
{
TranslateMessage(&Msg);
DispatchMessageW(&Msg);
}
}
return Msg.wParam;
}
static DWORD WINAPI
SendClientShutdown(LPVOID Parameter)
{
PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT)Parameter;
DWORD_PTR Result;
/* If the shutdown is aborted, just notify the process, there is no need to wait */
if ((Context->wParam & (MCS_QUERYENDSESSION | MCS_ENDSESSION)) == 0)
{
DPRINT("Called WM_CLIENTSHUTDOWN with wParam == 0 ...\n");
SendNotifyMessageW(Context->Wnd, WM_CLIENTSHUTDOWN,
Context->wParam, Context->lParam);
return QUERY_RESULT_CONTINUE;
}
if (SendMessageTimeoutW(Context->Wnd, WM_CLIENTSHUTDOWN,
Context->wParam, Context->lParam,
SMTO_NORMAL, Context->Timeout, &Result))
{
DWORD Ret;
if (Context->wParam & MCS_QUERYENDSESSION)
{
/* WM_QUERYENDSESSION case */
switch (Result)
{
case MCSR_DONOTSHUTDOWN:
Ret = QUERY_RESULT_ABORT;
break;
case MCSR_GOODFORSHUTDOWN:
case MCSR_SHUTDOWNFINISHED:
default:
Ret = QUERY_RESULT_CONTINUE;
}
}
else
{
/* WM_ENDSESSION case */
Ret = QUERY_RESULT_CONTINUE;
}
DPRINT("SendClientShutdown -- Return == %s\n",
Ret == QUERY_RESULT_CONTINUE ? "Continue" : "Abort");
return Ret;
}
DPRINT1("SendClientShutdown -- Error == %s\n",
GetLastError() == 0 ? "Timeout" : "error");
return (GetLastError() == 0 ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR);
}
static BOOL
NotifyTopLevelWindow(HWND Wnd, PNOTIFY_CONTEXT NotifyContext)
{
MESSAGE_CONTEXT MessageContext;
DWORD Now, Passed;
DWORD Timeout, WaitStatus;
HANDLE MessageThread;
HANDLE Threads[2];
SetForegroundWindow(Wnd);
Now = GetTickCount();
if (NotifyContext->StartTime == 0)
NotifyContext->StartTime = Now;
/*
* Note: Passed is computed correctly even when GetTickCount()
* wraps due to unsigned arithmetic.
*/
Passed = Now - NotifyContext->StartTime;
MessageContext.Wnd = Wnd;
MessageContext.Msg = NotifyContext->Msg;
MessageContext.wParam = NotifyContext->wParam;
MessageContext.lParam = NotifyContext->lParam;
MessageContext.Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
if (!NotifyContext->ShutdownSettings->AutoEndTasks)
{
MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
}
if (Passed < MessageContext.Timeout)
{
MessageContext.Timeout -= Passed;
MessageThread = CreateThread(NULL, 0, SendClientShutdown,
(LPVOID)&MessageContext, 0, NULL);
if (MessageThread == NULL)
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
return FALSE;
}
Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
if (Passed < Timeout)
{
Timeout -= Passed;
WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE);
}
else
{
WaitStatus = WAIT_TIMEOUT;
}
if (WAIT_TIMEOUT == WaitStatus)
{
NotifyContext->WndClient = Wnd;
if (NotifyContext->UIThread == NULL && NotifyContext->ShowUI)
{
NotifyContext->UIThread = CreateThread(NULL, 0,
EndNowThreadProc,
(LPVOID)NotifyContext,
0, NULL);
}
Threads[0] = MessageThread;
Threads[1] = NotifyContext->UIThread;
WaitStatus = WaitForMultipleObjectsEx(NotifyContext->UIThread == NULL ?
1 : 2,
Threads, FALSE, INFINITE,
FALSE);
if (WaitStatus == WAIT_OBJECT_0)
{
if (!GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
}
}
else if (WaitStatus == WAIT_OBJECT_0 + 1)
{
if (!GetExitCodeThread(NotifyContext->UIThread,
&NotifyContext->QueryResult))
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
}
}
else
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
}
if (WaitStatus != WAIT_OBJECT_0)
{
TerminateThread(MessageThread, QUERY_RESULT_TIMEOUT);
}
}
else if (WaitStatus == WAIT_OBJECT_0)
{
if (!GetExitCodeThread(MessageThread,
&NotifyContext->QueryResult))
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
}
}
else
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
}
CloseHandle(MessageThread);
}
else
{
NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT;
}
DPRINT("NotifyContext->QueryResult == %d\n", NotifyContext->QueryResult);
return (NotifyContext->QueryResult == QUERY_RESULT_CONTINUE);
}
static BOOLEAN
IsConsoleMode(VOID)
{
return (BOOLEAN)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE);
}
/************************************************/
static BOOL
ThreadShutdownNotify(IN PCSR_THREAD CsrThread,
IN ULONG Flags,
IN ULONG Flags2,
IN PNOTIFY_CONTEXT Context)
{
HWND TopWnd = NULL;
EnumThreadWindows(HandleToUlong(CsrThread->ClientId.UniqueThread),
FindTopLevelWnd, (LPARAM)&TopWnd);
if (TopWnd)
{
HWND hWndOwner;
/*** FOR TESTING PURPOSES ONLY!! ***/
HWND tmpWnd;
tmpWnd = TopWnd;
/***********************************/
while ((hWndOwner = GetWindow(TopWnd, GW_OWNER)) != NULL)
{
MY_DPRINT("GetWindow(TopWnd, GW_OWNER) not returned NULL...\n");
TopWnd = hWndOwner;
}
if (TopWnd != tmpWnd) MY_DPRINT("(TopWnd = %x) != (tmpWnd = %x)\n", TopWnd, tmpWnd);
}
else
{
return FALSE;
}
Context->wParam = Flags2;
Context->lParam = (0 != (Flags & EWX_CALLER_WINLOGON_LOGOFF) ?
ENDSESSION_LOGOFF : 0);
Context->StartTime = 0;
Context->UIThread = NULL;
Context->ShowUI = !IsConsoleMode() && (Flags2 & (MCS_QUERYENDSESSION | MCS_ENDSESSION));
Context->Dlg = NULL;
#if 0 // Obviously, switching desktops like that from within WINSRV doesn't work...
{
BOOL Success;
Context->OldDesktop = GetThreadDesktop(GetCurrentThreadId());
// Context->Desktop = GetThreadDesktop(HandleToUlong(CsrThread->ClientId.UniqueThread));
Context->Desktop = GetThreadDesktop(GetWindowThreadProcessId(TopWnd, NULL));
MY_DPRINT("Last error = %d\n", GetLastError());
MY_DPRINT("Before switching to desktop 0x%x\n", Context->Desktop);
Success = SwitchDesktop(Context->Desktop);
MY_DPRINT("After switching to desktop (Success = %s ; last error = %d); going to notify top-level...\n",
Success ? "TRUE" : "FALSE", GetLastError());
}
#endif
NotifyTopLevelWindow(TopWnd, Context);
/******************************************************************************/
#if 1
if (Context->UIThread)
{
MY_DPRINT("Context->UIThread != NULL\n");
if (Context->Dlg)
{
MY_DPRINT("Sending WM_CLOSE because Dlg is != NULL\n");
SendMessageW(Context->Dlg, WM_CLOSE, 0, 0);
}
else
{
MY_DPRINT("Terminating UIThread thread with QUERY_RESULT_ERROR\n");
TerminateThread(Context->UIThread, QUERY_RESULT_ERROR);
}
CloseHandle(Context->UIThread);
/**/Context->UIThread = NULL;/**/
/**/Context->Dlg = NULL;/**/
}
#endif
/******************************************************************************/
#if 0
MY_DPRINT("Switch back to old desktop 0x%x\n", Context->OldDesktop);
SwitchDesktop(Context->OldDesktop);
MY_DPRINT("Switched back ok\n");
#endif
return TRUE;
}
static ULONG
NotifyUserProcessForShutdown(PCSR_PROCESS CsrProcess,
PSHUTDOWN_SETTINGS ShutdownSettings,
UINT Flags)
{
DWORD QueryResult = QUERY_RESULT_CONTINUE;
PCSR_PROCESS Process;
PCSR_THREAD Thread;
PLIST_ENTRY NextEntry;
NOTIFY_CONTEXT Context;
BOOL FoundWindows = FALSE;
/* In case we make a forced shutdown, just kill the process */
if (Flags & EWX_FORCE)
return CsrShutdownCsrProcess;
Context.ShutdownSettings = ShutdownSettings;
Context.QueryResult = QUERY_RESULT_CONTINUE; // We continue shutdown by default.
/* Lock the process */
CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
/* Send first the QUERYENDSESSION messages to all the threads of the process */
MY_DPRINT2("Sending the QUERYENDSESSION messages...\n");
NextEntry = CsrProcess->ThreadList.Flink;
while (NextEntry != &CsrProcess->ThreadList)
{
/* Get the current thread entry */
Thread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
/* Move to the next entry */
NextEntry = NextEntry->Flink;
/* If the thread is being terminated, just skip it */
if (Thread->Flags & CsrThreadTerminated) continue;
/* Reference the thread and temporarily unlock the process */
CsrReferenceThread(Thread);
CsrUnlockProcess(Process);
Context.QueryResult = QUERY_RESULT_CONTINUE;
if (ThreadShutdownNotify(Thread, Flags, MCS_QUERYENDSESSION, &Context))
{
FoundWindows = TRUE;
}
/* Lock the process again and dereference the thread */
CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
CsrDereferenceThread(Thread);
// FIXME: Analyze Context.QueryResult !!
/**/if (Context.QueryResult == QUERY_RESULT_ABORT) goto Quit;/**/
}
if (!FoundWindows)
{
/* We looped all threads but no top level window was found so we didn't send any message */
/* Let the console server run the generic process shutdown handler */
CsrUnlockProcess(Process);
return CsrShutdownNonCsrProcess;
}
QueryResult = Context.QueryResult;
MY_DPRINT2("QueryResult = %s\n",
QueryResult == QUERY_RESULT_ABORT ? "Abort" : "Continue");
/* Now send the ENDSESSION messages to the threads */
MY_DPRINT2("Now sending the ENDSESSION messages...\n");
NextEntry = CsrProcess->ThreadList.Flink;
while (NextEntry != &CsrProcess->ThreadList)
{
/* Get the current thread entry */
Thread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
/* Move to the next entry */
NextEntry = NextEntry->Flink;
/* If the thread is being terminated, just skip it */
if (Thread->Flags & CsrThreadTerminated) continue;
/* Reference the thread and temporarily unlock the process */
CsrReferenceThread(Thread);
CsrUnlockProcess(Process);
Context.QueryResult = QUERY_RESULT_CONTINUE;
ThreadShutdownNotify(Thread, Flags,
(QUERY_RESULT_ABORT != QueryResult) ? MCS_ENDSESSION : 0,
&Context);
/* Lock the process again and dereference the thread */
CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
CsrDereferenceThread(Thread);
}
Quit:
/* Unlock the process */
CsrUnlockProcess(Process);
#if 0
if (Context.UIThread)
{
if (Context.Dlg)
{
SendMessageW(Context.Dlg, WM_CLOSE, 0, 0);
}
else
{
TerminateThread(Context.UIThread, QUERY_RESULT_ERROR);
}
CloseHandle(Context.UIThread);
}
#endif
/* Kill the process unless we abort shutdown */
if (QueryResult == QUERY_RESULT_ABORT)
return CsrShutdownCancelled;
return CsrShutdownCsrProcess;
}
static NTSTATUS FASTCALL
UserExitReactOS(PCSR_THREAD CsrThread, UINT Flags)
{
NTSTATUS Status;
LUID CallerLuid;
DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess);
DWORD ThreadId = HandleToUlong(CsrThread->ClientId.UniqueThread);
DPRINT1("SrvExitWindowsEx(ClientId: %lx.%lx, Flags: 0x%x)\n",
ProcessId, ThreadId, Flags);
/*
* Check for flags validity
*/
if (Flags & EWX_CALLER_WINLOGON)
{
/* Only Winlogon can call this */
if (ProcessId != LogonProcessId)
{
DPRINT1("SrvExitWindowsEx call not from Winlogon\n");
return STATUS_ACCESS_DENIED;
}
}
/* Implicitely add the shutdown flag when we poweroff or reboot */
if (Flags & (EWX_POWEROFF | EWX_REBOOT))
Flags |= EWX_SHUTDOWN;
/*
* Impersonate and retrieve the caller's LUID so that
* we can only shutdown processes in its context.
*/
if (!CsrImpersonateClient(NULL))
return STATUS_BAD_IMPERSONATION_LEVEL;
Status = CsrGetProcessLuid(NULL, &CallerLuid);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to get caller LUID, Status = 0x%08x\n", Status);
goto Quit;
}
DPRINT("Caller LUID is: %lx.%lx\n", CallerLuid.HighPart, CallerLuid.LowPart);
/* Shutdown loop */
while (TRUE)
{
/* Notify Win32k and potentially Winlogon of the shutdown */
Status = NtUserSetInformationThread(CsrThread->ThreadHandle,
UserThreadInitiateShutdown,
&Flags, sizeof(Flags));
DPRINT("Win32k says: %lx\n", Status);
switch (Status)
{
/* We cannot wait here, the caller should start a new thread */
case STATUS_CANT_WAIT:
DPRINT1("NtUserSetInformationThread returned STATUS_CANT_WAIT\n");
goto Quit;
/* Shutdown is in progress */
case STATUS_PENDING:
DPRINT1("NtUserSetInformationThread returned STATUS_PENDING\n");
goto Quit;
/* Abort */
case STATUS_RETRY:
{
DPRINT1("NtUserSetInformationThread returned STATUS_RETRY\n");
UNIMPLEMENTED;
continue;
}
default:
{
if (!NT_SUCCESS(Status))
{
// FIXME: Use some UserSetLastNTError or SetLastNtError
// that we have defined for user32 or win32k usage only...
SetLastError(RtlNtStatusToDosError(Status));
goto Quit;
}
}
}
/* All good */
break;
}
/*
* OK we can continue. Now magic happens:
*
* Terminate all Win32 processes, stop if we find one kicking
* and screaming it doesn't want to die.
*
* This function calls the ShutdownProcessCallback callback of
* each CSR server for each Win32 process.
*/
Status = CsrShutdownProcesses(&CallerLuid, Flags);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to shutdown processes, Status = 0x%08x\n", Status);
}
// FIXME: If Status == STATUS_CANCELLED, call RecordShutdownReason
/* Tell Win32k and potentially Winlogon that we're done */
NtUserSetInformationThread(CsrThread->ThreadHandle,
UserThreadEndShutdown,
&Status, sizeof(Status));
DPRINT("SrvExitWindowsEx returned 0x%08x\n", Status);
Quit:
/* We are done */
CsrRevertToSelf();
return Status;
}
ULONG
NTAPI
UserClientShutdown(IN PCSR_PROCESS CsrProcess,
IN ULONG Flags,
IN BOOLEAN FirstPhase)
{
ULONG result;
DPRINT("UserClientShutdown(0x%p, 0x%x, %s) - [0x%x, 0x%x], ShutdownFlags: %lu\n",
CsrProcess, Flags, FirstPhase ? "FirstPhase" : "LastPhase",
CsrProcess->ClientId.UniqueProcess, CsrProcess->ClientId.UniqueThread,
CsrProcess->ShutdownFlags);
/*
* Check for process validity
*/
/* Do not kill system processes when a user is logging off */
if ((Flags & EWX_SHUTDOWN) == EWX_LOGOFF &&
(CsrProcess->ShutdownFlags & (SHUTDOWN_OTHERCONTEXT | SHUTDOWN_SYSTEMCONTEXT)))
{
DPRINT("Do not kill a system process in a logoff request!\n");
return CsrShutdownNonCsrProcess;
}
/* Do not kill Winlogon */
if (CsrProcess->ClientId.UniqueProcess == UlongToHandle(LogonProcessId))
{
DPRINT("Not killing Winlogon; CsrProcess->ShutdownFlags = %lu\n",
CsrProcess->ShutdownFlags);
/* Returning CsrShutdownCsrProcess means that we handled this process by doing nothing */
/* This will mark winlogon as processed so consrv won't be notified again for it */
CsrDereferenceProcess(CsrProcess);
return CsrShutdownCsrProcess;
}
/* Notify the process for shutdown if needed */
result = NotifyUserProcessForShutdown(CsrProcess, &ShutdownSettings, Flags);
if (result == CsrShutdownCancelled || result == CsrShutdownNonCsrProcess)
{
if (result == CsrShutdownCancelled)
DPRINT1("Process 0x%x aborted shutdown\n", CsrProcess->ClientId.UniqueProcess);
return result;
}
/* Terminate this process */
#if DBG
{
WCHAR buffer[MAX_PATH];
if (!GetProcessImageFileNameW(CsrProcess->ProcessHandle, buffer, ARRAYSIZE(buffer)))
{
DPRINT1("Terminating process %x\n", CsrProcess->ClientId.UniqueProcess);
}
else
{
DPRINT1("Terminating process %x (%S)\n", CsrProcess->ClientId.UniqueProcess, buffer);
}
}
#endif
NtTerminateProcess(CsrProcess->ProcessHandle, 0);
WaitForSingleObject(CsrProcess->ProcessHandle, ShutdownSettings.ProcessTerminateTimeout);
/* We are done */
CsrDereferenceProcess(CsrProcess);
return CsrShutdownCsrProcess;
}
/* PUBLIC SERVER APIS *********************************************************/
/* API_NUMBER: UserpExitWindowsEx */
CSR_API(SrvExitWindowsEx)
{
NTSTATUS Status;
PUSER_EXIT_REACTOS ExitReactOSRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactOSRequest;
Status = NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseActiveDesktop,
NULL,
0);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to set thread desktop!\n");
return Status;
}
Status = UserExitReactOS(CsrGetClientThread(), ExitReactOSRequest->Flags);
ExitReactOSRequest->Success = NT_SUCCESS(Status);
ExitReactOSRequest->LastError = GetLastError();
NtUserSetInformationThread(NtCurrentThread(), UserThreadRestoreDesktop, NULL, 0);
return Status;
}
/* API_NUMBER: UserpEndTask */
CSR_API(SrvEndTask)
{
PUSER_END_TASK EndTaskRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.EndTaskRequest;
NTSTATUS Status;
// FIXME: This is HACK-plemented!!
DPRINT1("SrvEndTask is HACKPLEMENTED!!\n");
Status = NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseActiveDesktop,
NULL,
0);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to set thread desktop!\n");
return Status;
}
SendMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
// PostMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
if (IsWindow(EndTaskRequest->WndHandle))
{
if (EndTaskRequest->Force)
{
EndTaskRequest->Success = DestroyWindow(EndTaskRequest->WndHandle);
EndTaskRequest->LastError = GetLastError();
}
else
{
EndTaskRequest->Success = FALSE;
}
}
else
{
EndTaskRequest->Success = TRUE;
EndTaskRequest->LastError = ERROR_SUCCESS;
}
NtUserSetInformationThread(NtCurrentThread(), UserThreadRestoreDesktop, NULL, 0);
return STATUS_SUCCESS;
}
/* API_NUMBER: UserpRecordShutdownReason */
CSR_API(SrvRecordShutdownReason)
{
DPRINT1("%s not yet implemented\n", __FUNCTION__);
return STATUS_NOT_IMPLEMENTED;
}
/* EOF */