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 32 namespace { 33 34 // Sets the value of |property_key| to |property_value| in |property_store|. 35 bool 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 47 void __cdecl ForceCrashOnSigAbort(int) { 48 *((int*)0) = 0x1337; 49 } 50 51 const wchar_t kWindows8OSKRegPath[] = 52 L"Software\\Classes\\CLSID\\{054AAE20-4BEA-4347-8A35-64A533254A9D}" 53 L"\\LocalServer32"; 54 55 } // namespace 56 57 namespace base { 58 namespace win { 59 60 static bool g_crash_on_process_detach = false; 61 62 #define NONCLIENTMETRICS_SIZE_PRE_VISTA \ 63 SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont) 64 65 void 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 78 bool 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 107 bool IsShiftPressed() { 108 return (::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000; 109 } 110 111 bool IsCtrlPressed() { 112 return (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000; 113 } 114 115 bool IsAltPressed() { 116 return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000; 117 } 118 119 bool IsAltGrPressed() { 120 return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000 && 121 (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000; 122 } 123 124 bool 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 141 bool 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 155 bool 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 169 bool 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 181 static const char16 kAutoRunKeyPath[] = 182 L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"; 183 184 bool 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 191 bool 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 196 bool 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 203 void SetShouldCrashOnProcessDetach(bool crash) { 204 g_crash_on_process_detach = crash; 205 } 206 207 bool ShouldCrashOnProcessDetach() { 208 return g_crash_on_process_detach; 209 } 210 211 void 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 224 bool 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 235 bool 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 315 bool 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 330 typedef HWND (*MetroRootWindow) (); 331 332 // As of this writing, GetMonitorInfo function seem to return wrong values 333 // for rcWork.left and rcWork.top in case of split screen situation inside 334 // metro mode. In order to get required values we query for core window screen 335 // coordinates. 336 // TODO(shrikant): Remove detour code once GetMonitorInfo is fixed for 8.1. 337 BOOL GetMonitorInfoWrapper(HMONITOR monitor, MONITORINFO* mi) { 338 BOOL ret = ::GetMonitorInfo(monitor, mi); 339 #if !defined(USE_ASH) 340 if (base::win::IsMetroProcess() && 341 base::win::GetVersion() >= base::win::VERSION_WIN8_1) { 342 static MetroRootWindow root_window = NULL; 343 if (!root_window) { 344 HMODULE metro = base::win::GetMetroModule(); 345 // There are apparently instances when current process is inside metro 346 // environment but metro driver dll is not loaded. 347 if (!metro) { 348 return ret; 349 } 350 root_window = reinterpret_cast<MetroRootWindow>( 351 ::GetProcAddress(metro, "GetRootWindow")); 352 } 353 ret = ::GetWindowRect(root_window(), &(mi->rcWork)); 354 } 355 #endif 356 return ret; 357 } 358 359 bool IsEnrolledToDomain() { 360 LPWSTR domain; 361 NETSETUP_JOIN_STATUS join_status; 362 if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success) 363 return false; 364 ::NetApiBufferFree(domain); 365 366 return join_status == ::NetSetupDomainName; 367 } 368 369 } // namespace win 370 } // namespace base 371 372 #ifdef _MSC_VER 373 374 // There are optimizer bugs in x86 VS2012 pre-Update 1. 375 #if _MSC_VER == 1700 && defined _M_IX86 && _MSC_FULL_VER < 170051106 376 377 #pragma message("Relevant defines:") 378 #define __STR2__(x) #x 379 #define __STR1__(x) __STR2__(x) 380 #define __PPOUT__(x) "#define " #x " " __STR1__(x) 381 #if defined(_M_IX86) 382 #pragma message(__PPOUT__(_M_IX86)) 383 #endif 384 #if defined(_M_X64) 385 #pragma message(__PPOUT__(_M_X64)) 386 #endif 387 #if defined(_MSC_FULL_VER) 388 #pragma message(__PPOUT__(_MSC_FULL_VER)) 389 #endif 390 391 #pragma message("Visual Studio 2012 x86 must be updated to at least Update 1") 392 #error Must install Update 1 to Visual Studio 2012. 393 #endif 394 395 #endif // _MSC_VER 396 397