[Author: andreip]
[google-gears.git] / gears / installer / iemobile / process_restarter.cc
blob3e8917571541a14a13d36a2840878a8fba363794
1 // Copyright 2008, Google Inc.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
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"
28 #include <psapi.h>
29 #include <shellapi.h>
30 #include <tlhelp32.h>
31 #include <vector>
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() {
46 CloseAllHandles();
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) {
62 *was_found = false;
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) {
69 *was_found = true;
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
84 // Also nice method.
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'"),
106 process_name_));
109 return E_FAIL;
112 HRESULT ProcessRestarter::WaitForAllToDie(int timeout_msec) {
113 LOG16(_T("WaitForAllToDie"));
114 if (!FindProcessInstances()) {
115 return S_OK;
118 if (PrepareToKill(KILL_METHOD_1_WINDOW_MESSAGE)) {
119 return WaitForProcessInstancesToDie(timeout_msec) ?
120 S_OK : HRESULT_FROM_WIN32(WAIT_TIMEOUT);
123 return E_FAIL;
126 HRESULT ProcessRestarter::StartTheProcess(const std::string16& args) {
127 if (FindProcessInstances()) {
128 // Process is already running. Bail out.
129 return S_FALSE;
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 //------------------------------------------------------------------------------
147 // Internal
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(&current_process_entry, sizeof(PROCESSENTRY32));
163 current_process_entry.dwSize = sizeof(PROCESSENTRY32);
165 // Get the first process
166 BOOL success = ::Process32First(handle, &current_process_entry);
167 while (success) {
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, &current_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.
192 CloseAllHandles();
194 if (process_ids_.empty()) {
195 // no instances are running.
196 return false;
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]);
203 if (handle) {
204 process_handles_.push_back(handle);
205 } else {
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."),
212 process_name_,
213 method_mask));
214 CloseAllHandles();
215 return false;
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()) {
230 ++recursion_level_;
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;
237 return false;
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'."),
244 process_name_));
245 return false;
248 // recursively call the function
249 return PrepareToKill(method_mask);
251 recursion_level_ = 0;
252 return true;
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,
266 timeout_msec);
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)) {
272 return true;
274 #pragma warning(default : 4296)
276 LOG16((_T("WaitForProcessToDie timed out for '%s'. Waited for %d ms."),
277 process_name_,
278 timeout_msec));
279 return false;
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;
312 uint32 thread_id =
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();
318 ++it) {
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);
327 return TRUE;
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_));
336 return false;
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
343 // a process.
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)) {
371 do {
372 for (std::vector<uint32>::const_iterator it = process_ids_.begin();
373 it != process_ids_.end(); ++it) {
374 if (*it == thread_info.th32OwnerProcessID) {
375 // we have found it.
376 thread_ids->push_back(thread_info.th32ThreadID);
379 // The system changes this value, do not forget to reset to
380 // max possible.
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_));
395 return false;
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."),
427 process_name_,
428 ::GetLastError()));
429 } else {
430 at_least_one_terminated = true;
433 return at_least_one_terminated ?
434 WaitForProcessInstancesToDie(timeout_msec) : false;