[USERSRV] Hard-error improvements 3/7
[reactos.git] / win32ss / user / winsrv / usersrv / shutdown.c
index b0de4d3..5c2f464 100644 (file)
@@ -4,19 +4,14 @@
  * FILE:            win32ss/user/winsrv/usersrv/shutdown.c
  * PURPOSE:         Logout/shutdown
  * PROGRAMMERS:
- *
- * NOTE: The shutdown code must be rewritten completely. (hbelusca)
  */
 
 /* INCLUDES *******************************************************************/
 
 #include "usersrv.h"
 
-#include <stdlib.h>
-#include <winreg.h>
-#include <winlogon.h>
 #include <commctrl.h>
-#include <sddl.h>
+#include <psapi.h>
 
 #include "resource.h"
 
 
 /* GLOBALS ********************************************************************/
 
-typedef struct tagSHUTDOWN_SETTINGS
-{
-    BOOL AutoEndTasks;
-    DWORD HungAppTimeout;
-    DWORD WaitToKillAppTimeout;
-} SHUTDOWN_SETTINGS, *PSHUTDOWN_SETTINGS;
+// 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
 
-#define DEFAULT_AUTO_END_TASKS           FALSE
-#define DEFAULT_HUNG_APP_TIMEOUT         5000
-#define DEFAULT_WAIT_TO_KILL_APP_TIMEOUT 20000
+// 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
 {
-    DWORD ProcessId;
     UINT Msg;
     WPARAM wParam;
     LPARAM lParam;
-    HDESK Desktop;
-    HDESK OldDesktop;
+    // HDESK Desktop;
+    // HDESK OldDesktop;
     DWORD StartTime;
     DWORD QueryResult;
     HWND Dlg;
@@ -52,7 +47,6 @@ typedef struct tagNOTIFY_CONTEXT
     HANDLE UIThread;
     HWND WndClient;
     PSHUTDOWN_SETTINGS ShutdownSettings;
-    LPTHREAD_START_ROUTINE SendMessageProc;
 } NOTIFY_CONTEXT, *PNOTIFY_CONTEXT;
 
 #define QUERY_RESULT_ABORT    0
@@ -72,28 +66,31 @@ typedef struct tagMESSAGE_CONTEXT
     DWORD Timeout;
 } MESSAGE_CONTEXT, *PMESSAGE_CONTEXT;
 
-typedef struct tagPROCESS_ENUM_CONTEXT
-{
-    UINT ProcessCount;
-    PCSR_PROCESS *ProcessData;
-    TOKEN_ORIGIN TokenOrigin;
-    DWORD ShellProcess;
-    DWORD CsrssProcess;
-} PROCESS_ENUM_CONTEXT, *PPROCESS_ENUM_CONTEXT;
-
 
 /* FUNCTIONS ******************************************************************/
 
-/*
-NTSTATUS FASTCALL
-Win32CsrEnumProcesses(CSRSS_ENUM_PROCESS_PROC EnumProc,
-                      PVOID Context)
+static HMODULE hComCtl32Lib = NULL;
+
+static VOID
+CallInitCommonControls(VOID)
 {
-    return CsrEnumProcesses(EnumProc, Context);
+    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
+static VOID FASTCALL
 UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext)
 {
     DWORD Passed;
@@ -120,18 +117,18 @@ EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
     switch(Msg)
     {
     case WM_INITDIALOG:
-        NotifyContext = (PNOTIFY_CONTEXT) lParam;
+        NotifyContext = (PNOTIFY_CONTEXT)lParam;
         NotifyContext->EndNowResult = QUERY_RESULT_ABORT;
-        SetWindowLongPtrW(Dlg, DWLP_USER, (LONG_PTR) lParam);
+        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 (NULL != Title)
+        if (Title)
         {
             Len = GetWindowTextW(Dlg, Title, TitleLength + 1);
             SendMessageW(NotifyContext->WndClient, WM_GETTEXT,
-                         TitleLength + 1 - Len, (LPARAM) (Title + Len));
+                         TitleLength + 1 - Len, (LPARAM)(Title + Len));
             SetWindowTextW(Dlg, Title);
             HeapFree(UserServerHeap, 0, Title);
         }
@@ -144,7 +141,7 @@ EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
         break;
 
     case WM_TIMER:
-        NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER);
+        NotifyContext = (PNOTIFY_CONTEXT)GetWindowLongPtrW(Dlg, DWLP_USER);
         ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS);
         UpdateProgressBar(ProgressBar, NotifyContext);
         Result = TRUE;
@@ -153,8 +150,9 @@ EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
     case WM_COMMAND:
         if (BN_CLICKED == HIWORD(wParam) && IDC_END_NOW == LOWORD(wParam))
         {
-            NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER);
+            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;
         }
@@ -165,12 +163,14 @@ EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
         break;
 
     case WM_CLOSE:
+        MY_DPRINT("WM_CLOSE\n");
         DestroyWindow(Dlg);
         Result = TRUE;
         break;
 
     case WM_DESTROY:
-        NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER);
+        MY_DPRINT("WM_DESTROY\n");
+        NotifyContext = (PNOTIFY_CONTEXT)GetWindowLongPtrW(Dlg, DWLP_USER);
         NotifyContext->Dlg = NULL;
         KillTimer(Dlg, 0);
         PostQuitMessage(NotifyContext->EndNowResult);
@@ -185,47 +185,26 @@ EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
     return Result;
 }
 
-static void
-CallInitCommonControls()
-{
-    static BOOL Initialized = FALSE;
-    HMODULE Lib;
-    INITCOMMONCONTROLS_PROC InitProc;
-
-    if (Initialized) return;
-
-    Lib = LoadLibraryW(L"COMCTL32.DLL");
-    if (NULL == Lib) return;
-
-    InitProc = (INITCOMMONCONTROLS_PROC) GetProcAddress(Lib, "InitCommonControls");
-    if (NULL == InitProc) return;
-
-    (*InitProc)();
-
-    Initialized = TRUE;
-}
-
 static DWORD WINAPI
 EndNowThreadProc(LPVOID Parameter)
 {
-    PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) Parameter;
+    PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT)Parameter;
     MSG Msg;
 
-    SetThreadDesktop(NotifyContext->Desktop);
-    SwitchDesktop(NotifyContext->Desktop);
+    // SetThreadDesktop(NotifyContext->Desktop);
+    // SwitchDesktop(NotifyContext->Desktop);
     CallInitCommonControls();
     NotifyContext->Dlg = CreateDialogParam(UserServerDllInstance,
                                            MAKEINTRESOURCE(IDD_END_NOW), NULL,
-                                           EndNowDlgProc, (LPARAM) NotifyContext);
-    if (NULL == NotifyContext->Dlg)
-    {
+                                           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))
+        if (!IsDialogMessage(NotifyContext->Dlg, &Msg))
         {
             TranslateMessage(&Msg);
             DispatchMessageW(&Msg);
@@ -236,145 +215,134 @@ EndNowThreadProc(LPVOID Parameter)
 }
 
 static DWORD WINAPI
-SendQueryEndSession(LPVOID Parameter)
+SendClientShutdown(LPVOID Parameter)
 {
-    PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter;
+    PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT)Parameter;
     DWORD_PTR Result;
 
-    if (SendMessageTimeoutW(Context->Wnd, WM_QUERYENDSESSION, Context->wParam,
-                            Context->lParam, SMTO_NORMAL, Context->Timeout,
-                            &Result))
+    /* If the shutdown is aborted, just notify the process, there is no need to wait */
+    if ((Context->wParam & (MCS_QUERYENDSESSION | MCS_ENDSESSION)) == 0)
     {
-        return Result ? QUERY_RESULT_CONTINUE : QUERY_RESULT_ABORT;
+        DPRINT("Called WM_CLIENTSHUTDOWN with wParam == 0 ...\n");
+        SendNotifyMessageW(Context->Wnd, WM_CLIENTSHUTDOWN,
+                           Context->wParam, Context->lParam);
+        return QUERY_RESULT_CONTINUE;
     }
 
