1// Copyright (c) 2006-2008 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_util.h"
6
7#include <propvarutil.h>
8#include <sddl.h>
9
10#include "base/logging.h"
11#include "base/registry.h"
12#include "base/scoped_handle.h"
13#include "base/scoped_ptr.h"
14#include "base/string_util.h"
15
16namespace win_util {
17
18#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(struct_name, member) \
19    offsetof(struct_name, member) + \
20    (sizeof static_cast<struct_name*>(NULL)->member)
21#define NONCLIENTMETRICS_SIZE_PRE_VISTA \
22    SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont)
23
24const PROPERTYKEY kPKEYAppUserModelID =
25    { { 0x9F4C2855, 0x9F79, 0x4B39,
26    { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, } }, 5 };
27
28void GetNonClientMetrics(NONCLIENTMETRICS* metrics) {
29  DCHECK(metrics);
30
31  static const UINT SIZEOF_NONCLIENTMETRICS =
32      (GetWinVersion() >= WINVERSION_VISTA) ?
33      sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
34  metrics->cbSize = SIZEOF_NONCLIENTMETRICS;
35  const bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
36                                              SIZEOF_NONCLIENTMETRICS, metrics,
37                                              0);
38  DCHECK(success);
39}
40
41WinVersion GetWinVersion() {
42  static bool checked_version = false;
43  static WinVersion win_version = WINVERSION_PRE_2000;
44  if (!checked_version) {
45    OSVERSIONINFOEX version_info;
46    version_info.dwOSVersionInfoSize = sizeof version_info;
47    GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
48    if (version_info.dwMajorVersion == 5) {
49      switch (version_info.dwMinorVersion) {
50        case 0:
51          win_version = WINVERSION_2000;
52          break;
53        case 1:
54          win_version = WINVERSION_XP;
55          break;
56        case 2:
57        default:
58          win_version = WINVERSION_SERVER_2003;
59          break;
60      }
61    } else if (version_info.dwMajorVersion == 6) {
62      if (version_info.wProductType != VER_NT_WORKSTATION) {
63        // 2008 is 6.0, and 2008 R2 is 6.1.
64        win_version = WINVERSION_2008;
65      } else {
66        if (version_info.dwMinorVersion == 0) {
67          win_version = WINVERSION_VISTA;
68        } else {
69          win_version = WINVERSION_WIN7;
70        }
71      }
72    } else if (version_info.dwMajorVersion > 6) {
73      win_version = WINVERSION_WIN7;
74    }
75    checked_version = true;
76  }
77  return win_version;
78}
79
80void GetServicePackLevel(int* major, int* minor) {
81  DCHECK(major && minor);
82  static bool checked_version = false;
83  static int service_pack_major = -1;
84  static int service_pack_minor = -1;
85  if (!checked_version) {
86    OSVERSIONINFOEX version_info = {0};
87    version_info.dwOSVersionInfoSize = sizeof(version_info);
88    GetVersionEx(reinterpret_cast<OSVERSIONINFOW*>(&version_info));
89    service_pack_major = version_info.wServicePackMajor;
90    service_pack_minor = version_info.wServicePackMinor;
91    checked_version = true;
92  }
93
94  *major = service_pack_major;
95  *minor = service_pack_minor;
96}
97
98bool AddAccessToKernelObject(HANDLE handle, WELL_KNOWN_SID_TYPE known_sid,
99                             ACCESS_MASK access) {
100  PSECURITY_DESCRIPTOR descriptor = NULL;
101  PACL old_dacl = NULL;
102  PACL new_dacl = NULL;
103
104  if (ERROR_SUCCESS != GetSecurityInfo(handle, SE_KERNEL_OBJECT,
105            DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl, NULL,
106            &descriptor))
107    return false;
108
109  BYTE sid[SECURITY_MAX_SID_SIZE] = {0};
110  DWORD size_sid = SECURITY_MAX_SID_SIZE;
111
112  if (known_sid == WinSelfSid) {
113    // We hijack WinSelfSid when we want to add the current user instead of
114    // a known sid.
115    HANDLE token = NULL;
116    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
117      LocalFree(descriptor);
118      return false;
119    }
120
121    DWORD size = sizeof(TOKEN_USER) + size_sid;
122    scoped_array<BYTE> token_user_bytes(new BYTE[size]);
123    TOKEN_USER* token_user =
124        reinterpret_cast<TOKEN_USER*>(token_user_bytes.get());
125    BOOL ret = GetTokenInformation(token, TokenUser, token_user, size, &size);
126
127    CloseHandle(token);
128
129    if (!ret) {
130      LocalFree(descriptor);
131      return false;
132    }
133    memcpy(sid, token_user->User.Sid, size_sid);
134  } else {
135    if (!CreateWellKnownSid(known_sid , NULL, sid, &size_sid)) {
136      LocalFree(descriptor);
137      return false;
138    }
139  }
140
141  EXPLICIT_ACCESS new_access = {0};
142  new_access.grfAccessMode = GRANT_ACCESS;
143  new_access.grfAccessPermissions = access;
144  new_access.grfInheritance = NO_INHERITANCE;
145
146  new_access.Trustee.pMultipleTrustee = NULL;
147  new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
148  new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
149  new_access.Trustee.ptstrName = reinterpret_cast<LPWSTR>(&sid);
150
151  if (ERROR_SUCCESS != SetEntriesInAcl(1, &new_access, old_dacl, &new_dacl)) {
152    LocalFree(descriptor);
153    return false;
154  }
155
156  DWORD result = SetSecurityInfo(handle, SE_KERNEL_OBJECT,
157                                 DACL_SECURITY_INFORMATION, NULL, NULL,
158                                 new_dacl, NULL);
159
160  LocalFree(new_dacl);
161  LocalFree(descriptor);
162
163  if (ERROR_SUCCESS != result)
164    return false;
165
166  return true;
167}
168
169bool GetUserSidString(std::wstring* user_sid) {
170  // Get the current token.
171  HANDLE token = NULL;
172  if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
173    return false;
174  ScopedHandle token_scoped(token);
175
176  DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
177  scoped_array<BYTE> user_bytes(new BYTE[size]);
178  TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes.get());
179
180  if (!::GetTokenInformation(token, TokenUser, user, size, &size))
181    return false;
182
183  if (!user->User.Sid)
184    return false;
185
186  // Convert the data to a string.
187  wchar_t* sid_string;
188  if (!::ConvertSidToStringSid(user->User.Sid, &sid_string))
189    return false;
190
191  *user_sid = sid_string;
192
193  ::LocalFree(sid_string);
194
195  return true;
196}
197
198bool GetLogonSessionOnlyDACL(SECURITY_DESCRIPTOR** security_descriptor) {
199  // Get the current token.
200  HANDLE token = NULL;
201  if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
202    return false;
203  ScopedHandle token_scoped(token);
204
205  // Get the size of the TokenGroups structure.
206  DWORD size = 0;
207  BOOL result = GetTokenInformation(token, TokenGroups, NULL,  0, &size);
208  if (result != FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
209    return false;
210
211  // Get the data.
212  scoped_array<char> token_groups_chars(new char[size]);
213  TOKEN_GROUPS* token_groups =
214      reinterpret_cast<TOKEN_GROUPS*>(token_groups_chars.get());
215
216  if (!GetTokenInformation(token, TokenGroups, token_groups, size, &size))
217    return false;
218
219  // Look for the logon sid.
220  SID* logon_sid = NULL;
221  for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
222    if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) {
223        logon_sid = static_cast<SID*>(token_groups->Groups[i].Sid);
224        break;
225    }
226  }
227
228  if (!logon_sid)
229    return false;
230
231  // Convert the data to a string.
232  wchar_t* sid_string;
233  if (!ConvertSidToStringSid(logon_sid, &sid_string))
234    return false;
235
236  static const wchar_t dacl_format[] = L"D:(A;OICI;GA;;;%ls)";
237  wchar_t dacl[SECURITY_MAX_SID_SIZE + arraysize(dacl_format) + 1] = {0};
238  wsprintf(dacl, dacl_format, sid_string);
239
240  LocalFree(sid_string);
241
242  // Convert the string to a security descriptor
243  if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
244      dacl,
245      SDDL_REVISION_1,
246      reinterpret_cast<PSECURITY_DESCRIPTOR*>(security_descriptor),
247      NULL)) {
248    return false;
249  }
250
251  return true;
252}
253
254#pragma warning(push)
255#pragma warning(disable:4312 4244)
256WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) {
257  // The reason we don't return the SetwindowLongPtr() value is that it returns
258  // the orignal window procedure and not the current one. I don't know if it is
259  // a bug or an intended feature.
260  WNDPROC oldwindow_proc =
261      reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwnd, GWLP_WNDPROC));
262  SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(proc));
263  return oldwindow_proc;
264}
265
266void* SetWindowUserData(HWND hwnd, void* user_data) {
267  return
268      reinterpret_cast<void*>(SetWindowLongPtr(hwnd, GWLP_USERDATA,
269          reinterpret_cast<LONG_PTR>(user_data)));
270}
271
272void* GetWindowUserData(HWND hwnd) {
273  return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
274}
275
276// Maps to the WNDPROC for a window that was active before the subclass was
277// installed.
278static const wchar_t* const kHandlerKey = L"__ORIGINAL_MESSAGE_HANDLER__";
279
280bool IsSubclassed(HWND window, WNDPROC subclass_proc) {
281  WNDPROC original_handler =
282      reinterpret_cast<WNDPROC>(GetWindowLongPtr(window, GWLP_WNDPROC));
283  return original_handler == subclass_proc;
284}
285
286bool Subclass(HWND window, WNDPROC subclass_proc) {
287  WNDPROC original_handler =
288      reinterpret_cast<WNDPROC>(GetWindowLongPtr(window, GWLP_WNDPROC));
289  if (original_handler != subclass_proc) {
290    win_util::SetWindowProc(window, subclass_proc);
291    SetProp(window, kHandlerKey, original_handler);
292    return true;
293  }
294  return false;
295}
296
297bool Unsubclass(HWND window, WNDPROC subclass_proc) {
298  WNDPROC current_handler =
299      reinterpret_cast<WNDPROC>(GetWindowLongPtr(window, GWLP_WNDPROC));
300  if (current_handler == subclass_proc) {
301    HANDLE original_handler = GetProp(window, kHandlerKey);
302    if (original_handler) {
303      RemoveProp(window, kHandlerKey);
304      win_util::SetWindowProc(window,
305                              reinterpret_cast<WNDPROC>(original_handler));
306      return true;
307    }
308  }
309  return false;
310}
311
312WNDPROC GetSuperclassWNDPROC(HWND window) {
313  return reinterpret_cast<WNDPROC>(GetProp(window, kHandlerKey));
314}
315
316#pragma warning(pop)
317
318bool IsShiftPressed() {
319  return (::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000;
320}
321
322bool IsCtrlPressed() {
323  return (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000;
324}
325
326bool IsAltPressed() {
327  return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000;
328}
329
330std::wstring GetClassName(HWND window) {
331  // GetClassNameW will return a truncated result (properly null terminated) if
332  // the given buffer is not large enough.  So, it is not possible to determine
333  // that we got the entire class name if the result is exactly equal to the
334  // size of the buffer minus one.
335  DWORD buffer_size = MAX_PATH;
336  while (true) {
337    std::wstring output;
338    DWORD size_ret =
339        GetClassNameW(window, WriteInto(&output, buffer_size), buffer_size);
340    if (size_ret == 0)
341      break;
342    if (size_ret < (buffer_size - 1)) {
343      output.resize(size_ret);
344      return output;
345    }
346    buffer_size *= 2;
347  }
348  return std::wstring();  // error
349}
350
351bool UserAccountControlIsEnabled() {
352  RegKey key(HKEY_LOCAL_MACHINE,
353      L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System");
354  DWORD uac_enabled;
355  if (!key.ReadValueDW(L"EnableLUA", &uac_enabled))
356    return true;
357  // Users can set the EnableLUA value to something arbitrary, like 2, which
358  // Vista will treat as UAC enabled, so we make sure it is not set to 0.
359  return (uac_enabled != 0);
360}
361
362std::wstring FormatMessage(unsigned messageid) {
363  wchar_t* string_buffer = NULL;
364  unsigned string_length = ::FormatMessage(
365      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
366      FORMAT_MESSAGE_IGNORE_INSERTS, NULL, messageid, 0,
367      reinterpret_cast<wchar_t *>(&string_buffer), 0, NULL);
368
369  std::wstring formatted_string;
370  if (string_buffer) {
371    formatted_string = string_buffer;
372    LocalFree(reinterpret_cast<HLOCAL>(string_buffer));
373  } else {
374    // The formating failed. simply convert the message value into a string.
375    SStringPrintf(&formatted_string, L"message number %d", messageid);
376  }
377  return formatted_string;
378}
379
380std::wstring FormatLastWin32Error() {
381  return FormatMessage(GetLastError());
382}
383
384WORD KeyboardCodeToWin(base::KeyboardCode keycode) {
385  return static_cast<WORD>(keycode);
386}
387
388base::KeyboardCode WinToKeyboardCode(WORD keycode) {
389  return static_cast<base::KeyboardCode>(keycode);
390}
391
392bool SetAppIdForPropertyStore(IPropertyStore* property_store,
393                              const wchar_t* app_id) {
394  DCHECK(property_store);
395
396  // App id should be less than 128 chars and contain no space. And recommended
397  // format is CompanyName.ProductName[.SubProduct.ProductNumber].
398  // See http://msdn.microsoft.com/en-us/library/dd378459%28VS.85%29.aspx
399  DCHECK(lstrlen(app_id) < 128 && wcschr(app_id, L' ') == NULL);
400
401  PROPVARIANT property_value;
402  if (FAILED(InitPropVariantFromString(app_id, &property_value)))
403    return false;
404
405  HRESULT result = property_store->SetValue(kPKEYAppUserModelID,
406                                            property_value);
407  if (S_OK == result)
408    result = property_store->Commit();
409
410  PropVariantClear(&property_value);
411  return SUCCEEDED(result);
412}
413
414}  // namespace win_util
415
416#ifdef _MSC_VER
417//
418// If the ASSERT below fails, please install Visual Studio 2005 Service Pack 1.
419//
420extern char VisualStudio2005ServicePack1Detection[10];
421COMPILE_ASSERT(sizeof(&VisualStudio2005ServicePack1Detection) == sizeof(void*),
422               VS2005SP1Detect);
423//
424// Chrome requires at least Service Pack 1 for Visual Studio 2005.
425//
426#endif  // _MSC_VER
427
428#ifndef COPY_FILE_COPY_SYMLINK
429#error You must install the Windows 2008 or Vista Software Development Kit and \
430set it as your default include path to build this library. You can grab it by \
431searching for "download windows sdk 2008" in your favorite web search engine.  \
432Also make sure you register the SDK with Visual Studio, by selecting \
433"Integrate Windows SDK with Visual Studio 2005" from the Windows SDK \
434menu (see Start - All Programs - Microsoft Windows SDK - \
435Visual Studio Registration).
436#endif
437