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
GetNonClientMetrics(NONCLIENTMETRICS * metrics)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
GetWinVersion()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
GetServicePackLevel(int * major,int * minor)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
AddAccessToKernelObject(HANDLE handle,WELL_KNOWN_SID_TYPE known_sid,ACCESS_MASK access)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
GetUserSidString(std::wstring * user_sid)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
GetLogonSessionOnlyDACL(SECURITY_DESCRIPTOR ** security_descriptor)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)
SetWindowProc(HWND hwnd,WNDPROC proc)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
SetWindowUserData(HWND hwnd,void * user_data)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
GetWindowUserData(HWND hwnd)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
IsSubclassed(HWND window,WNDPROC subclass_proc)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
Subclass(HWND window,WNDPROC subclass_proc)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
Unsubclass(HWND window,WNDPROC subclass_proc)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
GetSuperclassWNDPROC(HWND window)312 WNDPROC GetSuperclassWNDPROC(HWND window) {
313 return reinterpret_cast<WNDPROC>(GetProp(window, kHandlerKey));
314 }
315
316 #pragma warning(pop)
317
IsShiftPressed()318 bool IsShiftPressed() {
319 return (::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000;
320 }
321
IsCtrlPressed()322 bool IsCtrlPressed() {
323 return (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000;
324 }
325
IsAltPressed()326 bool IsAltPressed() {
327 return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000;
328 }
329
GetClassName(HWND window)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
UserAccountControlIsEnabled()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
FormatMessage(unsigned messageid)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
FormatLastWin32Error()380 std::wstring FormatLastWin32Error() {
381 return FormatMessage(GetLastError());
382 }
383
KeyboardCodeToWin(base::KeyboardCode keycode)384 WORD KeyboardCodeToWin(base::KeyboardCode keycode) {
385 return static_cast<WORD>(keycode);
386 }
387
WinToKeyboardCode(WORD keycode)388 base::KeyboardCode WinToKeyboardCode(WORD keycode) {
389 return static_cast<base::KeyboardCode>(keycode);
390 }
391
SetAppIdForPropertyStore(IPropertyStore * property_store,const wchar_t * app_id)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