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 |