1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/win/win_util.h"
6
7 #include <aclapi.h>
8 #include <cfgmgr32.h>
9 #include <initguid.h>
10 #include <lm.h>
11 #include <powrprof.h>
12 #include <shobjidl.h> // Must be before propkey.
13
14 #include <inspectable.h>
15 #include <mdmregistration.h>
16 #include <objbase.h>
17 #include <propkey.h>
18 #include <psapi.h>
19 #include <roapi.h>
20 #include <sddl.h>
21 #include <setupapi.h>
22 #include <shellscalingapi.h>
23 #include <signal.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <strsafe.h>
27 #include <tchar.h> // Must be before tpcshrd.h or for any use of _T macro
28 #include <tpcshrd.h>
29 #include <uiviewsettingsinterop.h>
30 #include <windows.ui.viewmanagement.h>
31 #include <winstring.h>
32 #include <wrl/client.h>
33 #include <wrl/wrappers/corewrappers.h>
34
35 #include <limits>
36 #include <memory>
37 #include <utility>
38
39 #include "base/base_switches.h"
40 #include "base/command_line.h"
41 #include "base/files/file_path.h"
42 #include "base/logging.h"
43 #include "base/metrics/histogram_functions.h"
44 #include "base/notreached.h"
45 #include "base/scoped_native_library.h"
46 #include "base/strings/string_util.h"
47 #include "base/strings/string_util_win.h"
48 #include "base/strings/utf_string_conversions.h"
49 #include "base/threading/scoped_thread_priority.h"
50 #include "base/threading/thread_restrictions.h"
51 #include "base/timer/elapsed_timer.h"
52 #include "base/win/access_token.h"
53 #include "base/win/core_winrt_util.h"
54 #include "base/win/propvarutil.h"
55 #include "base/win/registry.h"
56 #include "base/win/scoped_co_mem.h"
57 #include "base/win/scoped_handle.h"
58 #include "base/win/scoped_hstring.h"
59 #include "base/win/scoped_propvariant.h"
60 #include "base/win/shlwapi.h"
61 #include "base/win/static_constants.h"
62 #include "base/win/windows_version.h"
63 #include "third_party/abseil-cpp/absl/types/optional.h"
64
65 namespace base {
66 namespace win {
67
68 namespace {
69
70 // Sets the value of |property_key| to |property_value| in |property_store|.
SetPropVariantValueForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,const ScopedPropVariant & property_value)71 bool SetPropVariantValueForPropertyStore(
72 IPropertyStore* property_store,
73 const PROPERTYKEY& property_key,
74 const ScopedPropVariant& property_value) {
75 DCHECK(property_store);
76
77 HRESULT result = property_store->SetValue(property_key, property_value.get());
78 if (result == S_OK)
79 result = property_store->Commit();
80 if (SUCCEEDED(result))
81 return true;
82 #if DCHECK_IS_ON()
83 if (HRESULT_FACILITY(result) == FACILITY_WIN32)
84 ::SetLastError(HRESULT_CODE(result));
85 // See third_party/perl/c/i686-w64-mingw32/include/propkey.h for GUID and
86 // PID definitions.
87 DPLOG(ERROR) << "Failed to set property with GUID "
88 << WStringFromGUID(property_key.fmtid) << " PID "
89 << property_key.pid;
90 #endif
91 return false;
92 }
93
ForceCrashOnSigAbort(int)94 void __cdecl ForceCrashOnSigAbort(int) {
95 *((volatile int*)nullptr) = 0x1337;
96 }
97
98 // Returns the current platform role. We use the PowerDeterminePlatformRoleEx
99 // API for that.
GetPlatformRole()100 POWER_PLATFORM_ROLE GetPlatformRole() {
101 return PowerDeterminePlatformRoleEx(POWER_PLATFORM_ROLE_V2);
102 }
103
104 // Enable V2 per-monitor high-DPI support for the process. This will cause
105 // Windows to scale dialogs, comctl32 controls, context menus, and non-client
106 // area owned by this process on a per-monitor basis. If per-monitor V2 is not
107 // available (i.e., prior to Windows 10 1703) or fails, returns false.
108 // https://docs.microsoft.com/en-us/windows/desktop/hidpi/dpi-awareness-context
EnablePerMonitorV2()109 bool EnablePerMonitorV2() {
110 if (!IsUser32AndGdi32Available())
111 return false;
112
113 static const auto set_process_dpi_awareness_context_func =
114 reinterpret_cast<decltype(&::SetProcessDpiAwarenessContext)>(
115 GetUser32FunctionPointer("SetProcessDpiAwarenessContext"));
116 if (set_process_dpi_awareness_context_func) {
117 return set_process_dpi_awareness_context_func(
118 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
119 }
120
121 DCHECK_LT(GetVersion(), Version::WIN10_RS2)
122 << "SetProcessDpiAwarenessContext should be available on all platforms"
123 " >= Windows 10 Redstone 2";
124
125 return false;
126 }
127
GetDomainEnrollmentStateStorage()128 bool* GetDomainEnrollmentStateStorage() {
129 static bool state = IsOS(OS_DOMAINMEMBER);
130 return &state;
131 }
132
GetRegisteredWithManagementStateStorage()133 bool* GetRegisteredWithManagementStateStorage() {
134 static bool state = []() {
135 // Mitigate the issues caused by loading DLLs on a background thread
136 // (http://crbug/973868).
137 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
138
139 ScopedNativeLibrary library(
140 FilePath(FILE_PATH_LITERAL("MDMRegistration.dll")));
141 if (!library.is_valid())
142 return false;
143
144 using IsDeviceRegisteredWithManagementFunction =
145 decltype(&::IsDeviceRegisteredWithManagement);
146 IsDeviceRegisteredWithManagementFunction
147 is_device_registered_with_management_function =
148 reinterpret_cast<IsDeviceRegisteredWithManagementFunction>(
149 library.GetFunctionPointer("IsDeviceRegisteredWithManagement"));
150 if (!is_device_registered_with_management_function)
151 return false;
152
153 BOOL is_managed = FALSE;
154 HRESULT hr =
155 is_device_registered_with_management_function(&is_managed, 0, nullptr);
156 return SUCCEEDED(hr) && is_managed;
157 }();
158
159 return &state;
160 }
161
162 // TODO (crbug/1300219): return a DSREG_JOIN_TYPE* instead of bool*.
GetAzureADJoinStateStorage()163 bool* GetAzureADJoinStateStorage() {
164 static bool state = []() {
165 base::ElapsedTimer timer;
166
167 // Mitigate the issues caused by loading DLLs on a background thread
168 // (http://crbug/973868).
169 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
170
171 ScopedNativeLibrary netapi32(
172 base::LoadSystemLibrary(FILE_PATH_LITERAL("netapi32.dll")));
173 if (!netapi32.is_valid())
174 return false;
175
176 const auto net_get_aad_join_information_function =
177 reinterpret_cast<decltype(&::NetGetAadJoinInformation)>(
178 netapi32.GetFunctionPointer("NetGetAadJoinInformation"));
179 if (!net_get_aad_join_information_function)
180 return false;
181
182 const auto net_free_aad_join_information_function =
183 reinterpret_cast<decltype(&::NetFreeAadJoinInformation)>(
184 netapi32.GetFunctionPointer("NetFreeAadJoinInformation"));
185 DPCHECK(net_free_aad_join_information_function);
186
187 DSREG_JOIN_INFO* join_info = nullptr;
188 HRESULT hr = net_get_aad_join_information_function(/*pcszTenantId=*/nullptr,
189 &join_info);
190 const bool is_aad_joined = SUCCEEDED(hr) && join_info;
191 if (join_info) {
192 net_free_aad_join_information_function(join_info);
193 }
194
195 base::UmaHistogramTimes("EnterpriseCheck.AzureADJoinStatusCheckTime",
196 timer.Elapsed());
197 return is_aad_joined;
198 }();
199 return &state;
200 }
201
PinUser32Internal(NativeLibraryLoadError * error)202 NativeLibrary PinUser32Internal(NativeLibraryLoadError* error) {
203 static NativeLibraryLoadError load_error;
204 static const NativeLibrary user32_module =
205 PinSystemLibrary(FILE_PATH_LITERAL("user32.dll"), &load_error);
206 if (!user32_module && error)
207 error->code = load_error.code;
208 return user32_module;
209 }
210
211 } // namespace
212
213 // Uses the Windows 10 WRL API's to query the current system state. The API's
214 // we are using in the function below are supported in Win32 apps as per msdn.
215 // It looks like the API implementation is buggy at least on Surface 4 causing
216 // it to always return UserInteractionMode_Touch which as per documentation
217 // indicates tablet mode.
IsWindows10OrGreaterTabletMode(HWND hwnd)218 bool IsWindows10OrGreaterTabletMode(HWND hwnd) {
219 if (GetVersion() >= Version::WIN11) {
220 // Only Win10 supports explicit tablet mode. On Win11,
221 // get_UserInteractionMode always returns UserInteractionMode_Mouse, so
222 // instead we check if we're in slate mode or not - 0 value means slate
223 // mode. See
224 // https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-gpiobuttons-convertibleslatemode
225
226 constexpr int kKeyboardPresent = 1;
227 base::win::RegKey registry_key(
228 HKEY_LOCAL_MACHINE,
229 L"System\\CurrentControlSet\\Control\\PriorityControl", KEY_READ);
230 DWORD slate_mode = 0;
231 bool value_exists = registry_key.ReadValueDW(L"ConvertibleSlateMode",
232 &slate_mode) == ERROR_SUCCESS;
233 // Some devices don't set the reg key to 1 for keyboard-only devices, so
234 // also check if the device is used as a tablet if it is not 1. Some devices
235 // don't set the registry key at all; fall back to checking if the device
236 // is used as a tablet for them as well.
237 return !(value_exists && slate_mode == kKeyboardPresent) &&
238 IsDeviceUsedAsATablet(/*reason=*/nullptr);
239 }
240
241 ScopedHString view_settings_guid = ScopedHString::Create(
242 RuntimeClass_Windows_UI_ViewManagement_UIViewSettings);
243 Microsoft::WRL::ComPtr<IUIViewSettingsInterop> view_settings_interop;
244 HRESULT hr = ::RoGetActivationFactory(view_settings_guid.get(),
245 IID_PPV_ARGS(&view_settings_interop));
246 if (FAILED(hr))
247 return false;
248
249 Microsoft::WRL::ComPtr<ABI::Windows::UI::ViewManagement::IUIViewSettings>
250 view_settings;
251 hr = view_settings_interop->GetForWindow(hwnd, IID_PPV_ARGS(&view_settings));
252 if (FAILED(hr))
253 return false;
254
255 ABI::Windows::UI::ViewManagement::UserInteractionMode mode =
256 ABI::Windows::UI::ViewManagement::UserInteractionMode_Mouse;
257 view_settings->get_UserInteractionMode(&mode);
258 return mode == ABI::Windows::UI::ViewManagement::UserInteractionMode_Touch;
259 }
260
261 // Returns true if a physical keyboard is detected on Windows 8 and up.
262 // Uses the Setup APIs to enumerate the attached keyboards and returns true
263 // if the keyboard count is 1 or more.. While this will work in most cases
264 // it won't work if there are devices which expose keyboard interfaces which
265 // are attached to the machine.
IsKeyboardPresentOnSlate(HWND hwnd,std::string * reason)266 bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason) {
267 bool result = false;
268
269 if (CommandLine::ForCurrentProcess()->HasSwitch(
270 switches::kDisableUsbKeyboardDetect)) {
271 if (reason)
272 *reason = "Detection disabled";
273 return false;
274 }
275
276 // This function should be only invoked for machines with touch screens.
277 if ((GetSystemMetrics(SM_DIGITIZER) & NID_INTEGRATED_TOUCH) !=
278 NID_INTEGRATED_TOUCH) {
279 if (reason) {
280 *reason += "NID_INTEGRATED_TOUCH\n";
281 result = true;
282 } else {
283 return true;
284 }
285 }
286
287 // If it is a tablet device we assume that there is no keyboard attached.
288 if (IsTabletDevice(reason, hwnd)) {
289 if (reason)
290 *reason += "Tablet device.\n";
291 return false;
292 }
293
294 if (!reason)
295 return true;
296
297 *reason += "Not a tablet device";
298 result = true;
299
300 // To determine whether a keyboard is present on the device, we do the
301 // following:-
302 // 1. Check whether the device supports auto rotation. If it does then
303 // it possibly supports flipping from laptop to slate mode. If it
304 // does not support auto rotation, then we assume it is a desktop
305 // or a normal laptop and assume that there is a keyboard.
306
307 // 2. If the device supports auto rotation, then we get its platform role
308 // and check the system metric SM_CONVERTIBLESLATEMODE to see if it is
309 // being used in slate mode. If yes then we return false here to ensure
310 // that the OSK is displayed.
311
312 // 3. If step 1 and 2 fail then we check attached keyboards and return true
313 // if we find ACPI\* or HID\VID* keyboards.
314
315 using GetAutoRotationState = decltype(&::GetAutoRotationState);
316 static const auto get_rotation_state = reinterpret_cast<GetAutoRotationState>(
317 GetUser32FunctionPointer("GetAutoRotationState"));
318 if (get_rotation_state) {
319 AR_STATE auto_rotation_state = AR_ENABLED;
320 get_rotation_state(&auto_rotation_state);
321 if ((auto_rotation_state & AR_NOSENSOR) ||
322 (auto_rotation_state & AR_NOT_SUPPORTED)) {
323 // If there is no auto rotation sensor or rotation is not supported in
324 // the current configuration, then we can assume that this is a desktop
325 // or a traditional laptop.
326 if (!reason)
327 return true;
328
329 *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
330 : "AR_NOT_SUPPORTED\n";
331 result = true;
332 }
333 }
334
335 const GUID KEYBOARD_CLASS_GUID = {
336 0x4D36E96B,
337 0xE325,
338 0x11CE,
339 {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}};
340
341 // Query for all the keyboard devices.
342 HDEVINFO device_info = SetupDiGetClassDevs(&KEYBOARD_CLASS_GUID, nullptr,
343 nullptr, DIGCF_PRESENT);
344 if (device_info == INVALID_HANDLE_VALUE) {
345 if (reason)
346 *reason += "No keyboard info\n";
347 return result;
348 }
349
350 // Enumerate all keyboards and look for ACPI\PNP and HID\VID devices. If
351 // the count is more than 1 we assume that a keyboard is present. This is
352 // under the assumption that there will always be one keyboard device.
353 for (DWORD i = 0;; ++i) {
354 SP_DEVINFO_DATA device_info_data = {0};
355 device_info_data.cbSize = sizeof(device_info_data);
356 if (!SetupDiEnumDeviceInfo(device_info, i, &device_info_data))
357 break;
358
359 // Get the device ID.
360 wchar_t device_id[MAX_DEVICE_ID_LEN];
361 CONFIGRET status = CM_Get_Device_ID(device_info_data.DevInst, device_id,
362 MAX_DEVICE_ID_LEN, 0);
363 if (status == CR_SUCCESS) {
364 // To reduce the scope of the hack we only look for ACPI and HID\\VID
365 // prefixes in the keyboard device ids.
366 if (StartsWith(device_id, L"ACPI", CompareCase::INSENSITIVE_ASCII) ||
367 StartsWith(device_id, L"HID\\VID", CompareCase::INSENSITIVE_ASCII)) {
368 if (reason) {
369 *reason += "device: ";
370 *reason += WideToUTF8(device_id);
371 *reason += '\n';
372 }
373 // The heuristic we are using is to check the count of keyboards and
374 // return true if the API's report one or more keyboards. Please note
375 // that this will break for non keyboard devices which expose a
376 // keyboard PDO.
377 result = true;
378 }
379 }
380 }
381 return result;
382 }
383
384 static bool g_crash_on_process_detach = false;
385
GetUserSidString(std::wstring * user_sid)386 bool GetUserSidString(std::wstring* user_sid) {
387 absl::optional<AccessToken> token = AccessToken::FromCurrentProcess();
388 if (!token)
389 return false;
390 absl::optional<std::wstring> sid_string = token->User().ToSddlString();
391 if (!sid_string)
392 return false;
393 *user_sid = *sid_string;
394 return true;
395 }
396
397 class ScopedAllowBlockingForUserAccountControl : public ScopedAllowBlocking {};
398
UserAccountControlIsEnabled()399 bool UserAccountControlIsEnabled() {
400 // This can be slow if Windows ends up going to disk. Should watch this key
401 // for changes and only read it once, preferably on the file thread.
402 // http://code.google.com/p/chromium/issues/detail?id=61644
403 ScopedAllowBlockingForUserAccountControl allow_blocking;
404
405 RegKey key(HKEY_LOCAL_MACHINE,
406 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
407 KEY_READ);
408 DWORD uac_enabled;
409 if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS) {
410 return true;
411 }
412 // Users can set the EnableLUA value to something arbitrary, like 2, which
413 // Vista will treat as UAC enabled, so we make sure it is not set to 0.
414 return (uac_enabled != 0);
415 }
416
SetBooleanValueForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,bool property_bool_value)417 bool SetBooleanValueForPropertyStore(IPropertyStore* property_store,
418 const PROPERTYKEY& property_key,
419 bool property_bool_value) {
420 ScopedPropVariant property_value;
421 if (FAILED(InitPropVariantFromBoolean(property_bool_value,
422 property_value.Receive()))) {
423 return false;
424 }
425
426 return SetPropVariantValueForPropertyStore(property_store, property_key,
427 property_value);
428 }
429
SetStringValueForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,const wchar_t * property_string_value)430 bool SetStringValueForPropertyStore(IPropertyStore* property_store,
431 const PROPERTYKEY& property_key,
432 const wchar_t* property_string_value) {
433 ScopedPropVariant property_value;
434 if (FAILED(InitPropVariantFromString(property_string_value,
435 property_value.Receive()))) {
436 return false;
437 }
438
439 return SetPropVariantValueForPropertyStore(property_store, property_key,
440 property_value);
441 }
442
SetClsidForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,const CLSID & property_clsid_value)443 bool SetClsidForPropertyStore(IPropertyStore* property_store,
444 const PROPERTYKEY& property_key,
445 const CLSID& property_clsid_value) {
446 ScopedPropVariant property_value;
447 if (FAILED(InitPropVariantFromCLSID(property_clsid_value,
448 property_value.Receive()))) {
449 return false;
450 }
451
452 return SetPropVariantValueForPropertyStore(property_store, property_key,
453 property_value);
454 }
455
SetAppIdForPropertyStore(IPropertyStore * property_store,const wchar_t * app_id)456 bool SetAppIdForPropertyStore(IPropertyStore* property_store,
457 const wchar_t* app_id) {
458 // App id should be less than 128 chars and contain no space. And recommended
459 // format is CompanyName.ProductName[.SubProduct.ProductNumber].
460 // See
461 // https://docs.microsoft.com/en-us/windows/win32/shell/appids#how-to-form-an-application-defined-appusermodelid
462 DCHECK_LT(lstrlen(app_id), 128);
463 DCHECK_EQ(wcschr(app_id, L' '), nullptr);
464
465 return SetStringValueForPropertyStore(property_store, PKEY_AppUserModel_ID,
466 app_id);
467 }
468
469 static const wchar_t kAutoRunKeyPath[] =
470 L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
471
AddCommandToAutoRun(HKEY root_key,const std::wstring & name,const std::wstring & command)472 bool AddCommandToAutoRun(HKEY root_key,
473 const std::wstring& name,
474 const std::wstring& command) {
475 RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
476 return (autorun_key.WriteValue(name.c_str(), command.c_str()) ==
477 ERROR_SUCCESS);
478 }
479
RemoveCommandFromAutoRun(HKEY root_key,const std::wstring & name)480 bool RemoveCommandFromAutoRun(HKEY root_key, const std::wstring& name) {
481 RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
482 return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS);
483 }
484
ReadCommandFromAutoRun(HKEY root_key,const std::wstring & name,std::wstring * command)485 bool ReadCommandFromAutoRun(HKEY root_key,
486 const std::wstring& name,
487 std::wstring* command) {
488 RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE);
489 return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS);
490 }
491
SetShouldCrashOnProcessDetach(bool crash)492 void SetShouldCrashOnProcessDetach(bool crash) {
493 g_crash_on_process_detach = crash;
494 }
495
ShouldCrashOnProcessDetach()496 bool ShouldCrashOnProcessDetach() {
497 return g_crash_on_process_detach;
498 }
499
SetAbortBehaviorForCrashReporting()500 void SetAbortBehaviorForCrashReporting() {
501 // Prevent CRT's abort code from prompting a dialog or trying to "report" it.
502 // Disabling the _CALL_REPORTFAULT behavior is important since otherwise it
503 // has the sideffect of clearing our exception filter, which means we
504 // don't get any crash.
505 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
506
507 // Set a SIGABRT handler for good measure. We will crash even if the default
508 // is left in place, however this allows us to crash earlier. And it also
509 // lets us crash in response to code which might directly call raise(SIGABRT)
510 signal(SIGABRT, ForceCrashOnSigAbort);
511 }
512
IsTabletDevice(std::string * reason,HWND hwnd)513 bool IsTabletDevice(std::string* reason, HWND hwnd) {
514 if (IsWindows10OrGreaterTabletMode(hwnd))
515 return true;
516
517 return IsDeviceUsedAsATablet(reason);
518 }
519
520 // This method is used to set the right interactions media queries,
521 // see https://drafts.csswg.org/mediaqueries-4/#mf-interaction. It doesn't
522 // check the Windows 10 tablet mode because it doesn't reflect the actual
523 // input configuration of the device and can be manually triggered by the user
524 // independently from the hardware state.
IsDeviceUsedAsATablet(std::string * reason)525 bool IsDeviceUsedAsATablet(std::string* reason) {
526 // Once this is set, it shouldn't be overridden, and it should be the ultimate
527 // return value, so that this method returns the same result whether or not
528 // reason is NULL.
529 absl::optional<bool> ret;
530
531 if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0) {
532 if (reason) {
533 *reason += "Device does not support touch.\n";
534 ret = false;
535 } else {
536 return false;
537 }
538 }
539
540 // If the device is docked, the user is treating the device as a PC.
541 if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) {
542 if (reason) {
543 *reason += "SM_SYSTEMDOCKED\n";
544 if (!ret.has_value())
545 ret = false;
546 } else {
547 return false;
548 }
549 }
550
551 // If the device is not supporting rotation, it's unlikely to be a tablet,
552 // a convertible or a detachable.
553 // See
554 // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
555 using GetAutoRotationStateType = decltype(GetAutoRotationState)*;
556 static const auto get_auto_rotation_state_func =
557 reinterpret_cast<GetAutoRotationStateType>(
558 GetUser32FunctionPointer("GetAutoRotationState"));
559 if (get_auto_rotation_state_func) {
560 AR_STATE rotation_state = AR_ENABLED;
561 if (get_auto_rotation_state_func(&rotation_state) &&
562 (rotation_state & (AR_NOT_SUPPORTED | AR_LAPTOP | AR_NOSENSOR)) != 0)
563 return ret.has_value() ? ret.value() : false;
564 }
565
566 // PlatformRoleSlate was added in Windows 8+.
567 POWER_PLATFORM_ROLE role = GetPlatformRole();
568 bool is_tablet = false;
569 if (role == PlatformRoleMobile || role == PlatformRoleSlate) {
570 is_tablet = !GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
571 if (!is_tablet) {
572 if (reason) {
573 *reason += "Not in slate mode.\n";
574 if (!ret.has_value())
575 ret = false;
576 } else {
577 return false;
578 }
579 } else if (reason) {
580 *reason += (role == PlatformRoleMobile) ? "PlatformRoleMobile\n"
581 : "PlatformRoleSlate\n";
582 }
583 } else if (reason) {
584 *reason += "Device role is not mobile or slate.\n";
585 }
586 return ret.has_value() ? ret.value() : is_tablet;
587 }
588
IsEnrolledToDomain()589 bool IsEnrolledToDomain() {
590 return *GetDomainEnrollmentStateStorage();
591 }
592
IsDeviceRegisteredWithManagement()593 bool IsDeviceRegisteredWithManagement() {
594 // GetRegisteredWithManagementStateStorage() can be true for devices running
595 // the Home sku, however the Home sku does not allow for management of the web
596 // browser. As such, we automatically exclude devices running the Home sku.
597 if (OSInfo::GetInstance()->version_type() == SUITE_HOME)
598 return false;
599 return *GetRegisteredWithManagementStateStorage();
600 }
601
IsJoinedToAzureAD()602 bool IsJoinedToAzureAD() {
603 return *GetAzureADJoinStateStorage();
604 }
605
IsUser32AndGdi32Available()606 bool IsUser32AndGdi32Available() {
607 static auto is_user32_and_gdi32_available = []() {
608 // If win32k syscalls aren't disabled, then user32 and gdi32 are available.
609 PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {};
610 if (::GetProcessMitigationPolicy(GetCurrentProcess(),
611 ProcessSystemCallDisablePolicy, &policy,
612 sizeof(policy))) {
613 return policy.DisallowWin32kSystemCalls == 0;
614 }
615
616 return true;
617 }();
618 return is_user32_and_gdi32_available;
619 }
620
GetLoadedModulesSnapshot(HANDLE process,std::vector<HMODULE> * snapshot)621 bool GetLoadedModulesSnapshot(HANDLE process, std::vector<HMODULE>* snapshot) {
622 DCHECK(snapshot);
623 DCHECK_EQ(0u, snapshot->size());
624 snapshot->resize(128);
625
626 // We will retry at least once after first determining |bytes_required|. If
627 // the list of modules changes after we receive |bytes_required| we may retry
628 // more than once.
629 int retries_remaining = 5;
630 do {
631 DWORD bytes_required = 0;
632 // EnumProcessModules returns 'success' even if the buffer size is too
633 // small.
634 DCHECK_GE(std::numeric_limits<DWORD>::max(),
635 snapshot->size() * sizeof(HMODULE));
636 if (!::EnumProcessModules(
637 process, &(*snapshot)[0],
638 static_cast<DWORD>(snapshot->size() * sizeof(HMODULE)),
639 &bytes_required)) {
640 DPLOG(ERROR) << "::EnumProcessModules failed.";
641 return false;
642 }
643 DCHECK_EQ(0u, bytes_required % sizeof(HMODULE));
644 size_t num_modules = bytes_required / sizeof(HMODULE);
645 if (num_modules <= snapshot->size()) {
646 // Buffer size was too big, presumably because a module was unloaded.
647 snapshot->erase(snapshot->begin() + static_cast<ptrdiff_t>(num_modules),
648 snapshot->end());
649 return true;
650 } else if (num_modules == 0) {
651 DLOG(ERROR) << "Can't determine the module list size.";
652 return false;
653 } else {
654 // Buffer size was too small. Try again with a larger buffer. A little
655 // more room is given to avoid multiple expensive calls to
656 // ::EnumProcessModules() just because one module has been added.
657 snapshot->resize(num_modules + 8, nullptr);
658 }
659 } while (--retries_remaining);
660
661 DLOG(ERROR) << "Failed to enumerate modules.";
662 return false;
663 }
664
EnableFlicks(HWND hwnd)665 void EnableFlicks(HWND hwnd) {
666 ::RemoveProp(hwnd, MICROSOFT_TABLETPENSERVICE_PROPERTY);
667 }
668
DisableFlicks(HWND hwnd)669 void DisableFlicks(HWND hwnd) {
670 ::SetProp(hwnd, MICROSOFT_TABLETPENSERVICE_PROPERTY,
671 reinterpret_cast<HANDLE>(TABLET_DISABLE_FLICKS |
672 TABLET_DISABLE_FLICKFALLBACKKEYS));
673 }
674
EnableHighDPISupport()675 void EnableHighDPISupport() {
676 if (!IsUser32AndGdi32Available())
677 return;
678
679 // Enable per-monitor V2 if it is available (Win10 1703 or later).
680 if (EnablePerMonitorV2())
681 return;
682
683 // Fall back to per-monitor DPI for older versions of Win10.
684 PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_PER_MONITOR_DPI_AWARE;
685 if (!::SetProcessDpiAwareness(process_dpi_awareness)) {
686 // For windows versions where SetProcessDpiAwareness fails, try its
687 // predecessor.
688 BOOL result = ::SetProcessDPIAware();
689 DCHECK(result) << "SetProcessDPIAware failed.";
690 }
691 }
692
WStringFromGUID(const::GUID & rguid)693 std::wstring WStringFromGUID(const ::GUID& rguid) {
694 // This constant counts the number of characters in the formatted string,
695 // including the null termination character.
696 constexpr int kGuidStringCharacters =
697 1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1;
698 wchar_t guid_string[kGuidStringCharacters];
699 CHECK(SUCCEEDED(StringCchPrintfW(
700 guid_string, kGuidStringCharacters,
701 L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", rguid.Data1,
702 rguid.Data2, rguid.Data3, rguid.Data4[0], rguid.Data4[1], rguid.Data4[2],
703 rguid.Data4[3], rguid.Data4[4], rguid.Data4[5], rguid.Data4[6],
704 rguid.Data4[7])));
705 return std::wstring(guid_string, kGuidStringCharacters - 1);
706 }
707
PinUser32(NativeLibraryLoadError * error)708 bool PinUser32(NativeLibraryLoadError* error) {
709 return PinUser32Internal(error) != nullptr;
710 }
711
GetUser32FunctionPointer(const char * function_name,NativeLibraryLoadError * error)712 void* GetUser32FunctionPointer(const char* function_name,
713 NativeLibraryLoadError* error) {
714 NativeLibrary user32_module = PinUser32Internal(error);
715 if (user32_module)
716 return GetFunctionPointerFromNativeLibrary(user32_module, function_name);
717 return nullptr;
718 }
719
GetWindowObjectName(HANDLE handle)720 std::wstring GetWindowObjectName(HANDLE handle) {
721 // Get the size of the name.
722 std::wstring object_name;
723
724 DWORD size = 0;
725 ::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
726 if (!size) {
727 DPCHECK(false);
728 return object_name;
729 }
730
731 LOG_ASSERT(size % sizeof(wchar_t) == 0u);
732
733 // Query the name of the object.
734 if (!::GetUserObjectInformation(
735 handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)),
736 size, &size)) {
737 DPCHECK(false);
738 }
739
740 return object_name;
741 }
742
IsRunningUnderDesktopName(WStringPiece desktop_name)743 bool IsRunningUnderDesktopName(WStringPiece desktop_name) {
744 HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
745 if (!thread_desktop)
746 return false;
747
748 std::wstring current_desktop_name = GetWindowObjectName(thread_desktop);
749 return EqualsCaseInsensitiveASCII(AsStringPiece16(current_desktop_name),
750 AsStringPiece16(desktop_name));
751 }
752
753 // This method is used to detect whether current session is a remote session.
754 // See:
755 // https://docs.microsoft.com/en-us/windows/desktop/TermServ/detecting-the-terminal-services-environment
IsCurrentSessionRemote()756 bool IsCurrentSessionRemote() {
757 if (::GetSystemMetrics(SM_REMOTESESSION))
758 return true;
759
760 DWORD current_session_id = 0;
761
762 if (!::ProcessIdToSessionId(::GetCurrentProcessId(), ¤t_session_id))
763 return false;
764
765 static constexpr wchar_t kRdpSettingsKeyName[] =
766 L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server";
767 RegKey key(HKEY_LOCAL_MACHINE, kRdpSettingsKeyName, KEY_READ);
768 if (!key.Valid())
769 return false;
770
771 static constexpr wchar_t kGlassSessionIdValueName[] = L"GlassSessionId";
772 DWORD glass_session_id = 0;
773 if (key.ReadValueDW(kGlassSessionIdValueName, &glass_session_id) !=
774 ERROR_SUCCESS)
775 return false;
776
777 return current_session_id != glass_session_id;
778 }
779
780 #if !defined(OFFICIAL_BUILD)
IsAppVerifierEnabled(const std::wstring & process_name)781 bool IsAppVerifierEnabled(const std::wstring& process_name) {
782 RegKey key;
783
784 // Look for GlobalFlag in the IFEO\chrome.exe key. If it is present then
785 // Application Verifier or gflags.exe are configured. Most GlobalFlag
786 // settings are experimentally determined to be incompatible with renderer
787 // code integrity and a safe set is not known so any GlobalFlag entry is
788 // assumed to mean that Application Verifier (or pageheap) are enabled.
789 // The settings are propagated to both 64-bit WOW6432Node versions of the
790 // registry on 64-bit Windows, so only one check is needed.
791 return key.Open(
792 HKEY_LOCAL_MACHINE,
793 (L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File "
794 L"Execution Options\\" +
795 process_name)
796 .c_str(),
797 KEY_READ | KEY_WOW64_64KEY) == ERROR_SUCCESS &&
798 key.HasValue(L"GlobalFlag");
799 }
800 #endif // !defined(OFFICIAL_BUILD)
801
IsAppVerifierLoaded()802 bool IsAppVerifierLoaded() {
803 return GetModuleHandleA(kApplicationVerifierDllName);
804 }
805
ScopedDomainStateForTesting(bool state)806 ScopedDomainStateForTesting::ScopedDomainStateForTesting(bool state)
807 : initial_state_(IsEnrolledToDomain()) {
808 *GetDomainEnrollmentStateStorage() = state;
809 }
810
~ScopedDomainStateForTesting()811 ScopedDomainStateForTesting::~ScopedDomainStateForTesting() {
812 *GetDomainEnrollmentStateStorage() = initial_state_;
813 }
814
815 ScopedDeviceRegisteredWithManagementForTesting::
ScopedDeviceRegisteredWithManagementForTesting(bool state)816 ScopedDeviceRegisteredWithManagementForTesting(bool state)
817 : initial_state_(IsDeviceRegisteredWithManagement()) {
818 *GetRegisteredWithManagementStateStorage() = state;
819 }
820
821 ScopedDeviceRegisteredWithManagementForTesting::
~ScopedDeviceRegisteredWithManagementForTesting()822 ~ScopedDeviceRegisteredWithManagementForTesting() {
823 *GetRegisteredWithManagementStateStorage() = initial_state_;
824 }
825
ScopedAzureADJoinStateForTesting(bool state)826 ScopedAzureADJoinStateForTesting::ScopedAzureADJoinStateForTesting(bool state)
827 : initial_state_(std::exchange(*GetAzureADJoinStateStorage(), state)) {}
828
~ScopedAzureADJoinStateForTesting()829 ScopedAzureADJoinStateForTesting::~ScopedAzureADJoinStateForTesting() {
830 *GetAzureADJoinStateStorage() = initial_state_;
831 }
832
833 } // namespace win
834 } // namespace base
835