blob: d9efb6eb47235ce9c590b36044d767769237b010 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/win/win_util.h"
#include <aclapi.h>
#include <cfgmgr32.h>
#include <initguid.h>
#include <powrprof.h>
#include <shobjidl.h> // Must be before propkey.
#include <inspectable.h>
#include <mdmregistration.h>
#include <objbase.h>
#include <propkey.h>
#include <propvarutil.h>
#include <psapi.h>
#include <roapi.h>
#include <sddl.h>
#include <setupapi.h>
#include <shellscalingapi.h>
#include <shlwapi.h>
#include <signal.h>
#include <stddef.h>
#include <stdlib.h>
#include <tchar.h> // Must be before tpcshrd.h or for any use of _T macro
#include <tpcshrd.h>
#include <uiviewsettingsinterop.h>
#include <windows.ui.viewmanagement.h>
#include <winstring.h>
#include <wrl/client.h>
#include <wrl/wrappers/corewrappers.h>
#include <memory>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/core_winrt_util.h"
#include "base/win/registry.h"
#include "base/win/scoped_co_mem.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_hstring.h"
#include "base/win/scoped_propvariant.h"
#include "base/win/win_client_metrics.h"
#include "base/win/windows_version.h"
namespace base {
namespace win {
namespace {
// Sets the value of |property_key| to |property_value| in |property_store|.
bool SetPropVariantValueForPropertyStore(
IPropertyStore* property_store,
const PROPERTYKEY& property_key,
const ScopedPropVariant& property_value) {
DCHECK(property_store);
HRESULT result = property_store->SetValue(property_key, property_value.get());
if (result == S_OK)
result = property_store->Commit();
if (SUCCEEDED(result))
return true;
#if DCHECK_IS_ON()
ScopedCoMem<OLECHAR> guidString;
::StringFromCLSID(property_key.fmtid, &guidString);
if (HRESULT_FACILITY(result) == FACILITY_WIN32)
::SetLastError(HRESULT_CODE(result));
// See third_party/perl/c/i686-w64-mingw32/include/propkey.h for GUID and
// PID definitions.
DPLOG(ERROR) << "Failed to set property with GUID " << guidString << " PID "
<< property_key.pid;
#endif
return false;
}
void __cdecl ForceCrashOnSigAbort(int) {
*((volatile int*)0) = 0x1337;
}
// Returns the current platform role. We use the PowerDeterminePlatformRoleEx
// API for that.
POWER_PLATFORM_ROLE GetPlatformRole() {
return PowerDeterminePlatformRoleEx(POWER_PLATFORM_ROLE_V2);
}
// Method used for Windows 8.1 and later.
// Since we support versions earlier than 8.1, we must dynamically load this
// function from user32.dll, so it won't fail to load in runtime. For earlier
// Windows versions GetProcAddress will return null and report failure so that
// callers can fall back on the deprecated SetProcessDPIAware.
bool SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value) {
decltype(&::SetProcessDpiAwareness) set_process_dpi_awareness_func =
reinterpret_cast<decltype(&::SetProcessDpiAwareness)>(GetProcAddress(
GetModuleHandle(L"user32.dll"), "SetProcessDpiAwarenessInternal"));
if (set_process_dpi_awareness_func) {
HRESULT hr = set_process_dpi_awareness_func(value);
if (SUCCEEDED(hr))
return true;
DLOG_IF(ERROR, hr == E_ACCESSDENIED)
<< "Access denied error from SetProcessDpiAwarenessInternal. Function "
"called twice, or manifest was used.";
NOTREACHED()
<< "SetProcessDpiAwarenessInternal failed with unexpected error: "
<< hr;
return false;
}
DCHECK_LT(GetVersion(), VERSION_WIN8_1) << "SetProcessDpiAwarenessInternal "
"should be available on all "
"platforms >= Windows 8.1";
return false;
}
} // namespace
static bool g_crash_on_process_detach = false;
void GetNonClientMetrics(NONCLIENTMETRICS_XP* metrics) {
DCHECK(metrics);
metrics->cbSize = sizeof(*metrics);
const bool success =
!!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics->cbSize,
reinterpret_cast<NONCLIENTMETRICS*>(metrics), 0);
DCHECK(success);
}
bool GetUserSidString(std::wstring* user_sid) {
// Get the current token.
HANDLE token = NULL;
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
return false;
ScopedHandle token_scoped(token);
DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
std::unique_ptr<BYTE[]> user_bytes(new BYTE[size]);
TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes.get());
if (!::GetTokenInformation(token, TokenUser, user, size, &size))
return false;
if (!user->User.Sid)
return false;
// Convert the data to a string.
wchar_t* sid_string;
if (!::ConvertSidToStringSid(user->User.Sid, &sid_string))
return false;
*user_sid = sid_string;
::LocalFree(sid_string);
return true;
}
bool UserAccountControlIsEnabled() {
RegKey key(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
KEY_READ);
DWORD uac_enabled;
if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS)
return true;
// Users can set the EnableLUA value to something arbitrary, like 2, which
// Vista will treat as UAC enabled, so we make sure it is not set to 0.
return (uac_enabled != 0);
}
bool SetBooleanValueForPropertyStore(IPropertyStore* property_store,
const PROPERTYKEY& property_key,
bool property_bool_value) {
ScopedPropVariant property_value;
if (FAILED(InitPropVariantFromBoolean(property_bool_value,
property_value.Receive()))) {
return false;
}
return SetPropVariantValueForPropertyStore(property_store, property_key,
property_value);
}
bool SetStringValueForPropertyStore(IPropertyStore* property_store,
const PROPERTYKEY& property_key,
const wchar_t* property_string_value) {
ScopedPropVariant property_value;
if (FAILED(InitPropVariantFromString(property_string_value,
property_value.Receive()))) {
return false;
}
return SetPropVariantValueForPropertyStore(property_store, property_key,
property_value);
}
bool SetClsidForPropertyStore(IPropertyStore* property_store,
const PROPERTYKEY& property_key,
const CLSID& property_clsid_value) {
ScopedPropVariant property_value;
if (FAILED(InitPropVariantFromCLSID(property_clsid_value,
property_value.Receive()))) {
return false;
}
return SetPropVariantValueForPropertyStore(property_store, property_key,
property_value);
}
bool SetAppIdForPropertyStore(IPropertyStore* property_store,
const wchar_t* app_id) {
// App id should be less than 64 chars and contain no space. And recommended
// format is CompanyName.ProductName[.SubProduct.ProductNumber].
// See http://msdn.microsoft.com/en-us/library/dd378459%28VS.85%29.aspx
DCHECK(lstrlen(app_id) < 64 && wcschr(app_id, L' ') == NULL);
return SetStringValueForPropertyStore(property_store, PKEY_AppUserModel_ID,
app_id);
}
static const char16 kAutoRunKeyPath[] =
L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
bool AddCommandToAutoRun(HKEY root_key,
const string16& name,
const string16& command) {
RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
return (autorun_key.WriteValue(name.c_str(), command.c_str()) ==
ERROR_SUCCESS);
}
bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name) {
RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS);
}
bool ReadCommandFromAutoRun(HKEY root_key,
const string16& name,
string16* command) {
RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE);
return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS);
}
void SetShouldCrashOnProcessDetach(bool crash) {
g_crash_on_process_detach = crash;
}
bool ShouldCrashOnProcessDetach() {
return g_crash_on_process_detach;
}
void SetAbortBehaviorForCrashReporting() {
// Prevent CRT's abort code from prompting a dialog or trying to "report" it.
// Disabling the _CALL_REPORTFAULT behavior is important since otherwise it
// has the sideffect of clearing our exception filter, which means we
// don't get any crash.
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
// Set a SIGABRT handler for good measure. We will crash even if the default
// is left in place, however this allows us to crash earlier. And it also
// lets us crash in response to code which might directly call raise(SIGABRT)
signal(SIGABRT, ForceCrashOnSigAbort);
}
bool IsUser32AndGdi32Available() {
static auto is_user32_and_gdi32_available = []() {
// If win32k syscalls aren't disabled, then user32 and gdi32 are available.
// Can't disable win32k prior to windows 8.
if (GetVersion() < VERSION_WIN8)
return true;
typedef decltype(
GetProcessMitigationPolicy)* GetProcessMitigationPolicyType;
GetProcessMitigationPolicyType get_process_mitigation_policy_func =
reinterpret_cast<GetProcessMitigationPolicyType>(GetProcAddress(
GetModuleHandle(L"kernel32.dll"), "GetProcessMitigationPolicy"));
if (!get_process_mitigation_policy_func)
return true;
PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {};
if (get_process_mitigation_policy_func(GetCurrentProcess(),
ProcessSystemCallDisablePolicy,
&policy, sizeof(policy))) {
return policy.DisallowWin32kSystemCalls == 0;
}
return true;
}();
return is_user32_and_gdi32_available;
}
bool GetLoadedModulesSnapshot(HANDLE process, std::vector<HMODULE>* snapshot) {
DCHECK(snapshot);
DCHECK_EQ(0u, snapshot->size());
snapshot->resize(128);
// We will retry at least once after first determining |bytes_required|. If
// the list of modules changes after we receive |bytes_required| we may retry
// more than once.
int retries_remaining = 5;
do {
DWORD bytes_required = 0;
// EnumProcessModules returns 'success' even if the buffer size is too
// small.
DCHECK_GE(std::numeric_limits<DWORD>::max(),
snapshot->size() * sizeof(HMODULE));
if (!::EnumProcessModules(
process, &(*snapshot)[0],
static_cast<DWORD>(snapshot->size() * sizeof(HMODULE)),
&bytes_required)) {
DPLOG(ERROR) << "::EnumProcessModules failed.";
return false;
}
DCHECK_EQ(0u, bytes_required % sizeof(HMODULE));
size_t num_modules = bytes_required / sizeof(HMODULE);
if (num_modules <= snapshot->size()) {
// Buffer size was too big, presumably because a module was unloaded.
snapshot->erase(snapshot->begin() + num_modules, snapshot->end());
return true;
} else if (num_modules == 0) {
DLOG(ERROR) << "Can't determine the module list size.";
return false;
} else {
// Buffer size was too small. Try again with a larger buffer. A little
// more room is given to avoid multiple expensive calls to
// ::EnumProcessModules() just because one module has been added.
snapshot->resize(num_modules + 8, NULL);
}
} while (--retries_remaining);
DLOG(ERROR) << "Failed to enumerate modules.";
return false;
}
void EnableFlicks(HWND hwnd) {
::RemoveProp(hwnd, MICROSOFT_TABLETPENSERVICE_PROPERTY);
}
void DisableFlicks(HWND hwnd) {
::SetProp(hwnd, MICROSOFT_TABLETPENSERVICE_PROPERTY,
reinterpret_cast<HANDLE>(TABLET_DISABLE_FLICKS |
TABLET_DISABLE_FLICKFALLBACKKEYS));
}
bool IsProcessPerMonitorDpiAware() {
enum class PerMonitorDpiAware {
UNKNOWN = 0,
PER_MONITOR_DPI_UNAWARE,
PER_MONITOR_DPI_AWARE,
};
static PerMonitorDpiAware per_monitor_dpi_aware = PerMonitorDpiAware::UNKNOWN;
if (per_monitor_dpi_aware == PerMonitorDpiAware::UNKNOWN) {
per_monitor_dpi_aware = PerMonitorDpiAware::PER_MONITOR_DPI_UNAWARE;
HMODULE shcore_dll = ::LoadLibrary(L"shcore.dll");
if (shcore_dll) {
auto get_process_dpi_awareness_func =
reinterpret_cast<decltype(::GetProcessDpiAwareness)*>(
::GetProcAddress(shcore_dll, "GetProcessDpiAwareness"));
if (get_process_dpi_awareness_func) {
PROCESS_DPI_AWARENESS awareness;
if (SUCCEEDED(get_process_dpi_awareness_func(nullptr, &awareness)) &&
awareness == PROCESS_PER_MONITOR_DPI_AWARE)
per_monitor_dpi_aware = PerMonitorDpiAware::PER_MONITOR_DPI_AWARE;
}
}
}
return per_monitor_dpi_aware == PerMonitorDpiAware::PER_MONITOR_DPI_AWARE;
}
void EnableHighDPISupport() {
// Enable per-monitor DPI for Win10 or above instead of Win8.1 since Win8.1
// does not have EnableChildWindowDpiMessage, necessary for correct non-client
// area scaling across monitors.
PROCESS_DPI_AWARENESS process_dpi_awareness =
GetVersion() >= VERSION_WIN10 ? PROCESS_PER_MONITOR_DPI_AWARE
: PROCESS_SYSTEM_DPI_AWARE;
if (!SetProcessDpiAwarenessWrapper(process_dpi_awareness)) {
// For windows versions where SetProcessDpiAwareness is not available or
// failed, try its predecessor.
BOOL result = ::SetProcessDPIAware();
DCHECK(result) << "SetProcessDPIAware failed.";
}
}
} // namespace win
} // namespace base