Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3117 lines
104 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: exitwin.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * History:
  7. * 07-23-92 ScottLu Created.
  8. \***************************************************************************/
  9. #include "precomp.h"
  10. #pragma hdrstop
  11. #include <wchar.h>
  12. #include <regstr.h>
  13. #include <reason.h>
  14. #define BEGIN_LPC_RECV(API) \
  15. P##API##MSG a = (P##API##MSG)&m->u.ApiMessageData; \
  16. PCSR_THREAD pcsrt; \
  17. PTEB Teb = NtCurrentTeb(); \
  18. NTSTATUS Status = STATUS_SUCCESS; \
  19. UNREFERENCED_PARAMETER(ReplyStatus); \
  20. \
  21. Teb->LastErrorValue = 0; \
  22. pcsrt = CSR_SERVER_QUERYCLIENTTHREAD();
  23. #define END_LPC_RECV() \
  24. a->dwLastError = Teb->LastErrorValue; \
  25. return Status;
  26. #define CCHBODYMAX 512
  27. #define CSR_THREAD_SHUTDOWNSKIP 0x00000008
  28. #define CSR_THREAD_HANGREPORTED 0x00000010
  29. /*
  30. * SrvRecordShutdownReason globals
  31. */
  32. ULONG g_ShutdownState;
  33. ULONG g_DirtyShutdownMax = 1;
  34. HMODULE g_SnapShotDllHandle;
  35. LONG g_SnapShot = 1;
  36. /*
  37. * Snapshot dll entry function.
  38. */
  39. typedef ULONG (*SNAPSHOTFUNC)(DWORD dwStrings, LPCTSTR *lpStrings, PLONG MaxBuffSize, LPTSTR SnapShotBuff);
  40. BOOL WowExitTask(PCSR_THREAD pcsrt);
  41. NTSTATUS UserClientShutdown(PCSR_PROCESS pcsrp, ULONG dwFlags, BOOLEAN fFirstPass);
  42. BOOL CancelExitWindows(VOID);
  43. /***************************************************************************\
  44. * _ExitWindowsEx
  45. *
  46. * Determines whether shutdown is allowed, and if so calls CSR to start
  47. * shutting down processes. If this succeeds all the way through, tell winlogon
  48. * so it'll either logoff or reboot the system. Shuts down the processes in
  49. * the caller's sid.
  50. *
  51. * History
  52. * 07-23-92 ScottLu Created.
  53. \***************************************************************************/
  54. NTSTATUS _ExitWindowsEx(
  55. PCSR_THREAD pcsrt,
  56. UINT dwFlags)
  57. {
  58. LUID luidCaller;
  59. NTSTATUS Status;
  60. if ((dwFlags & EWX_REBOOT) || (dwFlags & EWX_POWEROFF)) {
  61. dwFlags |= EWX_SHUTDOWN;
  62. }
  63. //
  64. // Only winlogon gets to set the high flags:
  65. //
  66. if ((dwFlags & ~EWX_VALID) != 0) {
  67. if (HandleToUlong(pcsrt->ClientId.UniqueProcess) != gIdLogon) {
  68. RIPMSG2(RIP_WARNING,
  69. "Process 0x%x tried to call ExitWindowsEx with flags 0x%x",
  70. pcsrt->ClientId.UniqueProcess,
  71. dwFlags);
  72. return STATUS_ACCESS_DENIED;
  73. }
  74. }
  75. /*
  76. * Find out the callers sid. Only want to shutdown processes in the
  77. * callers sid.
  78. */
  79. if (!CsrImpersonateClient(NULL)) {
  80. return STATUS_BAD_IMPERSONATION_LEVEL;
  81. }
  82. Status = CsrGetProcessLuid(NULL, &luidCaller);
  83. if (!NT_SUCCESS(Status)) {
  84. CsrRevertToSelf();
  85. return Status;
  86. }
  87. /*
  88. * Loop until we can do the shutdown. If we cannot do it, we'll go to
  89. * fastexit and bail.
  90. */
  91. while (TRUE) {
  92. Status = NtUserSetInformationThread(pcsrt->ThreadHandle,
  93. UserThreadInitiateShutdown,
  94. &dwFlags, sizeof(dwFlags));
  95. switch (Status) {
  96. case STATUS_PENDING:
  97. /*
  98. * The logoff/shutdown is in progress and nothing more needs to
  99. * be done.
  100. */
  101. goto fastexit;
  102. case STATUS_RETRY:
  103. if (!CancelExitWindows()) {
  104. Status = STATUS_PENDING;
  105. goto fastexit;
  106. } else {
  107. continue;
  108. }
  109. case STATUS_CANT_WAIT:
  110. /*
  111. * There is no notify window and the calling thread has
  112. * windows that prevent this request from succeeding.
  113. * The client handles this by starting another thread
  114. * to recall ExitWindowsEx.
  115. */
  116. goto fastexit;
  117. default:
  118. if (!NT_SUCCESS(Status)) {
  119. SetLastError(RtlNtStatusToDosError(Status));
  120. goto fastexit;
  121. }
  122. }
  123. break;
  124. }
  125. /*
  126. * This thread is doing the shutdown
  127. */
  128. EnterCrit();
  129. UserAssert(gdwThreadEndSession == 0);
  130. gdwThreadEndSession = HandleToUlong(pcsrt->ClientId.UniqueThread);
  131. LeaveCrit();
  132. /*
  133. * Call csr to loop through the processes shutting them down.
  134. */
  135. Status = CsrShutdownProcesses(&luidCaller, dwFlags);
  136. if (Status == STATUS_CANCELLED && IsSETEnabled()) {
  137. SHUTDOWN_REASON sr;
  138. sr.cbSize = sizeof(SHUTDOWN_REASON);
  139. sr.uFlags = dwFlags;
  140. sr.dwReasonCode = 0;
  141. sr.fShutdownCancelled = TRUE;
  142. sr.dwEventType = SR_EVENT_EXITWINDOWS;
  143. sr.lpszComment = NULL;
  144. /*
  145. * Record the fact that the shutdown was cancelled.
  146. */
  147. RecordShutdownReason(&sr);
  148. }
  149. /*
  150. * Tell win32k.sys we're done.
  151. */
  152. NtUserSetInformationThread(pcsrt->ThreadHandle, UserThreadEndShutdown, &Status, sizeof(Status));
  153. EnterCrit();
  154. gdwThreadEndSession = 0;
  155. NtSetEvent(gheventCancelled, NULL);
  156. LeaveCrit();
  157. fastexit:
  158. CsrRevertToSelf();
  159. return Status;
  160. }
  161. /***************************************************************************\
  162. * UserClientShutdown
  163. *
  164. * This gets called from CSR. If we recognize the application (i.e., it has a
  165. * top level window), then send queryend/end session messages to it. Otherwise
  166. * say we don't recognize it.
  167. *
  168. * 07-23-92 ScottLu Created.
  169. \***************************************************************************/
  170. NTSTATUS UserClientShutdown(
  171. PCSR_PROCESS pcsrp,
  172. ULONG dwFlags,
  173. BOOLEAN fFirstPass)
  174. {
  175. PLIST_ENTRY ListHead, ListNext;
  176. PCSR_PROCESS Process;
  177. PCSR_THREAD Thread;
  178. USERTHREAD_SHUTDOWN_INFORMATION ShutdownInfo;
  179. BOOL fNoMsgs;
  180. BOOL fNoMsgsEver = TRUE;
  181. BOOL Forced = FALSE;
  182. BOOL bDoBlock;
  183. BOOL fNoRetry;
  184. DWORD cmd = 0, dwClientFlags;
  185. NTSTATUS Status;
  186. NTSTATUS TerminateStatus = STATUS_ACCESS_DENIED;
  187. UINT cThreads;
  188. BOOL fSendEndSession = FALSE;
  189. #if DBG
  190. DWORD dwLocalThreadEndSession = gdwThreadEndSession;
  191. #endif
  192. /*
  193. * If this is a logoff and the process does not belong to
  194. * the account doing the logoff and is not LocalSystem,
  195. * do not send end-session messages. Console will notify
  196. * the app of the logoff.
  197. */
  198. if (!(dwFlags & EWX_SHUTDOWN) && (pcsrp->ShutdownFlags & SHUTDOWN_OTHERCONTEXT)) {
  199. Status = SHUTDOWN_UNKNOWN_PROCESS;
  200. goto CleanupAndExit;
  201. }
  202. /*
  203. * Calculate whether to allow exit and force-exit this process before
  204. * we unlock pcsrp.
  205. */
  206. fNoRetry = (pcsrp->ShutdownFlags & SHUTDOWN_NORETRY) || (dwFlags & EWX_FORCE);
  207. /*
  208. * Setup flags for WM_CLIENTSHUTDOWN
  209. * -Assume the process is going to OK the WM_QUERYENDSESSION (WMCS_EXIT)
  210. * -NT's shutdown always starts with a logoff.
  211. * -Shutdown or logoff? (WMCS_SHUTDOWN)
  212. * -Should display dialog for hung apps? (WMCS_NODLGIFHUNG)
  213. * -is this process in the context being logged off? (WMCS_CONTEXTLOGOFF)
  214. */
  215. dwClientFlags = WMCS_EXIT | (fNoRetry ? WMCS_NORETRY : 0);
  216. /*
  217. * Check the flags originally passed by the ExitWindows caller to see if we're
  218. * really just logging off.
  219. */
  220. if (!(dwFlags & (EWX_WINLOGON_OLD_REBOOT | EWX_WINLOGON_OLD_SHUTDOWN))) {
  221. dwClientFlags |= WMCS_LOGOFF;
  222. }
  223. if (dwFlags & EWX_FORCEIFHUNG) {
  224. dwClientFlags |= WMCS_NODLGIFHUNG;
  225. }
  226. if (!(pcsrp->ShutdownFlags & (SHUTDOWN_SYSTEMCONTEXT | SHUTDOWN_OTHERCONTEXT))) {
  227. dwClientFlags |= WMCS_CONTEXTLOGOFF;
  228. }
  229. /*
  230. * Lock the process while we walk the thread list. We know
  231. * that the process is valid and therefore do not need to
  232. * check the return status.
  233. */
  234. CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process);
  235. ShutdownInfo.StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS;
  236. /*
  237. * Go through the thread list and mark them as not
  238. * shutdown yet.
  239. */
  240. ListHead = &pcsrp->ThreadList;
  241. ListNext = ListHead->Flink;
  242. while (ListNext != ListHead) {
  243. Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
  244. Thread->Flags &= ~(CSR_THREAD_SHUTDOWNSKIP | CSR_THREAD_HANGREPORTED);
  245. ListNext = ListNext->Flink;
  246. }
  247. /*
  248. * Perform the proper shutdown operation on each thread. Keep
  249. * a count of the number of gui threads found.
  250. */
  251. cThreads = 0;
  252. ShutdownInfo.drdRestore.pdeskRestore = NULL;
  253. ShutdownInfo.drdRestore.hdeskNew = NULL;
  254. while (TRUE) {
  255. ListNext = ListHead->Flink;
  256. while (ListNext != ListHead) {
  257. Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
  258. /*
  259. * Skip the thread doing the shutdown. Assume that it's
  260. * ready.
  261. * gdwThreadEndSession shouldn't change while the shutdown
  262. * is in progress; so this should be thread safe.
  263. */
  264. UserAssert(gdwThreadEndSession == dwLocalThreadEndSession);
  265. if (HandleToUlong(Thread->ClientId.UniqueThread) == gdwThreadEndSession) {
  266. Thread->Flags |= CSR_THREAD_SHUTDOWNSKIP;
  267. }
  268. if (!(Thread->Flags &
  269. (CSR_THREAD_DESTROYED | CSR_THREAD_SHUTDOWNSKIP))) {
  270. break;
  271. }
  272. ListNext = ListNext->Flink;
  273. }
  274. if (ListNext == ListHead) {
  275. break;
  276. }
  277. Thread->Flags |= CSR_THREAD_SHUTDOWNSKIP;
  278. ShutdownInfo.dwFlags = dwClientFlags;
  279. CsrReferenceThread(Thread);
  280. CsrUnlockProcess(Process);
  281. Status = NtUserQueryInformationThread(Thread->ThreadHandle,
  282. UserThreadShutdownInformation,
  283. &ShutdownInfo,
  284. sizeof(ShutdownInfo),
  285. NULL);
  286. CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process);
  287. /*
  288. * When we release the process structure lock, Thread can go away.
  289. * Since we already ref'ed it, we're safe, but once we deref here
  290. * its ref count can go to zero and the memory will be freed, in
  291. * which case we must be careful not to touch it again.
  292. */
  293. if (CsrDereferenceThread(Thread) == 0) {
  294. continue;
  295. }
  296. if (!NT_SUCCESS(Status)) {
  297. continue;
  298. }
  299. if (ShutdownInfo.StatusShutdown == SHUTDOWN_UNKNOWN_PROCESS) {
  300. continue;
  301. }
  302. if (ShutdownInfo.StatusShutdown == SHUTDOWN_KNOWN_PROCESS) {
  303. CsrUnlockProcess(Process);
  304. Status = SHUTDOWN_KNOWN_PROCESS;
  305. goto RestoreDesktop;
  306. }
  307. /*
  308. * If this process is not in the account being logged off and it
  309. * is not on the windowstation being logged off, don't send
  310. * the end session messages.
  311. */
  312. if (!(dwClientFlags & WMCS_CONTEXTLOGOFF) && (ShutdownInfo.hwndDesktop == NULL)) {
  313. /*
  314. * This process is not in the context being logged off. Do
  315. * not terminate it and let console send an event to the process.
  316. */
  317. ShutdownInfo.StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS;
  318. continue;
  319. }
  320. /*
  321. * Shut down this process.
  322. */
  323. cThreads++;
  324. if (ISTS()) {
  325. Forced = (dwFlags & EWX_FORCE);
  326. fNoMsgs = (pcsrp->ShutdownFlags & SHUTDOWN_NORETRY) ||
  327. !(ShutdownInfo.dwFlags & USER_THREAD_GUI);
  328. fNoMsgsEver &= fNoMsgs;
  329. if (Forced && (!(dwFlags & EWX_NONOTIFY) || (gSessionId != 0))) {
  330. dwClientFlags &= ~WMCS_LOGOFF; // WinStation Reset or Shutdown. Don't do this for console session.
  331. }
  332. if (fNoMsgs || Forced) {
  333. BoostHardError((ULONG_PTR)Thread->ClientId.UniqueProcess, BHE_FORCE);
  334. }
  335. bDoBlock = (fNoMsgs == FALSE);
  336. } else {
  337. if (fNoRetry || !(ShutdownInfo.dwFlags & USER_THREAD_GUI)) {
  338. /*
  339. * Dispose of any hard errors.
  340. */
  341. BoostHardError((ULONG_PTR)Thread->ClientId.UniqueProcess, BHE_FORCE);
  342. bDoBlock = FALSE;
  343. } else {
  344. bDoBlock = TRUE;
  345. }
  346. }
  347. if (bDoBlock) {
  348. CsrReferenceThread(Thread);
  349. CsrUnlockProcess(Process);
  350. /*
  351. * There are problems in changing shutdown to send all the
  352. * QUERYENDSESSIONs at once before doing any ENDSESSIONs, like
  353. * Windows does. The whole machine needs to be modal if you do this.
  354. * If it isn't modal, then you have this problem. Imagine app 1 and 2.
  355. * 1 gets the queryendsession, no problem. 2 gets it and brings up a
  356. * dialog. Now being a simple user, you decide you need to change the
  357. * document in app 1. Now you switch back to app 2, hit ok, and
  358. * everything goes away - including app 1 without saving its changes.
  359. * Also, apps expect that once they've received the QUERYENDSESSION,
  360. * they are not going to get anything else of any particular interest
  361. * (unless it is a WM_ENDSESSION with FALSE). We had bugs pre 511 where
  362. * apps were blowing up because of this.
  363. * If this change is made, the entire system must be modal
  364. * while this is going on. - ScottLu 6/30/94
  365. */
  366. cmd = ThreadShutdownNotify(dwClientFlags | WMCS_QUERYEND, (ULONG_PTR)Thread, 0);
  367. CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process);
  368. CsrDereferenceThread(Thread);
  369. /*
  370. * If shutdown has been cancelled, let csr know about it.
  371. */
  372. switch (cmd) {
  373. case TSN_USERSAYSCANCEL:
  374. case TSN_APPSAYSNOTOK:
  375. if (!Forced) {
  376. dwClientFlags &= ~WMCS_EXIT;
  377. }
  378. /*
  379. * Fall through.
  380. */
  381. case TSN_APPSAYSOK:
  382. fSendEndSession = TRUE;
  383. break;
  384. case TSN_USERSAYSKILL:
  385. /*
  386. * Since we cannot just kill one thread, the whole process
  387. * is going down. Hence, there is no point on continuing
  388. * checking other threads. Also, the user wants it killed
  389. * so we won't waste any time sending more messages
  390. */
  391. dwClientFlags |= WMCS_EXIT;
  392. goto KillIt;
  393. case TSN_NOWINDOW:
  394. /*
  395. * Did this process have a window?
  396. * If this is the second pass we terminate the process even if it did
  397. * not have any windows in case the app was just starting up.
  398. * WOW hits this often because it takes so long to start up.
  399. * Logon (with WOW auto-starting) then logoff WOW won't die but will
  400. * lock some files open so you can't logon next time.
  401. */
  402. if (fFirstPass) {
  403. cThreads--;
  404. }
  405. break;
  406. }
  407. }
  408. }
  409. /*
  410. * If end session message need to be sent, do it now.
  411. */
  412. if (fSendEndSession) {
  413. /*
  414. * Go through the thread list and mark them as not
  415. * shutdown yet.
  416. */
  417. ListNext = ListHead->Flink;
  418. while (ListNext != ListHead) {
  419. Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
  420. Thread->Flags &= ~(CSR_THREAD_SHUTDOWNSKIP | CSR_THREAD_HANGREPORTED);
  421. ListNext = ListNext->Flink;
  422. }
  423. /*
  424. * Perform the proper shutdown operation on each thread.
  425. */
  426. while (TRUE) {
  427. ListHead = &pcsrp->ThreadList;
  428. ListNext = ListHead->Flink;
  429. while (ListNext != ListHead) {
  430. Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
  431. if (!(Thread->Flags &
  432. (CSR_THREAD_DESTROYED | CSR_THREAD_SHUTDOWNSKIP))) {
  433. break;
  434. }
  435. ListNext = ListNext->Flink;
  436. }
  437. if (ListNext == ListHead)
  438. break;
  439. CsrReferenceThread(Thread);
  440. CsrUnlockProcess(Process);
  441. Thread->Flags |= CSR_THREAD_SHUTDOWNSKIP;
  442. ShutdownInfo.dwFlags = dwClientFlags;
  443. Status = NtUserQueryInformationThread(Thread->ThreadHandle,
  444. UserThreadShutdownInformation, &ShutdownInfo, sizeof(ShutdownInfo), NULL);
  445. if (!NT_SUCCESS(Status))
  446. goto SkipThread;
  447. if (ShutdownInfo.StatusShutdown == SHUTDOWN_UNKNOWN_PROCESS ||
  448. !(ShutdownInfo.dwFlags & USER_THREAD_GUI))
  449. goto SkipThread;
  450. /*
  451. * Send the end session messages to the thread.
  452. */
  453. /*
  454. * If the user says kill it, the user wants it to go away now
  455. * no matter what. If the user didn't say kill, then call again
  456. * because we need to send WM_ENDSESSION messages.
  457. */
  458. ThreadShutdownNotify(dwClientFlags, (ULONG_PTR)Thread, 0);
  459. SkipThread:
  460. CsrLockProcessByClientId(pcsrp->ClientId.UniqueProcess, &Process);
  461. CsrDereferenceThread(Thread);
  462. }
  463. }
  464. KillIt:
  465. CsrUnlockProcess(Process);
  466. if (ISTS()) {
  467. bDoBlock = (!fNoMsgsEver && !(dwClientFlags & WMCS_EXIT));
  468. } else {
  469. bDoBlock = (!fNoRetry && !(dwClientFlags & WMCS_EXIT));
  470. }
  471. if (bDoBlock) {
  472. Status = SHUTDOWN_CANCEL;
  473. goto RestoreDesktop;
  474. }
  475. /*
  476. * Set the final shutdown status according to the number of gui
  477. * threads found. If the count is zero, we have an unknown process.
  478. */
  479. if (cThreads == 0)
  480. ShutdownInfo.StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS;
  481. else
  482. ShutdownInfo.StatusShutdown = SHUTDOWN_KNOWN_PROCESS;
  483. if (ShutdownInfo.StatusShutdown == SHUTDOWN_UNKNOWN_PROCESS ||
  484. !(dwClientFlags & WMCS_CONTEXTLOGOFF)) {
  485. /*
  486. * This process is not in the context being logged off. Do
  487. * not terminate it and let console send an event to the process.
  488. */
  489. Status = SHUTDOWN_UNKNOWN_PROCESS;
  490. if (ShutdownInfo.drdRestore.hdeskNew) {
  491. goto RestoreDesktop;
  492. }
  493. goto CleanupAndExit;
  494. }
  495. /*
  496. * Calling ExitProcess() in the app's context will not always work
  497. * because the app may have .dll termination deadlocks: so the thread
  498. * will hang with the rest of the process. To ensure apps go away,
  499. * we terminate the process with NtTerminateProcess().
  500. *
  501. * Pass this special value, DBG_TERMINATE_PROCESS, which tells
  502. * NtTerminateProcess() to return failure if it can't terminate the
  503. * process because the app is being debugged.
  504. */
  505. if (ISTS()) {
  506. NTSTATUS ExitStatus;
  507. HANDLE DebugPort;
  508. ExitStatus = DBG_TERMINATE_PROCESS;
  509. if (NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(),
  510. ProcessDebugPort,
  511. &DebugPort,
  512. sizeof(HANDLE),
  513. NULL)) &&
  514. (DebugPort != NULL)) {
  515. // Csr is being debugged - go ahead and kill the process
  516. ExitStatus = 0;
  517. }
  518. TerminateStatus = NtTerminateProcess(pcsrp->ProcessHandle, ExitStatus);
  519. } else {
  520. TerminateStatus = NtTerminateProcess(pcsrp->ProcessHandle, DBG_TERMINATE_PROCESS);
  521. }
  522. pcsrp->Flags |= CSR_PROCESS_TERMINATED;
  523. /*
  524. * Let csr know we know about this process - meaning it was our
  525. * responsibility to shut it down.
  526. */
  527. Status = SHUTDOWN_KNOWN_PROCESS;
  528. RestoreDesktop:
  529. /*
  530. * Release the desktop that was used.
  531. */
  532. {
  533. USERTHREAD_USEDESKTOPINFO utudi;
  534. utudi.hThread = NULL;
  535. RtlCopyMemory(&(utudi.drdRestore), &(ShutdownInfo.drdRestore), sizeof(DESKRESTOREDATA));
  536. NtUserSetInformationThread(NtCurrentThread(), UserThreadUseDesktop,
  537. &utudi, sizeof(utudi));
  538. }
  539. /*
  540. * Now that we're done with the process handle, derefence the csr
  541. * process structure.
  542. */
  543. if (Status != SHUTDOWN_UNKNOWN_PROCESS) {
  544. /*
  545. * If TerminateProcess returned STATUS_ACCESS_DENIED, then the process
  546. * is being debugged and it wasn't terminated.Otherwise we need to wait
  547. * anyway since TerminateProcess might return failure when the process
  548. * is going away (ie STATUS_PROCESS_IS_TERMINATING).If termination
  549. * indeed fail, something is wrong anyway so waiting a bit won't
  550. * hurt much.
  551. * If we wait give the process whatever exit timeout value configured
  552. * in the registry, but no less than the 5 second Hung App timeout.
  553. */
  554. if (TerminateStatus != STATUS_ACCESS_DENIED) {
  555. LARGE_INTEGER li;
  556. li.QuadPart = (LONGLONG)-10000 * gdwProcessTerminateTimeout;
  557. TerminateStatus = NtWaitForSingleObject(pcsrp->ProcessHandle,
  558. FALSE,
  559. &li);
  560. if (TerminateStatus != STATUS_WAIT_0) {
  561. RIPMSG2(RIP_WARNING,
  562. "UserClientShutdown: wait for process %x failed with status %x",
  563. pcsrp->ClientId.UniqueProcess, TerminateStatus);
  564. }
  565. }
  566. CsrDereferenceProcess(pcsrp);
  567. }
  568. CleanupAndExit:
  569. return Status;
  570. }
  571. /***************************************************************************\
  572. * WMCSCallback
  573. *
  574. * This function is passed to SendMessageCallback when sending the
  575. * WM_CLIENTSHUTDOWN message. It propagates the return value back
  576. * if ThreadShutdownNotify is still waiting for it; otherwise,
  577. * it just frees the memory.
  578. *
  579. * 03-04-97 GerardoB Created.
  580. \***************************************************************************/
  581. VOID CALLBACK WMCSCallback(
  582. HWND hwnd,
  583. UINT uMsg,
  584. ULONG_PTR dwData,
  585. LRESULT lResult)
  586. {
  587. PWMCSDATA pwmcsd = (PWMCSDATA)dwData;
  588. UNREFERENCED_PARAMETER(hwnd);
  589. UNREFERENCED_PARAMETER(uMsg);
  590. if (pwmcsd->dwFlags & WMCSD_IGNORE) {
  591. LocalFree(pwmcsd);
  592. return;
  593. }
  594. pwmcsd->dwFlags |= WMCSD_REPLY;
  595. pwmcsd->dwRet = (DWORD)lResult;
  596. }
  597. /***************************************************************************\
  598. * GetInputWindow
  599. *
  600. * We assume a thread is waiting for input if it's not hung, the (main)
  601. * window is disabled, and it owns an enabled popup.
  602. *
  603. * 03-06-97 GerardoB Created.
  604. \***************************************************************************/
  605. HWND GetInputWindow(
  606. PCSR_THREAD pcsrt,
  607. HWND hwnd)
  608. {
  609. DWORD dwTimeout;
  610. HWND hwndPopup;
  611. /*
  612. * Ask the kernel if the thread is hung.
  613. */
  614. dwTimeout = gCmsHungAppTimeout;
  615. NtUserQueryInformationThread(pcsrt->ThreadHandle,
  616. UserThreadHungStatus, &dwTimeout, sizeof(dwTimeout), NULL);
  617. /*
  618. * If not hung and disabled, see if it owns an enabled popup.
  619. */
  620. if (!dwTimeout && !IsWindowEnabled(hwnd)) {
  621. hwndPopup = GetWindow(hwnd, GW_ENABLEDPOPUP);
  622. if (hwndPopup != NULL && hwndPopup != hwnd) {
  623. return hwndPopup;
  624. }
  625. }
  626. return NULL;
  627. }
  628. /***************************************************************************\
  629. * GetApplicationText
  630. *
  631. * Gets the text that identifies the given window or thread
  632. *
  633. * 08-01-97 GerardoB Created.
  634. \***************************************************************************/
  635. VOID GetApplicationText(
  636. HWND hwnd,
  637. HANDLE hThread,
  638. WCHAR *pwcText,
  639. UINT uLen)
  640. {
  641. /*
  642. * GetWindowText doesn't call the hwnd's proc; otherwise, we could
  643. * get blocked here for good.
  644. */
  645. GetWindowText(hwnd, pwcText, uLen);
  646. if (*pwcText == 0) {
  647. /*
  648. * We couldn't get the window's title; let's try the thread's name.
  649. */
  650. NtUserQueryInformationThread(hThread, UserThreadTaskName,
  651. pwcText, uLen * sizeof(WCHAR), NULL);
  652. }
  653. }
  654. /***************************************************************************\
  655. * ReportHang
  656. *
  657. * This function launches an intermediate app (dumprep.exe) which packages up
  658. * the hang information & ships it up to MS. We create an event and wait on
  659. * it so that we know when the minidump information has been grabbed from the
  660. * app.
  661. *
  662. * 08-31-00 DerekM Created
  663. \***************************************************************************/
  664. VOID ReportHang(
  665. CLIENT_ID *pcidToKill)
  666. {
  667. PROCESS_SESSION_INFORMATION psi;
  668. SECURITY_ATTRIBUTES sa;
  669. SECURITY_DESCRIPTOR sd;
  670. PCSR_THREAD pcsrt = CSR_SERVER_QUERYCLIENTTHREAD();
  671. NTSTATUS Status;
  672. HANDLE rghWait[2] = { NULL, NULL };
  673. HANDLE hProc = NULL;
  674. WCHAR wszEvent[MAX_PATH], *pwszSuffix;
  675. DWORD dw, dwTimeout, dwStartWait;
  676. BOOL fIs64Bit = FALSE;
  677. #ifdef _WIN64
  678. ULONG_PTR Wow64Info = 0;
  679. HANDLE hProcKill = NULL;
  680. #endif
  681. #if defined(_DEBUG) || defined(DEBUG)
  682. dwTimeout = 600000; // 10 minutes
  683. #else
  684. dwTimeout = 120000; // 2 minutes
  685. #endif
  686. // we're going to launch dwwin in the context of the interactive user that
  687. // is logged on to the killing process's session. So we need to figure out
  688. // what session it's in.
  689. // If any of these fail, we have to bail cuz otherwise we'd have to create
  690. // an instance of dwwin.exe in local system context, and since dwwin.exe
  691. // can launch helpcenter, this is bad.
  692. hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
  693. HandleToLong(pcsrt->ClientId.UniqueProcess));
  694. if (hProc == NULL) {
  695. RIPMSG2(RIP_WARNING,
  696. "HangReporting: Couldn't open killing process (pid: %d) (err: %08x)\n",
  697. HandleToLong(pcsrt->ClientId.UniqueProcess), GetLastError());
  698. goto done;
  699. }
  700. Status = NtQueryInformationProcess(hProc, ProcessSessionInformation, &psi,
  701. sizeof(psi), NULL);
  702. if (NT_SUCCESS(Status) == FALSE) {
  703. RIPMSG2(RIP_WARNING,
  704. "HangReporting: Couldn't get the session ID (pid: %d) (err: %08x)\n",
  705. HandleToLong(pcsrt->ClientId.UniqueProcess),
  706. RtlNtStatusToDosError(Status));
  707. goto done;
  708. }
  709. #ifdef _WIN64
  710. // need to determine if we're a Wow64 process so we can build the appropriate
  711. // signatures...
  712. hProcKill = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
  713. HandleToLong(pcidToKill->UniqueProcess));
  714. if (hProcKill == NULL) {
  715. RIPMSG2(RIP_WARNING,
  716. "HangReporting: Couldn't open dying process (pid: %d) (err: %08x)\n",
  717. HandleToLong(pcidToKill->UniqueProcess), GetLastError());
  718. goto done;
  719. }
  720. Status = NtQueryInformationProcess(hProcKill, ProcessWow64Information,
  721. &Wow64Info, sizeof(Wow64Info), NULL);
  722. if (NT_SUCCESS(Status) == FALSE) {
  723. RIPMSG2(RIP_WARNING,
  724. "HangReporting: Couldn't get Wow64 info (pid: %d) (err: %08x)\n",
  725. HandleToLong(pcidToKill->UniqueProcess),
  726. RtlNtStatusToDosError(Status));
  727. goto done;
  728. }
  729. fIs64Bit = (Wow64Info == 0);
  730. #endif
  731. // Because of a bug where CreateProcessAsUser doesn't want to work from
  732. // the csrss process, we have to have a remote process sitting around
  733. // waiting on a pipe. It will call CreateProcessAsUser (as well as
  734. // determine the correct token for the session).
  735. //
  736. // Note that it only accepts requests from processes running as local
  737. // system.
  738. // Since a remote process does the creation of dumprep.exe, we need to
  739. // used a named event instead of relying on dumprep to inherit the event
  740. // handle.
  741. dw = swprintf(wszEvent, L"Global\\%d%x%x%x%x%x", psi.SessionId,
  742. GetTickCount(), HandleToLong(pcsrt->ClientId.UniqueProcess),
  743. HandleToLong(pcsrt->ClientId.UniqueThread),
  744. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess),
  745. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread));
  746. pwszSuffix = wszEvent + dw;
  747. // make sure to create this event with a NULL DACL so a generic usermode
  748. // process has access to it.
  749. Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
  750. if (NT_SUCCESS(Status) == FALSE) {
  751. RIPMSG1(RIP_WARNING, "HangReporting: could not create SD (err: %08x)\n",
  752. RtlNtStatusToDosError(Status));
  753. goto done;
  754. }
  755. // This is implemented in reclient.h & creates a SD with creator &
  756. // LocalSystem having full rights & world & anonymous having sync
  757. // rights.
  758. Status = AllocDefSD(&sd,
  759. STANDARD_RIGHTS_ALL | GENERIC_ALL | EVENT_ALL_ACCESS,
  760. EVENT_MODIFY_STATE | SYNCHRONIZE | GENERIC_READ);
  761. if (NT_SUCCESS(Status) == FALSE) {
  762. RIPMSG1(RIP_WARNING, "HangReporting: could not create SD (err: %08x)\n",
  763. RtlNtStatusToDosError(Status));
  764. goto done;
  765. }
  766. ZeroMemory(&sa, sizeof(sa));
  767. sa.nLength = sizeof(sa);
  768. sa.lpSecurityDescriptor = &sd;
  769. // Need an event so we know when the app we're killing is no longer
  770. // necessary. If the event already exists, try to create a new one.
  771. // But only do this a maximum of 7 times.
  772. for (dw = 0; dw < 7; dw++) {
  773. rghWait[0] = CreateEventW(&sa, TRUE, FALSE, wszEvent);
  774. if (rghWait[0] == NULL) {
  775. RIPMSG1(RIP_WARNING,
  776. "HangReporting: Error creating wait event (err: %08x)\n",
  777. GetLastError());
  778. goto done;
  779. }
  780. if (GetLastError() != ERROR_ALREADY_EXISTS) {
  781. break;
  782. }
  783. // Sleep for a millisecond to make the result of GetTickCount() even
  784. // more unpredictable...
  785. Sleep(1);
  786. _ltow(GetTickCount(), pwszSuffix, 16);
  787. }
  788. if (dw >= 7) {
  789. RIPMSG0(RIP_WARNING, "HangReporting: Could not find unique wait event name\n");
  790. goto done;
  791. }
  792. FreeDefSD(&sd);
  793. if (StartHangReport(psi.SessionId, wszEvent,
  794. HandleToLong(pcidToKill->UniqueProcess),
  795. HandleToLong(pcidToKill->UniqueThread),
  796. fIs64Bit, &rghWait[1]) == FALSE)
  797. {
  798. RIPMSG1(RIP_WARNING,
  799. "HangReporting: StartHangReport failed (err: %08x)\n",
  800. GetLastError());
  801. goto done;
  802. }
  803. // use MsgWaitForMultipleObjects in case this thread is doing UI processing
  804. // Not really likely, but you never know. Anyway, only wait 2 minutes for
  805. // dumprep to generate the minidump. If it still hasn't done it by then,
  806. // it isn't likely to ever finish.
  807. dwStartWait = GetTickCount();
  808. for (;;)
  809. {
  810. dw = MsgWaitForMultipleObjects(2, rghWait, FALSE, dwTimeout, QS_ALLINPUT);
  811. if (dw == WAIT_OBJECT_0 + 2)
  812. {
  813. DWORD dwNow;
  814. DWORD dwSub;
  815. MSG msg;
  816. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  817. TranslateMessage(&msg);
  818. DispatchMessage(&msg);
  819. }
  820. dwNow = GetTickCount();
  821. if (dwNow < dwStartWait)
  822. dwSub = ((DWORD)-1 - dwStartWait) + dwNow;
  823. else
  824. dwSub = dwNow - dwStartWait;
  825. if (dwSub > dwTimeout)
  826. dwTimeout = 0;
  827. else
  828. dwTimeout -= dwSub;
  829. continue;
  830. }
  831. break;
  832. }
  833. done:
  834. #ifdef _WIN64
  835. if (hProcKill != NULL) {
  836. CloseHandle(hProcKill);
  837. }
  838. #endif
  839. if (hProc != NULL) {
  840. CloseHandle(hProc);
  841. }
  842. if (rghWait[0] != NULL) {
  843. CloseHandle(rghWait[0]);
  844. }
  845. if (rghWait[1] != NULL) {
  846. CloseHandle(rghWait[1]);
  847. }
  848. }
  849. /***************************************************************************\
  850. * ThreadShutdownNotify
  851. *
  852. * This function notifies a given thread that it's time (or about time)
  853. * to go away. This is called from _EndTask to post the WM_CLOSE message
  854. * or from UserClientShutdown to send the WM_QUERYENDSESSION and
  855. * WM_ENDSESSION messages. If the thread fails to respond, then the
  856. * "End Application" dialog is brought up. This function is also called
  857. * from Console to display that dialog too.
  858. *
  859. * 03-07-97 GerardoB Created to replace SendShutdownMessages,
  860. * MySendEndSessionMessages and DoEndTaskDialog
  861. * 08-15-00 JasonSch Added code to limit number of CSRSS worker threads
  862. * stuck in _EndTask to 8.
  863. \***************************************************************************/
  864. DWORD ThreadShutdownNotify(
  865. DWORD dwClientFlags,
  866. ULONG_PTR dwThread,
  867. LPARAM lParam)
  868. {
  869. HWND hwnd, hwndOwner, hwndDlg;
  870. PWMCSDATA pwmcsd = NULL;
  871. ENDDLGPARAMS edp;
  872. DWORD dwRet, dwRealTimeout, dwTimeout, dwStartTiming, dwCmd;
  873. MSG msg;
  874. PCSR_THREAD pcsrt;
  875. HANDLE hThread;
  876. BOOL fEndTaskNow = FALSE;
  877. static DWORD dwTSNThreads = 0;
  878. #define ESMH_CANCELEVENT 0
  879. #define ESMH_THREAD 1
  880. #define ESMH_HANDLECOUNT 2
  881. HANDLE ahandles[ESMH_HANDLECOUNT];
  882. if (dwTSNThreads > TSN_MAX_THREADS) {
  883. /*
  884. * If we've already reached our limit in terms of CSRSS threads stuck
  885. * in this function, "fail" this call.
  886. */
  887. return TSN_USERSAYSCANCEL;
  888. }
  889. /*
  890. * If this is console, just set up the wait loop and
  891. * bring the dialog up right away. Otherwise, find
  892. * the notification window, notify it, and go wait.
  893. */
  894. if (dwClientFlags & WMCS_CONSOLE) {
  895. hThread = (HANDLE)dwThread;
  896. dwRealTimeout = 0;
  897. goto SetupWaitLoop;
  898. } else {
  899. pcsrt = (PCSR_THREAD)dwThread;
  900. hThread = pcsrt->ThreadHandle;
  901. hwnd = (HWND)lParam;
  902. }
  903. /*
  904. * If no window was provided, find a top-level window owned by the thread.
  905. */
  906. if (hwnd == NULL) {
  907. EnumThreadWindows(HandleToUlong(pcsrt->ClientId.UniqueThread),
  908. &FindWindowFromThread,
  909. (LPARAM)&hwnd);
  910. if (hwnd == NULL) {
  911. return TSN_NOWINDOW;
  912. }
  913. }
  914. /*
  915. * Find the root owner (we'll guess this is the "main" window).
  916. */
  917. while ((hwndOwner = GetWindow(hwnd, GW_OWNER)) != NULL) {
  918. hwnd = hwndOwner;
  919. }
  920. #ifdef FE_IME
  921. /*
  922. * If this is a console window, then just returns TSN_APPSAYSOK.
  923. * In this routine:
  924. * Normally windows NT environment, hwnd never point to console window.
  925. * However, In ConIme process, its owner window point to console window.
  926. */
  927. if (!(dwClientFlags & WMCS_ENDTASK)) {
  928. if ((HANDLE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE) == ghModuleWin) {
  929. return TSN_APPSAYSOK;
  930. }
  931. }
  932. #endif // FE_IME
  933. /*
  934. * If this is an EndTask request but the window is disabled,
  935. * then we want to bring the dialog up right way (the app
  936. * is probably waiting for input).
  937. *
  938. * Otherwise, we bring the window to the foreground, send/post
  939. * the request and wait.
  940. */
  941. /*
  942. * Make sure we respond to the user ASAP when they attempt to shutdown
  943. * an application that we know is hung.
  944. */
  945. if ((dwClientFlags & WMCS_ENDTASK)) {
  946. dwTimeout = gCmsHungAppTimeout;
  947. NtUserQueryInformationThread(pcsrt->ThreadHandle, UserThreadHungStatus, &dwTimeout, sizeof(dwTimeout), NULL);
  948. if (!IsWindowEnabled(hwnd) || dwTimeout) {
  949. dwRealTimeout = 0;
  950. fEndTaskNow = TRUE;
  951. }
  952. }
  953. if (!fEndTaskNow) {
  954. SetForegroundWindow(hwnd);
  955. dwRealTimeout = gCmsHungAppTimeout;
  956. if (dwClientFlags & WMCS_ENDTASK) {
  957. PostMessage(hwnd, WM_CLOSE, 0, 0);
  958. } else {
  959. /*
  960. * If the shutdown was canceled, we don't need to wait
  961. * (we're just sending the WM_ENDSESSION(FALSE)).
  962. */
  963. if (!(dwClientFlags & (WMCS_QUERYEND | WMCS_EXIT))) {
  964. SendNotifyMessage(hwnd, WM_CLIENTSHUTDOWN, dwClientFlags, 0);
  965. return TSN_APPSAYSOK;
  966. }
  967. /*
  968. * Allocate callback data. If out of memory, kill it.
  969. */
  970. pwmcsd = (PWMCSDATA)LocalAlloc(LPTR, sizeof(WMCSDATA));
  971. if (pwmcsd == NULL) {
  972. return TSN_USERSAYSKILL;
  973. }
  974. SendMessageCallback(hwnd, WM_CLIENTSHUTDOWN, dwClientFlags, 0,
  975. WMCSCallback, (ULONG_PTR)pwmcsd);
  976. }
  977. }
  978. SetupWaitLoop:
  979. /*
  980. * This thread is now officially going to be "stuck" in TSN. Increment our
  981. * count of threads so disposed.
  982. */
  983. ++dwTSNThreads;
  984. /*
  985. * This tells us if the hwndDlg is valid. This is set/cleared by EndTaskDlgProc.
  986. */
  987. ZeroMemory(&edp, sizeof(edp));
  988. edp.dwFlags = EDPF_NODLG;
  989. /*
  990. * Loop until the hwnd replies, the request is canceled
  991. * or the thread goes away. If it times out, bring up the
  992. * dialog and wait until the user tells us what to do.
  993. */
  994. *(ahandles + ESMH_CANCELEVENT) = gheventCancel;
  995. *(ahandles + ESMH_THREAD) = hThread;
  996. dwStartTiming = GetTickCount();
  997. dwCmd = 0;
  998. while (dwCmd == 0) {
  999. /*
  1000. * Calculate how long we have to wait.
  1001. */
  1002. dwTimeout = dwRealTimeout;
  1003. if ((dwTimeout != 0) && (dwTimeout != INFINITE)) {
  1004. dwTimeout -= (GetTickCount() - dwStartTiming);
  1005. if ((int)dwTimeout < 0) {
  1006. dwTimeout = 0;
  1007. }
  1008. }
  1009. dwRet = MsgWaitForMultipleObjects(ESMH_HANDLECOUNT, ahandles, FALSE, dwTimeout, QS_ALLINPUT);
  1010. switch (dwRet) {
  1011. case WAIT_OBJECT_0 + ESMH_CANCELEVENT:
  1012. /*
  1013. * The request has been canceled.
  1014. */
  1015. dwCmd = TSN_USERSAYSCANCEL;
  1016. break;
  1017. case WAIT_OBJECT_0 + ESMH_THREAD:
  1018. /*
  1019. * The thread is gone.
  1020. */
  1021. dwCmd = TSN_APPSAYSOK;
  1022. break;
  1023. case WAIT_OBJECT_0 + ESMH_HANDLECOUNT:
  1024. /*
  1025. * We got some input; process it.
  1026. */
  1027. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  1028. if ((edp.dwFlags & EDPF_NODLG)
  1029. || !IsDialogMessage(hwndDlg, &msg)) {
  1030. TranslateMessage(&msg);
  1031. DispatchMessage(&msg);
  1032. }
  1033. }
  1034. /*
  1035. * If we got a reply to the message, act on it
  1036. */
  1037. if (pwmcsd != NULL && (pwmcsd->dwFlags & WMCSD_REPLY)) {
  1038. switch (pwmcsd->dwRet) {
  1039. default:
  1040. /*
  1041. * If the message was not processed (the thread
  1042. * exited) or someone processed it and returned
  1043. * a bogus value, just shut them down.
  1044. *
  1045. * Fall through.
  1046. */
  1047. case WMCSR_ALLOWSHUTDOWN:
  1048. /*
  1049. * We're going to nuke this app, so get rid of
  1050. * any pending harderror boxes he might have.
  1051. */
  1052. BoostHardError((ULONG_PTR)pcsrt->ClientId.UniqueProcess, BHE_FORCE);
  1053. /*
  1054. * Fall through.
  1055. */
  1056. case WMCSR_DONE:
  1057. dwCmd = TSN_APPSAYSOK;
  1058. break;
  1059. case WMCSR_CANCEL:
  1060. dwCmd = TSN_APPSAYSNOTOK;
  1061. break;
  1062. }
  1063. }
  1064. /*
  1065. * Else if the dialog is still up, keep waiting for the user
  1066. * to tell us what to do
  1067. */
  1068. else if (!(edp.dwFlags & EDPF_NODLG)) {
  1069. break;
  1070. }
  1071. /*
  1072. * Else if the user dismissed the dialog, act on his response
  1073. */
  1074. else if (edp.dwFlags & EDPF_RESPONSE) {
  1075. switch(edp.dwRet) {
  1076. case IDC_ENDNOW:
  1077. /*
  1078. * The user wants us to kill it
  1079. */
  1080. dwCmd = TSN_USERSAYSKILL;
  1081. if ((dwClientFlags & WMCS_ENDTASK) != 0 &&
  1082. (edp.dwFlags & EDPF_HUNG) != 0) {
  1083. THREAD_BASIC_INFORMATION tbi;
  1084. CLIENT_ID *pcidToKill = NULL;
  1085. if ((dwClientFlags & WMCS_CONSOLE) != 0) {
  1086. if (NtQueryInformationThread(hThread,
  1087. ThreadBasicInformation,
  1088. &tbi,
  1089. sizeof(tbi),
  1090. NULL) == STATUS_SUCCESS) {
  1091. pcidToKill = &(tbi.ClientId);
  1092. }
  1093. } else {
  1094. pcidToKill = &(pcsrt->ClientId);
  1095. }
  1096. if (pcidToKill != NULL) {
  1097. if (!(pcsrt->Flags & CSR_THREAD_HANGREPORTED))
  1098. {
  1099. // insure that we only call this ONCE
  1100. pcsrt->Flags |= CSR_THREAD_HANGREPORTED;
  1101. ReportHang(pcidToKill);
  1102. }
  1103. }
  1104. }
  1105. break;
  1106. /* case IDCANCEL: */
  1107. default:
  1108. dwCmd = TSN_USERSAYSCANCEL;
  1109. break;
  1110. }
  1111. }
  1112. break;
  1113. case WAIT_TIMEOUT:
  1114. if (dwClientFlags & WMCS_NORETRY) {
  1115. /*
  1116. * We come here only for Terminal Server case. We return
  1117. * TSN_APPSAYSOK as Terminal Server 4 did in this case.
  1118. */
  1119. UserAssert(ISTS());
  1120. dwCmd = TSN_APPSAYSOK;
  1121. break;
  1122. }
  1123. /*
  1124. * Once we time out, we bring up the dialog and let
  1125. * its timer take over.
  1126. */
  1127. dwRealTimeout = INFINITE;
  1128. /*
  1129. * Check if the windows app is waiting for input;
  1130. * if not, we assume it is hung for EndTask. Otherwise,
  1131. * we enter a wait mode that brings the dialog up just
  1132. * to provide some (waiting) feedback. Console just gets
  1133. * the dialog right away.
  1134. */
  1135. if (!(dwClientFlags & WMCS_CONSOLE)) {
  1136. if (BoostHardError((ULONG_PTR)pcsrt->ClientId.UniqueProcess, BHE_TEST)
  1137. || (GetInputWindow(pcsrt, hwnd) != NULL)) {
  1138. edp.dwFlags |= EDPF_INPUT;
  1139. } else {
  1140. /*
  1141. * If the window's gone and the thread is still responsive, then
  1142. * this must be an app that just hides its window on WM_CLOSE
  1143. * (e.g., MSN Instant Messenger). Let's nuke the app w/o
  1144. * bringing up the EndTask dialog.
  1145. */
  1146. if (!IsWindow(hwnd)) {
  1147. DWORD dwThreadHung;
  1148. /*
  1149. * Ask the kernel if the thread is hung.
  1150. */
  1151. dwThreadHung = gCmsHungAppTimeout;
  1152. NtUserQueryInformationThread(pcsrt->ThreadHandle,
  1153. UserThreadHungStatus,
  1154. &dwThreadHung,
  1155. sizeof(dwThreadHung),
  1156. NULL);
  1157. if (!dwThreadHung) {
  1158. dwCmd = TSN_APPSAYSOK;
  1159. break;
  1160. }
  1161. }
  1162. /*
  1163. * EWX_FORCEIFHUNG support.
  1164. * Also, if this is an ExitWindows call and the process is
  1165. * not in the context being logged off, we won't kill it.
  1166. * So don't bother asking the user what to do.
  1167. */
  1168. if ((dwClientFlags & WMCS_NODLGIFHUNG)
  1169. || (!(dwClientFlags & WMCS_ENDTASK)
  1170. && !(dwClientFlags & WMCS_CONTEXTLOGOFF))) {
  1171. dwCmd = TSN_USERSAYSKILL;
  1172. break;
  1173. }
  1174. /*
  1175. * Hung or Wait?
  1176. */
  1177. if (dwClientFlags & WMCS_ENDTASK) {
  1178. edp.dwFlags |= EDPF_HUNG;
  1179. } else {
  1180. edp.dwFlags |= EDPF_WAIT;
  1181. }
  1182. }
  1183. }
  1184. /*
  1185. * If the registry says no dialog, then tell the caller
  1186. * the user wants to kill the app.
  1187. */
  1188. if (gfAutoEndTask) {
  1189. dwCmd = TSN_USERSAYSKILL;
  1190. break;
  1191. }
  1192. /*
  1193. * Setup the parameters needed by EndTaskDlgProc.
  1194. */
  1195. edp.dwRet = 0;
  1196. edp.dwClientFlags = dwClientFlags;
  1197. if (dwClientFlags & WMCS_CONSOLE) {
  1198. edp.pcsrt = NULL;
  1199. edp.lParam = lParam;
  1200. } else {
  1201. edp.pcsrt = pcsrt;
  1202. edp.lParam = (LPARAM)hwnd;
  1203. }
  1204. hwndDlg = CreateDialogParam(ghModuleWin, MAKEINTRESOURCE(IDD_ENDTASK),
  1205. NULL, EndTaskDlgProc, (LPARAM)(&edp));
  1206. /*
  1207. * If we cannot ask the user, then kill the app.
  1208. */
  1209. if (hwndDlg == NULL) {
  1210. edp.dwFlags |= EDPF_NODLG;
  1211. dwCmd = TSN_USERSAYSKILL;
  1212. break;
  1213. }
  1214. break;
  1215. default:
  1216. /*
  1217. * Unexpected return; something is wrong. Kill the app.
  1218. */
  1219. UserAssert(dwRet != dwRet);
  1220. dwCmd = TSN_USERSAYSKILL;
  1221. break;
  1222. }
  1223. }
  1224. /*
  1225. * If the dialog is up, nuke it.
  1226. */
  1227. if (!(edp.dwFlags & EDPF_NODLG)) {
  1228. DestroyWindow(hwndDlg);
  1229. }
  1230. /*
  1231. * Make sure pwmcsd is freed or marked to be freed by WMCSCallback
  1232. * when the reply comes.
  1233. */
  1234. if (pwmcsd != NULL) {
  1235. if (pwmcsd->dwFlags & WMCSD_REPLY) {
  1236. LocalFree(pwmcsd);
  1237. } else {
  1238. pwmcsd->dwFlags |= WMCSD_IGNORE;
  1239. }
  1240. }
  1241. #if DBG
  1242. /*
  1243. * If cancelling, let's name the app that didn't let us log off.
  1244. */
  1245. if ((dwClientFlags & WMCS_EXIT) && (dwCmd == TSN_APPSAYSNOTOK)) {
  1246. WCHAR achTitle[CCHBODYMAX];
  1247. WCHAR *pwcText;
  1248. UserAssert(!(dwClientFlags & WMCS_CONSOLE));
  1249. pwcText = achTitle;
  1250. *(achTitle + CCHBODYMAX - 1) = (WCHAR)0;
  1251. GetApplicationText(hwnd, hThread, pwcText, CCHBODYMAX - 1);
  1252. RIPMSG3(RIP_WARNING, "Log off canceled by pid:%#lx tid:%#lx - '%ws'.\n",
  1253. HandleToUlong(pcsrt->ClientId.UniqueProcess),
  1254. HandleToUlong(pcsrt->ClientId.UniqueThread),
  1255. pwcText);
  1256. }
  1257. #endif // DBG
  1258. /*
  1259. * If we're killing this dude, clean any hard errors.
  1260. * Also if wow takes care of it, then our caller doesn't need to
  1261. */
  1262. if ((dwCmd == TSN_USERSAYSKILL) && !(dwClientFlags & WMCS_CONSOLE)) {
  1263. BoostHardError((ULONG_PTR)pcsrt->ClientId.UniqueProcess, BHE_FORCE);
  1264. if (!(pcsrt->Flags & CSR_THREAD_DESTROYED) && WowExitTask(pcsrt)) {
  1265. dwCmd = TSN_APPSAYSOK;
  1266. }
  1267. }
  1268. --dwTSNThreads;
  1269. return dwCmd;
  1270. }
  1271. /***************************************************************************\
  1272. * SetEndTaskDlgStatus
  1273. *
  1274. * Displays the appropiate message and shows the dialog
  1275. *
  1276. * 03-11-97 GerardoB Created
  1277. \***************************************************************************/
  1278. VOID SetEndTaskDlgStatus(
  1279. ENDDLGPARAMS *pedp,
  1280. HWND hwndDlg,
  1281. UINT uStrId,
  1282. BOOL fInit)
  1283. {
  1284. BOOL f, fIsWaiting, fWasWaiting;
  1285. WCHAR *pwcText;
  1286. fWasWaiting = (pedp->uStrId == STR_ENDTASK_WAIT);
  1287. fIsWaiting = (pedp->dwFlags & EDPF_WAIT) != 0;
  1288. /*
  1289. * Store the current message id, load it and show it.
  1290. */
  1291. pedp->uStrId = uStrId;
  1292. pwcText = ServerLoadString(ghModuleWin, uStrId, NULL, &f);
  1293. if (pwcText != NULL) {
  1294. SetDlgItemText(hwndDlg, IDC_STATUSMSG, pwcText);
  1295. LocalFree(pwcText);
  1296. }
  1297. /*
  1298. * If we haven't decided that the app is hung, set a
  1299. * timer to keep an eye on it.
  1300. */
  1301. if (!(pedp->dwFlags & EDPF_HUNG) && !(pedp->dwClientFlags & WMCS_CONSOLE)) {
  1302. SetTimer(hwndDlg, IDT_CHECKAPPSTATE, gCmsHungAppTimeout, NULL);
  1303. }
  1304. /*
  1305. * If initializing or switching to/from the wait mode,
  1306. * set the proper status for IDC_STATUSCANCEL, IDCANCEL,
  1307. * IDC_ENDNOW and the start/stop the progress bar.
  1308. *
  1309. * Invalidate paint if/as needed.
  1310. */
  1311. if (fInit || (fIsWaiting ^ fWasWaiting)) {
  1312. RECT rc;
  1313. HWND hwndStatusCancel = GetDlgItem(hwndDlg, IDC_STATUSCANCEL);
  1314. HWND hwndCancelButton = GetDlgItem(hwndDlg, IDCANCEL);
  1315. HWND hwndEndButton = GetDlgItem(hwndDlg, IDC_ENDNOW);
  1316. DWORD dwSwpFlags;
  1317. /*
  1318. * If on wait mode, we hide the cancel button and its
  1319. * explanatory text. The End button will be moved to
  1320. * the cancel button position.
  1321. */
  1322. dwSwpFlags = ((fIsWaiting ? SWP_HIDEWINDOW : SWP_SHOWWINDOW)
  1323. | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOMOVE
  1324. | SWP_NOZORDER | SWP_NOSENDCHANGING
  1325. | SWP_NOACTIVATE);
  1326. /*
  1327. * If we're hiding the cancel button, give focus/def id to
  1328. * the End button.
  1329. *
  1330. * Note that DM_SETDEIF won't do the right thing unless
  1331. * both Cancel/End buttons are visible.
  1332. */
  1333. if (fIsWaiting) {
  1334. SendMessage(hwndDlg, DM_SETDEFID, IDC_ENDNOW, 0);
  1335. SetFocus(hwndEndButton);
  1336. }
  1337. SetWindowPos(hwndStatusCancel, NULL, 0, 0, 0, 0, dwSwpFlags);
  1338. SetWindowPos(hwndCancelButton, NULL, 0, 0, 0, 0, dwSwpFlags);
  1339. /*
  1340. * If the cancel button is visible, give it focus/def id.
  1341. */
  1342. if (!fIsWaiting) {
  1343. SendMessage(hwndDlg, DM_SETDEFID, IDCANCEL, 0);
  1344. SetFocus(hwndCancelButton);
  1345. }
  1346. /*
  1347. * Initialize progress bar (first time around).
  1348. */
  1349. if (fIsWaiting && (pedp->hbrProgress == NULL)) {
  1350. int iMagic;
  1351. /*
  1352. * Initialize progress bar stuff.
  1353. * The size and location calculations below were made up
  1354. * to make it look good(?).
  1355. * We need that location on dialog coordiantes since the
  1356. * progress bar is painted on the dialog's WM_PAINT.
  1357. */
  1358. GetClientRect(hwndStatusCancel, &pedp->rcBar);
  1359. iMagic = (pedp->rcBar.bottom - pedp->rcBar.top) / 4;
  1360. InflateRect(&pedp->rcBar, 0, -iMagic + GetSystemMetrics(SM_CYEDGE));
  1361. pedp->rcBar.right -= (5 * iMagic);
  1362. OffsetRect(&pedp->rcBar, 0, -iMagic);
  1363. MapWindowPoints(hwndStatusCancel, hwndDlg, (LPPOINT)&pedp->rcBar, 2);
  1364. /*
  1365. * Calculate drawing rectangle and dimensions. We kind of make it
  1366. * look like comctrl's progress bar.
  1367. */
  1368. pedp->rcProgress = pedp->rcBar;
  1369. InflateRect(&pedp->rcProgress, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE));
  1370. pedp->iProgressStop = pedp->rcProgress.right;
  1371. pedp->iProgressWidth = ((2 * (pedp->rcProgress.bottom - pedp->rcProgress.top)) / 3);
  1372. pedp->rcProgress.right = pedp->rcProgress.left + pedp->iProgressWidth - 1;
  1373. pedp->hbrProgress = CreateSolidBrush(GetSysColor(COLOR_ACTIVECAPTION));
  1374. /*
  1375. * Remember the End button position.
  1376. */
  1377. GetWindowRect(hwndEndButton, &pedp->rcEndButton);
  1378. MapWindowPoints(NULL, hwndDlg, (LPPOINT)&pedp->rcEndButton, 2);
  1379. }
  1380. /*
  1381. * Start/Stop progress bar and set End button position
  1382. */
  1383. if (fIsWaiting) {
  1384. RECT rcEndButton;
  1385. UINT uTimeout = (gdwHungToKillCount * gCmsHungAppTimeout)
  1386. / ((pedp->iProgressStop - pedp->rcProgress.left) / pedp->iProgressWidth);
  1387. SetTimer(hwndDlg, IDT_PROGRESS, uTimeout, NULL);
  1388. /*
  1389. * The Cancel and the End buttons might have different widths when
  1390. * localized. So make sure we position it inside the dialog and
  1391. * to the right end of the dialog.
  1392. */
  1393. GetWindowRect(hwndCancelButton, &rc);
  1394. GetWindowRect(hwndEndButton, &rcEndButton);
  1395. rc.left = rc.right - (rcEndButton.right - rcEndButton.left);
  1396. MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rc, 2);
  1397. } else if (fWasWaiting) {
  1398. KillTimer(hwndDlg, IDT_PROGRESS);
  1399. rc = pedp->rcEndButton;
  1400. }
  1401. /*
  1402. * Move the End button if needed
  1403. */
  1404. if (fIsWaiting || fWasWaiting) {
  1405. SetWindowPos(hwndEndButton, NULL, rc.left, rc.top, 0, 0,
  1406. SWP_NOREDRAW | SWP_NOSIZE | SWP_NOACTIVATE
  1407. | SWP_NOZORDER | SWP_NOSENDCHANGING);
  1408. }
  1409. /*
  1410. * Make sure we repaint if needed
  1411. */
  1412. if (!fInit) {
  1413. InvalidateRect(hwndDlg, NULL, TRUE);
  1414. }
  1415. }
  1416. /*
  1417. * If initializing or in hung mode, make sure the user can
  1418. * see the dialog; only bring it to the foreground on
  1419. * initialization (no rude focus stealing)
  1420. */
  1421. if (fInit || (pedp->dwFlags & EDPF_HUNG)) {
  1422. SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0,
  1423. SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW
  1424. | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
  1425. if (fInit) {
  1426. SetForegroundWindow(hwndDlg);
  1427. }
  1428. }
  1429. }
  1430. /***************************************************************************\
  1431. * EndTaskDlgProc
  1432. *
  1433. * This is the dialog procedure for the dialog that comes up when an app is
  1434. * not responding.
  1435. *
  1436. * 03-06-97 GerardoB Rewrote it once again. New template though.
  1437. * 07-23-92 ScottLu Rewrote it, but used same dialog template.
  1438. * 04-28-92 JonPa Created.
  1439. \***************************************************************************/
  1440. INT_PTR APIENTRY EndTaskDlgProc(
  1441. HWND hwndDlg,
  1442. UINT msg,
  1443. WPARAM wParam,
  1444. LPARAM lParam)
  1445. {
  1446. ENDDLGPARAMS* pedp;
  1447. WCHAR achTitle[CCHBODYMAX];
  1448. WCHAR *pwcText, *pwcTemp;
  1449. UINT uLen;
  1450. UINT uStrId;
  1451. PAINTSTRUCT ps;
  1452. HDC hdc, hdcMem;
  1453. BOOL fIsInput, fWasInput;
  1454. int iOldLayout;
  1455. switch (msg) {
  1456. case WM_INITDIALOG:
  1457. /*
  1458. * Save parameters
  1459. */
  1460. pedp = (ENDDLGPARAMS*)lParam;
  1461. SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (ULONG_PTR)pedp);
  1462. /*
  1463. * This tells the caller that the dialog is up
  1464. */
  1465. pedp->dwFlags &= ~EDPF_NODLG;
  1466. /*
  1467. * Build the dialog title making sure that
  1468. * we end up with a NULL terminated string.
  1469. */
  1470. *(achTitle + CCHBODYMAX - 1) = (WCHAR)0;
  1471. uLen = GetWindowText(hwndDlg, achTitle, CCHBODYMAX - 1);
  1472. pwcText = achTitle + uLen;
  1473. uLen = CCHBODYMAX - 1 - uLen;
  1474. /*
  1475. * Console provides the title; we figure it out for windows apps.
  1476. */
  1477. if (pedp->dwClientFlags & WMCS_CONSOLE) {
  1478. pwcTemp = (WCHAR *)pedp->lParam;
  1479. while (uLen-- && (*pwcText++ = *pwcTemp++));
  1480. } else {
  1481. GetApplicationText((HWND)pedp->lParam, pedp->pcsrt->ThreadHandle, pwcText, uLen);
  1482. }
  1483. SetWindowText(hwndDlg, achTitle);
  1484. /*
  1485. * Get the app's icon: first look for atomIconProperty.
  1486. * If not available, try the class icon.
  1487. * Else, use the default icon.
  1488. */
  1489. pedp->hIcon = (HICON)GetProp((HWND)pedp->lParam, ICON_PROP_NAME);
  1490. if (pedp->hIcon == NULL) {
  1491. pedp->hIcon = (HICON)GetClassLongPtr((HWND)pedp->lParam, GCLP_HICON);
  1492. if (pedp->hIcon == NULL) {
  1493. if (pedp->dwClientFlags & WMCS_CONSOLE) {
  1494. pedp->hIcon = LoadIcon(ghModuleWin, MAKEINTRESOURCE(IDI_CONSOLE));
  1495. }
  1496. else {
  1497. pedp->hIcon = LoadIcon(NULL, IDI_APPLICATION);
  1498. }
  1499. }
  1500. }
  1501. /*
  1502. * Figure out what message the caller wants initially
  1503. */
  1504. if (pedp->dwClientFlags & WMCS_CONSOLE) {
  1505. uStrId = STR_ENDTASK_CONSOLE;
  1506. } else if (pedp->dwFlags & EDPF_INPUT) {
  1507. uStrId = STR_ENDTASK_INPUT;
  1508. } else if (pedp->dwFlags & EDPF_WAIT) {
  1509. uStrId = STR_ENDTASK_WAIT;
  1510. } else {
  1511. uStrId = STR_ENDTASK_HUNG;
  1512. }
  1513. /*
  1514. * Display the message, set the focus and show the dialog
  1515. */
  1516. SetEndTaskDlgStatus(pedp, hwndDlg, uStrId, TRUE);
  1517. return FALSE;
  1518. case WM_PAINT:
  1519. pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  1520. if ((pedp == NULL) || (pedp->hIcon == NULL)) {
  1521. break;
  1522. }
  1523. /*
  1524. * Draw the icon
  1525. */
  1526. hdc = BeginPaint(hwndDlg, &ps);
  1527. iOldLayout = GetLayout(hdc);
  1528. if (iOldLayout != GDI_ERROR) {
  1529. SetLayout(hdc, iOldLayout|LAYOUT_BITMAPORIENTATIONPRESERVED);
  1530. }
  1531. DrawIcon(hdc, ETD_XICON, ETD_YICON, pedp->hIcon);
  1532. if (iOldLayout != GDI_ERROR) {
  1533. SetLayout(hdc, iOldLayout);
  1534. }
  1535. /*
  1536. * If waiting, draw the progress bar;
  1537. * else draw the warning sign.
  1538. */
  1539. if (pedp->dwFlags & EDPF_WAIT) {
  1540. RECT rc;
  1541. /*
  1542. * Draw a client-edge-looking border.
  1543. */
  1544. rc = pedp->rcBar;
  1545. DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_RECT | BF_ADJUST);
  1546. InflateRect(&rc, -1, -1);
  1547. /*
  1548. * Draw the blocks up to the current position.
  1549. */
  1550. rc.right = rc.left + pedp->iProgressWidth - 1;
  1551. while (rc.left < pedp->rcProgress.left) {
  1552. if (rc.right > pedp->iProgressStop) {
  1553. rc.right = pedp->iProgressStop;
  1554. if (rc.left >= rc.right) {
  1555. break;
  1556. }
  1557. }
  1558. FillRect(hdc, &rc, pedp->hbrProgress);
  1559. rc.left += pedp->iProgressWidth;
  1560. rc.right += pedp->iProgressWidth;
  1561. }
  1562. } else {
  1563. /*
  1564. * Load the bitmap the first time around and
  1565. * figure out where it goes.
  1566. */
  1567. if (pedp->hbmpWarning == NULL) {
  1568. BITMAP bmp;
  1569. pedp->hbmpWarning = LoadBitmap(ghModuleWin, MAKEINTRESOURCE(IDB_WARNING));
  1570. if (GetObject(pedp->hbmpWarning, sizeof(bmp), &bmp)) {
  1571. pedp->rcWarning.left = ETD_XICON;
  1572. pedp->rcWarning.top = ETD_XICON + 32 - bmp.bmHeight;
  1573. pedp->rcWarning.right = bmp.bmWidth;
  1574. pedp->rcWarning.bottom = bmp.bmHeight;
  1575. }
  1576. }
  1577. /*
  1578. * Blit it.
  1579. */
  1580. hdcMem = CreateCompatibleDC(hdc);
  1581. SelectObject(hdcMem, pedp->hbmpWarning);
  1582. GdiTransparentBlt(hdc, pedp->rcWarning.left, pedp->rcWarning.top,
  1583. pedp->rcWarning.right, pedp->rcWarning.bottom,
  1584. hdcMem, 0, 0, pedp->rcWarning.right, pedp->rcWarning.bottom, RGB(255, 0, 255));
  1585. DeleteDC(hdcMem);
  1586. }
  1587. EndPaint(hwndDlg, &ps);
  1588. return TRUE;
  1589. case WM_TIMER:
  1590. pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  1591. if (pedp == NULL) {
  1592. return TRUE;
  1593. }
  1594. switch (wParam) {
  1595. case IDT_CHECKAPPSTATE:
  1596. pedp->dwCheckTimerCount++;
  1597. /*
  1598. * Check if the app has switched from/to a waiting-for-input
  1599. * mode. If so, update the dialog and wait a little longer
  1600. */
  1601. fIsInput = (BoostHardError((ULONG_PTR)pedp->pcsrt->ClientId.UniqueProcess, BHE_TEST)
  1602. || (GetInputWindow(pedp->pcsrt, (HWND)pedp->lParam) != NULL));
  1603. fWasInput = (pedp->dwFlags & EDPF_INPUT);
  1604. if (fIsInput ^ fWasInput) {
  1605. UINT uProgress;
  1606. pedp->dwFlags &= ~(EDPF_INPUT | EDPF_WAIT);
  1607. pedp->dwFlags |= (fIsInput ? EDPF_INPUT : EDPF_WAIT);
  1608. SetEndTaskDlgStatus(pedp, hwndDlg,
  1609. (fIsInput ? STR_ENDTASK_INPUT : STR_ENDTASK_WAIT),
  1610. FALSE);
  1611. pedp->dwCheckTimerCount /= 2;
  1612. uProgress = pedp->rcProgress.left - pedp->rcBar.left - GetSystemMetrics(SM_CXEDGE);
  1613. uProgress /= 2;
  1614. pedp->rcProgress.left -= uProgress;
  1615. pedp->rcProgress.right -= uProgress;
  1616. }
  1617. /*
  1618. * Is it time to declare it hung?
  1619. */
  1620. if (pedp->dwCheckTimerCount >= gdwHungToKillCount) {
  1621. KillTimer(hwndDlg, IDT_CHECKAPPSTATE);
  1622. pedp->dwFlags &= ~(EDPF_INPUT | EDPF_WAIT);
  1623. pedp->dwFlags |= EDPF_HUNG;
  1624. SetEndTaskDlgStatus(pedp, hwndDlg, STR_ENDTASK_HUNG, FALSE);
  1625. }
  1626. break;
  1627. case IDT_PROGRESS:
  1628. /*
  1629. * Draw the next block in the progress bar.
  1630. */
  1631. if (pedp->rcProgress.right >= pedp->iProgressStop) {
  1632. pedp->rcProgress.right = pedp->iProgressStop;
  1633. if (pedp->rcProgress.left >= pedp->rcProgress.right) {
  1634. break;
  1635. }
  1636. }
  1637. hdc = GetDC(hwndDlg);
  1638. FillRect(hdc, &pedp->rcProgress, pedp->hbrProgress);
  1639. ReleaseDC(hwndDlg, hdc);
  1640. pedp->rcProgress.left += pedp->iProgressWidth;
  1641. pedp->rcProgress.right += pedp->iProgressWidth;
  1642. break;
  1643. }
  1644. return TRUE;
  1645. case WM_NCACTIVATE:
  1646. /*
  1647. * Make sure we're uncovered when active and not covering the app
  1648. * when inactive
  1649. */
  1650. pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  1651. if (pedp != NULL) {
  1652. HWND hwnd;
  1653. if (wParam) {
  1654. hwnd = HWND_TOPMOST;
  1655. } else if (pedp->dwClientFlags & WMCS_CONSOLE) {
  1656. hwnd = HWND_TOP;
  1657. } else {
  1658. hwnd = (HWND)pedp->lParam;
  1659. }
  1660. SetWindowPos(hwndDlg, hwnd,
  1661. 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
  1662. }
  1663. break;
  1664. case WM_COMMAND:
  1665. /*
  1666. * The user has made a choice, we're done.
  1667. */
  1668. pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  1669. if (pedp != NULL) {
  1670. pedp->dwRet = (DWORD)wParam;
  1671. }
  1672. DestroyWindow(hwndDlg);
  1673. break;
  1674. case WM_DESTROY:
  1675. /*
  1676. * We're dead. Make sure the caller knows we're history
  1677. */
  1678. pedp = (ENDDLGPARAMS*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  1679. if (pedp != NULL) {
  1680. pedp->dwFlags |= (EDPF_NODLG | EDPF_RESPONSE);
  1681. if (pedp->hbmpWarning != NULL) {
  1682. DeleteObject(pedp->hbmpWarning);
  1683. }
  1684. if (pedp->hbrProgress != NULL) {
  1685. DeleteObject(pedp->hbrProgress);
  1686. }
  1687. }
  1688. break;
  1689. }
  1690. return FALSE;
  1691. }
  1692. /***************************************************************************\
  1693. * _EndTask
  1694. *
  1695. * This routine is called from the task manager to end an application - for
  1696. * gui apps, either a win32 app or a win16 app. Note: Multiple console
  1697. * processes can live in a single console window. We'll pass these requests
  1698. * for destruction to console.
  1699. *
  1700. * 07-25-92 ScottLu Created.
  1701. \***************************************************************************/
  1702. BOOL _EndTask(
  1703. HWND hwnd,
  1704. BOOL fMeanKill)
  1705. {
  1706. BOOL fRet = TRUE;
  1707. PCSR_THREAD pcsrt = CSR_SERVER_QUERYCLIENTTHREAD();
  1708. PCSR_THREAD pcsrtKill;
  1709. DWORD dwThreadId;
  1710. DWORD dwProcessId;
  1711. LPWSTR lpszMsg;
  1712. BOOL fAllocated;
  1713. DWORD dwCmd;
  1714. USERTHREAD_USEDESKTOPINFO utudi;
  1715. NTSTATUS Status;
  1716. /*
  1717. * Set the current work thread to a desktop so we can
  1718. * go safely into win32k.sys.
  1719. */
  1720. utudi.hThread = pcsrt->ThreadHandle;
  1721. utudi.drdRestore.pdeskRestore = NULL;
  1722. Status = NtUserSetInformationThread(NtCurrentThread(),
  1723. UserThreadUseDesktop, &utudi, sizeof(utudi));
  1724. if (!NT_SUCCESS(Status)) {
  1725. /*
  1726. * We were unable to get the thread's desktop. Game over.
  1727. */
  1728. return TRUE;
  1729. }
  1730. /*
  1731. * Get the process and thread that owns hwnd.
  1732. */
  1733. dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcessId);
  1734. if (dwThreadId == 0) {
  1735. goto RestoreDesktop;
  1736. }
  1737. /*
  1738. * Don't allow destruction of winlogon.
  1739. */
  1740. if (dwProcessId == gIdLogon) {
  1741. goto RestoreDesktop;
  1742. }
  1743. /*
  1744. * If this is a console window, then just send the close message to
  1745. * it, and let console clean up the processes in it.
  1746. */
  1747. if ((HANDLE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE) == ghModuleWin) {
  1748. PostMessage(hwnd, WM_CLOSE, 0, 0);
  1749. goto RestoreDesktop;
  1750. }
  1751. /*
  1752. * Find the CSR_THREAD for the window.
  1753. */
  1754. Status = CsrLockThreadByClientId(LongToHandle(dwThreadId), &pcsrtKill);
  1755. if (!NT_SUCCESS(Status)) {
  1756. /*
  1757. * This is probably the ghost thread, which CSRSS doesn't know about
  1758. * (as it's created via RtlCreateUserThread, which doesn't LPC into
  1759. * CSRSS like regular CreateThread does). When the ghost window gets
  1760. * the WM_CLOSE it'll handle removing the real window and thread. If
  1761. * this *isn't* a ghost window, then no real harm done, so we post
  1762. * no matter what.
  1763. */
  1764. PostMessage(hwnd, WM_CLOSE, 0, 0);
  1765. goto RestoreDesktop;
  1766. }
  1767. CsrReferenceThread(pcsrtKill);
  1768. CsrUnlockThread(pcsrtKill);
  1769. /*
  1770. * If this is a WOW app, then shutdown just this wow application.
  1771. */
  1772. if (!fMeanKill) {
  1773. /*
  1774. * Find out what to do now - did the user cancel or the app cancel,
  1775. * etc? Only allow cancelling if we are not forcing the app to
  1776. * exit.
  1777. */
  1778. dwCmd = ThreadShutdownNotify(WMCS_ENDTASK, (ULONG_PTR)pcsrtKill, (LPARAM)hwnd);
  1779. switch (dwCmd) {
  1780. case TSN_APPSAYSNOTOK:
  1781. /*
  1782. * App says not ok - this'll let taskman bring up the "are you sure?"
  1783. * dialog to the user.
  1784. */
  1785. CsrDereferenceThread(pcsrtKill);
  1786. fRet = FALSE;
  1787. goto RestoreDesktop;
  1788. case TSN_USERSAYSCANCEL:
  1789. /*
  1790. * User hit cancel on the timeout dialog - so the user really meant
  1791. * it. Let taskman know everything is ok by returning TRUE.
  1792. */
  1793. CsrDereferenceThread(pcsrtKill);
  1794. goto RestoreDesktop;
  1795. }
  1796. }
  1797. /*
  1798. * Kill the application now. If the thread has not been destroyed,
  1799. * nuke the task. If WowExitTask returns that the thread is not
  1800. * a WOW task, terminate the process.
  1801. */
  1802. if (!(pcsrtKill->Flags & CSR_THREAD_DESTROYED) && !WowExitTask(pcsrtKill)) {
  1803. BOOL bDoBlock;
  1804. /*
  1805. * Calling ExitProcess() in the app's context will not always work
  1806. * because the app may have .dll termination deadlocks: so the thread
  1807. * will hang with the rest of the process. To ensure apps go away,
  1808. * we terminate the process with NtTerminateProcess().
  1809. *
  1810. * Pass this special value, DBG_TERMINATE_PROCESS, which tells
  1811. * NtTerminateProcess() to return failure if it can't terminate the
  1812. * process because the app is being debugged.
  1813. */
  1814. if (ISTS()) {
  1815. NTSTATUS ExitStatus;
  1816. HANDLE DebugPort;
  1817. ExitStatus = DBG_TERMINATE_PROCESS;
  1818. if (NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(),
  1819. ProcessDebugPort,
  1820. &DebugPort,
  1821. sizeof(HANDLE),
  1822. NULL)) &&
  1823. (DebugPort != NULL)) {
  1824. // Csr is being debugged - go ahead and kill the process
  1825. ExitStatus = 0;
  1826. }
  1827. Status = NtTerminateProcess(pcsrtKill->Process->ProcessHandle, ExitStatus);
  1828. if (!NT_SUCCESS(Status) && Status != STATUS_PROCESS_IS_TERMINATING) {
  1829. bDoBlock = TRUE;
  1830. } else {
  1831. bDoBlock = FALSE;
  1832. }
  1833. } else {
  1834. Status = NtTerminateProcess(pcsrtKill->Process->ProcessHandle, DBG_TERMINATE_PROCESS);
  1835. if (!NT_SUCCESS(Status) && Status != STATUS_PROCESS_IS_TERMINATING) {
  1836. bDoBlock = TRUE;
  1837. } else {
  1838. bDoBlock = FALSE;
  1839. }
  1840. }
  1841. if (bDoBlock) {
  1842. /*
  1843. * If the app is being debugged, don't close it - because that can
  1844. * cause a hang to the NtTerminateProcess() call.
  1845. */
  1846. lpszMsg = ServerLoadString(ghModuleWin, STR_APPDEBUGGED,
  1847. NULL, &fAllocated);
  1848. if (lpszMsg) {
  1849. MessageBoxEx(NULL, lpszMsg, NULL, MB_OK | MB_SETFOREGROUND, 0);
  1850. LocalFree(lpszMsg);
  1851. }
  1852. } else {
  1853. pcsrtKill->Process->Flags |= CSR_PROCESS_TERMINATED;
  1854. }
  1855. }
  1856. CsrDereferenceThread(pcsrtKill);
  1857. RestoreDesktop:
  1858. utudi.hThread = NULL;
  1859. Status = NtUserSetInformationThread(NtCurrentThread(),
  1860. UserThreadUseDesktop, &utudi, sizeof(utudi));
  1861. UserAssert(NT_SUCCESS(Status));
  1862. return fRet;
  1863. }
  1864. /***************************************************************************\
  1865. * WowExitTask
  1866. *
  1867. * Calls wow back to make sure a specific task has exited. Returns
  1868. * TRUE if the thread is a WOW task, FALSE if not.
  1869. *
  1870. * 08-02-92 ScottLu Created.
  1871. \***************************************************************************/
  1872. BOOL WowExitTask(
  1873. PCSR_THREAD pcsrt)
  1874. {
  1875. HANDLE ahandle[2];
  1876. USERTHREAD_WOW_INFORMATION WowInfo;
  1877. NTSTATUS Status;
  1878. ahandle[1] = gheventCancel;
  1879. /*
  1880. * Query task id and exit function.
  1881. */
  1882. Status = NtUserQueryInformationThread(pcsrt->ThreadHandle,
  1883. UserThreadWOWInformation, &WowInfo, sizeof(WowInfo), NULL);
  1884. if (!NT_SUCCESS(Status)) {
  1885. return FALSE;
  1886. }
  1887. /*
  1888. * If no task id was returned, it is not a WOW task
  1889. */
  1890. if (WowInfo.hTaskWow == 0) {
  1891. return FALSE;
  1892. }
  1893. /*
  1894. * Try to make it exit itself. This will work most of the time.
  1895. * If this doesn't work, terminate this process.
  1896. */
  1897. ahandle[0] = InternalCreateCallbackThread(pcsrt->Process->ProcessHandle,
  1898. (ULONG_PTR)WowInfo.lpfnWowExitTask,
  1899. (ULONG_PTR)WowInfo.hTaskWow);
  1900. if (ahandle[0] == NULL) {
  1901. NtTerminateProcess(pcsrt->Process->ProcessHandle, 0);
  1902. pcsrt->Process->Flags |= CSR_PROCESS_TERMINATED;
  1903. goto Exit;
  1904. }
  1905. WaitForMultipleObjects(2, ahandle, FALSE, INFINITE);
  1906. NtClose(ahandle[0]);
  1907. Exit:
  1908. return TRUE;
  1909. }
  1910. /***************************************************************************\
  1911. * InternalWaitCancel
  1912. *
  1913. * Console calls this to wait for objects or shutdown to be cancelled
  1914. *
  1915. * 29-Oct-1992 mikeke Created
  1916. \***************************************************************************/
  1917. DWORD InternalWaitCancel(
  1918. HANDLE handle,
  1919. DWORD dwMilliseconds)
  1920. {
  1921. HANDLE ahandle[2];
  1922. ahandle[0] = handle;
  1923. ahandle[1] = gheventCancel;
  1924. return WaitForMultipleObjects(2, ahandle, FALSE, dwMilliseconds);
  1925. }
  1926. /***************************************************************************\
  1927. * InternalCreateCallbackThread
  1928. *
  1929. * This routine creates a remote thread in the context of a given process.
  1930. * It is used to call the console control routine, as well as ExitProcess when
  1931. * forcing an exit. Returns a thread handle.
  1932. *
  1933. * 07-28-92 ScottLu Created.
  1934. \***************************************************************************/
  1935. HANDLE InternalCreateCallbackThread(
  1936. HANDLE hProcess,
  1937. ULONG_PTR lpfn,
  1938. ULONG_PTR dwData)
  1939. {
  1940. LONG BasePriority;
  1941. HANDLE hThread, hToken;
  1942. PTOKEN_DEFAULT_DACL lpDaclDefault;
  1943. TOKEN_DEFAULT_DACL daclDefault;
  1944. ULONG cbDacl;
  1945. SECURITY_ATTRIBUTES attrThread;
  1946. SECURITY_DESCRIPTOR sd;
  1947. DWORD idThread;
  1948. NTSTATUS Status;
  1949. hThread = NULL;
  1950. Status = NtOpenProcessToken(hProcess, TOKEN_QUERY, &hToken);
  1951. if (!NT_SUCCESS(Status)) {
  1952. KdPrint(("NtOpenProcessToken failed, status = %x\n", Status));
  1953. return NULL;
  1954. }
  1955. cbDacl = 0;
  1956. NtQueryInformationToken(hToken,
  1957. TokenDefaultDacl,
  1958. &daclDefault,
  1959. sizeof(daclDefault),
  1960. &cbDacl);
  1961. lpDaclDefault = (PTOKEN_DEFAULT_DACL)LocalAlloc(LMEM_FIXED, cbDacl);
  1962. if (lpDaclDefault == NULL) {
  1963. KdPrint(("LocalAlloc failed for lpDaclDefault"));
  1964. goto closeexit;
  1965. }
  1966. Status = NtQueryInformationToken(hToken,
  1967. TokenDefaultDacl,
  1968. lpDaclDefault,
  1969. cbDacl,
  1970. &cbDacl);
  1971. if (!NT_SUCCESS(Status)) {
  1972. KdPrint(("NtQueryInformationToken failed, status = %x\n", Status));
  1973. goto freeexit;
  1974. }
  1975. if (!NT_SUCCESS(RtlCreateSecurityDescriptor(&sd,
  1976. SECURITY_DESCRIPTOR_REVISION1))) {
  1977. UserAssert(FALSE);
  1978. goto freeexit;
  1979. }
  1980. RtlSetDaclSecurityDescriptor(&sd, TRUE, lpDaclDefault->DefaultDacl, TRUE);
  1981. attrThread.nLength = sizeof(attrThread);
  1982. attrThread.lpSecurityDescriptor = &sd;
  1983. attrThread.bInheritHandle = FALSE;
  1984. GetLastError();
  1985. hThread = CreateRemoteThread(hProcess,
  1986. &attrThread,
  1987. 0L,
  1988. (LPTHREAD_START_ROUTINE)lpfn,
  1989. (LPVOID)dwData,
  1990. 0,
  1991. &idThread);
  1992. if (hThread != NULL) {
  1993. BasePriority = THREAD_PRIORITY_HIGHEST;
  1994. NtSetInformationThread(hThread,
  1995. ThreadBasePriority,
  1996. &BasePriority,
  1997. sizeof(LONG));
  1998. }
  1999. freeexit:
  2000. LocalFree((HANDLE)lpDaclDefault);
  2001. closeexit:
  2002. NtClose(hToken);
  2003. return hThread;
  2004. }
  2005. ULONG
  2006. SrvExitWindowsEx(
  2007. IN OUT PCSR_API_MSG m,
  2008. IN OUT PCSR_REPLY_STATUS ReplyStatus)
  2009. {
  2010. BEGIN_LPC_RECV(EXITWINDOWSEX);
  2011. Status = _ExitWindowsEx(pcsrt, a->uFlags);
  2012. a->fSuccess = NT_SUCCESS(Status);
  2013. END_LPC_RECV();
  2014. }
  2015. ULONG
  2016. SrvEndTask(
  2017. IN OUT PCSR_API_MSG m,
  2018. IN OUT PCSR_REPLY_STATUS ReplyStatus)
  2019. {
  2020. PENDTASKMSG petm = (PENDTASKMSG)&m->u.ApiMessageData;
  2021. PCSR_THREAD pcsrt;
  2022. PTEB Teb = NtCurrentTeb();
  2023. Teb->LastErrorValue = 0;
  2024. pcsrt = CSR_SERVER_QUERYCLIENTTHREAD();
  2025. /*
  2026. * Don't block the client so it can respond to messages while we
  2027. * process this request -- we might bring up the End Application
  2028. * dialog or the hwnd being shutdown might request some user action.
  2029. */
  2030. if (pcsrt->Process->ClientPort != NULL) {
  2031. m->ReturnValue = STATUS_SUCCESS;
  2032. petm->dwLastError = 0;
  2033. petm->fSuccess = TRUE;
  2034. NtReplyPort(pcsrt->Process->ClientPort, (PPORT_MESSAGE)m);
  2035. *ReplyStatus = CsrServerReplied;
  2036. }
  2037. petm->fSuccess = _EndTask(petm->hwnd, petm->fForce);
  2038. petm->dwLastError = Teb->LastErrorValue;
  2039. return STATUS_SUCCESS;
  2040. }
  2041. /***************************************************************************\
  2042. * IsPrivileged
  2043. *
  2044. * Check to see if the client has the specified privileges
  2045. *
  2046. * History:
  2047. * 01-02-91 JimA Created.
  2048. \***************************************************************************/
  2049. BOOL IsPrivileged(
  2050. PPRIVILEGE_SET ppSet)
  2051. {
  2052. HANDLE hToken;
  2053. NTSTATUS Status;
  2054. BOOLEAN bResult = FALSE;
  2055. /*
  2056. * Impersonate the client.
  2057. */
  2058. if (!CsrImpersonateClient(NULL)) {
  2059. return FALSE;
  2060. }
  2061. /*
  2062. * Open the client's token.
  2063. */
  2064. Status = NtOpenThreadToken(NtCurrentThread(),
  2065. TOKEN_QUERY,
  2066. TRUE,
  2067. &hToken);
  2068. if (NT_SUCCESS(Status)) {
  2069. UNICODE_STRING strSubSystem;
  2070. RtlInitUnicodeString(&strSubSystem, L"USER32");
  2071. /*
  2072. * Perform the check.
  2073. */
  2074. Status = NtPrivilegeCheck(hToken, ppSet, &bResult);
  2075. NtPrivilegeObjectAuditAlarm(&strSubSystem, NULL, hToken,
  2076. 0, ppSet, bResult);
  2077. NtClose(hToken);
  2078. if (!bResult) {
  2079. SetLastError(ERROR_ACCESS_DENIED);
  2080. }
  2081. }
  2082. CsrRevertToSelf();
  2083. if (!NT_SUCCESS(Status)) {
  2084. SetLastError(RtlNtStatusToDosError(Status));
  2085. }
  2086. /*
  2087. * Return result of privilege check.
  2088. */
  2089. return (BOOL)(bResult && NT_SUCCESS(Status));
  2090. }
  2091. /***************************************************************************\
  2092. * SrvRegisterServicesProcess
  2093. *
  2094. * Register the services process.
  2095. *
  2096. * History:
  2097. * 05-05-95 BradG Created.
  2098. \***************************************************************************/
  2099. ULONG SrvRegisterServicesProcess(
  2100. IN OUT PCSR_API_MSG m,
  2101. IN OUT PCSR_REPLY_STATUS ReplyStatus)
  2102. {
  2103. PRIVILEGE_SET psTcb = { 1, PRIVILEGE_SET_ALL_NECESSARY,
  2104. { SE_TCB_PRIVILEGE, 0 }
  2105. };
  2106. BEGIN_LPC_RECV(REGISTERSERVICESPROCESS);
  2107. /*
  2108. * Allow only one services process and then only if it has TCB
  2109. * privilege.
  2110. */
  2111. EnterCrit();
  2112. if ((gdwServicesProcessId != 0) || !IsPrivileged(&psTcb)) {
  2113. SetLastError(ERROR_ACCESS_DENIED);
  2114. a->fSuccess = FALSE;
  2115. } else {
  2116. gdwServicesProcessId = a->dwProcessId;
  2117. a->fSuccess = TRUE;
  2118. }
  2119. LeaveCrit();
  2120. END_LPC_RECV();
  2121. }
  2122. #ifdef FE_IME
  2123. /***************************************************************************\
  2124. * IsImeWindow
  2125. *
  2126. * Returns TRUE if it's an IME window.
  2127. *
  2128. * History:
  2129. * 06-05-96 KazuM Created.
  2130. \***************************************************************************/
  2131. BOOL
  2132. IsImeWindow(
  2133. HWND hwnd)
  2134. {
  2135. int num;
  2136. WCHAR ClassName[16];
  2137. num = GetClassName(hwnd, ClassName, sizeof(ClassName)/sizeof(WCHAR)-1);
  2138. if (num == 0) {
  2139. return FALSE;
  2140. }
  2141. ClassName[num] = L'\0';
  2142. if (wcsncmp(ClassName, L"IME", 3) == 0) {
  2143. return TRUE;
  2144. }
  2145. return (GetClassLong(hwnd, GCL_STYLE) & CS_IME) != 0;
  2146. }
  2147. #endif // FE_IME
  2148. /***************************************************************************\
  2149. * CancelExitWindows
  2150. *
  2151. * Cancel any logoff/shutdown that is in progress. This is called from _ExitWindowsEx
  2152. * to cancel an existing exitwindows call if a new call arrives with a different sid.
  2153. * This call is also used for Personal Terminal Services single session scenatio so
  2154. * that a force logoff can be initiated once the existing ExitWindows call is
  2155. * cancelled.
  2156. *
  2157. * History:
  2158. \***************************************************************************/
  2159. BOOL CancelExitWindows(
  2160. VOID)
  2161. {
  2162. LARGE_INTEGER li;
  2163. /*
  2164. * Another logoff/shutdown is in progress and we need
  2165. * to cancel it so we can do an override.
  2166. *
  2167. * If someone else is trying to cancel shutdown, exit.
  2168. */
  2169. EnterCrit();
  2170. li.QuadPart = 0;
  2171. if (NtWaitForSingleObject(gheventCancel, FALSE, &li) == WAIT_OBJECT_0) {
  2172. LeaveCrit();
  2173. return FALSE;
  2174. }
  2175. /*
  2176. * If no one will set gheventCancelled, don't wait.
  2177. */
  2178. if (gdwThreadEndSession == 0) {
  2179. LeaveCrit();
  2180. return TRUE;
  2181. }
  2182. NtClearEvent(gheventCancelled);
  2183. NtSetEvent(gheventCancel, NULL);
  2184. LeaveCrit();
  2185. /*
  2186. * Wait for the other guy to be cancelled
  2187. */
  2188. NtWaitForSingleObject(gheventCancelled, FALSE, NULL);
  2189. EnterCrit();
  2190. /*
  2191. * This signals that we are no longer trying to cancel a
  2192. * shutdown
  2193. */
  2194. NtClearEvent(gheventCancel);
  2195. /*
  2196. * If someone managed to start a shutdown again, exit.
  2197. * Can this happen? Let's assert to check it out.
  2198. */
  2199. if (gdwThreadEndSession != 0) {
  2200. UserAssert(gdwThreadEndSession == 0);
  2201. LeaveCrit();
  2202. return FALSE;
  2203. }
  2204. LeaveCrit();
  2205. return TRUE;
  2206. }
  2207. /***************************************************************************\
  2208. * TestShutdownPrivilege
  2209. *
  2210. * Test to see if the clent has shutdown privilege.
  2211. *
  2212. * History:
  2213. * 02-02-20 qingboz Created.
  2214. \***************************************************************************/
  2215. BOOL
  2216. TestShutdownPrivilege(
  2217. HANDLE UserToken)
  2218. {
  2219. NTSTATUS Status;
  2220. LUID LuidPrivilege = RtlConvertLongToLuid(SE_SHUTDOWN_PRIVILEGE);
  2221. LUID TokenPrivilege;
  2222. ULONG BytesRequired;
  2223. ULONG i;
  2224. BOOL bHasPrivilege = FALSE;
  2225. BOOL bNetWork = FALSE;
  2226. PSID NetworkSid = NULL;
  2227. PTOKEN_PRIVILEGES Privileges = NULL;
  2228. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  2229. Status = RtlAllocateAndInitializeSid(&NtAuthority,
  2230. 1, SECURITY_NETWORK_RID,
  2231. 0, 0, 0, 0, 0, 0, 0,
  2232. &NetworkSid );
  2233. if (!NT_SUCCESS(Status)) {
  2234. NetworkSid = NULL;
  2235. goto Cleanup;
  2236. }
  2237. if (CheckTokenMembership(UserToken, NetworkSid, &bNetWork)) {
  2238. if (bNetWork) {
  2239. LuidPrivilege = RtlConvertLongToLuid(SE_REMOTE_SHUTDOWN_PRIVILEGE);
  2240. }
  2241. } else {
  2242. goto Cleanup;
  2243. }
  2244. Status = NtQueryInformationToken(
  2245. UserToken,
  2246. TokenPrivileges,
  2247. NULL,
  2248. 0,
  2249. &BytesRequired
  2250. );
  2251. if (Status != STATUS_BUFFER_TOO_SMALL) {
  2252. goto Cleanup;
  2253. }
  2254. Privileges = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, BytesRequired);
  2255. if (!Privileges) {
  2256. goto Cleanup;
  2257. }
  2258. Status = NtQueryInformationToken(
  2259. UserToken,
  2260. TokenPrivileges,
  2261. Privileges,
  2262. BytesRequired,
  2263. &BytesRequired
  2264. );
  2265. if (!NT_SUCCESS(Status)) {
  2266. goto Cleanup;
  2267. }
  2268. for (i=0; i<Privileges->PrivilegeCount; i++) {
  2269. TokenPrivilege = *((LUID UNALIGNED *) &Privileges->Privileges[i].Luid);
  2270. if (RtlEqualLuid(&TokenPrivilege, &LuidPrivilege)) {
  2271. bHasPrivilege = TRUE;
  2272. break;
  2273. }
  2274. }
  2275. Cleanup:
  2276. if (NetworkSid) {
  2277. RtlFreeSid(NetworkSid);
  2278. }
  2279. if (Privileges) {
  2280. LocalFree(Privileges);
  2281. }
  2282. return bHasPrivilege;
  2283. }
  2284. /***************************************************************************\
  2285. * GetUserSid
  2286. *
  2287. * Allocs space for the user sid, fills it in and returns a pointer.
  2288. *
  2289. * Returns pointer to sid or NULL on failure.
  2290. *
  2291. * History:
  2292. * 02-02-20 qingboz Created.
  2293. \***************************************************************************/
  2294. PSID GetUserSid(
  2295. HANDLE UserToken)
  2296. {
  2297. PTOKEN_USER pUser;
  2298. PSID pSid;
  2299. DWORD BytesRequired = 200;
  2300. NTSTATUS status;
  2301. if (UserToken == NULL) {
  2302. return NULL;
  2303. }
  2304. //
  2305. // Allocate space for the user info
  2306. //
  2307. pUser = (PTOKEN_USER)LocalAlloc(LMEM_FIXED, BytesRequired);
  2308. if (pUser == NULL) {
  2309. return NULL;
  2310. }
  2311. //
  2312. // Read in the UserInfo
  2313. //
  2314. status = NtQueryInformationToken(
  2315. UserToken,
  2316. TokenUser,
  2317. pUser,
  2318. BytesRequired,
  2319. &BytesRequired
  2320. );
  2321. if (status == STATUS_BUFFER_TOO_SMALL) {
  2322. //
  2323. // Allocate a bigger buffer and try again.
  2324. //
  2325. PTOKEN_USER pTemp = pUser;
  2326. pUser = LocalReAlloc(pUser, BytesRequired, LMEM_MOVEABLE);
  2327. if (pUser == NULL) {
  2328. LocalFree( pTemp );
  2329. return NULL;
  2330. }
  2331. status = NtQueryInformationToken(
  2332. UserToken,
  2333. TokenUser,
  2334. pUser,
  2335. BytesRequired,
  2336. &BytesRequired
  2337. );
  2338. }
  2339. if (!NT_SUCCESS(status)) {
  2340. LocalFree(pUser);
  2341. return NULL;
  2342. }
  2343. BytesRequired = RtlLengthSid(pUser->User.Sid);
  2344. pSid = LocalAlloc(LMEM_FIXED, BytesRequired);
  2345. if (pSid == NULL) {
  2346. LocalFree(pUser);
  2347. return NULL;
  2348. }
  2349. status = RtlCopySid(BytesRequired, pSid, pUser->User.Sid);
  2350. LocalFree(pUser);
  2351. if (!NT_SUCCESS(status)) {
  2352. LocalFree(pSid);
  2353. pSid = NULL;
  2354. }
  2355. return pSid;
  2356. }
  2357. /***************************************************************************\
  2358. * SrvRecordShutdownReason
  2359. *
  2360. * Process RecordShutdownReason request from clients. This will log an event
  2361. * in the event log, and optionally take a system snapshot.
  2362. *
  2363. * History:
  2364. * 01-12-12 qingboz Created.
  2365. * 02-02-20 qingboz Added privilege check and moved some stuff from
  2366. * client to server (such as user name and SID).
  2367. \***************************************************************************/
  2368. ULONG
  2369. SrvRecordShutdownReason(
  2370. IN OUT PCSR_API_MSG m,
  2371. IN OUT PCSR_REPLY_STATUS ReplyStatus)
  2372. {
  2373. PRECORDSHUTDOWNREASONMSG prm = (PRECORDSHUTDOWNREASONMSG)&m->u.ApiMessageData;
  2374. LPWSTR lpStrings[8];
  2375. WORD wEventType;
  2376. WORD wStringCnt = 0;
  2377. WCHAR szReasonCode[32];
  2378. BOOL bRet = FALSE;
  2379. BOOL bReportEvent = FALSE;
  2380. BOOL bIsCrsssOrWinlogon = FALSE;
  2381. BOOL bIsCancelEvent = FALSE;
  2382. struct {
  2383. DWORD Reason;
  2384. PWCHAR SnapShotBuf;
  2385. } SnapShot;
  2386. UINT SnapShotSize = 0;
  2387. HANDLE hEventLog = NULL;
  2388. LPWSTR lpszUserName = NULL;
  2389. LPWSTR lpszUserDomain = NULL;
  2390. LPWSTR lpszComputerName = NULL;
  2391. LPWSTR lpszReasonTitle = NULL;
  2392. LPWSTR lpszComment = NULL;
  2393. DWORD dwComputerNameLen = MAX_COMPUTERNAME_LENGTH + 1;
  2394. DWORD dwUserNameLen = MAX_PATH + 1;
  2395. DWORD dwUserDomainLen = MAX_PATH + 1;
  2396. DWORD dwReasonTitleLen = MAX_REASON_NAME_LEN;
  2397. PSID psid;
  2398. SID_NAME_USE eUse;
  2399. HANDLE hToken;
  2400. UNREFERENCED_PARAMETER(ReplyStatus);
  2401. /*
  2402. * Need to impersonate in order to check privilege and get user name.
  2403. */
  2404. if (!CsrImpersonateClient(NULL)) {
  2405. return FALSE;
  2406. }
  2407. if (!NT_SUCCESS(NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, (BOOLEAN)TRUE, &hToken))) {
  2408. CsrRevertToSelf();
  2409. return FALSE;
  2410. }
  2411. /*
  2412. * Find out whether this is csrss/winlogon calling us.
  2413. */
  2414. if (m->h.ClientId.UniqueProcess == NtCurrentTeb()->ClientId.UniqueProcess
  2415. || HandleToUlong(m->h.ClientId.UniqueProcess) == gIdLogon) {
  2416. bIsCrsssOrWinlogon = TRUE;
  2417. }
  2418. /*
  2419. * We just need the token, so we can revert now.
  2420. */
  2421. CsrRevertToSelf();
  2422. // Check for privilege.
  2423. if (!TestShutdownPrivilege(hToken)) {
  2424. NtClose(hToken);
  2425. return FALSE;
  2426. }
  2427. // Get SID for reporting event.
  2428. psid = GetUserSid(hToken);
  2429. NtClose(hToken); // done with the token.
  2430. if (!psid) {
  2431. return FALSE;
  2432. }
  2433. SnapShot.SnapShotBuf = NULL; // so cleanup won't free an uninitialized buffer when memory alloc fails later.
  2434. /*
  2435. * For cancel event we don't need capature buffer, so we only validate
  2436. * the buffer for non-cancelling events.
  2437. */
  2438. if (prm->dwEventType == SR_EVENT_EXITWINDOWS && prm->fShutdownCancelled
  2439. || prm->dwEventType == SR_EVENT_INITIATE_CLEAN_ABORT) {
  2440. bIsCancelEvent = TRUE;
  2441. } else {
  2442. if (!m->CaptureBuffer) {
  2443. goto Cleanup;
  2444. }
  2445. if (!CsrValidateMessageBuffer(m, &prm->psr, sizeof(SHUTDOWN_REASON), sizeof(BYTE))) {
  2446. goto Cleanup;
  2447. }
  2448. if (prm->dwProcessNameLen == 0 || prm->dwProcessNameLen - 1 > MAX_PATH
  2449. || !CsrValidateMessageBuffer(m, &prm->pwchProcessName, prm->dwProcessNameLen, sizeof(WCHAR))) {
  2450. goto Cleanup;
  2451. }
  2452. if (prm->dwShutdownTypeLen == 0 || prm->dwShutdownTypeLen > SHUTDOWN_TYPE_LEN
  2453. || !CsrValidateMessageBuffer(m, &prm->pwchShutdownType, prm->dwShutdownTypeLen, sizeof(WCHAR))) {
  2454. goto Cleanup;
  2455. }
  2456. if (prm->dwCommentLen
  2457. && (prm->dwCommentLen > MAX_REASON_COMMENT_LEN || !CsrValidateMessageBuffer(m, &prm->pwchComment, prm->dwCommentLen, sizeof(WCHAR)))) {
  2458. goto Cleanup;
  2459. }
  2460. SnapShot.Reason = prm->psr->dwReasonCode;
  2461. prm->pwchProcessName[prm->dwProcessNameLen - 1] = 0;
  2462. prm->pwchShutdownType[prm->dwShutdownTypeLen - 1] = 0;
  2463. if (prm->dwCommentLen) {
  2464. prm->pwchComment[prm->dwCommentLen - 1] = 0;
  2465. }
  2466. }
  2467. /*
  2468. * This function could be called multiple times during a single shutdown,
  2469. * we need to make sure that we don't log two shutdown events. We also
  2470. * need to make sure max one dirty event per reboot.
  2471. */
  2472. if (prm->dwEventType == SR_EVENT_DIRTY) {
  2473. if (InterlockedCompareExchange((volatile LONG*)&g_DirtyShutdownMax, 0L, 1L)) {
  2474. bReportEvent = TRUE;
  2475. }
  2476. } else {
  2477. if (prm->dwEventType == SR_EVENT_EXITWINDOWS && prm->fShutdownCancelled
  2478. || prm->dwEventType == SR_EVENT_INITIATE_CLEAN_ABORT) {
  2479. /*
  2480. * If csrss or winlogon issue the abort we will log the event no matter what.
  2481. */
  2482. if (bIsCrsssOrWinlogon) {
  2483. bReportEvent = TRUE;
  2484. InterlockedCompareExchange((volatile LONG*)&g_ShutdownState, 0L, 1L);
  2485. } else if (InterlockedCompareExchange((volatile LONG*)&g_ShutdownState, 0L, 1L)) {
  2486. bReportEvent = TRUE;
  2487. }
  2488. } else {
  2489. if (!InterlockedCompareExchange((volatile LONG*)&g_ShutdownState, 1L, 0L)) {
  2490. bReportEvent = TRUE;
  2491. }
  2492. }
  2493. }
  2494. if (!bReportEvent) {
  2495. return TRUE;
  2496. }
  2497. // Allocate buffers after validations.
  2498. lpszUserName = (LPWSTR)LocalAlloc(LPTR, dwUserNameLen * sizeof(WCHAR));
  2499. lpszUserDomain = (LPWSTR)LocalAlloc(LPTR, dwUserDomainLen * sizeof(WCHAR));
  2500. lpszComputerName = (LPWSTR)LocalAlloc(LPTR, dwComputerNameLen * sizeof(WCHAR));
  2501. lpszReasonTitle = (LPWSTR)LocalAlloc(LPTR, dwReasonTitleLen * sizeof(WCHAR));
  2502. if (!lpszUserName || !lpszUserDomain || !lpszComputerName || !lpszReasonTitle) {
  2503. goto Cleanup;
  2504. }
  2505. // Get the comment.
  2506. if (!bIsCancelEvent && prm->dwCommentLen > 0) {
  2507. lpszComment = LocalAlloc(LPTR, prm->dwCommentLen * sizeof(WCHAR));
  2508. if (!lpszComment) {
  2509. goto Cleanup;
  2510. }
  2511. wcsncpy(lpszComment, prm->pwchComment, prm->dwCommentLen);
  2512. lpszComment[prm->dwCommentLen-1] = 0;
  2513. }
  2514. // Get User name.
  2515. if (!LookupAccountSidW(NULL, psid, lpszUserName, &dwUserNameLen, lpszUserDomain,
  2516. &dwUserDomainLen, &eUse)) {
  2517. //
  2518. // log an event w/o user info, because shutdown might be initiated
  2519. // when lsass was terminated unexpected.
  2520. //
  2521. lpszUserName[0] = lpszUserDomain[0] = 0;
  2522. } else {
  2523. lpszUserName[MAX_PATH] = 0;
  2524. lpszUserDomain[MAX_PATH] = 0;
  2525. // We need to pack into a buffer of MAX_PATH + 1 in the form L"domain\\username"
  2526. if (wcslen(lpszUserDomain) + wcslen(lpszUserName) > MAX_PATH - 1) {
  2527. goto Cleanup;
  2528. }
  2529. if (wcslen(lpszUserDomain) > 0) {
  2530. wcscat(lpszUserDomain, L"\\");
  2531. }
  2532. wcscat(lpszUserDomain, lpszUserName);
  2533. }
  2534. // Get Computer Name.
  2535. if (!GetComputerNameW(lpszComputerName, &dwComputerNameLen)) {
  2536. //
  2537. // log an event w/o user info, because shutdown might be initiated
  2538. // when some critical process/service got terminated unexpected.
  2539. //
  2540. lpszComputerName[0]=0;
  2541. } else {
  2542. lpszComputerName[MAX_COMPUTERNAME_LENGTH] = 0;
  2543. }
  2544. // Get reason title.
  2545. if (!GetReasonTitleFromReasonCode(prm->psr->dwReasonCode, lpszReasonTitle, dwReasonTitleLen)) {
  2546. goto Cleanup;
  2547. }
  2548. lpszReasonTitle[MAX_REASON_NAME_LEN-1] = 0;
  2549. // Get the reason code string.
  2550. _snwprintf(szReasonCode, ARRAY_SIZE(szReasonCode), L"0x%x", prm->psr->dwReasonCode);
  2551. szReasonCode[ARRAY_SIZE(szReasonCode)-1] = 0;
  2552. switch (prm->dwEventType) {
  2553. LPWSTR lpCommentStart;
  2554. LPWSTR lpCommentEnd;
  2555. INT i;
  2556. case SR_EVENT_EXITWINDOWS:
  2557. if (prm->fShutdownCancelled) {
  2558. wEventType = EVENTLOG_WARNING_TYPE;
  2559. lpStrings[wStringCnt++] = lpszComputerName;
  2560. lpStrings[wStringCnt++] = lpszUserDomain;
  2561. } else {
  2562. wEventType = EVENTLOG_INFORMATION_TYPE;
  2563. lpStrings[wStringCnt++] = prm->pwchProcessName;
  2564. lpStrings[wStringCnt++] = lpszComputerName;
  2565. lpStrings[wStringCnt++] = lpszReasonTitle;
  2566. lpStrings[wStringCnt++] = szReasonCode;
  2567. lpStrings[wStringCnt++] = prm->pwchShutdownType;
  2568. lpStrings[wStringCnt++] = lpszComment;
  2569. lpStrings[wStringCnt++] = lpszUserDomain;
  2570. }
  2571. break;
  2572. case SR_EVENT_INITIATE_CLEAN:
  2573. wEventType = EVENTLOG_INFORMATION_TYPE;
  2574. lpStrings[wStringCnt++] = prm->pwchProcessName;
  2575. lpStrings[wStringCnt++] = lpszComputerName;
  2576. lpStrings[wStringCnt++] = lpszReasonTitle;
  2577. lpStrings[wStringCnt++] = szReasonCode;
  2578. lpStrings[wStringCnt++] = prm->pwchShutdownType;
  2579. lpStrings[wStringCnt++] = lpszComment;
  2580. lpStrings[wStringCnt++] = lpszUserDomain;
  2581. break;
  2582. case SR_EVENT_INITIATE_CLEAN_ABORT:
  2583. wEventType = EVENTLOG_WARNING_TYPE;
  2584. lpStrings[wStringCnt++] = lpszComputerName;
  2585. lpStrings[wStringCnt++] = lpszUserDomain;
  2586. break;
  2587. case SR_EVENT_DIRTY:
  2588. lpCommentStart = lpszComment;
  2589. lpCommentEnd = lpCommentStart + (lpCommentStart ? wcslen(lpCommentStart) : 0);
  2590. wEventType = EVENTLOG_WARNING_TYPE;
  2591. lpStrings[wStringCnt++] = lpszReasonTitle;
  2592. lpStrings[wStringCnt++] = szReasonCode;
  2593. /*
  2594. * In case of dirty event, the comment is in the following format:
  2595. * L"nnn\nccccccccnnn\ncccccccnnn\ncccccc"
  2596. * Basically it is three strings with each one headed by its length
  2597. * and a newline.
  2598. */
  2599. for (i = 0; i < 3; i++) {
  2600. INT cnt;
  2601. if (lpCommentStart >= lpCommentEnd) {
  2602. break;
  2603. }
  2604. cnt = _wtoi(lpCommentStart);
  2605. *lpCommentStart++ = L'\0';
  2606. while (lpCommentStart < lpCommentEnd && *lpCommentStart != L'\n') {
  2607. lpCommentStart++;
  2608. }
  2609. if (*lpCommentStart) {
  2610. lpStrings[wStringCnt++] = ++lpCommentStart;
  2611. } else {
  2612. lpStrings[wStringCnt++] = NULL;
  2613. }
  2614. lpCommentStart += cnt;
  2615. }
  2616. for (; i < 3; i++) {
  2617. lpStrings[wStringCnt++] = NULL;
  2618. }
  2619. lpStrings[wStringCnt++] = lpszUserDomain;
  2620. break;
  2621. default:
  2622. RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "Unknown prm->dwEventType 0x%x", prm->dwEventType);
  2623. goto Cleanup;
  2624. }
  2625. // First see if we need to take a snapshot.
  2626. if (prm->dwEventType == SR_EVENT_INITIATE_CLEAN) {
  2627. CONST WCHAR szSnapShotVal[] = L"Snapshot";
  2628. CONST ULONG ulMaxSnapShotSize = 2048;
  2629. SNAPSHOTFUNC pSnapShotProc;
  2630. DWORD DoSnapShotValue = SNAPSHOT_POLICY_UNPLANNED;
  2631. HKEY hKey = NULL;
  2632. // First try to read the policy.
  2633. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RELIABILITY_POLICY, 0, KEY_QUERY_VALUE, &hKey)) {
  2634. DWORD dwSize = sizeof(DWORD);
  2635. DWORD dwType;
  2636. if (ERROR_SUCCESS == RegQueryValueEx(hKey, szSnapShotVal, NULL, &dwType, (UCHAR*)&DoSnapShotValue, &dwSize)) {
  2637. if (dwType != REG_DWORD) {
  2638. DoSnapShotValue = SNAPSHOT_POLICY_UNPLANNED;
  2639. } else if (DoSnapShotValue == 0) {
  2640. DoSnapShotValue = SNAPSHOT_POLICY_NEVER;
  2641. } else {
  2642. DoSnapShotValue = SNAPSHOT_POLICY_UNPLANNED;
  2643. }
  2644. }
  2645. RegCloseKey(hKey);
  2646. } // else SNAPSHOT_POLICY_UNPLANNED will be used.
  2647. /*
  2648. * SNAPSHOT_POLICY_ALWAYS: we will take a snapshot when we get here.
  2649. * SNAPSHOT_POLICY_NEVER: we wont event proceed into the if (no snapshot)
  2650. * SNAPSHOT_POLICY_UNPLANNED: Snapshot only if the reason is unplanned.
  2651. */
  2652. if (DoSnapShotValue == SNAPSHOT_POLICY_ALWAYS
  2653. || (DoSnapShotValue == SNAPSHOT_POLICY_UNPLANNED
  2654. && !(prm->psr->dwReasonCode & SHTDN_REASON_FLAG_PLANNED))) {
  2655. SnapShotSize = ulMaxSnapShotSize/sizeof(WCHAR);
  2656. SnapShot.SnapShotBuf = LocalAlloc(LPTR, ulMaxSnapShotSize);
  2657. if (SnapShot.SnapShotBuf == NULL) {
  2658. goto Cleanup;
  2659. }
  2660. SnapShot.SnapShotBuf[0] = 0;
  2661. /*
  2662. * Snapshot.dll is loaded once and will be unloaded when system
  2663. * shutdowns down. If we fail in any way make sure we can try it
  2664. * again next time this function gets called.
  2665. */
  2666. if (InterlockedCompareExchange(&g_SnapShot, 0L, 1L)) {
  2667. g_SnapShotDllHandle = LoadLibrary(L"snapshot.dll");
  2668. if (!g_SnapShotDllHandle) {
  2669. InterlockedExchange(&g_SnapShot, 1L);
  2670. }
  2671. }
  2672. if (g_SnapShotDllHandle) {
  2673. pSnapShotProc = (SNAPSHOTFUNC)GetProcAddress(g_SnapShotDllHandle, "LogSystemSnapshot");
  2674. if (pSnapShotProc) {
  2675. (*pSnapShotProc)(wStringCnt, lpStrings, &SnapShotSize, SnapShot.SnapShotBuf);
  2676. } else {
  2677. SnapShot.SnapShotBuf[0] = L'\0';
  2678. }
  2679. } else {
  2680. SnapShot.SnapShotBuf[0] = L'\0';
  2681. }
  2682. SnapShot.SnapShotBuf[ulMaxSnapShotSize/sizeof(WCHAR)-1] = L'\0';
  2683. SnapShotSize = wcslen(SnapShot.SnapShotBuf);
  2684. if (SnapShotSize > 0 ) {
  2685. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RELIABILITY, 0, KEY_ALL_ACCESS, &hKey)) {
  2686. RegSetValueEx(hKey,
  2687. REGSTR_VAL_SHUTDOWN_STATE_SNAPSHOT,
  2688. 0,
  2689. REG_SZ,
  2690. (LPBYTE)SnapShot.SnapShotBuf,
  2691. SnapShotSize * sizeof(WCHAR));
  2692. RegCloseKey(hKey);
  2693. }
  2694. }
  2695. }
  2696. }
  2697. /*
  2698. * If the client is server, we need to revert so RegisterEventSourceW() can
  2699. * succeed if the user is not in admin group.
  2700. */
  2701. if (m->h.ClientId.UniqueProcess == NtCurrentTeb()->ClientId.UniqueProcess) {
  2702. CsrRevertToSelf();
  2703. }
  2704. hEventLog = RegisterEventSourceW(NULL, L"USER32");
  2705. if (m->h.ClientId.UniqueProcess == NtCurrentTeb()->ClientId.UniqueProcess) {
  2706. CsrImpersonateClient(NULL);
  2707. }
  2708. if (!hEventLog) {
  2709. goto Cleanup;
  2710. }
  2711. /*
  2712. * We are required to log the snapshot info (if a snopshot is taken)
  2713. * into the data part, so we need to realloc the snapshot buf in order
  2714. * to insert the reason code.
  2715. */
  2716. if (!SnapShot.SnapShotBuf || wcslen(SnapShot.SnapShotBuf) == 0) { // no snapshot, so just report reason.
  2717. bRet = ReportEventW(hEventLog, wEventType, 0, prm->dwEventID, psid,
  2718. wStringCnt, sizeof(DWORD),
  2719. lpStrings, &SnapShot);
  2720. } else { /* need to repack */
  2721. DWORD dwNewBuf = (lstrlenW(SnapShot.SnapShotBuf)+1) * sizeof(WCHAR) + sizeof(DWORD);
  2722. PWCHAR pBuf = LocalAlloc(LPTR, dwNewBuf);
  2723. if (pBuf) {
  2724. *((DWORD*)pBuf) = SnapShot.Reason;
  2725. swprintf(&pBuf[2], L"%s", SnapShot.SnapShotBuf);
  2726. LocalFree(SnapShot.SnapShotBuf);
  2727. SnapShot.SnapShotBuf = pBuf;
  2728. bRet = ReportEventW(hEventLog, wEventType, 0, prm->dwEventID, psid,
  2729. wStringCnt, dwNewBuf,
  2730. lpStrings, pBuf);
  2731. }
  2732. }
  2733. DeregisterEventSource(hEventLog);
  2734. Cleanup:
  2735. LocalFree(SnapShot.SnapShotBuf);
  2736. LocalFree(psid);
  2737. LocalFree(lpszUserDomain);
  2738. LocalFree(lpszUserName);
  2739. LocalFree(lpszComputerName);
  2740. LocalFree(lpszReasonTitle);
  2741. LocalFree(lpszComment);
  2742. return bRet;
  2743. }