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