Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: installer/src/installer-lib/process.cpp

Issue 5675960980471808: Updated installer with custom action (Closed)
Patch Set: Created March 8, 2014, 5:06 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 #include <stdexcept> 1 #include <stdexcept>
2 #include <functional>
3 #include <wctype.h>
4 // <thread> is C++11, but implemented in VS2012
5 #include <thread>
2 6
3 #include "process.h" 7 #include "process.h"
4 8
9 //-------------------------------------------------------
10 // Windows_Handle
11 //-------------------------------------------------------
5 Windows_Handle::Windows_Handle( HANDLE h ) 12 Windows_Handle::Windows_Handle( HANDLE h )
6 : handle( h ) 13 : handle( h )
7 { 14 {
15 validate_handle() ;
16 }
17
18 Windows_Handle::~Windows_Handle()
19 {
20 CloseHandle( handle ) ;
21 }
22
23 void Windows_Handle::operator=( HANDLE h )
24 {
25 this -> ~Windows_Handle() ;
26 handle = h ;
27 validate_handle() ;
28 }
29
30 void Windows_Handle::validate_handle()
31 {
8 if ( handle == INVALID_HANDLE_VALUE ) 32 if ( handle == INVALID_HANDLE_VALUE )
9 { 33 {
10 throw std::runtime_error( "Invalid handle" ) ; 34 throw std::runtime_error( "Invalid handle" ) ;
11 } 35 }
12 } 36 }
13 37
14 Windows_Handle::~Windows_Handle() 38 //-------------------------------------------------------
15 { 39 // process_by_name_CI
16 CloseHandle( handle ) ; 40 //-------------------------------------------------------
17 } 41 process_by_name_CI::process_by_name_CI( const wchar_t * name )
42 : name( name ), length( wcslen( name ) )
43 {}
44
45 bool process_by_name_CI::operator()( const PROCESSENTRY32W & process )
46 {
47 return 0 == wcsncmpi( process.szExeFile, name, length ) ;
48 }
49
50 //-------------------------------------------------------
51 // process_by_any_exe_name_CI
52 //-------------------------------------------------------
53 bool process_by_any_exe_name_CI::operator()( const PROCESSENTRY32W & process )
54 {
55 return names.find( process.szExeFile ) != names.end() ;
56 }
57
58 //-------------------------------------------------------
59 // wcscmpi
60 //-------------------------------------------------------
61 int wcscmpi( const wchar_t * s1, const wchar_t * s2 )
Oleksandr 2014/03/12 19:26:04 Why don't we just use _wcsicmp instead? http://msd
Eric 2014/03/17 12:26:46 We could use an MS-specific library function, I su
62 {
63 // Note: Equality of character sequences is case-insensitive in all predicates below.
64 // Loop invariant: s1[0..j) == s2[0..j)
65 const size_t LIMIT( 4294967295 ) ; // Runaway limit of 2^32 - 1 should be acce ptably long.
Eric 2014/03/17 12:26:46 This limit ought to be 2^16 - 1. I don't know why
66 for ( size_t j = 0 ; j < LIMIT ; ++j )
67 {
68 wchar_t c1 = towupper( *s1++ ) ;
69 wchar_t c2 = towupper( *s2++ ) ;
70 if ( c1 != c2 )
71 {
72 // Map to -1/+1 because c2 - c1 may not fit into an 'int'.
73 return ( c1 < c2 ) ? -1 : 1 ;
74 }
75 else
76 {
77 if ( c1 == L'\0' )
78 {
79 // Assert length( s1 ) == length( s2 ) == j
80 // Assert strings are equal at length < n
81 return 0 ;
82 }
83 }
84 }
85 // Assert j == LIMIT
86 // Assert s1[0..LIMIT) == s2[0..LIMIT)
87 // Both strings are longer than 64K, which violates the precondition
88 throw std::runtime_error( "String arguments too long for wcscmpi" ) ;
89 }
90
91 //-------------------------------------------------------
92 // wcsncmpi
93 //-------------------------------------------------------
94 int wcsncmpi( const wchar_t * s1, const wchar_t * s2, unsigned int n )
Oleksandr 2014/03/12 19:26:04 _wcsnicmp? http://msdn.microsoft.com/en-us/library
95 {
96 // Note: Equality of character sequences is case-insensitive in all predicates below.
97 // Loop invariant: s1[0..j) == s2[0..j)
98 for ( unsigned int j = 0 ; j < n ; ++j )
99 {
100 wchar_t c1 = towupper( *s1++ ) ;
101 wchar_t c2 = towupper( *s2++ ) ;
102 if ( c1 != c2 )
103 {
104 // Map to -1/+1 because c2 - c1 may not fit into an 'int'.
105 return ( c1 < c2 ) ? -1 : 1 ;
106 }
107 else
108 {
109 if ( c1 == L'\0' )
110 {
111 // Assert length( s1 ) == length( s2 ) == j
112 // Assert strings are equal at length < n
113 return 0 ;
114 }
115 }
116 }
117 // Assert j == n
118 // Assert s1[0..n) == s2[0..n)
119 // The semantics of n-compare ignore everything after the first 'n' characters .
120 return 0 ;
121 }
122
123 //-------------------------------------------------------
124 // creator_process
125 //-------------------------------------------------------
126 DWORD creator_process( HWND window )
127 {
128 DWORD pid ;
129 DWORD r = GetWindowThreadProcessId( window, & pid ) ;
130 if ( r == 0 )
131 {
132 // Assert GetWindowThreadProcessId returned an error
133 // If the window handle is invalid, we end up here.
134 throw std::runtime_error( "" ) ;
135 }
136 return pid ;
137 }
138
139 //-------------------------------------------------------
140 // Snapshot
141 //-------------------------------------------------------
142 Snapshot::Snapshot()
143 : handle( ::CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ) )
144 {
145 process.dwSize = sizeof( PROCESSENTRY32W ) ;
146 }
147
148 PROCESSENTRY32W * Snapshot::begin()
149 {
150 return ::Process32FirstW( handle, & process ) ? ( & process ) : 0 ;
151 }
152
153 PROCESSENTRY32W * Snapshot::next()
154 {
155 return ::Process32NextW( handle, & process ) ? ( & process ) : 0 ;
156 }
157
158 void Snapshot::refresh()
159 {
160 handle = ::CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ) ;
161 }
162
163
164 //-------------------------------------------------------
165 // send_message, send_endsession_messages
166 //-------------------------------------------------------
167 /**
168 * Default process exit wait time (per message) 5000 ms
169 *
170 * 5 seconds is time that the system will wait before it considers a process non -responsive.
171 */
172 static const unsigned int timeout = 5000 ; // milliseconds
173
174 /**
175 * An function object to process the results of sending window messages in send_ message.
176 *
177 * We are using send_message within a system iteration over windows.
178 * The system has its own convention for continuing/breaking the iteration.
179 * This convention is assured consistently in send_message, which also provides default behavior.
180 * This class provides the base for any variation from the default behavior.
181 */
182 struct message_accumulator
183 : public std::binary_function< DWORD_PTR, BOOL, bool >
184 {
185 virtual result_type operator()( first_argument_type result, second_argument_ty pe return_value ) = 0 ;
186 virtual ~message_accumulator() {} ;
187 } ;
188
189 /**
190 * Iteration action to send a message to a window and accumulate results.
191 *
192 * An error sending the message is not a failure for the function a whole.
193 * The goal is to close the process, and if the window is no longer present, the n the process may have already closed.
194 * Therefore, we're ignoring both the return value and the result.
195 */
196 class send_message
197 {
198 UINT message ; ///< Message type for windows message
199 WPARAM p1 ; ///< Generic parameter 1 for windows message
200 LPARAM p2 ; ///< Generic parameter 2 for windows message
201 message_accumulator * f ; ///< Processor for results of sending the messag e.
202
203 public:
204 /**
205 * Full contructor gathers message parameters and a message accumulator.
206 */
207 send_message( UINT message, WPARAM p1, LPARAM p2, message_accumulator & f )
208 : message( message ), p1( p1 ), p2( p2 ), f( & f )
209 {}
210
211 /**
212 * Abbreviated contructor gathers only message parameters.
213 * The message accumulator is absent.
214 */
215 send_message( UINT message, WPARAM p1, LPARAM p2 )
216 : message( message ), p1( p1 ), p2( p2 ), f( 0 )
217 {}
218
219 /*
220 * Enumeration function applied to each window.
221 */
222 bool operator()( HWND window )
223 {
224 DWORD_PTR result ;
225 BOOL rv = SendMessageTimeoutW( window, message, p1, p2, SMTO_BLOCK, timeout, & result ) ;
226 /*
227 * If we have no message accumulator, the default behavior is to iterate eve rything.
228 * If we do have one, we delegate to it the decision whether to break or to continue.
229 */
230 if ( ! f )
231 {
232 return true ;
233 }
234 return ( * f )( result, rv ) ;
235 }
236 } ;
237
238 /**
239 * Send WM_QUERYENDSESSION and WM_ENDSESSION to a window.
240 *
241 * This window processor tries to shut down each application individually.
242 * The alternative, gathering all the query results first and only then ending s essions, cannot be done with a single window enumeration.
243 */
244 class send_endsession_messages
245 {
246 public:
247 /*
248 * Enumeration function applied to each window.
249 */
250 bool operator()( HWND window )
251 {
252 DWORD_PTR result ;
253 if ( ! SendMessageTimeoutW( window, WM_QUERYENDSESSION, 0, ENDSESSION_CLOSEA PP, SMTO_BLOCK, timeout, & result ) )
254 {
255 // Assert sending the message failed
256 // Ignore failure, just as with send_message().
257 return true ;
258 }
259 // Assert result is FALSE if the process has refused notice that it should s hut down.
260 if ( ! result )
261 {
262 /*
263 * Returning false terminates iteration over windows.
264 * Since this process is refusing to shut down, we can't close all the pro cesses and the operation fails.
265 */
266 return false ;
267 }
268 SendMessageTimeoutW( window, WM_ENDSESSION, 0, ENDSESSION_CLOSEAPP, SMTO_BLO CK, timeout, 0 ) ;
269 return true ;
270 }
271 } ;
272
273 /**
274 * Accumulator for query-endsession message.
275 *
276 * Implements a conditional-conjunction of the query results.
277 * All answers must be true in order for this result to be true,
278 * and the calculation is terminated at the first answer 'false'.
279 * As usual, errors sending messages are ignored.
280 */
281 struct endsession_accumulator :
282 public message_accumulator
283 {
284 bool permit_end_session ; ///< Accumulator variable yields final result.
285
286 /**
287 * Enumeration function applied to each window.
288 */
289 bool operator()( DWORD_PTR result, BOOL return_value )
290 {
291 if ( ( ! return_value ) || result )
292 {
293 // 1. If the result is true, then the process will permit WM_ENDSESSION
294 // 2. An error sending the message counts as "no new information"
295 return true ;
296 }
297 // The first false is the result of the calculation.
298 // The second false means to terminate enumeration early.
299 permit_end_session = false ;
300 return false ;
301 }
302
303 /**
304 * Ordinary constructor.
305 */
306 endsession_accumulator()
307 : permit_end_session( true )
308 {}
309 } ;
310
311 //-------------------------------------------------------
312 // Process_Closer
313 //-------------------------------------------------------
314 /**
315 * Shut down all the processes in the pid_list.
316 *
317 * The method used here uses blocking system calls to send messages to target pr ocesses.
318 * Message processing delays, therefore, are sequential and the total delay is t heir sum.
319 * Windows has non-blocking message calls available, and using a multi-threaded implementation would shorten that delay.
320 * The code, hwoever, is significantly simpler without multi-threading.
321 * The present use of this method is not closing dozens of applications, so dela y performance is not critical.
322 *
323 * \return
324 * The negation of is_running.
325 * If is_running() was true at the beginning, then this function will have run refresh() before returning.
326 *
327 * \sa
328 * - MSDN [WM_QUERYENDSESSION message](http://msdn.microsoft.com/en-us/library /windows/desktop/aa376890%28v=vs.85%29.aspx)
329 * - MSDN [WM_ENDSESSION message](http://msdn.microsoft.com/en-us/library/wind ows/desktop/aa376889%28v=vs.85%29.aspx)
330 */
331 bool Process_Closer::shut_down()
332 {
333 /*
334 * If we're not running, we don't need to shut down.
335 */
336 if ( ! is_running() )
337 {
338 return true ;
339 }
340
341 /*
342 * Shutting down is a structure as an escalating series of attempts to shut do wn.
343 * After each one, we wait to see if the shut down has completed.
344 * Even though we're using a blocking call to send messages, applications need not block before exiting.
345 * Internet Explorer, in particular, does not.
346 *
347 * Note that termination occurs inside the default case within the switch stat ement
348 */
349 for ( unsigned int stage = 1 ; ; ++ stage )
350 {
351 // Assert is_running()
352 switch( stage )
353 {
354 case 1 :
355 /*
356 * Send WM_QUERYENDSESSION to every admissible window.
357 * Send WM_ENDSESSION if all processes are ready to shut down.
358 * We try this technique first, since this allows an application to restor e its application state when it starts up again.
359 */
360 {
361 endsession_accumulator acc ;
362 send_message m1( WM_QUERYENDSESSION, 0, ENDSESSION_CLOSEAPP, acc ) ;
363 iterate_our_windows( m1 ) ;
364
365 if ( acc.permit_end_session )
366 {
367 send_message m2( WM_ENDSESSION, 0, 0 ) ;
368 iterate_our_windows( m2 ) ;
369 }
370 }
371 break ;
372
373 case 2 :
374 {
375 /*
376 * Send WM_QUERYENDSESSION and WM_ENDSESSION to every admissible window singly, not accumulating results.
377 */
378 send_endsession_messages m ;
379 iterate_our_windows( m ) ;
380 }
381 break ;
382
383 case 3 :
384 {
385 /*
386 * Send WM_CLOSE to every admissible window.
387 */
388 send_message m( WM_CLOSE, 0, 0 ) ;
389 iterate_our_windows( m ) ;
390 }
391 break ;
392
393 default :
394 /*
395 * We're out of ways to try to shut down.
396 */
397 return false ;
398 }
399
400 /*
401 * Wait loop.
402 */
403 for ( unsigned int j = 0 ; j < 50 ; ++ j )
404 {
405 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ) ;
406 refresh() ;
407 if ( ! is_running() )
408 {
409 return true ;
410 }
411 }
412 // Assert is_running()
413 }
414 // No control path leaves the for-loop.
415 } ;
416
OLDNEW

Powered by Google App Engine
This is Rietveld