-    return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR;
-}
-
-static DWORD WINAPI
-SendEndSession(LPVOID Parameter)
-{
-    PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter;
-    DWORD_PTR Result;
-
-    if (Context->wParam)
+    if (SendMessageTimeoutW(Context->Wnd, WM_CLIENTSHUTDOWN,
+                            Context->wParam, Context->lParam,
+                            SMTO_NORMAL, Context->Timeout, &Result))
     {
-        if (SendMessageTimeoutW(Context->Wnd, WM_ENDSESSION, Context->wParam,
-                                Context->lParam, SMTO_NORMAL, Context->Timeout,
-                                &Result))
+        DWORD Ret;
+
+        if (Context->wParam & MCS_QUERYENDSESSION)
         {
-            return QUERY_RESULT_CONTINUE;
+            /* WM_QUERYENDSESSION case */
+            switch (Result)
+            {
+                case MCSR_DONOTSHUTDOWN:
+                    Ret = QUERY_RESULT_ABORT;
+                    break;
+
+                case MCSR_GOODFORSHUTDOWN:
+                case MCSR_SHUTDOWNFINISHED:
+                default:
+                    Ret = QUERY_RESULT_CONTINUE;
+            }
         }
-        return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR;
-    }
-    else
-    {
-        SendMessage(Context->Wnd, WM_ENDSESSION, Context->wParam,
-                    Context->lParam);
-        return 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 CALLBACK
-NotifyTopLevelEnum(HWND Wnd, LPARAM lParam)
+static BOOL
+NotifyTopLevelWindow(HWND Wnd, PNOTIFY_CONTEXT NotifyContext)
 {
-    PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) lParam;
     MESSAGE_CONTEXT MessageContext;
     DWORD Now, Passed;
     DWORD Timeout, WaitStatus;
-    DWORD ProcessId;
     HANDLE MessageThread;
     HANDLE Threads[2];
 
-    if (0 == GetWindowThreadProcessId(Wnd, &ProcessId))
+    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)
     {
-        NotifyContext->QueryResult = QUERY_RESULT_ERROR;
-        return FALSE;
+        MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
     }
-
-    if (ProcessId == NotifyContext->ProcessId)
+    if (Passed < MessageContext.Timeout)
     {
-        Now = GetTickCount();
-        if (0 == NotifyContext->StartTime)
+        MessageContext.Timeout -= Passed;
+        MessageThread = CreateThread(NULL, 0, SendClientShutdown,
+                                     (LPVOID)&MessageContext, 0, NULL);
+        if (MessageThread == NULL)
         {
-            NotifyContext->StartTime = Now;
+            NotifyContext->QueryResult = QUERY_RESULT_ERROR;
+            return FALSE;
         }
-        /* 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)
+        Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
+        if (Passed < Timeout)
         {
-            MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
+            Timeout -= Passed;
+            WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE);
         }
-        if (Passed < MessageContext.Timeout)
+        else
         {
-            MessageContext.Timeout -= Passed;
-            MessageThread = CreateThread(NULL, 0, NotifyContext->SendMessageProc,
-                                         (LPVOID) &MessageContext, 0, NULL);
-            if (NULL == MessageThread)
-            {
-                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)
             {
-                WaitStatus = WAIT_TIMEOUT;
+                NotifyContext->UIThread = CreateThread(NULL, 0,
+                                                       EndNowThreadProc,
+                                                       (LPVOID)NotifyContext,
+                                                       0, NULL);
             }
-            if (WAIT_TIMEOUT == WaitStatus)
+            Threads[0] = MessageThread;
+            Threads[1] = NotifyContext->UIThread;
+            WaitStatus = WaitForMultipleObjectsEx(NotifyContext->UIThread == NULL ?
+                                                  1 : 2,
+                                                  Threads, FALSE, INFINITE,
+                                                  FALSE);
+            if (WaitStatus == WAIT_OBJECT_0)
             {
-                NotifyContext->WndClient = Wnd;
-                if (NULL == NotifyContext->UIThread && NotifyContext->ShowUI)
-                {
-                    NotifyContext->UIThread = CreateThread(NULL, 0,
-                                                           EndNowThreadProc,
-                                                           (LPVOID) NotifyContext,
-                                                           0, NULL);
-                }
-                Threads[0] = MessageThread;
-                Threads[1] = NotifyContext->UIThread;
-                WaitStatus = WaitForMultipleObjectsEx(NULL == NotifyContext->UIThread ?
-                                                      1 : 2,
-                                                      Threads, FALSE, INFINITE,
-                                                      FALSE);
-                if (WAIT_OBJECT_0 == WaitStatus)
-                {
-                    if (! GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
-                    {
-                        NotifyContext->QueryResult = QUERY_RESULT_ERROR;
-                    }
-                }
-                else if (WAIT_OBJECT_0 + 1 == WaitStatus)
-                {
-                    if (! GetExitCodeThread(NotifyContext->UIThread,
-                                            &NotifyContext->QueryResult))
-                    {
-                        NotifyContext->QueryResult = QUERY_RESULT_ERROR;
-                    }
-                }
-                else
+                if (!GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
                 {
                     NotifyContext->QueryResult = QUERY_RESULT_ERROR;
                 }
-                if (WAIT_OBJECT_0 != WaitStatus)
-                {
-                    TerminateThread(MessageThread, QUERY_RESULT_TIMEOUT);
-                }
             }
-            else if (WAIT_OBJECT_0 == WaitStatus)
+            else if (WaitStatus == WAIT_OBJECT_0 + 1)
             {
-                if (! GetExitCodeThread(MessageThread,
-                                        &NotifyContext->QueryResult))
+                if (!GetExitCodeThread(NotifyContext->UIThread,
+                                       &NotifyContext->QueryResult))
                 {
                     NotifyContext->QueryResult = QUERY_RESULT_ERROR;
                 }
@@ -383,65 +351,40 @@ NotifyTopLevelEnum(HWND Wnd, LPARAM lParam)
             {
                 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
             }
-            CloseHandle(MessageThread);
+            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_TIMEOUT;
+            NotifyContext->QueryResult = QUERY_RESULT_ERROR;
         }
+        CloseHandle(MessageThread);
     }
-
-    return QUERY_RESULT_CONTINUE == NotifyContext->QueryResult;
-}
-
-static BOOL CALLBACK
-NotifyDesktopEnum(LPWSTR DesktopName, LPARAM lParam)
-{
-    PNOTIFY_CONTEXT Context = (PNOTIFY_CONTEXT) lParam;
-
-    Context->Desktop = OpenDesktopW(DesktopName, 0, FALSE,
-                                    DESKTOP_ENUMERATE | DESKTOP_SWITCHDESKTOP);
-    if (NULL == Context->Desktop)
+    else
     {
-        DPRINT1("OpenDesktop failed with error %d\n", GetLastError());
-        Context->QueryResult = QUERY_RESULT_ERROR;
-        return FALSE;
+        NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT;
     }
 
-    Context->OldDesktop = GetThreadDesktop(GetCurrentThreadId());
-    SwitchDesktop(Context->Desktop);
-
-    EnumDesktopWindows(Context->Desktop, NotifyTopLevelEnum, lParam);
-
-    SwitchDesktop(Context->OldDesktop);
-
-    CloseDesktop(Context->Desktop);
-
-    return QUERY_RESULT_CONTINUE == Context->QueryResult;
+    DPRINT("NotifyContext->QueryResult == %d\n", NotifyContext->QueryResult);
+    return (NotifyContext->QueryResult == QUERY_RESULT_CONTINUE);
 }
 
-static BOOL FASTCALL
-NotifyTopLevelWindows(PNOTIFY_CONTEXT Context)
+static BOOLEAN
+IsConsoleMode(VOID)
 {
-    HWINSTA WindowStation;
-
-    WindowStation = GetProcessWindowStation();
-    if (NULL == WindowStation)
-    {
-        DPRINT1("GetProcessWindowStation failed with error %d\n", GetLastError());
-        return TRUE;
-    }
-
-    EnumDesktopsW(WindowStation, NotifyDesktopEnum, (LPARAM) Context);
-
-    return TRUE;
+    return (BOOLEAN)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE);
 }
 
-static BOOL
-DtbgIsDesktopVisible(VOID)
-{
-    return !((BOOL)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE));
-}
 
 /* TODO: Find an other way to do it. */
 #if 0
@@ -454,11 +397,10 @@ ConioConsoleCtrlEventTimeout(DWORD Event, PCSR_PROCESS ProcessData, DWORD Timeou
 
     if (ProcessData->CtrlDispatcher)
     {
-
         Thread = CreateRemoteThread(ProcessData->ProcessHandle, NULL, 0,
                                     (LPTHREAD_START_ROUTINE) ProcessData->CtrlDispatcher,
                                     UlongToPtr(Event), 0, NULL);
-        if (NULL == Thread)
+        if (Thread == NULL)
         {
             DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError());
             return;
@@ -470,484 +412,439 @@ ConioConsoleCtrlEventTimeout(DWORD Event, PCSR_PROCESS ProcessData, DWORD Timeou
 #endif
 /************************************************/
 
-static BOOL FASTCALL
-NotifyAndTerminateProcess(PCSR_PROCESS ProcessData,
-                          PSHUTDOWN_SETTINGS ShutdownSettings,
-                          UINT Flags)
-{
-    NOTIFY_CONTEXT Context;
-    HANDLE Process;
-    DWORD QueryResult = QUERY_RESULT_CONTINUE;
 
-    Context.QueryResult = QUERY_RESULT_CONTINUE;
+static VOID
+ThreadShutdownNotify(IN PCSR_THREAD CsrThread,
+                     IN ULONG Flags,
+                     IN ULONG Flags2,
+                     IN PNOTIFY_CONTEXT Context)
+{
+    HWND TopWnd = NULL;
 
-    if (0 == (Flags & EWX_FORCE))
+    EnumThreadWindows(HandleToUlong(CsrThread->ClientId.UniqueThread),
+                      FindTopLevelWnd, (LPARAM)&TopWnd);
+    if (TopWnd)
     {
-        // TODO: Find an other way whether or not the process has a console.
-#if 0
-        if (NULL != ProcessData->Console)
-        {
-            ConioConsoleCtrlEventTimeout(CTRL_LOGOFF_EVENT, ProcessData,
-                                         ShutdownSettings->WaitToKillAppTimeout);
-        }
-        else
-#endif
-        {
-            Context.ProcessId = (DWORD_PTR) ProcessData->ClientId.UniqueProcess;
-            Context.wParam = 0;
-            Context.lParam = (0 != (Flags & EWX_INTERNAL_FLAG_LOGOFF) ?
-                              ENDSESSION_LOGOFF : 0);
-            Context.StartTime = 0;
-            Context.UIThread = NULL;
-            Context.ShowUI = DtbgIsDesktopVisible();
-            Context.Dlg = NULL;
-            Context.ShutdownSettings = ShutdownSettings;
-            Context.SendMessageProc = SendQueryEndSession;
-
-            NotifyTopLevelWindows(&Context);
-
-            Context.wParam = (QUERY_RESULT_ABORT != Context.QueryResult);
-            Context.lParam = (0 != (Flags & EWX_INTERNAL_FLAG_LOGOFF) ?
-                              ENDSESSION_LOGOFF : 0);
-            Context.SendMessageProc = SendEndSession;
-            Context.ShowUI = DtbgIsDesktopVisible() &&
-                             (QUERY_RESULT_ABORT != Context.QueryResult);
-            QueryResult = Context.QueryResult;
-            Context.QueryResult = QUERY_RESULT_CONTINUE;
-
-            NotifyTopLevelWindows(&Context);
+        HWND hWndOwner;
 
-            if (NULL != Context.UIThread)
-            {
-                if (NULL != Context.Dlg)
-                {
-                    SendMessageW(Context.Dlg, WM_CLOSE, 0, 0);
-                }
-                else
-                {
-                    TerminateThread(Context.UIThread, QUERY_RESULT_ERROR);
-                }
-                CloseHandle(Context.UIThread);
-            }
-        }
+        /*** FOR TESTING PURPOSES ONLY!! ***/
+        HWND tmpWnd;
+        tmpWnd = TopWnd;
+        /***********************************/
 
-        if (QUERY_RESULT_ABORT == QueryResult)
+        while ((hWndOwner = GetWindow(TopWnd, GW_OWNER)) != NULL)
         {
-            return FALSE;
+            MY_DPRINT("GetWindow(TopWnd, GW_OWNER) not returned NULL...\n");
+            TopWnd = hWndOwner;
         }
+        if (TopWnd != tmpWnd) MY_DPRINT("(TopWnd = %x) != (tmpWnd = %x)\n", TopWnd, tmpWnd);
     }
+    if (TopWnd == NULL)
+        return;
 
-    /* Terminate this process */
-    Process = OpenProcess(PROCESS_TERMINATE, FALSE,
-                          (DWORD_PTR) ProcessData->ClientId.UniqueProcess);
-    if (NULL == Process)
-    {
-        DPRINT1("Unable to open process %d, error %d\n", ProcessData->ClientId.UniqueProcess,
-                GetLastError());
-        return TRUE;
-    }
-    TerminateProcess(Process, 0);
-    CloseHandle(Process);
-
-    return TRUE;
-}
+    Context->wParam = Flags2;
+    Context->lParam = (0 != (Flags & EWX_CALLER_WINLOGON_LOGOFF) ?
+                       ENDSESSION_LOGOFF : 0);
 
-#if 0
-static NTSTATUS WINAPI
-ExitReactosProcessEnum(PCSR_PROCESS ProcessData, PVOID Data)
-{
-    HANDLE Process;
-    HANDLE Token;
-    TOKEN_ORIGIN Origin;
-    DWORD ReturnLength;
-    PPROCESS_ENUM_CONTEXT Context = (PPROCESS_ENUM_CONTEXT) Data;
-    PCSR_PROCESS *NewData;
-
-    /* Do not kill winlogon or csrss */
-    if ((DWORD_PTR) ProcessData->ClientId.UniqueProcess == Context->CsrssProcess ||
-            ProcessData->ClientId.UniqueProcess == LogonProcessId)
-    {
-        return STATUS_SUCCESS;
-    }
-
-    /* Get the login session of this process */
-    Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
-                          (DWORD_PTR) ProcessData->ClientId.UniqueProcess);
-    if (NULL == Process)
-    {
-        DPRINT1("Unable to open process %d, error %d\n", ProcessData->ClientId.UniqueProcess,
-                GetLastError());
-        return STATUS_UNSUCCESSFUL;
-    }
+    Context->StartTime = 0;
+    Context->UIThread = NULL;
+    Context->ShowUI = !IsConsoleMode() && (Flags2 & (MCS_QUERYENDSESSION | MCS_ENDSESSION));
+    Context->Dlg = NULL;
 
-    if (! OpenProcessToken(Process, TOKEN_QUERY, &Token))
+#if 0 // Obviously, switching desktops like that from within WINSRV doesn't work...
     {
-        DPRINT1("Unable to open token for process %d, error %d\n",
-                ProcessData->ClientId.UniqueProcess, GetLastError());
-        CloseHandle(Process);
-        return STATUS_UNSUCCESSFUL;
+    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());
     }
-    CloseHandle(Process);
+#endif
 
-    if (! GetTokenInformation(Token, TokenOrigin, &Origin,
-                              sizeof(TOKEN_ORIGIN), &ReturnLength))
-    {
-        DPRINT1("GetTokenInformation failed for process %d with error %d\n",
-                ProcessData->ClientId.UniqueProcess, GetLastError());
-        CloseHandle(Token);
-        return STATUS_UNSUCCESSFUL;
-    }
-    CloseHandle(Token);
+    NotifyTopLevelWindow(TopWnd, Context);
 
-    /* This process will be killed if it's in the correct logon session */
-    if (RtlEqualLuid(&(Context->TokenOrigin.OriginatingLogonSession),
-                     &(Origin.OriginatingLogonSession)))
+/******************************************************************************/
+#if 1
+    if (Context->UIThread)
     {
-        /* Kill the shell process last */
-        if ((DWORD_PTR) ProcessData->ClientId.UniqueProcess == Context->ShellProcess)
+        MY_DPRINT("Context->UIThread != NULL\n");
+        if (Context->Dlg)
         {
-            ProcessData->ShutdownLevel = 0;
+            MY_DPRINT("Sending WM_CLOSE because Dlg is != NULL\n");
+            SendMessageW(Context->Dlg, WM_CLOSE, 0, 0);
         }
-        NewData = HeapAlloc(UserServerHeap, 0, (Context->ProcessCount + 1)
-                            * sizeof(PCSR_PROCESS));
-        if (NULL == NewData)
-        {
-            return STATUS_NO_MEMORY;
-        }
-        if (0 != Context->ProcessCount)
+        else
         {
-            memcpy(NewData, Context->ProcessData,
-                   Context->ProcessCount * sizeof(PCSR_PROCESS));
-            HeapFree(UserServerHeap, 0, Context->ProcessData);
+            MY_DPRINT("Terminating UIThread thread with QUERY_RESULT_ERROR\n");
+            TerminateThread(Context->UIThread, QUERY_RESULT_ERROR);
         }
-        Context->ProcessData = NewData;
-        Context->ProcessData[Context->ProcessCount] = ProcessData;
-        Context->ProcessCount++;
+        CloseHandle(Context->UIThread);
+        /**/Context->UIThread = NULL;/**/
+        /**/Context->Dlg = NULL;/**/
     }
+#endif
+/******************************************************************************/
 
-    return STATUS_SUCCESS;
-}
+#if 0
+    MY_DPRINT("Switch back to old desktop 0x%x\n", Context->OldDesktop);
+    SwitchDesktop(Context->OldDesktop);
+    MY_DPRINT("Switched back ok\n");
 #endif
+}
 
-static int
-ProcessDataCompare(const void *Elem1, const void *Elem2)
+static BOOL
+NotifyProcessForShutdown(PCSR_PROCESS CsrProcess,
+                         PSHUTDOWN_SETTINGS ShutdownSettings,
+                         UINT Flags)
 {
-    const PCSR_PROCESS *ProcessData1 = (PCSR_PROCESS *) Elem1;
-    const PCSR_PROCESS *ProcessData2 = (PCSR_PROCESS *) Elem2;
+    DWORD QueryResult = QUERY_RESULT_CONTINUE;
 
-    if ((*ProcessData1)->ShutdownLevel < (*ProcessData2)->ShutdownLevel)
-    {
-        return +1;
-    }
-    else if ((*ProcessData2)->ShutdownLevel < (*ProcessData1)->ShutdownLevel)
-    {
-        return -1;
-    }
-    else if ((*ProcessData1)->ClientId.UniqueProcess < (*ProcessData2)->ClientId.UniqueProcess)
+    /* In case we make a forced shutdown, just kill the process */
+    if (Flags & EWX_FORCE)
+        return TRUE;
+
+    // TODO: Find an other way whether or not the process has a console.
+#if 0
+    if (CsrProcess->Console)
     {
-        return +1;
+        ConioConsoleCtrlEventTimeout(CTRL_LOGOFF_EVENT, CsrProcess,
+                                     ShutdownSettings->WaitToKillAppTimeout);
     }
-    else if ((*ProcessData2)->ClientId.UniqueProcess < (*ProcessData1)->ClientId.UniqueProcess)
+    else
+#endif
     {
-        return -1;
-    }
+        PCSR_PROCESS Process;
+        PCSR_THREAD Thread;
+        PLIST_ENTRY NextEntry;
 
-    return 0;
-}
+        NOTIFY_CONTEXT Context;
+        Context.ShutdownSettings = ShutdownSettings;
+        Context.QueryResult = QUERY_RESULT_CONTINUE; // We continue shutdown by default.
 
-static DWORD FASTCALL
-GetShutdownSettings(HKEY DesktopKey, LPCWSTR ValueName, DWORD DefaultValue)
-{
-    BYTE ValueBuffer[16];
-    LONG ErrCode;
-    DWORD Type;
-    DWORD ValueSize;
-    UNICODE_STRING StringValue;
-    ULONG Value;
-
-    ValueSize = sizeof(ValueBuffer);
-    ErrCode = RegQueryValueExW(DesktopKey, ValueName, NULL, &Type, ValueBuffer,
-                               &ValueSize);
-    if (ERROR_SUCCESS != ErrCode)
-    {
-        DPRINT("GetShutdownSettings for %S failed with error code %ld\n",
-               ValueName, ErrCode);
-        return DefaultValue;
-    }
+        /* Lock the process */
+        CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
 
-    if (REG_SZ == Type)
-    {
-        RtlInitUnicodeString(&StringValue, (LPCWSTR) ValueBuffer);
-        if (! NT_SUCCESS(RtlUnicodeStringToInteger(&StringValue, 10, &Value)))
+        /* 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)
         {
-            DPRINT1("Unable to convert value %S for setting %S\n",
-                    StringValue.Buffer, ValueName);
-            return DefaultValue;
+            /* 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,
+                                 MCS_QUERYENDSESSION,
+                                 &Context);
+
+            /* 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;/**/
         }
-        return (DWORD) Value;
-    }
-    else if (REG_DWORD == Type)
-    {
-        return *((DWORD *) ValueBuffer);
-    }
 
-    DPRINT1("Unexpected registry type %d for setting %S\n", Type, ValueName);
-    return DefaultValue;
-}
+        QueryResult = Context.QueryResult;
+        MY_DPRINT2("QueryResult = %s\n",
+                   QueryResult == QUERY_RESULT_ABORT ? "Abort" : "Continue");
 
-static void FASTCALL
-LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings)
-{
-    static WCHAR Subkey[] = L"\\Control Panel\\Desktop";
-    LPWSTR StringSid;
-    WCHAR InitialKeyName[128];
-    LPWSTR KeyName;
-    HKEY DesktopKey;
-    LONG ErrCode;
-
-    ShutdownSettings->AutoEndTasks = DEFAULT_AUTO_END_TASKS;
-    ShutdownSettings->HungAppTimeout = DEFAULT_HUNG_APP_TIMEOUT;
-    ShutdownSettings->WaitToKillAppTimeout = DEFAULT_WAIT_TO_KILL_APP_TIMEOUT;
-
-    if (! ConvertSidToStringSidW(Sid, &StringSid))
-    {
-        DPRINT1("ConvertSidToStringSid failed with error %d, using default shutdown settings\n",
-                GetLastError());
-        return;
-    }
-    if (wcslen(StringSid) + wcslen(Subkey) + 1 <=
-            sizeof(InitialKeyName) / sizeof(WCHAR))
-    {
-        KeyName = InitialKeyName;
-    }
-    else
-    {
-        KeyName = HeapAlloc(UserServerHeap, 0,
-                            (wcslen(StringSid) + wcslen(Subkey) + 1) *
-                            sizeof(WCHAR));
-        if (NULL == KeyName)
+        /* Now send the ENDSESSION messages to the threads */
+        MY_DPRINT2("Now sending the ENDSESSION messages...\n");
+
+        NextEntry = CsrProcess->ThreadList.Flink;
+        while (NextEntry != &CsrProcess->ThreadList)
         {
-            DPRINT1("Failed to allocate memory, using default shutdown settings\n");
-            LocalFree(StringSid);
-            return;
+            /* 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);
         }
-    }
-    wcscat(wcscpy(KeyName, StringSid), Subkey);
-    LocalFree(StringSid);
 
-    ErrCode = RegOpenKeyExW(HKEY_USERS, KeyName, 0, KEY_QUERY_VALUE, &DesktopKey);
-    if (KeyName != InitialKeyName)
-    {
-        HeapFree(UserServerHeap, 0, KeyName);
-    }
-    if (ERROR_SUCCESS != ErrCode)
-    {
-        DPRINT1("RegOpenKeyEx failed with error %ld, using default shutdown settings\n", ErrCode);
-        return;
-    }
+Quit:
+        /* Unlock the process */
+        CsrUnlockProcess(Process);
 
-    ShutdownSettings->AutoEndTasks = (BOOL) GetShutdownSettings(DesktopKey, L"AutoEndTasks",
-                                     (DWORD) DEFAULT_AUTO_END_TASKS);
-    ShutdownSettings->HungAppTimeout = GetShutdownSettings(DesktopKey,
-                                       L"HungAppTimeout",
-                                       DEFAULT_HUNG_APP_TIMEOUT);
-    ShutdownSettings->WaitToKillAppTimeout = GetShutdownSettings(DesktopKey,
-            L"WaitToKillAppTimeout",
-            DEFAULT_WAIT_TO_KILL_APP_TIMEOUT);
+#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
+    }
 
-    RegCloseKey(DesktopKey);
+    /* Kill the process unless we abort shutdown */
+    return (QueryResult != QUERY_RESULT_ABORT);
 }
 
 static NTSTATUS FASTCALL
-InternalExitReactos(DWORD ProcessId, DWORD ThreadId, UINT Flags)
+UserExitReactOS(PCSR_THREAD CsrThread, UINT Flags)
 {
-    HANDLE CallerThread;
-    HANDLE CallerToken;
     NTSTATUS Status;
-    PROCESS_ENUM_CONTEXT Context;
-    DWORD ReturnLength;
-    HWND ShellWnd;
-    UINT ProcessIndex;
-    char FixedUserInfo[64];
-    TOKEN_USER *UserInfo;
-    SHUTDOWN_SETTINGS ShutdownSettings;
-
-    if (ProcessId != (DWORD_PTR) LogonProcessId)
-    {
-        DPRINT1("Internal ExitWindowsEx call not from winlogon\n");
-        return STATUS_ACCESS_DENIED;
-    }
+    LUID CallerLuid;
 
-    DPRINT1("FIXME: Need to close all user processes!\n");
-    return STATUS_SUCCESS;
+    DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess);
+    DWORD ThreadId  = HandleToUlong(CsrThread->ClientId.UniqueThread);
 
-    CallerThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, ThreadId);
-    if (NULL == CallerThread)
-    {
-        DPRINT1("OpenThread failed with error %d\n", GetLastError());
-        return STATUS_UNSUCCESSFUL;
-    }
-    if (! OpenThreadToken(CallerThread, TOKEN_QUERY, FALSE, &CallerToken))
+    DPRINT1("SrvExitWindowsEx(ClientId: %lx.%lx, Flags: 0x%x)\n",
+            ProcessId, ThreadId, Flags);
+
+    /*
+     * Check for flags validity
+     */
+
+    if (Flags & EWX_CALLER_WINLOGON)
     {
-        DPRINT1("OpenThreadToken failed with error %d\n", GetLastError());
-        CloseHandle(CallerThread);
-        return STATUS_UNSUCCESSFUL;
+        /* Only Winlogon can call this */
+        if (ProcessId != LogonProcessId)
+        {
+            DPRINT1("SrvExitWindowsEx call not from Winlogon\n");
+            return STATUS_ACCESS_DENIED;
+        }
     }
-    CloseHandle(CallerThread);
 
-    Context.ProcessCount = 0;
-    Context.ProcessData = NULL;
-    if (! GetTokenInformation(CallerToken, TokenOrigin, &Context.TokenOrigin,
-                              sizeof(TOKEN_ORIGIN), &ReturnLength))
+    /* 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("GetTokenInformation failed with error %d\n", GetLastError());
-        CloseHandle(CallerToken);
-        return STATUS_UNSUCCESSFUL;
+        DPRINT1("Unable to get caller LUID, Status = 0x%08x\n", Status);
+        goto Quit;
     }
-    if (! GetTokenInformation(CallerToken, TokenUser, FixedUserInfo,
-                              sizeof(FixedUserInfo), &ReturnLength))
+
+    DPRINT("Caller LUID is: %lx.%lx\n", CallerLuid.HighPart, CallerLuid.LowPart);
+
+    /* Shutdown loop */
+    while (TRUE)
     {
-        if (sizeof(FixedUserInfo) < ReturnLength)
+        /* Notify Win32k and potentially Winlogon of the shutdown */
+        Status = NtUserSetInformationThread(CsrThread->ThreadHandle,
+                                            UserThreadInitiateShutdown,
+                                            &Flags, sizeof(Flags));
+        DPRINT("Win32k says: %lx\n", Status);
+        switch (Status)
         {
-            UserInfo = HeapAlloc(UserServerHeap, 0, ReturnLength);
-            if (NULL == UserInfo)
+            /* 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("Unable to allocate %u bytes for user info\n",
-                        (unsigned) ReturnLength);
-                CloseHandle(CallerToken);
-                return STATUS_NO_MEMORY;
+                DPRINT1("NtUserSetInformationThread returned STATUS_RETRY\n");
+                UNIMPLEMENTED;
+                continue;
             }
-            if (! GetTokenInformation(CallerToken, TokenUser, UserInfo,
-                                      ReturnLength, &ReturnLength))
+
+            default:
             {
-                DPRINT1("GetTokenInformation failed with error %d\n",
-                        GetLastError());
-                HeapFree(UserServerHeap, 0, UserInfo);
-                CloseHandle(CallerToken);
-                return STATUS_UNSUCCESSFUL;
+                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;
+                }
             }
         }
-        else
-        {
-            DPRINT1("GetTokenInformation failed with error %d\n", GetLastError());
-            CloseHandle(CallerToken);
-            return STATUS_UNSUCCESSFUL;
-        }
+
+        /* All good */
+        break;
     }
-    else
+
+    /*
+     * 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))
     {
-        UserInfo = (TOKEN_USER *) FixedUserInfo;
+        DPRINT1("Failed to shutdown processes, Status = 0x%08x\n", Status);
     }
-    CloseHandle(CallerToken);
-    LoadShutdownSettings(UserInfo->User.Sid, &ShutdownSettings);
-    if (UserInfo != (TOKEN_USER *) FixedUserInfo)
+
+    // 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)
+{
+    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)))
     {
-        HeapFree(UserServerHeap, 0, UserInfo);
+        DPRINT("Do not kill a system process in a logoff request!\n");
+        return CsrShutdownNonCsrProcess;
     }
-    Context.CsrssProcess = GetCurrentProcessId();
-    ShellWnd = GetShellWindow();
-    if (NULL == ShellWnd)
+
+    /* Do not kill Winlogon or CSRSS */
+    if (CsrProcess->ClientId.UniqueProcess == NtCurrentProcess() ||
+        CsrProcess->ClientId.UniqueProcess == UlongToHandle(LogonProcessId))
     {
-        DPRINT("No shell present\n");
-        Context.ShellProcess = 0;
+        DPRINT("Not killing %s; CsrProcess->ShutdownFlags = %lu\n",
+                CsrProcess->ClientId.UniqueProcess == NtCurrentProcess() ? "CSRSS" : "Winlogon",
+                CsrProcess->ShutdownFlags);
+
+        return CsrShutdownNonCsrProcess;
     }
-    else if (0 == GetWindowThreadProcessId(ShellWnd, &Context.ShellProcess))
+
+    /* Notify the process for shutdown if needed */
+    if (!NotifyProcessForShutdown(CsrProcess, &ShutdownSettings, Flags))
     {
-        DPRINT1("Can't get process id of shell window\n");
-        Context.ShellProcess = 0;
+        DPRINT1("Process 0x%x aborted shutdown\n", CsrProcess->ClientId.UniqueProcess);
+        /* Abort shutdown */
+        return CsrShutdownCancelled;
     }
 
-    // Status = Win32CsrEnumProcesses(ExitReactosProcessEnum, &Context);
-    if (! NT_SUCCESS(Status))
+    /* Terminate this process */
+#if DBG
     {
-        DPRINT1("Failed to enumerate registered processes, status 0x%x\n",
-                Status);
-        if (NULL != Context.ProcessData)
+        WCHAR buffer[MAX_PATH];
+        if (!GetProcessImageFileNameW(CsrProcess->ProcessHandle, buffer, MAX_PATH))
         {
-            HeapFree(UserServerHeap, 0, Context.ProcessData);
+            DPRINT1("Terminating process %x\n", CsrProcess->ClientId.UniqueProcess);
         }
-        return Status;
-    }
-
-    qsort(Context.ProcessData, Context.ProcessCount, sizeof(PCSR_PROCESS),
-          ProcessDataCompare);
-
-    /* Terminate processes, stop if we find one kicking and screaming it doesn't
-       want to die */
-    Status = STATUS_SUCCESS;
-    for (ProcessIndex = 0;
-            ProcessIndex < Context.ProcessCount && NT_SUCCESS(Status);
-            ProcessIndex++)
-    {
-        if (! NotifyAndTerminateProcess(Context.ProcessData[ProcessIndex],
-                                        &ShutdownSettings, Flags))
+        else
         {
-            Status = STATUS_REQUEST_ABORTED;
+            DPRINT1("Terminating process %x (%S)\n", CsrProcess->ClientId.UniqueProcess, buffer);
         }
     }
+#endif
+    NtTerminateProcess(CsrProcess->ProcessHandle, 0);
 
-    /* Cleanup */
-    if (NULL != Context.ProcessData)
-    {
-        HeapFree(UserServerHeap, 0, Context.ProcessData);
-    }
-
-    return Status;
+    /* We are done */
+    CsrDereferenceProcess(CsrProcess);
+    return CsrShutdownCsrProcess;
 }
 
-static NTSTATUS FASTCALL
-UserExitReactos(DWORD UserProcessId, UINT Flags)
+
+/* PUBLIC SERVER APIS *********************************************************/
+
+CSR_API(SrvExitWindowsEx)
 {
     NTSTATUS Status;
+    PUSER_EXIT_REACTOS ExitReactOSRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactOSRequest;
 
-    /* FIXME Inside 2000 says we should impersonate the caller here */
-    Status = NtUserCallTwoParam(UserProcessId, Flags, TWOPARAM_ROUTINE_EXITREACTOS);
-
-    /* If the message isn't handled, the return value is 0, so 0 doesn't indicate
-       success. Success is indicated by a 1 return value, if anything besides 0
-       or 1 it's a NTSTATUS value */
-    if (1 == Status)
-    {
-        Status = STATUS_SUCCESS;
-    }
-    else if (0 == Status)
-    {
-        Status = STATUS_NOT_IMPLEMENTED;
-    }
+    Status = UserExitReactOS(CsrGetClientThread(), ExitReactOSRequest->Flags);
+    ExitReactOSRequest->Success   = NT_SUCCESS(Status);
+    ExitReactOSRequest->LastError = GetLastError();
 
     return Status;
 }
 
+CSR_API(SrvEndTask)
+{
+    PUSER_END_TASK EndTaskRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.EndTaskRequest;
 
-/* PUBLIC SERVER APIS *********************************************************/
+    // FIXME: This is HACK-plemented!!
+    DPRINT1("SrvEndTask is HACKPLEMENTED!!\n");
 
-CSR_API(SrvExitWindowsEx)
-{
-    PUSER_EXIT_REACTOS ExitReactosRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactosRequest;
+    SendMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
+    // PostMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
 
-    if (0 == (ExitReactosRequest->Flags & EWX_INTERNAL_FLAG))
+    if (IsWindow(EndTaskRequest->WndHandle))
     {
-        return UserExitReactos((DWORD_PTR) ApiMessage->Header.ClientId.UniqueProcess,
-                               ExitReactosRequest->Flags);
+        if (EndTaskRequest->Force)
+        {
+            EndTaskRequest->Success   = DestroyWindow(EndTaskRequest->WndHandle);
+            EndTaskRequest->LastError = GetLastError();
+        }
+        else
+        {
+            EndTaskRequest->Success = FALSE;
+        }
     }
     else
     {
-        return InternalExitReactos((DWORD_PTR) ApiMessage->Header.ClientId.UniqueProcess,
-                                   (DWORD_PTR) ApiMessage->Header.ClientId.UniqueThread,
-                                   ExitReactosRequest->Flags);
+        EndTaskRequest->Success   = TRUE;
+        EndTaskRequest->LastError = ERROR_SUCCESS;
     }
-}
-
-CSR_API(SrvEndTask)
-{
-    DPRINT1("%s not yet implemented\n", __FUNCTION__);
-    return STATUS_NOT_IMPLEMENTED;
-}
 
-CSR_API(SrvLogon)
-{
-    DPRINT1("%s not yet implemented\n", __FUNCTION__);
-    return STATUS_NOT_IMPLEMENTED;
+    return STATUS_SUCCESS;
 }
 
 CSR_API(SrvRecordShutdownReason)