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