99f1bdf3387fea53b09c1928c1aaa23bffc12676
[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 extern DWORD get_host_winver(void);
32 static DWORD g_WinVersion;
33 #define WINVER_ANY 0
34 #define WINVER_VISTA 0x0600
35
36
37 /* apphelp.dll */
38 static BOOL(WINAPI* pSdbGetAppPatchDir)(PVOID, LPWSTR, DWORD);
39
40 /* aclayers.dll */
41 static PVOID(WINAPI* pGetHookAPIs)(LPCSTR, LPCWSTR, PDWORD);
42 static BOOL(WINAPI* pNotifyShims)(DWORD fdwReason, PVOID ptr);
43
44
45 static LONG g_Count;
46 static DEVMODEA g_LastDevmode;
47 static DWORD g_LastFlags;
48
49 LONG (WINAPI *pChangeDisplaySettingsA)(_In_opt_ PDEVMODEA lpDevMode, _In_ DWORD dwflags);
50 LONG WINAPI mChangeDisplaySettingsA(_In_opt_ PDEVMODEA lpDevMode, _In_ DWORD dwflags)
51 {
52 g_Count++;
53 g_LastDevmode = *lpDevMode;
54 g_LastFlags = dwflags;
55
56 return DISP_CHANGE_FAILED;
57 }
58
59
60 static void pre_8bit()
61 {
62 g_Count = 0;
63 memset(&g_LastDevmode, 0, sizeof(g_LastDevmode));
64 g_LastFlags = 0xffffffff;
65 }
66
67 static void post_8bit()
68 {
69 ok_int(g_Count, 1);
70 ok_hex(g_LastDevmode.dmFields & DM_BITSPERPEL, DM_BITSPERPEL);
71 ok_int(g_LastDevmode.dmBitsPerPel, 8);
72 ok_hex(g_LastFlags, CDS_FULLSCREEN);
73 }
74
75 static void pre_640()
76 {
77 g_Count = 0;
78 memset(&g_LastDevmode, 0, sizeof(g_LastDevmode));
79 g_LastFlags = 0xffffffff;
80 }
81
82 static void post_640()
83 {
84 ok_int(g_Count, 1);
85 ok_hex(g_LastDevmode.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT), (DM_PELSWIDTH | DM_PELSHEIGHT));
86 ok_int(g_LastDevmode.dmPelsWidth, 640);
87 ok_int(g_LastDevmode.dmPelsHeight, 480);
88 ok_hex(g_LastFlags, CDS_FULLSCREEN);
89 }
90
91
92
93
94 static PIMAGE_IMPORT_DESCRIPTOR FindImportDescriptor(PBYTE DllBase, PCSTR DllName)
95 {
96 ULONG Size;
97 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = RtlImageDirectoryEntryToData((HMODULE)DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &Size);
98 while (ImportDescriptor->Name && ImportDescriptor->OriginalFirstThunk)
99 {
100 PCHAR Name = (PCHAR)(DllBase + ImportDescriptor->Name);
101 if (!lstrcmpiA(Name, DllName))
102 {
103 return ImportDescriptor;
104 }
105 ImportDescriptor++;
106 }
107 return NULL;
108 }
109
110 static BOOL RedirectIat(HMODULE TargetDll, PCSTR DllName, PCSTR FunctionName, ULONG_PTR NewFunction, ULONG_PTR* OriginalFunction)
111 {
112 PBYTE DllBase = (PBYTE)TargetDll;
113 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = FindImportDescriptor(DllBase, DllName);
114 if (ImportDescriptor)
115 {
116 // On loaded images, OriginalFirstThunk points to the name / ordinal of the function
117 PIMAGE_THUNK_DATA OriginalThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->OriginalFirstThunk);
118 // FirstThunk points to the resolved address.
119 PIMAGE_THUNK_DATA FirstThunk = (PIMAGE_THUNK_DATA)(DllBase + ImportDescriptor->FirstThunk);
120 while (OriginalThunk->u1.AddressOfData && FirstThunk->u1.Function)
121 {
122 if (!IMAGE_SNAP_BY_ORDINAL32(OriginalThunk->u1.AddressOfData))
123 {
124 PIMAGE_IMPORT_BY_NAME ImportName = (PIMAGE_IMPORT_BY_NAME)(DllBase + OriginalThunk->u1.AddressOfData);
125 if (!lstrcmpiA((PCSTR)ImportName->Name, FunctionName))
126 {
127 DWORD dwOld;
128 VirtualProtect(&FirstThunk->u1.Function, sizeof(ULONG_PTR), PAGE_EXECUTE_READWRITE, &dwOld);
129 *OriginalFunction = FirstThunk->u1.Function;
130 FirstThunk->u1.Function = NewFunction;
131 VirtualProtect(&FirstThunk->u1.Function, sizeof(ULONG_PTR), dwOld, &dwOld);
132 return TRUE;
133 }
134 }
135 OriginalThunk++;
136 FirstThunk++;
137 }
138 skip("Unable to find the Import %s!%s\n", DllName, FunctionName);
139 }
140 else
141 {
142 skip("Unable to find the ImportDescriptor for %s\n", DllName);
143 }
144 return FALSE;
145 }
146
147
148
149 static void test_one(LPCSTR shim, DWORD dwReason, void(*pre)(), void(*post)())
150 {
151 DWORD num_shims = 0;
152 WCHAR wide_shim[50] = { 0 };
153 PVOID hook;
154 BOOL ret;
155 MultiByteToWideChar(CP_ACP, 0, shim, -1, wide_shim, 50);
156
157 if (pre)
158 pre();
159
160 hook = pGetHookAPIs("", wide_shim, &num_shims);
161 if (hook == NULL)
162 {
163 skip("Skipping tests for layers (%s) not present in this os (0x%x)\n", shim, g_WinVersion);
164 return;
165 }
166 ok(hook != NULL, "Expected hook to be a valid pointer for %s\n", shim);
167 ok(num_shims == 0, "Expected not to find any apihooks, got: %u for %s\n", num_shims, shim);
168
169 ret = pNotifyShims(dwReason, NULL);
170
171 /* Win7 and Win10 return 1, w2k3 returns a pointer */
172 ok(ret != 0, "Expected pNotifyShims to succeed (%i)\n", ret);
173
174 if (post)
175 post();
176 }
177
178 static struct test_info
179 {
180 const char* name;
181 DWORD winver;
182 DWORD reason;
183 void(*pre)();
184 void(*post)();
185 } tests[] =
186 {
187 { "Force8BitColor", WINVER_ANY, 1, pre_8bit, post_8bit },
188 { "Force8BitColor", WINVER_VISTA, 100, pre_8bit, post_8bit },
189 { "Force640x480", WINVER_ANY, 1, pre_640, post_640 },
190 { "Force640x480", WINVER_VISTA, 100, pre_640, post_640 },
191 /* { "DisableThemes" }, AcGenral.dll */
192 };
193
194
195 static void run_test(size_t n, WCHAR* buf, BOOL unload)
196 {
197 BOOL ret;
198 HMODULE dll;
199
200 trace("Running %d (%s)\n", n, tests[n].name);
201
202 dll = LoadLibraryW(buf);
203 pGetHookAPIs = (void*)GetProcAddress(dll, "GetHookAPIs");
204 pNotifyShims = (void*)GetProcAddress(dll, "NotifyShims");
205
206 if (!pGetHookAPIs || !pNotifyShims)
207 {
208 skip("aclayers.dll not loaded, or does not export GetHookAPIs or pNotifyShims (%s, %p, %p)\n",
209 tests[n].name, pGetHookAPIs, pNotifyShims);
210 return;
211 }
212
213 ret = RedirectIat(dll, "user32.dll", "ChangeDisplaySettingsA", (ULONG_PTR)mChangeDisplaySettingsA, (ULONG_PTR*)&pChangeDisplaySettingsA);
214 if (ret)
215 {
216 test_one(tests[n].name, tests[n].reason, tests[n].pre, tests[n].post);
217 }
218 else
219 {
220 ok(0, "Unable to redirect ChangeDisplaySettingsA!\n");
221 }
222 FreeLibrary(dll);
223 if (unload)
224 {
225 dll = GetModuleHandleW(buf);
226 ok(dll == NULL, "Unable to unload %s\n", wine_dbgstr_w(buf));
227 }
228 }
229
230
231 START_TEST(dispmode)
232 {
233 HMODULE dll = LoadLibraryA("apphelp.dll");
234 WCHAR buf[MAX_PATH];
235 WCHAR aclayers[] = L"\\aclayers.dll";
236 size_t n;
237 int argc;
238 char **argv;
239
240 pSdbGetAppPatchDir = (void*)GetProcAddress(dll, "SdbGetAppPatchDir");
241 if (!pSdbGetAppPatchDir)
242 {
243 skip("apphelp.dll not loaded, or does not export SdbGetAppPatchDir\n");
244 return;
245 }
246
247 g_WinVersion = get_host_winver();
248
249 pSdbGetAppPatchDir(NULL, buf, MAX_PATH);
250 StringCchCatW(buf, _countof(buf), aclayers);
251
252 argc = winetest_get_mainargs(&argv);
253 if (argc < 3)
254 {
255 WCHAR path[MAX_PATH];
256 GetModuleFileNameW(NULL, path, _countof(path));
257 dll = GetModuleHandleW(buf);
258 if (dll != NULL)
259 trace("Loaded under a shim, running each test in it's own process\n");
260
261 for (n = 0; n < _countof(tests); ++n)
262 {
263 if (g_WinVersion < tests[n].winver)
264 continue;
265
266 if (dll == NULL)
267 {
268 run_test(n, buf, TRUE);
269 }
270 else
271 {
272 WCHAR buf[MAX_PATH+40];
273 STARTUPINFOW si = { sizeof(si) };
274 PROCESS_INFORMATION pi;
275 BOOL created;
276
277 StringCchPrintfW(buf, _countof(buf), L"\"%ls\" dispmode %u", path, n);
278 created = CreateProcessW(NULL, buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
279 ok(created, "Expected CreateProcess to succeed\n");
280 if (created)
281 {
282 winetest_wait_child_process(pi.hProcess);
283 CloseHandle(pi.hThread);
284 CloseHandle(pi.hProcess);
285 }
286 }
287 }
288 }
289 else
290 {
291 n = (size_t)atoi(argv[2]);
292 if (n >= 0 && n < _countof(tests))
293 {
294 run_test(n, buf, FALSE);
295 }
296 else
297 {
298 ok(0, "Test out of range: %u\n", n);
299 }
300 }
301 }