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 static 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
;
59 static LONG g_ThemeCount
;
60 static DWORD g_LastThemeFlags
;
62 static void (WINAPI
*pSetThemeAppProperties
)(DWORD dwFlags
);
63 void WINAPI
mSetThemeAppProperties(DWORD dwFlags
)
66 g_LastThemeFlags
= dwFlags
;
70 static const WCHAR
* shim_dll(const WCHAR
* name
)
72 static WCHAR buf
[MAX_PATH
];
73 pSdbGetAppPatchDir(NULL
, buf
, MAX_PATH
);
74 StringCchCatW(buf
, _countof(buf
), name
);
78 static void pre_8bit(void)
81 memset(&g_LastDevmode
, 0, sizeof(g_LastDevmode
));
82 g_LastFlags
= 0xffffffff;
85 static void post_8bit(void)
88 ok_hex(g_LastDevmode
.dmFields
& DM_BITSPERPEL
, DM_BITSPERPEL
);
89 ok_int(g_LastDevmode
.dmBitsPerPel
, 8);
90 ok_hex(g_LastFlags
, CDS_FULLSCREEN
);
93 static void pre_640(void)
96 memset(&g_LastDevmode
, 0, sizeof(g_LastDevmode
));
97 g_LastFlags
= 0xffffffff;
100 static void post_640(void)
103 ok_hex(g_LastDevmode
.dmFields
& (DM_PELSWIDTH
| DM_PELSHEIGHT
), (DM_PELSWIDTH
| DM_PELSHEIGHT
));
104 ok_int(g_LastDevmode
.dmPelsWidth
, 640);
105 ok_int(g_LastDevmode
.dmPelsHeight
, 480);
106 ok_hex(g_LastFlags
, CDS_FULLSCREEN
);
109 static void pre_theme(void)
112 g_LastThemeFlags
= 0xffffffff;
115 static void post_theme(void)
117 ok_int(g_ThemeCount
, 1);
118 ok_hex(g_LastThemeFlags
, 0);
121 static PIMAGE_IMPORT_DESCRIPTOR
FindImportDescriptor(PBYTE DllBase
, PCSTR DllName
)
124 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor
= RtlImageDirectoryEntryToData((HMODULE
)DllBase
, TRUE
, IMAGE_DIRECTORY_ENTRY_IMPORT
, &Size
);
125 while (ImportDescriptor
->Name
&& ImportDescriptor
->OriginalFirstThunk
)
127 PCHAR Name
= (PCHAR
)(DllBase
+ ImportDescriptor
->Name
);
128 if (!lstrcmpiA(Name
, DllName
))
130 return ImportDescriptor
;
137 static BOOL
RedirectIat(HMODULE TargetDll
, PCSTR DllName
, PCSTR FunctionName
, ULONG_PTR NewFunction
, ULONG_PTR
* OriginalFunction
)
139 PBYTE DllBase
= (PBYTE
)TargetDll
;
140 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor
= FindImportDescriptor(DllBase
, DllName
);
141 if (ImportDescriptor
)
143 // On loaded images, OriginalFirstThunk points to the name / ordinal of the function
144 PIMAGE_THUNK_DATA OriginalThunk
= (PIMAGE_THUNK_DATA
)(DllBase
+ ImportDescriptor
->OriginalFirstThunk
);
145 // FirstThunk points to the resolved address.
146 PIMAGE_THUNK_DATA FirstThunk
= (PIMAGE_THUNK_DATA
)(DllBase
+ ImportDescriptor
->FirstThunk
);
147 while (OriginalThunk
->u1
.AddressOfData
&& FirstThunk
->u1
.Function
)
149 if (!IMAGE_SNAP_BY_ORDINAL32(OriginalThunk
->u1
.AddressOfData
))
151 PIMAGE_IMPORT_BY_NAME ImportName
= (PIMAGE_IMPORT_BY_NAME
)(DllBase
+ OriginalThunk
->u1
.AddressOfData
);
152 if (!lstrcmpiA((PCSTR
)ImportName
->Name
, FunctionName
))
155 VirtualProtect(&FirstThunk
->u1
.Function
, sizeof(ULONG_PTR
), PAGE_EXECUTE_READWRITE
, &dwOld
);
156 *OriginalFunction
= FirstThunk
->u1
.Function
;
157 FirstThunk
->u1
.Function
= NewFunction
;
158 VirtualProtect(&FirstThunk
->u1
.Function
, sizeof(ULONG_PTR
), dwOld
, &dwOld
);
165 skip("Unable to find the Import %s!%s\n", DllName
, FunctionName
);
169 skip("Unable to find the ImportDescriptor for %s\n", DllName
);
174 static BOOL
hook_disp(HMODULE dll
)
176 return RedirectIat(dll
, "user32.dll", "ChangeDisplaySettingsA", (ULONG_PTR
)mChangeDisplaySettingsA
, (ULONG_PTR
*)&pChangeDisplaySettingsA
);
179 static BOOL
hook_theme(HMODULE dll
)
181 return RedirectIat(dll
, "uxtheme.dll", "SetThemeAppProperties", (ULONG_PTR
)mSetThemeAppProperties
, (ULONG_PTR
*)&pSetThemeAppProperties
);
185 static void test_one(LPCSTR shim
, DWORD dwReason
, void(*pre
)(), void(*post
)())
188 WCHAR wide_shim
[50] = { 0 };
191 MultiByteToWideChar(CP_ACP
, 0, shim
, -1, wide_shim
, 50);
196 hook
= pGetHookAPIs("", wide_shim
, &num_shims
);
199 skip("Skipping tests for layers (%s) not present in this os (0x%x)\n", shim
, g_WinVersion
);
202 ok(hook
!= NULL
, "Expected hook to be a valid pointer for %s\n", shim
);
203 ok(num_shims
== 0, "Expected not to find any apihooks, got: %u for %s\n", num_shims
, shim
);
205 ret
= pNotifyShims(dwReason
, NULL
);
207 /* Win7 and Win10 return 1, w2k3 returns a pointer */
208 ok(ret
!= 0, "Expected pNotifyShims to succeed (%i)\n", ret
);
214 static struct test_info
220 BOOL(*hook
)(HMODULE
);
225 { "Force8BitColor", L
"\\aclayers.dll", WINVER_ANY
, 1, hook_disp
, pre_8bit
, post_8bit
},
226 { "Force8BitColor", L
"\\aclayers.dll", WINVER_VISTA
, 100, hook_disp
, pre_8bit
, post_8bit
},
227 { "Force640x480", L
"\\aclayers.dll", WINVER_ANY
, 1, hook_disp
, pre_640
, post_640
},
228 { "Force640x480", L
"\\aclayers.dll", WINVER_VISTA
, 100, hook_disp
, pre_640
, post_640
},
229 { "DisableThemes", L
"\\acgenral.dll", WINVER_ANY
, 1, hook_theme
, pre_theme
, post_theme
},
230 { "DisableThemes", L
"\\acgenral.dll", WINVER_VISTA
, 100, hook_theme
, pre_theme
, post_theme
},
234 static void run_test(size_t n
, BOOL unload
)
238 const WCHAR
* buf
= shim_dll(tests
[n
].dll
);
240 dll
= LoadLibraryW(shim_dll(tests
[n
].dll
));
241 pGetHookAPIs
= (void*)GetProcAddress(dll
, "GetHookAPIs");
242 pNotifyShims
= (void*)GetProcAddress(dll
, "NotifyShims");
244 if (!pGetHookAPIs
|| !pNotifyShims
)
246 skip("aclayers.dll not loaded, or does not export GetHookAPIs or pNotifyShims (%s, %p, %p)\n",
247 tests
[n
].name
, pGetHookAPIs
, pNotifyShims
);
251 ret
= tests
[n
].hook(dll
);
254 test_one(tests
[n
].name
, tests
[n
].reason
, tests
[n
].pre
, tests
[n
].post
);
258 ok(0, "Unable to redirect functions!\n");
263 dll
= GetModuleHandleW(buf
);
264 ok(dll
== NULL
, "Unable to unload %s\n", wine_dbgstr_w(buf
));
271 HMODULE dll
= LoadLibraryA("apphelp.dll");
276 pSdbGetAppPatchDir
= (void*)GetProcAddress(dll
, "SdbGetAppPatchDir");
277 if (!pSdbGetAppPatchDir
)
279 skip("apphelp.dll not loaded, or does not export SdbGetAppPatchDir\n");
283 g_WinVersion
= get_host_winver();
285 argc
= winetest_get_mainargs(&argv
);
288 WCHAR path
[MAX_PATH
];
289 GetModuleFileNameW(NULL
, path
, _countof(path
));
290 dll
= GetModuleHandleW(shim_dll(L
"\\aclayers.dll"));
292 dll
= GetModuleHandleW(shim_dll(L
"\\acgenral.dll"));
294 trace("Loaded under a shim, running each test in it's own process\n");
296 for (n
= 0; n
< _countof(tests
); ++n
)
298 LONG failures
= winetest_get_failures();
300 if (g_WinVersion
< tests
[n
].winver
)
309 WCHAR buf
[MAX_PATH
+40];
310 STARTUPINFOW si
= { sizeof(si
) };
311 PROCESS_INFORMATION pi
;
314 StringCchPrintfW(buf
, _countof(buf
), L
"\"%ls\" dispmode %u", path
, n
);
315 created
= CreateProcessW(NULL
, buf
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
);
316 ok(created
, "Expected CreateProcess to succeed\n");
319 winetest_wait_child_process(pi
.hProcess
);
320 CloseHandle(pi
.hThread
);
321 CloseHandle(pi
.hProcess
);
325 if (failures
!= winetest_get_failures())
327 trace("Failures from %d (%s)\n", n
, tests
[n
].name
);
333 n
= (size_t)atoi(argv
[2]);
334 if (n
>= 0 && n
< _countof(tests
))
340 ok(0, "Test out of range: %u\n", n
);