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 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); 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); 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); 117 void WINAPI mSetThemeAppProperties(DWORD dwFlags) 118 { 119 g_ThemeCount++; 120 g_LastThemeFlags = dwFlags; 121 } 122 123 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 132 static void pre_8bit_2(void) 133 { 134 bFix = FALSE; 135 136 pre_8bit(); 137 } 138 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 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 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 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 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 203 static void pre_640_2(void) 204 { 205 bFix = FALSE; 206 207 pre_640(); 208 } 209 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 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 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 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 268 static void pre_theme(void) 269 { 270 g_ThemeCount = 0; 271 g_LastThemeFlags = 0xffffffff; 272 } 273 274 static void post_theme(void) 275 { 276 ok_int(g_ThemeCount, 1); 277 ok_hex(g_LastThemeFlags, 0); 278 } 279 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 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 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 307 static BOOL hook_theme(HMODULE dll) 308 { 309 return RedirectIat(dll, "uxtheme.dll", "SetThemeAppProperties", (ULONG_PTR)mSetThemeAppProperties, (ULONG_PTR*)&pSetThemeAppProperties); 310 } 311 312 static VOID unhook_theme(HMODULE dll) 313 { 314 RestoreIat(dll, "uxtheme.dll", "SetThemeAppProperties", (ULONG_PTR)pSetThemeAppProperties); 315 } 316 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 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 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 >= 0 && 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