1 // Copyright 2010 The Chromium Authors
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/win_util.h"
6
7 #include <objbase.h>
8
9 #include <string_view>
10
11 #include "base/containers/contains.h"
12 #include "base/files/file_path.h"
13 #include "base/scoped_environment_variable_override.h"
14 #include "base/scoped_native_library.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/win/registry.h"
18 #include "base/win/scoped_co_mem.h"
19 #include "base/win/scoped_com_initializer.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 namespace base {
23 namespace win {
24
25 namespace {
26
27 // Saves the current thread's locale ID when initialized, and restores it when
28 // the instance is going out of scope.
29 class ThreadLocaleSaver {
30 public:
ThreadLocaleSaver()31 ThreadLocaleSaver() : original_locale_id_(GetThreadLocale()) {}
32
33 ThreadLocaleSaver(const ThreadLocaleSaver&) = delete;
34 ThreadLocaleSaver& operator=(const ThreadLocaleSaver&) = delete;
35
~ThreadLocaleSaver()36 ~ThreadLocaleSaver() { SetThreadLocale(original_locale_id_); }
37
38 private:
39 LCID original_locale_id_;
40 };
41
__anon87584a500202() 42 auto* csm_false = static_cast<bool (*)()>([]() -> bool { return false; });
43
__anon87584a500302() 44 auto* csm_true = static_cast<bool (*)()>([]() -> bool { return true; });
45
46 } // namespace
47
48 // The test is somewhat silly, because some bots some have UAC enabled and some
49 // have it disabled. At least we check that it does not crash.
TEST(BaseWinUtilTest,TestIsUACEnabled)50 TEST(BaseWinUtilTest, TestIsUACEnabled) {
51 UserAccountControlIsEnabled();
52 }
53
TEST(BaseWinUtilTest,TestGetUserSidString)54 TEST(BaseWinUtilTest, TestGetUserSidString) {
55 std::wstring user_sid;
56 EXPECT_TRUE(GetUserSidString(&user_sid));
57 EXPECT_TRUE(!user_sid.empty());
58 }
59
TEST(BaseWinUtilTest,TestGetLoadedModulesSnapshot)60 TEST(BaseWinUtilTest, TestGetLoadedModulesSnapshot) {
61 std::vector<HMODULE> snapshot;
62
63 ASSERT_TRUE(GetLoadedModulesSnapshot(::GetCurrentProcess(), &snapshot));
64 size_t original_snapshot_size = snapshot.size();
65 ASSERT_GT(original_snapshot_size, 0u);
66 snapshot.clear();
67
68 // Load in a new module. Pick zipfldr.dll as it is present from WinXP to
69 // Win10, including ARM64 Win10, and yet rarely used.
70 const FilePath::CharType dll_name[] = FILE_PATH_LITERAL("zipfldr.dll");
71 ASSERT_EQ(nullptr, ::GetModuleHandle(dll_name));
72
73 ScopedNativeLibrary new_dll((FilePath(dll_name)));
74 ASSERT_NE(static_cast<HMODULE>(nullptr), new_dll.get());
75 ASSERT_TRUE(GetLoadedModulesSnapshot(::GetCurrentProcess(), &snapshot));
76 ASSERT_GT(snapshot.size(), original_snapshot_size);
77 ASSERT_TRUE(Contains(snapshot, new_dll.get()));
78 }
79
TEST(BaseWinUtilTest,TestUint32ToInvalidHandle)80 TEST(BaseWinUtilTest, TestUint32ToInvalidHandle) {
81 // Ensure that INVALID_HANDLE_VALUE is preserved when going to a 32-bit value
82 // and back on 64-bit platforms.
83 uint32_t invalid_handle = HandleToUint32(INVALID_HANDLE_VALUE);
84 EXPECT_EQ(INVALID_HANDLE_VALUE, Uint32ToHandle(invalid_handle));
85 }
86
TEST(BaseWinUtilTest,WStringFromGUID)87 TEST(BaseWinUtilTest, WStringFromGUID) {
88 const GUID kGuid = {0x7698f759,
89 0xf5b0,
90 0x4328,
91 {0x92, 0x38, 0xbd, 0x70, 0x8a, 0x6d, 0xc9, 0x63}};
92 const std::wstring_view kGuidStr = L"{7698F759-F5B0-4328-9238-BD708A6DC963}";
93 auto guid_wstring = WStringFromGUID(kGuid);
94 EXPECT_EQ(guid_wstring, kGuidStr);
95 wchar_t guid_wchar[39];
96 ::StringFromGUID2(kGuid, guid_wchar, std::size(guid_wchar));
97 EXPECT_STREQ(guid_wstring.c_str(), guid_wchar);
98 ScopedCoMem<OLECHAR> clsid_string;
99 ::StringFromCLSID(kGuid, &clsid_string);
100 EXPECT_STREQ(guid_wstring.c_str(), clsid_string.get());
101 }
102
TEST(BaseWinUtilTest,GetWindowObjectName)103 TEST(BaseWinUtilTest, GetWindowObjectName) {
104 std::wstring created_desktop_name(L"test_desktop");
105 HDESK desktop_handle =
106 ::CreateDesktop(created_desktop_name.c_str(), nullptr, nullptr, 0,
107 DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS |
108 READ_CONTROL | WRITE_DAC | WRITE_OWNER,
109 nullptr);
110
111 ASSERT_NE(desktop_handle, nullptr);
112 EXPECT_EQ(created_desktop_name, GetWindowObjectName(desktop_handle));
113 ASSERT_TRUE(::CloseDesktop(desktop_handle));
114 }
115
TEST(BaseWinUtilTest,IsRunningUnderDesktopName)116 TEST(BaseWinUtilTest, IsRunningUnderDesktopName) {
117 HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
118
119 ASSERT_NE(thread_desktop, nullptr);
120 std::wstring desktop_name = GetWindowObjectName(thread_desktop);
121
122 EXPECT_TRUE(IsRunningUnderDesktopName(desktop_name));
123 EXPECT_TRUE(IsRunningUnderDesktopName(
124 AsWString(ToLowerASCII(AsStringPiece16(desktop_name)))));
125 EXPECT_TRUE(IsRunningUnderDesktopName(
126 AsWString(ToUpperASCII(AsStringPiece16(desktop_name)))));
127 EXPECT_FALSE(
128 IsRunningUnderDesktopName(desktop_name + L"_non_existent_desktop_name"));
129 }
130
TEST(BaseWinUtilTest,ExpandEnvironmentVariables)131 TEST(BaseWinUtilTest, ExpandEnvironmentVariables) {
132 constexpr char kTestEnvVar[] = "TEST_ENV_VAR";
133 constexpr char kTestEnvVarValue[] = "TEST_VALUE";
134 ScopedEnvironmentVariableOverride scoped_env(kTestEnvVar, kTestEnvVarValue);
135
136 auto path_with_env_var = UTF8ToWide(std::string("C:\\%") + kTestEnvVar + "%");
137 auto path_expanded = UTF8ToWide(std::string("C:\\") + kTestEnvVarValue);
138
139 EXPECT_EQ(ExpandEnvironmentVariables(path_with_env_var).value(),
140 path_expanded);
141 }
142
TEST(BaseWinUtilTest,ExpandEnvironmentVariablesEmptyValue)143 TEST(BaseWinUtilTest, ExpandEnvironmentVariablesEmptyValue) {
144 constexpr char kTestEnvVar[] = "TEST_ENV_VAR";
145 constexpr char kTestEnvVarValue[] = "";
146 ScopedEnvironmentVariableOverride scoped_env(kTestEnvVar, kTestEnvVarValue);
147
148 auto path_with_env_var = UTF8ToWide(std::string("C:\\%") + kTestEnvVar + "%");
149 auto path_expanded = UTF8ToWide(std::string("C:\\") + kTestEnvVarValue);
150
151 EXPECT_EQ(ExpandEnvironmentVariables(path_with_env_var).value(),
152 path_expanded);
153 }
154
TEST(BaseWinUtilTest,ExpandEnvironmentVariablesUndefinedValue)155 TEST(BaseWinUtilTest, ExpandEnvironmentVariablesUndefinedValue) {
156 constexpr char kTestEnvVar[] = "TEST_ENV_VAR";
157
158 auto path_with_env_var = UTF8ToWide(std::string("C:\\%") + kTestEnvVar + "%");
159
160 // Undefined env vars are left unexpanded.
161 auto path_expanded = path_with_env_var;
162
163 EXPECT_EQ(ExpandEnvironmentVariables(path_with_env_var).value(),
164 path_expanded);
165 }
166
TEST(DeviceConvertibilityTest,None)167 TEST(DeviceConvertibilityTest, None) {
168 ScopedCOMInitializer com_initializer;
169 ASSERT_TRUE(com_initializer.Succeeded());
170 ScopedDeviceConvertibilityStateForTesting scoper(false, false, csm_false,
171 std::nullopt, std::nullopt);
172 EXPECT_FALSE(QueryDeviceConvertibility());
173 }
174
TEST(DeviceConvertibilityTest,ConvertibilityDisabled)175 TEST(DeviceConvertibilityTest, ConvertibilityDisabled) {
176 ScopedCOMInitializer com_initializer;
177 ASSERT_TRUE(com_initializer.Succeeded());
178 // If convertibility is not enabled but the key exists, other values shouldn't
179 // be checked. Device is not convertible.
180 ScopedDeviceConvertibilityStateForTesting scoper(
181 /*form_convertible=*/true, /*chassis_convertible=*/true,
182 /*csm_changed=*/csm_false, /*convertible_chassis_key=*/std::nullopt,
183 /*convertibility_enabled=*/std::optional<bool>{false});
184 EXPECT_FALSE(QueryDeviceConvertibility());
185 }
186
TEST(DeviceConvertibilityTest,ConvertibilityEnabled)187 TEST(DeviceConvertibilityTest, ConvertibilityEnabled) {
188 ScopedCOMInitializer com_initializer;
189 ASSERT_TRUE(com_initializer.Succeeded());
190 ScopedDeviceConvertibilityStateForTesting scoper(
191 /*form_convertible=*/false, /*chassis_convertible=*/false,
192 /*csm_changed=*/csm_false, /*convertible_chassis_key=*/std::nullopt,
193 /*convertibility_enabled=*/std::optional<bool>{true});
194 EXPECT_TRUE(QueryDeviceConvertibility());
195 }
196
TEST(DeviceConvertibilityTest,ChassisConvertibleKeyTrue)197 TEST(DeviceConvertibilityTest, ChassisConvertibleKeyTrue) {
198 ScopedCOMInitializer com_initializer;
199 ASSERT_TRUE(com_initializer.Succeeded());
200 ScopedDeviceConvertibilityStateForTesting scoper(
201 /*form_convertible=*/false, /*chassis_convertible=*/false,
202 /*csm_changed=*/csm_false,
203 /*convertible_chassis_key=*/std::optional<bool>{true},
204 /*convertibility_enabled=*/std::nullopt);
205 EXPECT_TRUE(QueryDeviceConvertibility());
206 }
207
TEST(DeviceConvertibilityTest,ChassisConvertibleKeyFalse)208 TEST(DeviceConvertibilityTest, ChassisConvertibleKeyFalse) {
209 ScopedCOMInitializer com_initializer;
210 ASSERT_TRUE(com_initializer.Succeeded());
211 ScopedDeviceConvertibilityStateForTesting scoper(
212 /*form_convertible=*/false, /*chassis_convertible=*/true,
213 /*csm_changed=*/csm_true,
214 /*convertible_chassis_key=*/std::optional<bool>{false},
215 /*convertibility_enabled=*/std::nullopt);
216 EXPECT_FALSE(QueryDeviceConvertibility());
217 }
218
TEST(DeviceConvertibilityTest,FormConvertibleTrue)219 TEST(DeviceConvertibilityTest, FormConvertibleTrue) {
220 ScopedCOMInitializer com_initializer;
221 ASSERT_TRUE(com_initializer.Succeeded());
222 ScopedDeviceConvertibilityStateForTesting scoper(
223 /*form_convertible=*/true, /*chassis_convertible=*/false,
224 /*csm_changed=*/csm_false,
225 /*convertible_chassis_key=*/std::nullopt,
226 /*convertibility_enabled=*/std::nullopt);
227 EXPECT_TRUE(QueryDeviceConvertibility());
228 }
229
TEST(DeviceConvertibilityTest,ChassisConvertibleTrue)230 TEST(DeviceConvertibilityTest, ChassisConvertibleTrue) {
231 ScopedCOMInitializer com_initializer;
232 ASSERT_TRUE(com_initializer.Succeeded());
233 ScopedDeviceConvertibilityStateForTesting scoper(
234 /*form_convertible=*/false, /*chassis_convertible=*/true,
235 /*csm_changed=*/csm_false,
236 /*convertible_chassis_key=*/std::nullopt,
237 /*convertibility_enabled=*/std::nullopt);
238 EXPECT_TRUE(QueryDeviceConvertibility());
239 }
240
TEST(DeviceConvertibilityTest,ConvertibleSlateModeChangeTrue)241 TEST(DeviceConvertibilityTest, ConvertibleSlateModeChangeTrue) {
242 ScopedCOMInitializer com_initializer;
243 ASSERT_TRUE(com_initializer.Succeeded());
244 ScopedDeviceConvertibilityStateForTesting scoper(
245 /*form_convertible=*/false, /*chassis_convertible=*/false,
246 /*csm_changed=*/csm_true,
247 /*convertible_chassis_key=*/std::nullopt,
248 /*convertibility_enabled=*/std::nullopt);
249 EXPECT_TRUE(QueryDeviceConvertibility());
250 }
251
TEST(DeviceConvertibilityTest,ConvertibilityEnabledSanityCheck)252 TEST(DeviceConvertibilityTest, ConvertibilityEnabledSanityCheck) {
253 RegKey key(HKEY_LOCAL_MACHINE,
254 L"System\\CurrentControlSet\\Control\\PriorityControl", KEY_READ);
255 if (key.HasValue(L"ConvertibilityEnabled")) {
256 ASSERT_TRUE(GetConvertibilityEnabledOverride().has_value());
257 } else {
258 ASSERT_FALSE(GetConvertibilityEnabledOverride().has_value());
259 }
260 }
261
TEST(DeviceConvertibilityTest,ConvertibilityKeySanityCheck)262 TEST(DeviceConvertibilityTest, ConvertibilityKeySanityCheck) {
263 RegKey key(HKEY_CURRENT_USER,
264 L"SOFTWARE\\Microsoft\\TabletTip\\ConvertibleChassis", KEY_READ);
265 if (key.HasValue(L"ConvertibleChassis")) {
266 ASSERT_TRUE(GetConvertibleChassisKeyValue().has_value());
267 } else {
268 ASSERT_FALSE(GetConvertibleChassisKeyValue().has_value());
269 }
270 }
271
TEST(DeviceConvertibilityTest,DeviceFormAndChassisConvertible)272 TEST(DeviceConvertibilityTest, DeviceFormAndChassisConvertible) {
273 ScopedCOMInitializer com_initializer;
274 ASSERT_TRUE(com_initializer.Succeeded());
275 EXPECT_FALSE(IsDeviceFormConvertible() || IsChassisConvertible());
276 }
277
278 } // namespace win
279 } // namespace base
280