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