2 * Copyright 2016 Mark Jansen
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.
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.
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
20 #define WIN32_NO_STATUS
29 #include "wine/test.h"
31 extern DWORD
get_host_winver(void);
32 static DWORD g_WinVersion
;
34 #define WINVER_VISTA 0x0600
38 static BOOL(WINAPI
* pSdbGetAppPatchDir
)(PVOID
, LPWSTR
, DWORD
);
41 static PVOID(WINAPI
* pGetHookAPIs
)(LPCSTR
, LPCWSTR
, PDWORD
);
42 static BOOL(WINAPI
* pNotifyShims
)(DWORD fdwReason
, PVOID ptr
);
46 static DEVMODEA g_LastDevmode
;
47 static DWORD g_LastFlags
;
49 LONG (WINAPI
*pChangeDisplaySettingsA
)(_In_opt_ PDEVMODEA lpDevMode
, _In_ DWORD dwflags
);
50 LONG WINAPI
mChangeDisplaySettingsA(_In_opt_ PDEVMODEA lpDevMode
, _In_ DWORD dwflags
)
53 g_LastDevmode
= *lpDevMode
;
54 g_LastFlags
= dwflags
;
56 return DISP_CHANGE_FAILED
;
60 static void pre_8bit()
63 memset(&g_LastDevmode
, 0, sizeof(g_LastDevmode
));
64 g_LastFlags
= 0xffffffff;
67 static void post_8bit()
70 ok_hex(g_LastDevmode
.dmFields
& DM_BITSPERPEL
, DM_BITSPERPEL
);
71 ok_int(g_LastDevmode
.dmBitsPerPel
, 8);
72 ok_hex(g_LastFlags
, CDS_FULLSCREEN
);
78 memset(&g_LastDevmode
, 0, sizeof(g_LastDevmode
));
79 g_LastFlags
= 0xffffffff;
82 static void post_640()
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
);
94 static PIMAGE_IMPORT_DESCRIPTOR
FindImportDescriptor(PBYTE DllBase
, PCSTR DllName
)
97 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor
= RtlImageDirectoryEntryToData((HMODULE
)DllBase
, TRUE
, IMAGE_DIRECTORY_ENTRY_IMPORT
, &Size
);
98 while (ImportDescriptor
->Name
&& ImportDescriptor
->OriginalFirstThunk
)
100 PCHAR Name
= (PCHAR
)(DllBase
+ ImportDescriptor
->Name
);
101 if (!lstrcmpiA(Name
, DllName
))
103 return ImportDescriptor
;
110 static BOOL
RedirectIat(HMODULE TargetDll
, PCSTR DllName
, PCSTR FunctionName
, ULONG_PTR NewFunction
, ULONG_PTR
* OriginalFunction
)
112 PBYTE DllBase
= (PBYTE
)TargetDll
;
113 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor
= FindImportDescriptor(DllBase
, DllName
);
114 if (ImportDescriptor
)
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
)
122 if (!IMAGE_SNAP_BY_ORDINAL32(OriginalThunk
->u1
.AddressOfData
))
124 PIMAGE_IMPORT_BY_NAME ImportName
= (PIMAGE_IMPORT_BY_NAME
)(DllBase
+ OriginalThunk
->u1
.AddressOfData
);
125 if (!lstrcmpiA((PCSTR
)ImportName
->Name
, FunctionName
))
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
);
138 skip("Unable to find the Import %s!%s\n", DllName
, FunctionName
);
142 skip("Unable to find the ImportDescriptor for %s\n", DllName
);
149 static void test_one(LPCSTR shim
, DWORD dwReason
, void(*pre
)(), void(*post
)())
152 WCHAR wide_shim
[50] = { 0 };
155 MultiByteToWideChar(CP_ACP
, 0, shim
, -1, wide_shim
, 50);
160 hook
= pGetHookAPIs("", wide_shim
, &num_shims
);
163 skip("Skipping tests for layers (%s) not present in this os (0x%x)\n", shim
, g_WinVersion
);
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
);
169 ret
= pNotifyShims(dwReason
, NULL
);
171 /* Win7 and Win10 return 1, w2k3 returns a pointer */
172 ok(ret
!= 0, "Expected pNotifyShims to succeed (%i)\n", ret
);
178 static struct test_info
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 */
195 static void run_test(size_t n
, WCHAR
* buf
, BOOL unload
)
200 trace("Running %d (%s)\n", n
, tests
[n
].name
);
202 dll
= LoadLibraryW(buf
);
203 pGetHookAPIs
= (void*)GetProcAddress(dll
, "GetHookAPIs");
204 pNotifyShims
= (void*)GetProcAddress(dll
, "NotifyShims");
206 if (!pGetHookAPIs
|| !pNotifyShims
)
208 skip("aclayers.dll not loaded, or does not export GetHookAPIs or pNotifyShims (%s, %p, %p)\n",
209 tests
[n
].name
, pGetHookAPIs
, pNotifyShims
);
213 ret
= RedirectIat(dll
, "user32.dll", "ChangeDisplaySettingsA", (ULONG_PTR
)mChangeDisplaySettingsA
, (ULONG_PTR
*)&pChangeDisplaySettingsA
);
216 test_one(tests
[n
].name
, tests
[n
].reason
, tests
[n
].pre
, tests
[n
].post
);
220 ok(0, "Unable to redirect ChangeDisplaySettingsA!\n");
225 dll
= GetModuleHandleW(buf
);
226 ok(dll
== NULL
, "Unable to unload %s\n", wine_dbgstr_w(buf
));
233 HMODULE dll
= LoadLibraryA("apphelp.dll");
235 WCHAR aclayers
[] = L
"\\aclayers.dll";
240 pSdbGetAppPatchDir
= (void*)GetProcAddress(dll
, "SdbGetAppPatchDir");
241 if (!pSdbGetAppPatchDir
)
243 skip("apphelp.dll not loaded, or does not export SdbGetAppPatchDir\n");
247 g_WinVersion
= get_host_winver();
249 pSdbGetAppPatchDir(NULL
, buf
, MAX_PATH
);
250 StringCchCatW(buf
, _countof(buf
), aclayers
);
252 argc
= winetest_get_mainargs(&argv
);
255 WCHAR path
[MAX_PATH
];
256 GetModuleFileNameW(NULL
, path
, _countof(path
));
257 dll
= GetModuleHandleW(buf
);
259 trace("Loaded under a shim, running each test in it's own process\n");
261 for (n
= 0; n
< _countof(tests
); ++n
)
263 if (g_WinVersion
< tests
[n
].winver
)
268 run_test(n
, buf
, TRUE
);
272 WCHAR buf
[MAX_PATH
+40];
273 STARTUPINFOW si
= { sizeof(si
) };
274 PROCESS_INFORMATION pi
;
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");
282 winetest_wait_child_process(pi
.hProcess
);
283 CloseHandle(pi
.hThread
);
284 CloseHandle(pi
.hProcess
);
291 n
= (size_t)atoi(argv
[2]);
292 if (n
>= 0 && n
< _countof(tests
))
294 run_test(n
, buf
, FALSE
);
298 ok(0, "Test out of range: %u\n", n
);