Ejemplo n.º 1
0
/*++
 * @name CsrDereferenceWait
 * @implemented NT4
 *
 * The CsrDereferenceWait routine derefences a CSR Wait Block.
 *
 * @param WaitList
 *        Pointer to the Wait List associated to the wait.

 * @return None.
 *
 * @remarks None.
 *
 *--*/
VOID
NTAPI
CsrDereferenceWait(IN PLIST_ENTRY WaitList)
{
    PLIST_ENTRY NextEntry;
    PCSR_WAIT_BLOCK WaitBlock;

    /* Acquire the Process and Wait Locks */
    CsrAcquireProcessLock();
    CsrAcquireWaitLock();

    /* Set the list pointers */
    NextEntry = WaitList->Flink;

    /* Start the loop */
    while (NextEntry != WaitList)
    {
        /* Get the wait block */
        WaitBlock = CONTAINING_RECORD(NextEntry, CSR_WAIT_BLOCK, WaitList);

        /* Move to the next entry */
        NextEntry = NextEntry->Flink;

        /* Check if there's no Wait Routine */
        if (!WaitBlock->WaitFunction)
        {
            /* Remove it from the Wait List */
            if (WaitBlock->WaitList.Flink)
            {
                RemoveEntryList(&WaitBlock->WaitList);
            }

            /* Remove it from the User Wait List */
            if (WaitBlock->UserWaitList.Flink)
            {
                RemoveEntryList(&WaitBlock->UserWaitList);
            }

            /* Dereference the thread waiting on it */
            CsrDereferenceThread(WaitBlock->WaitThread);

            /* Free the block */
            RtlFreeHeap(CsrHeap, 0, WaitBlock);
        }
    }

    /* Release the locks */
    CsrReleaseWaitLock();
    CsrReleaseProcessLock();
}
Ejemplo n.º 2
0
static ULONG NTAPI
GuiConsoleInputThread(PVOID Param)
{
    NTSTATUS Status;
    PCSR_THREAD pcsrt = NULL;
    PGUI_INIT_INFO GuiInitInfo = (PGUI_INIT_INFO)Param;
    DESKTOP_CONSOLE_THREAD DesktopConsoleThreadInfo;
    ULONG_PTR InputThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread);
    HANDLE hThread = NULL;

    LONG WindowCount = 0;
    MSG msg;

    /*
     * This thread dispatches all the console notifications to the
     * notification window. It is common for all the console windows
     * in a given desktop in a window station.
     */

    /* Assign this console input thread to this desktop */
    DesktopConsoleThreadInfo.DesktopHandle = GuiInitInfo->Desktop; // Duplicated desktop handle
    DesktopConsoleThreadInfo.ThreadId = InputThreadId;
    Status = NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
                                  &DesktopConsoleThreadInfo,
                                  sizeof(DesktopConsoleThreadInfo));
    if (!NT_SUCCESS(Status)) goto Quit;

    /* Connect this CSR thread to the USER subsystem */
    pcsrt = CsrConnectToUser();
    if (pcsrt == NULL) goto Quit;
    hThread = pcsrt->ThreadHandle;

    /* Assign the desktop to this thread */
    if (!SetThreadDesktop(DesktopConsoleThreadInfo.DesktopHandle)) goto Quit;

    /* The thread has been initialized, set the event */
    NtSetEvent(GuiInitInfo->GuiThreadStartupEvent, NULL);
    Status = STATUS_SUCCESS;

    while (GetMessageW(&msg, NULL, 0, 0))
    {
        switch (msg.message)
        {
        case PM_CREATE_CONSOLE:
        {
            PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)msg.lParam;
            PCONSRV_CONSOLE Console = GuiData->Console;
            HWND NewWindow;
            RECT rcWnd;

            DPRINT("PM_CREATE_CONSOLE -- creating window\n");

            NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE,
                                        GUI_CONWND_CLASS,
                                        Console->Title.Buffer,
                                        WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
                                        CW_USEDEFAULT,
                                        CW_USEDEFAULT,
                                        CW_USEDEFAULT,
                                        CW_USEDEFAULT,
                                        GuiData->IsWindowVisible ? HWND_DESKTOP : HWND_MESSAGE,
                                        NULL,
                                        ConSrvDllInstance,
                                        (PVOID)GuiData);
            if (NewWindow == NULL)
            {
                DPRINT1("Failed to create a new console window\n");
                continue;
            }

            ASSERT(NewWindow == GuiData->hWindow);

            InterlockedIncrement(&WindowCount);

            //
            // FIXME: TODO: Move everything there into conwnd.c!OnNcCreate()
            //

            /* Retrieve our real position */
            // See conwnd.c!OnMove()
            GetWindowRect(GuiData->hWindow, &rcWnd);
            GuiData->GuiInfo.WindowOrigin.x = rcWnd.left;
            GuiData->GuiInfo.WindowOrigin.y = rcWnd.top;

            if (GuiData->IsWindowVisible)
            {
                /* Move and resize the window to the user's values */
                /* CAN WE DEADLOCK ?? */
                GuiConsoleMoveWindow(GuiData); // FIXME: This MUST be done via the CreateWindowExW call.
                SendMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
            }

            // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595.
            CreateSysMenu(GuiData->hWindow);

            if (GuiData->IsWindowVisible)
            {
                /* Switch to full-screen mode if necessary */
                // FIXME: Move elsewhere, it cause misdrawings of the window.
                if (GuiData->GuiInfo.FullScreen) SwitchFullScreen(GuiData, TRUE);

                DPRINT("PM_CREATE_CONSOLE -- showing window\n");
                // ShowWindow(NewWindow, (int)GuiData->GuiInfo.ShowWindow);
                ShowWindowAsync(NewWindow, (int)GuiData->GuiInfo.ShowWindow);
                DPRINT("Window showed\n");
            }
            else
            {
                DPRINT("PM_CREATE_CONSOLE -- hidden window\n");
                ShowWindowAsync(NewWindow, SW_HIDE);
            }

            continue;
        }

        case PM_DESTROY_CONSOLE:
        {
            PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)msg.lParam;
            MSG TempMsg;

            /* Exit the full screen mode if it was already set */
            // LeaveFullScreen(GuiData);

            /*
             * Window creation is done using a PostMessage(), so it's possible
             * that the window that we want to destroy doesn't exist yet.
             * So first empty the message queue.
             */
            /*
            while (PeekMessageW(&TempMsg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&TempMsg);
                DispatchMessageW(&TempMsg);
            }*/
            while (PeekMessageW(&TempMsg, NULL, 0, 0, PM_REMOVE)) ;

            if (GuiData->hWindow == NULL) continue;

            DestroyWindow(GuiData->hWindow);

            NtSetEvent(GuiData->hGuiTermEvent, NULL);

            if (InterlockedDecrement(&WindowCount) == 0)
            {
                DPRINT("CONSRV: Going to quit the Input Thread 0x%p\n", InputThreadId);
                goto Quit;
            }

            continue;
        }
        }

        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }

