| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 #include <stdexcept> | 
|  | 2 #include <functional> | 
|  | 3 #include <wctype.h> | 
|  | 4 // <thread> is C++11, but implemented in VS2012 | 
|  | 5 #include <thread> | 
|  | 6 | 
|  | 7 #include "installer-lib.h" | 
|  | 8 #include "process.h" | 
|  | 9 | 
|  | 10 //------------------------------------------------------- | 
|  | 11 //------------------------------------------------------- | 
|  | 12 bool process_by_any_exe_with_any_module::operator()( const PROCESSENTRY32W & pro
     cess ) | 
|  | 13 { | 
|  | 14   if (processNames.find(process.szExeFile) != processNames.end()) | 
|  | 15   { | 
|  | 16     if (moduleNames.empty()) | 
|  | 17       return true; | 
|  | 18 | 
|  | 19     Module_Snapshot ms(process.th32ProcessID); | 
|  | 20     const MODULEENTRY32W* me = ms.first(); | 
|  | 21     while (me != 0) | 
|  | 22     { | 
|  | 23       if (moduleNames.find(me->szModule) != moduleNames.end()) | 
|  | 24       { | 
|  | 25         return true; | 
|  | 26       } | 
|  | 27       me = ms.next(); | 
|  | 28     } | 
|  | 29   } | 
|  | 30   return false; | 
|  | 31 } | 
|  | 32 | 
|  | 33 //------------------------------------------------------- | 
|  | 34 // creator_process | 
|  | 35 //------------------------------------------------------- | 
|  | 36 DWORD creator_process( HWND window ) | 
|  | 37 { | 
|  | 38   DWORD pid ; | 
|  | 39   DWORD r = GetWindowThreadProcessId( window, & pid ) ; | 
|  | 40   if ( r == 0 ) | 
|  | 41   { | 
|  | 42     // Assert GetWindowThreadProcessId returned an error | 
|  | 43     // If the window handle is invalid, we end up here. | 
|  | 44     throw windows_api_error( "GetWindowThreadProcessId", r ) ; | 
|  | 45   } | 
|  | 46   return pid ; | 
|  | 47 } | 
|  | 48 | 
|  | 49 //------------------------------------------------------- | 
|  | 50 // send_message, send_endsession_messages | 
|  | 51 //------------------------------------------------------- | 
|  | 52 /** | 
|  | 53 * Default process exit wait time (per message) 5000 ms | 
|  | 54 * | 
|  | 55 * 5 seconds is time that the system will wait before it considers a process non-
     responsive. | 
|  | 56 */ | 
|  | 57 static const unsigned int timeout = 5000 ;    // milliseconds | 
|  | 58 | 
|  | 59 /** | 
|  | 60 * An function object to process the results of sending window messages in send_m
     essage. | 
|  | 61 * | 
|  | 62 * We are using send_message within a system iteration over windows. | 
|  | 63 * The system has its own convention for continuing/breaking the iteration. | 
|  | 64 * This convention is assured consistently in send_message, which also provides d
     efault behavior. | 
|  | 65 * This class provides the base for any variation from the default behavior. | 
|  | 66 */ | 
|  | 67 struct message_accumulator | 
|  | 68   : public std::binary_function< DWORD_PTR, bool, bool > | 
|  | 69 { | 
|  | 70   virtual result_type operator()( first_argument_type result, second_argument_ty
     pe return_value ) = 0 ; | 
|  | 71   virtual ~message_accumulator() {} ; | 
|  | 72 } ; | 
|  | 73 | 
|  | 74 /** | 
|  | 75 * Iteration action to send a message to a window and accumulate results. | 
|  | 76 * | 
|  | 77 * An error sending the message is not a failure for the function a whole. | 
|  | 78 * The goal is to close the process, and if the window is no longer present, then
      the process may have already closed. | 
|  | 79 * Therefore, we're ignoring both the return value and the result. | 
|  | 80 */ | 
|  | 81 class send_message | 
|  | 82 { | 
|  | 83   UINT message ;                ///< Message type for windows message | 
|  | 84   WPARAM p1 ;                   ///< Generic parameter 1 for windows message | 
|  | 85   LPARAM p2 ;                   ///< Generic parameter 2 for windows message | 
|  | 86   message_accumulator * f ;     ///< Processor for results of sending the messag
     e. | 
|  | 87 | 
|  | 88 public: | 
|  | 89   /** | 
|  | 90   * Full contructor gathers message parameters and a message accumulator. | 
|  | 91   */ | 
|  | 92   send_message( UINT message, WPARAM p1, LPARAM p2, message_accumulator & f ) | 
|  | 93     : message( message ), p1( p1 ), p2( p2 ), f( & f ) | 
|  | 94   {} | 
|  | 95 | 
|  | 96   /** | 
|  | 97   * Abbreviated contructor gathers only message parameters. | 
|  | 98   * The message accumulator is absent. | 
|  | 99   */ | 
|  | 100   send_message( UINT message, WPARAM p1, LPARAM p2 ) | 
|  | 101     : message( message ), p1( p1 ), p2( p2 ), f( 0 ) | 
|  | 102   {} | 
|  | 103 | 
|  | 104   /* | 
|  | 105   * Enumeration function applied to each window. | 
|  | 106   */ | 
|  | 107   bool operator()( HWND window ) | 
|  | 108   { | 
|  | 109     DWORD_PTR result ; | 
|  | 110     LRESULT rv = SendMessageTimeoutW( window, message, p1, p2, SMTO_BLOCK, timeo
     ut, & result ) ; | 
|  | 111     /* | 
|  | 112     * If we have no message accumulator, the default behavior is to iterate ever
     ything. | 
|  | 113     * If we do have one, we delegate to it the decision whether to break or to c
     ontinue. | 
|  | 114     */ | 
|  | 115     if ( ! f ) | 
|  | 116     { | 
|  | 117       return true ; | 
|  | 118     } | 
|  | 119     return ( * f )( result, (rv != 0) ) ; | 
|  | 120   } | 
|  | 121 } ; | 
|  | 122 | 
|  | 123 /** | 
|  | 124 * Send WM_QUERYENDSESSION and WM_ENDSESSION to a window. | 
|  | 125 * | 
|  | 126 * This window processor tries to shut down each application individually. | 
|  | 127 * The alternative, gathering all the query results first and only then ending se
     ssions, cannot be done with a single window enumeration. | 
|  | 128 */ | 
|  | 129 class send_endsession_messages | 
|  | 130 { | 
|  | 131 public: | 
|  | 132   /* | 
|  | 133   * Enumeration function applied to each window. | 
|  | 134   */ | 
|  | 135   bool operator()( HWND window ) | 
|  | 136   { | 
|  | 137     DWORD_PTR result ; | 
|  | 138     if ( ! SendMessageTimeoutW( window, WM_QUERYENDSESSION, 0, ENDSESSION_CLOSEA
     PP, SMTO_BLOCK, timeout, & result ) ) | 
|  | 139     { | 
|  | 140       // Assert sending the message failed | 
|  | 141       // Ignore failure, just as with send_message(). | 
|  | 142       return true ; | 
|  | 143     } | 
|  | 144     // Assert result is FALSE if the process has refused notice that it should s
     hut down. | 
|  | 145     if ( ! result ) | 
|  | 146     { | 
|  | 147       /* | 
|  | 148       * Returning false terminates iteration over windows. | 
|  | 149       * Since this process is refusing to shut down, we can't close all the proc
     esses and the operation fails. | 
|  | 150       */ | 
|  | 151       return false ; | 
|  | 152     } | 
|  | 153     SendMessageTimeoutW( window, WM_ENDSESSION, 0, ENDSESSION_CLOSEAPP, SMTO_BLO
     CK, timeout, 0 ) ; | 
|  | 154     return true ; | 
|  | 155   } | 
|  | 156 } ; | 
|  | 157 | 
|  | 158 /** | 
|  | 159 * Accumulator for query-endsession message. | 
|  | 160 * | 
|  | 161 * Implements a conditional-conjunction of the query results. | 
|  | 162 * All answers must be true in order for this result to be true, | 
|  | 163 *   and the calculation is terminated at the first answer 'false'. | 
|  | 164 * As usual, errors sending messages are ignored. | 
|  | 165 */ | 
|  | 166 struct endsession_accumulator : | 
|  | 167   public message_accumulator | 
|  | 168 { | 
|  | 169   bool permit_end_session ; ///< Accumulator variable yields final result. | 
|  | 170 | 
|  | 171   /** | 
|  | 172   * Enumeration function applied to each window. | 
|  | 173   */ | 
|  | 174   bool operator()( DWORD_PTR result, bool return_value ) | 
|  | 175   { | 
|  | 176     if ( ( ! return_value ) || result ) | 
|  | 177     { | 
|  | 178       // 1. If the result is true, then the process will permit WM_ENDSESSION | 
|  | 179       // 2. An error sending the message counts as "no new information" | 
|  | 180       return true ; | 
|  | 181     } | 
|  | 182     // The first false is the result of the calculation. | 
|  | 183     // The second false means to terminate enumeration early. | 
|  | 184     permit_end_session = false ; | 
|  | 185     return false ; | 
|  | 186   } | 
|  | 187 | 
|  | 188   /** | 
|  | 189   * Ordinary constructor. | 
|  | 190   */ | 
|  | 191   endsession_accumulator() | 
|  | 192     : permit_end_session( true ) | 
|  | 193   {} | 
|  | 194 } ; | 
|  | 195 | 
|  | 196 //------------------------------------------------------- | 
|  | 197 // Process_Closer | 
|  | 198 //------------------------------------------------------- | 
|  | 199 /** | 
|  | 200 * Shut down all the processes in the pid_list. | 
|  | 201 * | 
|  | 202 * The method used here uses blocking system calls to send messages to target pro
     cesses. | 
|  | 203 * Message processing delays, therefore, are sequential and the total delay is th
     eir sum. | 
|  | 204 * Windows has non-blocking message calls available, and using a multi-threaded i
     mplementation would shorten that delay. | 
|  | 205 * The code, hwoever, is significantly simpler without multi-threading. | 
|  | 206 * The present use of this method is not closing dozens of applications, so delay
      performance is not critical. | 
|  | 207 * | 
|  | 208 * \return | 
|  | 209 *   The negation of is_running. | 
|  | 210 *   If is_running() was true at the beginning, then this function will have run 
     refresh() before returning. | 
|  | 211 * | 
|  | 212 * \sa | 
|  | 213 *   - MSDN [WM_QUERYENDSESSION message](http://msdn.microsoft.com/en-us/library/
     windows/desktop/aa376890%28v=vs.85%29.aspx) | 
|  | 214 *   - MSDN [WM_ENDSESSION message](http://msdn.microsoft.com/en-us/library/windo
     ws/desktop/aa376889%28v=vs.85%29.aspx) | 
|  | 215 */ | 
|  | 216 bool Process_Closer::shut_down() | 
|  | 217 { | 
|  | 218   /* | 
|  | 219   * If we're not running, we don't need to shut down. | 
|  | 220   */ | 
|  | 221   if ( ! is_running() ) | 
|  | 222   { | 
|  | 223     return true ; | 
|  | 224   } | 
|  | 225 | 
|  | 226   /* | 
|  | 227   * Shutting down is a structure as an escalating series of attempts to shut dow
     n. | 
|  | 228   * After each one, we wait to see if the shut down has completed. | 
|  | 229   * Even though we're using a blocking call to send messages, applications need 
     not block before exiting. | 
|  | 230   * Internet Explorer, in particular, does not. | 
|  | 231   * | 
|  | 232   * Note that termination occurs inside the default case within the switch state
     ment | 
|  | 233   */ | 
|  | 234   for ( unsigned int stage = 1 ; ; ++ stage ) | 
|  | 235   { | 
|  | 236     // Assert is_running() | 
|  | 237     switch( stage ) | 
|  | 238     { | 
|  | 239     case 1 : | 
|  | 240       /* | 
|  | 241       * Send WM_QUERYENDSESSION to every admissible window. | 
|  | 242       * Send WM_ENDSESSION if all processes are ready to shut down. | 
|  | 243       * We try this technique first, since this allows an application to restore
      its application state when it starts up again. | 
|  | 244       */ | 
|  | 245       { | 
|  | 246         endsession_accumulator acc ; | 
|  | 247         send_message m1( WM_QUERYENDSESSION, 0, ENDSESSION_CLOSEAPP, acc ) ; | 
|  | 248         iterate_our_windows( m1 ) ; | 
|  | 249 | 
|  | 250         if ( acc.permit_end_session ) | 
|  | 251         { | 
|  | 252           send_message m2( WM_ENDSESSION, 0, 0 ) ; | 
|  | 253           iterate_our_windows( m2 ) ; | 
|  | 254         } | 
|  | 255       } | 
|  | 256       break ; | 
|  | 257 | 
|  | 258     case 2 : | 
|  | 259       { | 
|  | 260         /* | 
|  | 261         * Send WM_QUERYENDSESSION and WM_ENDSESSION to every admissible window s
     ingly, not accumulating results. | 
|  | 262         */ | 
|  | 263         send_endsession_messages m ; | 
|  | 264         iterate_our_windows( m ) ; | 
|  | 265       } | 
|  | 266       break ; | 
|  | 267 | 
|  | 268     case 3 : | 
|  | 269       { | 
|  | 270         /* | 
|  | 271         * Send WM_CLOSE to every admissible window. | 
|  | 272         */ | 
|  | 273         send_message m( WM_CLOSE, 0, 0 ) ; | 
|  | 274         iterate_our_windows( m ) ; | 
|  | 275       } | 
|  | 276       break ; | 
|  | 277 | 
|  | 278     default : | 
|  | 279       /* | 
|  | 280       * We're out of ways to try to shut down. | 
|  | 281       */ | 
|  | 282       return false ; | 
|  | 283     } | 
|  | 284 | 
|  | 285     /* | 
|  | 286     * Wait loop. | 
|  | 287     */ | 
|  | 288     for ( unsigned int j = 0 ; j < 50 ; ++ j ) | 
|  | 289     { | 
|  | 290       std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ) ; | 
|  | 291       refresh() ; | 
|  | 292       if ( ! is_running() ) | 
|  | 293       { | 
|  | 294         return true ; | 
|  | 295       } | 
|  | 296     } | 
|  | 297     // Assert is_running() | 
|  | 298   } | 
|  | 299   // No control path leaves the for-loop. | 
|  | 300 } ; | 
|  | 301 | 
| OLD | NEW | 
|---|