[APITEST] Move RedirectIat into it's own header so that it can be used from multiple...
[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 #include "apitest_iathook.h"
31
32 static DWORD g_Version;
33 #define WINVER_ANY 0
34
35
36 /* apphelp.dll */
37 static BOOL(WINAPI* pSdbGetAppPatchDir)(PVOID, LPWSTR, DWORD);
38
39 /* aclayers.dll / acgenral.dll */
40 static PVOID(WINAPI* pGetHookAPIs)(LPCSTR, LPCWSTR, PDWORD);
41 static BOOL(WINAPI* pNotifyShims)(DWORD fdwReason, PVOID ptr);
42
43 DWORD get_module_version(HMODULE mod)
44 {
45 DWORD dwVersion = 0;
46 HRSRC hResInfo = FindResource(mod, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
47 DWORD dwSize = SizeofResource(mod, hResInfo);
48 if (hResInfo && dwSize)
49 {
50 VS_FIXEDFILEINFO *lpFfi;
51 UINT uLen;
52
53 HGLOBAL hResData = LoadResource(mod, hResInfo);
54 LPVOID pRes = LockResource(hResData);
55 HLOCAL pResCopy = LocalAlloc(LMEM_FIXED, dwSize);
56
57 CopyMemory(pResCopy, pRes, dwSize);
58 FreeResource(hResData);
59
60 if (VerQueryValueW(pResCopy, L"\\", (LPVOID*)&lpFfi, &uLen))
61 {
62 dwVersion = (HIWORD(lpFfi->dwProductVersionMS) << 8) | LOWORD(lpFfi->dwProductVersionMS);
63 if (!dwVersion)
64 dwVersion = (HIWORD(lpFfi->dwFileVersionMS) << 8) | LOWORD(lpFfi->dwFileVersionMS);
65 }
66
67 LocalFree(pResCopy);
68 }
69
70 return dwVersion;
71 }
72
73 static LONG g_ChangeCount;
74 static DEVMODEA g_LastDevmode;
75 static DWORD g_LastFlags;
76
77 static LONG (WINAPI *pChangeDisplaySettingsA)(_In_opt_ PDEVMODEA lpDevMode, _In_ DWORD dwflags);
78 LONG WINAPI mChangeDisplaySettingsA(_In_opt_ PDEVMODEA lpDevMode, _In_ DWORD dwflags)
79 {
80 g_ChangeCount++;
81 g_LastDevmode = *lpDevMode;
82 g_LastFlags = dwflags;
83
84 return DISP_CHANGE_FAILED;
85 }
86
87 static LONG g_EnumCount;
88 static BOOL bFix = TRUE;
89
90 static BOOL (WINAPI *pEnumDisplaySettingsA)(_In_opt_ LPCSTR lpszDeviceName, _In_ DWORD iModeNum, _Inout_ PDEVMODEA lpDevMode);
91 BOOL WINAPI mEnumDisplaySettingsA(_In_opt_ LPCSTR lpszDeviceName, _In_ DWORD iModeNum, _Inout_ PDEVMODEA lpDevMode)
92 {
93 g_EnumCount++;
94 if (pEnumDisplaySettingsA(lpszDeviceName, iModeNum, lpDevMode))
95 {
96 if (bFix)
97 {
98 if (lpDevMode && lpDevMode->dmBitsPerPel == 8)
99 {
100 trace("Running at 8bpp, faking 16\n");
101 lpDevMode->dmBitsPerPel = 16;
102 }
103 if (lpDevMode && lpDevMode->dmPelsWidth == 640 && lpDevMode->dmPelsHeight == 480)
104 {
105 trace("Running at 640x480, faking 800x600\n");
106 lpDevMode->dmPelsWidth = 800;
107 lpDevMode->dmPelsHeight = 600;
108 }
109 }
110 else
111 {
112 if (lpDevMode)
113 {
114 lpDevMode->dmBitsPerPel = 8;
115 lpDevMode->dmPelsWidth = 640;
116 lpDevMode->dmPelsHeight = 480;
117 }
118 }
119 return TRUE;
120 }
121 return FALSE;
122 }
123
124
125
126 static LONG g_ThemeCount;
127 static DWORD g_LastThemeFlags;
128
129 static void (WINAPI *pSetThemeAppProperties)(DWORD dwFlags);
130 void WINAPI mSetThemeAppProperties(DWORD dwFlags)
131 {
132 g_ThemeCount++;
133 g_LastThemeFlags = dwFlags;
134 }
135
136
137 static const WCHAR* shim_dll(const WCHAR* name)
138 {
139 static WCHAR buf[MAX_PATH];
140 pSdbGetAppPatchDir(NULL, buf, MAX_PATH);
141 StringCchCatW(buf, _countof(buf), name);
142 return buf;
143 }
144
145 static void pre_8bit(void)
146 {
147 g_ChangeCount = 0;
148 memset(&g_LastDevmode, 0, sizeof(g_LastDevmode));
149 g_LastFlags = 0xffffffff;
150 g_EnumCount = 0;
151 }
152
153 static void pre_8bit_2(void)
154 {
155 bFix = FALSE;
156
157 pre_8bit();
158 }
159
160 static void post_8bit(void)
161 {
162 ok_int(g_ChangeCount, 1);
163 ok_hex(g_LastDevmode.dmFields & DM_BITSPERPEL, DM_BITSPERPEL);
164 ok_int(g_LastDevmode.dmBitsPerPel, 8);
165 ok_hex(g_LastFlags, CDS_FULLSCREEN);
166 ok_int(g_EnumCount, 1);
167 }
168
169 static void post_8bit_2(void)
170 {
171 ok_int(g_ChangeCount, 0);
172 ok_hex(g_LastFlags, 0xffffffff);
173 ok_int(g_EnumCount, 1);
174
175 bFix = TRUE;
176 }
177
178 static void post_8bit_no(void)
179 {
180 if (g_Version == _WIN32_WINNT_WS03)
181 {
182 ok_int(g_ChangeCount, 1);
183 ok_hex(g_LastDevmode.dmFields & DM_BITSPERPEL, DM_BITSPERPEL);
184 ok_int(g_LastDevmode.dmBitsPerPel, 8);
185 ok_hex(g_LastFlags, CDS_FULLSCREEN);
186 ok_int(g_EnumCount, 1);
187 }
188 else
189 {
190 ok_int(g_ChangeCount, 0);
191 ok_hex(g_LastFlags, 0xffffffff);
192 ok_int(g_EnumCount, 0);
193 }
194
195 bFix = TRUE;
196 }
197
198 static void post_8bit_2_no(void)
199 {
200 if (g_Version == _WIN32_WINNT_WS03)
201 {
202 ok_int(g_ChangeCount, 0);
203 ok_hex(g_LastFlags, 0xffffffff);
204 ok_int(g_EnumCount, 1);
205 }
206 else
207 {
208 ok_int(g_ChangeCount, 0);
209 ok_hex(g_LastFlags, 0xffffffff);
210 ok_int(g_EnumCount, 0);
211 }
212
213 bFix = TRUE;
214 }
215
216 static void pre_640(void)
217 {
218 g_ChangeCount = 0;
219 memset(&g_LastDevmode, 0, sizeof(g_LastDevmode));
220 g_LastFlags = 0xffffffff;
221 g_EnumCount = 0;
222 }
223
224 static void pre_640_2(void)
225 {
226 bFix = FALSE;
227
228 pre_640();
229 }
230
231 static void post_640(void)
232 {
233 ok_int(g_ChangeCount, 1);
234 ok_hex(g_LastDevmode.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT), (DM_PELSWIDTH | DM_PELSHEIGHT));
235 ok_int(g_LastDevmode.dmPelsWidth, 640);
236 ok_int(g_LastDevmode.dmPelsHeight, 480);
237 ok_hex(g_LastFlags, CDS_FULLSCREEN);
238 ok_int(g_EnumCount, 1);
239 }
240
241 static void post_640_2(void)
242 {
243 ok_int(g_ChangeCount, 0);
244 ok_hex(g_LastFlags, 0xffffffff);
245 ok_int(g_EnumCount, 1);
246
247 bFix = TRUE;
248 }
249
250 static void post_640_no(void)
251 {
252 if (g_Version == _WIN32_WINNT_WS03)
253 {
254 ok_int(g_ChangeCount, 1);
255 ok_hex(g_LastDevmode.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT), (DM_PELSWIDTH | DM_PELSHEIGHT));
256 ok_int(g_LastDevmode.dmPelsWidth, 640);
257 ok_int(g_LastDevmode.dmPelsHeight, 480);
258 ok_hex(g_LastFlags, CDS_FULLSCREEN);
259 ok_int(g_EnumCount, 1);
260 }
261 else
262 {
263 ok_int(g_ChangeCount, 0);
264 ok_hex(g_LastFlags, 0xffffffff);
265 ok_int(g_EnumCount, 0);
266 }
267
268 bFix = TRUE;
269 }
270
271 static void post_640_2_no(void)
272 {
273 if (g_Version == _WIN32_WINNT_WS03)
274 {
275 ok_int(g_ChangeCount, 0);
276 ok_hex(g_LastFlags, 0xffffffff);
277 ok_int(g_EnumCount, 1);
278 }
279 else
280 {
281 ok_int(g_ChangeCount, 0);
282 ok_hex(g_LastFlags, 0xffffffff);
283 ok_int(g_EnumCount, 0);
284 }
285
286 bFix = TRUE;
287 }
288
289 static void pre_theme(void)
290 {
291 g_ThemeCount = 0;
292 g_LastThemeFlags = 0xffffffff;
293 }
294
295 static void post_theme(void)
296 {
297 ok_int(g_ThemeCount, 1);
298 ok_hex(g_LastThemeFlags, 0);
299 }
300
301 static void post_theme_no(void)
302 {
303 if (g_Version == _WIN32_WINNT_WS03)
304 {
305 ok_int(g_ThemeCount, 1);
306 ok_hex(g_LastThemeFlags, 0);
307 }
308 else
309 {
310 ok_int(g_ThemeCount, 0);
311 ok_hex(g_LastThemeFlags, 0xffffffff);
312 }
313 }
314
315
316 static BOOL hook_disp(HMODULE dll)
317 {
318 return RedirectIat(dll, "user32.dll", "ChangeDisplaySettingsA", (ULONG_PTR)mChangeDisplaySettingsA, (ULONG_PTR*)&pChangeDisplaySettingsA) &&
319 RedirectIat(dll, "user32.dll", "EnumDisplaySettingsA", (ULONG_PTR)mEnumDisplaySettingsA, (ULONG_PTR*)&pEnumDisplaySettingsA);
320 }
321
322 static VOID unhook_disp(HMODULE dll)
323 {
324 RestoreIat(dll, "user32.dll", "ChangeDisplaySettingsA", (ULONG_PTR)pChangeDisplaySettingsA);
325 RestoreIat(dll, "user32.dll", "EnumDisplaySettingsA", (ULONG_PTR)pEnumDisplaySettingsA);
326 }
327
328 static BOOL hook_theme(HMODULE dll)
329 {
330 return RedirectIat(dll, "uxtheme.dll", "SetThemeAppProperties", (ULONG_PTR)mSetThemeAppProperties, (ULONG_PTR*)&pSetThemeAppProperties);
331 }
332
333 static VOID unhook_theme(HMODULE dll)
334 {
335 RestoreIat(dll, "uxtheme.dll", "SetThemeAppProperties", (ULONG_PTR)pSetThemeAppProperties);
336 }
337
338 static void test_one(LPCSTR shim, DWORD dwReason, void(*pre)(), void(*post)(), void(*second)(void))
339 {
340 DWORD num_shims = 0;
341 WCHAR wide_shim[50] = { 0 };
342 PVOID hook;
343 BOOL ret;
344 MultiByteToWideChar(CP_ACP, 0, shim, -1, wide_shim, 50);
345
346 if (pre)
347 pre();
348
349 hook = pGetHookAPIs("", wide_shim, &num_shims);
350 if (hook == NULL)
351 {
352 skip("Skipping tests for layers (%s) not present in this os (0x%x)\n", shim, g_Version);
353 return;
354 }
355 ok(hook != NULL, "Expected hook to be a valid pointer for %s\n", shim);
356 ok(num_shims == 0, "Expected not to find any apihooks, got: %u for %s\n", num_shims, shim);
357
358 ret = pNotifyShims(dwReason, NULL);
359
360 /* Win7 and Win10 return 1, w2k3 returns a pointer */
361 ok(ret != 0, "Expected pNotifyShims to succeed (%i)\n", ret);
362
363 if (post)
364 post();
365
366 /* Invoking it a second time does not call the init functions again! */
367 if (pre && second)
368 {
369 pre();
370
371 ret = pNotifyShims(dwReason, NULL);
372 ok(ret != 0, "Expected pNotifyShims to succeed (%i)\n", ret);
373
374 second();
375 }
376 }
377
378 /* In 2k3 0, 2, 4, 6, 8 are not guarded against re-initializations! */
379 static struct test_info
380 {
381 const char* name;
382 const WCHAR* dll;
383 DWORD winver;
384 DWORD reason;
385 BOOL(*hook)(HMODULE);
386 void(*unhook)(HMODULE);
387 void(*pre)(void);
388 void(*post)(void);
389 void(*second)(void);
390 } tests[] =
391 {
392 /* Success */
393 { "Force8BitColor", L"\\aclayers.dll", WINVER_ANY, 1, hook_disp, unhook_disp, pre_8bit, post_8bit, post_8bit_no },
394 { "Force8BitColor", L"\\aclayers.dll", _WIN32_WINNT_VISTA, 100, hook_disp, unhook_disp,pre_8bit, post_8bit, post_8bit_no },
395 { "Force640x480", L"\\aclayers.dll", WINVER_ANY, 1, hook_disp, unhook_disp, pre_640, post_640, post_640_no },
396 { "Force640x480", L"\\aclayers.dll", _WIN32_WINNT_VISTA, 100, hook_disp, unhook_disp, pre_640, post_640, post_640_no },
397 { "DisableThemes", L"\\acgenral.dll", WINVER_ANY, 1, hook_theme, unhook_theme, pre_theme, post_theme, post_theme_no },
398 { "DisableThemes", L"\\acgenral.dll", _WIN32_WINNT_VISTA, 100, hook_theme, unhook_theme, pre_theme, post_theme, post_theme_no },
399
400 /* No need to change anything */
401 { "Force8BitColor", L"\\aclayers.dll", WINVER_ANY, 1, hook_disp, unhook_disp, pre_8bit_2, post_8bit_2, post_8bit_2_no },
402 { "Force8BitColor", L"\\aclayers.dll", _WIN32_WINNT_VISTA, 100, hook_disp, unhook_disp, pre_8bit_2, post_8bit_2, post_8bit_2_no },
403 { "Force640x480", L"\\aclayers.dll", WINVER_ANY, 1, hook_disp, unhook_disp, pre_640_2, post_640_2, post_640_2_no },
404 { "Force640x480", L"\\aclayers.dll", _WIN32_WINNT_VISTA, 100, hook_disp, unhook_disp, pre_640_2, post_640_2, post_640_2_no },
405 };
406
407
408 static void run_test(size_t n, BOOL unload)
409 {
410 BOOL ret;
411 HMODULE dll;
412 const WCHAR* buf = shim_dll(tests[n].dll);
413
414 dll = LoadLibraryW(shim_dll(tests[n].dll));
415 pGetHookAPIs = (void*)GetProcAddress(dll, "GetHookAPIs");
416 pNotifyShims = (void*)GetProcAddress(dll, "NotifyShims");
417
418 if (!pGetHookAPIs || !pNotifyShims)
419 {
420 skip("aclayers.dll not loaded, or does not export GetHookAPIs or pNotifyShims (%s, %p, %p)\n",
421 tests[n].name, pGetHookAPIs, pNotifyShims);
422 return;
423 }
424
425 g_Version = get_module_version(dll);
426
427 if (!g_Version)
428 {
429 g_Version = _WIN32_WINNT_WS03;
430 trace("Module %s has no version, faking 2k3\n", wine_dbgstr_w(tests[n].dll));
431 }
432
433 if (g_Version >= tests[n].winver)
434 {
435 ret = tests[n].hook(dll);
436 if (ret)
437 {
438 test_one(tests[n].name, tests[n].reason, tests[n].pre, tests[n].post, tests[n].second);
439 }
440 else
441 {
442 ok(0, "Unable to redirect functions!\n");
443 }
444 }
445 FreeLibrary(dll);
446 if (unload)
447 {
448 dll = GetModuleHandleW(buf);
449 ok(dll == NULL, "Unable to unload %s\n", wine_dbgstr_w(buf));
450 }
451 }
452
453
454 START_TEST(dispmode)
455 {
456 HMODULE dll = LoadLibraryA("apphelp.dll");
457 size_t n;
458 int argc;
459 char **argv;
460
461 pSdbGetAppPatchDir = (void*)GetProcAddress(dll, "SdbGetAppPatchDir");
462 if (!pSdbGetAppPatchDir)
463 {
464 skip("apphelp.dll not loaded, or does not export SdbGetAppPatchDir\n");
465 return;
466 }
467
468 argc = winetest_get_mainargs(&argv);
469 if (argc < 3)
470 {
471 WCHAR path[MAX_PATH];
472 GetModuleFileNameW(NULL, path, _countof(path));
473 dll = GetModuleHandleW(shim_dll(L"\\aclayers.dll"));
474 if (!dll)
475 dll = GetModuleHandleW(shim_dll(L"\\acgenral.dll"));
476 if (dll != NULL)
477 trace("Loaded under a shim, running each test in it's own process\n");
478
479 for (n = 0; n < _countof(tests); ++n)
480 {
481 LONG failures = winetest_get_failures();
482
483 if (dll == NULL)
484 {
485 run_test(n, TRUE);
486 }
487 else
488 {
489 WCHAR buf[MAX_PATH+40];
490 STARTUPINFOW si = { sizeof(si) };
491 PROCESS_INFORMATION pi;
492 BOOL created;
493
494 StringCchPrintfW(buf, _countof(buf), L"\"%ls\" dispmode %u", path, n);
495 created = CreateProcessW(NULL, buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
496 ok(created, "Expected CreateProcess to succeed\n");
497 if (created)
498 {
499 winetest_wait_child_process(pi.hProcess);
500 CloseHandle(pi.hThread);
501 CloseHandle(pi.hProcess);
502 }
503 }
504
505 ok(failures == winetest_get_failures(), "Last %u failures are from %d (%s)\n",
506 winetest_get_failures() - failures, n, tests[n].name);
507 }
508 }
509 else
510 {
511 n = (size_t)atoi(argv[2]);
512 if (n >= 0 && n < _countof(tests))
513 {
514 run_test(n, FALSE);
515 }
516 else
517 {
518 ok(0, "Test out of range: %u\n", n);
519 }
520 }
521 }