Home | History | Annotate | Download | only in base
      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 
     16 namespace 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 
     24 const PROPERTYKEY kPKEYAppUserModelID =
     25     { { 0x9F4C2855, 0x9F79, 0x4B39,
     26     { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, } }, 5 };
     27 
     28 void 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 
     41 WinVersion 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 
     80 void 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 
     98 bool 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 
    169 bool 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 
    198 bool 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)
    256 WNDPROC 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 
    266 void* 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 
    272 void* 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.
    278 static const wchar_t* const kHandlerKey = L"__ORIGINAL_MESSAGE_HANDLER__";
    279 
    280 bool 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 
    286 bool 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 
    297 bool 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 
    312 WNDPROC GetSuperclassWNDPROC(HWND window) {
    313   return reinterpret_cast<WNDPROC>(GetProp(window, kHandlerKey));
    314 }
    315 
    316 #pragma warning(pop)
    317 
    318 bool IsShiftPressed() {
    319   return (::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000;
    320 }
    321 
    322 bool IsCtrlPressed() {
    323   return (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000;
    324 }
    325 
    326 bool IsAltPressed() {
    327   return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000;
    328 }
    329 
    330 std::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 
    351 bool 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 
    362 std::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 
    380 std::wstring FormatLastWin32Error() {
    381   return FormatMessage(GetLastError());
    382 }
    383 
    384 WORD KeyboardCodeToWin(base::KeyboardCode keycode) {
    385   return static_cast<WORD>(keycode);
    386 }
    387 
    388 base::KeyboardCode WinToKeyboardCode(WORD keycode) {
    389   return static_cast<base::KeyboardCode>(keycode);
    390 }
    391 
    392 bool 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 //
    420 extern char VisualStudio2005ServicePack1Detection[10];
    421 COMPILE_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 \
    430 set it as your default include path to build this library. You can grab it by \
    431 searching for "download windows sdk 2008" in your favorite web search engine.  \
    432 Also make sure you register the SDK with Visual Studio, by selecting \
    433 "Integrate Windows SDK with Visual Studio 2005" from the Windows SDK \
    434 menu (see Start - All Programs - Microsoft Windows SDK - \
    435 Visual Studio Registration).
    436 #endif
    437