Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Graceful application shutdown when Windows shuts down

I have an application that I'd like to shutdown gracefully when Windows shuts down (or user logs off). This used to work (in xp) but sometime in the last year it broke without anyone noticing. It's also broke (but differently) under Windows 7.

Our product has a main process (server.exe) that starts many other processes. A graceful shutdown would have server.exe asking all of the process that it starts to shut down. However, when I debug this code it seems that the other processes have already been terminated. Our main process (server.exe) is the only process that is handling the WM_QUERYENDSESSION and the WM_ENDSESSION messages. Code below (this used to work under XP but doesn't any more):

LRESULT CALLBACK master_wnd_proc
(
   HWND hwnd,      /* (in) handle to window */
   UINT uMsg,      /* (in) message identifier */
   WPARAM wParam,  /* (in) first message parameter */
   LPARAM lParam   /* (in) second message parameter */
)
{
   LRESULT result;   /* return value */
   long msg_code;

   switch (uMsg)
   {
      case WM_ENDSESSION:
         if (wParam)
         {
            msg_code = PCS_WINDOWS_SHUTDOWN;
            if( lParam & 0x01L )
               msg_code = WINDOWS_SHUT_CLOSE;
            if( lParam & 0x40000000L )
               msg_code = WINDOWS_SHUT_CRIT;
            if( (unsigned long)lParam & 0x80000000 )
               msg_code = WINDOWS_SHUT_LOGOFF;
            MsgGenerate(msg_code, MSG_SEVERE, MSG_LOG, "");

            ipc_declare_shutdown( msg_code );

            //We need one more message in the message queue
            //to force the message loop, below, to exit.
            PostQuitMessage(EXIT_SUCCESS);

            /* WARNING:  Don't call MsgGenerate() after this point! */
         }
         result = 0;
         break;

      case WM_QUERYENDSESSION:

         /* return TRUE to say "okay to shutdown"
          * If FALSE is returned, then other processes are not stopped
          * and the session isn't ended.
          */
         result = TRUE;
         break;

      /* for a Windows TIMER or for an IPC prompt, handle
       * the old server code and tcall messages and
       * once-per-second work.  Notice that the
       * once-per-second work could just be done on the WM_TIMER
       * and the tcall work could just be done on the WM_APP_IPC_POSTED
       * but I've merged them together here.  The merge isn't
       * necessary to fix a bug or anything, but rather to
       * make the code more robust in the face of unexpected
       * conditions.
       */
      case WM_TIMER:
      case WM_APP_IPC_POSTED:
         /* now handle tcall messages */
         (void) server();

         result = FALSE;
         break;

      default:
         result = DefWindowProc (hwnd, uMsg, wParam, lParam);
         break;
   }

   return result;
}

It seems as though we've changed something in the last year the would require all the child processes to handle the WM_QUERYENDSESSION message (I'd really like to avoid this). I don't seem to be able to find any any information as to when processes do or don't get this message.

I've make it work under Windows 7 using the new API but would like to figure out why it's broke under XP so I can have a solution that works for both OS's.

Any help?

like image 793
Brian Erickson Avatar asked Oct 23 '22 13:10

Brian Erickson


1 Answers

Things changed around Vista time, not so sure how that would affect your code. The best thing to do is to not leave it up to Windows to determine the shutdown order. Just ask it to have your server get the shutdown notification before the helper processes:

   DWORD dwLevel, dwFlags;
   BOOL fOkay = GetProcessShutdownParameters(&dwLevel, &dwFlags);
   ASSERT(fOkay);
   if (fOkay && dwLevel > 0x100) {
       fOkay = SetProcessShutdownParameters(dwLevel + 1, SHUTDOWN_NORETRY);
       ASSERT(fOkay);
   }
like image 128
Hans Passant Avatar answered Oct 27 '22 09:10

Hans Passant