1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 <lm.h> 9 #include <powrprof.h> 10 #include <shellapi.h> 11 #include <shlobj.h> 12 #include <shobjidl.h> // Must be before propkey. 13 #include <initguid.h> 14 #include <propkey.h> 15 #include <propvarutil.h> 16 #include <sddl.h> 17 #include <signal.h> 18 #include <stdlib.h> 19 20 #include "base/lazy_instance.h" 21 #include "base/logging.h" 22 #include "base/memory/scoped_ptr.h" 23 #include "base/strings/string_util.h" 24 #include "base/strings/stringprintf.h" 25 #include "base/threading/thread_restrictions.h" 26 #include "base/win/metro.h" 27 #include "base/win/registry.h" 28 #include "base/win/scoped_co_mem.h" 29 #include "base/win/scoped_handle.h" 30 #include "base/win/scoped_propvariant.h" 31 #include "base/win/windows_version.h" 32 33 namespace { 34 35 // Sets the value of |property_key| to |property_value| in |property_store|. 36 bool SetPropVariantValueForPropertyStore( 37 IPropertyStore* property_store, 38 const PROPERTYKEY& property_key, 39 const base::win::ScopedPropVariant& property_value) { 40 DCHECK(property_store); 41 42 HRESULT result = property_store->SetValue(property_key, property_value.get()); 43 if (result == S_OK) 44 result = property_store->Commit(); 45 return SUCCEEDED(result); 46 } 47 48 void __cdecl ForceCrashOnSigAbort(int) { 49 *((int*)0) = 0x1337; 50 } 51 52 const wchar_t kWindows8OSKRegPath[] = 53 L"Software\\Classes\\CLSID\\{054AAE20-4BEA-4347-8A35-64A533254A9D}" 54 L"\\LocalServer32"; 55 56 } // namespace 57 58 namespace base { 59 namespace win { 60 61 static bool g_crash_on_process_detach = false; 62 63 #define NONCLIENTMETRICS_SIZE_PRE_VISTA \ 64 SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont) 65 66 void GetNonClientMetrics(NONCLIENTMETRICS* metrics) { 67 DCHECK(metrics); 68 69 static const UINT SIZEOF_NONCLIENTMETRICS = 70 (base::win::GetVersion() >= base::win::VERSION_VISTA) ? 71 sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA; 72 metrics->cbSize = SIZEOF_NONCLIENTMETRICS; 73 const bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 74 SIZEOF_NONCLIENTMETRICS, metrics, 75 0); 76 DCHECK(success); 77 } 78 79 bool GetUserSidString(std::wstring* user_sid) { 80 // Get the current token. 81 HANDLE token = NULL; 82 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) 83 return false; 84 base::win::ScopedHandle token_scoped(token); 85 86 DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; 87 scoped_ptr<BYTE[]> user_bytes(new BYTE[size]); 88 TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes.get()); 89 90 if (!::GetTokenInformation(token, TokenUser, user, size, &size)) 91 return false; 92 93 if (!user->User.Sid) 94 return false; 95 96 // Convert the data to a string. 97 wchar_t* sid_string; 98 if (!::ConvertSidToStringSid(user->User.Sid, &sid_string)) 99 return false; 100 101 *user_sid = sid_string; 102 103 ::LocalFree(sid_string); 104 105 return true; 106 } 107 108 bool IsShiftPressed() { 109 return (::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000; 110 } 111 112 bool IsCtrlPressed() { 113 return (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000; 114 } 115 116 bool IsAltPressed() { 117 return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000; 118 } 119 120 bool IsAltGrPressed() { 121 return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000 && 122 (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000; 123 } 124 125 bool UserAccountControlIsEnabled() { 126 // This can be slow if Windows ends up going to disk. Should watch this key 127 // for changes and only read it once, preferably on the file thread. 128 // http://code.google.com/p/chromium/issues/detail?id=61644 129 base::ThreadRestrictions::ScopedAllowIO allow_io; 130 131 base::win::RegKey key(HKEY_LOCAL_MACHINE, 132 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", 133 KEY_READ); 134 DWORD uac_enabled; 135 if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS) 136 return true; 137 // Users can set the EnableLUA value to something arbitrary, like 2, which 138 // Vista will treat as UAC enabled, so we make sure it is not set to 0. 139 return (uac_enabled != 0); 140 } 141 142 bool SetBooleanValueForPropertyStore(IPropertyStore* property_store, 143 const PROPERTYKEY& property_key, 144 bool property_bool_value) { 145 ScopedPropVariant property_value; 146 if (FAILED(InitPropVariantFromBoolean(property_bool_value, 147 property_value.Receive()))) { 148 return false; 149 } 150 151 return SetPropVariantValueForPropertyStore(property_store, 152 property_key, 153 property_value); 154 } 155 156 bool SetStringValueForPropertyStore(IPropertyStore* property_store, 157 const PROPERTYKEY& property_key, 158 const wchar_t* property_string_value) { 159 ScopedPropVariant property_value; 160 if (FAILED(InitPropVariantFromString(property_string_value, 161 property_value.Receive()))) { 162 return false; 163 } 164 165 return SetPropVariantValueForPropertyStore(property_store, 166 property_key, 167 property_value); 168 } 169 170 bool SetAppIdForPropertyStore(IPropertyStore* property_store, 171 const wchar_t* app_id) { 172 // App id should be less than 64 chars and contain no space. And recommended 173 // format is CompanyName.ProductName[.SubProduct.ProductNumber]. 174 // See http://msdn.microsoft.com/en-us/library/dd378459%28VS.85%29.aspx 175 DCHECK(lstrlen(app_id) < 64 && wcschr(app_id, L' ') == NULL); 176 177 return SetStringValueForPropertyStore(property_store, 178 PKEY_AppUserModel_ID, 179 app_id); 180 } 181 182 static const char16 kAutoRunKeyPath[] = 183 L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"; 184 185 bool AddCommandToAutoRun(HKEY root_key, const string16& name, 186 const string16& command) { 187 base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE); 188 return (autorun_key.WriteValue(name.c_str(), command.c_str()) == 189 ERROR_SUCCESS); 190 } 191 192 bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name) { 193 base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE); 194 return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS); 195 } 196 197 bool ReadCommandFromAutoRun(HKEY root_key, 198 const string16& name, 199 string16* command) { 200 base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE); 201 return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS); 202 } 203 204 void SetShouldCrashOnProcessDetach(bool crash) { 205 g_crash_on_process_detach = crash; 206 } 207 208 bool ShouldCrashOnProcessDetach() { 209 return g_crash_on_process_detach; 210 } 211 212 void SetAbortBehaviorForCrashReporting() { 213 // Prevent CRT's abort code from prompting a dialog or trying to "report" it. 214 // Disabling the _CALL_REPORTFAULT behavior is important since otherwise it 215 // has the sideffect of clearing our exception filter, which means we 216 // don't get any crash. 217 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); 218 219 // Set a SIGABRT handler for good measure. We will crash even if the default 220 // is left in place, however this allows us to crash earlier. And it also 221 // lets us crash in response to code which might directly call raise(SIGABRT) 222 signal(SIGABRT, ForceCrashOnSigAbort); 223 } 224 225 bool IsTabletDevice() { 226 if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0) 227 return false; 228 229 base::win::Version version = base::win::GetVersion(); 230 if (version == base::win::VERSION_XP) 231 return (GetSystemMetrics(SM_TABLETPC) != 0); 232 233 // If the device is docked, the user is treating the device as a PC. 234 if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) 235 return false; 236 237 // PlatformRoleSlate was only added in Windows 8, but prior to Win8 it is 238 // still possible to check for a mobile power profile. 239 POWER_PLATFORM_ROLE role = PowerDeterminePlatformRole(); 240 bool mobile_power_profile = (role == PlatformRoleMobile); 241 bool slate_power_profile = false; 242 if (version >= base::win::VERSION_WIN8) 243 slate_power_profile = (role == PlatformRoleSlate); 244 245 if (mobile_power_profile || slate_power_profile) 246 return (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); 247 248 return false; 249 } 250 251 bool DisplayVirtualKeyboard() { 252 if (base::win::GetVersion() < base::win::VERSION_WIN8) 253 return false; 254 255 static base::LazyInstance<string16>::Leaky osk_path = 256 LAZY_INSTANCE_INITIALIZER; 257 258 if (osk_path.Get().empty()) { 259 // We need to launch TabTip.exe from the location specified under the 260 // LocalServer32 key for the {{054AAE20-4BEA-4347-8A35-64A533254A9D}} 261 // CLSID. 262 // TabTip.exe is typically found at 263 // c:\program files\common files\microsoft shared\ink on English Windows. 264 // We don't want to launch TabTip.exe from 265 // c:\program files (x86)\common files\microsoft shared\ink. This path is 266 // normally found on 64 bit Windows. 267 base::win::RegKey key(HKEY_LOCAL_MACHINE, 268 kWindows8OSKRegPath, 269 KEY_READ | KEY_WOW64_64KEY); 270 DWORD osk_path_length = 1024; 271 if (key.ReadValue(NULL, 272 WriteInto(&osk_path.Get(), osk_path_length), 273 &osk_path_length, 274 NULL) != ERROR_SUCCESS) { 275 DLOG(WARNING) << "Failed to read on screen keyboard path from registry"; 276 return false; 277 } 278 size_t common_program_files_offset = 279 osk_path.Get().find(L"%CommonProgramFiles%"); 280 // Typically the path to TabTip.exe read from the registry will start with 281 // %CommonProgramFiles% which needs to be replaced with the corrsponding 282 // expanded string. 283 // If the path does not begin with %CommonProgramFiles% we use it as is. 284 if (common_program_files_offset != string16::npos) { 285 // Preserve the beginning quote in the path. 286 osk_path.Get().erase(common_program_files_offset, 287 wcslen(L"%CommonProgramFiles%")); 288 // The path read from the registry contains the %CommonProgramFiles% 289 // environment variable prefix. On 64 bit Windows the SHGetKnownFolderPath 290 // function returns the common program files path with the X86 suffix for 291 // the FOLDERID_ProgramFilesCommon value. 292 // To get the correct path to TabTip.exe we first read the environment 293 // variable CommonProgramW6432 which points to the desired common 294 // files path. Failing that we fallback to the SHGetKnownFolderPath API. 295 296 // We then replace the %CommonProgramFiles% value with the actual common 297 // files path found in the process. 298 string16 common_program_files_path; 299 scoped_ptr<wchar_t[]> common_program_files_wow6432; 300 DWORD buffer_size = 301 GetEnvironmentVariable(L"CommonProgramW6432", NULL, 0); 302 if (buffer_size) { 303 common_program_files_wow6432.reset(new wchar_t[buffer_size]); 304 GetEnvironmentVariable(L"CommonProgramW6432", 305 common_program_files_wow6432.get(), 306 buffer_size); 307 common_program_files_path = common_program_files_wow6432.get(); 308 DCHECK(!common_program_files_path.empty()); 309 } else { 310 base::win::ScopedCoMem<wchar_t> common_program_files; 311 if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, NULL, 312 &common_program_files))) { 313 return false; 314 } 315 common_program_files_path = common_program_files; 316 } 317 318 osk_path.Get().insert(1, common_program_files_path); 319 } 320 } 321 322 HINSTANCE ret = ::ShellExecuteW(NULL, 323 L"", 324 osk_path.Get().c_str(), 325 NULL, 326 NULL, 327 SW_SHOW); 328 return reinterpret_cast<int>(ret) > 32; 329 } 330 331 bool DismissVirtualKeyboard() { 332 if (base::win::GetVersion() < base::win::VERSION_WIN8) 333 return false; 334 335 // We dismiss the virtual keyboard by generating the ESC keystroke 336 // programmatically. 337 const wchar_t kOSKClassName[] = L"IPTip_Main_Window"; 338 HWND osk = ::FindWindow(kOSKClassName, NULL); 339 if (::IsWindow(osk) && ::IsWindowEnabled(osk)) { 340 PostMessage(osk, WM_SYSCOMMAND, SC_CLOSE, 0); 341 return true; 342 } 343 return false; 344 } 345 346 typedef HWND (*MetroRootWindow) (); 347 348 enum DomainEnrollementState {UNKNOWN = -1, NOT_ENROLLED, ENROLLED}; 349 static volatile long int g_domain_state = UNKNOWN; 350 351 bool IsEnrolledToDomain() { 352 // Doesn't make any sense to retry inside a user session because joining a 353 // domain will only kick in on a restart. 354 if (g_domain_state == UNKNOWN) { 355 LPWSTR domain; 356 NETSETUP_JOIN_STATUS join_status; 357 if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success) 358 return false; 359 ::NetApiBufferFree(domain); 360 ::InterlockedCompareExchange(&g_domain_state, 361 join_status == ::NetSetupDomainName ? 362 ENROLLED : NOT_ENROLLED, 363 UNKNOWN); 364 } 365 366 return g_domain_state == ENROLLED; 367 } 368 369 void SetDomainStateForTesting(bool state) { 370 g_domain_state = state ? ENROLLED : NOT_ENROLLED; 371 } 372 373 } // namespace win 374 } // namespace base 375 376 #ifdef _MSC_VER 377 378 // There are optimizer bugs in x86 VS2012 pre-Update 1. 379 #if _MSC_VER == 1700 && defined _M_IX86 && _MSC_FULL_VER < 170051106 380 381 #pragma message("Relevant defines:") 382 #define __STR2__(x) #x 383 #define __STR1__(x) __STR2__(x) 384 #define __PPOUT__(x) "#define " #x " " __STR1__(x) 385 #if defined(_M_IX86) 386 #pragma message(__PPOUT__(_M_IX86)) 387 #endif 388 #if defined(_M_X64) 389 #pragma message(__PPOUT__(_M_X64)) 390 #endif 391 #if defined(_MSC_FULL_VER) 392 #pragma message(__PPOUT__(_MSC_FULL_VER)) 393 #endif 394 395 #pragma message("Visual Studio 2012 x86 must be updated to at least Update 1") 396 #error Must install Update 1 to Visual Studio 2012. 397 #endif 398 399 #endif // _MSC_VER 400 401