Home | History | Annotate | Download | only in win
      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