| OLD | NEW |
| (Empty) |
| 1 // Copyright 2004-2009 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 // | |
| 16 // Defines class Process to incapsulate win32 | |
| 17 // functions for creation and some manipulations of | |
| 18 // processes. | |
| 19 | |
| 20 #include "omaha/base/process.h" | |
| 21 | |
| 22 #include <ntsecapi.h> | |
| 23 #include <psapi.h> | |
| 24 #include <stierr.h> | |
| 25 #include <tlhelp32.h> | |
| 26 #include <vector> | |
| 27 | |
| 28 #ifndef NT_SUCCESS | |
| 29 #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) | |
| 30 #endif | |
| 31 | |
| 32 #include "omaha/base/debug.h" | |
| 33 #include "omaha/base/disk.h" | |
| 34 #include "omaha/base/error.h" | |
| 35 #include "omaha/base/logging.h" | |
| 36 #include "omaha/base/scoped_any.h" | |
| 37 #include "omaha/base/string.h" | |
| 38 #include "omaha/base/system.h" | |
| 39 #include "omaha/base/system_info.h" | |
| 40 #include "omaha/base/utils.h" | |
| 41 #include "omaha/base/user_info.h" | |
| 42 #include "omaha/base/window_utils.h" | |
| 43 | |
| 44 namespace omaha { | |
| 45 | |
| 46 const int kNumRetriesToFindProcess = 4; | |
| 47 const int kFindProcessRetryIntervalMs = 500; | |
| 48 const int kMaxCmdLineLengthBytes = 4096; | |
| 49 | |
| 50 // Constructor | |
| 51 Process::Process(const TCHAR* name, | |
| 52 const TCHAR* window_class_name) | |
| 53 : process_id_(0), | |
| 54 exit_code_(0), | |
| 55 number_of_restarts_(static_cast<uint32>(-1)), | |
| 56 name_(name), | |
| 57 shutdown_event_(NULL) { | |
| 58 ASSERT1(name); | |
| 59 command_line_ = name; | |
| 60 window_class_name_ = window_class_name; | |
| 61 } | |
| 62 | |
| 63 // Constructor | |
| 64 Process::Process(uint32 process_id) | |
| 65 : process_id_(process_id), | |
| 66 exit_code_(0), | |
| 67 number_of_restarts_(static_cast<uint32>(-1)), | |
| 68 name_(itostr(static_cast<uint32>(process_id))), | |
| 69 shutdown_event_(NULL) { | |
| 70 reset(process_, ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, | |
| 71 false, | |
| 72 process_id)); | |
| 73 if (!valid(process_)) { | |
| 74 UTIL_LOG(LEVEL_ERROR, | |
| 75 (_T("[Process::Process - failed to open process][%u][0x%x]"), | |
| 76 process_id, HRESULTFromLastError())); | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 // Destructor | |
| 81 Process::~Process() { | |
| 82 } | |
| 83 | |
| 84 // Start with command params | |
| 85 HRESULT Process::Start(const TCHAR* command_line_parameters, | |
| 86 HANDLE runas_token) { | |
| 87 if (command_line_parameters && *command_line_parameters) { | |
| 88 command_line_parameters_ = command_line_parameters; | |
| 89 } | |
| 90 | |
| 91 number_of_restarts_ = static_cast<uint32>(-1); | |
| 92 time_of_start_ = GetTickCount(); | |
| 93 | |
| 94 return Restart(runas_token); | |
| 95 } | |
| 96 | |
| 97 // Restart with the old command params | |
| 98 HRESULT Process::Restart(HANDLE runas_token) { | |
| 99 // Can't start the same process twice in the same containing object. | |
| 100 if (Running()) { | |
| 101 return E_FAIL; | |
| 102 } | |
| 103 | |
| 104 PROCESS_INFORMATION process_info = {0}; | |
| 105 HRESULT hr = runas_token ? | |
| 106 System::StartProcessAsUser(runas_token, | |
| 107 command_line_, | |
| 108 command_line_parameters_, | |
| 109 _T("WinSta0\\Default"), | |
| 110 &process_info) : | |
| 111 System::StartProcessWithArgsAndInfo(command_line_, | |
| 112 command_line_parameters_, | |
| 113 &process_info); | |
| 114 | |
| 115 if (SUCCEEDED(hr)) { | |
| 116 VERIFY1(::CloseHandle(process_info.hThread)); | |
| 117 | |
| 118 reset(process_, process_info.hProcess); | |
| 119 process_id_ = process_info.dwProcessId; | |
| 120 | |
| 121 ASSERT1(process_id_); | |
| 122 number_of_restarts_++; | |
| 123 } else { | |
| 124 UTIL_LOG(LE, (_T("[Process Restart failed][%s][0x%x]"), command_line_, hr)); | |
| 125 } | |
| 126 | |
| 127 return hr; | |
| 128 } | |
| 129 | |
| 130 // Check if the process is running. | |
| 131 bool Process::Running() const { | |
| 132 if (!get(process_)) { | |
| 133 return false; | |
| 134 } | |
| 135 | |
| 136 return (::WaitForSingleObject(get(process_), 0) == WAIT_TIMEOUT); | |
| 137 } | |
| 138 | |
| 139 // Create a job and assign the process to it | |
| 140 HANDLE Process::AssignToJob() { | |
| 141 // Make sure that the process handle is valid | |
| 142 if (!get(process_)) { | |
| 143 return false; | |
| 144 } | |
| 145 | |
| 146 // Create a job | |
| 147 scoped_job job(::CreateJobObject(NULL, NULL)); | |
| 148 if (!valid(job)) { | |
| 149 UTIL_LOG(LEVEL_ERROR, | |
| 150 (_T("[Process::AssignToJob - CreateJobObject failed][0x%x]"), | |
| 151 HRESULTFromLastError())); | |
| 152 return false; | |
| 153 } | |
| 154 | |
| 155 // Assign the process to the job | |
| 156 if (!::AssignProcessToJobObject(get(job), get(process_))) { | |
| 157 UTIL_LOG(LEVEL_ERROR, | |
| 158 (_T("[Process::AssignToJob-AssignProcessToJobObject fail][0x%x]"), | |
| 159 HRESULTFromLastError())); | |
| 160 return false; | |
| 161 } | |
| 162 | |
| 163 return release(job); | |
| 164 } | |
| 165 | |
| 166 // Wait till the process finishes | |
| 167 bool Process::WaitUntilDead(uint32 timeout_msec) { | |
| 168 ASSERT1(timeout_msec); | |
| 169 | |
| 170 if (!get(process_)) { | |
| 171 return false; | |
| 172 } | |
| 173 | |
| 174 uint32 ret = 0; | |
| 175 if (shutdown_event_) { | |
| 176 HANDLE wait_handles[2] = {0}; | |
| 177 wait_handles[0] = get(process_); | |
| 178 wait_handles[1] = shutdown_event_; | |
| 179 ret = ::WaitForMultipleObjectsEx(2, | |
| 180 wait_handles, | |
| 181 false, | |
| 182 timeout_msec, | |
| 183 true); | |
| 184 } else { | |
| 185 ret = ::WaitForSingleObjectEx(get(process_), timeout_msec, true); | |
| 186 } | |
| 187 if (ret == WAIT_OBJECT_0) { | |
| 188 UTIL_LOG(L2, (_T("[Process::WaitUntilDead - succeeded to wait process]") | |
| 189 _T("[%s]"), GetName())); | |
| 190 return true; | |
| 191 } else if (ret == WAIT_IO_COMPLETION) { | |
| 192 UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead-recv APC][%s][%u][%u]"), | |
| 193 GetName(), process_id_)); | |
| 194 return false; | |
| 195 } else { | |
| 196 UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead - fail to wait process,") | |
| 197 _T("possibly timeout][%s][%u][%u]"), | |
| 198 GetName(), process_id_, ret)); | |
| 199 return false; | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 // Wait some time till the process and all its descendent processes finish | |
| 204 // | |
| 205 // Background: | |
| 206 // Some process might spawn another process and get itself terminated | |
| 207 // without waiting the descendant process to finish. | |
| 208 // | |
| 209 // Args: | |
| 210 // job: Job to which the process is assigned | |
| 211 // AssignToJob() will be called when NULL value is passed | |
| 212 // timeout_msec: Timeout value in msec | |
| 213 // path_to_exclude: Path of descendant process to excluded from waiting | |
| 214 // (this should be in long format) | |
| 215 // exit_code: To hold the exit code being returned | |
| 216 bool Process::WaitUntilAllDead(HANDLE job, | |
| 217 uint32 timeout_msec, | |
| 218 const TCHAR* path_to_exclude, | |
| 219 uint32* exit_code) { | |
| 220 ASSERT1(timeout_msec); | |
| 221 | |
| 222 UTIL_LOG(L2, (_T("[Process::WaitUntilAllDead][%u][%s]"), | |
| 223 timeout_msec, path_to_exclude)); | |
| 224 | |
| 225 if (exit_code) { | |
| 226 *exit_code = 0; | |
| 227 } | |
| 228 | |
| 229 scoped_job job_guard; | |
| 230 if (!job) { | |
| 231 reset(job_guard, AssignToJob()); | |
| 232 if (!valid(job_guard)) { | |
| 233 return false; | |
| 234 } | |
| 235 job = get(job_guard); | |
| 236 } | |
| 237 | |
| 238 return InternalWaitUntilAllDead(job, | |
| 239 timeout_msec, | |
| 240 path_to_exclude, | |
| 241 exit_code); | |
| 242 } | |
| 243 | |
| 244 // Helper function to wait till the process and all its descendent processes | |
| 245 // finish. | |
| 246 bool Process::InternalWaitUntilAllDead(HANDLE job, | |
| 247 uint32 timeout_msec, | |
| 248 const TCHAR* path_to_exclude, | |
| 249 uint32* exit_code) { | |
| 250 ASSERT1(job); | |
| 251 ASSERT1(timeout_msec); | |
| 252 | |
| 253 // Wait until current process finishes | |
| 254 if (!WaitUntilDead(timeout_msec)) { | |
| 255 return false; | |
| 256 } | |
| 257 | |
| 258 // Find descendant process | |
| 259 uint32 desc_process_id = GetDescendantProcess( | |
| 260 job, | |
| 261 false, // child_only | |
| 262 exit_code != NULL, // sole_descendent | |
| 263 NULL, // search_name | |
| 264 path_to_exclude); | |
| 265 | |
| 266 if (desc_process_id) { | |
| 267 // Open descendent process | |
| 268 Process desc_process(desc_process_id); | |
| 269 | |
| 270 // If descendant process dies too soon, do not need to wait for it | |
| 271 if (desc_process.Running()) { | |
| 272 // Release the parent process handle | |
| 273 // This to handle the scenario that Firefox uninstall code will wait till | |
| 274 // parent process handle becomes NULL | |
| 275 reset(process_); | |
| 276 | |
| 277 UTIL_LOG(L2, (_T("[Process::InternalWaitUntilAllDead]") | |
| 278 _T("[waiting descendant process][%u]"), desc_process_id)); | |
| 279 | |
| 280 // Propagate the shutdown event to descendent process | |
| 281 if (shutdown_event_) { | |
| 282 desc_process.SetShutdownEvent(shutdown_event_); | |
| 283 } | |
| 284 | |
| 285 // Wait till descendant process finishes | |
| 286 bool wait_ret = desc_process.InternalWaitUntilAllDead(job, | |
| 287 timeout_msec, | |
| 288 path_to_exclude, | |
| 289 exit_code); | |
| 290 | |
| 291 return wait_ret; | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 // Use the exit code from parent process | |
| 296 if (exit_code) { | |
| 297 VERIFY1(GetExitCode(exit_code)); | |
| 298 } | |
| 299 | |
| 300 // Release the parent process handle | |
| 301 reset(process_); | |
| 302 | |
| 303 return true; | |
| 304 } | |
| 305 | |
| 306 // Wait until process is dead or a windows message arrives (for use in a message | |
| 307 // loop while waiting) | |
| 308 HRESULT Process::WaitUntilDeadOrInterrupt(uint32 msec) { | |
| 309 if (!get(process_)) { | |
| 310 return E_FAIL; | |
| 311 } | |
| 312 | |
| 313 HANDLE events[1] = { get(process_) }; | |
| 314 uint32 dw = ::MsgWaitForMultipleObjects(1, events, FALSE, msec, QS_ALLEVENTS); | |
| 315 switch (dw) { | |
| 316 case WAIT_OBJECT_0: | |
| 317 return CI_S_PROCESSWAIT_DEAD; | |
| 318 case WAIT_OBJECT_0 + 1: | |
| 319 return CI_S_PROCESSWAIT_MESSAGE; | |
| 320 case WAIT_TIMEOUT: | |
| 321 return CI_S_PROCESSWAIT_TIMEOUT; | |
| 322 case WAIT_FAILED: | |
| 323 default: | |
| 324 return E_FAIL; | |
| 325 } | |
| 326 } | |
| 327 | |
| 328 #if !SHIPPING | |
| 329 CString Process::GetDebugInfo() const { | |
| 330 return debug_info_; | |
| 331 } | |
| 332 #endif | |
| 333 | |
| 334 // Return the process ID | |
| 335 uint32 Process::GetId() const { | |
| 336 return process_id_; | |
| 337 } | |
| 338 | |
| 339 // Return the process name | |
| 340 const TCHAR *Process::GetName() const { | |
| 341 return name_; | |
| 342 } | |
| 343 | |
| 344 // Return win32 handle to the process. | |
| 345 HANDLE Process::GetHandle() const { | |
| 346 return get(process_); | |
| 347 } | |
| 348 | |
| 349 // Get process exit code. | |
| 350 bool Process::GetExitCode(uint32* exit_code) const { | |
| 351 ASSERT1(exit_code); | |
| 352 | |
| 353 if (!get(process_)) { | |
| 354 return false; | |
| 355 } | |
| 356 | |
| 357 if (!::GetExitCodeProcess(get(process_), | |
| 358 reinterpret_cast<DWORD*>(&exit_code_))) { | |
| 359 UTIL_LOG(LEVEL_ERROR, | |
| 360 (_T("[Process::GetExitCode - failed to get exit code][%u][0x%x]"), | |
| 361 process_id_, HRESULTFromLastError())); | |
| 362 return false; | |
| 363 } | |
| 364 if (exit_code_ == STILL_ACTIVE) { | |
| 365 return false; | |
| 366 } | |
| 367 | |
| 368 *exit_code = exit_code_; | |
| 369 return true; | |
| 370 } | |
| 371 | |
| 372 // default implementation allows termination | |
| 373 bool Process::IsTerminationAllowed() const { | |
| 374 return true; | |
| 375 } | |
| 376 | |
| 377 // Terminate the process. If wait_for_terminate_msec == 0 return value doesn't | |
| 378 // mean that the process actualy terminated. It becomes assync. operation. | |
| 379 // Check the status with Running accessor function in this case. | |
| 380 bool Process::Terminate(uint32 wait_for_terminate_msec) { | |
| 381 if (!Running()) { | |
| 382 return true; | |
| 383 } | |
| 384 | |
| 385 if (!IsTerminationAllowed()) { | |
| 386 return false; | |
| 387 } | |
| 388 | |
| 389 if (!::TerminateProcess(get(process_), 1)) { | |
| 390 return false; | |
| 391 } | |
| 392 | |
| 393 return wait_for_terminate_msec ? WaitUntilDead(wait_for_terminate_msec) : | |
| 394 true; | |
| 395 } | |
| 396 | |
| 397 // Default returns INFINITE means never restart. | |
| 398 // Return any number of msec if overwriting | |
| 399 uint32 Process::GetRestartInterval() const { | |
| 400 return INFINITE; | |
| 401 } | |
| 402 | |
| 403 // How many times the process can be restarted | |
| 404 // in case it crashes. When overriding return any | |
| 405 // number or INFINITE to restart forever. | |
| 406 uint32 Process::GetMaxNumberOfRestarts() const { | |
| 407 return 0; | |
| 408 } | |
| 409 | |
| 410 // what is the time window for number of crashes returned by | |
| 411 // GetMaxNumberOfRestarts(). If crashed more that this number of restarts | |
| 412 // in a specified time window - do not restart it anymore. | |
| 413 // Default implementation returns INFINITE which means that this is not time | |
| 414 // based at all, if the process crashed more than the value returned by | |
| 415 // GetMaxNumberOfRestarts it will not be restarted no matter how long it took. | |
| 416 uint32 Process::GetTimeWindowForCrashes() const { | |
| 417 return INFINITE; | |
| 418 } | |
| 419 | |
| 420 uint32 Process::GetMaxMemory() const { | |
| 421 return 0; | |
| 422 } | |
| 423 | |
| 424 // Have we exceeded the number of maximum restarting? | |
| 425 bool Process::AllowedToRestart() const { | |
| 426 uint32 max_number_of_restarts = GetMaxNumberOfRestarts(); | |
| 427 | |
| 428 if ((max_number_of_restarts == INFINITE) || | |
| 429 (number_of_restarts_ < max_number_of_restarts)) { | |
| 430 return true; | |
| 431 } | |
| 432 | |
| 433 // process crashed too many times. Let's look at the rate of crashes. | |
| 434 // Maybe we can "forgive" the process if it took some time for it to crash. | |
| 435 if ((::GetTickCount() - time_of_start_) < GetTimeWindowForCrashes()) { | |
| 436 return false; // not forgiven | |
| 437 } | |
| 438 | |
| 439 // Everything is forgiven. Give the process | |
| 440 // new start in life. | |
| 441 time_of_start_ = ::GetTickCount(); | |
| 442 number_of_restarts_ = static_cast<uint32>(-1); | |
| 443 | |
| 444 return true; | |
| 445 } | |
| 446 | |
| 447 // Set shutdown event using in signaling the process watch | |
| 448 void Process::SetShutdownEvent(HANDLE shutdown_event) { | |
| 449 ASSERT1(shutdown_event); | |
| 450 | |
| 451 shutdown_event_ = shutdown_event; | |
| 452 } | |
| 453 | |
| 454 // Set priority class to the process. | |
| 455 bool Process::SetPriority(uint32 priority_class) const { | |
| 456 if (!get(process_)) { | |
| 457 return false; | |
| 458 } | |
| 459 | |
| 460 VERIFY1(::SetPriorityClass(get(process_), priority_class)); | |
| 461 return true; | |
| 462 } | |
| 463 | |
| 464 HRESULT Process::GetParentProcessId(uint32* parent_pid) { | |
| 465 ASSERT1(parent_pid); | |
| 466 *parent_pid = 0; | |
| 467 | |
| 468 scoped_hfile process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); | |
| 469 if (!process_snap) { | |
| 470 HRESULT hr = HRESULTFromLastError(); | |
| 471 UTIL_LOG(LE, (_T("[GetParentProcessId][Failed snapshot][0x%x]"), hr)); | |
| 472 return hr; | |
| 473 } | |
| 474 | |
| 475 // Eumerate all processes in the snapshot | |
| 476 PROCESSENTRY32 pe32; | |
| 477 SetZero(pe32); | |
| 478 pe32.dwSize = sizeof(PROCESSENTRY32); | |
| 479 if (!::Process32First(get(process_snap), &pe32)) { | |
| 480 HRESULT hr = HRESULTFromLastError(); | |
| 481 UTIL_LOG(LE, (_T("[Process32First failed][0x%x]"), hr)); | |
| 482 return hr; | |
| 483 } | |
| 484 | |
| 485 do { | |
| 486 if (pe32.th32ProcessID != process_id_) { | |
| 487 continue; | |
| 488 } | |
| 489 | |
| 490 if (pe32.th32ParentProcessID) { | |
| 491 *parent_pid = pe32.th32ParentProcessID; | |
| 492 return S_OK; | |
| 493 } | |
| 494 } while (::Process32Next(get(process_snap), &pe32)); | |
| 495 | |
| 496 return E_FAIL; | |
| 497 } | |
| 498 | |
| 499 // Try to get a descendant process. Return process id if found. | |
| 500 uint32 Process::GetDescendantProcess(HANDLE job, | |
| 501 bool child_only, | |
| 502 bool sole_descedent, | |
| 503 const TCHAR* search_name, | |
| 504 const TCHAR* path_to_exclude) { | |
| 505 ASSERT1(job); | |
| 506 | |
| 507 // Find all descendent processes | |
| 508 std::vector<ProcessInfo> descendant_processes; | |
| 509 if (FAILED(GetAllDescendantProcesses(job, | |
| 510 child_only, | |
| 511 search_name, | |
| 512 path_to_exclude, | |
| 513 &descendant_processes))) { | |
| 514 return 0; | |
| 515 } | |
| 516 | |
| 517 // If more than one decendent processes is found, filter out those that are | |
| 518 // not direct children. This is because it might be the case that in a very | |
| 519 // short period of time, process A spawns B and B spawns C, and we capture | |
| 520 // both B and C. | |
| 521 std::vector<ProcessInfo> child_processes; | |
| 522 typedef std::vector<ProcessInfo>::const_iterator ProcessInfoConstIterator; | |
| 523 if (descendant_processes.size() > 1) { | |
| 524 for (ProcessInfoConstIterator it(descendant_processes.begin()); | |
| 525 it != descendant_processes.end(); ++it) { | |
| 526 if (it->parent_id == process_id_) { | |
| 527 child_processes.push_back(*it); | |
| 528 } | |
| 529 } | |
| 530 if (!child_processes.empty()) { | |
| 531 descendant_processes = child_processes; | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 // Save the debugging information if needed | |
| 536 #if !SHIPPING | |
| 537 if (sole_descedent && descendant_processes.size() > 1) { | |
| 538 debug_info_ = _T("More than one descendent process is found for process "); | |
| 539 debug_info_ += itostr(process_id_); | |
| 540 debug_info_ += _T("\n"); | |
| 541 for (ProcessInfoConstIterator it(descendant_processes.begin()); | |
| 542 it != descendant_processes.end(); ++it) { | |
| 543 debug_info_.AppendFormat(_T("%u %u %s\n"), | |
| 544 it->process_id, | |
| 545 it->parent_id, | |
| 546 it->exe_file); | |
| 547 } | |
| 548 } | |
| 549 #else | |
| 550 sole_descedent; // unreferenced formal parameter | |
| 551 #endif | |
| 552 | |
| 553 return descendant_processes.empty() ? 0 : descendant_processes[0].process_id; | |
| 554 } | |
| 555 | |
| 556 BOOL Process::IsProcessInJob(HANDLE process_handle, | |
| 557 HANDLE job_handle, | |
| 558 PBOOL result) { | |
| 559 typedef BOOL (WINAPI *Fun)(HANDLE process_handle, | |
| 560 HANDLE job_handle, | |
| 561 PBOOL result); | |
| 562 | |
| 563 HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll")); | |
| 564 ASSERT1(kernel_instance); | |
| 565 Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(kernel_instance, | |
| 566 "IsProcessInJob")); | |
| 567 ASSERT(pfn, (_T("IsProcessInJob export not found in kernel32.dll"))); | |
| 568 return pfn ? (*pfn)(process_handle, job_handle, result) : FALSE; | |
| 569 } | |
| 570 | |
| 571 // Try to get all matching descendant processes | |
| 572 HRESULT Process::GetAllDescendantProcesses( | |
| 573 HANDLE job, | |
| 574 bool child_only, | |
| 575 const TCHAR* search_name, | |
| 576 const TCHAR* path_to_exclude, | |
| 577 std::vector<ProcessInfo>* descendant_processes) { | |
| 578 ASSERT1(job); | |
| 579 ASSERT1(descendant_processes); | |
| 580 | |
| 581 // Take a snapshot | |
| 582 // Note that we do not have a seperate scoped_* type defined to wrap the | |
| 583 // handle returned by CreateToolhelp32Snapshot. So scoped_hfile with similar | |
| 584 // behavior is used. | |
| 585 scoped_hfile process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); | |
| 586 if (!process_snap) { | |
| 587 HRESULT hr = HRESULTFromLastError(); | |
| 588 UTIL_LOG(LEVEL_ERROR, | |
| 589 (_T("[Process::GetAllDescendantProcesses - fail to get snapshot]") | |
| 590 _T("[0x%x]"), hr)); | |
| 591 return hr; | |
| 592 } | |
| 593 | |
| 594 // Eumerate all processes in the snapshot | |
| 595 PROCESSENTRY32 pe32; | |
| 596 SetZero(pe32); | |
| 597 pe32.dwSize = sizeof(PROCESSENTRY32); | |
| 598 if (!::Process32First(get(process_snap), &pe32)) { | |
| 599 HRESULT hr = HRESULTFromLastError(); | |
| 600 UTIL_LOG(LEVEL_ERROR, (_T("[Process::GetAllDescendantProcesses - failed to") | |
| 601 _T("get first process][0x%x]"), hr)); | |
| 602 return hr; | |
| 603 } | |
| 604 | |
| 605 do { | |
| 606 // Skip process 0 and current process | |
| 607 if (pe32.th32ProcessID == 0 || pe32.th32ProcessID == process_id_) { | |
| 608 continue; | |
| 609 } | |
| 610 | |
| 611 // If searching for child only, perform the check | |
| 612 if (child_only && pe32.th32ParentProcessID != process_id_) { | |
| 613 continue; | |
| 614 } | |
| 615 | |
| 616 // Open the process | |
| 617 scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION | | |
| 618 SYNCHRONIZE, | |
| 619 false, | |
| 620 pe32.th32ProcessID)); | |
| 621 if (!valid(process)) { | |
| 622 continue; | |
| 623 } | |
| 624 | |
| 625 // Determines whether the process is running in the specified job | |
| 626 BOOL result = FALSE; | |
| 627 if (!IsProcessInJob(get(process), job, &result) || !result) { | |
| 628 continue; | |
| 629 } | |
| 630 | |
| 631 // Check whether the process is still running | |
| 632 if (::WaitForSingleObject(get(process), 0) != WAIT_TIMEOUT) { | |
| 633 continue; | |
| 634 } | |
| 635 | |
| 636 // Compare the name if needed | |
| 637 if (search_name && *search_name) { | |
| 638 if (_tcsicmp(pe32.szExeFile, search_name) != 0) { | |
| 639 continue; | |
| 640 } | |
| 641 } | |
| 642 | |
| 643 // If we need to exclude certain path, check it now | |
| 644 if (path_to_exclude && *path_to_exclude) { | |
| 645 if (IsProcessRunningWithPath(pe32.th32ProcessID, path_to_exclude)) { | |
| 646 continue; | |
| 647 } | |
| 648 } | |
| 649 | |
| 650 // Add to the list | |
| 651 ProcessInfo proc_info; | |
| 652 proc_info.process_id = pe32.th32ProcessID; | |
| 653 proc_info.parent_id = pe32.th32ParentProcessID; | |
| 654 #if !SHIPPING | |
| 655 proc_info.exe_file = pe32.szExeFile; | |
| 656 #endif | |
| 657 descendant_processes->push_back(proc_info); | |
| 658 } while (::Process32Next(get(process_snap), &pe32)); | |
| 659 | |
| 660 return S_OK; | |
| 661 } | |
| 662 | |
| 663 HRESULT Process::FindProcesses(uint32 exclude_mask, | |
| 664 const TCHAR* search_name, | |
| 665 bool search_main_executable_only, | |
| 666 std::vector<uint32>* process_ids_found) { | |
| 667 ASSERT1(process_ids_found); | |
| 668 // Remove the only include processes owned by user mask from the exclude | |
| 669 // mask. This is needed as this is the behavior expected by the method, | |
| 670 // before the addition of the user_sid. | |
| 671 exclude_mask &= (~INCLUDE_ONLY_PROCESS_OWNED_BY_USER); | |
| 672 std::vector<CString> command_lines; | |
| 673 return FindProcesses(exclude_mask, search_name, search_main_executable_only, | |
| 674 _T(""), command_lines, process_ids_found); | |
| 675 } | |
| 676 | |
| 677 bool Process::IsStringPresentInList(const CString& process_command_line, | |
| 678 const std::vector<CString>& list) { | |
| 679 std::vector<CString>::const_iterator iter = list.begin(); | |
| 680 for (; iter != list.end(); ++iter) { | |
| 681 CString value_to_find = *iter; | |
| 682 | |
| 683 // If we are able to open the process command line, then we should | |
| 684 // ensure that it does not contain the value that we are looking for. | |
| 685 if (process_command_line.Find(value_to_find) != -1) { | |
| 686 // Found a match. | |
| 687 return true; | |
| 688 } | |
| 689 } | |
| 690 | |
| 691 return false; | |
| 692 } | |
| 693 | |
| 694 // TODO(omaha): Change the implementation of this method to take in a | |
| 695 // predicate that determines whether a process should be included in the | |
| 696 // result set. | |
| 697 HRESULT Process::FindProcesses(uint32 exclude_mask, | |
| 698 const TCHAR* search_name, | |
| 699 bool search_main_executable_only, | |
| 700 const CString& user_sid, | |
| 701 const std::vector<CString>& command_lines, | |
| 702 std::vector<uint32>* process_ids_found) { | |
| 703 ASSERT1(search_name && *search_name); | |
| 704 ASSERT1(process_ids_found); | |
| 705 ASSERT1(!((exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) && | |
| 706 (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING))); | |
| 707 | |
| 708 const TCHAR* const kLocalSystemSid = _T("S-1-5-18"); | |
| 709 | |
| 710 // Clear the output queue | |
| 711 process_ids_found->clear(); | |
| 712 | |
| 713 // Get the list of process identifiers. | |
| 714 uint32 process_ids[kMaxProcesses] = {0}; | |
| 715 uint32 bytes_returned = 0; | |
| 716 if (!::EnumProcesses(reinterpret_cast<DWORD*>(process_ids), | |
| 717 sizeof(process_ids), | |
| 718 reinterpret_cast<DWORD*>(&bytes_returned))) { | |
| 719 HRESULT hr = HRESULTFromLastError(); | |
| 720 UTIL_LOG(LEVEL_ERROR, (_T("[Process::FindProcesses-fail to EnumProcesses]") | |
| 721 _T("[0x%x]"), hr)); | |
| 722 return hr; | |
| 723 } | |
| 724 | |
| 725 // Enumerate all processes | |
| 726 int num_processes = bytes_returned / sizeof(process_ids[0]); | |
| 727 // We have found an elevated number of crashes in 1.2.584.15114 on what | |
| 728 // we believe are Italian systems. The first step to solving this Italian job | |
| 729 // is to assert on the condition while we are further testing this. | |
| 730 ASSERT1(num_processes <= kMaxProcesses); | |
| 731 | |
| 732 // In Vista, SeDebugPrivilege is required to open the process not owned by | |
| 733 // current user. Also required for XP admins to open Local System processes | |
| 734 // with PROCESS_QUERY_INFORMATION access rights. | |
| 735 System::AdjustPrivilege(SE_DEBUG_NAME, true); | |
| 736 | |
| 737 const uint32 cur_process_id = ::GetCurrentProcessId(); | |
| 738 | |
| 739 uint32 parent_process_id = 0; | |
| 740 if (exclude_mask & EXCLUDE_PARENT_PROCESS) { | |
| 741 Process current_process(cur_process_id); | |
| 742 uint32 ppid = 0; | |
| 743 HRESULT hr = current_process.GetParentProcessId(&ppid); | |
| 744 parent_process_id = SUCCEEDED(hr) ? ppid : 0; | |
| 745 } | |
| 746 | |
| 747 // Get SID of current user | |
| 748 CString cur_user_sid; | |
| 749 HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &cur_user_sid); | |
| 750 if (FAILED(hr)) { | |
| 751 return hr; | |
| 752 } | |
| 753 | |
| 754 UTIL_LOG(L4, (_T("[Process::FindProcesses][processes=%d]"), num_processes)); | |
| 755 for (int i = 0; i < num_processes; ++i) { | |
| 756 // Skip the system idle process. | |
| 757 if (process_ids[i] == 0) { | |
| 758 continue; | |
| 759 } | |
| 760 | |
| 761 // Skip the current process if needed. | |
| 762 if ((exclude_mask & EXCLUDE_CURRENT_PROCESS) && | |
| 763 (process_ids[i] == cur_process_id)) { | |
| 764 UTIL_LOG(L4, (_T("[Excluding current process %d"), process_ids[i])); | |
| 765 continue; | |
| 766 } | |
| 767 | |
| 768 // Skip the parent process if needed. | |
| 769 if ((exclude_mask & EXCLUDE_PARENT_PROCESS) && | |
| 770 (process_ids[i] == parent_process_id)) { | |
| 771 UTIL_LOG(L4, (_T("[Excluding parent process(%d) of %d"), | |
| 772 process_ids[i], cur_process_id)); | |
| 773 continue; | |
| 774 } | |
| 775 | |
| 776 | |
| 777 // Get the owner sid. | |
| 778 // Note that we may fail to get the owner which is not current user. | |
| 779 // So if the owner_sid is empty, the process is sure not to be owned by the | |
| 780 // current user. | |
| 781 CString owner_sid; | |
| 782 Process::GetProcessOwner(process_ids[i], &owner_sid); | |
| 783 | |
| 784 if ((exclude_mask & INCLUDE_ONLY_PROCESS_OWNED_BY_USER) && | |
| 785 owner_sid != user_sid) { | |
| 786 UTIL_LOG(L4, | |
| 787 (_T("[Excluding process as not owned by user][%d]"), process_ids[i])); | |
| 788 continue; | |
| 789 } | |
| 790 | |
| 791 if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_CURRENT_USER) && | |
| 792 owner_sid == cur_user_sid) { | |
| 793 UTIL_LOG(L4, | |
| 794 (_T("[Excluding process as owned by current user][%d]"), | |
| 795 process_ids[i])); | |
| 796 continue; | |
| 797 } | |
| 798 if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_SYSTEM) && | |
| 799 owner_sid == kLocalSystemSid) { | |
| 800 UTIL_LOG(L4, | |
| 801 (_T("[Excluding process as owned by system][%d]"), process_ids[i])); | |
| 802 continue; | |
| 803 } | |
| 804 if (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING || | |
| 805 exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) { | |
| 806 CString process_command_line; | |
| 807 HRESULT hr = GetCommandLine(process_ids[i], &process_command_line); | |
| 808 if (FAILED(hr)) { | |
| 809 UTIL_LOG(L4, | |
| 810 (_T("[Excluding process could not get command line][%d]"), | |
| 811 process_ids[i])); | |
| 812 continue; | |
| 813 } | |
| 814 | |
| 815 // If we are able to open the process command line, then we should | |
| 816 // ensure that it does not contain the value that we are looking for if | |
| 817 // we are excluding the command line or that it contains the command line | |
| 818 // that we are looking for in case the include switch is specified. | |
| 819 bool present = IsStringPresentInList(process_command_line, command_lines); | |
| 820 if ((present && | |
| 821 (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING)) || | |
| 822 (!present && | |
| 823 (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING))) { | |
| 824 UTIL_LOG(L4, (_T("[Process command line matches criteria][%d]'[%s]'"), | |
| 825 process_ids[i], process_command_line)); | |
| 826 continue; | |
| 827 } | |
| 828 } | |
| 829 | |
| 830 // If search_name is provided, make sure it matches | |
| 831 if (Process::IsProcessUsingExeOrDll(process_ids[i], | |
| 832 search_name, | |
| 833 search_main_executable_only)) { | |
| 834 UTIL_LOG(L4, | |
| 835 (_T("[Including process][%d][%s]"), process_ids[i], search_name)); | |
| 836 process_ids_found->push_back(process_ids[i]); | |
| 837 } | |
| 838 } | |
| 839 | |
| 840 return S_OK; | |
| 841 } | |
| 842 | |
| 843 HRESULT Process::FindProcessesInSession( | |
| 844 DWORD session_id, | |
| 845 uint32 exclude_mask, | |
| 846 const TCHAR* search_name, | |
| 847 bool search_main_executable_only, | |
| 848 const CString& user_sid, | |
| 849 const std::vector<CString>& cmd_lines, | |
| 850 std::vector<uint32>* process_ids_found) { | |
| 851 HRESULT hr = FindProcesses(exclude_mask, | |
| 852 search_name, | |
| 853 search_main_executable_only, | |
| 854 user_sid, | |
| 855 cmd_lines, | |
| 856 process_ids_found); | |
| 857 if (FAILED(hr)) { | |
| 858 return hr; | |
| 859 } | |
| 860 | |
| 861 // Filter to processes running under session_id. | |
| 862 std::vector<uint32>::iterator iter = process_ids_found->begin(); | |
| 863 while (iter != process_ids_found->end()) { | |
| 864 uint32 process_pid = *iter; | |
| 865 DWORD process_session = 0; | |
| 866 hr = S_OK; | |
| 867 if (!::ProcessIdToSessionId(process_pid, &process_session)) { | |
| 868 hr = HRESULTFromLastError(); | |
| 869 UTIL_LOG(LE, (_T("[::ProcessIdToSessionId failed][0x%x]"), hr)); | |
| 870 } else if (process_session != session_id) { | |
| 871 UTIL_LOG(L4, (_T("[Excluding process, different session][%d][%d][%d]"), | |
| 872 process_pid, process_session, session_id)); | |
| 873 } | |
| 874 | |
| 875 if (FAILED(hr) || process_session != session_id) { | |
| 876 // Remove from list and continue. | |
| 877 iter = process_ids_found->erase(iter); | |
| 878 continue; | |
| 879 } | |
| 880 | |
| 881 ++iter; | |
| 882 } | |
| 883 | |
| 884 return S_OK; | |
| 885 } | |
| 886 | |
| 887 bool Process::IsModuleMatchingExeOrDll(const TCHAR* module_name, | |
| 888 const TCHAR* search_name, | |
| 889 bool is_fully_qualified_name) { | |
| 890 CString module_file_name; | |
| 891 if (is_fully_qualified_name) { | |
| 892 if (FAILED(GetLongPathName(module_name, &module_file_name))) { | |
| 893 return false; | |
| 894 } | |
| 895 } else { | |
| 896 module_file_name = ::PathFindFileName(module_name); | |
| 897 ASSERT1(!module_file_name.IsEmpty()); | |
| 898 if (module_file_name.IsEmpty()) { | |
| 899 return false; | |
| 900 } | |
| 901 } | |
| 902 | |
| 903 return (module_file_name.CompareNoCase(search_name) == 0); | |
| 904 } | |
| 905 | |
| 906 DWORD Process::GetProcessImageFileName(HANDLE proc_handle, | |
| 907 LPTSTR image_file, | |
| 908 DWORD file_size) { | |
| 909 typedef DWORD (WINAPI *Fun)(HANDLE proc_handle, | |
| 910 LPWSTR image_file, | |
| 911 DWORD file_size); | |
| 912 | |
| 913 HINSTANCE psapi_instance = ::GetModuleHandle(_T("Psapi.dll")); | |
| 914 ASSERT1(psapi_instance); | |
| 915 Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(psapi_instance, | |
| 916 "GetProcessImageFileNameW")); | |
| 917 if (!pfn) { | |
| 918 UTIL_LOG(L1, (_T("::GetProcessImageFileNameW() not found in Psapi.dll"))); | |
| 919 return 0; | |
| 920 } | |
| 921 return (*pfn)(proc_handle, image_file, file_size); | |
| 922 } | |
| 923 | |
| 924 bool Process::IsProcImageMatch(HANDLE proc_handle, | |
| 925 const TCHAR* search_name, | |
| 926 bool is_fully_qualified_name) { | |
| 927 TCHAR image_name[MAX_PATH] = _T(""); | |
| 928 if (!GetProcessImageFileName(proc_handle, | |
| 929 image_name, | |
| 930 arraysize(image_name))) { | |
| 931 UTIL_LOG(L4, (_T("[GetProcessImageFileName fail[0x%x]"), | |
| 932 HRESULTFromLastError())); | |
| 933 return false; | |
| 934 } | |
| 935 | |
| 936 UTIL_LOG(L4, (_T("[GetProcessImageFileName][%s]"), image_name)); | |
| 937 CString dos_name; | |
| 938 HRESULT hr(DevicePathToDosPath(image_name, &dos_name)); | |
| 939 if (FAILED(hr)) { | |
| 940 UTIL_LOG(L4, (_T("[DevicePathToDosPath fail[0x%x]"), hr)); | |
| 941 return false; | |
| 942 } | |
| 943 | |
| 944 return IsModuleMatchingExeOrDll(dos_name, | |
| 945 search_name, | |
| 946 is_fully_qualified_name); | |
| 947 } | |
| 948 | |
| 949 // Is the process using the specified exe/dll? | |
| 950 bool Process::IsProcessUsingExeOrDll(uint32 process_id, | |
| 951 const TCHAR* search_name, | |
| 952 bool search_main_executable_only) { | |
| 953 ASSERT1(search_name); | |
| 954 | |
| 955 // Open the process | |
| 956 scoped_process process_handle(::OpenProcess( | |
| 957 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, | |
| 958 FALSE, | |
| 959 process_id)); | |
| 960 if (!process_handle) { | |
| 961 UTIL_LOG(L4, (_T("[::OpenProcess failed][0x%x]"), HRESULTFromLastError())); | |
| 962 return false; | |
| 963 } | |
| 964 | |
| 965 // Does the name represent a fully qualified name? | |
| 966 // We only do a simple check here | |
| 967 bool is_fully_qualified_name = String_FindChar(search_name, _T('\\')) != -1; | |
| 968 CString long_search_name; | |
| 969 if (is_fully_qualified_name) { | |
| 970 HRESULT hr(GetLongPathName(search_name, &long_search_name)); | |
| 971 if (FAILED(hr)) { | |
| 972 UTIL_LOG(L4, (_T("[GetLongPathName fail][hr=x%x]"), hr)); | |
| 973 return false; | |
| 974 } | |
| 975 search_name = long_search_name; | |
| 976 } | |
| 977 | |
| 978 // Take a snapshot of all modules in the specified process | |
| 979 int num_modules_to_fetch = search_main_executable_only ? 1 : | |
| 980 kMaxProcessModules; | |
| 981 HMODULE module_handles[kMaxProcessModules]; | |
| 982 SetZero(module_handles); | |
| 983 uint32 bytes_needed = 0; | |
| 984 if (!::EnumProcessModules(get(process_handle), | |
| 985 module_handles, | |
| 986 num_modules_to_fetch * sizeof(HMODULE), | |
| 987 reinterpret_cast<DWORD*>(&bytes_needed))) { | |
| 988 HRESULT hr = HRESULTFromLastError(); | |
| 989 UTIL_LOG(LEVEL_ERROR, (_T("[EnumProcessModules failed][0x%x]"), hr)); | |
| 990 | |
| 991 if (IsWow64(::GetCurrentProcessId())) { | |
| 992 // ::EnumProcessModules from a WoW64 process fails for x64 processes. | |
| 993 // We try ::GetProcessImageFileName as a workaround here. | |
| 994 return search_main_executable_only ? | |
| 995 IsProcImageMatch(get(process_handle), | |
| 996 search_name, | |
| 997 is_fully_qualified_name) : | |
| 998 false; | |
| 999 } else { | |
| 1000 return false; | |
| 1001 } | |
| 1002 } | |
| 1003 | |
| 1004 int num_modules = bytes_needed / sizeof(HMODULE); | |
| 1005 if (num_modules > num_modules_to_fetch) { | |
| 1006 num_modules = num_modules_to_fetch; | |
| 1007 } | |
| 1008 | |
| 1009 for (int i = 0; i < num_modules; ++i) { | |
| 1010 TCHAR module_name[MAX_PATH]; | |
| 1011 SetZero(module_name); | |
| 1012 if (!::GetModuleFileNameEx(get(process_handle), | |
| 1013 module_handles[i], | |
| 1014 module_name, | |
| 1015 arraysize(module_name))) { | |
| 1016 UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx fail[x%x]"), | |
| 1017 HRESULTFromLastError())); | |
| 1018 continue; | |
| 1019 } | |
| 1020 | |
| 1021 if (IsModuleMatchingExeOrDll(module_name, | |
| 1022 search_name, | |
| 1023 is_fully_qualified_name)) { | |
| 1024 return true; | |
| 1025 } | |
| 1026 } | |
| 1027 | |
| 1028 return false; | |
| 1029 } | |
| 1030 | |
| 1031 // Helper function to get long path name | |
| 1032 HRESULT Process::GetLongPathName(const TCHAR* short_name, CString* long_name) { | |
| 1033 ASSERT1(short_name); | |
| 1034 ASSERT1(long_name); | |
| 1035 | |
| 1036 TCHAR temp_name[MAX_PATH]; | |
| 1037 SetZero(temp_name); | |
| 1038 | |
| 1039 HRESULT hr = S_OK; | |
| 1040 if (!::GetLongPathName(short_name, temp_name, arraysize(temp_name))) { | |
| 1041 hr = HRESULTFromLastError(); | |
| 1042 } else { | |
| 1043 long_name->SetString(temp_name); | |
| 1044 } | |
| 1045 | |
| 1046 return hr; | |
| 1047 } | |
| 1048 | |
| 1049 // Retrieve the FQPN for the executable file for a process. (Note: Using | |
| 1050 // GetModuleFileNameEx is slower than GetProcessImageFileName, but the former | |
| 1051 // is available on Win2K, while the latter is only only on XP and up.) | |
| 1052 HRESULT Process::GetExecutablePath(uint32 process_id, CString *exe_path) { | |
| 1053 ASSERT1(process_id); | |
| 1054 ASSERT1(exe_path); | |
| 1055 | |
| 1056 TCHAR temp_path[MAX_PATH]; | |
| 1057 SetZero(temp_path); | |
| 1058 | |
| 1059 scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION | | |
| 1060 PROCESS_VM_READ, | |
| 1061 FALSE, | |
| 1062 process_id)); | |
| 1063 if (!valid(process)) { | |
| 1064 return HRESULTFromLastError(); | |
| 1065 } | |
| 1066 | |
| 1067 if (0 == ::GetModuleFileNameEx(get(process), NULL, temp_path, MAX_PATH)) { | |
| 1068 return HRESULTFromLastError(); | |
| 1069 } | |
| 1070 | |
| 1071 exe_path->SetString(temp_path); | |
| 1072 return S_OK; | |
| 1073 } | |
| 1074 | |
| 1075 // Type definitions needed for GetCommandLine() and GetProcessIdFromHandle() | |
| 1076 // From MSDN document on NtQueryInformationProcess() and other sources | |
| 1077 typedef struct _PROCESS_BASIC_INFORMATION { | |
| 1078 PVOID Reserved1; | |
| 1079 BYTE *PebBaseAddress; | |
| 1080 PVOID Reserved2[2]; | |
| 1081 ULONG_PTR UniqueProcessId; | |
| 1082 PVOID Reserved3; | |
| 1083 } PROCESS_BASIC_INFORMATION; | |
| 1084 | |
| 1085 typedef enum _PROCESSINFOCLASS { | |
| 1086 ProcessBasicInformation = 0, | |
| 1087 ProcessWow64Information = 26 | |
| 1088 } PROCESSINFOCLASS; | |
| 1089 | |
| 1090 typedef WINBASEAPI DWORD WINAPI | |
| 1091 GetProcessIdFn( | |
| 1092 HANDLE Process | |
| 1093 ); | |
| 1094 | |
| 1095 typedef LONG WINAPI | |
| 1096 NtQueryInformationProcess( | |
| 1097 IN HANDLE ProcessHandle, | |
| 1098 IN PROCESSINFOCLASS ProcessInformationClass, | |
| 1099 OUT PVOID ProcessInformation, | |
| 1100 IN ULONG ProcessInformationLength, | |
| 1101 OUT PULONG ReturnLength OPTIONAL | |
| 1102 ); | |
| 1103 | |
| 1104 typedef struct _RTL_DRIVE_LETTER_CURDIR { | |
| 1105 USHORT Flags; | |
| 1106 USHORT Length; | |
| 1107 ULONG TimeStamp; | |
| 1108 UNICODE_STRING DosPath; | |
| 1109 } RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; | |
| 1110 | |
| 1111 typedef struct _RTL_USER_PROCESS_PARAMETERS { | |
| 1112 ULONG MaximumLength; | |
| 1113 ULONG Length; | |
| 1114 ULONG Flags; | |
| 1115 ULONG DebugFlags; | |
| 1116 PVOID ConsoleHandle; | |
| 1117 ULONG ConsoleFlags; | |
| 1118 HANDLE StdInputHandle; | |
| 1119 HANDLE StdOutputHandle; | |
| 1120 HANDLE StdErrorHandle; | |
| 1121 UNICODE_STRING CurrentDirectoryPath; | |
| 1122 HANDLE CurrentDirectoryHandle; | |
| 1123 UNICODE_STRING DllPath; | |
| 1124 UNICODE_STRING ImagePathName; | |
| 1125 UNICODE_STRING CommandLine; | |
| 1126 PVOID Environment; | |
| 1127 ULONG StartingPositionLeft; | |
| 1128 ULONG StartingPositionTop; | |
| 1129 ULONG Width; | |
| 1130 ULONG Height; | |
| 1131 ULONG CharWidth; | |
| 1132 ULONG CharHeight; | |
| 1133 ULONG ConsoleTextAttributes; | |
| 1134 ULONG WindowFlags; | |
| 1135 ULONG ShowWindowFlags; | |
| 1136 UNICODE_STRING WindowTitle; | |
| 1137 UNICODE_STRING DesktopName; | |
| 1138 UNICODE_STRING ShellInfo; | |
| 1139 UNICODE_STRING RuntimeData; | |
| 1140 RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; | |
| 1141 } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; | |
| 1142 | |
| 1143 // Get the function pointer to GetProcessId in KERNEL32.DLL | |
| 1144 static HRESULT EnsureGPIFunction(GetProcessIdFn** gpi_func_ptr) { | |
| 1145 static GetProcessIdFn* gpi_func = NULL; | |
| 1146 if (!gpi_func) { | |
| 1147 HMODULE kernel32_module = ::GetModuleHandle(_T("kernel32.dll")); | |
| 1148 if (!kernel32_module) { | |
| 1149 return HRESULTFromLastError(); | |
| 1150 } | |
| 1151 gpi_func = reinterpret_cast<GetProcessIdFn*>( | |
| 1152 ::GetProcAddress(kernel32_module, "GetProcessId")); | |
| 1153 if (!gpi_func) { | |
| 1154 return HRESULTFromLastError(); | |
| 1155 } | |
| 1156 } | |
| 1157 | |
| 1158 *gpi_func_ptr = gpi_func; | |
| 1159 return S_OK; | |
| 1160 } | |
| 1161 | |
| 1162 // Get the function pointer to NtQueryInformationProcess in NTDLL.DLL | |
| 1163 static HRESULT EnsureQIPFunction(NtQueryInformationProcess** qip_func_ptr) { | |
| 1164 static NtQueryInformationProcess* qip_func = NULL; | |
| 1165 if (!qip_func) { | |
| 1166 HMODULE ntdll_module = ::GetModuleHandle(_T("ntdll.dll")); | |
| 1167 if (!ntdll_module) { | |
| 1168 return HRESULTFromLastError(); | |
| 1169 } | |
| 1170 qip_func = reinterpret_cast<NtQueryInformationProcess*>( | |
| 1171 ::GetProcAddress(ntdll_module, "NtQueryInformationProcess")); | |
| 1172 if (!qip_func) { | |
| 1173 return HRESULTFromLastError(); | |
| 1174 } | |
| 1175 } | |
| 1176 | |
| 1177 *qip_func_ptr = qip_func; | |
| 1178 return S_OK; | |
| 1179 } | |
| 1180 | |
| 1181 // Obtain the process ID from a hProcess HANDLE | |
| 1182 ULONG Process::GetProcessIdFromHandle(HANDLE hProcess) { | |
| 1183 if (SystemInfo::IsRunningOnXPSP1OrLater()) { | |
| 1184 // Thunk to the documented ::GetProcessId() API | |
| 1185 GetProcessIdFn* gpi_func = NULL; | |
| 1186 HRESULT hr = EnsureGPIFunction(&gpi_func); | |
| 1187 if (FAILED(hr)) { | |
| 1188 ASSERT(FALSE, | |
| 1189 (_T("Process::GetProcessIdFromHandle - EnsureGPIFunction") | |
| 1190 _T(" failed[0x%x]"), hr)); | |
| 1191 return 0; | |
| 1192 } | |
| 1193 ASSERT1(gpi_func); | |
| 1194 return gpi_func(hProcess); | |
| 1195 } | |
| 1196 | |
| 1197 // For lower versions of Windows, we use undocumented | |
| 1198 // function NtQueryInformationProcess to get at the PID | |
| 1199 NtQueryInformationProcess* qip_func = NULL; | |
| 1200 HRESULT hr = EnsureQIPFunction(&qip_func); | |
| 1201 if (FAILED(hr)) { | |
| 1202 ASSERT(FALSE, | |
| 1203 (_T("Process::GetProcessIdFromHandle - EnsureQIPFunction") | |
| 1204 _T(" failed[0x%x]"), hr)); | |
| 1205 return 0; | |
| 1206 } | |
| 1207 ASSERT1(qip_func); | |
| 1208 | |
| 1209 PROCESS_BASIC_INFORMATION info; | |
| 1210 SetZero(info); | |
| 1211 if (!NT_SUCCESS(qip_func(hProcess, | |
| 1212 ProcessBasicInformation, | |
| 1213 &info, | |
| 1214 sizeof(info), | |
| 1215 NULL))) { | |
| 1216 ASSERT(FALSE, (_T("Process::GetProcessIdFromHandle - ") | |
| 1217 _T("NtQueryInformationProcess failed!"))); | |
| 1218 return 0; | |
| 1219 } | |
| 1220 | |
| 1221 return info.UniqueProcessId; | |
| 1222 } | |
| 1223 | |
| 1224 // Get the command line of a process | |
| 1225 HRESULT Process::GetCommandLine(uint32 process_id, CString* cmd_line) { | |
| 1226 ASSERT1(process_id); | |
| 1227 ASSERT1(cmd_line); | |
| 1228 | |
| 1229 // Open the process | |
| 1230 scoped_process process_handle(::OpenProcess( | |
| 1231 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, | |
| 1232 false, | |
| 1233 process_id)); | |
| 1234 if (!process_handle) { | |
| 1235 return HRESULTFromLastError(); | |
| 1236 } | |
| 1237 | |
| 1238 // Obtain Process Environment Block | |
| 1239 // Note that NtQueryInformationProcess is not available in Windows 95/98/ME | |
| 1240 NtQueryInformationProcess* qip_func = NULL; | |
| 1241 HRESULT hr = EnsureQIPFunction(&qip_func); | |
| 1242 | |
| 1243 if (FAILED(hr)) { | |
| 1244 return hr; | |
| 1245 } | |
| 1246 ASSERT1(qip_func); | |
| 1247 | |
| 1248 PROCESS_BASIC_INFORMATION info; | |
| 1249 SetZero(info); | |
| 1250 if (!NT_SUCCESS(qip_func(get(process_handle), | |
| 1251 ProcessBasicInformation, | |
| 1252 &info, | |
| 1253 sizeof(info), | |
| 1254 NULL))) { | |
| 1255 return E_FAIL; | |
| 1256 } | |
| 1257 BYTE* peb = info.PebBaseAddress; | |
| 1258 | |
| 1259 // Read address of parameters (see some PEB reference) | |
| 1260 // TODO(omaha): use offsetof(PEB, ProcessParameters) to replace 0x10 | |
| 1261 // http://msdn.microsoft.com/en-us/library/aa813706.aspx | |
| 1262 SIZE_T bytes_read = 0; | |
| 1263 uint32 dw = 0; | |
| 1264 if (!::ReadProcessMemory(get(process_handle), | |
| 1265 peb + 0x10, | |
| 1266 &dw, | |
| 1267 sizeof(dw), | |
| 1268 &bytes_read)) { | |
| 1269 return HRESULTFromLastError(); | |
| 1270 } | |
| 1271 | |
| 1272 // Read all the parameters | |
| 1273 RTL_USER_PROCESS_PARAMETERS params; | |
| 1274 SetZero(params); | |
| 1275 if (!::ReadProcessMemory(get(process_handle), | |
| 1276 reinterpret_cast<PVOID>(dw), | |
| 1277 ¶ms, | |
| 1278 sizeof(params), | |
| 1279 &bytes_read)) { | |
| 1280 return HRESULTFromLastError(); | |
| 1281 } | |
| 1282 | |
| 1283 // Read the command line parameter | |
| 1284 const int max_cmd_line_len = std::min( | |
| 1285 static_cast<int>(params.CommandLine.MaximumLength), | |
| 1286 kMaxCmdLineLengthBytes); | |
| 1287 if (!::ReadProcessMemory(get(process_handle), | |
| 1288 params.CommandLine.Buffer, | |
| 1289 cmd_line->GetBufferSetLength(max_cmd_line_len), | |
| 1290 max_cmd_line_len, | |
| 1291 &bytes_read)) { | |
| 1292 return HRESULTFromLastError(); | |
| 1293 } | |
| 1294 | |
| 1295 cmd_line->ReleaseBuffer(); | |
| 1296 | |
| 1297 return S_OK; | |
| 1298 } | |
| 1299 | |
| 1300 // Check if the process is running with a specified path | |
| 1301 bool Process::IsProcessRunningWithPath(uint32 process_id, const TCHAR* path) { | |
| 1302 ASSERT1(process_id); | |
| 1303 ASSERT1(path && *path); | |
| 1304 | |
| 1305 const int kProcessWaitModuleFullyUpMs = 100; | |
| 1306 const int kProcessWaitModuleRetries = 10; | |
| 1307 | |
| 1308 // Open the process | |
| 1309 scoped_process process(::OpenProcess( | |
| 1310 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, | |
| 1311 false, | |
| 1312 process_id)); | |
| 1313 if (!process) { | |
| 1314 UTIL_LOG(LEVEL_ERROR, | |
| 1315 (_T("[Process::IsProcessRunningWithPath - OpenProcess failed]") | |
| 1316 _T("[%u][0x%x]"), | |
| 1317 process_id, HRESULTFromLastError())); | |
| 1318 return false; | |
| 1319 } | |
| 1320 | |
| 1321 for (int i = 0; i < kProcessWaitModuleRetries; ++i) { | |
| 1322 // Get the command line path of the main module | |
| 1323 // Note that we are using psapi functions which is not supported in Windows | |
| 1324 // 95/98/ME | |
| 1325 // | |
| 1326 // Sometimes it might be the case that the process is created but the main | |
| 1327 // module is not fully loaded. If so, wait a while and then try again | |
| 1328 TCHAR process_path[MAX_PATH]; | |
| 1329 if (::GetModuleFileNameEx(get(process), | |
| 1330 NULL, | |
| 1331 process_path, | |
| 1332 arraysize(process_path))) { | |
| 1333 // Do the check | |
| 1334 if (String_StartsWith(process_path, path, true)) { | |
| 1335 return true; | |
| 1336 } | |
| 1337 | |
| 1338 // Try again with short form | |
| 1339 TCHAR short_path[MAX_PATH]; | |
| 1340 if (::GetShortPathName(path, short_path, arraysize(short_path)) && | |
| 1341 String_StartsWith(process_path, short_path, true)) { | |
| 1342 return true; | |
| 1343 } | |
| 1344 | |
| 1345 return false; | |
| 1346 } | |
| 1347 | |
| 1348 UTIL_LOG(LEVEL_ERROR, | |
| 1349 (_T("[Process::IsProcessRunningWithPath - GetModuleFileNameEx ") | |
| 1350 _T("failed][%u][0x%x]"), | |
| 1351 process_id, HRESULTFromLastError())); | |
| 1352 | |
| 1353 ::Sleep(kProcessWaitModuleFullyUpMs); | |
| 1354 } | |
| 1355 | |
| 1356 UTIL_LOG(LEVEL_ERROR, | |
| 1357 (_T("[Process::IsProcessRunningWithPath - failed to get process ") | |
| 1358 _T("path][%u][0x%x]"), | |
| 1359 process_id, HRESULTFromLastError())); | |
| 1360 | |
| 1361 return false; | |
| 1362 } | |
| 1363 | |
| 1364 // Get the process owner | |
| 1365 // Note that we may fail to get the owner which is not current user. | |
| 1366 HRESULT Process::GetProcessOwner(uint32 pid, CString* owner_sid) { | |
| 1367 ASSERT1(pid); | |
| 1368 ASSERT1(owner_sid); | |
| 1369 | |
| 1370 scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION, false, pid)); | |
| 1371 if (!valid(process)) { | |
| 1372 return HRESULTFromLastError(); | |
| 1373 } | |
| 1374 | |
| 1375 CAccessToken token; | |
| 1376 CSid sid; | |
| 1377 if (!token.GetProcessToken(READ_CONTROL | TOKEN_QUERY, get(process)) || | |
| 1378 !token.GetUser(&sid)) { | |
| 1379 return HRESULTFromLastError(); | |
| 1380 } | |
| 1381 | |
| 1382 *owner_sid = sid.Sid(); | |
| 1383 return S_OK; | |
| 1384 } | |
| 1385 | |
| 1386 // Creates an impersonation token for the user running process_id. | |
| 1387 // The caller is responsible for closing the returned handle. | |
| 1388 HRESULT Process::GetImpersonationToken(DWORD process_id, HANDLE* user_token) { | |
| 1389 // Get a handle to the process. | |
| 1390 scoped_process process(::OpenProcess( | |
| 1391 PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, | |
| 1392 TRUE, | |
| 1393 process_id)); | |
| 1394 if (!valid(process)) { | |
| 1395 HRESULT hr(HRESULTFromLastError()); | |
| 1396 UTIL_LOG(LEVEL_ERROR, | |
| 1397 (_T("[GetImpersonationToken - ::OpenProcess failed][0x%x]"), | |
| 1398 hr)); | |
| 1399 return hr; | |
| 1400 } | |
| 1401 | |
| 1402 HRESULT result = S_OK; | |
| 1403 scoped_handle process_token; | |
| 1404 if (!::OpenProcessToken(get(process), TOKEN_DUPLICATE | TOKEN_QUERY, | |
| 1405 address(process_token))) { | |
| 1406 result = HRESULTFromLastError(); | |
| 1407 } else { | |
| 1408 if (!::DuplicateTokenEx(get(process_token), | |
| 1409 TOKEN_IMPERSONATE | TOKEN_QUERY | | |
| 1410 TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE, | |
| 1411 NULL, | |
| 1412 SecurityImpersonation, | |
| 1413 TokenPrimary, | |
| 1414 user_token)) { | |
| 1415 result = HRESULTFromLastError(); | |
| 1416 } | |
| 1417 } | |
| 1418 | |
| 1419 ASSERT(SUCCEEDED(result), (_T("[GetImpersonationToken Failed][hr=0x%x]"), | |
| 1420 result)); | |
| 1421 return result; | |
| 1422 } | |
| 1423 | |
| 1424 HRESULT Process::GetUsersOfProcesses(const TCHAR* task_name, | |
| 1425 int maximum_users, | |
| 1426 scoped_handle user_tokens[], | |
| 1427 int* number_of_users) { | |
| 1428 ASSERT1(task_name && *task_name); | |
| 1429 ASSERT1(maximum_users); | |
| 1430 ASSERT1(user_tokens); | |
| 1431 ASSERT1(number_of_users); | |
| 1432 | |
| 1433 scoped_hfile th32cs_snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, | |
| 1434 0)); | |
| 1435 if (!valid(th32cs_snapshot)) { | |
| 1436 HRESULT hr(HRESULTFromLastError()); | |
| 1437 UTIL_LOG(LEVEL_ERROR, (_T("[::CreateToolhelp32Snapshot fail][0x%x]"), hr)); | |
| 1438 return hr; | |
| 1439 } | |
| 1440 | |
| 1441 HRESULT result = S_OK; | |
| 1442 *number_of_users = 0; | |
| 1443 // Walk the list of processes. | |
| 1444 PROCESSENTRY32 process = {0}; | |
| 1445 process.dwSize = sizeof(PROCESSENTRY32); | |
| 1446 for (BOOL found = ::Process32First(get(th32cs_snapshot), &process); found; | |
| 1447 found = ::Process32Next(get(th32cs_snapshot), &process)) { | |
| 1448 // Check if it is one of the processes we are looking for. | |
| 1449 if (_tcsicmp(task_name, process.szExeFile) == 0) { | |
| 1450 // We match. Get the user's token. | |
| 1451 scoped_handle user_token; | |
| 1452 if (FAILED(GetImpersonationToken(process.th32ProcessID, | |
| 1453 address(user_token)))) | |
| 1454 continue; | |
| 1455 | |
| 1456 // Search through the existing list to see if it's a duplicate. | |
| 1457 // It's O(n^2) but we should have very few logged on users. | |
| 1458 int i = 0; | |
| 1459 for (; i < *number_of_users; i++) { | |
| 1460 if (get(user_tokens[i]) == get(user_token)) { | |
| 1461 // It's a duplicate. | |
| 1462 break; | |
| 1463 } | |
| 1464 } | |
| 1465 if (i >= *number_of_users) { | |
| 1466 // It's a new one. Add it if there's room. | |
| 1467 ASSERT1(i < maximum_users); | |
| 1468 if (i < maximum_users) { | |
| 1469 // Release the user_token, we don't want it to be closed | |
| 1470 // by the user_token destructor | |
| 1471 reset(user_tokens[(*number_of_users)++], release(user_token)); | |
| 1472 } | |
| 1473 } | |
| 1474 } | |
| 1475 } | |
| 1476 return result; | |
| 1477 } | |
| 1478 | |
| 1479 HRESULT Process::GetImagePath(const CString& process_name, | |
| 1480 const CString& user_sid, | |
| 1481 CString* path) { | |
| 1482 ASSERT1(path); | |
| 1483 | |
| 1484 // Search for running processes with process_name. | |
| 1485 uint32 mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER; | |
| 1486 std::vector<CString> command_line; | |
| 1487 std::vector<uint32> process_ids; | |
| 1488 HRESULT hr = FindProcesses(mask, | |
| 1489 process_name, | |
| 1490 true, | |
| 1491 user_sid, | |
| 1492 command_line, | |
| 1493 &process_ids); | |
| 1494 if (FAILED(hr)) { | |
| 1495 UTIL_LOG(LEVEL_WARNING, (_T("[FindProcesses failed][0x%08x]"), hr)); | |
| 1496 return hr; | |
| 1497 } | |
| 1498 | |
| 1499 if (process_ids.empty()) { | |
| 1500 return E_FAIL; | |
| 1501 } | |
| 1502 | |
| 1503 uint32 process_id = process_ids[0]; | |
| 1504 UTIL_LOG(L4, (_T("[GetImagePath][pid=%d]"), process_id)); | |
| 1505 scoped_process process_handle(::OpenProcess(PROCESS_QUERY_INFORMATION | | |
| 1506 PROCESS_VM_READ, | |
| 1507 FALSE, | |
| 1508 process_id)); | |
| 1509 if (!process_handle) { | |
| 1510 HRESULT hr = HRESULTFromLastError(); | |
| 1511 UTIL_LOG(L4, (_T("[OpenProcess failed][0x%08x]"), hr)); | |
| 1512 return hr; | |
| 1513 } | |
| 1514 | |
| 1515 HMODULE module_handle = NULL; | |
| 1516 DWORD bytes_needed = 0; | |
| 1517 if (!::EnumProcessModules(get(process_handle), | |
| 1518 &module_handle, | |
| 1519 sizeof(HMODULE), | |
| 1520 &bytes_needed)) { | |
| 1521 HRESULT hr = HRESULTFromLastError(); | |
| 1522 UTIL_LOG(LEVEL_WARNING, (_T("[EnumProcessModules failed][0x%08x]"), hr)); | |
| 1523 // ::EnumProcessModules from a WoW64 process fails for x64 processes. We try | |
| 1524 // ::GetProcessImageFileName as a workaround here. | |
| 1525 TCHAR image_name[MAX_PATH] = {0}; | |
| 1526 if (!GetProcessImageFileName(get(process_handle), | |
| 1527 image_name, | |
| 1528 arraysize(image_name))) { | |
| 1529 HRESULT hr = HRESULTFromLastError(); | |
| 1530 UTIL_LOG(LE, (_T("[GetProcessImageFileName failed][0x%08x]"), hr)); | |
| 1531 return hr; | |
| 1532 } else { | |
| 1533 *path = image_name; | |
| 1534 return S_OK; | |
| 1535 } | |
| 1536 } | |
| 1537 | |
| 1538 TCHAR module_name[MAX_PATH] = {0}; | |
| 1539 if (!::GetModuleFileNameEx(get(process_handle), | |
| 1540 module_handle, | |
| 1541 module_name, | |
| 1542 arraysize(module_name))) { | |
| 1543 HRESULT hr = HRESULTFromLastError(); | |
| 1544 UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx failed][0x%08x]"), hr)); | |
| 1545 return hr; | |
| 1546 } | |
| 1547 | |
| 1548 *path = module_name; | |
| 1549 return S_OK; | |
| 1550 } | |
| 1551 | |
| 1552 bool Process::IsWow64(uint32 pid) { | |
| 1553 typedef BOOL (WINAPI *IsWow64Process)(HANDLE, BOOL*); | |
| 1554 scoped_process handle(::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, | |
| 1555 false, | |
| 1556 pid)); | |
| 1557 if (!handle) { | |
| 1558 return false; | |
| 1559 } | |
| 1560 | |
| 1561 HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll")); | |
| 1562 if (kernel_instance == NULL) { | |
| 1563 ASSERT1(false); | |
| 1564 HRESULT hr = HRESULTFromLastError(); | |
| 1565 UTIL_LOG(LW, (_T("[::GetModuleHandle kernel32.dll failed][0x%08x]"), hr)); | |
| 1566 return false; | |
| 1567 } | |
| 1568 | |
| 1569 IsWow64Process pfn = reinterpret_cast<IsWow64Process>(::GetProcAddress( | |
| 1570 kernel_instance, | |
| 1571 "IsWow64Process")); | |
| 1572 if (!pfn) { | |
| 1573 UTIL_LOG(LW, (_T("[::IsWow64Process() not found in kernel32.dll]"))); | |
| 1574 return false; | |
| 1575 } | |
| 1576 | |
| 1577 BOOL wow64 = FALSE; | |
| 1578 if (!(*pfn)(get(handle), &wow64)) { | |
| 1579 HRESULT hr = HRESULTFromLastError(); | |
| 1580 UTIL_LOG(LW, (_T("[::IsWow64Process() failed][0x%08x]"), hr)); | |
| 1581 return false; | |
| 1582 } | |
| 1583 | |
| 1584 return (wow64 != 0); | |
| 1585 } | |
| 1586 | |
| 1587 HRESULT Process::MakeProcessWindowForeground(const CString& executable) { | |
| 1588 UTIL_LOG(L3, (_T("[MakeProcessWindowForeground]"))); | |
| 1589 | |
| 1590 CString sid; | |
| 1591 HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &sid); | |
| 1592 if (FAILED(hr)) { | |
| 1593 return hr; | |
| 1594 } | |
| 1595 | |
| 1596 // This code does not handle two cases: | |
| 1597 // 1. If a new process instance is starting up but there are other process | |
| 1598 // instances running, then we will not wait for the new process instance. | |
| 1599 // One way to fix this is to pass the number of expected processes to this | |
| 1600 // method. | |
| 1601 // 2. If we find multiple processes, and we are able to find the windows only | |
| 1602 // for some of the processes (maybe because the rest are still starting up) | |
| 1603 // then we will only set the windows of the one that we found to the | |
| 1604 // foreground and ignore the rest. | |
| 1605 bool found = false; | |
| 1606 for (int retries = 0; retries < kNumRetriesToFindProcess && !found; | |
| 1607 ++retries) { | |
| 1608 std::vector<CString> command_lines; | |
| 1609 std::vector<uint32> processes; | |
| 1610 DWORD flags = EXCLUDE_CURRENT_PROCESS | INCLUDE_ONLY_PROCESS_OWNED_BY_USER; | |
| 1611 hr = Process::FindProcesses(flags, | |
| 1612 executable, | |
| 1613 true, | |
| 1614 sid, | |
| 1615 command_lines, | |
| 1616 &processes); | |
| 1617 if (FAILED(hr)) { | |
| 1618 UTIL_LOG(LW, (_T("[FindProcesses failed][0x%08x]"), hr)); | |
| 1619 return hr; | |
| 1620 } | |
| 1621 | |
| 1622 UTIL_LOG(L3, (_T("[Found %d processes]"), processes.size())); | |
| 1623 for (size_t i = 0; i < processes.size(); ++i) { | |
| 1624 CSimpleArray<HWND> windows; | |
| 1625 if (!WindowUtils::FindProcessWindows(processes[i], 0, &windows)) { | |
| 1626 UTIL_LOG(L3, (_T("[FindProcessWindows failed][0x%08x]"), hr)); | |
| 1627 continue; | |
| 1628 } | |
| 1629 | |
| 1630 for (int j = 0; j < windows.GetSize(); ++j) { | |
| 1631 if (WindowUtils::IsMainWindow(windows[j])) { | |
| 1632 UTIL_LOG(L4, (_T("[Found main window of process %d]"), processes[i])); | |
| 1633 WindowUtils::MakeWindowForeground(windows[j]); | |
| 1634 ::FlashWindow(windows[j], true); | |
| 1635 found = true; | |
| 1636 break; | |
| 1637 } | |
| 1638 } | |
| 1639 } | |
| 1640 | |
| 1641 if (!found) { | |
| 1642 ::Sleep(kFindProcessRetryIntervalMs); | |
| 1643 } | |
| 1644 } | |
| 1645 | |
| 1646 return S_OK; | |
| 1647 } | |
| 1648 | |
| 1649 } // namespace omaha | |
| 1650 | |
| OLD | NEW |