123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925 |
- // 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 <lm.h>
- #include <powrprof.h>
- #include <shobjidl.h> // Must be before propkey.
- #include <inspectable.h>
- #include <mdmregistration.h>
- #include <objbase.h>
- #include <propkey.h>
- #include <psapi.h>
- #include <roapi.h>
- #include <sddl.h>
- #include <setupapi.h>
- #include <shellscalingapi.h>
- #include <signal.h>
- #include <stddef.h>
- #include <stdlib.h>
- #include <strsafe.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 <limits>
- #include <memory>
- #include <utility>
- #include "base/base_switches.h"
- #include "base/command_line.h"
- #include "base/files/file_path.h"
- #include "base/logging.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/notreached.h"
- #include "base/scoped_native_library.h"
- #include "base/strings/string_util.h"
- #include "base/strings/string_util_win.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/threading/scoped_thread_priority.h"
- #include "base/threading/thread_restrictions.h"
- #include "base/timer/elapsed_timer.h"
- #include "base/win/access_token.h"
- #include "base/win/core_winrt_util.h"
- #include "base/win/propvarutil.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/shlwapi.h"
- #include "base/win/static_constants.h"
- #include "base/win/windows_version.h"
- #include "third_party/abseil-cpp/absl/types/optional.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()
- 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 "
- << WStringFromGUID(property_key.fmtid) << " PID "
- << property_key.pid;
- #endif
- return false;
- }
- void __cdecl ForceCrashOnSigAbort(int) {
- *((volatile int*)nullptr) = 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) {
- if (!IsUser32AndGdi32Available())
- return false;
- static const auto set_process_dpi_awareness_func =
- reinterpret_cast<decltype(&::SetProcessDpiAwareness)>(
- GetUser32FunctionPointer("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;
- }
- // Enable V2 per-monitor high-DPI support for the process. This will cause
- // Windows to scale dialogs, comctl32 controls, context menus, and non-client
- // area owned by this process on a per-monitor basis. If per-monitor V2 is not
- // available (i.e., prior to Windows 10 1703) or fails, returns false.
- // https://docs.microsoft.com/en-us/windows/desktop/hidpi/dpi-awareness-context
- bool EnablePerMonitorV2() {
- if (!IsUser32AndGdi32Available())
- return false;
- static const auto set_process_dpi_awareness_context_func =
- reinterpret_cast<decltype(&::SetProcessDpiAwarenessContext)>(
- GetUser32FunctionPointer("SetProcessDpiAwarenessContext"));
- if (set_process_dpi_awareness_context_func) {
- return set_process_dpi_awareness_context_func(
- DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
- }
- DCHECK_LT(GetVersion(), Version::WIN10_RS2)
- << "SetProcessDpiAwarenessContext should be available on all platforms"
- " >= Windows 10 Redstone 2";
- return false;
- }
- bool* GetDomainEnrollmentStateStorage() {
- static bool state = IsOS(OS_DOMAINMEMBER);
- return &state;
- }
- bool* GetRegisteredWithManagementStateStorage() {
- static bool state = []() {
- // Mitigate the issues caused by loading DLLs on a background thread
- // (http://crbug/973868).
- SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
- ScopedNativeLibrary library(
- FilePath(FILE_PATH_LITERAL("MDMRegistration.dll")));
- if (!library.is_valid())
- return false;
- using IsDeviceRegisteredWithManagementFunction =
- decltype(&::IsDeviceRegisteredWithManagement);
- IsDeviceRegisteredWithManagementFunction
- is_device_registered_with_management_function =
- reinterpret_cast<IsDeviceRegisteredWithManagementFunction>(
- library.GetFunctionPointer("IsDeviceRegisteredWithManagement"));
- if (!is_device_registered_with_management_function)
- return false;
- BOOL is_managed = FALSE;
- HRESULT hr =
- is_device_registered_with_management_function(&is_managed, 0, nullptr);
- return SUCCEEDED(hr) && is_managed;
- }();
- return &state;
- }
- // TODO (crbug/1300219): return a DSREG_JOIN_TYPE* instead of bool*.
- bool* GetAzureADJoinStateStorage() {
- static bool state = []() {
- base::ElapsedTimer timer;
- // Mitigate the issues caused by loading DLLs on a background thread
- // (http://crbug/973868).
- SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
- ScopedNativeLibrary netapi32(
- base::LoadSystemLibrary(FILE_PATH_LITERAL("netapi32.dll")));
- if (!netapi32.is_valid())
- return false;
- const auto net_get_aad_join_information_function =
- reinterpret_cast<decltype(&::NetGetAadJoinInformation)>(
- netapi32.GetFunctionPointer("NetGetAadJoinInformation"));
- if (!net_get_aad_join_information_function)
- return false;
- const auto net_free_aad_join_information_function =
- reinterpret_cast<decltype(&::NetFreeAadJoinInformation)>(
- netapi32.GetFunctionPointer("NetFreeAadJoinInformation"));
- DPCHECK(net_free_aad_join_information_function);
- DSREG_JOIN_INFO* join_info = nullptr;
- HRESULT hr = net_get_aad_join_information_function(/*pcszTenantId=*/nullptr,
- &join_info);
- const bool is_aad_joined = SUCCEEDED(hr) && join_info;
- if (join_info) {
- net_free_aad_join_information_function(join_info);
- }
- base::UmaHistogramTimes("EnterpriseCheck.AzureADJoinStatusCheckTime",
- timer.Elapsed());
- return is_aad_joined;
- }();
- return &state;
- }
- NativeLibrary PinUser32Internal(NativeLibraryLoadError* error) {
- static NativeLibraryLoadError load_error;
- static const NativeLibrary user32_module =
- PinSystemLibrary(FILE_PATH_LITERAL("user32.dll"), &load_error);
- if (!user32_module && error)
- error->code = load_error.code;
- return user32_module;
- }
- } // namespace
- // Uses the Windows 10 WRL API's to query the current system state. The API's
- // we are using in the function below are supported in Win32 apps as per msdn.
- // It looks like the API implementation is buggy at least on Surface 4 causing
- // it to always return UserInteractionMode_Touch which as per documentation
- // indicates tablet mode.
- bool IsWindows10OrGreaterTabletMode(HWND hwnd) {
- if (GetVersion() >= Version::WIN11) {
- // Only Win10 supports explicit tablet mode. On Win11,
- // get_UserInteractionMode always returns UserInteractionMode_Mouse, so
- // instead we check if we're in slate mode or not - 0 value means slate
- // mode. See
- // https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-gpiobuttons-convertibleslatemode
- constexpr int kKeyboardPresent = 1;
- base::win::RegKey registry_key(
- HKEY_LOCAL_MACHINE,
- L"System\\CurrentControlSet\\Control\\PriorityControl", KEY_READ);
- DWORD slate_mode = 0;
- bool value_exists = registry_key.ReadValueDW(L"ConvertibleSlateMode",
- &slate_mode) == ERROR_SUCCESS;
- // Some devices don't set the reg key to 1 for keyboard-only devices, so
- // also check if the device is used as a tablet if it is not 1. Some devices
- // don't set the registry key at all; fall back to checking if the device
- // is used as a tablet for them as well.
- return !(value_exists && slate_mode == kKeyboardPresent) &&
- IsDeviceUsedAsATablet(/*reason=*/nullptr);
- }
- if (!ResolveCoreWinRTDelayload() ||
- !ScopedHString::ResolveCoreWinRTStringDelayload()) {
- return false;
- }
- ScopedHString view_settings_guid = ScopedHString::Create(
- RuntimeClass_Windows_UI_ViewManagement_UIViewSettings);
- Microsoft::WRL::ComPtr<IUIViewSettingsInterop> view_settings_interop;
- HRESULT hr = win::RoGetActivationFactory(
- view_settings_guid.get(), IID_PPV_ARGS(&view_settings_interop));
- if (FAILED(hr))
- return false;
- Microsoft::WRL::ComPtr<ABI::Windows::UI::ViewManagement::IUIViewSettings>
- view_settings;
- hr = view_settings_interop->GetForWindow(hwnd, IID_PPV_ARGS(&view_settings));
- if (FAILED(hr))
- return false;
- ABI::Windows::UI::ViewManagement::UserInteractionMode mode =
- ABI::Windows::UI::ViewManagement::UserInteractionMode_Mouse;
- view_settings->get_UserInteractionMode(&mode);
- return mode == ABI::Windows::UI::ViewManagement::UserInteractionMode_Touch;
- }
- // Returns true if a physical keyboard is detected on Windows 8 and up.
- // Uses the Setup APIs to enumerate the attached keyboards and returns true
- // if the keyboard count is 1 or more.. While this will work in most cases
- // it won't work if there are devices which expose keyboard interfaces which
- // are attached to the machine.
- bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason) {
- bool result = false;
- if (GetVersion() < Version::WIN8) {
- if (reason)
- *reason = "Detection not supported";
- return false;
- }
- // This function is only supported for Windows 8 and up.
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableUsbKeyboardDetect)) {
- if (reason)
- *reason = "Detection disabled";
- return false;
- }
- // This function should be only invoked for machines with touch screens.
- if ((GetSystemMetrics(SM_DIGITIZER) & NID_INTEGRATED_TOUCH) !=
- NID_INTEGRATED_TOUCH) {
- if (reason) {
- *reason += "NID_INTEGRATED_TOUCH\n";
- result = true;
- } else {
- return true;
- }
- }
- // If it is a tablet device we assume that there is no keyboard attached.
- if (IsTabletDevice(reason, hwnd)) {
- if (reason)
- *reason += "Tablet device.\n";
- return false;
- }
- if (!reason)
- return true;
- *reason += "Not a tablet device";
- result = true;
- // To determine whether a keyboard is present on the device, we do the
- // following:-
- // 1. Check whether the device supports auto rotation. If it does then
- // it possibly supports flipping from laptop to slate mode. If it
- // does not support auto rotation, then we assume it is a desktop
- // or a normal laptop and assume that there is a keyboard.
- // 2. If the device supports auto rotation, then we get its platform role
- // and check the system metric SM_CONVERTIBLESLATEMODE to see if it is
- // being used in slate mode. If yes then we return false here to ensure
- // that the OSK is displayed.
- // 3. If step 1 and 2 fail then we check attached keyboards and return true
- // if we find ACPI\* or HID\VID* keyboards.
- using GetAutoRotationState = decltype(&::GetAutoRotationState);
- static const auto get_rotation_state = reinterpret_cast<GetAutoRotationState>(
- GetUser32FunctionPointer("GetAutoRotationState"));
- if (get_rotation_state) {
- AR_STATE auto_rotation_state = AR_ENABLED;
- get_rotation_state(&auto_rotation_state);
- if ((auto_rotation_state & AR_NOSENSOR) ||
- (auto_rotation_state & AR_NOT_SUPPORTED)) {
- // If there is no auto rotation sensor or rotation is not supported in
- // the current configuration, then we can assume that this is a desktop
- // or a traditional laptop.
- if (!reason)
- return true;
- *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
- : "AR_NOT_SUPPORTED\n";
- result = true;
- }
- }
- const GUID KEYBOARD_CLASS_GUID = {
- 0x4D36E96B,
- 0xE325,
- 0x11CE,
- {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}};
- // Query for all the keyboard devices.
- HDEVINFO device_info = SetupDiGetClassDevs(&KEYBOARD_CLASS_GUID, nullptr,
- nullptr, DIGCF_PRESENT);
- if (device_info == INVALID_HANDLE_VALUE) {
- if (reason)
- *reason += "No keyboard info\n";
- return result;
- }
- // Enumerate all keyboards and look for ACPI\PNP and HID\VID devices. If
- // the count is more than 1 we assume that a keyboard is present. This is
- // under the assumption that there will always be one keyboard device.
- for (DWORD i = 0;; ++i) {
- SP_DEVINFO_DATA device_info_data = {0};
- device_info_data.cbSize = sizeof(device_info_data);
- if (!SetupDiEnumDeviceInfo(device_info, i, &device_info_data))
- break;
- // Get the device ID.
- wchar_t device_id[MAX_DEVICE_ID_LEN];
- CONFIGRET status = CM_Get_Device_ID(device_info_data.DevInst, device_id,
- MAX_DEVICE_ID_LEN, 0);
- if (status == CR_SUCCESS) {
- // To reduce the scope of the hack we only look for ACPI and HID\\VID
- // prefixes in the keyboard device ids.
- if (StartsWith(device_id, L"ACPI", CompareCase::INSENSITIVE_ASCII) ||
- StartsWith(device_id, L"HID\\VID", CompareCase::INSENSITIVE_ASCII)) {
- if (reason) {
- *reason += "device: ";
- *reason += WideToUTF8(device_id);
- *reason += '\n';
- }
- // The heuristic we are using is to check the count of keyboards and
- // return true if the API's report one or more keyboards. Please note
- // that this will break for non keyboard devices which expose a
- // keyboard PDO.
- result = true;
- }
- }
- }
- return result;
- }
- static bool g_crash_on_process_detach = false;
- bool GetUserSidString(std::wstring* user_sid) {
- absl::optional<AccessToken> token = AccessToken::FromCurrentProcess();
- if (!token)
- return false;
- absl::optional<std::wstring> sid_string = token->User().ToSddlString();
- if (!sid_string)
- return false;
- *user_sid = *sid_string;
- return true;
- }
- bool UserAccountControlIsEnabled() {
- // This can be slow if Windows ends up going to disk. Should watch this key
- // for changes and only read it once, preferably on the file thread.
- // http://code.google.com/p/chromium/issues/detail?id=61644
- ThreadRestrictions::ScopedAllowIO allow_io;
- 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 128 chars and contain no space. And recommended
- // format is CompanyName.ProductName[.SubProduct.ProductNumber].
- // See
- // https://docs.microsoft.com/en-us/windows/win32/shell/appids#how-to-form-an-application-defined-appusermodelid
- DCHECK_LT(lstrlen(app_id), 128);
- DCHECK_EQ(wcschr(app_id, L' '), nullptr);
- return SetStringValueForPropertyStore(property_store, PKEY_AppUserModel_ID,
- app_id);
- }
- static const wchar_t kAutoRunKeyPath[] =
- L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
- bool AddCommandToAutoRun(HKEY root_key,
- const std::wstring& name,
- const std::wstring& 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 std::wstring& 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 std::wstring& name,
- std::wstring* 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 IsTabletDevice(std::string* reason, HWND hwnd) {
- if (GetVersion() < Version::WIN8) {
- if (reason)
- *reason = "Tablet device detection not supported below Windows 8\n";
- return false;
- }
- if (IsWindows10OrGreaterTabletMode(hwnd))
- return true;
- return IsDeviceUsedAsATablet(reason);
- }
- // This method is used to set the right interactions media queries,
- // see https://drafts.csswg.org/mediaqueries-4/#mf-interaction. It doesn't
- // check the Windows 10 tablet mode because it doesn't reflect the actual
- // input configuration of the device and can be manually triggered by the user
- // independently from the hardware state.
- bool IsDeviceUsedAsATablet(std::string* reason) {
- // Once this is set, it shouldn't be overridden, and it should be the ultimate
- // return value, so that this method returns the same result whether or not
- // reason is NULL.
- absl::optional<bool> ret;
- if (GetVersion() < Version::WIN8) {
- if (reason)
- *reason = "Tablet device detection not supported below Windows 8\n";
- return false;
- }
- if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0) {
- if (reason) {
- *reason += "Device does not support touch.\n";
- ret = false;
- } else {
- return false;
- }
- }
- // If the device is docked, the user is treating the device as a PC.
- if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) {
- if (reason) {
- *reason += "SM_SYSTEMDOCKED\n";
- if (!ret.has_value())
- ret = false;
- } else {
- return false;
- }
- }
- // If the device is not supporting rotation, it's unlikely to be a tablet,
- // a convertible or a detachable.
- // See
- // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
- using GetAutoRotationStateType = decltype(GetAutoRotationState)*;
- static const auto get_auto_rotation_state_func =
- reinterpret_cast<GetAutoRotationStateType>(
- GetUser32FunctionPointer("GetAutoRotationState"));
- if (get_auto_rotation_state_func) {
- AR_STATE rotation_state = AR_ENABLED;
- if (get_auto_rotation_state_func(&rotation_state) &&
- (rotation_state & (AR_NOT_SUPPORTED | AR_LAPTOP | AR_NOSENSOR)) != 0)
- return ret.has_value() ? ret.value() : false;
- }
- // PlatformRoleSlate was added in Windows 8+.
- POWER_PLATFORM_ROLE role = GetPlatformRole();
- bool is_tablet = false;
- if (role == PlatformRoleMobile || role == PlatformRoleSlate) {
- is_tablet = !GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
- if (!is_tablet) {
- if (reason) {
- *reason += "Not in slate mode.\n";
- if (!ret.has_value())
- ret = false;
- } else {
- return false;
- }
- } else if (reason) {
- *reason += (role == PlatformRoleMobile) ? "PlatformRoleMobile\n"
- : "PlatformRoleSlate\n";
- }
- } else if (reason) {
- *reason += "Device role is not mobile or slate.\n";
- }
- return ret.has_value() ? ret.value() : is_tablet;
- }
- bool IsEnrolledToDomain() {
- return *GetDomainEnrollmentStateStorage();
- }
- bool IsDeviceRegisteredWithManagement() {
- return *GetRegisteredWithManagementStateStorage();
- }
- bool IsJoinedToAzureAD() {
- return *GetAzureADJoinStateStorage();
- }
- 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;
- using GetProcessMitigationPolicyType =
- decltype(GetProcessMitigationPolicy)*;
- 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() + static_cast<ptrdiff_t>(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, nullptr);
- }
- } 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() {
- if (!IsUser32AndGdi32Available())
- return;
- // Enable per-monitor V2 if it is available (Win10 1703 or later).
- if (EnablePerMonitorV2())
- return;
- // Fall back to per-monitor DPI for older versions of Win10 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.";
- }
- }
- std::wstring WStringFromGUID(REFGUID rguid) {
- // This constant counts the number of characters in the formatted string,
- // including the null termination character.
- constexpr int kGuidStringCharacters =
- 1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1;
- wchar_t guid_string[kGuidStringCharacters];
- CHECK(SUCCEEDED(StringCchPrintfW(
- guid_string, kGuidStringCharacters,
- L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", rguid.Data1,
- rguid.Data2, rguid.Data3, rguid.Data4[0], rguid.Data4[1], rguid.Data4[2],
- rguid.Data4[3], rguid.Data4[4], rguid.Data4[5], rguid.Data4[6],
- rguid.Data4[7])));
- return std::wstring(guid_string, kGuidStringCharacters - 1);
- }
- bool PinUser32(NativeLibraryLoadError* error) {
- return PinUser32Internal(error) != nullptr;
- }
- void* GetUser32FunctionPointer(const char* function_name,
- NativeLibraryLoadError* error) {
- NativeLibrary user32_module = PinUser32Internal(error);
- if (user32_module)
- return GetFunctionPointerFromNativeLibrary(user32_module, function_name);
- return nullptr;
- }
- std::wstring GetWindowObjectName(HANDLE handle) {
- // Get the size of the name.
- std::wstring object_name;
- DWORD size = 0;
- ::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
- if (!size) {
- DPCHECK(false);
- return object_name;
- }
- LOG_ASSERT(size % sizeof(wchar_t) == 0u);
- // Query the name of the object.
- if (!::GetUserObjectInformation(
- handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)),
- size, &size)) {
- DPCHECK(false);
- }
- return object_name;
- }
- bool IsRunningUnderDesktopName(WStringPiece desktop_name) {
- HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
- if (!thread_desktop)
- return false;
- std::wstring current_desktop_name = GetWindowObjectName(thread_desktop);
- return EqualsCaseInsensitiveASCII(AsStringPiece16(current_desktop_name),
- AsStringPiece16(desktop_name));
- }
- // This method is used to detect whether current session is a remote session.
- // See:
- // https://docs.microsoft.com/en-us/windows/desktop/TermServ/detecting-the-terminal-services-environment
- bool IsCurrentSessionRemote() {
- if (::GetSystemMetrics(SM_REMOTESESSION))
- return true;
- DWORD current_session_id = 0;
- if (!::ProcessIdToSessionId(::GetCurrentProcessId(), ¤t_session_id))
- return false;
- static constexpr wchar_t kRdpSettingsKeyName[] =
- L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server";
- RegKey key(HKEY_LOCAL_MACHINE, kRdpSettingsKeyName, KEY_READ);
- if (!key.Valid())
- return false;
- static constexpr wchar_t kGlassSessionIdValueName[] = L"GlassSessionId";
- DWORD glass_session_id = 0;
- if (key.ReadValueDW(kGlassSessionIdValueName, &glass_session_id) !=
- ERROR_SUCCESS)
- return false;
- return current_session_id != glass_session_id;
- }
- #if !defined(OFFICIAL_BUILD)
- bool IsAppVerifierEnabled(const std::wstring& process_name) {
- RegKey key;
- // Look for GlobalFlag in the IFEO\chrome.exe key. If it is present then
- // Application Verifier or gflags.exe are configured. Most GlobalFlag
- // settings are experimentally determined to be incompatible with renderer
- // code integrity and a safe set is not known so any GlobalFlag entry is
- // assumed to mean that Application Verifier (or pageheap) are enabled.
- // The settings are propagated to both 64-bit WOW6432Node versions of the
- // registry on 64-bit Windows, so only one check is needed.
- return key.Open(
- HKEY_LOCAL_MACHINE,
- (L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File "
- L"Execution Options\\" +
- process_name)
- .c_str(),
- KEY_READ | KEY_WOW64_64KEY) == ERROR_SUCCESS &&
- key.HasValue(L"GlobalFlag");
- }
- #endif // !defined(OFFICIAL_BUILD)
- bool IsAppVerifierLoaded() {
- return GetModuleHandleA(kApplicationVerifierDllName);
- }
- ScopedDomainStateForTesting::ScopedDomainStateForTesting(bool state)
- : initial_state_(IsEnrolledToDomain()) {
- *GetDomainEnrollmentStateStorage() = state;
- }
- ScopedDomainStateForTesting::~ScopedDomainStateForTesting() {
- *GetDomainEnrollmentStateStorage() = initial_state_;
- }
- ScopedDeviceRegisteredWithManagementForTesting::
- ScopedDeviceRegisteredWithManagementForTesting(bool state)
- : initial_state_(IsDeviceRegisteredWithManagement()) {
- *GetRegisteredWithManagementStateStorage() = state;
- }
- ScopedDeviceRegisteredWithManagementForTesting::
- ~ScopedDeviceRegisteredWithManagementForTesting() {
- *GetRegisteredWithManagementStateStorage() = initial_state_;
- }
- ScopedAzureADJoinStateForTesting::ScopedAzureADJoinStateForTesting(bool state)
- : initial_state_(std::exchange(*GetAzureADJoinStateStorage(), state)) {}
- ScopedAzureADJoinStateForTesting::~ScopedAzureADJoinStateForTesting() {
- *GetAzureADJoinStateStorage() = initial_state_;
- }
- } // namespace win
- } // namespace base
|