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