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

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

Issue 6202981292703744: Whole installer (Closed)
Patch Set: Created June 24, 2014, 7:27 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
(Empty)
1 /**
2 * \file process.h
3 */
4
5 #ifndef PROCESS_H
6 #define PROCESS_H
7
8 #include "installer-lib.h"
9 #include "handle.h"
10
11 #include <string>
12 #include <cctype>
13 #include <vector>
14 #include <set>
15 #include <algorithm>
16 #include <memory>
17
18 #include <Windows.h>
19 #include <TlHelp32.h>
20
21 //-------------------------------------------------------
22 // wstring_ci: case-insensitive wide string
23 //-------------------------------------------------------
24
25 /**
26 * Traits class for case-insensitive strings.
27 */
28 template< class T >
29 struct ci_traits: std::char_traits< T >
30 {
31 static bool eq( T c1, T c2 )
32 {
33 return std::tolower( c1 ) == std::tolower( c2 ) ;
34 }
35
36 static bool lt( T c1, T c2 )
37 {
38 return std::tolower( c1 ) < std::tolower( c2 ) ;
39 }
40
41 /**
42 * Trait comparison function.
43 *
44 * Note that this is not a comparison of C-style strings.
45 * In particular, there's no concern over null characters '\0'.
46 * The argument 'n' is the minimum length of the two strings being compared.
47 * We may assume that the intervals p1[0..n) and p2[0..n) are both valid subst rings.
48 */
49 static int compare( const T * p1, const T * p2, size_t n )
50 {
51 while ( n-- > 0 )
52 {
53 T l1 = std::tolower( * p1 ++ ) ;
54 T l2 = std::tolower( * p2 ++ ) ;
55 if ( l1 == l2 )
56 {
57 continue ;
58 }
59 return ( l1 < l2 ) ? -1 : +1 ;
60 }
61 return 0 ;
62 }
63 } ;
64
65 typedef std::basic_string< wchar_t, ci_traits< wchar_t > > wstring_ci ;
66
67 //-------------------------------------------------------
68 // file_name_set: case-insensitive wide-string set
69 //-------------------------------------------------------
70 struct file_name_set
71 : public std::set< wstring_ci >
72 {
73 /**
74 * Empty set constructor.
75 */
76 file_name_set()
77 {}
78
79 /**
80 * Constructor initialization from an array.
81 */
82 template< size_t n_file_names >
83 file_name_set( const wchar_t * ( & file_name_list )[ n_file_names ] )
84 {
85 for ( unsigned int j = 0 ; j < n_file_names ; ++ j )
86 {
87 insert( wstring_ci( file_name_list[ j ] ) ) ;
88 }
89 }
90 } ;
91
92 //-------------------------------------------------------
93 //-------------------------------------------------------
94 /**
95 * Filter by process name. Comparison is case-insensitive. With ABP module loade d
96 */
97 class process_by_any_exe_with_any_module
98 : public std::binary_function< PROCESSENTRY32W, file_name_set, bool >
99 {
100 /**
101 * Set of file names from which to match candidate process names.
102 *
103 * This is a reference to, not a copy of, the set.
104 * The lifetime of this object must be subordinate to that of its referent.
105 * The set used to instantiate this class is a member of Process_Closer,
106 * and so also is this class.
107 * Hence the lifetimes are coterminous, and the reference is not problematic.
108 */
109 const file_name_set & processNames ;
110 const file_name_set & moduleNames;
111 public:
112 bool operator()( const PROCESSENTRY32W & ) ;
113 process_by_any_exe_with_any_module( const file_name_set & names, const file_na me_set & moduleNames )
114 : processNames( names ), moduleNames( moduleNames )
115 {}
116 } ;
117
118
119 //-------------------------------------------------------
120 // Process utility functions.
121 //-------------------------------------------------------
122 /**
123 * A promiscuous filter admits everything.
124 */
125 struct every_process
126 : public std::unary_function< PROCESSENTRY32W, bool >
127 {
128 bool operator()( const PROCESSENTRY32W & ) { return true ; } ;
129 } ;
130
131 /**
132 * Extractor that copies the entire process structure.
133 */
134 struct copy_all
135 : public std::unary_function< PROCESSENTRY32W, PROCESSENTRY32W >
136 {
137 PROCESSENTRY32W operator()( const PROCESSENTRY32W & process ) { return process ; }
138 } ;
139
140 /**
141 * Extractor that copies only the PID.
142 */
143 struct copy_PID
144 : public std::unary_function< PROCESSENTRY32W, DWORD >
145 {
146 inline DWORD operator()( const PROCESSENTRY32W & process ) { return process.th 32ProcessID ; }
147 } ;
148
149 /**
150 * Retrieve the process ID that created a window.
151 *
152 * Wrapper around GetWindowThreadProcessId.
153 * Converts an error return from the system call into an exception.
154 * The system call can also retrieve the creating thread; we ignore it.
155 *
156 * \param window
157 * Handle of the window
158 * \return
159 * ID of the process that created the argument window
160 *
161 * \sa
162 * MSDN [GetWindowThreadProcessId function](http://msdn.microsoft.com/en-us/li brary/windows/desktop/ms633522%28v=vs.85%29.aspx)
163 */
164 DWORD creator_process( HWND window ) ;
165
166 //-------------------------------------------------------
167 // Snapshot
168 //-------------------------------------------------------
169 /**
170 * Traits class for snapshots of all processes on the system.
171 */
172 struct Process_Snapshot_Traits
173 {
174 /**
175 * The type of the data resulting from CreateToolhelp32Snapshot.
176 */
177 typedef PROCESSENTRY32W result_type ;
178
179 /**
180 * Flags used to call CreateToolhelp32Snapshot.
181 */
182 const static DWORD snapshot_flags = TH32CS_SNAPPROCESS ;
183
184 /**
185 * Wrapper for 'first' function for processes
186 */
187 static BOOL first( HANDLE arg1, LPPROCESSENTRY32 arg2 )
188 {
189 return ::Process32First( arg1, arg2 ) ;
190 }
191
192 /**
193 * Wrapper for 'next' function for processes
194 */
195 static BOOL next( HANDLE arg1, LPPROCESSENTRY32 arg2 )
196 {
197 return ::Process32Next( arg1, arg2 ) ;
198 }
199 } ;
200
201 /**
202 * Traits class for snapshots of all modules loaded by a process.
203 */
204 struct Module_Snapshot_Traits
205 {
206 /**
207 * The type of the data resulting from CreateToolhelp32Snapshot.
208 */
209 typedef MODULEENTRY32W result_type ;
210
211 /**
212 * Flags used to call CreateToolhelp32Snapshot.
213 */
214 const static DWORD snapshot_flags = TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32 ;
215
216 /**
217 * Wrapper for 'first' function for modules
218 */
219 static BOOL first( HANDLE arg1, LPMODULEENTRY32 arg2 )
220 {
221 return ::Module32First( arg1, arg2 ) ;
222 }
223
224 /**
225 * Wrapper for 'next' function for modules
226 */
227 static BOOL next( HANDLE arg1, LPMODULEENTRY32 arg2 )
228 {
229 return ::Module32Next( arg1, arg2 ) ;
230 }
231 } ;
232
233 /**
234 * A snapshot wrapping the results of CreateToolhelp32Snapshot system call.
235 *
236 * Unfortunately, we cannot provide standard iterator for this class.
237 * Standard iterators must be copy-constructible, which entails the possibility of multiple, coexisting iteration states.
238 * The iteration behavior provided by Process32First and Process32Next relies up on state held within the snapshot itself.
239 * Thus, there can be only one iterator at a time for the snapshot.
240 * The two requirements are not simultaneously satisfiable.
241 *
242 * Instead of a standard iterator, we provide a first() and next() functions wra pping the corresponding system calls.
243 *
244 * \par Implementation
245 *
246 * - MSDN [CreateToolhelp32Snapshot function](http://msdn.microsoft.com/en-us/li brary/windows/desktop/ms682489%28v=vs.85%29.aspx)
247 * - MSDN [Process32First function](http://msdn.microsoft.com/en-us/library/wind ows/desktop/ms684834%28v=vs.85%29.aspx)
248 * - MSDN [Process32Next function](http://msdn.microsoft.com/en-us/library/windo ws/desktop/ms684836%28v=vs.85%29.aspx)
249 * - MSDN [PROCESSENTRY32 structure](http://msdn.microsoft.com/en-us/library/win dows/desktop/ms684839%28v=vs.85%29.aspx)
250 *
251 * \par Design Note
252 * The traits class defines first() and next() functions instead of using func tion pointers.
253 * This arises from a limitation in the compiler.
254 * The system calls are declared 'WINAPI', which is a compiler-specific extens ion.
255 * That extension, however, does not go far enough to be able to declare a poi nter with the same modifier.
256 * Hence the system calls must be called directly; they are wrapped in the tra it functions.
257 */
258 template< class Traits >
259 class Snapshot
260 {
261 public:
262 /**
263 * Expose the result type from the traits class as our own.
264 */
265 typedef typename Traits::result_type result_type ;
266
267 private:
268 /**
269 * Process ID argument for CreateToolhelp32Snapshot.
270 */
271 DWORD _id ;
272
273 /**
274 * Handle to the underlying snapshot.
275 */
276 Windows_Handle handle ;
277
278 /**
279 * Buffer for reading a single process entry out of the snapshot.
280 *
281 * This buffer is constant insofar as the code outside this class is concerned .
282 * The accessor functions first() and next() return pointers to constant resul t_type.
283 */
284 result_type buffer;
285
286 /**
287 * Copy constructor declared private and not defined.
288 *
289 * \par Implementation
290 * Add "= delete" for C++11.
291 */
292 Snapshot( const Snapshot & ) ;
293
294 /**
295 * Copy assignment declared private and not defined.
296 *
297 * \par Implementation
298 * Add "= delete" for C++11.
299 */
300 Snapshot operator=( const Snapshot & ) ;
301
302 /**
303 * Create a new snapshot and return its handle.
304 */
305 Windows_Handle::handle_type make_handle()
306 {
307 Windows_Handle::handle_type h = ::CreateToolhelp32Snapshot( Traits::snapshot _flags, _id ) ;
308 if ( h == INVALID_HANDLE_VALUE )
309 {
310 throw windows_api_error( "CreateToolhelp32Snapshot", "INVALID_HANDLE_VALUE " ) ;
311 }
312 return h ;
313 }
314
315 protected:
316 /**
317 * Constructor takes a snapshot.
318 */
319 Snapshot( DWORD id )
320 : _id( id ), handle( make_handle() )
321 {
322 // The various result types all define 'dwSize' with the same semantics.
323 buffer.dwSize = sizeof( result_type ) ;
324 }
325
326 public:
327 /**
328 * Reconstruct the current instance with a new system snapshot.
329 *
330 * This function uses reinitialization assignment in the Windows_Handle class,
331 * which takes care of closing the old handle.
332 */
333 void refresh()
334 {
335 handle = make_handle();
336 }
337
338 /**
339 * Retrieve the first snapshot item into our member buffer.
340 *
341 * \return
342 * Pointer to our member buffer if there was a first item
343 * 0 otherwise
344 *
345 * \par Design Note
346 * There's no error handling in the present version of this function.
347 * In part that's because the underlying system call returns either true or false, both of which are ordinarily valid answers.
348 * The trouble is that a false return is overloaded.
349 * It can mean either that (ordinary) there are no more items or (exceptiona l) the snapshot did not contain the right kind of item.
350 * GetLastError is no help here; it doesn't distinguish between these cases.
351 * The upshot is that we rely that our implementation calls the right functi ons on the snapshot,
352 * and so we ignore the case where we've passed bad arguments to the syste m call.
353 */
354 const result_type * first()
355 {
356 return Traits::first(handle, &buffer) ? &buffer : 0;
357 }
358
359 /**
360 * Retrieve the next snapshot item into our member buffer and return a pointer to it.
361 * begin() must have been called first.
362 *
363 * \return
364 * Pointer to our member buffer if there was a first item
365 * 0 otherwise
366 *
367 * \par Design Note
368 * See the Design Note for first(); the same considerations apply here.
369 */
370 const result_type * next()
371 {
372 return Traits::next(handle, &buffer) ? &buffer : 0;
373 }
374 } ;
375
376 /**
377 * A snapshot of all processes running on the system.
378 */
379 struct Process_Snapshot
380 : public Snapshot< Process_Snapshot_Traits >
381 {
382 Process_Snapshot()
383 : Snapshot( 0 )
384 {}
385 } ;
386
387 /**
388 * A snapshot of all modules loaded for a given process.
389 */
390 struct Module_Snapshot
391 : public Snapshot< Module_Snapshot_Traits >
392 {
393 Module_Snapshot( DWORD process_id )
394 : Snapshot( process_id )
395 {}
396 } ;
397
398 //-------------------------------------------------------
399 // initialize_process_list
400 //-------------------------------------------------------
401 /**
402 * \tparam T The type into which a PROCESSENTRY32W struture is extracted.
403 * \tparam Admittance Function type for argument 'admit'
404 * \tparam Extractor Function type for argument 'extract'
405 * \param admit A unary predicate function class that determines what's included
406 * A process appears in the list only if the predicate returns true.
407 * The use of this predicate is analogous to that in std::copy_if.
408 * \param convert A conversion function that takes a PROCESSENTRY32W as input ar gument and returns an element of type T.
409 */
410 template<class T, class Admittance, class Extractor>
411 void initialize_process_list(std::vector<T>& v, Process_Snapshot &snap, Admittan ce admit = Admittance(), Extractor extract = Extractor())
412 {
413 const PROCESSENTRY32W* p = snap.first();
414 while (p != 0)
415 {
416 if (admit(*p ))
417 {
418 /*
419 * We don't have C++11 emplace_back, which can construct the element in p lace.
420 * Instead, we copy the return value of the converter.
421 */
422 v.push_back(extract(*p));
423 }
424 p = snap.next();
425 }
426 };
427
428 //-------------------------------------------------------
429 // initialize_process_set
430 //-------------------------------------------------------
431 /**
432 * \tparam T The type into which a PROCESSENTRY32W struture is extracted.
433 * \tparam Admittance Function type for argument 'admit'
434 * \tparam Extractor Function type for argument 'extract'
435 * \param admit A unary predicate function class that determines what's included
436 * A process appears in the list only if the predicate returns true.
437 * The use of this predicate is analogous to that in std::copy_if.
438 * \param convert A conversion function that takes a PROCESSENTRY32W as input ar gument and returns an element of type T.
439 */
440 template<class T, class Admittance, class Extractor>
441 void initialize_process_set(std::set< T > & set, Process_Snapshot &snap, Admitta nce admit = Admittance(), Extractor extract = Extractor())
442 {
443 const PROCESSENTRY32W* p = snap.first();
444 while (p != 0)
445 {
446 if (admit(*p))
447 {
448 set.insert(extract(*p));
449 }
450 p = snap.next();
451 }
452 };
453
454 //-------------------------------------------------------
455 // enumerate_windows
456 //-------------------------------------------------------
457
458 /**
459 * States of a window enumeration.
460 */
461 typedef enum
462 {
463 started, ///< The iteration is currently running
464 normal, ///< Iteration terminated without error.
465 early, ///< Callback returned false and terminated iteration early.
466 exception, ///< Callback threw an exception and thereby terminated iterat ion.
467 error ///< Callback always return true but EnumWindows failed.
468 } enumerate_windows_state ;
469
470 /**
471 * Data to perform a window enumeration, shared between the main function and th e callback function.
472 */
473 template< class F >
474 struct ew_data
475 {
476 /**
477 * Function to be applied to each enumerated window.
478 */
479 F & f ;
480
481 /**
482 * Completion status of the enumeration.
483 */
484 enumerate_windows_state status ;
485
486 /**
487 * An exception to be transported across the callback.
488 *
489 * The enumerator and the callback are not guaranteed to share a call stack,
490 * nor need they even share compatible exception conventions,
491 * and might not even be in the same thread.
492 * Thus, if the applied function throws an exception,
493 * we catch it in the callback and re-throw it in the enumerator.
494 * This member holds such an exception.
495 *
496 * This member holds an exception only if 'status' has the value 'exception'.
497 * Otherwise it's a null pointer.
498 */
499 std::unique_ptr< std::exception > ee ;
500
501 /**
502 * Ordinary constructor.
503 */
504 ew_data( F & f )
505 : f( f ), status( started )
506 {}
507 } ;
508
509 /**
510 * Callback function for EnumWindows.
511 *
512 * This function provides two standard behaviors.
513 * It records early termination of the enumeration, should that happen by the ap plied function returning false.
514 * It captures any exception thrown for transport back to the enumerator.
515 */
516 template< class F >
517 BOOL CALLBACK enumeration_callback( HWND window, LPARAM x )
518 {
519 // LPARAM is always the same size as a pointer
520 ew_data< F > * data = reinterpret_cast< ew_data< F > * >( x ) ;
521 /*
522 * Top-level try statement prevents exception from propagating back to system.
523 */
524 try
525 {
526 bool r = data -> f( window ) ;
527 if ( ! r )
528 {
529 data -> status = early ;
530 }
531 return r ;
532 }
533 catch ( std::exception e )
534 {
535 data -> ee = std::unique_ptr< std::exception >( new( std::nothrow ) std::exc eption( e ) ) ;
536 data -> status = exception ;
537 return FALSE ;
538 }
539 catch ( ... )
540 {
541 data -> ee = std::unique_ptr< std::exception >() ;
542 data -> status = exception ;
543 return FALSE ;
544 }
545 }
546
547 /**
548 * Enumerate windows, applying a function to each one.
549 */
550 template< class F >
551 bool enumerate_windows( F f )
552 {
553 ew_data< F > data( f ) ;
554 BOOL x( ::EnumWindows( enumeration_callback< F >, reinterpret_cast< LPARAM >( & data ) ) ) ;
555 bool r ;
556 if ( data.status != started )
557 {
558 // Assert status was changed within the callback
559 if ( data.status == exception )
560 {
561 /*
562 * The callback threw an exception of some sort.
563 * We forward it to the extent we are able.
564 */
565 if ( data.ee )
566 {
567 throw * data.ee ;
568 }
569 else
570 {
571 throw std::runtime_error( "Unknown exception thrown in callback function ." ) ;
572 }
573 }
574 r = false ;
575 }
576 else
577 {
578 if ( x )
579 {
580 data.status = normal ;
581 r = true ;
582 }
583 else
584 {
585 // Assert EnumWindows failed
586 data.status = error ;
587 r = false ;
588 }
589 }
590 return r ;
591 }
592
593 //-------------------------------------------------------
594 // Process_Closer
595 //-------------------------------------------------------
596 class Process_Closer
597 {
598 /**
599 * Set of process identifiers matching one of the executable names.
600 */
601 std::set< DWORD > pid_set ;
602
603 /**
604 * Set of executable names by which to filter.
605 *
606 * The argument of the filter constructor is a set by reference.
607 * Since it does not make a copy for itself, we define it as a class member to provide its allocation.
608 */
609 file_name_set process_names ;
610
611 /**
612 * Set of module (DLL) names by which to filter.
613 */
614 file_name_set module_names ;
615
616 process_by_any_exe_with_any_module filter ;
617
618 /**
619 * Copy function object copies just the process ID.
620 */
621 copy_PID copy ;
622
623 /**
624 * Snapshot of running processes.
625 */
626 Process_Snapshot & snapshot ;
627
628 void update()
629 {
630 initialize_process_set( pid_set, snapshot, filter, copy ) ;
631 } ;
632
633 template< class F >
634 class only_our_processes
635 {
636 Process_Closer & self ;
637
638 F f ;
639
640 public:
641 only_our_processes( Process_Closer & self, F f )
642 : f( f ), self( self )
643 {}
644
645 bool operator()( HWND window )
646 {
647 bool b ;
648 try
649 {
650 b = self.contains( creator_process( window ) ) ;
651 }
652 catch ( ... )
653 {
654 // ignore window handles that are no longer valid
655 return true ;
656 }
657 if ( ! b )
658 {
659 // Assert the process that created the window is not in our pid_set
660 return true ;
661 }
662 return f( window ) ;
663 }
664 } ;
665
666 public:
667 template <size_t n_file_names, size_t n_module_names>
668 Process_Closer(Process_Snapshot & snapshot, const wchar_t* (&file_name_list)[n _file_names], const wchar_t* (&module_name_list)[n_module_names])
669 : snapshot(snapshot), process_names(file_name_list), module_names(module_nam e_list), filter(process_names, module_names)
670 {
671 update() ;
672 }
673 template <size_t n_file_names>
674 Process_Closer(Process_Snapshot & snapshot, const wchar_t * (&file_name_list)[ n_file_names])
675 : snapshot(snapshot), process_names(file_name_list), module_names(), filter( process_names, module_names)
676 {
677 update() ;
678 }
679
680 /**
681 * Refresh our state to match the snapshot state.
682 */
683 void refresh()
684 {
685 pid_set.clear() ;
686 update() ;
687 }
688
689 bool is_running() { return ! pid_set.empty() ; } ;
690
691 bool contains( DWORD pid ) const { return pid_set.find( pid ) != pid_set.end() ; } ;
692
693 template< class F >
694 bool iterate_our_windows( F f )
695 {
696 only_our_processes< F > g( * this, f ) ;
697 return enumerate_windows( g ) ;
698 }
699
700 /*
701 * Shut down every process in the pid_set.
702 */
703 bool shut_down() ;
704
705 } ;
706
707 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld