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

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

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 #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
OLDNEW

Powered by Google App Engine
This is Rietveld