test: exitwin.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# exitwin.c

Go to the documentation of this file.

    00001 /**************************** Module Header ********************************\
    00002 * Module Name: exitwin.c
    00003 *
    00004 * Copyright (c) 1985 - 1999, Microsoft Corporation
    00005 *
    00006 * NT: Logoff user
    00007 * DOS: Exit windows
    00008 *
    00009 * History:
    00010 * 07-23-92 ScottLu      Created.
    00011 \***************************************************************************/
    00012
    00013 #include "precomp.h"
    00014 #pragma hdrstop
    00015
    00016 #define BEGIN_LPC_RECV(API)                                                 \
    00017     P##API##MSG a = (P##API##MSG)&m->u.ApiMessageData;                      \
    00018     PCSR_THREAD pcsrt;                                                      \
    00019     PTEB Teb = NtCurrentTeb();                                              \
    00020     NTSTATUS Status = STATUS_SUCCESS;                                       \
    00021     UNREFERENCED_PARAMETER(ReplyStatus);                                    \
    00022                                                                             \
    00023     Teb->LastErrorValue = 0;                                                \
    00024     pcsrt = CSR_SERVER_QUERYCLIENTTHREAD();
    00025
    00026 #define END_LPC_RECV()                                                  \
    00027     a->dwLastError = Teb->LastErrorValue;                               \
    00028     return Status;
    00029
    00030 #define CCHMSGMAX   256
    00031 #define CCHBODYMAX  512
    00032
    00033 #define CSR_THREAD_SHUTDOWNSKIP 0x00000008
    00034
    00035 BOOL WowExitTask(PCSR_THREAD pcsrt);
    00036 NTSTATUS UserClientShutdown(PCSR_PROCESS pcsrp, ULONG dwFlags, BOOLEAN fFirstPass);
    00037
    00038 /***************************************************************************\
    00039 * _ExitWindowsEx
    00040 *
    00041 * Determines whether shutdown is allowed, and if so calls CSR to start
    00042 * shutting down processes. If this succeeds all the way through, tell winlogon
    00043 * so it'll either logoff or reboot the system. Shuts down the processes in
    00044 * the caller's sid.
    00045 *
    00046 * History
    00047 * 07-23-92 ScottLu      Created.
    00048 \***************************************************************************/
    00049
    00050 NTSTATUS _ExitWindowsEx(
    00051     PCSR_THREAD pcsrt,
    00052     UINT dwFlags,
    00053     DWORD dwReserved)
    00054 {
    00055     LUID luidCaller;
    00056     NTSTATUS Status = STATUS_SUCCESS;
    00057
    00058     UNREFERENCED_PARAMETER(dwReserved);
    00059
    00060     if ((dwFlags & EWX_REBOOT) || (dwFlags & EWX_POWEROFF)) {
    00061         dwFlags |= EWX_SHUTDOWN;
    00062     }
    00063
    00064     //
    00065     // Only winlogon gets to set the high flags:
    00066     //
    00067
    00068     if ( ( dwFlags & ( ~ ( EWX_VALID ) ) ) != 0 )
    00069     {
    00070         if ( HandleToUlong(pcsrt->ClientId.UniqueProcess) != gIdLogon )
    00071         {
    00072             KdPrint(( "Process %x tried to call ExitWindowsEx with flags %x\n",
    00073                         pcsrt->ClientId.UniqueProcess, dwFlags ));
    00074
    00075             return STATUS_ACCESS_DENIED ;
    00076         }
    00077     }
    00078
    00079     /*
    00080      * Find out the callers sid. Only want to shutdown processes in the
    00081      * callers sid.
    00082      */
    00083     if (!CsrImpersonateClient(NULL)) {
    00084         return STATUS_BAD_IMPERSONATION_LEVEL;
    00085     }
    00086
    00087     Status = CsrGetProcessLuid(NULL, &luidCaller);
    00088     if (!NT_SUCCESS(Status)) {
    00089         CsrRevertToSelf();
    00090         return Status;
    00091     }
    00092
    00093     /*
    00094      * Loop until we can do the shutdown; if we cannot do it,
    00095      *  we'll go to fastexit and bail.
    00096      */
    00097     while (TRUE) {
    00098
    00099         LARGE_INTEGER li;
    00100
    00101         Status = NtUserSetInformationThread(pcsrt->ThreadHandle,
    00102                                             UserThreadInitiateShutdown,
    00103                                             &dwFlags, sizeof(dwFlags));
    00104
    00105         switch (Status) {
    00106         case STATUS_PENDING:
    00107             /*
    00108              * The logoff/shutdown is in progress and nothing
    00109              * more needs to be done.
    00110              */
    00111             goto fastexit;
    00112
    00113         case STATUS_RETRY:
    00114             /*
    00115              * Another logoff/shutdown is in progress and we need
    00116              * to cancel it so we can do an override.
    00117              *
    00118              * if someone else is trying to cancel shutdown, exit
    00119              */
    00120             EnterCrit();
    00121             li.QuadPart  = 0;
    00122             if (NtWaitForSingleObject(gheventCancel, FALSE, &li) == WAIT_OBJECT_0) {
    00123                 Status = STATUS_PENDING;
    00124                 LeaveCrit();
    00125                 goto fastexit;
    00126             }
    00127             /*
    00128              * If no one will set gheventCancelled, don't wait.
    00129              */
    00130             if (gdwThreadEndSession == 0) {
    00131                 LeaveCrit();
    00132                 continue;
    00133             }
    00134
    00135             NtClearEvent(gheventCancelled);
    00136             NtSetEvent(gheventCancel, NULL);
    00137             LeaveCrit();
    00138             /*
    00139              * Wait for the other guy to be cancelled
    00140              */
    00141             NtWaitForSingleObject(gheventCancelled, FALSE, NULL);
    00142
    00143             EnterCrit();
    00144             /*
    00145              * This signals that we are no longer trying to cancel a
    00146              * shutdown
    00147              */
    00148             NtClearEvent(gheventCancel);
    00149             /*
    00150              * If someone managed to start a shutdown again, exit
    00151              *  Can this happen? Let's assert to check it out.
    00152              */
    00153             if (gdwThreadEndSession != 0) {
    00154                 UserAssert(gdwThreadEndSession == 0);
    00155                 Status = STATUS_PENDING;
    00156                 LeaveCrit();
    00157                 goto fastexit;
    00158             }
    00159             LeaveCrit();
    00160             continue;
    00161
    00162         case STATUS_CANT_WAIT:
    00163
    00164             /*
    00165              * There is no notify window and the calling thread has
    00166              * windows that prevent this request from succeeding.
    00167              * The client handles this by starting another thread
    00168              * to recall ExitWindowsEx.
    00169              */
    00170             goto fastexit;
    00171
    00172         default:
    00173             if (!NT_SUCCESS(Status)) {
    00174                 SetLastError(RtlNtStatusToDosError(Status));
    00175                 goto fastexit;
    00176             }
    00177         }
    00178         break;
    00179     }
    00180
    00181     /*
    00182      * This thread is doing the shutdown
    00183      */
    00184     EnterCrit();
    00185     UserAssert(gdwThreadEndSession == 0);
    00186     gdwThreadEndSession = HandleToUlong(pcsrt->ClientId.UniqueThread);
    00187     LeaveCrit();
    00188
    00189     /*
    00190      * Call csr to loop through the processes shutting them down.
    00191      */
    00192     Status = CsrShutdownProcesses(&luidCaller, dwFlags);
    00193     /*
    00194      * Tell win32k.sys we're done.
    00195      */
    00196     NtUserSetInformationThread(pcsrt->ThreadHandle, UserThreadEndShutdown, &Status, sizeof(Status));
    00197
    00198     EnterCrit();
    00199     gdwThreadEndSession = 0;
    00200     NtSetEvent(gheventCancelled, NULL);
    00201     LeaveCrit();
    00202
    00203 fastexit:
    00204     CsrRevertToSelf();
    00205
    00206     return Status;
    00207 }
    00208
    00209 /***************************************************************************\
    00210 * UserClientShutdown
    00211 *
    00212 * This gets called from CSR. If we recognize the application (i.e., it has a
    00213 * top level window), then send queryend/end session messages to it. Otherwise
    00214 * say we don't recognize it.
    00215 *
    00216 * 07-23-92 ScottLu      Created.
    00217 \***************************************************************************/
    00218
    00219 NTSTATUS UserClientShutdown(
    00220     PCSR_PROCESS pcsrp,
    00221     ULONG dwFlags,
    00222     BOOLEAN fFirstPass)
    00223 {
    00224     PLIST_ENTRY ListHead, ListNext;
    00225     PCSR_PROCESS Process;
    00226     PCSR_THREAD Thread;
    00227     USERTHREAD_SHUTDOWN_INFORMATION ShutdownInfo;
    00228     BOOL fNoMsgs;
    00229     BOOL fNoMsgsEver = TRUE;
    00230     BOOL Forced = FALSE;
    00231     BOOL bDoBlock;
    00232     BOOL fNoRetry;
    00233     DWORD cmd = 0, dwClientFlags;
    00234     NTSTATUS Status;
    00235     NTSTATUS TerminateStatus = STATUS_ACCESS_DENIED;
    00236     UINT cThreads;
    00237     BOOL fSendEndSession = FALSE;
    00238
    00239 #if DBG
    00240     DWORD dwLocalThreadEndSession = gdwThreadEndSession;
    00241 #endif
    00242
    00243     /*
    00244      * If this is a logoff and the process does not belong to
    00245      * the account doing the logoff and is not LocalSystem,
    00246      * do not send end-session messages.  Console will notify
    00247      * the app of the logoff.
    00248      */
    00249     if (!(dwFlags & EWX_SHUTDOWN) && (pcsrp->ShutdownFlags & SHUTDOWN_OTHERCONTEXT)) {
    00250         Status = SHUTDOWN_UNKNOWN_PROCESS;
    00251         goto CleanupAndExit;
    00252     }
    00253
    00254     /*
    00255      * Calculate whether to allow exit and force-exit this process before
    00256      * we unlock pcsrp.
    00257      */
    00258     fNoRetry = (pcsrp->ShutdownFlags & SHUTDOWN_NORETRY) ||
    00259             (dwFlags & EWX_FORCE);
    00260
    00261     /*
    00262      * Setup flags for WM_CLIENTSHUTDOWN
    00263      * -Assume the process is going to OK the WM_QUERYENDSESSION (WMCS_EXIT)
    00264      * -NT's shutdown always starts with a logoff.
    00265      * -Shutdown or logoff? (WMCS_SHUTDOWN)
    00266      * -Should display dialog for hung apps? (WMCS_NODLGIFHUNG)
    00267      * -is this process in the context being logged off? (WMCS_CONTEXTLOGOFF)
    00268      */
    00269     dwClientFlags = WMCS_EXIT | (fNoRetry ? WMCS_NORETRY : 0);
    00270     //}
    00271
    00272     /*
    00273      * Check the flags originally passed by the ExitWindows caller to see if we're
    00274      *  really just logging off.
    00275      */
    00276     if (!(dwFlags & (EWX_WINLOGON_OLD_REBOOT | EWX_WINLOGON_OLD_SHUTDOWN))) {
    00277         dwClientFlags |= WMCS_LOGOFF;
    00278     }
    00279
    00280     if (dwFlags & EWX_FORCEIFHUNG) {
    00281         dwClientFlags |= WMCS_NODLGIFHUNG;
    00282     }
    00283     if (!(pcsrp->ShutdownFlags & (SHUTDOWN_SYSTEMCONTEXT | SHUTDOWN_OTHERCONTEXT))) {
    00284         dwClientFlags |= WMCS_CONTEXTLOGOFF;
    00285     }
    00286
    00287
    00288     /*
    00289      * Lock the process while we walk the thread list.  We know
    00290      * that the process is valid and therefore do not need to
    00291      * check the return status.
    00292      */
    00293     CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process);
    00294
    00295     ShutdownInfo.StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS;
    00296
    00297     /*
    00298      * Go through the thread list and mark them as not
    00299      * shutdown yet.
    00300      */
    00301     ListHead = &pcsrp->ThreadList;
    00302     ListNext = ListHead->Flink;
    00303     while (ListNext != ListHead) {
    00304         Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
    00305         Thread->Flags &= ~CSR_THREAD_SHUTDOWNSKIP;
    00306         ListNext = ListNext->Flink;
    00307     }
    00308
    00309     /*
    00310      * Perform the proper shutdown operation on each thread.  Keep
    00311      * a count of the number of gui threads found.
    00312      */
    00313     cThreads = 0;
    00314     ShutdownInfo.drdRestore.pdeskRestore = NULL;
    00315     ShutdownInfo.drdRestore.hdeskNew = NULL;
    00316     while (TRUE) {
    00317         ListNext = ListHead->Flink;
    00318         while (ListNext != ListHead) {
    00319             Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
    00320             /*
    00321              * Skip the thread doing the shutdown.  Assume that it's
    00322              * ready.
    00323              * gdwThreadEndSession shouldn't change while the shutdown
    00324              *  is in progress; so this should be thread safe.
    00325              */
    00326             UserAssert(gdwThreadEndSession == dwLocalThreadEndSession);
    00327             if (HandleToUlong(Thread->ClientId.UniqueThread) == gdwThreadEndSession) {
    00328                 Thread->Flags |= CSR_THREAD_SHUTDOWNSKIP;
    00329             }
    00330
    00331             if (!(Thread->Flags &
    00332                     (CSR_THREAD_DESTROYED | CSR_THREAD_SHUTDOWNSKIP))) {
    00333                 break;
    00334             }
    00335             ListNext = ListNext->Flink;
    00336         }
    00337         if (ListNext == ListHead)
    00338             break;
    00339
    00340         Thread->Flags |= CSR_THREAD_SHUTDOWNSKIP;
    00341         ShutdownInfo.dwFlags = dwClientFlags;
    00342
    00343         Status = NtUserQueryInformationThread(Thread->ThreadHandle,
    00344                 UserThreadShutdownInformation, &ShutdownInfo, sizeof(ShutdownInfo), NULL);
    00345
    00346         if (!NT_SUCCESS(Status))
    00347             continue;
    00348         if (ShutdownInfo.StatusShutdown == SHUTDOWN_UNKNOWN_PROCESS)
    00349             continue;
    00350         if (ShutdownInfo.StatusShutdown == SHUTDOWN_KNOWN_PROCESS) {
    00351             CsrUnlockProcess(Process);
    00352             Status = SHUTDOWN_KNOWN_PROCESS;
    00353             goto RestoreDesktop;
    00354         }
    00355
    00356         /*
    00357          * If this process is not in the account being logged off and it
    00358          * is not on the windowstation being logged off, don't send
    00359          * the end session messages.
    00360          */
    00361         if (!(dwClientFlags & WMCS_CONTEXTLOGOFF) && (ShutdownInfo.hwndDesktop == NULL)) {
    00362             /*
    00363              * This process is not in the context being logged off.  Do
    00364              * not terminate it and let console send an event to the process.
    00365              */
    00366             ShutdownInfo.StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS;
    00367             continue;
    00368         }
    00369
    00370         /*
    00371          * Shut down this process.
    00372          */
    00373         cThreads++;
    00374
    00375         if (ISTS()) {
    00376             Forced = (dwFlags & EWX_FORCE);
    00377             fNoMsgs =  (pcsrp->ShutdownFlags & SHUTDOWN_NORETRY) ||
    00378                        !(ShutdownInfo.dwFlags & USER_THREAD_GUI);
    00379             fNoMsgsEver &= fNoMsgs;
    00380             if (Forced && (!(dwFlags & EWX_NONOTIFY) || (gSessionId != 0)))  {
    00381                 dwClientFlags &= ~WMCS_LOGOFF; // WinStation Reset or Shutdown. Don't do this for console session.
    00382             }
    00383
    00384             if (fNoMsgs || Forced) {
    00385                 BoostHardError((ULONG_PTR)Thread->ClientId.UniqueProcess, BHE_FORCE);
    00386             }
    00387             bDoBlock = (fNoMsgs == FALSE);
    00388
    00389         } else {
    00390             if (fNoRetry || !(ShutdownInfo.dwFlags & USER_THREAD_GUI)) {
    00391
    00392                 /*
    00393                  * Dispose of any hard errors.
    00394                  */
    00395                 BoostHardError((ULONG_PTR)Thread->ClientId.UniqueProcess, BHE_FORCE);
    00396                 bDoBlock = FALSE;
    00397             } else {
    00398                 bDoBlock = TRUE;
    00399             }
    00400         }
    00401
    00402         if (bDoBlock) {
    00403             CsrReferenceThread(Thread);
    00404             CsrUnlockProcess(Process);
    00405
    00406             /*
    00407              * There are problems in changing shutdown to send all the
    00408              * QUERYENDSESSIONs at once before doing any ENDSESSIONs, like
    00409              * Windows does. The whole machine needs to be modal if you do this.
    00410              * If it isn't modal, then you have this problem. Imagine app 1 and 2.
    00411              * 1 gets the queryendsession, no problem. 2 gets it and brings up a
    00412              * dialog. Now being a simple user, you decide you need to change the
    00413              * document in app 1. Now you switch back to app 2, hit ok, and
    00414              * everything goes away - including app 1 without saving its changes.
    00415              * Also, apps expect that once they've received the QUERYENDSESSION,
    00416              * they are not going to get anything else of any particular interest
    00417              * (unless it is a WM_ENDSESSION with FALSE) We had bugs pre 511 where
    00418              * apps were blowing up because of this.
    00419              * If this change is made, the entire system must be modal
    00420              * while this is going on. - ScottLu 6/30/94
    00421              */
    00422             cmd = ThreadShutdownNotify(dwClientFlags | WMCS_QUERYEND, (ULONG_PTR)Thread, 0);
    00423
    00424             CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process);
    00425             CsrDereferenceThread(Thread);
    00426
    00427             /*
    00428              * If shutdown has been cancelled, let csr know about it.
    00429              */
    00430             switch (cmd) {
    00431             case TSN_USERSAYSCANCEL:
    00432             case TSN_APPSAYSNOTOK:
    00433                 if (!Forced) {
    00434                     dwClientFlags &= ~WMCS_EXIT;
    00435                 }
    00436
    00437                 /*
    00438                  * Fall through.
    00439                  */
    00440             case TSN_APPSAYSOK:
    00441                 fSendEndSession = TRUE;
    00442                 break;
    00443
    00444             case TSN_USERSAYSKILL:
    00445                 /*
    00446                  * Since we cannot just kill one thread, the whole process
    00447                  *  is going down. Hence, there is no point on continuing
    00448                  *  checking other threads. Also, the user wants it killed
    00449                  *  so we won't waste any time sending more messages
    00450                  */
    00451                 dwClientFlags |= WMCS_EXIT;
    00452                 goto KillIt;
    00453
    00454             case TSN_NOWINDOW:
    00455                 /*
    00456                  * Did this process have a window?
    00457                  * If this is the second pass we terminate the process even if it did
    00458                  * not have any windows in case the app was just starting up.
    00459                  * WOW hits this often when because it takes so long to start up.
    00460                  * Logon (with WOW auto-starting) then logoff WOW won't die but will
    00461                  * lock some files open so you can't logon next time.
    00462                  */
    00463                 if (fFirstPass) {
    00464                     cThreads--;
    00465                 }
    00466                 break;
    00467             }
    00468         }
    00469     }
    00470
    00471     /*
    00472      * If end session message need to be sent, do it now.
    00473      */
    00474     if (fSendEndSession) {
    00475
    00476         /*
    00477          * Go through the thread list and mark them as not
    00478          * shutdown yet.
    00479          */
    00480         ListNext = ListHead->Flink;
    00481         while (ListNext != ListHead) {
    00482             Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
    00483             Thread->Flags &= ~CSR_THREAD_SHUTDOWNSKIP;
    00484             ListNext = ListNext->Flink;
    00485         }
    00486
    00487         /*
    00488          * Perform the proper shutdown operation on each thread.
    00489          */
    00490         while (TRUE) {
    00491             ListHead = &pcsrp->ThreadList;
    00492             ListNext = ListHead->Flink;
    00493             while (ListNext != ListHead) {
    00494                 Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
    00495                 if (!(Thread->Flags &
    00496                         (CSR_THREAD_DESTROYED | CSR_THREAD_SHUTDOWNSKIP))) {
    00497                     break;
    00498                 }
    00499                 ListNext = ListNext->Flink;
    00500             }
    00501             if (ListNext == ListHead)
    00502                 break;
    00503
    00504             Thread->Flags |= CSR_THREAD_SHUTDOWNSKIP;
    00505             ShutdownInfo.dwFlags = dwClientFlags;
    00506
    00507             Status = NtUserQueryInformationThread(Thread->ThreadHandle,
    00508                     UserThreadShutdownInformation, &ShutdownInfo, sizeof(ShutdownInfo), NULL);
    00509
    00510             if (!NT_SUCCESS(Status))
    00511                 continue;
    00512
    00513             if (ShutdownInfo.StatusShutdown == SHUTDOWN_UNKNOWN_PROCESS ||
    00514                     !(ShutdownInfo.dwFlags & USER_THREAD_GUI))
    00515                 continue;
    00516
    00517             /*
    00518              * Send the end session messages to the thread.
    00519              */
    00520             CsrReferenceThread(Thread);
    00521             CsrUnlockProcess(Process);
    00522
    00523             /*
    00524              * If the user says kill it, the user wants it to go away now
    00525              * no matter what. If the user didn't say kill, then call again
    00526              * because we need to send WM_ENDSESSION messages.
    00527              */
    00528             ThreadShutdownNotify(dwClientFlags, (ULONG_PTR)Thread, 0);
    00529
    00530             CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process);
    00531             CsrDereferenceThread(Thread);
    00532         }
    00533     }
    00534
    00535 KillIt:
    00536     CsrUnlockProcess(Process);
    00537
    00538     if (ISTS()) {
    00539         bDoBlock = (!fNoMsgsEver && !(dwClientFlags & WMCS_EXIT));
    00540     } else {
    00541         bDoBlock = (!fNoRetry && !(dwClientFlags & WMCS_EXIT));
    00542     }
    00543
    00544     if (bDoBlock) {
    00545         Status = SHUTDOWN_CANCEL;
    00546         goto RestoreDesktop;
    00547     }
    00548
    00549     /*
    00550      * Set the final shutdown status according to the number of gui
    00551      * threads found.  If the count is zero, we have an unknown process.
    00552      */
    00553     if (cThreads == 0)
    00554         ShutdownInfo.StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS;
    00555     else
    00556         ShutdownInfo.StatusShutdown = SHUTDOWN_KNOWN_PROCESS;
    00557
    00558     if (ShutdownInfo.StatusShutdown == SHUTDOWN_UNKNOWN_PROCESS ||
    00559             !(dwClientFlags & WMCS_CONTEXTLOGOFF)) {
    00560
    00561         /*
    00562          * This process is not in the context being logged off.  Do
    00563          * not terminate it and let console send an event to the process.
    00564          */
    00565         Status = SHUTDOWN_UNKNOWN_PROCESS;
    00566
    00567         if (ShutdownInfo.drdRestore.hdeskNew) {
    00568             goto RestoreDesktop;
    00569         }
    00570         goto CleanupAndExit;
    00571     }
    00572
    00573     /*
    00574      * Calling ExitProcess() in the app's context will not always work
    00575      * because the app may have .dll termination deadlocks: so the thread
    00576      * will hang with the rest of the process. To ensure apps go away,
    00577      * we terminate the process with NtTerminateProcess().
    00578      *
    00579      * Pass this special value, DBG_TERMINATE_PROCESS, which tells
    00580      * NtTerminateProcess() to return failure if it can't terminate the
    00581      * process because the app is being debugged.
    00582      */
    00583
    00584     if (ISTS()) {
    00585         NTSTATUS ExitStatus;
    00586         HANDLE DebugPort;
    00587
    00588         ExitStatus = DBG_TERMINATE_PROCESS;
    00589         if (NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(),
    00590                                                  ProcessDebugPort,
    00591                                                  &DebugPort,
    00592                                                  sizeof(HANDLE),
    00593                                                  NULL)) &&
    00594             (DebugPort != NULL)) {
    00595             // Csr is being debugged - go ahead and kill the process
    00596             ExitStatus = 0;
    00597         }
    00598         TerminateStatus = NtTerminateProcess(pcsrp->ProcessHandle, ExitStatus);
    00599     } else {
    00600         TerminateStatus = NtTerminateProcess(pcsrp->ProcessHandle, DBG_TERMINATE_PROCESS);
    00601     }
    00602
    00603     pcsrp->Flags |= CSR_PROCESS_TERMINATED;
    00604
    00605
    00606     /*
    00607      * Let csr know we know about this process - meaning it was our
    00608      * responsibility to shut it down.
    00609      */
    00610     Status = SHUTDOWN_KNOWN_PROCESS;
    00611
    00612 RestoreDesktop:
    00613
    00614     /*
    00615      * Release the desktop that was used.
    00616      */
    00617     {
    00618         USERTHREAD_USEDESKTOPINFO utudi;
    00619         utudi.hThread = NULL;
    00620         RtlCopyMemory(&(utudi.drdRestore), &(ShutdownInfo.drdRestore), sizeof(DESKRESTOREDATA));
    00621
    00622         NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop,
    00623                 &utudi, sizeof(utudi));
    00624     }
    00625
    00626     /*
    00627      * Now that we're done with the process handle, derefence the csr
    00628      * process structure.
    00629      */
    00630     if (Status != SHUTDOWN_UNKNOWN_PROCESS) {
    00631
    00632         /*
    00633                  * If TerminateProcess returned STATUS_ACCESS_DENIED, then the process
    00634                  * is being debugged and it wasn't terminated.Otherwise we need to wait
    00635                  * anyway since TerminateProcess might return failure when the process
    00636                  * is going away (ie STATUS_PROCESS_IS_TERMINATING).If termination
    00637                  * indeed fail, something is wrong anyway so waiting a bit won't
    00638                  * hurt much.
    00639          * If we wait give the process whatever exit timeout value configured
    00640                  * in the registry, but no less  than the 5 second Hung App timeout.
    00641
    00642          */
    00643         if (TerminateStatus != STATUS_ACCESS_DENIED) {
    00644             LARGE_INTEGER li;
    00645
    00646             li.QuadPart = (LONGLONG)-10000 * gdwProcessTerminateTimeout;
    00647             TerminateStatus = NtWaitForSingleObject(pcsrp->ProcessHandle,
    00648                                            FALSE,
    00649                                            &li);
    00650             if (TerminateStatus != STATUS_WAIT_0) {
    00651                 RIPMSG2(RIP_WARNING,
    00652                         "UserClientShutdown: wait for process %x failed with status %x",
    00653                         pcsrp->ClientId.UniqueProcess, TerminateStatus);
    00654             }
    00655         }
    00656
    00657         CsrDereferenceProcess(pcsrp);
    00658     }
    00659
    00660
    00661 CleanupAndExit:
    00662
    00663     return Status;
    00664 }
    00665
    00666 /***************************************************************************\
    00667 * WMCSCallback
    00668 *
    00669 * This function is passed to SendMessageCallback when sending the
    00670 *  WM_CLIENTSHUTDOWN message. It propagates the return value back
    00671 *  if ThreadShutdownNotify is still waiting for it; otherwise,
    00672 *  it just frees the memory.
    00673 *
    00674 * 03-04-97 GerardoB     Created.
    00675 \***************************************************************************/
    00676 VOID CALLBACK WMCSCallback(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
    00677 {
    00678     PWMCSDATA pwmcsd = (PWMCSDATA)dwData;
    00679     if (pwmcsd->dwFlags & WMCSD_IGNORE) {
    00680         LocalFree(pwmcsd);
    00681         return;
    00682     }
    00683
    00684     pwmcsd->dwFlags |= WMCSD_REPLY;
    00685     pwmcsd->dwRet = (DWORD)lResult;
    00686
    00687     UNREFERENCED_PARAMETER(hwnd);
    00688     UNREFERENCED_PARAMETER(uMsg);
    00689 }
    00690 /***************************************************************************\
    00691 * GetInputWindow
    00692 *
    00693 * We assume a thread is waiting for input if it's not hung, the (main)
    00694 *  window is disabled and it owns an enabled popup.
    00695 *
    00696 * 03-06-97 GerardoB     Created.
    00697 \***************************************************************************/
    00698 HWND GetInputWindow (PCSR_THREAD pcsrt, HWND hwnd)
    00699 {
    00700     DWORD dwTimeout;
    00701     HWND hwndPopup;
    00702     /*
    00703      * Ask the kernel if the thread is hung
    00704      */
    00705     dwTimeout = gCmsHungAppTimeout;
    00706     NtUserQueryInformationThread(pcsrt->ThreadHandle,
    00707        UserThreadHungStatus, &dwTimeout, sizeof(dwTimeout), NULL);
    00708
    00709     /*
    00710      * If not hung and disabled, see if it owns an enabled popup
    00711      */
    00712     if (!dwTimeout && !IsWindowEnabled(hwnd)) {
    00713         hwndPopup = GetWindow(hwnd, GW_ENABLEDPOPUP);
    00714         if ((hwndPopup != NULL) && (hwndPopup != hwnd)) {
    00715             return hwndPopup;
    00716         }
    00717     }
    00718
    00719     return NULL;
    00720 }
    00721 /***************************************************************************\
    00722 * GetApplicationText
    00723 *
    00724 * Gets the text that identifies the given window or thread
    00725 *
    00726 * 08-01-97 GerardoB     Created.
    00727 \***************************************************************************/
    00728 void GetApplicationText (HWND hwnd, HANDLE hThread, WCHAR *pwcText, UINT uLen)
    00729 {
    00730     /*
    00731      * GetWindowText doesn't call the hwnd's proc; otherwise, we could
    00732      *  get blocked here for good.
    00733      */
    00734     GetWindowText(hwnd, pwcText, uLen);
    00735     if (*pwcText == 0) {
    00736         /*
    00737          * We couldn't get window title; let's try thread name
    00738          */
    00739         NtUserQueryInformationThread(hThread, UserThreadTaskName,
    00740                                      pwcText, uLen * sizeof(WCHAR), NULL);
    00741     }
    00742 }
    00743 /***************************************************************************\
    00744 * ThreadShutdownNotify
    00745 *
    00746 * This function notifies a given thread that it's time (or about time)
    00747 *  to go away. This is called from _EndTask to post the WM_CLOSE message
    00748 *  or from UserClientShutdown to send the WM_QUERYENDSESSION and
    00749 *  WM_ENDSESSION messages. If the thread fails to respond, then the
    00750 *  "End Application" dialog is brought up. This function is also called
    00751 *  from Console to display that dialog too.
    00752 *
    00753 * 03-07-97 GerardoB     Created to replace SendShutdownMessages,
    00754 *                        MySendEndSessionMessages and DoEndTaskDialog
    00755 \***************************************************************************/
    00756
    00757 DWORD ThreadShutdownNotify(DWORD dwClientFlags, ULONG_PTR dwThread, LPARAM lParam)
    00758 {
    00759     HWND hwnd, hwndOwner;
    00760     PWMCSDATA pwmcsd = NULL;
    00761     HWND hwndDlg;
    00762     ENDDLGPARAMS edp;
    00763     DWORD dwRet, dwRealTimeout, dwTimeout, dwStartTiming, dwCmd;
    00764     MSG msg;
    00765     PCSR_THREAD pcsrt;
    00766     HANDLE hThread;
    00767     BOOL    fEndTaskNow = FALSE;
    00768
    00769 #define ESMH_CANCELEVENT     0
    00770 #define ESMH_THREAD          1
    00771 #define ESMH_HANDLECOUNT     2
    00772     HANDLE ahandles[ESMH_HANDLECOUNT];
    00773
    00774     /*
    00775      * If this is console, just set up the wait loop and
    00776      *   bring the dialog up right away. Otherwise, find
    00777      *   the notification window, notify it and go wait.
    00778      */
    00779     if (dwClientFlags & WMCS_CONSOLE) {
    00780         hThread = (HANDLE)dwThread;
    00781         dwRealTimeout = 0;
    00782         goto SetupWaitLoop;
    00783     } else {
    00784         pcsrt = (PCSR_THREAD)dwThread;
    00785         hThread = pcsrt->ThreadHandle;
    00786         hwnd = (HWND)lParam;
    00787     }
    00788
    00789     /*
    00790      * If no window was provided,
    00791      *  find a top-level window owned by the thread
    00792      */
    00793     if (hwnd == NULL) {
    00794         EnumThreadWindows(HandleToUlong(pcsrt->ClientId.UniqueThread),
    00795                             &FindWindowFromThread, (LPARAM)&hwnd);
    00796     }
    00797     if (hwnd == NULL) {
    00798         return TSN_NOWINDOW;
    00799     }
    00800     /*
    00801      * Find the root owner (we'll guess this is the "main" window)
    00802      */
    00803     while ((hwndOwner = GetWindow(hwnd, GW_OWNER)) != NULL) {
    00804         hwnd = hwndOwner;
    00805     }
    00806
    00807 #if defined(FE_IME)
    00808     /*
    00809      * If this is a console window, then just returns TSN_APPSAYSOK.
    00810      * In this routine:
    00811      * Normally windows NT environment, hwnd never point to console window.
    00812      * However, In ConIme process, its owner window point to console window.
    00813      */
    00814     if (!(dwClientFlags & WMCS_ENDTASK)) {
    00815         if ((HANDLE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE) == ghModuleWin) {
    00816             return TSN_APPSAYSOK;
    00817         }
    00818     }
    00819 #endif
    00820
    00821     /*
    00822      * If this is an EndTask request but the window is disabled,
    00823      *   then we want to bring the dialog up right way (the app
    00824      *   is probably waiting for input).
    00825      * Otherwise, we bring the window to the foreground, send/post
    00826      *  the request and wait
    00827      */
    00828
    00829
    00830     /*
    00831      * Bug 296188 - joejo
    00832      *
    00833      * Make sure we respond to the user ASAP when they
    00834      * attempt to shutdown an application that we know is hung.
    00835      */
    00836
    00837     if ((dwClientFlags & WMCS_ENDTASK)) {
    00838
    00839         dwTimeout = gCmsHungAppTimeout;
    00840         NtUserQueryInformationThread(pcsrt->ThreadHandle, UserThreadHungStatus, &dwTimeout, sizeof(dwTimeout), NULL);
    00841
    00842         if (!IsWindowEnabled(hwnd) || dwTimeout){
    00843             dwRealTimeout = 0;
    00844             fEndTaskNow = TRUE;
    00845         }
    00846
    00847
    00848     }
    00849
    00850     if (!fEndTaskNow) {
    00851
    00852         SetForegroundWindow(hwnd);
    00853         dwRealTimeout = gCmsHungAppTimeout;
    00854         if (dwClientFlags & WMCS_ENDTASK) {
    00855             PostMessage(hwnd, WM_CLOSE, 0, 0);
    00856         } else {
    00857             /*
    00858              * If the shutdown was canceled, we don't need to wait.
    00859              *  (we're just sending the WM_ENDSESSION(FALSE))
    00860              */
    00861             if (!(dwClientFlags & (WMCS_QUERYEND | WMCS_EXIT))) {
    00862                 SendNotifyMessage(hwnd, WM_CLIENTSHUTDOWN, dwClientFlags, 0);
    00863                 return TSN_APPSAYSOK;
    00864             }
    00865             /*
    00866              * Allocate callback data. If out of memory, kill it.
    00867              */
    00868             pwmcsd = (PWMCSDATA)LocalAlloc(LPTR, sizeof(WMCSDATA));
    00869             if (pwmcsd == NULL) {
    00870                 return TSN_USERSAYSKILL;
    00871             }
    00872
    00873             SendMessageCallback(hwnd, WM_CLIENTSHUTDOWN, dwClientFlags, 0,
    00874                                 WMCSCallback, (ULONG_PTR)pwmcsd);
    00875         }
    00876     }
    00877
    00878 SetupWaitLoop:
    00879     /*
    00880      * This tells us if the hwndDlg is valid. This is set/cleared by
    00881      *  EndTaskDlgProc
    00882      */
    00883     ZeroMemory(&edp, sizeof(edp));
    00884     edp.dwFlags = EDPF_NODLG;
    00885     /*
    00886      * Loop until the hwnd replies, the request is canceled
    00887      *  or the thread goes away. It time out, bring up the
    00888      *  dialog and wait until the user tells us what to do.
    00889      */
    00890     *(ahandles + ESMH_CANCELEVENT) = gheventCancel;
    00891     *(ahandles + ESMH_THREAD) = hThread;
    00892     dwStartTiming = GetTickCount();
    00893     dwCmd = 0;
    00894     while (dwCmd == 0) {
    00895         /*
    00896          * Calculate how long we have to wait.
    00897          */
    00898         dwTimeout = dwRealTimeout;
    00899         if ((dwTimeout != 0) && (dwTimeout != INFINITE)) {
    00900             dwTimeout -= (GetTickCount() - dwStartTiming);
    00901             if ((int)dwTimeout < 0) {
    00902                 dwTimeout = 0;
    00903             }
    00904         }
    00905
    00906         dwRet = MsgWaitForMultipleObjects(ESMH_HANDLECOUNT, ahandles, FALSE, dwTimeout, QS_ALLINPUT);
    00907
    00908         switch (dwRet) {
    00909             case WAIT_OBJECT_0 + ESMH_CANCELEVENT:
    00910                 /*
    00911                  * The request has been canceled.
    00912                  */
    00913                 dwCmd = TSN_USERSAYSCANCEL;
    00914                 break;
    00915
    00916             case WAIT_OBJECT_0 + ESMH_THREAD:
    00917                 /*
    00918                  * The thread is gone.
    00919                  */
    00920                 dwCmd = TSN_APPSAYSOK;
    00921                 break;
    00922
    00923             case WAIT_OBJECT_0 + ESMH_HANDLECOUNT:
    00924                 /*
    00925                  * We got some input; process it.
    00926                  */
    00927                 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
    00928                     if ((edp.dwFlags & EDPF_NODLG)
    00929                             || !IsDialogMessage(hwndDlg, &msg)) {
    00930
    00931                         TranslateMessage(&msg);
    00932                         DispatchMessage(&msg);
    00933                     }
    00934                 }
    00935                 /*
    00936                  * If we got a reply to the message, act on it
    00937                  */
    00938                 if ((pwmcsd != NULL) && (pwmcsd->dwFlags & WMCSD_REPLY)) {
    00939
    00940                     switch (pwmcsd->dwRet) {
    00941                         default:
    00942                             /*
    00943                              * If the message was not processed (the thread
    00944                              *  exited) or someone processed it and returned
    00945                              *  a bogus value, just shut them down.
    00946                              * Fall through
    00947                              */
    00948                         case WMCSR_ALLOWSHUTDOWN:
    00949                             /*
    00950                              * We're going to nuke this app, so get rid of
    00951                              *  any pending harderror boxes he might have
    00952                              */
    00953                             BoostHardError((ULONG_PTR)pcsrt->ClientId.UniqueProcess, BHE_FORCE);
    00954                             /*
    00955                              * Fall through.
    00956                              */
    00957                         case WMCSR_DONE:
    00958                             dwCmd = TSN_APPSAYSOK;
    00959                             break;
    00960
    00961                         case WMCSR_CANCEL:
    00962                             dwCmd = TSN_APPSAYSNOTOK;
    00963                             break;
    00964                     }
    00965                 }
    00966                 /*
    00967                  * Else if the dialog is still up, keep waiting for the user
    00968                  *  to tell us what to do
    00969                  */
    00970                 else if (!(edp.dwFlags & EDPF_NODLG)) {
    00971                     break;
    00972                 }
    00973                 /*
    00974                  * Else if the user dismissed the dialog, act on his response
    00975                  */
    00976                 else if (edp.dwFlags & EDPF_RESPONSE) {
    00977                     switch(edp.dwRet) {
    00978                         case IDC_ENDNOW:
    00979                             /*
    00980                              * The user wants us to kill it
    00981                              */
    00982                             dwCmd = TSN_USERSAYSKILL;
    00983                             break;
    00984
    00985                         /* case IDCANCEL: */
    00986                         default:
    00987                             dwCmd = TSN_USERSAYSCANCEL;
    00988                             break;
    00989                     }
    00990                 }
    00991                 break;
    00992
    00993             case WAIT_TIMEOUT:
    00994
    00995                 if (dwClientFlags & WMCS_NORETRY) {
    00996
    00997                     /*
    00998                      * We come here only for Terminal Server case. We return
    00999                      * TSN_APPSAYSOK as Terminal Server 4 did in this case.
    01000                      */
    01001                     UserAssert(ISTS());
    01002
    01003                     dwCmd = TSN_APPSAYSOK;
    01004                     break;
    01005                 }
    01006
    01007
    01008                 /*
    01009                  * Once we time out, we bring up the dialog and let
    01010                  *  its timer take over.
    01011                  */
    01012                 dwRealTimeout = INFINITE;
    01013                 /*
    01014                  * Check if the windows app is waiting for input;
    01015                  *  if not, we assume it is hung for EndTask;
    01016                  *   otherwise we enter a wait mode that brings the
    01017                  *   dialog up just to provide some (waiting) feedback
    01018                  * Console just gets the dialog right away
    01019                  */
    01020                 if (!(dwClientFlags & WMCS_CONSOLE)) {
    01021                     if (BoostHardError((ULONG_PTR)pcsrt->ClientId.UniqueProcess, BHE_TEST)
    01022                            || (GetInputWindow(pcsrt, hwnd) != NULL)) {
    01023
    01024                         edp.dwFlags |= EDPF_INPUT;
    01025                     } else {
    01026                         /*
    01027                          * EWX_FORCEIFHUNG support.
    01028                          * Also, if this is an ExitWindows call and the process is not in
    01029                          * the context being logged off, we won't kill it.
    01030                          * So don't bother asking the user what to do
    01031                          */
    01032                         if ((dwClientFlags & WMCS_NODLGIFHUNG)
    01033                                 || (!(dwClientFlags & WMCS_ENDTASK)
    01034                                         && !(dwClientFlags & WMCS_CONTEXTLOGOFF))) {
    01035
    01036                             dwCmd = TSN_USERSAYSKILL;
    01037                             break;
    01038                         }
    01039                         /*
    01040                          * Hung or Wait?
    01041                          */
    01042                         if (dwClientFlags & WMCS_ENDTASK) {
    01043                             edp.dwFlags |= EDPF_HUNG;
    01044                         } else {
    01045                             edp.dwFlags |= EDPF_WAIT;
    01046                         }
    01047                     }
    01048                 }
    01049
    01050                 /*
    01051                  * If the registry says no dialog, then tell the caller
    01052                  *  the user wants to kill the app.
    01053                  */
    01054                 if (gfAutoEndTask) {
    01055                     dwCmd = TSN_USERSAYSKILL;
    01056                     break;
    01057                 }
    01058                 /*
    01059                  * Setup the parameters needed by EndTaskDlgProc
    01060                  */
    01061                 edp.dwRet = 0;
    01062                 edp.dwClientFlags = dwClientFlags;
    01063                 if (dwClientFlags & WMCS_CONSOLE) {
    01064                     edp.pcsrt = NULL;
    01065                     edp.lParam = lParam;
    01066                 } else {
    01067                     edp.pcsrt = pcsrt;
    01068                     edp.lParam = (LPARAM)hwnd;
    01069                 }
    01070
    01071                 hwndDlg = CreateDialogParam (ghModuleWin, MAKEINTRESOURCE(IDD_ENDTASK),
    01072                                         NULL, EndTaskDlgProc, (LPARAM)(&edp));
    01073                 /*
    01074                  * If we cannot ask the user, then kill the app.
    01075                  */
    01076                 if (hwndDlg == NULL) {
    01077                     edp.dwFlags |= EDPF_NODLG;
    01078                     dwCmd = TSN_USERSAYSKILL;
    01079                     break;
    01080                 }
    01081                 break;
    01082
    01083             default:
    01084                 /*
    01085                  * Unexpected return; something is wrong. Kill the app.
    01086                  */
    01087                 UserAssert(dwRet != dwRet);
    01088                 dwCmd = TSN_USERSAYSKILL;
    01089                 break;
    01090         } /* switch (dwRet) */
    01091     } /* while (dwCmd == 0) */
    01092
    01093
    01094     /*
    01095      * If the dialog is up, nuke it.
    01096      */
    01097     if (!(edp.dwFlags & EDPF_NODLG)) {
    01098         DestroyWindow(hwndDlg);
    01099     }
    01100     /*
    01101      * Make sure pwmcsd is freed or marked to be freed by WMCSCallback
    01102      *  when the reply comes
    01103      */
    01104     if (pwmcsd != NULL) {
    01105         if (pwmcsd->dwFlags & WMCSD_REPLY) {
    01106             LocalFree(pwmcsd);
    01107         } else {
    01108             pwmcsd->dwFlags |= WMCSD_IGNORE;
    01109         }
    01110     }
    01111 #if DBG
    01112     /*
    01113      * If Canceling, let's the name the app that doesn't let us log off
    01114      */
    01115     if ((dwClientFlags & WMCS_EXIT) && (dwCmd == TSN_APPSAYSNOTOK)) {
    01116         WCHAR achTitle[CCHBODYMAX];
    01117         WCHAR *pwcText;
    01118         UserAssert(!(dwClientFlags & WMCS_CONSOLE));
    01119         pwcText = achTitle;
    01120         *(achTitle + CCHBODYMAX - 1) = (WCHAR)0;
    01121         GetApplicationText(hwnd, hThread, pwcText, CCHBODYMAX - 1);
    01122         KdPrint(("Log off canceled by pid:%#lx tid:%#lx - '%ws'.\n",
    01123                     HandleToUlong(pcsrt->ClientId.UniqueProcess),
    01124                     HandleToUlong(pcsrt->ClientId.UniqueThread),
    01125                     pwcText));
    01126     }
    01127 #endif
    01128     /*
    01129      * If we're killing this dude, clean any hard errors.
    01130      * Also if wow takes care of it, then our caller doesn't need to
    01131      */
    01132     if ((dwCmd == TSN_USERSAYSKILL) && !(dwClientFlags & WMCS_CONSOLE)) {
    01133
    01134         BoostHardError((ULONG_PTR)pcsrt->ClientId.UniqueProcess, BHE_FORCE);
    01135
    01136         if (!(pcsrt->Flags & CSR_THREAD_DESTROYED) && WowExitTask(pcsrt)) {
    01137             dwCmd = TSN_APPSAYSOK;
    01138         }
    01139     }
    01140
    01141     return dwCmd;
    01142 }
    01143
    01144 /***************************************************************************\
    01145 * SetEndTaskDlgStatus
    01146 *
    01147 * Displays the appropiate message and shows the dialog
    01148 *
    01149 * 03-11-97 GerardoB     Created
    01150 \***************************************************************************/
    01151 void SetEndTaskDlgStatus(ENDDLGPARAMS *pedp, HWND hwndDlg, UINT uStrId, BOOL fInit)
    01152 {
    01153     BOOL f, fIsWaiting, fWasWaiting;
    01154     WCHAR *pwcText;
    01155
    01156     fWasWaiting = (pedp->uStrId == STR_ENDTASK_WAIT);
    01157     fIsWaiting = (pedp->dwFlags & EDPF_WAIT);
    01158     /*
    01159      * Store the current message id, load it and show it
    01160      */
    01161     pedp->uStrId = uStrId;
    01162     pwcText = ServerLoadString(ghModuleWin, uStrId, NULL, &f);
    01163     if (pwcText != NULL) {
    01164         SetDlgItemText(hwndDlg, IDC_STATUSMSG, pwcText);
    01165         LocalFree(pwcText);
    01166     }
    01167     /*
    01168      * If we haven't decided that the app is hung, set a
    01169      *  timer to keep an eye on it.
    01170      */
    01171     if (!(pedp->dwFlags & EDPF_HUNG) && !(pedp->dwClientFlags & WMCS_CONSOLE)) {
    01172         SetTimer(hwndDlg, IDT_CHECKAPPSTATE, gCmsHungAppTimeout, NULL);
    01173     }
    01174     /*
    01175      * If initializing or switching to/from the wait mode,
    01176      *  set the proper status for IDC_STATUSCANCEL, IDCANCEL,
    01177      *  IDC_ENDNOW and  the start/stop the progress bar.
    01178      *  Invalidate paint if/as needed
    01179      */
    01180     if (fInit || (fIsWaiting ^ fWasWaiting)) {
    01181         RECT rc;
    01182         HWND hwndStatusCancel = GetDlgItem(hwndDlg, IDC_STATUSCANCEL);
    01183         HWND hwndCancelButton = GetDlgItem(hwndDlg, IDCANCEL);
    01184         HWND hwndEndButton = GetDlgItem(hwndDlg, IDC_ENDNOW);
    01185         DWORD dwSwpFlags;
    01186         /*
    01187          * If on wait mode, we hide the cancel button and its
    01188          *  explanatory text. The End button will be moved to
    01189          *  the cancel button position.
    01190          */
    01191         dwSwpFlags = ((fIsWaiting ? SWP_HIDEWINDOW : SWP_SHOWWINDOW)
    01192                                 | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOMOVE
    01193                                 | SWP_NOZORDER | SWP_NOSENDCHANGING
    01194                                 | SWP_NOACTIVATE);
    01195         /*
    01196          * If we're hiding the cancel button, give focus/def id to
    01197          *  the End button.
    01198          * Note that DM_SETDEIF won't do the right thing unless
    01199          *  both Cancel/End buttons are visible.
    01200          */
    01201         if (fIsWaiting) {
    01202             SendMessage(hwndDlg, DM_SETDEFID, IDC_ENDNOW, 0);
    01203             SetFocus(hwndEndButton);
    01204         }
    01205         SetWindowPos(hwndStatusCancel, NULL, 0, 0, 0, 0, dwSwpFlags);
    01206         SetWindowPos(hwndCancelButton, NULL, 0, 0, 0, 0, dwSwpFlags);
    01207         /*
    01208          * If the cancel button is visible, give it focus/def id.
    01209          */
    01210         if (!fIsWaiting) {
    01211             SendMessage(hwndDlg, DM_SETDEFID, IDCANCEL, 0);
    01212             SetFocus(hwndCancelButton);
    01213         }
    01214         /*
    01215          * Initialize progress bar (first time around)
    01216          */
    01217         if (fIsWaiting && (pedp->hbrProgress == NULL)) {
    01218             int iMagic;
    01219             /*
    01220              * Initialize progress bar stuff.
    01221              * The size and location calculations below were made up
    01222              *  to make it look good(?).
    01223              * We need that location on dialog coordiantes since the
    01224              *  progress bar is painted on the dialog's WM_PAINT.
    01225              */
    01226             GetClientRect(hwndStatusCancel, &pedp->rcBar);
    01227             iMagic = (pedp->rcBar.bottom - pedp->rcBar.top) / 4;
    01228             InflateRect(&pedp->rcBar, 0, -iMagic + GetSystemMetrics(SM_CYEDGE));
    01229             pedp->rcBar.right -= (5 * iMagic);
    01230             OffsetRect(&pedp->rcBar, 0, -iMagic);
    01231             MapWindowPoints(hwndStatusCancel, hwndDlg, (LPPOINT)&pedp->rcBar, 2);
    01232             /*
    01233              * Calculate drawing rectangle and dimensions.
    01234              *  We kind of make it look like comctrl's progress bar.
    01235              */
    01236             pedp->rcProgress = pedp->rcBar;
    01237             InflateRect(&pedp->rcProgress, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE));
    01238             pedp->iProgressStop = pedp->rcProgress.right;
    01239             pedp->iProgressWidth = ((2 * (pedp->rcProgress.bottom - pedp->rcProgress.top)) / 3);
    01240
    01241             pedp->rcProgress.right = pedp->rcProgress.left + pedp->iProgressWidth - 1;
    01242
    01243             pedp->hbrProgress = CreateSolidBrush(GetSysColor(COLOR_ACTIVECAPTION));
    01244             /*
    01245              * Remember the End button position.
    01246              */
    01247             GetWindowRect(hwndEndButton, &pedp->rcEndButton);
    01248             MapWindowPoints(NULL, hwndDlg, (LPPOINT)&pedp->rcEndButton, 2);
    01249         }
    01250         /*
    01251          * Start/Stop progress bar and set End button position
    01252          */
    01253         if (fIsWaiting) {
    01254             RECT rcEndButton;
    01255             UINT uTimeout = (gdwHungToKillCount * gCmsHungAppTimeout)
    01256                             / ((pedp->iProgressStop - pedp->rcProgress.left) / pedp->iProgressWidth);
    01257             SetTimer(hwndDlg, IDT_PROGRESS, uTimeout, NULL);
    01258             /*
    01259              * The Cancel and the End buttons might have different widths when
    01260              *  localized. So make sure we position it inside the dialog and
    01261              *  to the right end of the dialog.
    01262              */
    01263             GetWindowRect(hwndCancelButton, &rc);
    01264             GetWindowRect(hwndEndButton, &rcEndButton);
    01265             rc.left = rc.right - (rcEndButton.right - rcEndButton.left);
    01266             MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rc, 2);
    01267         } else if (fWasWaiting) {
    01268             KillTimer(hwndDlg, IDT_PROGRESS);
    01269             rc = pedp->rcEndButton;
    01270         }
    01271         /*
    01272          * Move the End button if needed
    01273          */
    01274         if (fIsWaiting || fWasWaiting) {
    01275             SetWindowPos(hwndEndButton, NULL, rc.left, rc.top, 0, 0,
    01276                             SWP_NOREDRAW | SWP_NOSIZE | SWP_NOACTIVATE
    01277                             | SWP_NOZORDER | SWP_NOSENDCHANGING);
    01278         }
    01279         /*
    01280          * Make sure we repaint if needed
    01281          */
    01282         if (!fInit) {
    01283             InvalidateRect(hwndDlg, NULL, TRUE);
    01284         }
    01285     } /* if (fInit || (fIsWaiting ^ fWasWaiting)) */
    01286     /*
    01287      * If initializing or in hung mode, make sure the user can
    01288      * see the dialog; only bring it to the foreground on
    01289      * initialization (no rude focus stealing)
    01290      */
    01291     if (fInit || (pedp->dwFlags & EDPF_HUNG)) {
    01292         SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0,
    01293                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW
    01294                      | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
    01295
    01296         if (fInit) {
    01297             SetForegroundWindow(hwndDlg);
    01298         }
    01299     }
    01300 }
    01301 /***************************************************************************\
    01302 * EndTaskDlgProc
    01303 *
    01304 * This is the dialog procedure for the dialog that comes up when an app is
    01305 * not responding.
    01306 *
    01307 * 03-06-97 GerardoB     Rewrote it once again. New template though
    01308 * 07-23-92 ScottLu      Rewrote it, but used same dialog template.
    01309 * 04-28-92 JonPa        Created.
    01310 \***************************************************************************/
    01311
    01312 INT_PTR APIENTRY EndTaskDlgProc(
    01313     HWND hwndDlg,
    01314     UINT msg,
    01315     WPARAM wParam,
    01316     LPARAM lParam)
    01317 {
    01318     ENDDLGPARAMS* pedp;
    01319     WCHAR achTitle[CCHBODYMAX];
    01320     WCHAR *pwcText, *pwcTemp;
    01321     UINT uLen;
    01322     UINT uStrId;
    01323     PAINTSTRUCT ps;
    01324     HDC hdc, hdcMem;
    01325     BOOL fIsInput, fWasInput;
    01326 #ifdef USE_MIRRORING
    01327     int iOldLayout;
    01328 #endif
    01329
    01330     switch (msg) {
    01331     case WM_INITDIALOG:
    01332         /*
    01333          * Save parameters
    01334          */
    01335         pedp = (ENDDLGPARAMS*)lParam;
    01336         SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (ULONG_PTR)pedp);
    01337         /*
    01338          * This tells the caller that the dialog is up
    01339          */
    01340         pedp->dwFlags &= ~EDPF_NODLG;
    01341         /*
    01342          * Build the dialog title making sure that
    01343          *  we end up with a NULL terminated string.
    01344          */
    01345         *(achTitle + CCHBODYMAX - 1) = (WCHAR)0;
    01346         uLen = GetWindowText(hwndDlg, achTitle, CCHBODYMAX - 1);
    01347         pwcText = achTitle + uLen;
    01348         uLen = CCHBODYMAX - 1 - uLen;
    01349         /*
    01350          * Console provides the title; we figure it out for windows apps.
    01351          */
    01352         if (pedp->dwClientFlags & WMCS_CONSOLE) {
    01353             pwcTemp = (WCHAR *)pedp->lParam;
    01354             while (uLen-- && (*pwcText++ = *pwcTemp++));
    01355         } else {
    01356             GetApplicationText((HWND)pedp->lParam, pedp->pcsrt->ThreadHandle, pwcText, uLen);
    01357         }
    01358
    01359         SetWindowText(hwndDlg, achTitle);
    01360         /*
    01361          * Get the app's icon: first look for atomIconProperty
    01362          * if not available, try the class icon.
    01363          * else, use the default icon.
    01364          */
    01365         pedp->hIcon = (HICON)GetProp((HWND)pedp->lParam, ICON_PROP_NAME);
    01366
    01367         if (pedp->hIcon == NULL) {
    01368
    01369             pedp->hIcon = (HICON)GetClassLongPtr((HWND)pedp->lParam, GCLP_HICON);
    01370
    01371             if (pedp->hIcon == NULL) {
    01372
    01373                 if (pedp->dwClientFlags & WMCS_CONSOLE) {
    01374                     pedp->hIcon = LoadIcon(ghModuleWin, MAKEINTRESOURCE(IDI_CONSOLE));
    01375                 }
    01376                 else {
    01377                     pedp->hIcon = LoadIcon(NULL, IDI_APPLICATION);
    01378                 }
    01379             }
    01380         }
    01381
    01382         /*
    01383          * Figure out what message the caller wants initially
    01384          */
    01385         if (pedp->dwClientFlags & WMCS_CONSOLE) {
    01386             uStrId = STR_ENDTASK_CONSOLE;
    01387         } else if (pedp->dwFlags & EDPF_INPUT) {
    01388             uStrId = STR_ENDTASK_INPUT;
    01389         } else if (pedp->dwFlags & EDPF_WAIT) {
    01390             uStrId = STR_ENDTASK_WAIT;
    01391         } else {
    01392             uStrId = STR_ENDTASK_HUNG;
    01393         }
    01394         /*
    01395          * Display the message, set the focus and show the dialog
    01396          */
    01397         SetEndTaskDlgStatus(pedp, hwndDlg, uStrId, TRUE);
    01398         return FALSE;
    01399
    01400
    01401     case WM_PAINT:
    01402         pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
    01403         if ((pedp == NULL) || (pedp->hIcon == NULL)) {
    01404             break;
    01405         }
    01406         /*
    01407          * Draw the icon
    01408          */
    01409         hdc = BeginPaint(hwndDlg, &ps);
    01410 #ifdef USE_MIRRORING
    01411         iOldLayout = GetLayout(hdc);
    01412         if (iOldLayout != GDI_ERROR) {
    01413             SetLayout(hdc, iOldLayout|LAYOUT_BITMAPORIENTATIONPRESERVED);
    01414         }
    01415 #endif
    01416         DrawIcon(hdc, ETD_XICON, ETD_YICON, pedp->hIcon);
    01417 #ifdef USE_MIRRORING
    01418         if (iOldLayout != GDI_ERROR) {
    01419             SetLayout(hdc, iOldLayout);
    01420         }
    01421 #endif
    01422         /*
    01423          * If waiting, draw the progress bar;
    01424          *  else draw the warning sign
    01425          */
    01426         if (pedp->dwFlags & EDPF_WAIT) {
    01427             RECT rc;
    01428             /*
    01429              * Draw a client-edge-looking border.
    01430              */
    01431             rc = pedp->rcBar;
    01432             DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_RECT | BF_ADJUST);
    01433             InflateRect(&rc, -1, -1);
    01434             /*
    01435              * Draw the blocks up to current position
    01436              */
    01437             rc.right = rc.left + pedp->iProgressWidth - 1;
    01438             while (rc.left < pedp->rcProgress.left) {
    01439                 if (rc.right > pedp->iProgressStop) {
    01440                     rc.right = pedp->iProgressStop;
    01441                     if (rc.left >= rc.right) {
    01442                         break;
    01443                     }
    01444                 }
    01445                 FillRect(hdc, &rc, pedp->hbrProgress);
    01446                 rc.left += pedp->iProgressWidth;
    01447                 rc.right += pedp->iProgressWidth;
    01448             }
    01449         } else {
    01450             /*
    01451              * Load the bitmap the first time around and
    01452              *  figure out where it goes
    01453             */
    01454             if (pedp->hbmpWarning == NULL) {
    01455                 BITMAP bmp;
    01456                 pedp->hbmpWarning = LoadBitmap(ghModuleWin, MAKEINTRESOURCE(IDB_WARNING));
    01457                 if (GetObject(pedp->hbmpWarning, sizeof(bmp), &bmp)) {
    01458                     pedp->rcWarning.left = ETD_XICON;
    01459                     pedp->rcWarning.top = ETD_XICON + 32 - bmp.bmHeight;
    01460                     pedp->rcWarning.right = bmp.bmWidth;
    01461                     pedp->rcWarning.bottom = bmp.bmHeight;
    01462                 }
    01463             }
    01464             /*
    01465              * Blit it
    01466              */
    01467             hdcMem = CreateCompatibleDC(hdc);
    01468             SelectObject(hdcMem, pedp->hbmpWarning);
    01469             GdiTransparentBlt(hdc, pedp->rcWarning.left, pedp->rcWarning.top,
    01470                    pedp->rcWarning.right, pedp->rcWarning.bottom,
    01471                    hdcMem, 0, 0, pedp->rcWarning.right, pedp->rcWarning.bottom, RGB(255, 0, 255));
    01472             DeleteDC(hdcMem);
    01473         }
    01474
    01475         EndPaint(hwndDlg, &ps);
    01476         return TRUE;
    01477
    01478     case WM_TIMER:
    01479         pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
    01480         if (pedp == NULL) {
    01481             return TRUE;
    01482         }
    01483         switch (wParam) {
    01484         case IDT_CHECKAPPSTATE:
    01485             pedp->dwCheckTimerCount++;
    01486             /*
    01487              * Check if the app has switched from/to a waiting-for-input
    01488              *  mode. If so, update the dialog and wait a little longer
    01489              */
    01490             fIsInput = (BoostHardError((ULONG_PTR)pedp->pcsrt->ClientId.UniqueProcess, BHE_TEST)
    01491                         || (GetInputWindow(pedp->pcsrt, (HWND)pedp->lParam) != NULL));
    01492             fWasInput = (pedp->dwFlags & EDPF_INPUT);
    01493             if (fIsInput ^ fWasInput) {
    01494                 UINT uProgress;
    01495                 pedp->dwFlags &= ~(EDPF_INPUT | EDPF_WAIT);
    01496                 pedp->dwFlags |= (fIsInput ? EDPF_INPUT : EDPF_WAIT);
    01497                 SetEndTaskDlgStatus(pedp, hwndDlg,
    01498                                     (fIsInput ? STR_ENDTASK_INPUT : STR_ENDTASK_WAIT),
    01499                                      FALSE);
    01500                 pedp->dwCheckTimerCount /= 2;
    01501                 uProgress = pedp->rcProgress.left - pedp->rcBar.left - GetSystemMetrics(SM_CXEDGE);
    01502                 uProgress /= 2;
    01503                 pedp->rcProgress.left -= uProgress;
    01504                 pedp->rcProgress.right -= uProgress;
    01505             }
    01506             /*
    01507              * Is it time to declare it hung?
    01508              */
    01509             if (pedp->dwCheckTimerCount >= gdwHungToKillCount) {
    01510                 KillTimer(hwndDlg, IDT_CHECKAPPSTATE);
    01511                 pedp->dwFlags &= ~(EDPF_INPUT | EDPF_WAIT);
    01512                 pedp->dwFlags |= EDPF_HUNG;
    01513                 SetEndTaskDlgStatus(pedp, hwndDlg, STR_ENDTASK_HUNG, FALSE);
    01514             }
    01515         break;
    01516
    01517         case IDT_PROGRESS:
    01518             /*
    01519              * Draw the next block in the progress bar.
    01520              */
    01521             if (pedp->rcProgress.right >= pedp->iProgressStop) {
    01522                 pedp->rcProgress.right = pedp->iProgressStop;
    01523                 if (pedp->rcProgress.left >= pedp->rcProgress.right) {
    01524                     break;
    01525                 }
    01526             }
    01527             hdc = GetDC(hwndDlg);
    01528             FillRect(hdc, &pedp->rcProgress, pedp->hbrProgress);
    01529             ReleaseDC(hwndDlg, hdc);
    01530             pedp->rcProgress.left += pedp->iProgressWidth;
    01531             pedp->rcProgress.right += pedp->iProgressWidth;
    01532         break;
    01533         }
    01534         return TRUE;
    01535
    01536
    01537     case WM_NCACTIVATE:
    01538         /*
    01539          * Make sure we're uncovered when active and not covering the app
    01540          *  when inactive
    01541          */
    01542         pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
    01543         if (pedp != NULL) {
    01544             HWND hwnd;
    01545             if (wParam) {
    01546                 hwnd = HWND_TOPMOST;
    01547             } else if (pedp->dwClientFlags & WMCS_CONSOLE) {
    01548                 hwnd = HWND_TOP;
    01549             } else {
    01550                 hwnd = (HWND)pedp->lParam;
    01551             }
    01552             SetWindowPos(hwndDlg, hwnd,
    01553                          0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
    01554         }
    01555         break;
    01556
    01557
    01558     case WM_COMMAND:
    01559         /*
    01560          * The user has made a choice, we're done.
    01561          */
    01562         pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
    01563         if (pedp != NULL) {
    01564             pedp->dwRet = (DWORD)wParam;
    01565         }
    01566         DestroyWindow(hwndDlg);
    01567         break;
    01568
    01569
    01570     case WM_DESTROY:
    01571         /*
    01572          * We're dead. Make sure the caller knows we're history
    01573          */
    01574         pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
    01575         if (pedp != NULL) {
    01576             pedp->dwFlags |= (EDPF_NODLG | EDPF_RESPONSE);
    01577             if (pedp->hbmpWarning != NULL) {
    01578                 DeleteObject(pedp->hbmpWarning);
    01579             }
    01580             if (pedp->hbrProgress != NULL) {
    01581                 DeleteObject(pedp->hbrProgress);
    01582             }
    01583         }
    01584         break;
    01585     }
    01586
    01587     return FALSE;
    01588 }
    01589
    01590 /***************************************************************************\
    01591 * _EndTask
    01592 *
    01593 * This routine is called from the task manager to end an application - for
    01594 * gui apps, either a win32 app or a win16 app. Note: Multiple console
    01595 * processes can live in a single console window. We'll pass these requests
    01596 * for destruction to console.
    01597 *
    01598 * 07-25-92 ScottLu      Created.
    01599 \***************************************************************************/
    01600
    01601 BOOL _EndTask(
    01602     HWND hwnd,
    01603     BOOL fShutdown,
    01604     BOOL fMeanKill)
    01605 {
    01606     BOOL fRet = TRUE;
    01607     PCSR_THREAD pcsrt = CSR_SERVER_QUERYCLIENTTHREAD();
    01608     PCSR_THREAD pcsrtKill;
    01609     DWORD dwThreadId;
    01610     DWORD dwProcessId;
    01611     LPWSTR lpszMsg;
    01612     BOOL fAllocated;
    01613     DWORD dwCmd;
    01614     USERTHREAD_USEDESKTOPINFO utudi;
    01615     NTSTATUS Status;
    01616
    01617     /*
    01618      * Note: fShutdown isn't used for anything in this routine!
    01619      * They are still there because I haven't removed them: the old endtask
    01620      * code relied on them.
    01621      */
    01622     UNREFERENCED_PARAMETER(fShutdown);
    01623
    01624     /*
    01625      * Set the current work thread to a desktop so we can
    01626      *  go safely into win32k.sys
    01627      */
    01628     utudi.hThread = pcsrt->ThreadHandle;
    01629     utudi.drdRestore.pdeskRestore = NULL;
    01630
    01631     Status = NtUserSetInformationThread(NtCurrentThread(),
    01632             UserThreadUseDesktop, &utudi, sizeof(utudi));
    01633     if (!NT_SUCCESS(Status)) {
    01634         /*
    01635          * We were unable to get the thread's desktop. Game over
    01636          */
    01637         return TRUE;
    01638     }
    01639
    01640
    01641     /*
    01642      * Get the process and thread that owns hwnd.
    01643      */
    01644     dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcessId);
    01645     if (dwThreadId == 0) {
    01646         goto RestoreDesktop;
    01647     }
    01648
    01649     /*
    01650      * Don't allow destruction of winlogon
    01651      */
    01652     if (dwProcessId == gIdLogon) {
    01653         goto RestoreDesktop;
    01654     }
    01655
    01656     /*
    01657      * If this is a console window, then just send the close message to
    01658      * it, and let console clean up the processes in it.
    01659      */
    01660     if ((HANDLE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE) == ghModuleWin) {
    01661         PostMessage(hwnd, WM_CLOSE, 0, 0);
    01662         goto RestoreDesktop;
    01663     }
    01664
    01665     /*
    01666      * Find the CSR_THREAD for the window.
    01667      */
    01668     CsrLockThreadByClientId((HANDLE)LongToHandle( dwThreadId ), &pcsrtKill);
    01669     if (pcsrtKill == NULL) {
    01670         goto RestoreDesktop;
    01671     }
    01672     CsrReferenceThread(pcsrtKill);
    01673     CsrUnlockThread(pcsrtKill);
    01674
    01675     /*
    01676      * If this is a WOW app, then shutdown just this wow application.
    01677      */
    01678     if (!fMeanKill) {
    01679         /*
    01680          * Find out what to do now - did the user cancel or the app cancel,
    01681          * etc? Only allow cancelling if we are not forcing the app to
    01682          * exit.
    01683          */
    01684         dwCmd = ThreadShutdownNotify(WMCS_ENDTASK, (ULONG_PTR)pcsrtKill, (LPARAM)hwnd);
    01685
    01686         switch (dwCmd) {
    01687         case TSN_APPSAYSNOTOK:
    01688             /*
    01689              * App says not ok - this'll let taskman bring up the "are you sure?"
    01690              * dialog to the user.
    01691              */
    01692             CsrDereferenceThread(pcsrtKill);
    01693             fRet = FALSE;
    01694             goto RestoreDesktop;
    01695
    01696         case TSN_USERSAYSCANCEL:
    01697             /*
    01698              * User hit cancel on the timeout dialog - so the user really meant
    01699              * it. Let taskman know everything is ok by returning TRUE.
    01700              */
    01701             CsrDereferenceThread(pcsrtKill);
    01702             goto RestoreDesktop;
    01703         }
    01704     }
    01705
    01706     /*
    01707      * Kill the application now.  If the thread has not been destroyed,
    01708      * nuke the task.  If WowExitTask returns that the thread is not
    01709      * a WOW task, terminate the process.
    01710      */
    01711     if (!(pcsrtKill->Flags & CSR_THREAD_DESTROYED) && !WowExitTask(pcsrtKill)) {
    01712
    01713         BOOL bDoBlock;
    01714
    01715         /*
    01716          * Calling ExitProcess() in the app's context will not always work
    01717          * because the app may have .dll termination deadlocks: so the thread
    01718          * will hang with the rest of the process. To ensure apps go away,
    01719          * we terminate the process with NtTerminateProcess().
    01720          *
    01721          * Pass this special value, DBG_TERMINATE_PROCESS, which tells
    01722          * NtTerminateProcess() to return failure if it can't terminate the
    01723          * process because the app is being debugged.
    01724          */
    01725         if (ISTS()) {
    01726             NTSTATUS ExitStatus;
    01727             HANDLE DebugPort;
    01728
    01729             ExitStatus = DBG_TERMINATE_PROCESS;
    01730             if (NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(),
    01731                                                      ProcessDebugPort,
    01732                                                      &DebugPort,
    01733                                                      sizeof(HANDLE),
    01734                                                      NULL)) &&
    01735                 (DebugPort != NULL)) {
    01736                 // Csr is being debugged - go ahead and kill the process
    01737                 ExitStatus = 0;
    01738             }
    01739             if (!NT_SUCCESS(NtTerminateProcess(pcsrtKill->Process->ProcessHandle,
    01740                     ExitStatus))) {
    01741
    01742                 bDoBlock = TRUE;
    01743             } else {
    01744                 bDoBlock = FALSE;
    01745             }
    01746         } else {
    01747             if (!NT_SUCCESS(NtTerminateProcess(pcsrtKill->Process->ProcessHandle,
    01748                     DBG_TERMINATE_PROCESS))) {
    01749                 bDoBlock = TRUE;
    01750             } else {
    01751                 bDoBlock = FALSE;
    01752             }
    01753         }
    01754
    01755         if (bDoBlock) {
    01756
    01757             /*
    01758              * If the app is being debugged, don't close it - because that can
    01759              * cause a hang to the NtTerminateProcess() call.
    01760              */
    01761             lpszMsg = ServerLoadString(ghModuleWin, STR_APPDEBUGGED,
    01762                     NULL, &fAllocated);
    01763             if (lpszMsg) {
    01764                 MessageBoxEx(NULL, lpszMsg, NULL, MB_OK | MB_SETFOREGROUND, 0);
    01765                 LocalFree(lpszMsg);
    01766             }
    01767         } else {
    01768             pcsrtKill->Process->Flags |= CSR_PROCESS_TERMINATED;
    01769         }
    01770     }
    01771     CsrDereferenceThread(pcsrtKill);
    01772
    01773 RestoreDesktop:
    01774     utudi.hThread = NULL;
    01775     Status = NtUserSetInformationThread(NtCurrentThread(),
    01776             UserThreadUseDesktop, &utudi, sizeof(utudi));
    01777     UserAssert(NT_SUCCESS(Status));
    01778
    01779     return fRet;
    01780 }
    01781
    01782 /***************************************************************************\
    01783 * WowExitTask
    01784 *
    01785 * Calls wow back to make sure a specific task has exited.  Returns
    01786 * TRUE if the thread is a WOW task, FALSE if not.
    01787 *
    01788 * 08-02-92 ScottLu      Created.
    01789 \***************************************************************************/
    01790
    01791 BOOL WowExitTask(
    01792     PCSR_THREAD pcsrt)
    01793 {
    01794     HANDLE ahandle[2];
    01795     USERTHREAD_WOW_INFORMATION WowInfo;
    01796     NTSTATUS Status;
    01797
    01798     ahandle[1] = gheventCancel;
    01799
    01800     /*
    01801      * Query task id and exit function.
    01802      */
    01803     Status = NtUserQueryInformationThread(pcsrt->ThreadHandle,
    01804             UserThreadWOWInformation, &WowInfo, sizeof(WowInfo), NULL);
    01805     if (!NT_SUCCESS(Status))
    01806         return FALSE;
    01807
    01808     /*
    01809      * If no task id was returned, it is not a WOW task
    01810      */
    01811     if (WowInfo.hTaskWow == 0)
    01812         return FALSE;
    01813
    01814     /*
    01815      * Try to make it exit itself. This will work most of the time.
    01816      * If this doesn't work, terminate this process.
    01817      */
    01818     ahandle[0] = InternalCreateCallbackThread(pcsrt->Process->ProcessHandle,
    01819                                               (ULONG_PTR)WowInfo.lpfnWowExitTask,
    01820                                               (ULONG_PTR)WowInfo.hTaskWow);
    01821     if (ahandle[0] == NULL) {
    01822         NtTerminateProcess(pcsrt->Process->ProcessHandle, 0);
    01823         pcsrt->Process->Flags |= CSR_PROCESS_TERMINATED;
    01824         goto Exit;
    01825     }
    01826
    01827     WaitForMultipleObjects(2, ahandle, FALSE, INFINITE);
    01828     NtClose(ahandle[0]);
    01829
    01830 Exit:
    01831     return TRUE;
    01832 }
    01833
    01834 /***************************************************************************\
    01835 * InternalWaitCancel
    01836 *
    01837 * Console calls this to wait for objects or shutdown to be cancelled
    01838 *
    01839 * 29-Oct-1992 mikeke    Created
    01840 \***************************************************************************/
    01841
    01842 DWORD InternalWaitCancel(
    01843     HANDLE handle,
    01844     DWORD dwMilliseconds)
    01845 {
    01846     HANDLE ahandle[2];
    01847
    01848     ahandle[0] = handle;
    01849     ahandle[1] = gheventCancel;
    01850
    01851     return WaitForMultipleObjects(2, ahandle, FALSE, dwMilliseconds);
    01852 }
    01853
    01854
    01855 /***************************************************************************\
    01856 * InternalCreateCallbackThread
    01857 *
    01858 * This routine creates a remote thread in the context of a given process.
    01859 * It is used to call the console control routine, as well as ExitProcess when
    01860 * forcing an exit. Returns a thread handle.
    01861 *
    01862 * 07-28-92 ScottLu      Created.
    01863 \***************************************************************************/
    01864
    01865 HANDLE InternalCreateCallbackThread(
    01866     HANDLE hProcess,
    01867     ULONG_PTR lpfn,
    01868     ULONG_PTR dwData)
    01869 {
    01870     LONG BasePriority;
    01871     HANDLE hThread, hToken;
    01872     PTOKEN_DEFAULT_DACL lpDaclDefault;
    01873     TOKEN_DEFAULT_DACL daclDefault;
    01874     ULONG cbDacl;
    01875     SECURITY_ATTRIBUTES attrThread;
    01876     SECURITY_DESCRIPTOR sd;
    01877     DWORD idThread;
    01878     NTSTATUS Status;
    01879
    01880     hThread = NULL;
    01881
    01882     Status = NtOpenProcessToken(hProcess, TOKEN_QUERY, &hToken);
    01883     if (!NT_SUCCESS(Status)) {
    01884         KdPrint(("NtOpenProcessToken failed, status = %x\n", Status));
    01885         return NULL;
    01886     }
    01887
    01888     cbDacl = 0;
    01889     NtQueryInformationToken(hToken,
    01890             TokenDefaultDacl,
    01891             &daclDefault,
    01892             sizeof(daclDefault),
    01893             &cbDacl);
    01894
    01895     lpDaclDefault = (PTOKEN_DEFAULT_DACL)LocalAlloc(LMEM_FIXED, cbDacl);
    01896     if (lpDaclDefault == NULL) {
    01897         KdPrint(("LocalAlloc failed for lpDaclDefault"));
    01898         goto closeexit;
    01899     }
    01900
    01901     Status = NtQueryInformationToken(hToken,
    01902             TokenDefaultDacl,
    01903             lpDaclDefault,
    01904             cbDacl,
    01905             &cbDacl);
    01906     if (!NT_SUCCESS(Status)) {
    01907         KdPrint(("NtQueryInformationToken failed, status = %x\n", Status));
    01908         goto freeexit;
    01909     }
    01910
    01911     if (!NT_SUCCESS(RtlCreateSecurityDescriptor(&sd,
    01912             SECURITY_DESCRIPTOR_REVISION1))) {
    01913         UserAssert(FALSE);
    01914         goto freeexit;
    01915     }
    01916
    01917     RtlSetDaclSecurityDescriptor(&sd, TRUE, lpDaclDefault->DefaultDacl, TRUE);
    01918
    01919     attrThread.nLength = sizeof(attrThread);
    01920     attrThread.lpSecurityDescriptor = &sd;
    01921     attrThread.bInheritHandle = FALSE;
    01922
    01923     GetLastError();
    01924     hThread = CreateRemoteThread(hProcess,
    01925         &attrThread,
    01926         0L,
    01927         (LPTHREAD_START_ROUTINE)lpfn,
    01928         (LPVOID)dwData,
    01929         0,
    01930         &idThread);
    01931
    01932     if (hThread != NULL) {
    01933         BasePriority = THREAD_PRIORITY_HIGHEST;
    01934         NtSetInformationThread(hThread,
    01935                                ThreadBasePriority,
    01936                                &BasePriority,
    01937                                sizeof(LONG));
    01938     }
    01939
    01940 freeexit:
    01941     LocalFree((HANDLE)lpDaclDefault);
    01942
    01943 closeexit:
    01944     NtClose(hToken);
    01945
    01946     return hThread;
    01947 }
    01948
    01949 ULONG
    01950 SrvExitWindowsEx(
    01951     IN OUT PCSR_API_MSG m,
    01952     IN OUT PCSR_REPLY_STATUS ReplyStatus)
    01953 {
    01954     BEGIN_LPC_RECV(EXITWINDOWSEX);
    01955
    01956     Status = _ExitWindowsEx(pcsrt, a->uFlags, a->dwReserved);
    01957     a->fSuccess = NT_SUCCESS(Status);
    01958
    01959     END_LPC_RECV();
    01960 }
    01961
    01962 ULONG
    01963 SrvEndTask(
    01964     IN OUT PCSR_API_MSG m,
    01965     IN OUT PCSR_REPLY_STATUS ReplyStatus)
    01966 {
    01967     PENDTASKMSG petm = (PENDTASKMSG)&m->u.ApiMessageData;
    01968     PCSR_THREAD pcsrt;
    01969     PTEB Teb = NtCurrentTeb();
    01970
    01971     Teb->LastErrorValue = 0;
    01972     pcsrt = CSR_SERVER_QUERYCLIENTTHREAD();
    01973     /*
    01974      * Don't block the client so it can respond to messages while we
    01975      *  process this request -- we might bring up the End Application
    01976      *  dialog or the hwnd being shutdown might request some user action.
    01977      */
    01978     if (pcsrt->Process->ClientPort != NULL) {
    01979         m->ReturnValue = STATUS_SUCCESS;
    01980         petm->dwLastError = 0;
    01981         petm->fSuccess = TRUE;
    01982         NtReplyPort(pcsrt->Process->ClientPort, (PPORT_MESSAGE)m);
    01983         *ReplyStatus = CsrServerReplied;
    01984     }
    01985
    01986     petm->fSuccess = _EndTask(petm->hwnd, petm->fShutdown, petm->fForce);
    01987
    01988     petm->dwLastError = Teb->LastErrorValue;
    01989     return STATUS_SUCCESS;
    01990 }
    01991
    01992 /***************************************************************************\
    01993 * IsPrivileged
    01994 *
    01995 * Check to see if the client has the specified privileges
    01996 *
    01997 * History:
    01998 * 01-02-91 JimA       Created.
    01999 \***************************************************************************/
    02000
    02001 BOOL IsPrivileged(
    02002     PPRIVILEGE_SET ppSet)
    02003 {
    02004     HANDLE hToken;
    02005     NTSTATUS Status;
    02006     BOOLEAN bResult = FALSE;
    02007     UNICODE_STRING strSubSystem;
    02008
    02009     /*
    02010      * Impersonate the client
    02011      */
    02012     if (!CsrImpersonateClient(NULL))
    02013         return FALSE;
    02014
    02015     /*
    02016      * Open the client's token
    02017      */
    02018     RtlInitUnicodeString(&strSubSystem, L"USER32");
    02019     if (NT_SUCCESS(Status = NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY,
    02020             (BOOLEAN)TRUE, &hToken))) {
    02021
    02022         /*
    02023          * Perform the check
    02024          */
    02025         Status = NtPrivilegeCheck(hToken, ppSet, &bResult);
    02026         NtPrivilegeObjectAuditAlarm(&strSubSystem, NULL, hToken,
    02027                 0, ppSet, bResult);
    02028         NtClose(hToken);
    02029         if (!bResult) {
    02030             SetLastError(ERROR_ACCESS_DENIED);
    02031         }
    02032     }
    02033     CsrRevertToSelf();
    02034     if (!NT_SUCCESS(Status))
    02035         SetLastError(RtlNtStatusToDosError(Status));
    02036
    02037     /*
    02038      * Return result of privilege check
    02039      */
    02040     return (BOOL)(bResult && NT_SUCCESS(Status));
    02041 }
    02042
    02043 /***************************************************************************\
    02044 * _RegisterServicesProcess
    02045 *
    02046 * Register the services process.
    02047 *
    02048 * History:
    02049 * 05-05-95 BradG         Created.
    02050 \***************************************************************************/
    02051
    02052 ULONG
    02053 SrvRegisterServicesProcess(
    02054     IN OUT PCSR_API_MSG m,
    02055     IN OUT PCSR_REPLY_STATUS ReplyStatus)
    02056 {
    02057     PRIVILEGE_SET psTcb = { 1, PRIVILEGE_SET_ALL_NECESSARY,
    02058         { SE_TCB_PRIVILEGE, 0 }
    02059     };
    02060
    02061     BEGIN_LPC_RECV(REGISTERSERVICESPROCESS);
    02062
    02063     /*
    02064      * Allow only one services process and then only if it has TCB
    02065      * privilege.
    02066      */
    02067     EnterCrit();
    02068     if ((gdwServicesProcessId != 0) || !IsPrivileged(&psTcb)) {
    02069         SetLastError(ERROR_ACCESS_DENIED);
    02070         a->fSuccess = FALSE;
    02071     } else {
    02072         gdwServicesProcessId = a->dwProcessId;
    02073         a->fSuccess = TRUE;
    02074     }
    02075     LeaveCrit();
    02076
    02077     END_LPC_RECV();
    02078 }
    02079
    02080 #if defined(FE_IME)
    02081 /***************************************************************************\
    02082 * IsImeWindow
    02083 *
    02084 * return TRUE if it's IME window
    02085 *
    02086 * History:
    02087 * 06-05-96 KazuM         Created.
    02088 \***************************************************************************/
    02089 BOOL
    02090 IsImeWindow(
    02091     HWND hwnd
    02092     )
    02093 {
    02094     int num;
    02095     WCHAR ClassName[16];
    02096     DWORD ClassStyle;
    02097
    02098     num = GetClassName(hwnd, ClassName, sizeof(ClassName)/sizeof(WCHAR)-1);
    02099     if (num == 0)
    02100         return FALSE;
    02101
    02102     ClassName[num] = L'\0';
    02103     if (wcsncmp(ClassName, L"IME", 3) == 0)
    02104         return TRUE;
    02105
    02106     ClassStyle = GetClassLong(hwnd, GCL_STYLE);
    02107     if (ClassStyle & CS_IME)
    02108         return TRUE;
    02109     else
    02110         return FALSE;
    02111 }
    02112 #endif