blob: 41095f8e92b28566fbd8103d99037e44007e20b2 [file] [log] [blame]
// Copyright 2004-2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
#include "omaha/common/system.h"
#include <objidl.h>
#include <psapi.h>
#include <winioctl.h>
#include <wtsapi32.h>
#include "omaha/common/commands.h"
#include "omaha/common/const_config.h"
#include "omaha/common/debug.h"
#include "omaha/common/disk.h"
#include "omaha/common/dynamic_link_kernel32.h"
#include "omaha/common/error.h"
#include "omaha/common/file.h"
#include "omaha/common/logging.h"
#include "omaha/common/module_utils.h"
#include "omaha/common/path.h"
#include "omaha/common/scoped_any.h"
#include "omaha/common/string.h"
#include "omaha/common/system_info.h"
#include "omaha/common/utils.h"
namespace omaha {
// Constant
const TCHAR kNeedRebootHiddenFileSuffix[] = _T(".needreboot");
HRESULT System::WaitForDiskActivity(const uint32 max_delay_milliseconds,
const uint32 sleep_time_ms,
uint32 *time_waited) {
ASSERT(time_waited, (L""));
uint32 sleep_time = sleep_time_ms;
if (sleep_time < 20) { sleep_time = 20; }
else if (sleep_time > 1000) { sleep_time = 1000; }
HRESULT r;
*time_waited = 0;
uint64 writes = 0;
uint64 new_writes = 0;
// get current counters
if (FAILED(r=GetDiskActivityCounters(NULL, &writes, NULL, NULL))) {
return r;
}
// wait until a write - reads may be cached
while (1) {
if (FAILED(r=GetDiskActivityCounters(NULL, &new_writes, NULL, NULL))) {
return r;
}
if (new_writes > writes) { return S_OK; }
if (*time_waited > max_delay_milliseconds) { return E_FAIL; }
SleepEx(sleep_time, TRUE);
*time_waited += sleep_time;
}
}
HRESULT System::GetDiskActivityCounters(uint64* reads,
uint64* writes,
uint64* bytes_read,
uint64* bytes_written) {
if (reads) {
*reads = 0;
}
if (writes) {
*writes = 0;
}
if (bytes_read) {
*bytes_read = 0;
}
if (bytes_written) {
*bytes_written = 0;
}
// Don't want to risk displaying UI errors here
DisableThreadErrorUI disable_error_dialog_box;
// for all drives
for (int drive = 0; ; drive++) {
struct _DISK_PERFORMANCE perf_data;
const int max_device_len = 50;
// check whether we can access this device
CString device_name;
device_name.Format(_T("\\\\.\\PhysicalDrive%d"), drive);
scoped_handle device(::CreateFile(device_name, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL));
if (get(device) == INVALID_HANDLE_VALUE) {
if (!drive) {
UTIL_LOG(LEVEL_ERROR, (_T("[Failed to access drive %i][0x%x]"),
drive,
HRESULTFromLastError()));
}
break;
}
// disk performance counters must be on (diskperf -y on older machines;
// defaults to on on newer windows)
DWORD size = 0;
if (::DeviceIoControl(get(device),
IOCTL_DISK_PERFORMANCE,
NULL,
0,
&perf_data,
sizeof(_DISK_PERFORMANCE),
&size,
NULL)) {
if (reads) {
*reads += perf_data.ReadCount;
}
if (writes) {
*writes += perf_data.WriteCount;
}
if (bytes_read) {
*bytes_read += perf_data.BytesRead.QuadPart;
}
if (bytes_written) {
*bytes_written += perf_data.BytesWritten.QuadPart;
}
} else {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR,
(_T("[System::GetDiskActivityCounters - failed to ")
_T("DeviceIoControl][0x%x]"), hr));
return hr;
}
}
return S_OK;
}
HRESULT System::GetDiskStatistics(const TCHAR* path,
uint64 *free_bytes_current_user,
uint64 *total_bytes_current_user,
uint64 *free_bytes_all_users) {
ASSERT1(path);
ASSERT1(free_bytes_current_user);
ASSERT1(total_bytes_current_user);
ASSERT1(free_bytes_all_users);
ASSERT1(sizeof(LARGE_INTEGER) == sizeof(uint64)); // NOLINT
DisableThreadErrorUI disable_error_dialog_box;
if (!::GetDiskFreeSpaceEx(
path,
reinterpret_cast<PULARGE_INTEGER>(free_bytes_current_user),
reinterpret_cast<PULARGE_INTEGER>(total_bytes_current_user),
reinterpret_cast<PULARGE_INTEGER>(free_bytes_all_users))) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR,
(_T("[Failed to GetDiskFreeSpaceEx][%s][0x%x]"), path, hr));
return hr;
}
return S_OK;
}
HRESULT System::GetProcessMemoryStatistics(uint64 *current_working_set,
uint64 *peak_working_set,
uint64 *min_working_set_size,
uint64 *max_working_set_size) {
HANDLE process_handle = GetCurrentProcess();
HRESULT hr = S_OK;
DWORD min_size(0), max_size(0);
if (GetProcessWorkingSetSize(process_handle, &min_size, &max_size)) {
UTIL_LOG(L2, (_T("working set %lu %lu"), min_size, max_size));
if (min_working_set_size) {
*min_working_set_size = min_size;
}
if (max_working_set_size) {
*max_working_set_size = max_size;
}
} else {
if (min_working_set_size) {
*min_working_set_size = 0;
}
if (max_working_set_size) {
*max_working_set_size = 0;
}
hr = E_FAIL;
}
if (current_working_set) { *current_working_set = 0; }
if (peak_working_set) { *peak_working_set = 0; }
// including this call (w/psapi.lib) adds 24k to the process memory
// according to task manager in one test, memory usage according to task
// manager increased by 4k after calling this
PROCESS_MEMORY_COUNTERS counters = { sizeof(counters), 0 };
if (GetProcessMemoryInfo(process_handle,
&counters,
sizeof(PROCESS_MEMORY_COUNTERS))) {
if (current_working_set) {
*current_working_set = counters.WorkingSetSize;
}
if (peak_working_set) {
*peak_working_set = counters.PeakWorkingSetSize;
}
UTIL_LOG(L2, (_T("current/peak working set %s %s"),
String_Int64ToString(*current_working_set, 10),
String_Int64ToString(*peak_working_set, 10)));
} else {
if (current_working_set) {
*current_working_set = 0;
}
if (peak_working_set) {
*peak_working_set = 0;
}
hr = E_FAIL;
}
return hr;
}
HRESULT System::MaxPhysicalMemoryAvailable(uint64* max_bytes) {
ASSERT1(max_bytes);
*max_bytes = 0;
uint32 memory_load_percentage = 0;
uint64 free_physical_memory = 0;
RET_IF_FAILED(System::GetGlobalMemoryStatistics(&memory_load_percentage,
&free_physical_memory, NULL, NULL, NULL, NULL, NULL));
UTIL_LOG(L4, (_T("mem load %u max physical memory available %s"),
memory_load_percentage,
String_Int64ToString(free_physical_memory, 10)));
*max_bytes = free_physical_memory;
return S_OK;
}
HRESULT System::GetGlobalMemoryStatistics(uint32 *memory_load_percentage,
uint64 *free_physical_memory,
uint64 *total_physical_memory,
uint64 *free_paged_memory,
uint64 *total_paged_memory,
uint64 *process_free_virtual_memory,
uint64 *process_total_virtual_mem) {
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
if (!GlobalMemoryStatusEx(&status)) {
UTIL_LOG(LEVEL_ERROR, (_T("memory status error %u"), GetLastError()));
return E_FAIL;
}
if (memory_load_percentage) { *memory_load_percentage = status.dwMemoryLoad; }
if (free_physical_memory) { *free_physical_memory = status.ullAvailPhys; }
if (total_physical_memory) { *total_physical_memory = status.ullTotalPhys; }
if (free_paged_memory) { *free_paged_memory = status.ullAvailPageFile; }
if (total_paged_memory) { *total_paged_memory = status.ullTotalPageFile; }
if (process_free_virtual_memory) {
*process_free_virtual_memory = status.ullAvailVirtual;
}
if (process_total_virtual_mem) {
*process_total_virtual_mem = status.ullTotalVirtual;
}
// GetPerformanceInfo;
return S_OK;
}
void System::FreeProcessWorkingSet() {
// -1,-1 is a special signal to the OS to temporarily trim the working set
// size to 0. See MSDN for further information.
::SetProcessWorkingSetSize(::GetCurrentProcess(), (SIZE_T)-1, (SIZE_T)-1);
}
HRESULT System::SetThreadPriority(enum Priority priority) {
int pri;
switch (priority) {
case LOW: pri = THREAD_PRIORITY_BELOW_NORMAL; break;
case HIGH: pri = THREAD_PRIORITY_HIGHEST; break;
case NORMAL: pri = THREAD_PRIORITY_NORMAL; break;
case IDLE: pri = THREAD_PRIORITY_IDLE; break;
default: return E_FAIL;
}
if (::SetThreadPriority(GetCurrentThread(), pri)) {
return S_OK;
} else {
return E_FAIL;
}
}
HRESULT System::SetProcessPriority(enum Priority priority) {
DWORD pri = 0;
switch (priority) {
case LOW: pri = BELOW_NORMAL_PRIORITY_CLASS; break;
case HIGH: pri = ABOVE_NORMAL_PRIORITY_CLASS; break;
case NORMAL: pri = NORMAL_PRIORITY_CLASS; break;
case IDLE: return E_INVALIDARG;
default: return E_INVALIDARG;
}
DWORD pid = ::GetCurrentProcessId();
scoped_handle handle(::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid));
if (!valid(handle)) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LE, (_T("[::OpenProcess failed][%u][0x%x]"), pid, hr));
return hr;
}
if (!::SetPriorityClass(get(handle), pri)) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LE, (_T("[::SetPriorityClass failed][%u][0x%x]"), pid, hr));
return hr;
}
return S_OK;
}
// start another process painlessly via ::CreateProcess. Use the
// ShellExecuteProcessXXX variants instead of these methods where possible,
// since ::ShellExecuteEx has better behavior on Windows Vista.
// When using this method, avoid using process_name - see
// http://blogs.msdn.com/oldnewthing/archive/2006/05/15/597984.aspx.
HRESULT System::StartProcess(const TCHAR* process_name,
TCHAR* command_line,
PROCESS_INFORMATION* pi) {
ASSERT1(pi);
ASSERT1(command_line || process_name);
ASSERT(!process_name, (_T("Avoid using process_name. See method comment.")));
STARTUPINFO si = {sizeof(si), 0};
// Feedback cursor is off while the process is starting.
si.dwFlags = STARTF_FORCEOFFFEEDBACK;
UTIL_LOG(L3, (_T("[System::StartProcess][process %s][cmd %s]"),
process_name, command_line));
BOOL success = ::CreateProcess(
process_name, // Module name
command_line, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
pi); // Pointer to PROCESS_INFORMATION structure
if (!success) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR,
(_T("[System::StartProcess][::CreateProcess failed][0x%x]"), hr));
return hr;
}
OPT_LOG(L1, (_T("[Started process][%u]"), pi->dwProcessId));
return S_OK;
}
// start another process painlessly via ::CreateProcess. Use the
// ShellExecuteProcessXXX variants instead of these methods where possible,
// since ::ShellExecuteEx has better behavior on Windows Vista.
HRESULT System::StartProcessWithArgsAndInfo(const TCHAR *process_name,
const TCHAR *cmd_line_arguments,
PROCESS_INFORMATION *pi) {
ASSERT1(process_name && cmd_line_arguments && pi);
CString command_line(process_name);
EnclosePath(&command_line);
command_line.AppendChar(_T(' '));
command_line.Append(cmd_line_arguments);
return System::StartProcess(NULL, command_line.GetBuffer(), pi);
}
// start another process painlessly via ::CreateProcess. Use the
// ShellExecuteProcessXXX variants instead of these methods where possible,
// since ::ShellExecuteEx has better behavior on Windows Vista.
HRESULT System::StartProcessWithArgs(const TCHAR *process_name,
const TCHAR *cmd_line_arguments) {
ASSERT1(process_name && cmd_line_arguments);
PROCESS_INFORMATION pi = {0};
HRESULT hr = System::StartProcessWithArgsAndInfo(process_name,
cmd_line_arguments,
&pi);
if (SUCCEEDED(hr)) {
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
}
return hr;
}
// start another process painlessly via ::ShellExecuteEx. Use this method
// instead of the StartProcessXXX methods that use ::CreateProcess where
// possible, since ::ShellExecuteEx has better behavior on Windows Vista.
//
// ShellExecuteExEnsureParent displays the PID of the started process if it is
// returned. It is only returned if the mask includes SEE_MASK_NOCLOSEPROCESS.
// Therefore, we always set this flag and pass a handle. If the caller did not
// request the handle, we close it.
HRESULT System::ShellExecuteProcess(const TCHAR* file_name_to_execute,
const TCHAR* command_line_parameters,
HWND hwnd,
HANDLE* process_handle) {
ASSERT1(file_name_to_execute);
UTIL_LOG(L3, (_T("[System::ShellExecuteProcess]")
_T("[file_name_to_execute '%s' command_line_parameters '%s']"),
file_name_to_execute, command_line_parameters));
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
// SEE_MASK_NOZONECHECKS is set below to work around a problem in systems that
// had Internet Explorer 7 Beta installed. See http://b/804674.
// This only works for Windows XP SP1 and later.
sei.fMask = SEE_MASK_NOCLOSEPROCESS | // Set hProcess to process handle.
SEE_MASK_FLAG_NO_UI | // Do not display an error message box.
SEE_MASK_NOZONECHECKS | // Do not perform a zone check.
SEE_MASK_NOASYNC; // Wait to complete before returning.
sei.lpVerb = _T("open");
sei.lpFile = file_name_to_execute;
sei.lpParameters = command_line_parameters;
sei.nShow = SW_SHOWNORMAL;
sei.hwnd = hwnd;
// Use ShellExecuteExEnsureParent to ensure that we always have a parent
// window. We need to use the HWND property to be acknowledged as a foreground
// application on Windows Vista. Otherwise, the elevation prompt will appear
// minimized on the taskbar.
if (!ShellExecuteExEnsureParent(&sei)) {
HRESULT hr(HRESULTFromLastError());
OPT_LOG(LEVEL_ERROR, (_T("[Failed to ::ShellExecuteEx][%s][%s][0x%08x]"),
file_name_to_execute, command_line_parameters, hr));
return hr;
}
if (process_handle) {
*process_handle = sei.hProcess;
} else {
::CloseHandle(sei.hProcess);
}
return S_OK;
}
// start another process painlessly via ::ShellExecuteEx. Use this method
// instead of the StartProcessXXX methods that use ::CreateProcess where
// possible, since ::ShellExecuteEx has better behavior on Windows Vista.
HRESULT System::ShellExecuteCommandLine(const TCHAR* command_line_to_execute,
HWND hwnd,
HANDLE* process_handle) {
ASSERT1(command_line_to_execute);
CString exe;
CString args;
HRESULT hr = CommandParsingSimple::SplitExeAndArgs(command_line_to_execute,
&exe,
&args);
if (SUCCEEDED(hr)) {
hr = System::ShellExecuteProcess(exe, args, hwnd, process_handle);
if (FAILED(hr)) {
UTIL_LOG(LEVEL_ERROR, (_T("[System::ShellExecuteProcess failed]")
_T("[%s][%s][0x%08x]"), exe, args, hr));
}
}
return hr;
}
// returns the number of ms the system has had no user input
int System::GetUserIdleTime() {
LASTINPUTINFO last_input_info;
last_input_info.cbSize = sizeof(LASTINPUTINFO);
// get time in windows ticks since system start of last activity
BOOL b = GetLastInputInfo(&last_input_info);
if (b == TRUE) {
return (GetTickCount()-last_input_info.dwTime); // compute idle time
}
return 0;
}
bool System::IsUserIdle() {
// Only notify when the user has been idle less than this time
static int user_idle_threshold_ms = kUserIdleThresholdMs;
bool is_user_idle = (GetUserIdleTime() > user_idle_threshold_ms);
UTIL_LOG(L2, (_T("System::IsUserIdle() %s; user_idle_threshold_ms = %d"),
is_user_idle ? _T("TRUE") : _T("FALSE"),
user_idle_threshold_ms));
return is_user_idle;
}
bool System::IsUserBusy() {
// The user is busy typing or interacting with another application
// if the user is below the minimum threshold:
static int user_idle_min_threshold_ms = kUserIdleMinThresholdMs;
// The user is probably not paying attention
// if the user is above the maximum threshold:
static int user_idle_max_threshold_ms = kUserIdleMaxThresholdMs;
int user_idle_time = GetUserIdleTime();
bool is_user_busy = user_idle_time < user_idle_min_threshold_ms ||
user_idle_time > user_idle_max_threshold_ms;
UTIL_LOG(L2, (_T("[System::IsUserBusy() %s][user_idle_time = %d]")
_T("[user_idle_min_threshold_ms = %d]")
_T("[user_idle_max_threshold_ms = %d]"),
is_user_busy? _T("TRUE") : _T("FALSE"),
user_idle_time,
user_idle_min_threshold_ms,
user_idle_max_threshold_ms));
return is_user_busy;
}
bool System::IsScreensaverRunning() {
// NT 4.0 and below require testing OpenDesktop("screen-saver")
// We require W2K or better so we have an easier way
DWORD result = 0;
::SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &result, 0);
bool is_screensaver_running = (result != FALSE);
UTIL_LOG(L2, (_T("System::IsScreensaverRunning() %s"),
is_screensaver_running? _T("TRUE") : _T("FALSE")));
return is_screensaver_running;
}
bool System::IsWorkstationLocked() {
bool is_workstation_locked = true;
HDESK inputdesk = ::OpenInputDesktop(0, 0, GENERIC_READ);
if (NULL != inputdesk) {
TCHAR name[256];
DWORD needed = arraysize(name);
BOOL ok = ::GetUserObjectInformation(inputdesk,
UOI_NAME,
name,
sizeof(name),
&needed);
::CloseDesktop(inputdesk);
if (ok) {
is_workstation_locked = (0 != lstrcmpi(name, NOTRANSL(_T("default"))));
}
}
UTIL_LOG(L2, (_T("System::IsWorkstationLocked() %s"),
is_workstation_locked? _T("TRUE") : _T("FALSE")));
return is_workstation_locked;
}
bool System::IsUserAway() {
return IsScreensaverRunning() || IsWorkstationLocked();
}
uint32 System::GetProcessHandleCount() {
typedef LONG (CALLBACK *Fun)(HANDLE, int32, PVOID, ULONG, PULONG);
// This new version of getting the number of open handles works on win2k.
HMODULE h = GetModuleHandle(_T("ntdll.dll"));
Fun NtQueryInformationProcess =
reinterpret_cast<Fun>(::GetProcAddress(h, "NtQueryInformationProcess"));
if (!NtQueryInformationProcess) {
UTIL_LOG(LEVEL_ERROR, (_T("[NtQueryInformationProcess failed][0x%x]"),
HRESULTFromLastError()));
return 0;
}
DWORD count = 0;
VERIFY(NtQueryInformationProcess(GetCurrentProcess(),
kProcessHandleCount,
&count,
sizeof(count),
NULL) >= 0, (L""));
return count;
}
uint32 System::GetProcessHandleCountOld() {
typedef BOOL (CALLBACK * Fun)(HANDLE, PDWORD);
// GetProcessHandleCount not available on win2k
HMODULE handle = GetModuleHandle(_T("kernel32"));
Fun f = reinterpret_cast<Fun>(GetProcAddress(handle,
"GetProcessHandleCount"));
if (!f) return 0;
DWORD count = 0;
VERIFY((*f)(GetCurrentProcess(), &count), (L""));
return count;
// DWORD GetGuiResources (HANDLE hProcess, DWORD uiFlags);
// Parameters, hProcess
// [in] Handle to the process. The handle must have the
// PROCESS_QUERY_INFORMATION access right. For more information, see Process
// Security and Access Rights.
// uiFlags
// [in] GUI object type. This parameter can be one of the following values.
// Value Meaning
// GR_GDIOBJECTS Return the count of GDI objects.
// GR_USEROBJECTS Return the count of USER objects.
}
void System::GetGuiObjectCount(uint32 *gdi, uint32 *user) {
if (gdi) {
*gdi = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
}
if (user) {
*user = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS);
}
}
HRESULT System::GetRebootCheckDummyFileName(const TCHAR* base_file,
CString* dummy_file) {
ASSERT1(dummy_file);
if (base_file && *base_file) {
ASSERT1(File::Exists(base_file));
dummy_file->SetString(base_file);
} else {
RET_IF_FAILED(GetModuleFileName(NULL, dummy_file));
}
dummy_file->Append(_T(".needreboot"));
return S_OK;
}
// Is the system being rebooted?
bool System::IsRebooted(const TCHAR* base_file) {
CString dummy_file;
if (SUCCEEDED(GetRebootCheckDummyFileName(base_file, &dummy_file))) {
if (File::Exists(dummy_file)) {
// If the file exists but it is not found in the
// PendingFileRenameOperations, (probably becaused that this key is messed
// up and thus the system restart fails to delete the file), re-add it
if (!File::AreMovesPendingReboot(dummy_file, true)) {
File::MoveAfterReboot(dummy_file, NULL);
}
return false;
} else {
return true;
}
}
return false;
}
// Mark the system as reboot required
HRESULT System::MarkAsRebootRequired(const TCHAR* base_file) {
// Create a dummy file if needed
CString dummy_file;
RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file));
if (File::Exists(dummy_file)) {
return S_OK;
}
File file;
RET_IF_FAILED(file.Open(dummy_file, true, false));
RET_IF_FAILED(file.Close());
// Hide it
DWORD file_attr = ::GetFileAttributes(dummy_file);
if (file_attr == INVALID_FILE_ATTRIBUTES ||
!::SetFileAttributes(dummy_file, file_attr | FILE_ATTRIBUTE_HIDDEN)) {
return HRESULTFromLastError();
}
// Mark it as being deleted after reboot
return File::MoveAfterReboot(dummy_file, NULL);
}
// Unmark the system as reboot required
HRESULT System::UnmarkAsRebootRequired(const TCHAR* base_file) {
CString dummy_file;
RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file));
return File::RemoveFromMovesPendingReboot(dummy_file, false);
}
// Restart the computer
HRESULT System::RestartComputer() {
RET_IF_FAILED(AdjustPrivilege(SE_SHUTDOWN_NAME, true));
if (!::ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION |
SHTDN_REASON_MINOR_INSTALLATION |
SHTDN_REASON_FLAG_PLANNED)) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR, (_T("[System::RestartComputer - failed to")
_T(" ExitWindowsEx][0x%x]"), hr));
return hr;
}
return S_OK;
}
// The implementation works on all Windows versions. On NT and XP the screen
// saver is actually stored in registry at
// HKEY_CURRENT_USER\Control Panel\Desktop\SCRNSAVE.EXE but the
// GetPrivateProfileString call is automatically mapped to the registry
HRESULT System::GetCurrentScreenSaver(CString* fileName) {
if (!fileName) return E_POINTER;
DWORD nChars = ::GetPrivateProfileString(_T("boot"),
_T("SCRNSAVE.EXE"),
_T(""),
fileName->GetBuffer(MAX_PATH),
MAX_PATH,
_T("system.ini"));
fileName->ReleaseBufferSetLength(nChars);
return S_OK;
}
// Create an instance of a COM Local Server class using either plain vanilla
// CoCreateInstance, or using the Elevation moniker depending on the operating
// system
HRESULT System::CoCreateInstanceAsAdmin(HWND hwnd,
REFCLSID rclsid,
REFIID riid,
void** ppv) {
if (SystemInfo::IsRunningOnVistaOrLater()) {
// Use the Elevation Moniker to create the Install Manager in Windows Vista.
// If the UI is running in medium integrity, this will result in a
// elevation prompt
scoped_window hwnd_parent;
if (!hwnd) {
reset(hwnd_parent, CreateForegroundParentWindowForUAC());
if (!hwnd_parent) {
return HRESULTFromLastError();
}
// Use the newly created dummy window as the hwnd
hwnd = get(hwnd_parent);
}
CString moniker_name(_T("Elevation:Administrator!new:"));
moniker_name += GuidToString(rclsid);
BIND_OPTS3 bo;
SetZero(bo);
bo.cbStruct = sizeof(bo);
bo.hwnd = hwnd;
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
return ::CoGetObject(moniker_name, &bo, riid, ppv);
} else {
// Use plain-vanilla ::CoCreateInstance()
return ::CoCreateInstance(rclsid, NULL, CLSCTX_LOCAL_SERVER, riid, ppv);
}
}
HRESULT System::IsPrivilegeEnabled(const TCHAR* privilege, bool* present) {
ASSERT1(privilege);
ASSERT1(present);
*present = false;
scoped_handle token;
if (!::OpenProcessToken(::GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
address(token))) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to ")
_T("OpenProcessToken][0x%x]"), hr));
return hr;
}
LUID luid = {0};
if (!::LookupPrivilegeValue(NULL, privilege, &luid)) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to")
_T("LookupPrivilegeValue][0x%x]"), hr));
return hr;
}
PRIVILEGE_SET required_privilege = {0};
required_privilege.PrivilegeCount = 1;
required_privilege.Control = PRIVILEGE_SET_ALL_NECESSARY;
required_privilege.Privilege[0].Luid = luid;
BOOL result = FALSE;
if (!::PrivilegeCheck(get(token), &required_privilege, &result)) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to")
_T("PrivilegeCheck][0x%x]"), hr));
return hr;
}
if (required_privilege.Privilege[0].Attributes &
SE_PRIVILEGE_USED_FOR_ACCESS) {
*present = true;
}
return S_OK;
}
// Attempts to adjust current process privileges.
// Only process running with administrator privileges will succeed.
HRESULT System::AdjustPrivilege(const TCHAR* privilege, bool enable) {
ASSERT1(privilege);
scoped_handle token;
if (!::OpenProcessToken(::GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
address(token))) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ")
_T("OpenProcessToken][0x%x]"), hr));
return hr;
}
LUID luid = {0};
if (!::LookupPrivilegeValue(NULL, privilege, &luid)) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to")
_T("LookupPrivilegeValue][0x%x]"), hr));
return hr;
}
TOKEN_PRIVILEGES privs;
privs.PrivilegeCount = 1;
privs.Privileges[0].Luid = luid;
privs.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
if (!::AdjustTokenPrivileges(get(token), FALSE, &privs, 0, NULL, 0)) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ")
_T("AdjustTokenPrivileges][0x%x]"), hr));
return hr;
}
return S_OK;
}
DWORD System::WTSGetActiveConsoleSessionId() {
typedef DWORD (* Fun)();
HINSTANCE hInst = ::GetModuleHandle(_T("kernel32.dll"));
ASSERT1(hInst);
Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(
hInst,
"WTSGetActiveConsoleSessionId"));
return !pfn ? kInvalidSessionId : (*pfn)();
}
// Get the session the current process is running under
DWORD System::GetCurrentSessionId() {
DWORD session_id = kInvalidSessionId;
DWORD* session_id_ptr = NULL;
DWORD bytes_returned = 0;
if (::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
WTS_CURRENT_SESSION,
WTSSessionId,
reinterpret_cast<LPTSTR*>(&session_id_ptr),
&bytes_returned)) {
ASSERT1(bytes_returned == sizeof(*session_id_ptr));
session_id = *session_id_ptr;
::WTSFreeMemory(session_id_ptr);
UTIL_LOG(L6, (_T("[System::GetCurrentSessionId]")
_T("[session_id from ::WTSQuerySessionInformation][%d]"),
session_id));
return session_id;
}
// ::WTSQuerySessionInformation can fail if we are not running
// in a Terminal Services scenario, in which case, we use
// ::ProcessIdToSessionId()
if (::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) {
UTIL_LOG(L6, (_T("[System::GetCurrentSessionId]")
_T("[session_id from ::ProcessIdToSessionId][%d]"),
session_id));
return session_id;
}
UTIL_LOG(LEVEL_ERROR,
(_T("[System::GetCurrentSessionId - both")
_T("::WTSQuerySessionInformation and ")
_T("::ProcessIdToSessionId failed][0x%x]"),
::GetLastError()));
return kInvalidSessionId;
}
// Get the best guess as to the currently active session, or kInvalidSessionId
// if there is no active session.
DWORD System::GetActiveSessionId() {
// WTSGetActiveConsoleSessionId retrieves the Terminal Services session
// currently attached to the physical console.
DWORD active_session_id = WTSGetActiveConsoleSessionId();
if (IsSessionActive(active_session_id)) {
UTIL_LOG(L6, (_T("[System::GetActiveSessionId]")
_T("[Active session id from ::WTSGetActiveConsoleSessionId]")
_T("[%d]"), active_session_id));
return active_session_id;
}
// WTSGetActiveConsoleSessionId works for FUS, but it does not work for TS
// servers where the current active session is always the console. We then use
// a different method as below. We get all the sessions that are present on
// the system, to see if we can find an active session.
active_session_id = kInvalidSessionId;
WTS_SESSION_INFO* session_info = NULL;
DWORD num_sessions = 0;
if (::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1,
&session_info, &num_sessions)) {
// Pick the first active session we can find
for (DWORD i = 0 ; i < num_sessions; ++i) {
if (session_info[i].State == WTSActive) {
// There is a user logged on to the WinStation associated with the
// session.
active_session_id = session_info[i].SessionId;
break;
}
}
::WTSFreeMemory(session_info);
UTIL_LOG(L6, (_T("[System::GetActiveSessionId]")
_T("[Active session id from ::WTSEnumerateSessions][0x%x]"),
active_session_id));
return active_session_id;
}
UTIL_LOG(LEVEL_ERROR,
(_T("[System::GetActiveSessionId - ")
_T("Both ::WTSGetActiveConsoleSessionId and ::WTSEnumerateSessions ")
_T("failed][0x%x]"),
::GetLastError()));
return kInvalidSessionId;
}
// Is there a user logged on and active in the specified session?
bool System::IsSessionActive(DWORD session_id) {
if (kInvalidSessionId == session_id) {
return false;
}
WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;
DWORD bytes_returned = 0;
if (::WTSQuerySessionInformation(
WTS_CURRENT_SERVER_HANDLE,
session_id,
WTSConnectState,
reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
&bytes_returned)) {
ASSERT1(bytes_returned == sizeof(*ptr_wts_connect_state));
wts_connect_state = *ptr_wts_connect_state;
::WTSFreeMemory(ptr_wts_connect_state);
UTIL_LOG(L6, (_T("[System::IsSessionActive]")
_T("[wts_connect_state %d]"), wts_connect_state));
return WTSActive == wts_connect_state;
}
UTIL_LOG(LE, (_T("[WTSQuerySessionInformation failed][0x%x]"),
::GetLastError()));
return false;
}
// Is the current process running under WinSta0
bool System::IsCurrentProcessInteractive() {
// Use a non-scoped handle, since a handle retrieved via
// ::GetProcessWindowStation() should not be closed.
HWINSTA handle_window_station(::GetProcessWindowStation());
DWORD len = 0;
CString str_window_station;
if (!handle_window_station || handle_window_station == INVALID_HANDLE_VALUE) {
UTIL_LOG(LEVEL_ERROR,
(_T("[System::IsCurrentProcessInteractive - ")
_T("::GetProcessWindowStation() failed (%d)]"),
::GetLastError()));
return false;
}
if (!::GetUserObjectInformation(handle_window_station,
UOI_NAME,
CStrBuf(str_window_station, MAX_PATH),
MAX_PATH,
&len)) {
UTIL_LOG(LEVEL_ERROR,
(_T("[System::IsCurrentProcessInteractive - ")
_T("::GetUserObjectInfoformation(hWinSta) failed (%d)]"),
::GetLastError()));
return false;
}
UTIL_LOG(L6, (_T("[System::IsCurrentProcessInteractive]")
_T("[WindowStation name][%s]"),
str_window_station));
return (str_window_station == _T("WinSta0"));
}
// is the current process running under WinSta0 for the currently active session
bool System::IsCurrentProcessActiveAndInteractive() {
return IsSessionActive(GetCurrentSessionId()) &&
IsCurrentProcessInteractive();
}
bool System::IsRunningOnBatteries() {
SYSTEM_POWER_STATUS system_power_status = {0};
if (::GetSystemPowerStatus(&system_power_status)) {
bool has_battery = !(system_power_status.BatteryFlag & 128);
bool ac_status_offline = system_power_status.ACLineStatus == 0;
return ac_status_offline && has_battery;
}
return false;
}
} // namespace omaha