[APPSHIM_APITEST] Add tests for the DisableThemes shim. CORE-11927
[reactos.git] / rostests / apitests / appshim / dispmode.c
1 /*
2 * Copyright 2016 Mark Jansen
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <ntstatus.h>
20 #define WIN32_NO_STATUS
21 #include <windows.h>
22 #ifdef __REACTOS__
23 #include <ntndk.h>
24 #else
25 #include <winternl.h>
26 #endif
27 #include <stdio.h>
28 #include <strsafe.h>
29 #include "wine/test.h"
30
31 extern DWORD get_host_winver(void);
32 static DWORD g_WinVersion;
33 #define WINVER_ANY 0
34 #define WINVER_VISTA 0x0600
35
36
37 /* apphelp.dll */
38 static BOOL(WINAPI* pSdbGetAppPatchDir)(PVOID, LPWSTR, DWORD);
39
40 /* aclayers.dll */
41 static PVOID(WINAPI* pGetHookAPIs)(LPCSTR, LPCWSTR, PDWORD);
42 static BOOL(WINAPI* pNotifyShims)(DWORD fdwReason, PVOID ptr);
43
44
45 static LONG g_Count;
46 static DEVMODEA g_LastDevmode;
47 static DWORD g_LastFlags;
48
49 static LONG (WINAPI *pChangeDisplaySettingsA)(_In_opt_ PDEVMODEA lpDevMode, _In_ DWORD dwflags);
50 LONG WINAPI mChangeDisplaySettingsA(_In_opt_ PDEVMODEA lpDevMode, _In_ DWORD dwflags)
51 {
52 g_Count++;
53 g_LastDevmode = *lpDevMode;
54 g_LastFlags = dwflags;
55
56 return DISP_CHANGE_FAILED;
57 }
58
59 static LONG g_ThemeCount;
60 static DWORD g_LastThemeFlags;
61
62 static void (WINAPI *pSetThemeAppProperties)(DWORD dwFlags);
63 void WINAPI mSetThemeAppProperties(DWORD dwFlags)
64 {
65 g_ThemeCount++;
66 g_LastThemeFlags = dwFlags;
67 }
68
69
70 static const WCHAR* shim_dll(const WCHAR* name)
71 {
72 static WCHAR buf[MAX_PATH];
73 pSdbGetAppPatchDir(NULL, buf, MAX_PATH);
74 StringCchCatW(buf, _countof(buf), name);
75 return buf;
76 }
77
78 static void pre_8bit(void)
79 {
80 g_Count = 0;
81 memset(&g_LastDevmode, 0, sizeof(g_LastDevmode));
82 g_LastFlags = 0xffffffff;
83 }
84
85 static void post_8bit(void)
86 {
87 ok_int(g_Count, 1);
88 ok_hex(g_LastDevmode.dmFields & DM_BITSPERPEL, DM_BITSPERPEL);
89 ok_int(g_LastDevmode.dmBitsPerPel, 8);
90 ok_hex(g_LastFlags, CDS_FULLSCREEN);
91 }
92
93 static void pre_640(void)
94 {
95 g_Count = 0;
96 memset(&g_LastDevmode, 0, sizeof(g_LastDevmode));
97 g_LastFlags = 0xffffffff;
98 }
99
100 static void post_640(void)
101 {
102 ok_int(g_Count, 1);
103 ok_hex(g_LastDevmode.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT), (DM_PELSWIDTH | DM_PELSHEIGHT));
104 ok_int(g_LastDevmode.dmPelsWidth, 640);
105 ok_int(g_LastDevmode.dmPelsHeight, 480);
106 ok_hex(g_LastFlags, CDS_FULLSCREEN);
107 }
108
109 static void pre_theme(void)
110 {
111 g_ThemeCount = 0;
112 g_LastThemeFlags = 0xffffffff;
113 }
114
115 static void post_theme(void)
116 {
117 ok_int(g_ThemeCount, 1);
118 ok_hex(g_LastThemeFlags, 0);
119 }
120
121 static PIMAGE_IMPORT_DESCRIPTOR FindImportDescriptor(PBYTE DllBase, PCSTR DllName)
122 {
123 ULONG Size;
124 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = RtlImageDirectoryEntryToData((HMODULE)DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &Size);
125 while (ImportDescriptor->Name && ImportDescriptor->OriginalFirstThunk)
126 {
127 PCHAR Name = (PCHAR)(DllBase + ImportDescriptor->Name);
128 if (!lstrcmpiA(Name, DllName))
129 {
130 return ImportDescriptor;
131 }
132 ImportDescriptor++;
133 }
134 return NULL;
135 }
136
137 static BOOL RedirectIat(HMODULE TargetDll, PCSTR DllName, PCSTR FunctionName, ULONG_PTR NewFunction, ULONG_PTR* OriginalFunction)
138 {
139 PBYTE DllBase = (PBYTE)TargetDll;
140 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = FindImportDescriptor(DllBase, DllName);
141 if (ImportDescriptor)
142 {
143 // On loaded images, OriginalFirstThunk points to the name / ordinal of the function
144 PIMAGE_THUNK_DATA OriginalThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->OriginalFirstThunk);
145 // FirstThunk points to the resolved address.
146 PIMAGE_THUNK_DATA FirstThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->FirstThunk);
147 while (OriginalThunk->u1.AddressOfData && FirstThunk->u1.Function)
148 {
149 if (!IMAGE_SNAP_BY_ORDINAL32(OriginalThunk->u1.AddressOfData))
150 {
151 PIMAGE_IMPORT_BY_NAME ImportName = (PIMAGE_IMPORT_BY_NAME)(DllBase + OriginalThunk->u1.AddressOfData);
152 if (!lstrcmpiA((PCSTR)ImportName->Name, FunctionName))
153 {
154 DWORD dwOld;
155 VirtualProtect(&FirstThunk->u1.Function, sizeof(ULONG_PTR), PAGE_EXECUTE_READWRITE, &dwOld);
156 *OriginalFunction = FirstThunk->u1.Function;
157 FirstThunk->u1.Function = NewFunction;
158 VirtualProtect(&FirstThunk->u1.Function, sizeof(ULONG_PTR), dwOld, &dwOld);
159 return TRUE;
160 }
161 }
162 OriginalThunk++;
163 FirstThunk++;
164 }
165 skip("Unable to find the Import %s!%s\n", DllName, FunctionName);
166 }
167 else
168 {
169 skip("Unable to find the ImportDescriptor for %s\n", DllName);
170 }
171 return FALSE;
172 }
173
174 static BOOL hook_disp(HMODULE dll)
175 {
176 return RedirectIat(dll, "user32.dll", "ChangeDisplaySettingsA", (ULONG_PTR)mChangeDisplaySettingsA, (ULONG_PTR*)&pChangeDisplaySettingsA);
177 }
178
179 static BOOL hook_theme(HMODULE dll)
180 {
181 return RedirectIat(dll, "uxtheme.dll", "SetThemeAppProperties", (ULONG_PTR)mSetThemeAppProperties, (ULONG_PTR*)&pSetThemeAppProperties);
182 }
183
184
185 static void test_one(LPCSTR shim, DWORD dwReason, void(*pre)(), void(*post)())
186 {
187 DWORD num_shims = 0;
188 WCHAR wide_shim[50] = { 0 };
189 PVOID hook;
190 BOOL ret;
191 MultiByteToWideChar(CP_ACP, 0, shim, -1, wide_shim, 50);
192
193 if (pre)
194 pre();
195
196 hook = pGetHookAPIs("", wide_shim, &num_shims);
197 if (hook == NULL)
198 {
199 skip("Skipping tests for layers (%s) not present in this os (0x%x)\n", shim, g_WinVersion);
200 return;
201 }
202 ok(hook != NULL, "Expected hook to be a valid pointer for %s\n", shim);
203 ok(num_shims == 0, "Expected not to find any apihooks, got: %u for %s\n", num_shims, shim);
204
205 ret = pNotifyShims(dwReason, NULL);
206
207 /* Win7 and Win10 return 1, w2k3 returns a pointer */
208 ok(ret != 0, "Expected pNotifyShims to succeed (%i)\n", ret);
209
210 if (post)
211 post();
212 }
213
214 static struct test_info
215 {
216 const char* name;
217 const WCHAR* dll;
218 DWORD winver;
219 DWORD reason;
220 BOOL(*hook)(HMODULE);
221 void(*pre)(void);
222 void(*post)(void);
223 } tests[] =
224 {
225 { "Force8BitColor", L"\\aclayers.dll", WINVER_ANY, 1, hook_disp, pre_8bit, post_8bit },
226 { "Force8BitColor", L"\\aclayers.dll", WINVER_VISTA, 100, hook_disp, pre_8bit, post_8bit },
227 { "Force640x480", L"\\aclayers.dll", WINVER_ANY, 1, hook_disp, pre_640, post_640 },
228 { "Force640x480", L"\\aclayers.dll", WINVER_VISTA, 100, hook_disp, pre_640, post_640 },
229 { "DisableThemes", L"\\acgenral.dll", WINVER_ANY, 1, hook_theme, pre_theme, post_theme },
230 { "DisableThemes", L"\\acgenral.dll", WINVER_VISTA, 100, hook_theme, pre_theme, post_theme },
231 };
232
233
234 static void run_test(size_t n, BOOL unload)
235 {
236 BOOL ret;
237 HMODULE dll;
238 const WCHAR* buf = shim_dll(tests[n].dll);
239
240 dll = LoadLibraryW(shim_dll(tests[n].dll));
241 pGetHookAPIs = (void*)GetProcAddress(dll, "GetHookAPIs");
242 pNotifyShims = (void*)GetProcAddress(dll, "NotifyShims");
243
244 if (!pGetHookAPIs || !pNotifyShims)
245 {
246 skip("aclayers.dll not loaded, or does not export GetHookAPIs or pNotifyShims (%s, %p, %p)\n",
247 tests[n].name, pGetHookAPIs, pNotifyShims);
248 return;
249 }
250
251 ret = tests[n].hook(dll);
252 if (ret)
253 {
254 test_one(tests[n].name, tests[n].reason, tests[n].pre, tests[n].post);
255 }
256 else
257 {
258 ok(0, "Unable to redirect functions!\n");
259 }
260 FreeLibrary(dll);
261 if (unload)
262 {
263 dll = GetModuleHandleW(buf);
264 ok(dll == NULL, "Unable to unload %s\n", wine_dbgstr_w(buf));
265 }
266 }
267
268
269 START_TEST(dispmode)
270 {
271 HMODULE dll = LoadLibraryA("apphelp.dll");
272 size_t n;
273 int argc;
274 char **argv;
275
276 pSdbGetAppPatchDir = (void*)GetProcAddress(dll, "SdbGetAppPatchDir");
277 if (!pSdbGetAppPatchDir)
278 {
279 skip("apphelp.dll not loaded, or does not export SdbGetAppPatchDir\n");
280 return;
281 }
282
283 g_WinVersion = get_host_winver();
284
285 argc = winetest_get_mainargs(&argv);
286 if (argc < 3)
287 {
288 WCHAR path[MAX_PATH];
289 GetModuleFileNameW(NULL, path, _countof(path));
290 dll = GetModuleHandleW(shim_dll(L"\\aclayers.dll"));
291 if (!dll)
292 dll = GetModuleHandleW(shim_dll(L"\\acgenral.dll"));
293 if (dll != NULL)
294 trace("Loaded under a shim, running each test in it's own process\n");
295
296 for (n = 0; n < _countof(tests); ++n)
297 {
298 LONG failures = winetest_get_failures();
299
300 if (g_WinVersion < tests[n].winver)
301 continue;
302
303 if (dll == NULL)
304 {
305 run_test(n, TRUE);
306 }
307 else
308 {
309 WCHAR buf[MAX_PATH+40];
310 STARTUPINFOW si = { sizeof(si) };
311 PROCESS_INFORMATION pi;
312 BOOL created;
313
314 StringCchPrintfW(buf, _countof(buf), L"\"%ls\" dispmode %u", path, n);
315 created = CreateProcessW(NULL, buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
316 ok(created, "Expected CreateProcess to succeed\n");
317 if (created)
318 {
319 winetest_wait_child_process(pi.hProcess);
320 CloseHandle(pi.hThread);
321 CloseHandle(pi.hProcess);
322 }
323 }
324
325 if (failures != winetest_get_failures())
326 {
327 trace("Failures from %d (%s)\n", n, tests[n].name);
328 }
329 }
330 }
331 else
332 {
333 n = (size_t)atoi(argv[2]);
334 if (n >= 0 && n < _countof(tests))
335 {
336 run_test(n, FALSE);
337 }
338 else
339 {
340 ok(0, "Test out of range: %u\n", n);
341 }
342 }
343 }