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 |