Block Windows Shut Down

It's hard to describe just how much I HATE rebooting my computer. If I have to use a Windows computer for any extended period of time, then I always change update policies to disallow automatic reboots. In fact, I usually click the irritating "Remind me in ten minutes" button every ten minutes for three weeks before I finally allow Windows to restart (or until I stop the Automatic Updates service).

So you can imagine how annoyed I was to come into work twice this week to the blue Windows logon screen. Every time this happens it takes me twenty minutes to figure out what I was doing the day before, what I have to do today, and where I stopped with my work. And really what made this so much more painful was that it happened without any advanced warning.

And that's what got me thinking: could I block restart requests? I researched the Windows shut down process online and then went to work on a prototype. From what I read, calling ExitWindowsEx sends WM_QUERYENDSESSION to all top-level windows. Applications that are not ready to shut down should return false. I figured the best strategy was to install a system-wide hook and filter the message.

Initially I attempted to capture WM_QUERYENDSESSION with the WH_GETMESSAGE hook and replace it with WM_NULL, but trial-and-error revealed that it's sent through SendMessage and not posted to the window's queue. This meant that I couldn't filter out the message.

I switched to WH_CALLWNDPROC and was able to capture the message, but not actually modify it. Since my DLL is memory-mapped into the local process space, it seemed like the only way to filter the message was to create a new WindowProc function that handles WM_QUERYENDSESSION and always returns false. Then inside the hook procedure, I could intercept the message and call SetWindowLong to replace the window's message procedure.

This demonstrates the basic concept:

LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
  if (nCode == HC_ACTION) {
      CWPSTRUCT *msg = (CWPSTRUCT*)lParam;
 
      /* hijack the window proc when we see a shut down message */
      if (msg->message == WM_QUERYENDSESSION)
          oldwndproc = SetWindowLong(msg->hwnd, GWL_WNDPROC, (DWORD)WindowProc);
  }
 
  return CallNextHookEx(g_callwndhk, nCode, wParam, lParam);
}
 
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uiMessage, 
        WPARAM wParam, LPARAM lParam)
{
  /* intercept shut down messages */
  if (uiMessage == WM_QUERYENDSESSION)
      return 0;
 
  return DefWindowProc(hWnd, uiMessage, wParam, lParam);
}

When my little application starts, it calls SetProcessShutdownParameters with level 0x4FF to increase the chances of trapping the message first. I figured this was a good idea since I know its WindowProc function can be safely hijacked. Now when Windows sends WM_QUERYENDSESSION the response is always "NO!". The added exclamation there is a call to AbortSystemShutdown, which is probably unnecessary but I do it just to be safe. Also, I added an alert message box to warn me when a reboot is triggered.

I'm sort of amazed this actually worked. Some day I'll test it against InitiateSystemShutdown and ExitWindowsEx with EWX_FORCE to see how it holds up. Interestingly, Windows Vista/7 provides ShutdownBlockReasonCreate for seemingly outright blocking shut down attempts.