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>
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"
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
) {
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
,
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
) {
52 win_version
= WINVERSION_2000
;
55 win_version
= WINVERSION_XP
;
59 win_version
= WINVERSION_SERVER_2003
;
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
;
67 if (version_info
.dwMinorVersion
== 0) {
68 win_version
= WINVERSION_VISTA
;
70 win_version
= WINVERSION_WIN7
;
73 } else if (version_info
.dwMajorVersion
> 6) {
74 win_version
= WINVERSION_WIN7
;
76 checked_version
= true;
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
,
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
117 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS
, &token
)) {
118 LocalFree(descriptor
);
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
);
131 LocalFree(descriptor
);
134 memcpy(sid
, token_user
->User
.Sid
, size_sid
);
136 if (!CreateWellKnownSid(known_sid
, NULL
, sid
, &size_sid
)) {
137 LocalFree(descriptor
);
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
);
157 DWORD result
= SetSecurityInfo(handle
, SE_KERNEL_OBJECT
,
158 DACL_SECURITY_INFORMATION
, NULL
, NULL
,
162 LocalFree(descriptor
);
164 if (ERROR_SUCCESS
!= result
)
170 bool GetUserSidString(std::wstring
* user_sid
) {
171 // Get the current token.
173 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY
, &token
))
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
))
187 // Convert the data to a string.
189 if (!::ConvertSidToStringSid(user
->User
.Sid
, &sid_string
))
192 *user_sid
= sid_string
;
194 ::LocalFree(sid_string
);
199 bool GetLogonSessionOnlyDACL(SECURITY_DESCRIPTOR
** security_descriptor
) {
200 // Get the current token.
202 if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY
, &token
))
204 ScopedHandle
token_scoped(token
);
206 // Get the size of the TokenGroups structure.
208 BOOL result
= GetTokenInformation(token
, TokenGroups
, NULL
, 0, &size
);
209 if (result
!= FALSE
&& GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
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
))
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
);
232 // Convert the data to a string.
234 if (!ConvertSidToStringSid(logon_sid
, &sid_string
))
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(
247 reinterpret_cast<PSECURITY_DESCRIPTOR
*>(security_descriptor
),
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
) {
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
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
);
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
));
313 WNDPROC
GetSuperclassWNDPROC(HWND window
) {
314 return reinterpret_cast<WNDPROC
>(GetProp(window
, kHandlerKey
));
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
;
340 GetClassNameW(window
, WriteInto(&output
, buffer_size
), buffer_size
);
343 if (size_ret
< (buffer_size
- 1)) {
344 output
.resize(size_ret
);
349 return std::wstring(); // error
352 bool UserAccountControlIsEnabled() {
353 RegKey
key(HKEY_LOCAL_MACHINE
,
354 L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
357 if (!key
.ReadValueDW(L
"EnableLUA", &uac_enabled
))
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
;
373 formatted_string
= string_buffer
;
374 LocalFree(reinterpret_cast<HLOCAL
>(string_buffer
));
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
)))
407 HRESULT result
= property_store
->SetValue(kPKEYAppUserModelID
,
410 result
= property_store
->Commit();
412 PropVariantClear(&property_value
);
413 return SUCCEEDED(result
);
416 } // namespace win_util
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*),
426 // Chrome requires at least Service Pack 1 for Visual Studio 2005.
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).