ahmd0
ahmd0

Reputation: 17293

Working with a shut-down state of a user session on Windows?

Say, if I open a Notepad, type something in it and don't save it, then call the following API from the same user session:

ExitWindowsEx(EWX_LOGOFF, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED);

That user session will enter a "shut-down state", where OS will show an overlay window displaying a message that Notepad prevents system from logging off a user. This overlay will not go away until a user clicks "Cancel" or "Force Quit" buttons.

So two part question:

  1. Is there any way to know which processes blocked logging-off/shut-down process?

  2. Is there any way to cancel this user-session "shut-down state" programmatically?

PS. This state can be detected by calling GetSystemMetrics(SM_SHUTTINGDOWN);

EDIT: Contrary to the answer below, I am not trying to stop system from shutting down, nor that any user-mode process is "hung."

EDIT2: Here's a screenshot of the overlay I'm trying to cancel/close: enter image description here

Upvotes: 1

Views: 2756

Answers (2)

Anthill
Anthill

Reputation: 1249

Following your edit which makes your question clearer:

If you monitor WM_QUERYENDSESSION and respond FALSE to it you can poll from your still running process for a predetermined period of time and then issue an ExitWindowsEx call with flag EWX_FORCEIFHUNG, for example on WM_ENDSESSION. Or you could actually call this pre-emptively on receiving WM_QUERYENDSESSION - the problem is: what if forcing the shutdown results in serious data loss? You're not doing the user of the system any good at that point.

Update following your comments:

How about this:

To find out blocking application:

  1. Register your app with SetProcessShutdownParameters to be first in line to get WM_QUERYENDSESSION.
  2. Respond FALSE to WM_QUERYENDSESSION and on Vista onwards call ShutdownBlockReasonCreate to buy yourself the time.
  3. Get the first window in the chain HWND top = GetTopWindow(NULL)
  4. Get the process ThreadId from hwnd GetWindowThreadProcessId() (compare it with your running app's ;) )
  5. Use SendMessageTimeOut with a message to the hWnd - if you receive no response (timeout) you may have found the blocking window. Go to step 6. If not skip to 7.
  6. Use OpenProcess() with the handle returned from GetWindowThreadProcessId to get a process handle and call GetModuleBaseName() to get the name of the hung process.
  7. If you haven't found the hung window, enumerate the next Window with GetNextWindow() and go back to step 4.

You can also try using the enumerate window handle technique above to find out if you can get a handle to the "hung window" overlay. Having this might give you a chance to do a send key to cancel the state. My bet is that you won't be able to access it but I haven't tried :)

Again your mileage may vary :)

Upvotes: 0

Anthill
Anthill

Reputation: 1249

Question 2: "Is there any way to cancel this shut-down state programmatically?"

The short is answer is not really. And neither should you want to really stop shutdown programatically UNLESS: shutting down will result in serious data loss or significantly affect the user experience on a subsequent system start up. But to mention just one example: imagine a computer is overheating - stopping shutdown programmatically could result in a fried system (and a very irate user).

System shutdown is also not the only thing you need to monitor. There's also hibernate and suspend events (have a look at WM_POWERBROADCAST message).

That said, Windows provides a plethora of mechanisms for detecting system shutdown. For instance:

If your application has a message pump you can choose to return FALSE when Windows polls running applications to vote on WM_QUERYENDSESSION , however Windows from Vista onwards will still force a shutdown after a time-out. From Vista onwards you can (and need to) ShutdownBlockReasonCreate after returning false to WM_QUERYENDSESSION.

If your application is running as a service you can use RegisterServiceCtrHandlerEx and then SetServiceStatus to get a 3 minute shutdown extension grace by setting SERVICE_ACCEPT_PRESHUTDOWN which will get you a SERVICE_CONTROL_PRESHUTDOWN notification. Naturally, you won't receive logoff notification because a service is not affected by logoff. Pre-Vista you can register for SERVICE_CONTROL_SHUTDOWN notification.

Console applications (and gui apps as well but it does not make sense) can use SetConsoleCtrlHandler to be notified of CTRL_LOGOFF and CTRL_SHUTDOWN_EVENT.

At a much lower level one can try hooking API functions such as NTShutdown or even NtSetSystemPowerState which apparently is "the last thing called during ANY type of reboot". But I would strongly suggest not to attempt this.

That said there are ways to really strongly insist that the system should not be shutdown. Consider the following:

1.) Try to register your application to be first in line to receive Shutdown notification. Something like:

    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686227(v=vs.85).aspx
    if(!SetProcessShutdownParameters(0x4ff, 0)) //  greedy highest documented System reserved FirstShutdown
    {
        // Fallback
        if(!SetProcessShutdownParameters(0x3ff, 0)) // highest notification range for applications
        {
             // shouldn't happen
        }
    }

2.) Return FALSE on WM_QUERYENDSESSION From Vista onwards call ShutdownBlockReasonCreate() after returning false on WM_QUERYENDSESSION.

3.) Tell Windows that you need the system to stay up and available. Have a look at http://msdn.microsoft.com/en-us/library/windows/desktop/aa373208(v=vs.85).aspx

SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);

4.) Clean up, call ShutdownBlockReasonDestroy() on Vista onwards, and THEN shutdown the system cleanly.

You could also try the undocumented function (at least it's not on MSDN anymore) CancelShutdown in "user32.dll" which at some point (still may) used to function very much like calling shutdown.exe with the abort flag.

Your mileage may vary.

Upvotes: 4

Related Questions