Shutdown the Shutdown

The software I write at work is often left running overnight, if not for a couple of days, collecting data. There's very little that angers a user more than coming in in the morning and finding a rebooted computer instead of a full data file. Unfortunately recent versions of windows come configured to check for updates in the middle of night and automatically installing any that are found followed by a reboot if needed. It's not uncommon for our users to either not be aware of this or not have the requisite control over their machines to change this. Not that they should have to since it is possible for programs to block the reboot, though Microsoft does discourage it both explicitly in the documentation and implicitly by changing the rules for preventing shutdown from version to version of Windows. The documentation is also a bit unclear as to what each shutdown prevention method actually prevents.

In the Windows XP days one needed to trap the WM_QUERYENDSESSION and WM_POWERBROADCAST messages, the former to prevent shutdown and the latter to prevent sleep and hibernation. At some point Microsoft became dissatisfied with how developers were using these messages and in Windows Vista and later the responses to these messages are ignored. Instead these versions require the use of SetThreadExecutionState to prevent sleep and hibernation and ShutdownBlockReasonCreate/ShutdownBlockReasonDestroy to prevent shutdown.

Calling these functions is easier than trapping messages so in a way Microsoft has done us a favor, as long as you aren't in the unenviable position of having to still support XP (as I do). If you are stuck with XP, as I guess many are, then you need to both trap the messages and call the functions. The wrinkle is that the ShutdownBlockReason… commands are not available in XP so you can't call them directly. Instead you have to do a LoadLibrary/GetProcAddress dance to load the functions manually, only calling them if you could actually load them. SetThreadExecutionState is available in XP so no dance is necessary in its case.

In searching the Internet you'll come across the AbortSystemShutdown function, and statements that it is nescesssary to call this to prevent shutdowns. In my experiments this function is only nescesssary if you want to prevent shutdowns triggered by someone calling shutdown from a command line. In this case you'll need to call the function constantly, it only works if there is a shutdown pending and there doesn't appear to be any way to detect when this is the case. When the shutdown is aborted the user will see a message in the system tray the shutdown was aborted, but no reason will be given for the abort. Contrasted with the ShutdownBlockReason…functions which, as the name implies, gives the user a reason for blocking shutdown (and allows the user to override the block), the AbortSystemShutdown function is just rude to use. Also note that you will have to request special permissions for your program from the OS in order to successfully call it, another mark against it.

AbortSystemShutdown also caused all sorts of strange issues when I was calling it. While I collecting data my program had a timer callback running that would call AbortSystemShutdownonce a second. Once I put this code in I started getting all sorts of strange crashes in threads other than the one that was calling AbortSystemShutdown. The one constant though was that the thread that was running the timer was always in a call to AbortSystemShutdown when the other thread crashed. I was not able to find a way around this, and given the information above I just removed the call and moved on. ShutdownBlockReason… did everything I needed.

Advertisements
%d bloggers like this: