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