restart.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 39k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "shellprv.h"
  2. #pragma  hdrstop
  3. #ifdef WINNT
  4. // NT APM support
  5. #include <ntddapmt.h>
  6. #else
  7. // Win95 APM support
  8. #include <vmm.h>
  9. #include <bios.h>
  10. #define NOPOWERSTATUSDEFINES
  11. #include <pwrioctl.h>
  12. #include <wshioctl.h>
  13. #include <dbt.h>
  14. #include <pbt.h>
  15. #define Not_VxD
  16. #define No_CM_Calls
  17. #undef LODWORD
  18. #undef HIDWORD
  19. #include <configmg.h>
  20. #endif
  21. // Username length constant
  22. #include <lmcons.h>
  23. // Hydra functions/constants
  24. #include <winsta.h>
  25. #define DOCKSTATE_DOCKED            0
  26. #define DOCKSTATE_UNDOCKED          1
  27. #define DOCKSTATE_UNKNOWN           2
  28. // BUGBUG:: This will be pif.h...
  29. #ifndef OPENPROPS_FORCEREALMODE
  30. #define OPENPROPS_FORCEREALMODE 0x0004
  31. #endif
  32. void FlushRunDlgMRU(void);
  33. BOOL IsPowerOffAllowed(void);
  34. // in shlexec.c
  35. DWORD SHProcessMessagesUntilEvent(HWND hwnd, HANDLE hEvent, DWORD dwTimeout);
  36. // Disconnect API fn-ptr
  37. typedef BOOLEAN (*PWINSTATION_DISCONNECT) (
  38.                                            HANDLE hServer,
  39.                                            ULONG SessionId,
  40.                                            BOOL bWait
  41.                                            );
  42. // thunk to 16 bit code to do this
  43. //
  44. STDAPI_(BOOL) SHRestartWindows(DWORD dwReturn);
  45. #define ROP_DPna        0x000A0329
  46. #ifndef WINNT // {
  47. // BUGBUG perf(space): change to just use an ascii-encoded DWORD (itoa/atoi)
  48. STDAPI_(void) SHExitWindowsEx(HWND hwndParent, HINSTANCE hinstEXE, LPSTR pszCmdLine, int nCmdShow)
  49. {
  50.     DWORD dwExitWinCode = 0;
  51.     StrToIntEx(pszCmdLine, STIF_SUPPORT_HEX, &dwExitWinCode);
  52.     TraceMsg(DM_TRACE, "s.SHewe: call ExitWindowsEx(0x%x)", dwExitWinCode);
  53.     // The hwndParent needs to be destroyed. If not, one of the BroadcastSystemMessages that 
  54.     // occurs when the network drives are dismounted, will cause a dead-lock during the 
  55.     // ExitWindowsEx() call below.
  56.     // This is a fix for a QFE issue.
  57.     DestroyWindow(hwndParent);
  58.     
  59.     ExitWindowsEx(dwExitWinCode, 0);
  60. }
  61. //***   WaitExitWindowsProc -- wait for ExitWindows process to complete
  62. // ENTRY/EXIT
  63. //  returns     TRUE if ExitWindows completed, FALSE if cancelled (or failed)
  64. DWORD WaitExitWindowsProc(HANDLE hEvent, DWORD dwTimeout)
  65. {
  66.     MSG msg;
  67.     DWORD dwObject;
  68.     TraceMsg(DM_TRACE, "s.mwfmo: waiting for RunDll...");
  69.     while (1)
  70.     {
  71.         dwObject = MsgWaitForMultipleObjects(1, &hEvent, FALSE, dwTimeout, QS_ALLINPUT);
  72.         // Are we done waiting?
  73.         switch (dwObject) {
  74.         case WAIT_OBJECT_0:         // process returned
  75.         case WAIT_FAILED:
  76.             return FALSE;           // ExitWindows/WM_ENDSESSION *canceled*
  77.         case WAIT_OBJECT_0 + 1:     // got some input
  78.             // Almost.
  79.             TraceMsg(DM_TRACE, "s.mwfmo: almost done waiting");
  80.             while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  81.             {
  82.                 TranslateMessage(&msg);
  83.                 DispatchMessage(&msg);
  84.                 if (msg.message == WM_ENDSESSION) {
  85.                     TraceMsg(DM_TRACE, "s.mwfmo: WM_ENDSESSION wP=%d ret wP", msg.wParam);
  86.                     return msg.wParam;      /* tell caller whether session is ending */
  87.                 }
  88.             }
  89.             TraceMsg(DM_TRACE, "s.mwfmo: !PeekMsg (!)");
  90.             break;
  91.         }
  92.     }
  93.     // never gets here
  94.     // return dwObject;
  95. }
  96. #endif // }
  97. //***   DoExitWindowsEx -- exit windows w/ some OS-dependent hacks
  98. // NOTES
  99. //  on win95 any files held open by the process that calls ExitWindows can't
  100. //  be roamed, because the process is still running when we try to copy the
  101. //  files.
  102. //  on win95 ExitWindows doesn't notify the calling thread (process?).
  103. //  to avoid both of these pblms, we spawn a worker process to do the call.
  104. //
  105. BOOL DoExitWindowsEx(DWORD dwExitWinCode, DWORD dwReserved)
  106. {
  107.     BOOL fOk;
  108. #ifdef WINNT
  109. /*NOTHING*/
  110. #else
  111.     char szCmdLine[MAX_PATH];
  112.     SHELLEXECUTEINFO ei;
  113.     DWORD dwValue, cbSize;
  114. #endif
  115.     ASSERT(dwReserved == 0);
  116. #ifdef WINNT
  117.     fOk = ExitWindowsEx(dwExitWinCode, dwReserved);
  118. #else
  119.     cbSize = SIZEOF(dwValue);
  120.     if (NO_ERROR == SHGetValue(HKEY_LOCAL_MACHINE,
  121.       TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer"),
  122.       TEXT("NoExecExitWindows"), NULL, &dwValue, &cbSize) &&
  123.       dwValue == 1) {
  124.         TraceMsg(DM_WARNING, "s.dewe: NoExecExitWindows=%d skip spawn", dwValue);
  125.         goto Lcall;
  126.     }
  127.     wsprintf(szCmdLine, TEXT("shell32.dll,SHExitWindowsEx 0x%x"), dwExitWinCode);
  128. #ifdef DEBUG
  129.     {
  130.         #define OFF_0x  28
  131.         DWORD dwTmp = (DWORD)-1;
  132.         ASSERT(szCmdLine[OFF_0x] == TEXT('0') && szCmdLine[OFF_0x + 1] == TEXT('x'));
  133.         StrToIntEx(szCmdLine + OFF_0x, STIF_SUPPORT_HEX, &dwTmp);
  134.         ASSERT(dwTmp == dwExitWinCode);
  135.         #undef  OFF_0x
  136.     }
  137. #endif
  138.     TraceMsg(DM_TRACE, "s.dewe: dwExitWin=%x szCmd=%s", dwExitWinCode, szCmdLine);
  139.     ei.cbSize          = sizeof(SHELLEXECUTEINFO);
  140.     ei.hwnd            = NULL;
  141.     ei.lpVerb          = NULL;
  142.     ei.lpFile          = "rundll32.exe";
  143.     ei.lpParameters    = szCmdLine;
  144.     ei.lpDirectory     = NULL;
  145.     ei.nShow           = SW_SHOWNORMAL;
  146.     ei.fMask           = (SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI);
  147.     if (ShellExecuteEx(&ei) && (ei.hProcess != NULL)) {
  148.         fOk = WaitExitWindowsProc(ei.hProcess, INFINITE);
  149.         CloseHandle(ei.hProcess);
  150.         TraceMsg(DM_TRACE, "s.dewe: back from wait (?)");
  151.     }
  152.     else {
  153.         TraceMsg(DM_WARNING, "s.dewe: spawn failed");
  154. Lcall:
  155.         fOk = ExitWindowsEx(dwExitWinCode, dwReserved);
  156.     }
  157. #endif
  158.     return fOk;
  159. }
  160. // Process all of the strange ExitWindowsEx codes and privileges.
  161. //
  162. STDAPI_(BOOL) CommonRestart(DWORD dwExitWinCode)
  163. {
  164.     BOOL fOk, bOkToPowerOff = FALSE;
  165.     DWORD dwExtraExitCode = 0;
  166. #ifdef WINNT
  167.     DWORD OldState, Status;
  168.     DWORD dwErrorSave;
  169. #endif
  170.     DebugMsg(DM_TRACE, TEXT("CommonRestart(0x%x)"), dwExitWinCode);
  171.     if ((dwExitWinCode == EWX_SHUTDOWN) && IsPowerOffAllowed())
  172.     {
  173.         dwExtraExitCode = EWX_POWEROFF;
  174.     }
  175. #ifdef WINNT
  176.     SetLastError(0);        // Be really safe about last error value!
  177.     Status = SetPrivilegeAttribute(SE_SHUTDOWN_NAME,
  178.                                    SE_PRIVILEGE_ENABLED,
  179.                                    &OldState);
  180.     dwErrorSave = GetLastError();       // ERROR_NOT_ALL_ASSIGNED sometimes
  181. #endif
  182.     switch (dwExitWinCode) {
  183.     case EWX_SHUTDOWN:
  184.     case EWX_REBOOT:
  185.     case EWX_LOGOFF:
  186.         if (GetKeyState(VK_CONTROL) < 0)
  187.         {
  188.             dwExtraExitCode |= EWX_FORCE;
  189.         }
  190.         fOk = DoExitWindowsEx(dwExitWinCode|dwExtraExitCode, 0);
  191.         break;
  192.     default:
  193.         fOk = SHRestartWindows(dwExitWinCode);
  194.         break;
  195.     }
  196. #ifdef WINNT
  197.     //
  198.     // If we were able to set the privilege, then reset it.
  199.     //
  200.     if (NT_SUCCESS(Status) && dwErrorSave == 0)
  201.     {
  202.         SetPrivilegeAttribute(SE_SHUTDOWN_NAME, OldState, NULL);
  203.     }
  204.     else
  205.     {
  206.         //
  207.         // Otherwise, if we failed, then it must have been some
  208.         // security stuff.
  209.         //
  210.         if (!fOk)
  211.         {
  212.             ShellMessageBox(HINST_THISDLL, NULL,
  213.                             dwExitWinCode == EWX_SHUTDOWN ?
  214.                              MAKEINTRESOURCE(IDS_NO_PERMISSION_SHUTDOWN) :
  215.                              MAKEINTRESOURCE(IDS_NO_PERMISSION_RESTART),
  216.                             dwExitWinCode == EWX_SHUTDOWN ?
  217.                              MAKEINTRESOURCE(IDS_SHUTDOWN) :
  218.                              MAKEINTRESOURCE(IDS_RESTART),
  219.                             MB_OK | MB_ICONSTOP);
  220.         }
  221.     }
  222. #endif
  223.     DebugMsg(DM_TRACE, TEXT("CommonRestart done"));
  224.     return fOk;
  225. }
  226. void EarlySaveSomeShellState()
  227. {
  228. // We flush two MRU's here (RecentMRU and RunDlgMRU).
  229. // Note that they won't flush if there is any reference count.
  230. FlushRunDlgMRU();
  231. _IconCacheSave();
  232. }
  233. /* Display a dialog asking the user to restart Windows, with a button that
  234. ** will do it for them if possible.
  235. */
  236. STDAPI_(int) RestartDialog(HWND hParent, LPCTSTR lpPrompt, DWORD dwReturn)
  237. {
  238.     UINT id;
  239.     LPCTSTR pszMsg;
  240. EarlySaveSomeShellState();
  241.     if (lpPrompt && *lpPrompt == TEXT('#'))
  242.     {
  243.         pszMsg = lpPrompt + 1;
  244.     }
  245.     else if (dwReturn == EWX_SHUTDOWN)
  246.     {
  247.         pszMsg = MAKEINTRESOURCE(IDS_RSDLG_SHUTDOWN);
  248.     }
  249.     else
  250.     {
  251.         pszMsg = MAKEINTRESOURCE(IDS_RSDLG_RESTART);
  252.     }
  253.     id = ShellMessageBox(HINST_THISDLL, hParent, pszMsg, MAKEINTRESOURCE(IDS_RSDLG_TITLE),
  254.                 MB_YESNO | MB_ICONQUESTION, lpPrompt ? lpPrompt : c_szNULL);
  255.     if (id == IDYES)
  256.     {
  257.         CommonRestart(dwReturn);
  258.     }
  259.     return id;
  260. }
  261. //---------------------------------------------------------------------------
  262. const WORD c_GrayBits[] = {0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA};
  263. HBRUSH CreateDitheredBrush(void)
  264. {
  265.     HBITMAP hbmp = CreateBitmap(8, 8, 1, 1, c_GrayBits);
  266.     if (hbmp)
  267.     {
  268.         HBRUSH hbr = CreatePatternBrush(hbmp);
  269.         DeleteObject(hbmp);
  270.         return hbr;
  271.     }
  272.     return NULL;
  273. }
  274. // ---------------------------------------------------------------------------
  275. LRESULT CALLBACK FakeDesktopWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
  276. {
  277.     switch (msg) {
  278.     case WM_CREATE:
  279.         return TRUE;
  280.     case WM_NCPAINT:
  281.         return 0;
  282.     case WM_ACTIVATE:
  283.         DebugMsg(DM_TRACE, TEXT("FakeWndProc; WM_ACTIVATE %d hwnd=%d"), LOWORD(wparam), lparam);
  284.         if (LOWORD(wparam) == WA_INACTIVE)
  285.         {
  286.             if (lparam == 0 || GetWindow((HWND)lparam,GW_OWNER) != hwnd)
  287.             {
  288.     // Also kill ourselves if the user clicks on us or types at us
  289.     // This is important because there might be a dialog box
  290.     // the user is trying to get to which we are accidentally covering
  291.     case WM_KEYDOWN:
  292.     case WM_SYSKEYDOWN:
  293.     case WM_LBUTTONDOWN:
  294.     case WM_RBUTTONDOWN:
  295.     case WM_MBUTTONDOWN:
  296.                 DebugMsg(DM_TRACE, TEXT("FakeWndProc: death"));
  297.                 ShowWindow(hwnd, SW_HIDE);
  298.                 PostMessage(hwnd, WM_CLOSE, 0, 0);
  299.             }
  300.             return 0;
  301.         }
  302.         break;
  303.     }
  304.     return DefWindowProc(hwnd, msg, wparam, lparam);
  305. }
  306. TCHAR const c_szFakeDesktopClass[] = TEXT("FakeDesktopWClass");
  307. #if defined(WINNT) && defined(DEBUG)
  308. #define UnderDebugger() IsDebuggerPresent()
  309. #else
  310. #define UnderDebugger() FALSE
  311. #endif
  312. // ---------------------------------------------------------------------------
  313. // Create a topmost window that sits on top of the desktop but which
  314. // ignores WM_ERASEBKGND and never paints itself.
  315. HWND CreateFakeDesktopWindow(void)
  316. {
  317.     BOOL fScreenReader;
  318.     // Bad things will happen if we try to do this multiple times...
  319.     if (FindWindow(c_szFakeDesktopClass, NULL))
  320.     {
  321.         DebugMsg(DM_ERROR, TEXT("s.cfdw: Shutdown desktop already exists."));
  322.         
  323.         return NULL;
  324.     }
  325.     else if (UnderDebugger() || IsRemoteSession() || 
  326.         (SystemParametersInfo(SPI_GETSCREENREADER, 0, &fScreenReader, 0) && fScreenReader))
  327.     {
  328.         // Don't create the window; a debugger is attached or we are running
  329.         // a terminal server session.
  330.         return NULL;
  331.     }
  332.     else
  333.     {
  334.         WNDCLASS wc;
  335.         POINT ptDesktop =
  336.             {GetSystemMetrics(SM_XVIRTUALSCREEN),
  337.             GetSystemMetrics(SM_YVIRTUALSCREEN)};
  338.         SIZE sDesktop =
  339.             {GetSystemMetrics(SM_CXVIRTUALSCREEN),
  340.             GetSystemMetrics(SM_CYVIRTUALSCREEN)};
  341.         // for pre-Nashville platforms
  342.         if (!sDesktop.cx || !sDesktop.cy)
  343.         {
  344.             sDesktop.cx = GetSystemMetrics(SM_CXSCREEN);
  345.             sDesktop.cy = GetSystemMetrics(SM_CYSCREEN);
  346.         }
  347.         wc.style = 0;
  348.         wc.lpfnWndProc = FakeDesktopWndProc;
  349.         wc.cbClsExtra = 0;
  350.         wc.cbWndExtra = 0;
  351.         wc.hInstance = HINST_THISDLL;
  352.         wc.hIcon = NULL;
  353.         wc.hCursor = LoadCursor(NULL, IDC_ARROW) ;
  354.         wc.hbrBackground = NULL;
  355.         wc.lpszMenuName = NULL;
  356.         wc.lpszClassName = c_szFakeDesktopClass;
  357.         // don't really care if this is already registered...
  358.         RegisterClass(&wc);
  359.         return CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
  360.                 c_szFakeDesktopClass, NULL, WS_POPUP,
  361.                 ptDesktop.x, ptDesktop.y, sDesktop.cx, sDesktop.cy,
  362.                 NULL, NULL, HINST_THISDLL, NULL);
  363.     }
  364. }
  365. // ---------------------------------------------------------------------------
  366. void DitherWindow(HWND hwnd)
  367. {
  368.     HDC hdc = GetDC(hwnd);
  369.     if (hdc)
  370.     {
  371.         HBRUSH hbr = CreateDitheredBrush();
  372.         if (hbr)
  373.         {
  374.             RECT rc;
  375.             HBRUSH hbrOld = SelectObject(hdc, hbr);
  376.             GetClientRect(hwnd, &rc);
  377.             PatBlt(hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, ROP_DPna);
  378.             SelectObject(hdc, hbrOld);
  379.             DeleteObject(hbr);
  380.         }
  381.         ReleaseDC(hwnd, hdc);
  382.     }
  383. }
  384. // ---------------------------------------------------------------------------
  385. /* Check if system is currently in a docking station.
  386.  *
  387.  * Returns: TRUE if docked, FALSE if undocked or can't tell.
  388.  *
  389.  */
  390. BOOL GetDockedState(void)
  391. {
  392. #ifndef WINNT                       // BUGBUG - Fix this when NT gets docking capability
  393.     struct _ghwpi {                 // Get_Hardware_Profile_Info parameter blk
  394.         CMAPI   cmApi;
  395.         ULONG   ulIndex;
  396.         PFARHWPROFILEINFO pHWProfileInfo;
  397.         ULONG   ulFlags;
  398.         HWPROFILEINFO HWProfileInfo;
  399.     } *pghwpi;
  400.     HANDLE hCMHeap;
  401.     UINT Result = DOCKSTATE_UNKNOWN;
  402.     DWORD dwRecipients = BSM_VXDS;
  403. #define HEAP_SHARED     0x04000000      /* put heap in shared memory--undoc'd */
  404.     // Create a shared heap for CONFIGMG parameters
  405.     if ((hCMHeap = HeapCreate(HEAP_SHARED, 1, 4096)) == NULL)
  406.         return DOCKSTATE_UNKNOWN;
  407. #undef HEAP_SHARED
  408.     // Allocate parameter block in shared memory
  409.     pghwpi = (struct _ghwpi *)HeapAlloc(hCMHeap, HEAP_ZERO_MEMORY,
  410.                                         SIZEOF(*pghwpi));
  411.     if (pghwpi == NULL)
  412.     {
  413.         HeapDestroy(hCMHeap);
  414.         return DOCKSTATE_UNKNOWN;
  415.     }
  416.     pghwpi->cmApi.dwCMAPIRet     = 0;
  417.     pghwpi->cmApi.dwCMAPIService = GetVxDServiceOrdinal(_CONFIGMG_Get_Hardware_Profile_Info);
  418.     pghwpi->cmApi.pCMAPIStack    = (DWORD)(((LPBYTE)pghwpi) + SIZEOF(pghwpi->cmApi));
  419.     pghwpi->ulIndex              = 0xFFFFFFFF;
  420.     pghwpi->pHWProfileInfo       = &pghwpi->HWProfileInfo;
  421.     pghwpi->ulFlags              = 0;
  422.     // "Call" _CONFIGMG_Get_Hardware_Profile_Info service
  423.     BroadcastSystemMessage(0, &dwRecipients, WM_DEVICECHANGE, DBT_CONFIGMGAPI32,
  424.                            (LPARAM)pghwpi);
  425.     if (pghwpi->cmApi.dwCMAPIRet == CR_SUCCESS) {
  426.         switch (pghwpi->HWProfileInfo.HWPI_dwFlags) {
  427.             case CM_HWPI_DOCKED:
  428.                 Result = DOCKSTATE_DOCKED;
  429.                 break;
  430.             case CM_HWPI_UNDOCKED:
  431.                 Result = DOCKSTATE_UNDOCKED;
  432.                 break;
  433.             default:
  434.                 Result = DOCKSTATE_UNKNOWN;
  435.                 break;
  436.         }
  437.     }
  438.     HeapDestroy(hCMHeap);
  439.     return Result;
  440. #else
  441.     return(DOCKSTATE_DOCKED);
  442. #endif
  443. }
  444. const TCHAR c_szREGSTR_ROOT_APM[] = REGSTR_KEY_ENUM TEXT("\") REGSTR_KEY_ROOTENUM TEXT("\") REGSTR_KEY_APM TEXT("\") REGSTR_DEFAULT_INSTANCE;
  445. const TCHAR c_szREGSTR_BIOS_APM[] = REGSTR_KEY_ENUM TEXT("\") REGSTR_KEY_BIOSENUM TEXT("\") REGSTR_KEY_APM;
  446. const TCHAR c_szREGSTR_VAL_APMMENUSUSPEND[] = REGSTR_VAL_APMMENUSUSPEND;
  447. /* Open the registry APM device key
  448.  */
  449. BOOL OpenAPMKey(HKEY *phKey)
  450. {
  451.     HKEY hBiosSys;
  452.     BOOL rc = FALSE;
  453.     TCHAR szInst[MAX_PATH+1];
  454.     DWORD cchInst = ARRAYSIZE(szInst);
  455.     // Open HKLMEnumRoot*PNP0C05000 - This is the APM key for
  456.     // non-PnP BIOS machines.
  457.     if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szREGSTR_ROOT_APM, phKey) == ERROR_SUCCESS)
  458.         return TRUE;
  459.     // Open HKLMEnumBIOS*PNP0C05, Enum the 1st subkey, open that.  Example:
  460.     // HKLMEnumBIOS*PNP0C053.
  461.     if (RegOpenKey(HKEY_LOCAL_MACHINE,c_szREGSTR_BIOS_APM,&hBiosSys) == ERROR_SUCCESS)
  462.     {
  463.         if (RegEnumKey(hBiosSys, 0, szInst, cchInst) == ERROR_SUCCESS &&
  464.             RegOpenKey(hBiosSys, szInst, phKey) == ERROR_SUCCESS)
  465.             rc = TRUE;
  466.         RegCloseKey(hBiosSys);
  467.     }
  468.     return rc;
  469. }
  470. #ifndef WINNT
  471. const TCHAR c_szPOWERDevice[] = TEXT("\\.\VPOWERD");
  472. #else
  473. const TCHAR c_szPOWERDevice[] = TEXT("\\.\APMTEST");
  474. #endif
  475. BOOL CheckBIOS (void)
  476. {
  477.     HKEY hkey;
  478.     BOOL fRet = TRUE;
  479.     BOOL fSuspendUndocked = TRUE;
  480.     /* Check the Registry APM key for an APMMenuSuspend value.
  481.      * APMMenuSuspend may have the following values: APMMENUSUSPEND_DISABLED,
  482.      * APMMENUSUSPEND_ENABLED, or APMMENUSUSPEND_UNDOCKED.
  483.      *
  484.      * An APMMenuSuspend value of APMMENUSUSPEND_DISABLED means the
  485.      * tray should never show the Suspend menu item on its menu.
  486.      *
  487.      * APMMENUSUSPEND_ENABLED means the Suspend menu item should be shown
  488.      * if the machine has APM support enabled (VPOWERD is loaded).  This is
  489.      * the default.
  490.      *
  491.      * APMMENUSUSPEND_UNDOCKED means the Suspend menu item should be shown,
  492.      * but only enabled when the machine is not in a docking station.
  493.      *
  494.      */
  495.     if (OpenAPMKey(&hkey))
  496.     {
  497.         BYTE bMenuSuspend = APMMENUSUSPEND_ENABLED;
  498.         DWORD dwType, cbSize = SIZEOF(bMenuSuspend);
  499.         if (SHQueryValueEx(hkey, c_szREGSTR_VAL_APMMENUSUSPEND, 0,
  500.                             &dwType, &bMenuSuspend, &cbSize) == ERROR_SUCCESS)
  501.         {
  502.             bMenuSuspend &= ~(APMMENUSUSPEND_NOCHANGE);     // don't care about nochange flag
  503.             if (bMenuSuspend == APMMENUSUSPEND_UNDOCKED)
  504.                 fSuspendUndocked = TRUE;
  505.             else
  506.             {
  507.                 fSuspendUndocked = FALSE;
  508.                 if (bMenuSuspend == APMMENUSUSPEND_DISABLED)
  509.                     fRet = FALSE;
  510.             }
  511.         }
  512.         RegCloseKey(hkey);
  513.     }
  514.     if (fRet)
  515.     {
  516.         // Disable Suspend menu item if 1) only wanted when undocked and
  517.         // system is currently docked, 2) power mgnt level < advanced
  518.         if (fSuspendUndocked && GetDockedState() == DOCKSTATE_DOCKED)
  519.             fRet = FALSE;
  520.         else
  521.         {
  522.             DWORD dwPmLevel, cbOut;
  523.             BOOL fIoSuccess;
  524.             HANDLE hVPowerD;
  525.             hVPowerD = CreateFile(c_szPOWERDevice,
  526.                                   GENERIC_READ|GENERIC_WRITE,
  527.                                   FILE_SHARE_READ|FILE_SHARE_WRITE,
  528.                                   NULL, OPEN_EXISTING, 0, NULL);
  529.             if (hVPowerD != INVALID_HANDLE_VALUE) {
  530.                 fIoSuccess = DeviceIoControl(hVPowerD,
  531. #ifdef WINNT                   // BUGBUG - why does NT use a different define?
  532.                                              APM_IOCTL_GET_PM_LEVEL,
  533. #else
  534.                                              VPOWERD_IOCTL_GET_PM_LEVEL,
  535. #endif
  536.                                              NULL, 0, &dwPmLevel, SIZEOF(dwPmLevel), &cbOut, NULL);
  537.                 fRet = (fIoSuccess && (dwPmLevel == PMLEVEL_ADVANCED));
  538.                 CloseHandle (hVPowerD);
  539.             } else {
  540.                 fRet = FALSE;
  541.             }
  542.         }
  543.     }
  544.     return fRet;
  545. }
  546. typedef BOOLEAN (*PFNSETSUSPENDSTATE)(BOOLEAN, BOOLEAN, BOOLEAN);
  547. BOOL SuspendComputer (BOOL bHibernate, BOOL bForce, BOOL bDisableWakeup)
  548. {
  549.     PFNSETSUSPENDSTATE pfnSetSuspendState;
  550.     HINSTANCE hInstPowrProf;
  551.     BOOLEAN bResult;
  552.     OSVERSIONINFO osvi;
  553.     osvi.dwOSVersionInfoSize = sizeof(osvi);
  554.     GetVersionEx(&osvi);
  555.     //
  556.     // See if we are running on one of the 4.0 products.
  557.     //
  558.     // BugBug:  Memphis hasn't implemented all the NT* power mgmt
  559.     // functions yet, so revert back to calling SetSystemPowerState
  560.     // until they're ready.
  561. #if 0
  562.     if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0))
  563. #else
  564.     if (osvi.dwMajorVersion == 4)
  565. #endif
  566.     {
  567.         if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
  568.         {
  569.             return FALSE;
  570.         }
  571.         return SetSystemPowerState (!bHibernate, bForce);
  572.     }
  573.     //
  574.     // For NT5 and Memphis, use the new power mgmt stuff.
  575.     //
  576.     hInstPowrProf = LoadLibrary(TEXT("POWRPROF.DLL"));
  577.     if (!hInstPowrProf)
  578.     {
  579.         return FALSE;
  580.     }
  581.     pfnSetSuspendState =
  582.         (PFNSETSUSPENDSTATE)GetProcAddress(hInstPowrProf,
  583.                                            "SetSuspendState");
  584.     if (!pfnSetSuspendState) {
  585.         FreeLibrary (hInstPowrProf);
  586.         return FALSE;
  587.     }
  588.     //
  589.     // Call SetSuspendState
  590.     //
  591.     bResult = pfnSetSuspendState((BOOLEAN)bHibernate, (BOOLEAN)bForce,
  592.                                  (BOOLEAN) bDisableWakeup);
  593.     FreeLibrary (hInstPowrProf);
  594.     return (BOOL) bResult;
  595. }
  596. typedef BOOLEAN (*PFNISPWRSUSPENDALLOWED)(VOID);
  597. BOOL QueryPowerInformation (VOID)
  598. {
  599.     PFNISPWRSUSPENDALLOWED pfnIsPwrSuspendAllowed;
  600.     HINSTANCE hInstPowrProf;
  601.     BOOLEAN bResult;
  602.     //
  603.     // Load powrprof.dll
  604.     //
  605.     hInstPowrProf = LoadLibrary(TEXT("POWRPROF.DLL"));
  606.     if (!hInstPowrProf)
  607.     {
  608.         return FALSE;
  609.     }
  610.     pfnIsPwrSuspendAllowed =
  611.         (PFNISPWRSUSPENDALLOWED)GetProcAddress(hInstPowrProf,
  612.                                               "IsPwrSuspendAllowed");
  613.     if (!pfnIsPwrSuspendAllowed) {
  614.         FreeLibrary (hInstPowrProf);
  615.         return FALSE;
  616.     }
  617.     //
  618.     // Call IsPwrSuspendAllowed to query for the capabilities
  619.     //
  620.     bResult = pfnIsPwrSuspendAllowed();
  621.     FreeLibrary (hInstPowrProf);
  622.     return (BOOL) bResult;
  623. }
  624. typedef BOOLEAN (*PFNISPWRHIBERNATEALLOWED)(VOID);
  625. BOOL IsHibernateAllowed(void)
  626. {
  627.     PFNISPWRHIBERNATEALLOWED pfnIsPwrHibernateAllowed;
  628.     HINSTANCE hInstPowrProf;
  629.     BOOLEAN bResult;
  630.     OSVERSIONINFO osvi;
  631.     osvi.dwOSVersionInfoSize = sizeof(osvi);
  632.     GetVersionEx(&osvi);
  633.     //
  634.     // See if we are running on one of the 4.0 products.
  635.     //
  636.     // bugbug:  Memphis doesn't support calling for hibernate yet
  637. #if 0
  638.     if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0))
  639. #else
  640.     if (osvi.dwMajorVersion == 4)
  641. #endif
  642.     {
  643.         return FALSE;
  644.     }
  645.     //
  646.     // For NT5 and Memphis, use the new power mgmt stuff.
  647.     //
  648.     hInstPowrProf = LoadLibrary(TEXT("POWRPROF.DLL"));
  649.     if (!hInstPowrProf)
  650.     {
  651.         return FALSE;
  652.     }
  653.     pfnIsPwrHibernateAllowed =
  654.         (PFNISPWRHIBERNATEALLOWED)GetProcAddress(hInstPowrProf,
  655.                                               "IsPwrHibernateAllowed");
  656.     if (!pfnIsPwrHibernateAllowed) {
  657.         FreeLibrary (hInstPowrProf);
  658.         return FALSE;
  659.     }
  660.     //
  661.     // Call IsPwrHibernateAllowed to query for the capabilities
  662.     //
  663.     bResult = pfnIsPwrHibernateAllowed();
  664.     FreeLibrary (hInstPowrProf);
  665.     return (BOOL) bResult;
  666. }
  667. typedef BOOLEAN (*PFNISPWRSHUTDOWNALLOWED)(VOID);
  668. BOOL IsPowerOffAllowed(void)
  669. {
  670.     PFNISPWRSHUTDOWNALLOWED pfnIsPwrShutdownAllowed;
  671.     HINSTANCE hInstPowrProf;
  672.     BOOLEAN bResult;
  673.     OSVERSIONINFO osvi;
  674.     osvi.dwOSVersionInfoSize = sizeof(osvi);
  675.     GetVersionEx(&osvi);
  676.     //
  677.     // See if we are running on one of the 4.0 products.
  678.     //
  679.     if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0))
  680.     {
  681.         if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
  682.         {
  683.             if (0 == GetProfileInt(TEXT("WINLOGON"), TEXT("PowerdownAfterShutdown"), 0))
  684.             {
  685.                 return FALSE;
  686.             }
  687.         }
  688.         return TRUE;
  689.     }
  690.     //
  691.     // For NT5 and Memphis, use the new power mgmt stuff.
  692.     //
  693.     hInstPowrProf = LoadLibrary(TEXT("POWRPROF.DLL"));
  694.     if (!hInstPowrProf)
  695.     {
  696.         return FALSE;
  697.     }
  698.     pfnIsPwrShutdownAllowed =
  699.         (PFNISPWRSHUTDOWNALLOWED)GetProcAddress(hInstPowrProf,
  700.                                               "IsPwrShutdownAllowed");
  701.     if (!pfnIsPwrShutdownAllowed) {
  702.         FreeLibrary (hInstPowrProf);
  703.         return FALSE;
  704.     }
  705.     //
  706.     // Call IsPwrShutdownAllowed to query for the capabilities
  707.     //
  708.     bResult = pfnIsPwrShutdownAllowed();
  709.     FreeLibrary (hInstPowrProf);
  710.     return (BOOL) bResult;
  711. }
  712. /* Determine if "Suspend" should appear in the shutdown dialog.
  713.  *
  714.  * Returns: TRUE if Suspend should appear, FALSE if not.
  715.  */
  716. STDAPI_(BOOL) IsSuspendAllowed(void)
  717. {
  718.     OSVERSIONINFO osvi;
  719.     osvi.dwOSVersionInfoSize = sizeof(osvi);
  720.     GetVersionEx(&osvi);
  721.     //
  722.     // See if we are running on one of the 4.0 products.
  723.     //
  724.     if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0))
  725.     {
  726.         //
  727.         // NT 4 has no power mgmt support.
  728.         //
  729.         if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
  730.         {
  731.             return FALSE;
  732.         }
  733.         //
  734.         // On Win95, check the BIOS.
  735.         //
  736.         return CheckBIOS();
  737.     }
  738.     //
  739.     // For NT5 and Memphis, use the new power mgmt stuff.
  740.     //
  741.     return QueryPowerInformation();
  742. }
  743. // ---------------------------------------------------------------------------
  744. BOOL _LogoffAvailable()
  745. {
  746.         // If dwStartMenuLogoff is zero, then we remove it.
  747.     BOOL fUpgradeFromIE4 = FALSE;
  748.     BOOL fUserWantsLogoff = FALSE;
  749.     DWORD dwStartMenuLogoff = 0;
  750.     TCHAR sz[MAX_PATH];
  751.     DWORD dwRestriction = SHRestricted(REST_STARTMENULOGOFF);
  752.     DWORD cbData = sizeof(dwStartMenuLogoff);
  753.     if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER TEXT("\Advanced"), 
  754.                     TEXT("StartMenuLogoff"), NULL, &dwStartMenuLogoff, &cbData))
  755.     {
  756.         fUserWantsLogoff = (dwStartMenuLogoff != 0);
  757.     }
  758.     cbData = ARRAYSIZE(sz);
  759.     if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_EXPLORER TEXT("\WindowsUpdate"), 
  760.                     TEXT("UpdateURL"), NULL, (DWORD*)sz, &cbData))
  761.     {
  762.         fUpgradeFromIE4 = (sz[0] != TEXT(''));
  763.     }
  764.     // Admin is forcing the logoff to be on the menu
  765.     if (dwRestriction == 2)
  766.         return FALSE;
  767.     // The user does wants logoff on the start menu.
  768.     // Or it's an upgrade from IE4
  769.     if ((fUpgradeFromIE4 || fUserWantsLogoff) && dwRestriction != 1)
  770.         return FALSE;
  771.     return TRUE;
  772. }
  773. DWORD GetShutdownOptions()
  774. {
  775.     LONG lResult = ERROR_SUCCESS + 1;
  776.     DWORD dwOptions = SHTDN_SHUTDOWN;
  777.     // No shutdown on terminal server
  778.     if (!IsRemoteSession())
  779.     {
  780.         dwOptions |= SHTDN_RESTART;
  781.     }
  782.     // Add logoff if supported
  783.     if (_LogoffAvailable())
  784.     {
  785.         dwOptions |= SHTDN_LOGOFF;
  786.     }
  787.     // No restart to DOS if not supported (always in NT)
  788. #ifndef WINNT
  789.     // Don't have restart to dos if restricted or in clean boot as it
  790.     // won't work there!
  791.     if (!SHRestricted(REST_NOEXITTODOS) && !GetSystemMetrics(SM_CLEANBOOT))
  792.     {
  793.         dwOptions |= SHTDN_RESTART_DOS;
  794.     }
  795. #endif
  796.     //
  797.     // Add the hibernate option if it's supported.
  798.     //
  799.     if (IsHibernateAllowed())
  800.     {
  801.         dwOptions |= SHTDN_HIBERNATE;
  802.     }
  803.     if (IsSuspendAllowed())
  804.     {
  805.         HKEY hKey;
  806.         DWORD dwAdvSuspend = 0;
  807.         DWORD dwType, dwSize;
  808.         // At least basic sleep is supported
  809.         dwOptions |= SHTDN_SLEEP;
  810.         //
  811.         // Check if we should offer advanced suspend options
  812.         //
  813.         if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
  814.                           TEXT("SYSTEM\CurrentControlSet\Control\Session Manager\Power"),
  815.                           0, KEY_READ, &hKey) == ERROR_SUCCESS)
  816.         {
  817.             dwSize = sizeof(dwAdvSuspend);
  818.             SHQueryValueEx (hKey, TEXT("Shutdown"), NULL, &dwType,
  819.                                  (LPBYTE) &dwAdvSuspend, &dwSize);
  820.             RegCloseKey (hKey);
  821.         }
  822.         if (dwAdvSuspend != 0)
  823.         {
  824.             dwOptions |= SHTDN_SLEEP2;
  825.         }
  826.     }
  827.     return dwOptions;
  828. }
  829. BOOL_PTR CALLBACK LogoffWindowsDlgProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
  830. {
  831.     static BOOL s_fLogoffDialog = FALSE;
  832.     HICON hIcon;
  833.     switch (msg)
  834.     {
  835.     case WM_INITMENUPOPUP:
  836.         EnableMenuItem((HMENU)wparam, SC_MOVE, MF_BYCOMMAND|MF_GRAYED);
  837.         break;
  838.     case WM_INITDIALOG:
  839. // We could call them when the user actually selects the shutdown,
  840. // but I put them here to leave the shutdown process faster.
  841. //
  842. EarlySaveSomeShellState();
  843.         s_fLogoffDialog = FALSE;
  844.         hIcon = LoadImage (HINST_THISDLL, MAKEINTRESOURCE(IDI_STLOGOFF),
  845.                            IMAGE_ICON, 48, 48, LR_DEFAULTCOLOR);
  846.         if (hIcon)
  847.         {
  848.             SendDlgItemMessage (hdlg, IDD_LOGOFFICON, STM_SETICON, (WPARAM) hIcon, 0);
  849.         }
  850.         return TRUE;
  851.     // Blow off moves (only really needed for 32bit land).
  852.     case WM_SYSCOMMAND:
  853.         if ((wparam & ~0x0F) == SC_MOVE)
  854.             return TRUE;
  855.         break;
  856.     case WM_COMMAND:
  857.         switch (LOWORD(wparam))
  858.         {
  859.         case IDOK:
  860.             s_fLogoffDialog = TRUE;
  861.             EndDialog(hdlg, SHTDN_LOGOFF);
  862.             break;
  863.         case IDCANCEL:
  864.             s_fLogoffDialog = TRUE;
  865.             EndDialog(hdlg, SHTDN_NONE);
  866.             break;
  867.         case IDHELP:
  868.             WinHelp(hdlg, TEXT("windows.hlp>proc4"), HELP_CONTEXT, (DWORD) IDH_TRAY_SHUTDOWN_HELP);
  869.             break;
  870.         }
  871.         break;
  872.     case WM_ACTIVATE:
  873.         // If we're loosing the activation for some other reason than
  874.         // the user click OK/CANCEL then bail.
  875.         if (LOWORD(wparam) == WA_INACTIVE && !s_fLogoffDialog)
  876.         {
  877.             s_fLogoffDialog = TRUE;
  878.             EndDialog(hdlg, SHTDN_NONE);
  879.         }
  880.         break;
  881.     }
  882.     return FALSE;
  883. }
  884. #ifdef WINNT
  885. #define ExitToDos(hwnd)
  886. #else
  887. // ---------------------------------------------------------------------------
  888. // Function to try to exit to dos
  889. void ExitToDos(HWND hwnd)
  890. {
  891.     // For now I will assume that the pif is called sdam.pif in the windows
  892.     // directory... If it is not found we should generate it, but for now I will
  893.     // fallback to ExitWindowsExec of command.com...
  894.     TCHAR szPath[MAX_PATH];
  895.     TCHAR szCommand[MAX_PATH];
  896.     GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
  897.     LoadString(HINST_THISDLL,  IsLFNDrive(szPath)? IDS_RSDLG_PIFFILENAME : IDS_RSDLG_PIFSHORTFILENAME,
  898.             szCommand, ARRAYSIZE(szCommand));
  899.     PathCombine(szPath, szPath, szCommand);
  900.     if (!PathFileExists(szPath))
  901.     {
  902.         PROPPRG ProgramProps;
  903.         HANDLE hPif;
  904.         lstrcpy(szCommand, TEXT("command.com"));
  905.         PathResolve(szCommand, NULL, 0);
  906.         hPif = PifMgr_OpenProperties(szCommand, szPath, 0, OPENPROPS_INFONLY|OPENPROPS_FORCEREALMODE);
  907.         if (!hPif)
  908.         {
  909.             DebugMsg(DM_TRACE, TEXT("ExitToDos: PifMgr_OpenProperties *failed*"));
  910.             goto Abort;
  911.         }
  912.         if (!PifMgr_GetProperties(hPif, (LPSTR)MAKEINTATOM(GROUP_PRG), &ProgramProps, SIZEOF(ProgramProps), 0))
  913.         {
  914.             DebugMsg(DM_TRACE, TEXT("ExitToDos: PifMgr_GetProperties *failed*"));
  915.             goto Abort;
  916.         }
  917.         PathQuoteSpaces(szCommand);
  918.         lstrcpyn(ProgramProps.achCmdLine, szCommand, ARRAYSIZE(ProgramProps.achCmdLine));
  919.         // We probably do not wan't to prompt the user again that they are going into
  920.         // MSDOS Mode...
  921.         //
  922.         ProgramProps.flPrgInit |= PRGINIT_REALMODESILENT | PRGINIT_REALMODE;
  923.         if (!PifMgr_SetProperties(hPif, MAKEINTATOM(GROUP_PRG), &ProgramProps, SIZEOF(ProgramProps), 0))
  924.         {
  925.             DebugMsg(DM_TRACE, TEXT("ExitToDos: PifMgr_SetProperties *failed*"));
  926. Abort:
  927.             // BUGBUG:: SHould probably put up a message here...
  928.             MessageBeep(0);
  929.             return;
  930.         }
  931.         PifMgr_CloseProperties(hPif, 0);
  932.     }
  933.     ShellExecute(hwnd, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);
  934. }
  935. #endif
  936. BOOL CanDoFastRestart()
  937. {
  938.     return GetAsyncKeyState(VK_SHIFT) < 0;
  939. }
  940. // ---------------------------------------------------------------------------
  941. // Shutdown thread
  942. typedef struct {
  943.     DWORD_PTR nCmd;
  944.     HWND hwndParent;
  945. } SDTP_PARAMS;
  946. // Hydra-specific
  947. #ifdef WINNT
  948. void Disconnect()
  949. {
  950.     static PWINSTATION_DISCONNECT pfnWinStationDisconnect = NULL;
  951.     HANDLE dllHandle;
  952.     if (IsRemoteSession())
  953.     {
  954.         if (NULL == pfnWinStationDisconnect)
  955.         {
  956.             //
  957.             // Load winsta.dll
  958.             //
  959.             dllHandle = LoadLibraryW(L"winsta.dll");
  960.             if (dllHandle != NULL) 
  961.             {
  962.                 //WinStationSetInformationW
  963.                 pfnWinStationDisconnect = (PWINSTATION_DISCONNECT) GetProcAddress(dllHandle, "WinStationDisconnect");
  964.             }
  965.         }
  966.         if (pfnWinStationDisconnect != NULL)
  967.         {
  968.             pfnWinStationDisconnect( SERVERNAME_CURRENT, LOGONID_CURRENT, FALSE);
  969.         }
  970.         else
  971.         {
  972.             // It should never happen. What shall we do ?
  973.             // How about assert false?
  974.             ASSERT(FALSE);
  975.         }
  976.     }
  977. }
  978. #else // !WINNT
  979. void Disconnect()
  980. {
  981.     // Don't call this on non-NT!
  982.     ASSERT(FALSE);
  983. }
  984. #endif //WINNT
  985. DWORD CALLBACK ShutdownThreadProc(void *pv)
  986. {
  987.     SDTP_PARAMS *psdtp = (SDTP_PARAMS *)pv;
  988.     BOOL fShutdownWorked = FALSE;
  989.     // tell USER that anybody can steal foreground from us
  990.     // This allows apps to put up UI during shutdown/suspend/etc.
  991. //    AllowSetForegroundWindow(ASFW_ANY);
  992.  
  993.     switch (psdtp->nCmd) {
  994.     case SHTDN_SHUTDOWN:
  995.         fShutdownWorked = CommonRestart(EWX_SHUTDOWN);
  996.         break;
  997.     case SHTDN_RESTART:
  998.         fShutdownWorked = CommonRestart(CanDoFastRestart() ? EW_RESTARTWINDOWS : EWX_REBOOT);
  999.         break;
  1000.     case SHTDN_LOGOFF:
  1001.         fShutdownWorked = CommonRestart(EWX_LOGOFF);
  1002.         break;
  1003.     case SHTDN_RESTART_DOS:        // Special hack to mean exit to dos
  1004.         // restart to dos, implies that we need to find the appropriate
  1005.         // pif file and exec it.  We may also need to initialize it if
  1006.         // it does not exist...
  1007.         ExitToDos(psdtp->hwndParent);
  1008.         // Fall through and leave f False...
  1009.         break;
  1010.     case SHTDN_SLEEP:
  1011.     case SHTDN_SLEEP2:
  1012.     case SHTDN_HIBERNATE:
  1013.         SuspendComputer ((psdtp->nCmd == SHTDN_HIBERNATE) ? TRUE : FALSE,
  1014.                          (GetKeyState(VK_CONTROL) < 0) ? TRUE : FALSE,
  1015.                          (psdtp->nCmd == SHTDN_SLEEP2) ? TRUE : FALSE);
  1016.         break;
  1017.     }
  1018.     //
  1019.     // if the shutdown worked terminate the calling app
  1020.     //
  1021.     TraceMsg(DM_TRACE, "s32.stp: pre-postmsg fShutdownWorked=%d", fShutdownWorked);
  1022.     if (fShutdownWorked) 
  1023.     { 
  1024. #ifndef WINNT
  1025.         // on NT, we cannot do this because it keeps explorer from saving state.
  1026.         // on win95, we have to do this because this is how taskman and shell breaks out 
  1027.         // of the message loop.
  1028.         // we cannot do it on the return of the api because before then, we kick off
  1029.         // a thread that sends out the endsession message.  after that, we do not 
  1030.         // receive the WM_QUIT from out queue.  -Chee
  1031.         ASSERT(0);      // now that we do ExitWindows in sep proc, we're killed
  1032.         PostMessage(psdtp->hwndParent, WM_QUIT, 0, 0);
  1033. #endif
  1034.         
  1035.     } 
  1036.     else 
  1037.     {
  1038.         HWND hwnd = FindWindow(c_szFakeDesktopClass, NULL);
  1039.         if (hwnd)
  1040.             PostMessage(hwnd, WM_CLOSE, 0, 0);
  1041.     }
  1042.     LocalFree(psdtp);
  1043.     return fShutdownWorked;
  1044. }
  1045. VOID ExitOrLogoffWindowsDialog(HWND hwndParent, BOOL bLogoff)
  1046. {
  1047.     INT_PTR nCmd;
  1048.     HWND hwndBackground = CreateFakeDesktopWindow();
  1049.     if (hwndBackground)
  1050.     {
  1051.         ShowWindow(hwndBackground, SW_SHOW);
  1052.         SetForegroundWindow(hwndBackground);
  1053.         DitherWindow(hwndBackground);
  1054.     }
  1055.     if (bLogoff)
  1056.     {
  1057.         nCmd = DialogBox(HINST_THISDLL, MAKEINTRESOURCE(DLG_LOGOFFWINDOWS),
  1058.             hwndBackground, LogoffWindowsDlgProc);
  1059.     }
  1060.     else
  1061.     {
  1062.         BOOL fGinaShutdownCalled = FALSE;
  1063.         HINSTANCE hGina;
  1064.         TCHAR szUsername[UNLEN];
  1065.         DWORD cchUsernameLength = UNLEN;
  1066.         DWORD dwOptions;
  1067.         if (WNetGetUser(NULL, szUsername, &cchUsernameLength) != NO_ERROR)
  1068.         {
  1069.             szUsername[0] = TEXT('');
  1070.         }
  1071.       
  1072.         EarlySaveSomeShellState();
  1073.         // Load MSGINA.DLL and get the ShellShutdownDialog function
  1074.         hGina = LoadLibrary(TEXT("msgina.dll"));
  1075.         if (hGina != NULL)
  1076.         {
  1077.             PFNSHELLSHUTDOWNDIALOG pfnShellShutdownDialog = (PFNSHELLSHUTDOWNDIALOG)
  1078.                 GetProcAddress(hGina, "ShellShutdownDialog");
  1079.             if (pfnShellShutdownDialog != NULL)
  1080.             {
  1081.                 nCmd = pfnShellShutdownDialog(hwndBackground,
  1082.                     szUsername, 0);
  1083.                 // Handle disconnect right now
  1084.                 if (nCmd == SHTDN_DISCONNECT)
  1085.                 {
  1086.                     Disconnect();
  1087.                     // No other action
  1088.                     nCmd = SHTDN_NONE;
  1089.                 }
  1090.                 fGinaShutdownCalled = TRUE;
  1091.             }
  1092.             FreeLibrary(hGina);
  1093.         }
  1094.         if (!fGinaShutdownCalled)
  1095.         {
  1096.             dwOptions = GetShutdownOptions();
  1097.     
  1098.             // Gina call failed; use our cheesy private version
  1099.             nCmd = DownlevelShellShutdownDialog(hwndBackground,
  1100.                     dwOptions, szUsername);
  1101.         }
  1102.         _IconCacheSave();
  1103.         InvalidateDriveType(-1);
  1104.     }
  1105.     if (hwndBackground)
  1106.         SetForegroundWindow(hwndBackground);
  1107.     if (nCmd == SHTDN_NONE)
  1108.     {
  1109.         if (hwndBackground)
  1110.         {
  1111.             ShowWindow(hwndBackground, SW_HIDE);
  1112.             PostMessage(hwndBackground, WM_CLOSE, 0, 0);
  1113.         }
  1114.     }
  1115.     else
  1116.     {
  1117.         SDTP_PARAMS *psdtp = LocalAlloc(LPTR, sizeof(*psdtp));
  1118.         if (psdtp)
  1119.         {
  1120.             DWORD dw;
  1121.             HANDLE h;
  1122.             psdtp->nCmd = nCmd;
  1123.             psdtp->hwndParent = hwndParent;
  1124.             //  have another thread call ExitWindows() so our
  1125.             //  main pump keeps running durring shutdown.
  1126.             //
  1127.             h = CreateThread(NULL, 0, ShutdownThreadProc, psdtp, 0, &dw);
  1128.             if (h)
  1129.             {
  1130.                 CloseHandle(h);
  1131.             }
  1132.             else
  1133.             {
  1134.                 if (hwndBackground)
  1135.                     ShowWindow(hwndBackground, SW_HIDE);
  1136.                 ShutdownThreadProc(psdtp);
  1137.             }
  1138.         }
  1139.     }
  1140. }
  1141. // API functions
  1142. STDAPI_(void) ExitWindowsDialog(HWND hwndParent)
  1143. {
  1144.     ExitOrLogoffWindowsDialog (hwndParent, FALSE);
  1145. }
  1146. STDAPI_(void) LogoffWindowsDialog(HWND hwndParent)
  1147. {
  1148.     ExitOrLogoffWindowsDialog (hwndParent, TRUE);
  1149. }