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