| OLD | NEW |
| (Empty) |
| 1 // Copyright 2004-2010 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 #include "omaha/base/system.h" | |
| 17 | |
| 18 #include <objidl.h> | |
| 19 #include <psapi.h> | |
| 20 #include <winioctl.h> | |
| 21 #include <wtsapi32.h> | |
| 22 #include "omaha/base/commands.h" | |
| 23 #include "omaha/base/commontypes.h" | |
| 24 #include "omaha/base/const_config.h" | |
| 25 #include "omaha/base/debug.h" | |
| 26 #include "omaha/base/disk.h" | |
| 27 #include "omaha/base/dynamic_link_kernel32.h" | |
| 28 #include "omaha/base/error.h" | |
| 29 #include "omaha/base/file.h" | |
| 30 #include "omaha/base/logging.h" | |
| 31 #include "omaha/base/module_utils.h" | |
| 32 #include "omaha/base/path.h" | |
| 33 #include "omaha/base/scope_guard.h" | |
| 34 #include "omaha/base/scoped_any.h" | |
| 35 #include "omaha/base/string.h" | |
| 36 #include "omaha/base/system_info.h" | |
| 37 #include "omaha/base/utils.h" | |
| 38 #include "omaha/base/vistautil.h" | |
| 39 | |
| 40 namespace omaha { | |
| 41 | |
| 42 // Constant | |
| 43 const TCHAR kNeedRebootHiddenFileSuffix[] = _T(".needreboot"); | |
| 44 | |
| 45 HRESULT System::WaitForDiskActivity(const uint32 max_delay_milliseconds, | |
| 46 const uint32 sleep_time_ms, | |
| 47 uint32 *time_waited) { | |
| 48 ASSERT(time_waited, (L"")); | |
| 49 uint32 sleep_time = sleep_time_ms; | |
| 50 if (sleep_time < 20) { sleep_time = 20; } | |
| 51 else if (sleep_time > 1000) { sleep_time = 1000; } | |
| 52 HRESULT r; | |
| 53 *time_waited = 0; | |
| 54 uint64 writes = 0; | |
| 55 uint64 new_writes = 0; | |
| 56 // get current counters | |
| 57 if (FAILED(r=GetDiskActivityCounters(NULL, &writes, NULL, NULL))) { | |
| 58 return r; | |
| 59 } | |
| 60 | |
| 61 // wait until a write - reads may be cached | |
| 62 while (1) { | |
| 63 if (FAILED(r=GetDiskActivityCounters(NULL, &new_writes, NULL, NULL))) { | |
| 64 return r; | |
| 65 } | |
| 66 if (new_writes > writes) { return S_OK; } | |
| 67 if (*time_waited > max_delay_milliseconds) { return E_FAIL; } | |
| 68 SleepEx(sleep_time, TRUE); | |
| 69 *time_waited += sleep_time; | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 HRESULT System::GetDiskActivityCounters(uint64* reads, | |
| 74 uint64* writes, | |
| 75 uint64* bytes_read, | |
| 76 uint64* bytes_written) { | |
| 77 if (reads) { | |
| 78 *reads = 0; | |
| 79 } | |
| 80 | |
| 81 if (writes) { | |
| 82 *writes = 0; | |
| 83 } | |
| 84 | |
| 85 if (bytes_read) { | |
| 86 *bytes_read = 0; | |
| 87 } | |
| 88 | |
| 89 if (bytes_written) { | |
| 90 *bytes_written = 0; | |
| 91 } | |
| 92 | |
| 93 // Don't want to risk displaying UI errors here | |
| 94 DisableThreadErrorUI disable_error_dialog_box; | |
| 95 | |
| 96 // for all drives | |
| 97 for (int drive = 0; ; drive++) { | |
| 98 struct _DISK_PERFORMANCE perf_data; | |
| 99 const int max_device_len = 50; | |
| 100 | |
| 101 // check whether we can access this device | |
| 102 CString device_name; | |
| 103 device_name.Format(_T("\\\\.\\PhysicalDrive%d"), drive); | |
| 104 scoped_handle device(::CreateFile(device_name, 0, | |
| 105 FILE_SHARE_READ | FILE_SHARE_WRITE, | |
| 106 NULL, OPEN_EXISTING, 0, NULL)); | |
| 107 | |
| 108 if (get(device) == INVALID_HANDLE_VALUE) { | |
| 109 if (!drive) { | |
| 110 UTIL_LOG(LEVEL_ERROR, (_T("[Failed to access drive %i][0x%x]"), | |
| 111 drive, | |
| 112 HRESULTFromLastError())); | |
| 113 } | |
| 114 break; | |
| 115 } | |
| 116 | |
| 117 // disk performance counters must be on (diskperf -y on older machines; | |
| 118 // defaults to on on newer windows) | |
| 119 DWORD size = 0; | |
| 120 if (::DeviceIoControl(get(device), | |
| 121 IOCTL_DISK_PERFORMANCE, | |
| 122 NULL, | |
| 123 0, | |
| 124 &perf_data, | |
| 125 sizeof(_DISK_PERFORMANCE), | |
| 126 &size, | |
| 127 NULL)) { | |
| 128 if (reads) { | |
| 129 *reads += perf_data.ReadCount; | |
| 130 } | |
| 131 | |
| 132 if (writes) { | |
| 133 *writes += perf_data.WriteCount; | |
| 134 } | |
| 135 | |
| 136 if (bytes_read) { | |
| 137 *bytes_read += perf_data.BytesRead.QuadPart; | |
| 138 } | |
| 139 | |
| 140 if (bytes_written) { | |
| 141 *bytes_written += perf_data.BytesWritten.QuadPart; | |
| 142 } | |
| 143 } else { | |
| 144 HRESULT hr = HRESULTFromLastError(); | |
| 145 UTIL_LOG(LEVEL_ERROR, | |
| 146 (_T("[System::GetDiskActivityCounters - failed to ") | |
| 147 _T("DeviceIoControl][0x%x]"), hr)); | |
| 148 return hr; | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 return S_OK; | |
| 153 } | |
| 154 | |
| 155 HRESULT System::GetDiskStatistics(const TCHAR* path, | |
| 156 uint64 *free_bytes_current_user, | |
| 157 uint64 *total_bytes_current_user, | |
| 158 uint64 *free_bytes_all_users) { | |
| 159 ASSERT1(path); | |
| 160 ASSERT1(free_bytes_current_user); | |
| 161 ASSERT1(total_bytes_current_user); | |
| 162 ASSERT1(free_bytes_all_users); | |
| 163 ASSERT1(sizeof(LARGE_INTEGER) == sizeof(uint64)); // NOLINT | |
| 164 | |
| 165 DisableThreadErrorUI disable_error_dialog_box; | |
| 166 | |
| 167 if (!::GetDiskFreeSpaceEx( | |
| 168 path, | |
| 169 reinterpret_cast<PULARGE_INTEGER>(free_bytes_current_user), | |
| 170 reinterpret_cast<PULARGE_INTEGER>(total_bytes_current_user), | |
| 171 reinterpret_cast<PULARGE_INTEGER>(free_bytes_all_users))) { | |
| 172 HRESULT hr = HRESULTFromLastError(); | |
| 173 UTIL_LOG(LEVEL_ERROR, | |
| 174 (_T("[Failed to GetDiskFreeSpaceEx][%s][0x%x]"), path, hr)); | |
| 175 return hr; | |
| 176 } | |
| 177 | |
| 178 return S_OK; | |
| 179 } | |
| 180 | |
| 181 HRESULT System::GetProcessMemoryStatistics(uint64 *current_working_set, | |
| 182 uint64 *peak_working_set, | |
| 183 uint64 *min_working_set_size, | |
| 184 uint64 *max_working_set_size) { | |
| 185 HANDLE process_handle = GetCurrentProcess(); | |
| 186 HRESULT hr = S_OK; | |
| 187 | |
| 188 DWORD min_size(0), max_size(0); | |
| 189 if (GetProcessWorkingSetSize(process_handle, &min_size, &max_size)) { | |
| 190 UTIL_LOG(L2, (_T("[working set][min: %lu][max: %lu]"), min_size, max_size)); | |
| 191 if (min_working_set_size) { | |
| 192 *min_working_set_size = min_size; | |
| 193 } | |
| 194 if (max_working_set_size) { | |
| 195 *max_working_set_size = max_size; | |
| 196 } | |
| 197 } else { | |
| 198 if (min_working_set_size) { | |
| 199 *min_working_set_size = 0; | |
| 200 } | |
| 201 if (max_working_set_size) { | |
| 202 *max_working_set_size = 0; | |
| 203 } | |
| 204 hr = E_FAIL; | |
| 205 } | |
| 206 | |
| 207 if (current_working_set) { *current_working_set = 0; } | |
| 208 if (peak_working_set) { *peak_working_set = 0; } | |
| 209 | |
| 210 // including this call (w/psapi.lib) adds 24k to the process memory | |
| 211 // according to task manager in one test, memory usage according to task | |
| 212 // manager increased by 4k after calling this | |
| 213 PROCESS_MEMORY_COUNTERS counters = { sizeof(counters), 0 }; | |
| 214 if (GetProcessMemoryInfo(process_handle, | |
| 215 &counters, | |
| 216 sizeof(PROCESS_MEMORY_COUNTERS))) { | |
| 217 if (current_working_set) { | |
| 218 *current_working_set = counters.WorkingSetSize; | |
| 219 } | |
| 220 if (peak_working_set) { | |
| 221 *peak_working_set = counters.PeakWorkingSetSize; | |
| 222 } | |
| 223 UTIL_LOG(L2, (_T("[working set][current: %s][peak: %s]"), | |
| 224 String_Int64ToString(*current_working_set, 10), | |
| 225 String_Int64ToString(*peak_working_set, 10))); | |
| 226 } else { | |
| 227 if (current_working_set) { | |
| 228 *current_working_set = 0; | |
| 229 } | |
| 230 if (peak_working_set) { | |
| 231 *peak_working_set = 0; | |
| 232 } | |
| 233 hr = E_FAIL; | |
| 234 } | |
| 235 | |
| 236 return hr; | |
| 237 } | |
| 238 | |
| 239 HRESULT System::MaxPhysicalMemoryAvailable(uint64* max_bytes) { | |
| 240 ASSERT1(max_bytes); | |
| 241 | |
| 242 *max_bytes = 0; | |
| 243 | |
| 244 uint32 memory_load_percentage = 0; | |
| 245 uint64 free_physical_memory = 0; | |
| 246 | |
| 247 RET_IF_FAILED(System::GetGlobalMemoryStatistics(&memory_load_percentage, | |
| 248 &free_physical_memory, NULL, NULL, NULL, NULL, NULL)); | |
| 249 | |
| 250 UTIL_LOG(L4, (_T("mem load %u max physical memory available %s"), | |
| 251 memory_load_percentage, | |
| 252 String_Int64ToString(free_physical_memory, 10))); | |
| 253 | |
| 254 *max_bytes = free_physical_memory; | |
| 255 | |
| 256 return S_OK; | |
| 257 } | |
| 258 | |
| 259 HRESULT System::GetGlobalMemoryStatistics(uint32 *memory_load_percentage, | |
| 260 uint64 *free_physical_memory, | |
| 261 uint64 *total_physical_memory, | |
| 262 uint64 *free_paged_memory, | |
| 263 uint64 *total_paged_memory, | |
| 264 uint64 *process_free_virtual_memory, | |
| 265 uint64 *process_total_virtual_mem) { | |
| 266 MEMORYSTATUSEX status; | |
| 267 status.dwLength = sizeof(status); | |
| 268 if (!GlobalMemoryStatusEx(&status)) { | |
| 269 UTIL_LOG(LEVEL_ERROR, (_T("memory status error %u"), GetLastError())); | |
| 270 return E_FAIL; | |
| 271 } | |
| 272 if (memory_load_percentage) { *memory_load_percentage = status.dwMemoryLoad; } | |
| 273 if (free_physical_memory) { *free_physical_memory = status.ullAvailPhys; } | |
| 274 if (total_physical_memory) { *total_physical_memory = status.ullTotalPhys; } | |
| 275 if (free_paged_memory) { *free_paged_memory = status.ullAvailPageFile; } | |
| 276 if (total_paged_memory) { *total_paged_memory = status.ullTotalPageFile; } | |
| 277 if (process_free_virtual_memory) { | |
| 278 *process_free_virtual_memory = status.ullAvailVirtual; | |
| 279 } | |
| 280 if (process_total_virtual_mem) { | |
| 281 *process_total_virtual_mem = status.ullTotalVirtual; | |
| 282 } | |
| 283 // GetPerformanceInfo; | |
| 284 return S_OK; | |
| 285 } | |
| 286 | |
| 287 void System::FreeProcessWorkingSet() { | |
| 288 // -1,-1 is a special signal to the OS to temporarily trim the working set | |
| 289 // size to 0. See MSDN for further information. | |
| 290 ::SetProcessWorkingSetSize(::GetCurrentProcess(), (SIZE_T)-1, (SIZE_T)-1); | |
| 291 } | |
| 292 | |
| 293 HRESULT System::SetThreadPriority(enum Priority priority) { | |
| 294 int pri; | |
| 295 | |
| 296 switch (priority) { | |
| 297 case LOW: pri = THREAD_PRIORITY_BELOW_NORMAL; break; | |
| 298 case HIGH: pri = THREAD_PRIORITY_HIGHEST; break; | |
| 299 case NORMAL: pri = THREAD_PRIORITY_NORMAL; break; | |
| 300 case IDLE: pri = THREAD_PRIORITY_IDLE; break; | |
| 301 default: return E_FAIL; | |
| 302 } | |
| 303 | |
| 304 if (::SetThreadPriority(GetCurrentThread(), pri)) { | |
| 305 return S_OK; | |
| 306 } else { | |
| 307 return E_FAIL; | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 HRESULT System::SetProcessPriority(enum Priority priority) { | |
| 312 DWORD pri = 0; | |
| 313 switch (priority) { | |
| 314 case LOW: pri = BELOW_NORMAL_PRIORITY_CLASS; break; | |
| 315 case HIGH: pri = ABOVE_NORMAL_PRIORITY_CLASS; break; | |
| 316 case NORMAL: pri = NORMAL_PRIORITY_CLASS; break; | |
| 317 case IDLE: return E_INVALIDARG; | |
| 318 default: return E_INVALIDARG; | |
| 319 } | |
| 320 | |
| 321 DWORD pid = ::GetCurrentProcessId(); | |
| 322 | |
| 323 scoped_handle handle(::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)); | |
| 324 if (!valid(handle)) { | |
| 325 HRESULT hr = HRESULTFromLastError(); | |
| 326 UTIL_LOG(LE, (_T("[::OpenProcess failed][%u][0x%x]"), pid, hr)); | |
| 327 return hr; | |
| 328 } | |
| 329 | |
| 330 if (!::SetPriorityClass(get(handle), pri)) { | |
| 331 HRESULT hr = HRESULTFromLastError(); | |
| 332 UTIL_LOG(LE, (_T("[::SetPriorityClass failed][%u][0x%x]"), pid, hr)); | |
| 333 return hr; | |
| 334 } | |
| 335 | |
| 336 return S_OK; | |
| 337 } | |
| 338 | |
| 339 // start another process painlessly via ::CreateProcess. Use the | |
| 340 // ShellExecuteProcessXXX variants instead of these methods where possible, | |
| 341 // since ::ShellExecuteEx has better behavior on Windows Vista. | |
| 342 // When using this method, avoid using process_name - see | |
| 343 // http://blogs.msdn.com/oldnewthing/archive/2006/05/15/597984.aspx. | |
| 344 HRESULT System::StartProcess(const TCHAR* process_name, | |
| 345 TCHAR* command_line, | |
| 346 PROCESS_INFORMATION* pi) { | |
| 347 ASSERT1(pi); | |
| 348 ASSERT1(command_line || process_name); | |
| 349 ASSERT(!process_name, (_T("Avoid using process_name. See method comment."))); | |
| 350 | |
| 351 STARTUPINFO si = {sizeof(si), 0}; | |
| 352 | |
| 353 // Feedback cursor is off while the process is starting. | |
| 354 si.dwFlags = STARTF_FORCEOFFFEEDBACK; | |
| 355 | |
| 356 UTIL_LOG(L3, (_T("[System::StartProcess][process %s][cmd %s]"), | |
| 357 process_name, command_line)); | |
| 358 | |
| 359 BOOL success = ::CreateProcess( | |
| 360 process_name, // Module name | |
| 361 command_line, // Command line | |
| 362 NULL, // Process handle not inheritable | |
| 363 NULL, // Thread handle not inheritable | |
| 364 FALSE, // Set handle inheritance to FALSE | |
| 365 0, // No creation flags | |
| 366 NULL, // Use parent's environment block | |
| 367 NULL, // Use parent's starting directory | |
| 368 &si, // Pointer to STARTUPINFO structure | |
| 369 pi); // Pointer to PROCESS_INFORMATION structure | |
| 370 | |
| 371 if (!success) { | |
| 372 HRESULT hr = HRESULTFromLastError(); | |
| 373 UTIL_LOG(LEVEL_ERROR, | |
| 374 (_T("[System::StartProcess][::CreateProcess failed][0x%x]"), hr)); | |
| 375 return hr; | |
| 376 } | |
| 377 | |
| 378 OPT_LOG(L1, (_T("[Started process][%u]"), pi->dwProcessId)); | |
| 379 | |
| 380 return S_OK; | |
| 381 } | |
| 382 | |
| 383 // start another process painlessly via ::CreateProcess. Use the | |
| 384 // ShellExecuteProcessXXX variants instead of these methods where possible, | |
| 385 // since ::ShellExecuteEx has better behavior on Windows Vista. | |
| 386 HRESULT System::StartProcessWithArgsAndInfo(const TCHAR *process_name, | |
| 387 const TCHAR *cmd_line_arguments, | |
| 388 PROCESS_INFORMATION *pi) { | |
| 389 ASSERT1(process_name && cmd_line_arguments && pi); | |
| 390 | |
| 391 CString command_line(process_name); | |
| 392 EnclosePath(&command_line); | |
| 393 command_line.AppendChar(_T(' ')); | |
| 394 command_line.Append(cmd_line_arguments); | |
| 395 return System::StartProcess(NULL, CStrBuf(command_line), pi); | |
| 396 } | |
| 397 | |
| 398 // start another process painlessly via ::CreateProcess. Use the | |
| 399 // ShellExecuteProcessXXX variants instead of these methods where possible, | |
| 400 // since ::ShellExecuteEx has better behavior on Windows Vista. | |
| 401 HRESULT System::StartProcessWithArgs(const TCHAR *process_name, | |
| 402 const TCHAR *cmd_line_arguments) { | |
| 403 ASSERT1(process_name && cmd_line_arguments); | |
| 404 PROCESS_INFORMATION pi = {0}; | |
| 405 HRESULT hr = System::StartProcessWithArgsAndInfo(process_name, | |
| 406 cmd_line_arguments, | |
| 407 &pi); | |
| 408 if (SUCCEEDED(hr)) { | |
| 409 ::CloseHandle(pi.hProcess); | |
| 410 ::CloseHandle(pi.hThread); | |
| 411 } | |
| 412 return hr; | |
| 413 } | |
| 414 | |
| 415 HRESULT System::StartCommandLine(const TCHAR* command_line_to_execute) { | |
| 416 ASSERT1(command_line_to_execute); | |
| 417 | |
| 418 CString command_line(command_line_to_execute); | |
| 419 PROCESS_INFORMATION pi = {0}; | |
| 420 HRESULT hr = System::StartProcess(NULL, CStrBuf(command_line), &pi); | |
| 421 if (SUCCEEDED(hr)) { | |
| 422 ::CloseHandle(pi.hProcess); | |
| 423 ::CloseHandle(pi.hThread); | |
| 424 } | |
| 425 return hr; | |
| 426 } | |
| 427 | |
| 428 // TODO(omaha3): Unit test this method. | |
| 429 HRESULT System::StartProcessAsUser(HANDLE user_token, | |
| 430 const CString& executable_path, | |
| 431 const CString& parameters, | |
| 432 LPWSTR desktop, | |
| 433 PROCESS_INFORMATION* pi) { | |
| 434 UTIL_LOG(L3, (_T("[StartProcessAsUser][%s][%s][%s]"), | |
| 435 executable_path, parameters, desktop)); | |
| 436 ASSERT1(pi); | |
| 437 | |
| 438 CString cmd(executable_path); | |
| 439 EnclosePath(&cmd); | |
| 440 cmd.AppendChar(_T(' ')); | |
| 441 cmd.Append(parameters); | |
| 442 | |
| 443 STARTUPINFO startup_info = { sizeof(startup_info) }; | |
| 444 startup_info.lpDesktop = desktop; | |
| 445 DWORD creation_flags(0); | |
| 446 | |
| 447 void* environment_block(NULL); | |
| 448 if (!::CreateEnvironmentBlock(&environment_block, user_token, TRUE)) { | |
| 449 HRESULT hr = HRESULTFromLastError(); | |
| 450 ASSERT(false, (_T("[::CreateEnvironmentBlock failed][0x%x]"), hr)); | |
| 451 return hr; | |
| 452 } | |
| 453 | |
| 454 ON_SCOPE_EXIT(::DestroyEnvironmentBlock, environment_block); | |
| 455 | |
| 456 creation_flags |= CREATE_UNICODE_ENVIRONMENT; | |
| 457 BOOL success = ::CreateProcessAsUser(user_token, | |
| 458 0, | |
| 459 CStrBuf(cmd, MAX_PATH), | |
| 460 0, | |
| 461 0, | |
| 462 false, | |
| 463 creation_flags, | |
| 464 environment_block, | |
| 465 0, | |
| 466 &startup_info, | |
| 467 pi); | |
| 468 | |
| 469 if (!success) { | |
| 470 HRESULT hr(HRESULTFromLastError()); | |
| 471 UTIL_LOG(LE, (_T("[::CreateProcessAsUser failed][%s][0x%x]"), cmd, hr)); | |
| 472 return hr; | |
| 473 } | |
| 474 | |
| 475 return S_OK; | |
| 476 } | |
| 477 | |
| 478 // start another process painlessly via ::ShellExecuteEx. Use this method | |
| 479 // instead of the StartProcessXXX methods that use ::CreateProcess where | |
| 480 // possible, since ::ShellExecuteEx has better behavior on Windows Vista. | |
| 481 // | |
| 482 // ShellExecuteExEnsureParent displays the PID of the started process if it is | |
| 483 // returned. It is only returned if the mask includes SEE_MASK_NOCLOSEPROCESS. | |
| 484 // Therefore, we always set this flag and pass a handle. If the caller did not | |
| 485 // request the handle, we close it. | |
| 486 HRESULT System::ShellExecuteProcess(const TCHAR* file_name_to_execute, | |
| 487 const TCHAR* command_line_parameters, | |
| 488 HWND hwnd, | |
| 489 HANDLE* process_handle) { | |
| 490 ASSERT1(file_name_to_execute); | |
| 491 | |
| 492 UTIL_LOG(L3, (_T("[System::ShellExecuteProcess]") | |
| 493 _T("[file_name_to_execute '%s' command_line_parameters '%s']"), | |
| 494 file_name_to_execute, command_line_parameters)); | |
| 495 | |
| 496 SHELLEXECUTEINFO sei = {0}; | |
| 497 sei.cbSize = sizeof(sei); | |
| 498 // SEE_MASK_NOZONECHECKS is set below to work around a problem in systems that | |
| 499 // had Internet Explorer 7 Beta installed. See http://b/804674. | |
| 500 // This only works for Windows XP SP1 and later. | |
| 501 sei.fMask = SEE_MASK_NOCLOSEPROCESS | // Set hProcess to process handle. | |
| 502 SEE_MASK_FLAG_NO_UI | // Do not display an error message box. | |
| 503 SEE_MASK_NOZONECHECKS | // Do not perform a zone check. | |
| 504 SEE_MASK_NOASYNC; // Wait to complete before returning. | |
| 505 sei.lpVerb = _T("open"); | |
| 506 sei.lpFile = file_name_to_execute; | |
| 507 sei.lpParameters = command_line_parameters; | |
| 508 sei.nShow = SW_SHOWNORMAL; | |
| 509 sei.hwnd = hwnd; | |
| 510 | |
| 511 // Use ShellExecuteExEnsureParent to ensure that we always have a parent | |
| 512 // window. We need to use the HWND property to be acknowledged as a foreground | |
| 513 // application on Windows Vista. Otherwise, the elevation prompt will appear | |
| 514 // minimized on the taskbar. | |
| 515 if (!ShellExecuteExEnsureParent(&sei)) { | |
| 516 HRESULT hr(HRESULTFromLastError()); | |
| 517 OPT_LOG(LEVEL_ERROR, (_T("[Failed to ::ShellExecuteEx][%s][%s][0x%08x]"), | |
| 518 file_name_to_execute, command_line_parameters, hr)); | |
| 519 return hr; | |
| 520 } | |
| 521 | |
| 522 if (process_handle) { | |
| 523 *process_handle = sei.hProcess; | |
| 524 } else { | |
| 525 ::CloseHandle(sei.hProcess); | |
| 526 } | |
| 527 | |
| 528 return S_OK; | |
| 529 } | |
| 530 | |
| 531 // start another process painlessly via ::ShellExecuteEx. Use this method | |
| 532 // instead of the StartProcessXXX methods that use ::CreateProcess where | |
| 533 // possible, since ::ShellExecuteEx has better behavior on Windows Vista. | |
| 534 HRESULT System::ShellExecuteCommandLine(const TCHAR* command_line_to_execute, | |
| 535 HWND hwnd, | |
| 536 HANDLE* process_handle) { | |
| 537 ASSERT1(command_line_to_execute); | |
| 538 | |
| 539 CString exe; | |
| 540 CString args; | |
| 541 | |
| 542 HRESULT hr = CommandParsingSimple::SplitExeAndArgs(command_line_to_execute, | |
| 543 &exe, | |
| 544 &args); | |
| 545 | |
| 546 if (SUCCEEDED(hr)) { | |
| 547 hr = System::ShellExecuteProcess(exe, args, hwnd, process_handle); | |
| 548 if (FAILED(hr)) { | |
| 549 UTIL_LOG(LEVEL_ERROR, (_T("[System::ShellExecuteProcess failed]") | |
| 550 _T("[%s][%s][0x%08x]"), exe, args, hr)); | |
| 551 } | |
| 552 } | |
| 553 | |
| 554 return hr; | |
| 555 } | |
| 556 | |
| 557 // returns the number of ms the system has had no user input | |
| 558 int System::GetUserIdleTime() { | |
| 559 LASTINPUTINFO last_input_info; | |
| 560 last_input_info.cbSize = sizeof(LASTINPUTINFO); | |
| 561 // get time in windows ticks since system start of last activity | |
| 562 BOOL b = GetLastInputInfo(&last_input_info); | |
| 563 if (b == TRUE) { | |
| 564 return (GetTickCount()-last_input_info.dwTime); // compute idle time | |
| 565 } | |
| 566 return 0; | |
| 567 } | |
| 568 | |
| 569 bool System::IsUserIdle() { | |
| 570 // Only notify when the user has been idle less than this time | |
| 571 static int user_idle_threshold_ms = kUserIdleThresholdMs; | |
| 572 | |
| 573 bool is_user_idle = (GetUserIdleTime() > user_idle_threshold_ms); | |
| 574 UTIL_LOG(L2, (_T("System::IsUserIdle() %s; user_idle_threshold_ms = %d"), | |
| 575 is_user_idle ? _T("TRUE") : _T("FALSE"), | |
| 576 user_idle_threshold_ms)); | |
| 577 return is_user_idle; | |
| 578 } | |
| 579 | |
| 580 bool System::IsUserBusy() { | |
| 581 // The user is busy typing or interacting with another application | |
| 582 // if the user is below the minimum threshold: | |
| 583 static int user_idle_min_threshold_ms = kUserIdleMinThresholdMs; | |
| 584 // The user is probably not paying attention | |
| 585 // if the user is above the maximum threshold: | |
| 586 static int user_idle_max_threshold_ms = kUserIdleMaxThresholdMs; | |
| 587 | |
| 588 int user_idle_time = GetUserIdleTime(); | |
| 589 bool is_user_busy = user_idle_time < user_idle_min_threshold_ms || | |
| 590 user_idle_time > user_idle_max_threshold_ms; | |
| 591 UTIL_LOG(L2, (_T("[System::IsUserBusy() %s][user_idle_time = %d]") | |
| 592 _T("[user_idle_min_threshold_ms = %d]") | |
| 593 _T("[user_idle_max_threshold_ms = %d]"), | |
| 594 is_user_busy? _T("TRUE") : _T("FALSE"), | |
| 595 user_idle_time, | |
| 596 user_idle_min_threshold_ms, | |
| 597 user_idle_max_threshold_ms)); | |
| 598 return is_user_busy; | |
| 599 } | |
| 600 | |
| 601 bool System::IsScreensaverRunning() { | |
| 602 // NT 4.0 and below require testing OpenDesktop("screen-saver") | |
| 603 // We require W2K or better so we have an easier way | |
| 604 DWORD result = 0; | |
| 605 ::SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &result, 0); | |
| 606 bool is_screensaver_running = (result != FALSE); | |
| 607 UTIL_LOG(L2, (_T("System::IsScreensaverRunning() %s"), | |
| 608 is_screensaver_running? _T("TRUE") : _T("FALSE"))); | |
| 609 return is_screensaver_running; | |
| 610 } | |
| 611 | |
| 612 bool System::IsWorkstationLocked() { | |
| 613 bool is_workstation_locked = true; | |
| 614 HDESK inputdesk = ::OpenInputDesktop(0, 0, GENERIC_READ); | |
| 615 if (NULL != inputdesk) { | |
| 616 TCHAR name[256]; | |
| 617 DWORD needed = arraysize(name); | |
| 618 BOOL ok = ::GetUserObjectInformation(inputdesk, | |
| 619 UOI_NAME, | |
| 620 name, | |
| 621 sizeof(name), | |
| 622 &needed); | |
| 623 ::CloseDesktop(inputdesk); | |
| 624 if (ok) { | |
| 625 is_workstation_locked = (0 != lstrcmpi(name, NOTRANSL(_T("default")))); | |
| 626 } | |
| 627 } | |
| 628 | |
| 629 UTIL_LOG(L2, (_T("System::IsWorkstationLocked() %s"), | |
| 630 is_workstation_locked? _T("TRUE") : _T("FALSE"))); | |
| 631 return is_workstation_locked; | |
| 632 } | |
| 633 | |
| 634 bool System::IsUserAway() { | |
| 635 return IsScreensaverRunning() || IsWorkstationLocked(); | |
| 636 } | |
| 637 | |
| 638 uint32 System::GetProcessHandleCount() { | |
| 639 typedef LONG (CALLBACK *Fun)(HANDLE, int32, PVOID, ULONG, PULONG); | |
| 640 | |
| 641 // This new version of getting the number of open handles works on win2k. | |
| 642 HMODULE h = GetModuleHandle(_T("ntdll.dll")); | |
| 643 Fun NtQueryInformationProcess = | |
| 644 reinterpret_cast<Fun>(::GetProcAddress(h, "NtQueryInformationProcess")); | |
| 645 | |
| 646 if (!NtQueryInformationProcess) { | |
| 647 UTIL_LOG(LEVEL_ERROR, (_T("[NtQueryInformationProcess failed][0x%x]"), | |
| 648 HRESULTFromLastError())); | |
| 649 return 0; | |
| 650 } | |
| 651 | |
| 652 DWORD count = 0; | |
| 653 VERIFY(NtQueryInformationProcess(GetCurrentProcess(), | |
| 654 kProcessHandleCount, | |
| 655 &count, | |
| 656 sizeof(count), | |
| 657 NULL) >= 0, (L"")); | |
| 658 | |
| 659 return count; | |
| 660 } | |
| 661 | |
| 662 uint32 System::GetProcessHandleCountOld() { | |
| 663 typedef BOOL (CALLBACK * Fun)(HANDLE, PDWORD); | |
| 664 | |
| 665 // GetProcessHandleCount not available on win2k | |
| 666 HMODULE handle = GetModuleHandle(_T("kernel32")); | |
| 667 Fun f = reinterpret_cast<Fun>(GetProcAddress(handle, | |
| 668 "GetProcessHandleCount")); | |
| 669 | |
| 670 if (!f) return 0; | |
| 671 | |
| 672 DWORD count = 0; | |
| 673 VERIFY((*f)(GetCurrentProcess(), &count), (L"")); | |
| 674 return count; | |
| 675 | |
| 676 // DWORD GetGuiResources (HANDLE hProcess, DWORD uiFlags); | |
| 677 // Parameters, hProcess | |
| 678 // [in] Handle to the process. The handle must have the | |
| 679 // PROCESS_QUERY_INFORMATION access right. For more information, see Process | |
| 680 // Security and Access Rights. | |
| 681 // uiFlags | |
| 682 // [in] GUI object type. This parameter can be one of the following values. | |
| 683 // Value Meaning | |
| 684 // GR_GDIOBJECTS Return the count of GDI objects. | |
| 685 // GR_USEROBJECTS Return the count of USER objects. | |
| 686 } | |
| 687 | |
| 688 void System::GetGuiObjectCount(uint32 *gdi, uint32 *user) { | |
| 689 if (gdi) { | |
| 690 *gdi = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS); | |
| 691 } | |
| 692 if (user) { | |
| 693 *user = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS); | |
| 694 } | |
| 695 } | |
| 696 | |
| 697 HRESULT System::GetRebootCheckDummyFileName(const TCHAR* base_file, | |
| 698 CString* dummy_file) { | |
| 699 ASSERT1(dummy_file); | |
| 700 | |
| 701 if (base_file && *base_file) { | |
| 702 ASSERT1(File::Exists(base_file)); | |
| 703 dummy_file->SetString(base_file); | |
| 704 } else { | |
| 705 RET_IF_FAILED(GetModuleFileName(NULL, dummy_file)); | |
| 706 } | |
| 707 dummy_file->Append(_T(".needreboot")); | |
| 708 return S_OK; | |
| 709 } | |
| 710 | |
| 711 // Is the system being rebooted? | |
| 712 bool System::IsRebooted(const TCHAR* base_file) { | |
| 713 CString dummy_file; | |
| 714 if (SUCCEEDED(GetRebootCheckDummyFileName(base_file, &dummy_file))) { | |
| 715 if (File::Exists(dummy_file)) { | |
| 716 // If the file exists but it is not found in the | |
| 717 // PendingFileRenameOperations, (probably becaused that this key is messed | |
| 718 // up and thus the system restart fails to delete the file), re-add it | |
| 719 if (!File::AreMovesPendingReboot(dummy_file, true)) { | |
| 720 File::MoveAfterReboot(dummy_file, NULL); | |
| 721 } | |
| 722 return false; | |
| 723 } else { | |
| 724 return true; | |
| 725 } | |
| 726 } | |
| 727 return false; | |
| 728 } | |
| 729 | |
| 730 // Mark the system as reboot required | |
| 731 HRESULT System::MarkAsRebootRequired(const TCHAR* base_file) { | |
| 732 // Create a dummy file if needed | |
| 733 CString dummy_file; | |
| 734 RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file)); | |
| 735 if (File::Exists(dummy_file)) { | |
| 736 return S_OK; | |
| 737 } | |
| 738 | |
| 739 File file; | |
| 740 RET_IF_FAILED(file.Open(dummy_file, true, false)); | |
| 741 RET_IF_FAILED(file.Close()); | |
| 742 | |
| 743 // Hide it | |
| 744 DWORD file_attr = ::GetFileAttributes(dummy_file); | |
| 745 if (file_attr == INVALID_FILE_ATTRIBUTES || | |
| 746 !::SetFileAttributes(dummy_file, file_attr | FILE_ATTRIBUTE_HIDDEN)) { | |
| 747 return HRESULTFromLastError(); | |
| 748 } | |
| 749 | |
| 750 // Mark it as being deleted after reboot | |
| 751 return File::MoveAfterReboot(dummy_file, NULL); | |
| 752 } | |
| 753 | |
| 754 // Unmark the system as reboot required | |
| 755 HRESULT System::UnmarkAsRebootRequired(const TCHAR* base_file) { | |
| 756 CString dummy_file; | |
| 757 RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file)); | |
| 758 | |
| 759 return File::RemoveFromMovesPendingReboot(dummy_file, false); | |
| 760 } | |
| 761 | |
| 762 // Restart the computer | |
| 763 HRESULT System::RestartComputer() { | |
| 764 RET_IF_FAILED(AdjustPrivilege(SE_SHUTDOWN_NAME, true)); | |
| 765 | |
| 766 if (!::ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION | | |
| 767 SHTDN_REASON_MINOR_INSTALLATION | | |
| 768 SHTDN_REASON_FLAG_PLANNED)) { | |
| 769 HRESULT hr = HRESULTFromLastError(); | |
| 770 UTIL_LOG(LEVEL_ERROR, (_T("[System::RestartComputer - failed to") | |
| 771 _T(" ExitWindowsEx][0x%x]"), hr)); | |
| 772 return hr; | |
| 773 } | |
| 774 | |
| 775 return S_OK; | |
| 776 } | |
| 777 | |
| 778 // The implementation works on all Windows versions. On NT and XP the screen | |
| 779 // saver is actually stored in registry at | |
| 780 // HKEY_CURRENT_USER\Control Panel\Desktop\SCRNSAVE.EXE but the | |
| 781 // GetPrivateProfileString call is automatically mapped to the registry | |
| 782 HRESULT System::GetCurrentScreenSaver(CString* fileName) { | |
| 783 if (!fileName) return E_POINTER; | |
| 784 | |
| 785 DWORD nChars = ::GetPrivateProfileString(_T("boot"), | |
| 786 _T("SCRNSAVE.EXE"), | |
| 787 _T(""), | |
| 788 fileName->GetBuffer(MAX_PATH), | |
| 789 MAX_PATH, | |
| 790 _T("system.ini")); | |
| 791 fileName->ReleaseBufferSetLength(nChars); | |
| 792 | |
| 793 return S_OK; | |
| 794 } | |
| 795 | |
| 796 HRESULT System::CoCreateInstanceAsAdmin(HWND hwnd, | |
| 797 REFCLSID rclsid, | |
| 798 REFIID riid, | |
| 799 void** ppv) { | |
| 800 UTIL_LOG(L6, (_T("[CoCreateInstanceAsAdmin][%d][%s][%s]"), | |
| 801 hwnd, GuidToString(rclsid), GuidToString(riid))); | |
| 802 | |
| 803 if (vista_util::IsUserAdmin()) { | |
| 804 return ::CoCreateInstance(rclsid, NULL, CLSCTX_LOCAL_SERVER, riid, ppv); | |
| 805 } | |
| 806 | |
| 807 if (!SystemInfo::IsRunningOnVistaOrLater()) { | |
| 808 return E_ACCESSDENIED; | |
| 809 } | |
| 810 | |
| 811 CString moniker_name(_T("Elevation:Administrator!new:")); | |
| 812 moniker_name += GuidToString(rclsid); | |
| 813 BIND_OPTS3 bo; | |
| 814 SetZero(bo); | |
| 815 bo.cbStruct = sizeof(bo); | |
| 816 bo.hwnd = hwnd; | |
| 817 bo.dwClassContext = CLSCTX_LOCAL_SERVER; | |
| 818 | |
| 819 return ::CoGetObject(moniker_name, &bo, riid, ppv); | |
| 820 } | |
| 821 | |
| 822 HRESULT System::IsPrivilegeEnabled(const TCHAR* privilege, bool* present) { | |
| 823 ASSERT1(privilege); | |
| 824 ASSERT1(present); | |
| 825 | |
| 826 *present = false; | |
| 827 | |
| 828 scoped_handle token; | |
| 829 if (!::OpenProcessToken(::GetCurrentProcess(), | |
| 830 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, | |
| 831 address(token))) { | |
| 832 HRESULT hr = HRESULTFromLastError(); | |
| 833 UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to ") | |
| 834 _T("OpenProcessToken][0x%x]"), hr)); | |
| 835 return hr; | |
| 836 } | |
| 837 | |
| 838 LUID luid = {0}; | |
| 839 if (!::LookupPrivilegeValue(NULL, privilege, &luid)) { | |
| 840 HRESULT hr = HRESULTFromLastError(); | |
| 841 UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to") | |
| 842 _T("LookupPrivilegeValue][0x%x]"), hr)); | |
| 843 return hr; | |
| 844 } | |
| 845 | |
| 846 PRIVILEGE_SET required_privilege = {0}; | |
| 847 required_privilege.PrivilegeCount = 1; | |
| 848 required_privilege.Control = PRIVILEGE_SET_ALL_NECESSARY; | |
| 849 required_privilege.Privilege[0].Luid = luid; | |
| 850 | |
| 851 BOOL result = FALSE; | |
| 852 if (!::PrivilegeCheck(get(token), &required_privilege, &result)) { | |
| 853 HRESULT hr = HRESULTFromLastError(); | |
| 854 UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to") | |
| 855 _T("PrivilegeCheck][0x%x]"), hr)); | |
| 856 return hr; | |
| 857 } | |
| 858 | |
| 859 if (required_privilege.Privilege[0].Attributes & | |
| 860 SE_PRIVILEGE_USED_FOR_ACCESS) { | |
| 861 *present = true; | |
| 862 } | |
| 863 | |
| 864 return S_OK; | |
| 865 } | |
| 866 | |
| 867 // Attempts to adjust current process privileges. | |
| 868 // Only process running with administrator privileges will succeed. | |
| 869 HRESULT System::AdjustPrivilege(const TCHAR* privilege, bool enable) { | |
| 870 ASSERT1(privilege); | |
| 871 | |
| 872 scoped_handle token; | |
| 873 if (!::OpenProcessToken(::GetCurrentProcess(), | |
| 874 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, | |
| 875 address(token))) { | |
| 876 HRESULT hr = HRESULTFromLastError(); | |
| 877 UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ") | |
| 878 _T("OpenProcessToken][0x%x]"), hr)); | |
| 879 return hr; | |
| 880 } | |
| 881 | |
| 882 LUID luid = {0}; | |
| 883 if (!::LookupPrivilegeValue(NULL, privilege, &luid)) { | |
| 884 HRESULT hr = HRESULTFromLastError(); | |
| 885 UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to") | |
| 886 _T("LookupPrivilegeValue][0x%x]"), hr)); | |
| 887 return hr; | |
| 888 } | |
| 889 | |
| 890 TOKEN_PRIVILEGES privs; | |
| 891 privs.PrivilegeCount = 1; | |
| 892 privs.Privileges[0].Luid = luid; | |
| 893 privs.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; | |
| 894 | |
| 895 if (!::AdjustTokenPrivileges(get(token), FALSE, &privs, 0, NULL, 0)) { | |
| 896 HRESULT hr = HRESULTFromLastError(); | |
| 897 UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ") | |
| 898 _T("AdjustTokenPrivileges][0x%x]"), hr)); | |
| 899 return hr; | |
| 900 } | |
| 901 | |
| 902 return S_OK; | |
| 903 } | |
| 904 | |
| 905 DWORD System::WTSGetActiveConsoleSessionId() { | |
| 906 typedef DWORD (* Fun)(); | |
| 907 | |
| 908 HINSTANCE hInst = ::GetModuleHandle(_T("kernel32.dll")); | |
| 909 ASSERT1(hInst); | |
| 910 Fun pfn = reinterpret_cast<Fun>(::GetProcAddress( | |
| 911 hInst, | |
| 912 "WTSGetActiveConsoleSessionId")); | |
| 913 return !pfn ? kInvalidSessionId : (*pfn)(); | |
| 914 } | |
| 915 | |
| 916 // Get the session the current process is running under | |
| 917 DWORD System::GetCurrentSessionId() { | |
| 918 DWORD session_id = kInvalidSessionId; | |
| 919 DWORD* session_id_ptr = NULL; | |
| 920 DWORD bytes_returned = 0; | |
| 921 | |
| 922 if (::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, | |
| 923 WTS_CURRENT_SESSION, | |
| 924 WTSSessionId, | |
| 925 reinterpret_cast<LPTSTR*>(&session_id_ptr), | |
| 926 &bytes_returned)) { | |
| 927 ASSERT1(bytes_returned == sizeof(*session_id_ptr)); | |
| 928 session_id = *session_id_ptr; | |
| 929 ::WTSFreeMemory(session_id_ptr); | |
| 930 UTIL_LOG(L6, (_T("[System::GetCurrentSessionId]") | |
| 931 _T("[session_id from ::WTSQuerySessionInformation][%d]"), | |
| 932 session_id)); | |
| 933 return session_id; | |
| 934 } | |
| 935 | |
| 936 // ::WTSQuerySessionInformation can fail if we are not running | |
| 937 // in a Terminal Services scenario, in which case, we use | |
| 938 // ::ProcessIdToSessionId() | |
| 939 if (::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) { | |
| 940 UTIL_LOG(L6, (_T("[System::GetCurrentSessionId]") | |
| 941 _T("[session_id from ::ProcessIdToSessionId][%d]"), | |
| 942 session_id)); | |
| 943 return session_id; | |
| 944 } | |
| 945 | |
| 946 UTIL_LOG(LEVEL_ERROR, | |
| 947 (_T("[System::GetCurrentSessionId - both") | |
| 948 _T("::WTSQuerySessionInformation and ") | |
| 949 _T("::ProcessIdToSessionId failed][0x%x]"), | |
| 950 ::GetLastError())); | |
| 951 | |
| 952 return kInvalidSessionId; | |
| 953 } | |
| 954 | |
| 955 // Get the best guess as to the currently active session, or kInvalidSessionId | |
| 956 // if there is no active session. | |
| 957 DWORD System::GetActiveSessionId() { | |
| 958 // WTSGetActiveConsoleSessionId retrieves the Terminal Services session | |
| 959 // currently attached to the physical console. | |
| 960 DWORD active_session_id = WTSGetActiveConsoleSessionId(); | |
| 961 | |
| 962 if (IsSessionActive(active_session_id)) { | |
| 963 UTIL_LOG(L6, (_T("[System::GetActiveSessionId]") | |
| 964 _T("[Active session id from ::WTSGetActiveConsoleSessionId]") | |
| 965 _T("[%d]"), active_session_id)); | |
| 966 | |
| 967 return active_session_id; | |
| 968 } | |
| 969 | |
| 970 // WTSGetActiveConsoleSessionId works for FUS, but it does not work for TS | |
| 971 // servers where the current active session is always the console. We then use | |
| 972 // a different method as below. We get all the sessions that are present on | |
| 973 // the system, to see if we can find an active session. | |
| 974 active_session_id = kInvalidSessionId; | |
| 975 WTS_SESSION_INFO* session_info = NULL; | |
| 976 DWORD num_sessions = 0; | |
| 977 if (::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, | |
| 978 &session_info, &num_sessions)) { | |
| 979 // Pick the first active session we can find | |
| 980 for (DWORD i = 0 ; i < num_sessions; ++i) { | |
| 981 if (session_info[i].State == WTSActive) { | |
| 982 // There is a user logged on to the WinStation associated with the | |
| 983 // session. | |
| 984 active_session_id = session_info[i].SessionId; | |
| 985 break; | |
| 986 } | |
| 987 } | |
| 988 | |
| 989 ::WTSFreeMemory(session_info); | |
| 990 UTIL_LOG(L6, (_T("[System::GetActiveSessionId]") | |
| 991 _T("[Active session id from ::WTSEnumerateSessions][0x%x]"), | |
| 992 active_session_id)); | |
| 993 | |
| 994 return active_session_id; | |
| 995 } | |
| 996 | |
| 997 UTIL_LOG(LEVEL_ERROR, | |
| 998 (_T("[System::GetActiveSessionId - ") | |
| 999 _T("Both ::WTSGetActiveConsoleSessionId and ::WTSEnumerateSessions ") | |
| 1000 _T("failed][0x%x]"), | |
| 1001 ::GetLastError())); | |
| 1002 | |
| 1003 return kInvalidSessionId; | |
| 1004 } | |
| 1005 | |
| 1006 // Is there a user logged on and active in the specified session? | |
| 1007 bool System::IsSessionActive(DWORD session_id) { | |
| 1008 if (kInvalidSessionId == session_id) { | |
| 1009 return false; | |
| 1010 } | |
| 1011 | |
| 1012 WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected; | |
| 1013 WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL; | |
| 1014 DWORD bytes_returned = 0; | |
| 1015 if (::WTSQuerySessionInformation( | |
| 1016 WTS_CURRENT_SERVER_HANDLE, | |
| 1017 session_id, | |
| 1018 WTSConnectState, | |
| 1019 reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state), | |
| 1020 &bytes_returned)) { | |
| 1021 ASSERT1(bytes_returned == sizeof(*ptr_wts_connect_state)); | |
| 1022 wts_connect_state = *ptr_wts_connect_state; | |
| 1023 ::WTSFreeMemory(ptr_wts_connect_state); | |
| 1024 | |
| 1025 UTIL_LOG(L6, (_T("[System::IsSessionActive]") | |
| 1026 _T("[wts_connect_state %d]"), wts_connect_state)); | |
| 1027 return WTSActive == wts_connect_state; | |
| 1028 } | |
| 1029 | |
| 1030 UTIL_LOG(LE, (_T("[WTSQuerySessionInformation failed][0x%x]"), | |
| 1031 ::GetLastError())); | |
| 1032 return false; | |
| 1033 } | |
| 1034 | |
| 1035 // Is the current process running under WinSta0 | |
| 1036 bool System::IsCurrentProcessInteractive() { | |
| 1037 // Use a non-scoped handle, since a handle retrieved via | |
| 1038 // ::GetProcessWindowStation() should not be closed. | |
| 1039 HWINSTA handle_window_station(::GetProcessWindowStation()); | |
| 1040 DWORD len = 0; | |
| 1041 CString str_window_station; | |
| 1042 | |
| 1043 if (!handle_window_station || handle_window_station == INVALID_HANDLE_VALUE) { | |
| 1044 UTIL_LOG(LEVEL_ERROR, | |
| 1045 (_T("[System::IsCurrentProcessInteractive - ") | |
| 1046 _T("::GetProcessWindowStation() failed (%d)]"), | |
| 1047 ::GetLastError())); | |
| 1048 return false; | |
| 1049 } | |
| 1050 | |
| 1051 if (!::GetUserObjectInformation(handle_window_station, | |
| 1052 UOI_NAME, | |
| 1053 CStrBuf(str_window_station, MAX_PATH), | |
| 1054 MAX_PATH, | |
| 1055 &len)) { | |
| 1056 UTIL_LOG(LEVEL_ERROR, | |
| 1057 (_T("[System::IsCurrentProcessInteractive - ") | |
| 1058 _T("::GetUserObjectInfoformation(hWinSta) failed (%d)]"), | |
| 1059 ::GetLastError())); | |
| 1060 return false; | |
| 1061 } | |
| 1062 | |
| 1063 UTIL_LOG(L6, (_T("[System::IsCurrentProcessInteractive]") | |
| 1064 _T("[WindowStation name][%s]"), | |
| 1065 str_window_station)); | |
| 1066 return (str_window_station == _T("WinSta0")); | |
| 1067 } | |
| 1068 | |
| 1069 // is the current process running under WinSta0 for the currently active session | |
| 1070 bool System::IsCurrentProcessActiveAndInteractive() { | |
| 1071 return IsSessionActive(GetCurrentSessionId()) && | |
| 1072 IsCurrentProcessInteractive(); | |
| 1073 } | |
| 1074 | |
| 1075 bool System::IsRunningOnBatteries() { | |
| 1076 SYSTEM_POWER_STATUS system_power_status = {0}; | |
| 1077 if (::GetSystemPowerStatus(&system_power_status)) { | |
| 1078 bool has_battery = !(system_power_status.BatteryFlag & 128); | |
| 1079 bool ac_status_offline = system_power_status.ACLineStatus == 0; | |
| 1080 return ac_status_offline && has_battery; | |
| 1081 } | |
| 1082 return false; | |
| 1083 } | |
| 1084 | |
| 1085 } // namespace omaha | |
| 1086 | |
| OLD | NEW |