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.

619 lines
17 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include <msginaexports.h>
  4. #include <ntddapmt.h>
  5. #include <lmcons.h> // Username length constant
  6. #include <winsta.h> // Hydra functions/constants
  7. #include <powrprof.h>
  8. #include "SwitchUserDialog.h"
  9. #include "filetbl.h"
  10. #define DOCKSTATE_DOCKED 0
  11. #define DOCKSTATE_UNDOCKED 1
  12. #define DOCKSTATE_UNKNOWN 2
  13. void FlushRunDlgMRU(void);
  14. // Disconnect API fn-ptr
  15. typedef BOOLEAN (WINAPI *PWINSTATION_DISCONNECT) (HANDLE hServer, ULONG SessionId, BOOL bWait);
  16. // Process all of the strange ExitWindowsEx codes and privileges.
  17. STDAPI_(BOOL) CommonRestart(DWORD dwExitWinCode, DWORD dwReasonCode)
  18. {
  19. BOOL fOk;
  20. DWORD dwExtraExitCode = 0;
  21. DWORD OldState;
  22. DWORD dwError;
  23. DebugMsg(DM_TRACE, TEXT("CommonRestart(0x%x, 0x%x)"), dwExitWinCode, dwReasonCode);
  24. IconCacheSave();
  25. if ((dwExitWinCode == EWX_SHUTDOWN) && IsPwrShutdownAllowed())
  26. {
  27. dwExtraExitCode = EWX_POWEROFF;
  28. }
  29. dwError = SetPrivilegeAttribute(SE_SHUTDOWN_NAME, SE_PRIVILEGE_ENABLED, &OldState);
  30. switch (dwExitWinCode)
  31. {
  32. case EWX_SHUTDOWN:
  33. case EWX_REBOOT:
  34. case EWX_LOGOFF:
  35. if (GetKeyState(VK_CONTROL) < 0)
  36. {
  37. dwExtraExitCode |= EWX_FORCE;
  38. }
  39. break;
  40. }
  41. fOk = ExitWindowsEx(dwExitWinCode | dwExtraExitCode, dwReasonCode);
  42. // If we were able to set the privilege, then reset it.
  43. if (dwError == ERROR_SUCCESS)
  44. {
  45. SetPrivilegeAttribute(SE_SHUTDOWN_NAME, OldState, NULL);
  46. }
  47. else
  48. {
  49. // Otherwise, if we failed, then it must have been some
  50. // security stuff.
  51. if (!fOk)
  52. {
  53. ShellMessageBox(HINST_THISDLL, NULL,
  54. dwExitWinCode == EWX_SHUTDOWN ?
  55. MAKEINTRESOURCE(IDS_NO_PERMISSION_SHUTDOWN) :
  56. MAKEINTRESOURCE(IDS_NO_PERMISSION_RESTART),
  57. dwExitWinCode == EWX_SHUTDOWN ?
  58. MAKEINTRESOURCE(IDS_SHUTDOWN) :
  59. MAKEINTRESOURCE(IDS_RESTART),
  60. MB_OK | MB_ICONSTOP);
  61. }
  62. }
  63. DebugMsg(DM_TRACE, TEXT("CommonRestart done"));
  64. return fOk;
  65. }
  66. void EarlySaveSomeShellState()
  67. {
  68. // We flush two MRU's here (RecentMRU and RunDlgMRU).
  69. // Note that they won't flush if there is any reference count.
  70. FlushRunDlgMRU();
  71. }
  72. /*
  73. * Display a dialog asking the user to restart Windows, with a button that
  74. * will do it for them if possible.
  75. */
  76. STDAPI_(int) RestartDialog(HWND hParent, LPCTSTR lpPrompt, DWORD dwReturn)
  77. {
  78. return RestartDialogEx(hParent, lpPrompt, dwReturn, 0);
  79. }
  80. STDAPI_(int) RestartDialogEx(HWND hParent, LPCTSTR lpPrompt, DWORD dwReturn, DWORD dwReasonCode)
  81. {
  82. UINT id;
  83. LPCTSTR pszMsg;
  84. EarlySaveSomeShellState();
  85. if (lpPrompt && *lpPrompt == TEXT('#'))
  86. {
  87. pszMsg = lpPrompt + 1;
  88. }
  89. else if (dwReturn == EWX_SHUTDOWN)
  90. {
  91. pszMsg = MAKEINTRESOURCE(IDS_RSDLG_SHUTDOWN);
  92. }
  93. else
  94. {
  95. pszMsg = MAKEINTRESOURCE(IDS_RSDLG_RESTART);
  96. }
  97. id = ShellMessageBox(HINST_THISDLL, hParent, pszMsg, MAKEINTRESOURCE(IDS_RSDLG_TITLE),
  98. MB_YESNO | MB_ICONQUESTION, lpPrompt ? lpPrompt : c_szNULL);
  99. if (id == IDYES)
  100. {
  101. CommonRestart(dwReturn, dwReasonCode);
  102. }
  103. return id;
  104. }
  105. BOOL IsShutdownAllowed(void)
  106. {
  107. return SHTestTokenPrivilege(NULL, SE_SHUTDOWN_NAME);
  108. }
  109. // Determine if "Suspend" should appear in the shutdown dialog.
  110. // Returns: TRUE if Suspend should appear, FALSE if not.
  111. STDAPI_(BOOL) IsSuspendAllowed(void)
  112. {
  113. //
  114. // Suspend requires SE_SHUTDOWN_PRIVILEGE
  115. // Call IsShutdownAllowed() to test for this
  116. //
  117. return IsShutdownAllowed() && IsPwrSuspendAllowed();
  118. }
  119. BOOL _LogoffAvailable()
  120. {
  121. // If dwStartMenuLogoff is zero, then we remove it.
  122. BOOL fUpgradeFromIE4 = FALSE;
  123. BOOL fUserWantsLogoff = FALSE;
  124. DWORD dwStartMenuLogoff = 0;
  125. TCHAR sz[MAX_PATH];
  126. DWORD dwRestriction = SHRestricted(REST_STARTMENULOGOFF);
  127. DWORD cbData = sizeof(dwStartMenuLogoff);
  128. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED,
  129. TEXT("StartMenuLogoff"), NULL, &dwStartMenuLogoff, &cbData))
  130. {
  131. fUserWantsLogoff = (dwStartMenuLogoff != 0);
  132. }
  133. cbData = ARRAYSIZE(sz);
  134. if (SUCCEEDED(SKGetValue(SHELLKEY_HKLM_EXPLORER, TEXT("WindowsUpdate"),
  135. TEXT("UpdateURL"), NULL, sz, &cbData)))
  136. {
  137. fUpgradeFromIE4 = (sz[0] != TEXT('\0'));
  138. }
  139. // Admin is forcing the logoff to be on the menu
  140. if (dwRestriction == 2)
  141. return FALSE;
  142. // The user does wants logoff on the start menu.
  143. // Or it's an upgrade from IE4
  144. if ((fUpgradeFromIE4 || fUserWantsLogoff) && dwRestriction != 1)
  145. return FALSE;
  146. return TRUE;
  147. }
  148. DWORD GetShutdownOptions()
  149. {
  150. LONG lResult = ERROR_SUCCESS + 1;
  151. DWORD dwOptions = SHTDN_SHUTDOWN;
  152. // No shutdown on terminal server
  153. if (!GetSystemMetrics(SM_REMOTESESSION))
  154. {
  155. dwOptions |= SHTDN_RESTART;
  156. }
  157. // Add logoff if supported
  158. if (_LogoffAvailable())
  159. {
  160. dwOptions |= SHTDN_LOGOFF;
  161. }
  162. // Add the hibernate option if it's supported.
  163. if (IsPwrHibernateAllowed())
  164. {
  165. dwOptions |= SHTDN_HIBERNATE;
  166. }
  167. if (IsSuspendAllowed())
  168. {
  169. HKEY hKey;
  170. DWORD dwAdvSuspend = 0;
  171. DWORD dwType, dwSize;
  172. // At least basic sleep is supported
  173. dwOptions |= SHTDN_SLEEP;
  174. //
  175. // Check if we should offer advanced suspend options
  176. //
  177. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  178. TEXT("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power"),
  179. 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
  180. {
  181. dwSize = sizeof(dwAdvSuspend);
  182. SHQueryValueEx(hKey, TEXT("Shutdown"), NULL, &dwType,
  183. (LPBYTE) &dwAdvSuspend, &dwSize);
  184. RegCloseKey(hKey);
  185. }
  186. if (dwAdvSuspend != 0)
  187. {
  188. dwOptions |= SHTDN_SLEEP2;
  189. }
  190. }
  191. return dwOptions;
  192. }
  193. BOOL_PTR CALLBACK LogoffDlgProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
  194. {
  195. static BOOL s_fLogoffDialog = FALSE;
  196. HICON hIcon;
  197. switch (msg)
  198. {
  199. case WM_INITMENUPOPUP:
  200. EnableMenuItem((HMENU)wparam, SC_MOVE, MF_BYCOMMAND|MF_GRAYED);
  201. break;
  202. case WM_INITDIALOG:
  203. // We could call them when the user actually selects the shutdown,
  204. // but I put them here to leave the shutdown process faster.
  205. //
  206. EarlySaveSomeShellState();
  207. s_fLogoffDialog = FALSE;
  208. hIcon = LoadImage (HINST_THISDLL, MAKEINTRESOURCE(IDI_STLOGOFF),
  209. IMAGE_ICON, 48, 48, LR_DEFAULTCOLOR);
  210. if (hIcon)
  211. {
  212. SendDlgItemMessage (hdlg, IDD_LOGOFFICON, STM_SETICON, (WPARAM) hIcon, 0);
  213. }
  214. return TRUE;
  215. // Blow off moves (only really needed for 32bit land).
  216. case WM_SYSCOMMAND:
  217. if ((wparam & ~0x0F) == SC_MOVE)
  218. return TRUE;
  219. break;
  220. case WM_COMMAND:
  221. switch (LOWORD(wparam))
  222. {
  223. case IDOK:
  224. s_fLogoffDialog = TRUE;
  225. EndDialog(hdlg, SHTDN_LOGOFF);
  226. break;
  227. case IDCANCEL:
  228. s_fLogoffDialog = TRUE;
  229. EndDialog(hdlg, SHTDN_NONE);
  230. break;
  231. case IDHELP:
  232. WinHelp(hdlg, TEXT("windows.hlp>proc4"), HELP_CONTEXT, (DWORD) IDH_TRAY_SHUTDOWN_HELP);
  233. break;
  234. }
  235. break;
  236. case WM_ACTIVATE:
  237. // If we're loosing the activation for some other reason than
  238. // the user click OK/CANCEL then bail.
  239. if (LOWORD(wparam) == WA_INACTIVE && !s_fLogoffDialog)
  240. {
  241. s_fLogoffDialog = TRUE;
  242. EndDialog(hdlg, SHTDN_NONE);
  243. }
  244. break;
  245. }
  246. return FALSE;
  247. }
  248. // These dialog procedures more or less mirror the behavior of LogoffDlgProc.
  249. INT_PTR CALLBACK DisconnectDlgProc(HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
  250. {
  251. static BOOL s_fIgnoreActivate = FALSE;
  252. INT_PTR ipResult = FALSE;
  253. switch (uMsg)
  254. {
  255. case WM_INITMENUPOPUP:
  256. EnableMenuItem((HMENU)wParam, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
  257. break;
  258. case WM_INITDIALOG:
  259. {
  260. HICON hIcon;
  261. EarlySaveSomeShellState();
  262. s_fIgnoreActivate = FALSE;
  263. hIcon = LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_MU_DISCONN), IMAGE_ICON, 48, 48, LR_DEFAULTCOLOR);
  264. if (hIcon != NULL)
  265. {
  266. SendDlgItemMessage(hwndDialog, IDD_DISCONNECTICON, STM_SETICON, (WPARAM)hIcon, 0);
  267. }
  268. ipResult = TRUE;
  269. break;
  270. }
  271. case WM_SYSCOMMAND:
  272. ipResult = ((wParam & ~0x0F) == SC_MOVE);
  273. break;
  274. case WM_COMMAND:
  275. switch (LOWORD(wParam))
  276. {
  277. case IDOK:
  278. s_fIgnoreActivate = TRUE;
  279. TBOOL(EndDialog(hwndDialog, SHTDN_DISCONNECT));
  280. break;
  281. case IDCANCEL:
  282. s_fIgnoreActivate = TRUE;
  283. TBOOL(EndDialog(hwndDialog, SHTDN_NONE));
  284. break;
  285. }
  286. break;
  287. case WM_ACTIVATE:
  288. if ((WA_INACTIVE == LOWORD(wParam)) && !s_fIgnoreActivate)
  289. {
  290. s_fIgnoreActivate = TRUE;
  291. TBOOL(EndDialog(hwndDialog, SHTDN_NONE));
  292. }
  293. break;
  294. }
  295. return ipResult;
  296. }
  297. BOOL CanDoFastRestart()
  298. {
  299. return GetAsyncKeyState(VK_SHIFT) < 0;
  300. }
  301. // ---------------------------------------------------------------------------
  302. // Shutdown thread
  303. typedef struct
  304. {
  305. DWORD_PTR nCmd;
  306. HWND hwndParent;
  307. } SDTP_PARAMS;
  308. // Hydra-specific
  309. void Disconnect(void)
  310. {
  311. TW32(ShellSwitchUser(FALSE));
  312. }
  313. DWORD CALLBACK ShutdownThreadProc(void *pv)
  314. {
  315. SDTP_PARAMS *psdtp = (SDTP_PARAMS *)pv;
  316. BOOL fShutdownWorked = FALSE;
  317. // tell USER that anybody can steal foreground from us
  318. // This allows apps to put up UI during shutdown/suspend/etc.
  319. // AllowSetForegroundWindow(ASFW_ANY);
  320. switch (psdtp->nCmd)
  321. {
  322. case SHTDN_SHUTDOWN:
  323. fShutdownWorked = CommonRestart(EWX_SHUTDOWN, 0);
  324. break;
  325. case SHTDN_RESTART:
  326. fShutdownWorked = CommonRestart(CanDoFastRestart() ? EW_RESTARTWINDOWS : EWX_REBOOT, 0);
  327. break;
  328. case SHTDN_LOGOFF:
  329. fShutdownWorked = CommonRestart(EWX_LOGOFF, 0);
  330. break;
  331. case SHTDN_RESTART_DOS: // Special hack to mean exit to dos
  332. case SHTDN_SLEEP:
  333. case SHTDN_SLEEP2:
  334. case SHTDN_HIBERNATE:
  335. SetSuspendState((psdtp->nCmd == SHTDN_HIBERNATE) ? TRUE : FALSE,
  336. (GetKeyState(VK_CONTROL) < 0) ? TRUE : FALSE,
  337. (psdtp->nCmd == SHTDN_SLEEP2) ? TRUE : FALSE);
  338. break;
  339. }
  340. LocalFree(psdtp);
  341. return fShutdownWorked;
  342. }
  343. #define DIALOG_LOGOFF 1
  344. #define DIALOG_EXIT 2
  345. #define DIALOG_DISCONNECT 3
  346. void CloseWindowsDialog(HWND hwndParent, int iDialogType)
  347. {
  348. INT_PTR nCmd = SHTDN_NONE;
  349. IUnknown* pIUnknown;
  350. HWND hwndBackground;
  351. if (FAILED(ShellDimScreen(&pIUnknown, &hwndBackground)))
  352. {
  353. pIUnknown = NULL;
  354. hwndBackground = NULL;
  355. }
  356. switch (iDialogType)
  357. {
  358. LPCTSTR pszDialogID;
  359. DLGPROC pfnDialogProc;
  360. case DIALOG_LOGOFF:
  361. case DIALOG_DISCONNECT:
  362. {
  363. if (!GetSystemMetrics(SM_REMOTESESSION) && IsOS(OS_FRIENDLYLOGONUI) && IsOS(OS_FASTUSERSWITCHING))
  364. {
  365. // If not remote with friendly UI and FUS show the licky button dialog.
  366. nCmd = SwitchUserDialog_Show(hwndBackground);
  367. pszDialogID = 0;
  368. pfnDialogProc = NULL;
  369. }
  370. else if (iDialogType == DIALOG_LOGOFF)
  371. {
  372. // Otherwise show the Win32 log off dialog if log off.
  373. pszDialogID = MAKEINTRESOURCE(DLG_LOGOFFWINDOWS);
  374. pfnDialogProc = LogoffDlgProc;
  375. }
  376. else if (iDialogType == DIALOG_DISCONNECT)
  377. {
  378. // Or the Win32 disconnect dialog if disconnect.
  379. pszDialogID = MAKEINTRESOURCE(DLG_DISCONNECTWINDOWS);
  380. pfnDialogProc = DisconnectDlgProc;
  381. }
  382. else
  383. {
  384. ASSERTMSG(FALSE, "Unexpected case hit in CloseWindowsDialog");
  385. }
  386. if ((pszDialogID != 0) && (pfnDialogProc != NULL))
  387. {
  388. nCmd = DialogBoxParam(HINST_THISDLL, pszDialogID, hwndBackground, pfnDialogProc, 0);
  389. }
  390. if (nCmd == SHTDN_DISCONNECT)
  391. {
  392. Disconnect();
  393. nCmd = SHTDN_NONE;
  394. }
  395. break;
  396. }
  397. case DIALOG_EXIT:
  398. {
  399. BOOL fGinaShutdownCalled = FALSE;
  400. HINSTANCE hGina;
  401. TCHAR szUsername[UNLEN];
  402. DWORD cchUsernameLength = UNLEN;
  403. DWORD dwOptions;
  404. if (WNetGetUser(NULL, szUsername, &cchUsernameLength) != NO_ERROR)
  405. {
  406. szUsername[0] = TEXT('\0');
  407. }
  408. EarlySaveSomeShellState();
  409. // Load MSGINA.DLL and get appropriate shutdown function
  410. hGina = LoadLibrary(TEXT("msgina.dll"));
  411. if (hGina != NULL)
  412. {
  413. if (IsOS(OS_FRIENDLYLOGONUI))
  414. {
  415. nCmd = ShellTurnOffDialog(hwndBackground);
  416. fGinaShutdownCalled = TRUE;
  417. }
  418. else
  419. {
  420. PFNSHELLSHUTDOWNDIALOG pfnShellShutdownDialog = (PFNSHELLSHUTDOWNDIALOG)
  421. GetProcAddress(hGina, "ShellShutdownDialog");
  422. if (pfnShellShutdownDialog != NULL)
  423. {
  424. nCmd = pfnShellShutdownDialog(hwndBackground,
  425. szUsername, 0);
  426. // Handle disconnect right now
  427. if (nCmd == SHTDN_DISCONNECT)
  428. {
  429. Disconnect();
  430. // No other action
  431. nCmd = SHTDN_NONE;
  432. }
  433. fGinaShutdownCalled = TRUE;
  434. }
  435. }
  436. FreeLibrary(hGina);
  437. }
  438. if (!fGinaShutdownCalled)
  439. {
  440. dwOptions = GetShutdownOptions();
  441. // Gina call failed; use our cheesy private version
  442. nCmd = DownlevelShellShutdownDialog(hwndBackground,
  443. dwOptions, szUsername);
  444. }
  445. break;
  446. }
  447. }
  448. if (hwndBackground)
  449. SetForegroundWindow(hwndBackground);
  450. if (nCmd == SHTDN_NONE)
  451. {
  452. if (hwndBackground)
  453. {
  454. ShowWindow(hwndBackground, SW_HIDE);
  455. PostMessage(hwndBackground, WM_CLOSE, 0, 0);
  456. }
  457. }
  458. else
  459. {
  460. SDTP_PARAMS *psdtp = LocalAlloc(LPTR, sizeof(*psdtp));
  461. if (psdtp)
  462. {
  463. DWORD dw;
  464. HANDLE h;
  465. psdtp->nCmd = nCmd;
  466. psdtp->hwndParent = hwndParent;
  467. // have another thread call ExitWindows() so our
  468. // main pump keeps running durring shutdown.
  469. //
  470. h = CreateThread(NULL, 0, ShutdownThreadProc, psdtp, 0, &dw);
  471. if (h)
  472. {
  473. CloseHandle(h);
  474. }
  475. else
  476. {
  477. if (hwndBackground)
  478. ShowWindow(hwndBackground, SW_HIDE);
  479. ShutdownThreadProc(psdtp);
  480. }
  481. }
  482. }
  483. if (pIUnknown != NULL)
  484. {
  485. pIUnknown->lpVtbl->Release(pIUnknown);
  486. }
  487. }
  488. // API functions
  489. STDAPI_(void) ExitWindowsDialog(HWND hwndParent)
  490. {
  491. if (!IsOS(OS_FRIENDLYLOGONUI) || IsShutdownAllowed())
  492. {
  493. CloseWindowsDialog (hwndParent, DIALOG_EXIT);
  494. }
  495. else
  496. {
  497. LogoffWindowsDialog(hwndParent);
  498. }
  499. }
  500. STDAPI_(void) LogoffWindowsDialog(HWND hwndParent)
  501. {
  502. CloseWindowsDialog (hwndParent, DIALOG_LOGOFF);
  503. }
  504. STDAPI_(void) DisconnectWindowsDialog(HWND hwndParent)
  505. {
  506. CloseWindowsDialog(hwndParent, DIALOG_DISCONNECT);
  507. }