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