Quit:
    DPRINT("CONSRV: Quit the Input Thread 0x%p, Status = 0x%08lx\n", InputThreadId, Status);

    /* Remove this console input thread from this desktop */
    // DesktopConsoleThreadInfo.DesktopHandle;
    DesktopConsoleThreadInfo.ThreadId = 0;
    NtUserConsoleControl(ConsoleCtrlDesktopConsoleThread,
                         &DesktopConsoleThreadInfo,
                         sizeof(DesktopConsoleThreadInfo));

    /* Close the duplicated desktop handle */
    CloseDesktop(DesktopConsoleThreadInfo.DesktopHandle); // NtUserCloseDesktop

    /* Cleanup CSR thread */
    if (pcsrt)
    {
        if (hThread != pcsrt->ThreadHandle)
            DPRINT1("WARNING!! hThread (0x%p) != pcsrt->ThreadHandle (0x%p), you may expect crashes soon!!\n", hThread, pcsrt->ThreadHandle);

        CsrDereferenceThread(pcsrt);
    }

    /* Exit the thread */
    RtlExitUserThread(Status);
    return 0;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
/*++
 * @name CsrNotifyWaitBlock
 *
 * The CsrNotifyWaitBlock routine calls the wait function for a registered
 * CSR Wait Block, and replies to the attached CSR API Message, if any.
 *
 * @param WaitBlock
 *        Pointer to the CSR Wait Block
 *
 * @param WaitList
 *        Pointer to the wait list for this wait.
 *
 * @param WaitArgument[1-2]
 *        User-defined values to pass to the wait function.
 *
 * @param WaitFlags
 *        Wait flags for this wait.
 *
 * @param DereferenceThread
 *        Specifies whether the CSR Thread should be dereferenced at the
 *        end of this wait.
 *
 * @return TRUE in case of success, FALSE otherwise.
 *
 * @remarks After a wait block is notified, the wait function becomes invalid.
 *
 *--*/
BOOLEAN
NTAPI
CsrNotifyWaitBlock(IN PCSR_WAIT_BLOCK WaitBlock,
                   IN PLIST_ENTRY WaitList,
                   IN PVOID WaitArgument1,
                   IN PVOID WaitArgument2,
                   IN ULONG WaitFlags,
                   IN BOOLEAN DereferenceThread)
{
    /* Call the wait function */
    if ((WaitBlock->WaitFunction)(WaitList,
                                  WaitBlock->WaitThread,
                                  &WaitBlock->WaitApiMessage,
                                  WaitBlock->WaitContext,
                                  WaitArgument1,
                                  WaitArgument2,
                                  WaitFlags))
    {
        /* The wait is done, clear the block */
        WaitBlock->WaitThread->WaitBlock = NULL;

        /* Check for captured arguments */
        if (WaitBlock->WaitApiMessage.CsrCaptureData)
        {
            /* Release them */
            CsrReleaseCapturedArguments(&WaitBlock->WaitApiMessage);
        }

        /* Reply to the port */
        NtReplyPort(WaitBlock->WaitThread->Process->ClientPort,
                    (PPORT_MESSAGE)&WaitBlock->WaitApiMessage);

        /* Check if we should dereference the thread */
        if (DereferenceThread)
        {
            /* Remove it from the Wait List */
            if (WaitBlock->WaitList.Flink)
            {
                RemoveEntryList(&WaitBlock->WaitList);
            }

            /* Remove it from the User Wait List */
            if (WaitBlock->UserWaitList.Flink)
            {
                RemoveEntryList(&WaitBlock->UserWaitList);
            }

            /* Dereference teh thread */
            CsrDereferenceThread(WaitBlock->WaitThread);

            /* Free the wait block */
            RtlFreeHeap(CsrHeap, 0, WaitBlock);
        }
        else
        {
            /* The wait is complete, but the thread is being kept alive */
            WaitBlock->WaitFunction = NULL;
        }
    
        /* The wait suceeded */
        return TRUE;
    }
    
    /* The wait failed */
    return FALSE;
}
Ejemplo n.º 5
0
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;
}