Unified Diff

Block Windows Shut Down

by Bob on 28 January 2010, under Hacks, How To, Why Sys Admins Hate Me, Win32 & MFC, Windows

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 0×4FF 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.

You can obtain the sources to this project here.


Leave a Reply

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!