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