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)
9 #define WIN32_NO_STATUS
18 #include "wine/test.h"
19 #include "apitest_iathook.h"
21 static DWORD g_Version
;
26 static BOOL(WINAPI
* pSdbGetAppPatchDir
)(PVOID
, LPWSTR
, DWORD
);
28 /* aclayers.dll / acgenral.dll */
29 static PVOID(WINAPI
* pGetHookAPIs
)(LPCSTR
, LPCWSTR
, PDWORD
);
30 static BOOL(WINAPI
* pNotifyShims
)(DWORD fdwReason
, PVOID ptr
);
32 DWORD
get_module_version(HMODULE mod
)
35 HRSRC hResInfo
= FindResource(mod
, MAKEINTRESOURCE(VS_VERSION_INFO
), RT_VERSION
);
36 DWORD dwSize
= SizeofResource(mod
, hResInfo
);
37 if (hResInfo
&& dwSize
)
39 VS_FIXEDFILEINFO
*lpFfi
;
42 HGLOBAL hResData
= LoadResource(mod
, hResInfo
);
43 LPVOID pRes
= LockResource(hResData
);
44 HLOCAL pResCopy
= LocalAlloc(LMEM_FIXED
, dwSize
);
46 CopyMemory(pResCopy
, pRes
, dwSize
);
47 FreeResource(hResData
);
49 if (VerQueryValueW(pResCopy
, L
"\\", (LPVOID
*)&lpFfi
, &uLen
))
51 dwVersion
= (HIWORD(lpFfi
->dwProductVersionMS
) << 8) | LOWORD(lpFfi
->dwProductVersionMS
);
53 dwVersion
= (HIWORD(lpFfi
->dwFileVersionMS
) << 8) | LOWORD(lpFfi
->dwFileVersionMS
);
62 static LONG g_ChangeCount
;
63 static DEVMODEA g_LastDevmode
;
64 static DWORD g_LastFlags
;
66 static LONG (WINAPI
*pChangeDisplaySettingsA
)(_In_opt_ PDEVMODEA lpDevMode
, _In_ DWORD dwflags
);
67 LONG WINAPI
mChangeDisplaySettingsA(_In_opt_ PDEVMODEA lpDevMode
, _In_ DWORD dwflags
)
70 g_LastDevmode
= *lpDevMode
;
71 g_LastFlags
= dwflags
;
73 return DISP_CHANGE_FAILED
;
76 static LONG g_EnumCount
;
77 static BOOL bFix
= TRUE
;
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
)
83 if (pEnumDisplaySettingsA(lpszDeviceName
, iModeNum
, lpDevMode
))
87 if (lpDevMode
&& lpDevMode
->dmBitsPerPel
== 8)
89 trace("Running at 8bpp, faking 16\n");
90 lpDevMode
->dmBitsPerPel
= 16;
92 if (lpDevMode
&& lpDevMode
->dmPelsWidth
== 640 && lpDevMode
->dmPelsHeight
== 480)
94 trace("Running at 640x480, faking 800x600\n");
95 lpDevMode
->dmPelsWidth
= 800;
96 lpDevMode
->dmPelsHeight
= 600;
103 lpDevMode
->dmBitsPerPel
= 8;
104 lpDevMode
->dmPelsWidth
= 640;
105 lpDevMode
->dmPelsHeight
= 480;
115 static LONG g_ThemeCount
;
116 static DWORD g_LastThemeFlags
;
118 static void (WINAPI
*pSetThemeAppProperties
)(DWORD dwFlags
);
119 void WINAPI
mSetThemeAppProperties(DWORD dwFlags
)
122 g_LastThemeFlags
= dwFlags
;
126 static const WCHAR
* shim_dll(const WCHAR
* name
)
128 static WCHAR buf
[MAX_PATH
];
129 pSdbGetAppPatchDir(NULL
, buf
, MAX_PATH
);
130 StringCchCatW(buf
, _countof(buf
), name
);
134 static void pre_8bit(void)
137 memset(&g_LastDevmode
, 0, sizeof(g_LastDevmode
));
138 g_LastFlags
= 0xffffffff;
142 static void pre_8bit_2(void)
149 static void post_8bit(void)
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);
158 static void post_8bit_2(void)
160 ok_int(g_ChangeCount
, 0);
161 ok_hex(g_LastFlags
, 0xffffffff);
162 ok_int(g_EnumCount
, 1);
167 static void post_8bit_no(void)
169 if (g_Version
== _WIN32_WINNT_WS03
)
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);
179 ok_int(g_ChangeCount
, 0);
180 ok_hex(g_LastFlags
, 0xffffffff);
181 ok_int(g_EnumCount
, 0);
187 static void post_8bit_2_no(void)
189 if (g_Version
== _WIN32_WINNT_WS03
)
191 ok_int(g_ChangeCount
, 0);
192 ok_hex(g_LastFlags
, 0xffffffff);
193 ok_int(g_EnumCount
, 1);
197 ok_int(g_ChangeCount
, 0);
198 ok_hex(g_LastFlags
, 0xffffffff);
199 ok_int(g_EnumCount
, 0);
205 static void pre_640(void)
208 memset(&g_LastDevmode
, 0, sizeof(g_LastDevmode
));
209 g_LastFlags
= 0xffffffff;
213 static void pre_640_2(void)
220 static void post_640(void)
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);
230 static void post_640_2(void)
232 ok_int(g_ChangeCount
, 0);
233 ok_hex(g_LastFlags
, 0xffffffff);
234 ok_int(g_EnumCount
, 1);
239 static void post_640_no(void)
241 if (g_Version
== _WIN32_WINNT_WS03
)
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);
252 ok_int(g_ChangeCount
, 0);
253 ok_hex(g_LastFlags
, 0xffffffff);
254 ok_int(g_EnumCount
, 0);
260 static void post_640_2_no(void)
262 if (g_Version
== _WIN32_WINNT_WS03
)
264 ok_int(g_ChangeCount
, 0);
265 ok_hex(g_LastFlags
, 0xffffffff);
266 ok_int(g_EnumCount
, 1);
270 ok_int(g_ChangeCount
, 0);
271 ok_hex(g_LastFlags
, 0xffffffff);
272 ok_int(g_EnumCount
, 0);
278 static void pre_theme(void)
281 g_LastThemeFlags
= 0xffffffff;
284 static void post_theme(void)
286 ok_int(g_ThemeCount
, 1);
287 ok_hex(g_LastThemeFlags
, 0);
290 static void post_theme_no(void)
292 if (g_Version
== _WIN32_WINNT_WS03
)
294 ok_int(g_ThemeCount
, 1);
295 ok_hex(g_LastThemeFlags
, 0);
299 ok_int(g_ThemeCount
, 0);
300 ok_hex(g_LastThemeFlags
, 0xffffffff);
305 static BOOL
hook_disp(HMODULE dll
)
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
);
311 static VOID
unhook_disp(HMODULE dll
)
313 RestoreIat(dll
, "user32.dll", "ChangeDisplaySettingsA", (ULONG_PTR
)pChangeDisplaySettingsA
);
314 RestoreIat(dll
, "user32.dll", "EnumDisplaySettingsA", (ULONG_PTR
)pEnumDisplaySettingsA
);
317 static BOOL
hook_theme(HMODULE dll
)
319 return RedirectIat(dll
, "uxtheme.dll", "SetThemeAppProperties", (ULONG_PTR
)mSetThemeAppProperties
, (ULONG_PTR
*)&pSetThemeAppProperties
);
322 static VOID
unhook_theme(HMODULE dll
)
324 RestoreIat(dll
, "uxtheme.dll", "SetThemeAppProperties", (ULONG_PTR
)pSetThemeAppProperties
);
327 static void test_one(LPCSTR shim
, DWORD dwReason
, void(*pre
)(), void(*post
)(), void(*second
)(void))
330 WCHAR wide_shim
[50] = { 0 };
333 MultiByteToWideChar(CP_ACP
, 0, shim
, -1, wide_shim
, 50);
338 hook
= pGetHookAPIs("", wide_shim
, &num_shims
);
341 skip("Skipping tests for layers (%s) not present in this os (0x%x)\n", shim
, g_Version
);
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
);
347 ret
= pNotifyShims(dwReason
, NULL
);
349 /* Win7 and Win10 return 1, w2k3 returns a pointer */
350 ok(ret
!= 0, "Expected pNotifyShims to succeed (%i)\n", ret
);
355 /* Invoking it a second time does not call the init functions again! */
360 ret
= pNotifyShims(dwReason
, NULL
);
361 ok(ret
!= 0, "Expected pNotifyShims to succeed (%i)\n", ret
);
367 /* In 2k3 0, 2, 4, 6, 8 are not guarded against re-initializations! */
368 static struct test_info
374 BOOL(*hook
)(HMODULE
);
375 void(*unhook
)(HMODULE
);
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
},
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
},
397 static void run_test(size_t n
, BOOL unload
)
401 const WCHAR
* buf
= shim_dll(tests
[n
].dll
);
403 dll
= LoadLibraryW(shim_dll(tests
[n
].dll
));
404 pGetHookAPIs
= (void*)GetProcAddress(dll
, "GetHookAPIs");
405 pNotifyShims
= (void*)GetProcAddress(dll
, "NotifyShims");
407 if (!pGetHookAPIs
|| !pNotifyShims
)
409 skip("aclayers.dll not loaded, or does not export GetHookAPIs or pNotifyShims (%s, %p, %p)\n",
410 tests
[n
].name
, pGetHookAPIs
, pNotifyShims
);
414 g_Version
= get_module_version(dll
);
418 g_Version
= _WIN32_WINNT_WS03
;
419 trace("Module %s has no version, faking 2k3\n", wine_dbgstr_w(tests
[n
].dll
));
422 if (g_Version
>= tests
[n
].winver
)
424 ret
= tests
[n
].hook(dll
);
427 test_one(tests
[n
].name
, tests
[n
].reason
, tests
[n
].pre
, tests
[n
].post
, tests
[n
].second
);
428 tests
[n
].unhook(dll
);
432 ok(0, "Unable to redirect functions!\n");
438 dll
= GetModuleHandleW(buf
);
439 ok(dll
== NULL
, "Unable to unload %s\n", wine_dbgstr_w(buf
));
446 HMODULE dll
= LoadLibraryA("apphelp.dll");
451 pSdbGetAppPatchDir
= (void*)GetProcAddress(dll
, "SdbGetAppPatchDir");
452 if (!pSdbGetAppPatchDir
)
454 skip("apphelp.dll not loaded, or does not export SdbGetAppPatchDir\n");
458 argc
= winetest_get_mainargs(&argv
);
461 WCHAR path
[MAX_PATH
];
462 GetModuleFileNameW(NULL
, path
, _countof(path
));
463 dll
= GetModuleHandleW(shim_dll(L
"\\aclayers.dll"));
465 dll
= GetModuleHandleW(shim_dll(L
"\\acgenral.dll"));
467 trace("Loaded under a shim, running each test in it's own process\n");
469 for (n
= 0; n
< _countof(tests
); ++n
)
471 LONG failures
= winetest_get_failures();
479 WCHAR buf
[MAX_PATH
+40];
480 STARTUPINFOW si
= { sizeof(si
) };
481 PROCESS_INFORMATION pi
;
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");
489 winetest_wait_child_process(pi
.hProcess
);
490 CloseHandle(pi
.hThread
);
491 CloseHandle(pi
.hProcess
);
495 ok(failures
== winetest_get_failures(), "Last %u failures are from %d (%s)\n",
496 winetest_get_failures() - failures
, n
, tests
[n
].name
);
501 n
= (size_t)atoi(argv
[2]);
502 if (n
>= 0 && n
< _countof(tests
))
508 ok(0, "Test out of range: %u\n", n
);