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