1 // Copyright 2008, Google Inc.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
6 // 1. Redistributions of source code must retain the above copyright notice,
7 // this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright notice,
9 // this list of conditions and the following disclaimer in the documentation
10 // and/or other materials provided with the distribution.
11 // 3. Neither the name of Google Inc. nor the names of its contributors may be
12 // used to endorse or promote products derived from this software without
13 // specific prior written permission.
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "gears/installer/iemobile/process_restarter.h"
33 #include "gears/third_party/scoped_ptr/scoped_ptr.h"
35 //------------------------------------------------------------------------------
36 // class ProcessRestarter
37 //------------------------------------------------------------------------------
39 ProcessRestarter::ProcessRestarter(const std::string16
& process_name
)
40 : recursion_level_(0),
41 process_name_(process_name
) {
44 // Cclean up if we've left the process handle open
45 ProcessRestarter::~ProcessRestarter() {
49 // Tries to kill the process that was specified in the constructor.
50 // timeout_msec specifies a grace period before trying the next killing method.
51 // method_mask specifies what killing methods to try.
52 // was_found is optional and can be NULL.
53 // Returns S_OK if all instances were killed, S_FALSE if process wasn't running,
54 // and E_FAIL if one or more instances weren't killed.
55 // Always sets was_found correctly, regardless of return value.
56 HRESULT
ProcessRestarter::KillTheProcess(int timeout_msec
,
57 uint32 method_mask
, bool* was_found
) {
58 LOG16(_T("KillTheProcess"));
60 if (!FindProcessInstances()) {
61 if (was_found
!= NULL
) {
64 return S_FALSE
; // process is not running, so don't return a FAILED hr
67 // If got here, found at least one process to kill
68 if (was_found
!= NULL
) {
72 // Try the nicest, cleanest method of closing a process: window messages.
73 if (method_mask
& KILL_METHOD_1_WINDOW_MESSAGE
) {
74 if (PrepareToKill(KILL_METHOD_1_WINDOW_MESSAGE
)) {
75 KillProcessViaWndMessages(timeout_msec
);
78 // Are any instances of the process still running?
79 if (!FindProcessInstances()) {
80 return S_OK
; // killed them all
85 if (method_mask
& KILL_METHOD_2_THREAD_MESSAGE
) {
86 if (PrepareToKill(KILL_METHOD_2_THREAD_MESSAGE
)) {
87 KillProcessViaThreadMessages(timeout_msec
);
89 // Are any instances of the process still running?
90 if (!FindProcessInstances()) {
91 return S_OK
; // killed them all
95 // Try the the crude one.
96 if (method_mask
& KILL_METHOD_3_TERMINATE_PROCESS
) {
97 if (PrepareToKill(KILL_METHOD_3_TERMINATE_PROCESS
)) {
98 KillProcessViaTerminate(timeout_msec
);
100 // Are any instances of the process still running?
101 if (!FindProcessInstances()) {
102 return S_OK
; // killed them all
105 LOG16((_T("KillTheProcess - totally unable to kill process '%s'"),
112 HRESULT
ProcessRestarter::WaitForAllToDie(int timeout_msec
) {
113 LOG16(_T("WaitForAllToDie"));
114 if (!FindProcessInstances()) {
118 if (PrepareToKill(KILL_METHOD_1_WINDOW_MESSAGE
)) {
119 return WaitForProcessInstancesToDie(timeout_msec
) ?
120 S_OK
: HRESULT_FROM_WIN32(WAIT_TIMEOUT
);
126 HRESULT
ProcessRestarter::StartTheProcess(const std::string16
& args
) {
127 if (FindProcessInstances()) {
128 // Process is already running. Bail out.
131 SHELLEXECUTEINFO info
;
132 ZeroMemory(&info
, sizeof(SHELLEXECUTEINFO
));
133 info
.cbSize
= sizeof(SHELLEXECUTEINFO
);
134 info
.fMask
= SEE_MASK_FLAG_NO_UI
| SEE_MASK_NOCLOSEPROCESS
;
135 info
.lpFile
= process_name_
.c_str();
136 info
.lpParameters
= args
.c_str();
137 info
.nShow
= SW_SHOWNORMAL
;
138 if (::ShellExecuteEx(&info
) == TRUE
) return S_OK
;
139 return HRESULT_FROM_WIN32(::GetLastError());
142 bool ProcessRestarter::IsProcessRunning() {
143 return FindProcessInstances();
146 //------------------------------------------------------------------------------
148 //------------------------------------------------------------------------------
150 // Finds all instances of the process.
151 // See http://msdn2.microsoft.com/en-us/library/aa446560.aspx for details
152 // on how to enumerate processes on Windows Mobile.
153 bool ProcessRestarter::FindProcessInstances() {
154 // Clear the process_ids_.
155 process_ids_
.clear();
156 // Create a snapshot of the processes running in the system
157 HANDLE handle
= ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
158 if (handle
== INVALID_HANDLE_VALUE
) return false;
160 // Create a new process entry structure
161 PROCESSENTRY32 current_process_entry
;
162 ZeroMemory(¤t_process_entry
, sizeof(PROCESSENTRY32
));
163 current_process_entry
.dwSize
= sizeof(PROCESSENTRY32
);
165 // Get the first process
166 BOOL success
= ::Process32First(handle
, ¤t_process_entry
);
168 // Save the process id if the exe file name matches
169 if (wcscmp(current_process_entry
.szExeFile
, process_name_
.c_str()) == 0) {
170 process_ids_
.push_back(current_process_entry
.th32ProcessID
);
172 // We need to reset this after each call to Process32Next
173 current_process_entry
.dwSize
= sizeof(PROCESSENTRY32
);
174 success
= ::Process32Next(handle
, ¤t_process_entry
);
177 // Done, so close the handle, free the process structure and return
178 ::CloseToolhelp32Snapshot(handle
);
179 return !process_ids_
.empty();
182 // Given the process_ids_ array, this method will try to
183 // open a handle to each instance.
184 // Leaves process handles open (in member process_handles_)
185 // This function recursively calls itself if by the time
186 // it tries to open handles to process instances,
187 // some of the processes died or naturally exited.
188 bool ProcessRestarter::PrepareToKill(uint32 method_mask
) {
189 LOG16(_T("PrepareToKill"));
191 // do clean up in case some handles are opened.
194 if (process_ids_
.empty()) {
195 // no instances are running.
199 for (size_t i
= 0; i
< process_ids_
.size(); ++i
) {
200 // On Windows Mobile, the first two parameters
201 // to OpenProcess must be 0 and false.
202 HANDLE handle
= ::OpenProcess(0, false, process_ids_
[i
]);
204 process_handles_
.push_back(handle
);
206 DWORD last_error
= ::GetLastError();
207 if (last_error
== ERROR_ACCESS_DENIED
) {
208 // If we are here that means that we do not have enough priveleges
209 // to open the process for a given kill method. No reason to attempt
210 // other instances. Just clean up and return false.
211 LOG16((_T("PrepareToKill failed for '%s'. Kill method %d."),
220 // We already handled the case when we don't have enough privileges to
221 // open the process. So if we have less handles than process ids, then some
222 // of the processes have died since we made a snapshot untill the time we
223 // tried to open handles. We need to do another snapshot and try to
224 // open handles one more time. We need number of handles and number of
225 // ids to be equal. We can do it with recursion.
226 // The idea is: make the next snapshot and open handles. Hopefully the number
227 // will be equal. Stop recursion at the third level.
229 if (process_handles_
.size() != process_ids_
.size()) {
232 // We have a disbalance here. This means that Some of the processes died
233 // already so we need to take another snapshot.
234 if (!FindProcessInstances()) {
235 // they are all dead.
236 recursion_level_
= 0;
240 // Try to obtain the balance three times, no more.
241 if (recursion_level_
>= 3) {
242 recursion_level_
= 0;
243 LOG16((_T("Recursion level too deep in PrepareToKill for '%s'."),
248 // recursively call the function
249 return PrepareToKill(method_mask
);
251 recursion_level_
= 0;
255 // Wait for a while till all process instances will die.
256 bool ProcessRestarter::WaitForProcessInstancesToDie(int timeout_msec
) const {
257 LOG16((_T("WaitForProcessInstancesToDie")));
258 size_t size
= process_handles_
.size();
259 scoped_array
<HANDLE
> handles(new HANDLE
[size
]);
261 for (size_t i
= 0; i
< size
; ++i
) {
262 handles
[i
] = process_handles_
[i
];
265 DWORD wait_result
= ::WaitForMultipleObjects(size
, handles
.get(), true,
268 #pragma warning(disable : 4296)
269 // C4296: '>=' : expression is always true
270 if ((wait_result
>= WAIT_OBJECT_0
) &&
271 (wait_result
< WAIT_OBJECT_0
+ size
)) {
274 #pragma warning(default : 4296)
276 LOG16((_T("WaitForProcessToDie timed out for '%s'. Waited for %d ms."),
282 // Close all currently opened handles.
283 void ProcessRestarter::CloseAllHandles() {
284 LOG16((_T("CloseAllHandles")));
285 // Do clean up if we have opened handles.
286 for (size_t i
= 0; i
< process_handles_
.size(); ++i
) {
287 VERIFY(::CloseHandle(process_handles_
[i
]));
289 process_handles_
.clear();
292 //------------------------------------------------------------------------------
293 // Implementation of KILL_METHOD_1_WINDOW_MESSAGE
294 //------------------------------------------------------------------------------
296 // Just calls built-in enumeration function
297 bool ProcessRestarter::FindProcessWindows() {
298 window_handles_
.clear();
299 return ::EnumWindows(EnumAllWindowsProc
, reinterpret_cast<LPARAM
>(this)) &&
300 !window_handles_
.empty();
303 // During enumeration, this function will try to find a match between
304 // process ids we already found and process ids obtained from each window.
305 // If there is a match, we record the window in an array.
306 BOOL
ProcessRestarter::EnumAllWindowsProc(HWND hwnd
, LPARAM lparam
) {
307 ProcessRestarter
* this_pointer
=
308 reinterpret_cast<ProcessRestarter
*>(lparam
);
309 ASSERT(this_pointer
);
311 uint32 process_id
= 0;
313 ::GetWindowThreadProcessId(hwnd
, reinterpret_cast<DWORD
*>(&process_id
));
315 for (std::vector
<uint32
>::const_iterator it
=
316 this_pointer
->process_ids_
.begin();
317 it
!= this_pointer
->process_ids_
.end();
319 if (*it
== process_id
) {
320 // Only close visible windows (please note that, on Windows Mobile,
321 // EnumWindows only enumerates top level windows).
322 if (::IsWindowVisible(hwnd
)) {
323 this_pointer
->window_handles_
.push_back(hwnd
);
330 // Attempts to kill the process by delivering a WM_CLOSE message to
331 // the process' visible windows.
332 bool ProcessRestarter::KillProcessViaWndMessages(int timeout_msec
) {
333 LOG16((_T("KillProcessViaWndMessages")));
334 if (!FindProcessWindows()) {
335 LOG16((_T("failed to find any windows for '%s'"), process_name_
));
339 bool post_messages_succeeded
= false;
341 for (size_t i
= 0; i
< window_handles_
.size(); ++i
) {
342 // On Windows Mobile, only WM_CLOSE is capable of closing
344 if (::PostMessage(window_handles_
[i
], WM_CLOSE
, NULL
, NULL
)) {
345 post_messages_succeeded
= true;
349 if (!post_messages_succeeded
) {
350 LOG16((_T("failed to PostMessage to windows of '%s'"), process_name_
));
352 // If we succeeded in posting message at least one time, we have to wait.
353 // We don't know the relationship between windows in the process.
354 return post_messages_succeeded
&& WaitForProcessInstancesToDie(timeout_msec
);
357 //------------------------------------------------------------------------------
358 // Implementation of KILL_METHOD_2_THREAD_MESSAGE
359 //------------------------------------------------------------------------------
361 // find all the threads running in a given process.
362 bool ProcessRestarter::FindProcessThreads(std::vector
<uint32
>* thread_ids
) {
363 // Get a snapshot that includes thread ids
364 HANDLE process_snapshot
= ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD
, 0);
365 if (process_snapshot
== INVALID_HANDLE_VALUE
) return false;
367 THREADENTRY32 thread_info
= {0}; // zero it out just in case.
368 thread_info
.dwSize
= sizeof(THREADENTRY32
);
370 if (::Thread32First(process_snapshot
, &thread_info
)) {
372 for (std::vector
<uint32
>::const_iterator it
= process_ids_
.begin();
373 it
!= process_ids_
.end(); ++it
) {
374 if (*it
== thread_info
.th32OwnerProcessID
) {
376 thread_ids
->push_back(thread_info
.th32ThreadID
);
379 // The system changes this value, do not forget to reset to
381 thread_info
.dwSize
= sizeof(THREADENTRY32
);
382 } while (::Thread32Next(process_snapshot
, &thread_info
));
385 return !thread_ids
->empty();
388 // Try to post a thread message.
389 bool ProcessRestarter::KillProcessViaThreadMessages(int timeout_msec
) {
390 LOG16((_T("KillProcessViaThreadMessages")));
391 std::vector
<uint32
> thread_ids
;
393 if (!FindProcessThreads(&thread_ids
)) {
394 LOG16((_T("failed to find any threads for '%s'"), process_name_
));
398 bool post_messages_succeeded
= false;
399 for (size_t i
= 0; i
< thread_ids
.size(); ++i
) {
400 if (::PostThreadMessage(thread_ids
[i
], WM_CLOSE
, 0, 0)) {
401 post_messages_succeeded
= true;
405 if (!post_messages_succeeded
) {
406 LOG16((_T("[failed to PostMessage to threads of '%s'."), process_name_
));
409 // If we succeded in posting message to at least one thread we have to wait.
410 // We don't know the relationship between threads in the process.
411 return post_messages_succeeded
&& WaitForProcessInstancesToDie(timeout_msec
);
414 //------------------------------------------------------------------------------
415 // Implementation of KILL_METHOD_3_TERMINATE_PROCESS
416 //------------------------------------------------------------------------------
418 // Last and crude method to kill the process. Should be used only
419 // if all other methods have failed.
420 bool ProcessRestarter::KillProcessViaTerminate(int timeout_msec
) {
421 LOG16((_T("KillProcessViaTerminate")));
422 bool at_least_one_terminated
= false;
424 for (size_t i
= 0; i
< process_handles_
.size(); ++i
) {
425 if (!::TerminateProcess(process_handles_
[i
], 0)) {
426 LOG16((_T("[failed for instance of '%s'.System error %d."),
430 at_least_one_terminated
= true;
433 return at_least_one_terminated
?
434 WaitForProcessInstancesToDie(timeout_msec
) : false;