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