1c2c66affSColin Finck /*
2c2c66affSColin Finck * PROJECT: ReactOS API Tests
3c2c66affSColin Finck * LICENSE: LGPL - See COPYING.LIB in the top level directory
4c2c66affSColin Finck * PURPOSE: Tests for SHIM engine caching.
5c2c66affSColin Finck * PROGRAMMER: Mark Jansen
6c2c66affSColin Finck */
7c2c66affSColin Finck
8283bbe73SAmine Khaldi #include "precomp.h"
9c2c66affSColin Finck
10283bbe73SAmine Khaldi #include <winsvc.h>
11*465745b6STimo Kreuzer #include <versionhelpers.h>
12c2c66affSColin Finck
13c2c66affSColin Finck enum ServiceCommands
14c2c66affSColin Finck {
15c2c66affSColin Finck RegisterShimCacheWithHandle = 128,
16c2c66affSColin Finck RegisterShimCacheWithoutHandle = 129,
17c2c66affSColin Finck };
18c2c66affSColin Finck
196fe9441dSSerge Gautherie static NTSTATUS (NTAPI *pNtApphelpCacheControl)(APPHELPCACHESERVICECLASS, PAPPHELP_CACHE_SERVICE_LOOKUP);
20c2c66affSColin Finck
CallCacheControl(UNICODE_STRING * PathName,BOOLEAN WithMapping,APPHELPCACHESERVICECLASS Service)21c2c66affSColin Finck NTSTATUS CallCacheControl(UNICODE_STRING* PathName, BOOLEAN WithMapping, APPHELPCACHESERVICECLASS Service)
22c2c66affSColin Finck {
23c2c66affSColin Finck APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} };
24c2c66affSColin Finck NTSTATUS Status;
25c2c66affSColin Finck CacheEntry.ImageName = *PathName;
26c2c66affSColin Finck if (WithMapping)
27c2c66affSColin Finck {
28c2c66affSColin Finck OBJECT_ATTRIBUTES LocalObjectAttributes;
29c2c66affSColin Finck IO_STATUS_BLOCK IoStatusBlock;
30c2c66affSColin Finck InitializeObjectAttributes(&LocalObjectAttributes, PathName,
31c2c66affSColin Finck OBJ_CASE_INSENSITIVE, NULL, NULL);
32c2c66affSColin Finck Status = NtOpenFile(&CacheEntry.ImageHandle,
33c2c66affSColin Finck SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_EXECUTE,
34c2c66affSColin Finck &LocalObjectAttributes, &IoStatusBlock,
35c2c66affSColin Finck FILE_SHARE_READ | FILE_SHARE_DELETE,
36c2c66affSColin Finck FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
37c2c66affSColin Finck ok_ntstatus(Status, STATUS_SUCCESS);
38c2c66affSColin Finck }
39c2c66affSColin Finck else
40c2c66affSColin Finck {
41c2c66affSColin Finck CacheEntry.ImageHandle = INVALID_HANDLE_VALUE;
42c2c66affSColin Finck }
436fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(Service, &CacheEntry);
44c2c66affSColin Finck if (CacheEntry.ImageHandle != INVALID_HANDLE_VALUE)
45c2c66affSColin Finck NtClose(CacheEntry.ImageHandle);
46c2c66affSColin Finck return Status;
47c2c66affSColin Finck }
48c2c66affSColin Finck
InitEnv(UNICODE_STRING * PathName)49c2c66affSColin Finck int InitEnv(UNICODE_STRING* PathName)
50c2c66affSColin Finck {
51c2c66affSColin Finck NTSTATUS Status = CallCacheControl(PathName, FALSE, ApphelpCacheServiceRemove);
52c2c66affSColin Finck if (Status == STATUS_INVALID_PARAMETER)
53c2c66affSColin Finck {
54c2c66affSColin Finck /* Windows Vista+ has a different layout for APPHELP_CACHE_SERVICE_LOOKUP */
55c2c66affSColin Finck return 0;
56c2c66affSColin Finck }
57c2c66affSColin Finck ok(Status == STATUS_SUCCESS || Status == STATUS_NOT_FOUND,
58c2c66affSColin Finck "Wrong value for Status, expected: SUCCESS or NOT_FOUND, got: 0x%lx\n",
59c2c66affSColin Finck Status);
60c2c66affSColin Finck return 1;
61c2c66affSColin Finck }
62c2c66affSColin Finck
CheckValidation(UNICODE_STRING * PathName)63c2c66affSColin Finck void CheckValidation(UNICODE_STRING* PathName)
64c2c66affSColin Finck {
65c2c66affSColin Finck APPHELP_CACHE_SERVICE_LOOKUP CacheEntry = { {0} };
66c2c66affSColin Finck NTSTATUS Status;
67c2c66affSColin Finck
68c2c66affSColin Finck /* Validate the handling of a NULL pointer */
696fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(ApphelpCacheServiceRemove, NULL);
70c2c66affSColin Finck ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
716fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, NULL);
72c2c66affSColin Finck ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
73c2c66affSColin Finck
74c2c66affSColin Finck /* Validate the handling of a NULL pointer inside the struct */
756fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(ApphelpCacheServiceRemove, &CacheEntry);
76c2c66affSColin Finck ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
776fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
78c2c66affSColin Finck ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
79c2c66affSColin Finck
80c2c66affSColin Finck /* Just call the dump function */
816fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(ApphelpCacheServiceDump, NULL);
82c2c66affSColin Finck ok_ntstatus(Status, STATUS_SUCCESS);
83c2c66affSColin Finck
84c2c66affSColin Finck /* Validate the handling of an invalid handle inside the struct */
85c2c66affSColin Finck CacheEntry.ImageName = *PathName;
86c2c66affSColin Finck CacheEntry.ImageHandle = (HANDLE)2;
876fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
88*465745b6STimo Kreuzer ok_ntstatus(Status, IsWindows7OrGreater() ? STATUS_NOT_FOUND : STATUS_INVALID_PARAMETER);
89c2c66affSColin Finck
90c2c66affSColin Finck /* Validate the handling of an invalid service number */
916fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(999, NULL);
92c2c66affSColin Finck ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
936fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(999, &CacheEntry);
94c2c66affSColin Finck ok_ntstatus(Status, STATUS_INVALID_PARAMETER);
95c2c66affSColin Finck }
96c2c66affSColin Finck
RequestAddition(SC_HANDLE service_handle,BOOLEAN WithMapping)97c2c66affSColin Finck static BOOLEAN RequestAddition(SC_HANDLE service_handle, BOOLEAN WithMapping)
98c2c66affSColin Finck {
99c2c66affSColin Finck SERVICE_STATUS Status;
100c2c66affSColin Finck ControlService(service_handle, WithMapping ? RegisterShimCacheWithHandle :
101c2c66affSColin Finck RegisterShimCacheWithoutHandle, &Status);
102c2c66affSColin Finck /* TODO: how to get a return code from the service? */
103c2c66affSColin Finck return TRUE;
104c2c66affSColin Finck }
105c2c66affSColin Finck
RunApphelpCacheControlTests(SC_HANDLE service_handle)106c2c66affSColin Finck static void RunApphelpCacheControlTests(SC_HANDLE service_handle)
107c2c66affSColin Finck {
108c2c66affSColin Finck WCHAR szPath[MAX_PATH];
109c2c66affSColin Finck UNICODE_STRING ntPath;
110c2c66affSColin Finck BOOLEAN Result;
111c2c66affSColin Finck NTSTATUS Status;
112c2c66affSColin Finck APPHELP_CACHE_SERVICE_LOOKUP CacheEntry;
113c2c66affSColin Finck
114c2c66affSColin Finck GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0]));
115c2c66affSColin Finck Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL);
116c2c66affSColin Finck ok(Result == TRUE, "RtlDosPathNameToNtPathName_U\n");
117c2c66affSColin Finck if (!InitEnv(&ntPath))
118c2c66affSColin Finck {
119c2c66affSColin Finck skip("NtApphelpCacheControl expects a different structure layout\n");
120c2c66affSColin Finck return;
121c2c66affSColin Finck }
122c2c66affSColin Finck /* At this point we have made sure that our binary is not present in the cache,
123c2c66affSColin Finck and that the NtApphelpCacheControl function expects the struct layout we use. */
124c2c66affSColin Finck CheckValidation(&ntPath);
125c2c66affSColin Finck
126c2c66affSColin Finck /* We expect not to find it */
127c2c66affSColin Finck Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
128c2c66affSColin Finck ok_ntstatus(Status, STATUS_NOT_FOUND);
129c2c66affSColin Finck Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup);
130c2c66affSColin Finck ok_ntstatus(Status, STATUS_NOT_FOUND);
131c2c66affSColin Finck
132c2c66affSColin Finck /* First we add our process without a file handle (so it will be registered without file info) */
133c2c66affSColin Finck RequestAddition(service_handle, FALSE);
134c2c66affSColin Finck
135c2c66affSColin Finck /* now we try to find it without validating file info */
136c2c66affSColin Finck Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup);
137c2c66affSColin Finck ok_ntstatus(Status, STATUS_SUCCESS);
138c2c66affSColin Finck /* when validating file info the cache notices the file is wrong, so it is dropped from the cache */
139c2c66affSColin Finck Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
140c2c66affSColin Finck ok_ntstatus(Status, STATUS_NOT_FOUND);
141c2c66affSColin Finck /* making the second check without info also fail. */
142c2c66affSColin Finck Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup);
143c2c66affSColin Finck ok_ntstatus(Status, STATUS_NOT_FOUND);
144c2c66affSColin Finck
145c2c66affSColin Finck
146c2c66affSColin Finck /* Now we add the file with file info */
147c2c66affSColin Finck RequestAddition(service_handle, TRUE);
148c2c66affSColin Finck
149c2c66affSColin Finck /* so both checks should succeed */
150c2c66affSColin Finck Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
151c2c66affSColin Finck ok_ntstatus(Status, STATUS_SUCCESS);
152c2c66affSColin Finck Status = CallCacheControl(&ntPath, FALSE, ApphelpCacheServiceLookup);
153c2c66affSColin Finck ok_ntstatus(Status, STATUS_SUCCESS);
154c2c66affSColin Finck
155c2c66affSColin Finck /* We know the file is in the cache now (assuming previous tests succeeded,
156c2c66affSColin Finck let's test invalid handle behavior */
157c2c66affSColin Finck CacheEntry.ImageName = ntPath;
158c2c66affSColin Finck CacheEntry.ImageHandle = 0;
1596fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
160*465745b6STimo Kreuzer ok_ntstatus(Status, IsWindows7OrGreater() ? STATUS_NOT_FOUND : STATUS_INVALID_PARAMETER);
161c2c66affSColin Finck
162c2c66affSColin Finck /* re-add it for the next test */
163c2c66affSColin Finck RequestAddition(service_handle, TRUE);
164c2c66affSColin Finck Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
165c2c66affSColin Finck ok_ntstatus(Status, STATUS_SUCCESS);
166c2c66affSColin Finck CacheEntry.ImageHandle = (HANDLE)1;
1676fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
168*465745b6STimo Kreuzer ok_ntstatus(Status, IsWindows7OrGreater() ? STATUS_NOT_FOUND : STATUS_INVALID_PARAMETER);
169c2c66affSColin Finck
170c2c66affSColin Finck /* and again */
171c2c66affSColin Finck RequestAddition(service_handle, TRUE);
172c2c66affSColin Finck Status = CallCacheControl(&ntPath, TRUE, ApphelpCacheServiceLookup);
173c2c66affSColin Finck ok_ntstatus(Status, STATUS_SUCCESS);
1746b1ca758STimo Kreuzer #ifdef _WIN64
1756b1ca758STimo Kreuzer CacheEntry.ImageHandle = (HANDLE)0x8000000000000000ULL;
1766b1ca758STimo Kreuzer #else
177c2c66affSColin Finck CacheEntry.ImageHandle = (HANDLE)0x80000000;
1786b1ca758STimo Kreuzer #endif
1796fe9441dSSerge Gautherie Status = pNtApphelpCacheControl(ApphelpCacheServiceLookup, &CacheEntry);
180*465745b6STimo Kreuzer ok_ntstatus(Status, IsWindows7OrGreater() ? STATUS_NOT_FOUND : STATUS_INVALID_PARAMETER);
181c2c66affSColin Finck
182c2c66affSColin Finck RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);
183c2c66affSColin Finck }
184c2c66affSColin Finck
185c2c66affSColin Finck
186c2c66affSColin Finck /* Most service related code was taken from services_winetest:service and modified for usage here
187c2c66affSColin Finck The rest came from MSDN */
188c2c66affSColin Finck
189c2c66affSColin Finck static SERVICE_STATUS_HANDLE (WINAPI *pRegisterServiceCtrlHandlerExA)(LPCSTR,LPHANDLER_FUNCTION_EX,LPVOID);
190c2c66affSColin Finck static char service_name[100] = "apphelp_test_service";
191c2c66affSColin Finck static HANDLE service_stop_event;
192c2c66affSColin Finck static SERVICE_STATUS_HANDLE service_status;
193c2c66affSColin Finck
RegisterInShimCache(BOOLEAN WithMapping)194c2c66affSColin Finck static BOOLEAN RegisterInShimCache(BOOLEAN WithMapping)
195c2c66affSColin Finck {
196c2c66affSColin Finck WCHAR szPath[MAX_PATH];
197c2c66affSColin Finck UNICODE_STRING ntPath;
198c2c66affSColin Finck BOOLEAN Result;
199c2c66affSColin Finck NTSTATUS Status;
200c2c66affSColin Finck GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(szPath[0]));
201c2c66affSColin Finck Result = RtlDosPathNameToNtPathName_U(szPath, &ntPath, NULL, NULL);
202c2c66affSColin Finck if (!Result)
203c2c66affSColin Finck {
204c2c66affSColin Finck DbgPrint("RegisterInShimCache: RtlDosPathNameToNtPathName_U failed\n");
205c2c66affSColin Finck return FALSE;
206c2c66affSColin Finck }
207c2c66affSColin Finck
208c2c66affSColin Finck Status = CallCacheControl(&ntPath, WithMapping, ApphelpCacheServiceUpdate);
209c2c66affSColin Finck if (!NT_SUCCESS(Status))
210c2c66affSColin Finck {
211c2c66affSColin Finck DbgPrint("RegisterInShimCache: CallCacheControl failed\n");
212c2c66affSColin Finck RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);
213c2c66affSColin Finck return FALSE;
214c2c66affSColin Finck }
215c2c66affSColin Finck RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath.Buffer);
216c2c66affSColin Finck return TRUE;
217c2c66affSColin Finck }
218c2c66affSColin Finck
219c2c66affSColin Finck
service_handler(DWORD ctrl,DWORD event_type,void * event_data,void * context)220c2c66affSColin Finck static DWORD WINAPI service_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
221c2c66affSColin Finck {
222c2c66affSColin Finck SERVICE_STATUS status = {0};
223c2c66affSColin Finck status.dwServiceType = SERVICE_WIN32;
224c2c66affSColin Finck status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
225c2c66affSColin Finck
226c2c66affSColin Finck switch(ctrl)
227c2c66affSColin Finck {
228c2c66affSColin Finck case SERVICE_CONTROL_STOP:
229c2c66affSColin Finck case SERVICE_CONTROL_SHUTDOWN:
230c2c66affSColin Finck status.dwCurrentState = SERVICE_STOP_PENDING;
231c2c66affSColin Finck status.dwControlsAccepted = 0;
232c2c66affSColin Finck SetServiceStatus(service_status, &status);
233c2c66affSColin Finck SetEvent(service_stop_event);
234c2c66affSColin Finck return NO_ERROR;
235c2c66affSColin Finck case RegisterShimCacheWithHandle:
236c2c66affSColin Finck if (!RegisterInShimCache(TRUE))
237c2c66affSColin Finck {
238c2c66affSColin Finck /* TODO: how should we communicate a failure? */
239c2c66affSColin Finck }
240c2c66affSColin Finck break;
241c2c66affSColin Finck case RegisterShimCacheWithoutHandle:
242c2c66affSColin Finck if (!RegisterInShimCache(FALSE))
243c2c66affSColin Finck {
244c2c66affSColin Finck /* TODO: how should we communicate a failure? */
245c2c66affSColin Finck }
246c2c66affSColin Finck break;
247c2c66affSColin Finck default:
248c2c66affSColin Finck DbgPrint("Unhandled: %d\n", ctrl);
249c2c66affSColin Finck break;
250c2c66affSColin Finck }
251c2c66affSColin Finck status.dwCurrentState = SERVICE_RUNNING;
252c2c66affSColin Finck SetServiceStatus(service_status, &status);
253c2c66affSColin Finck return NO_ERROR;
254c2c66affSColin Finck }
255c2c66affSColin Finck
service_main(DWORD argc,char ** argv)256c2c66affSColin Finck static void WINAPI service_main(DWORD argc, char **argv)
257c2c66affSColin Finck {
258c2c66affSColin Finck SERVICE_STATUS status = {0};
259c2c66affSColin Finck service_status = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL);
260c2c66affSColin Finck if(!service_status)
261c2c66affSColin Finck return;
262c2c66affSColin Finck
263c2c66affSColin Finck status.dwServiceType = SERVICE_WIN32;
264c2c66affSColin Finck status.dwCurrentState = SERVICE_RUNNING;
265c2c66affSColin Finck status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
266c2c66affSColin Finck SetServiceStatus(service_status, &status);
267c2c66affSColin Finck
268c2c66affSColin Finck WaitForSingleObject(service_stop_event, INFINITE);
269c2c66affSColin Finck
270c2c66affSColin Finck status.dwCurrentState = SERVICE_STOPPED;
271c2c66affSColin Finck status.dwControlsAccepted = 0;
272c2c66affSColin Finck SetServiceStatus(service_status, &status);
273c2c66affSColin Finck }
274c2c66affSColin Finck
InstallService(SC_HANDLE scm_handle)275c2c66affSColin Finck static SC_HANDLE InstallService(SC_HANDLE scm_handle)
276c2c66affSColin Finck {
277c2c66affSColin Finck char service_cmd[MAX_PATH+150], *ptr;
278c2c66affSColin Finck SC_HANDLE service;
279c2c66affSColin Finck
280c2c66affSColin Finck ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH);
281c2c66affSColin Finck strcpy(ptr, " NtApphelpCacheControl service");
282c2c66affSColin Finck ptr += strlen(ptr);
283c2c66affSColin Finck
284c2c66affSColin Finck service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL,
285c2c66affSColin Finck SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
286c2c66affSColin Finck service_cmd, NULL, NULL, NULL, NULL, NULL);
287c2c66affSColin Finck if (!service)
288c2c66affSColin Finck {
289c2c66affSColin Finck skip("Could not create helper service\n");
290c2c66affSColin Finck return NULL;
291c2c66affSColin Finck }
292c2c66affSColin Finck return service;
293c2c66affSColin Finck }
294c2c66affSColin Finck
WaitService(SC_HANDLE service_handle,DWORD Status,SERVICE_STATUS_PROCESS * ssp)295c2c66affSColin Finck static void WaitService(SC_HANDLE service_handle, DWORD Status, SERVICE_STATUS_PROCESS* ssp)
296c2c66affSColin Finck {
297c2c66affSColin Finck DWORD dwBytesNeeded;
298c2c66affSColin Finck DWORD dwStartTime = GetTickCount();
299c2c66affSColin Finck while (ssp->dwCurrentState != Status)
300c2c66affSColin Finck {
301c2c66affSColin Finck Sleep(40);
302c2c66affSColin Finck if (!QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO,
303c2c66affSColin Finck (LPBYTE)ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded ))
304c2c66affSColin Finck {
305c2c66affSColin Finck ok(0, "QueryServiceStatusEx failed waiting for %lu\n", Status);
306c2c66affSColin Finck break;
307c2c66affSColin Finck }
308c2c66affSColin Finck if ((GetTickCount() - dwStartTime) > 1000)
309c2c66affSColin Finck {
310c2c66affSColin Finck ok(0, "Timeout waiting for (%lu) from service, is: %lu.\n",
311c2c66affSColin Finck Status, ssp->dwCurrentState);
312c2c66affSColin Finck break;
313c2c66affSColin Finck }
314c2c66affSColin Finck }
315c2c66affSColin Finck }
316c2c66affSColin Finck
RunTest()317c2c66affSColin Finck static void RunTest()
318c2c66affSColin Finck {
319c2c66affSColin Finck SC_HANDLE scm_handle = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
320c2c66affSColin Finck SC_HANDLE service_handle = InstallService(scm_handle);
321c2c66affSColin Finck if (service_handle)
322c2c66affSColin Finck {
323c2c66affSColin Finck SERVICE_STATUS_PROCESS ssp = {0};
324c2c66affSColin Finck BOOL res = StartServiceA(service_handle, 0, NULL);
325c2c66affSColin Finck if (res)
326c2c66affSColin Finck {
327c2c66affSColin Finck WaitService(service_handle, SERVICE_RUNNING, &ssp);
328c2c66affSColin Finck RunApphelpCacheControlTests(service_handle);
329c2c66affSColin Finck ControlService(service_handle, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp);
330c2c66affSColin Finck WaitService(service_handle, SERVICE_STOPPED, &ssp);
331c2c66affSColin Finck }
332c2c66affSColin Finck else
333c2c66affSColin Finck {
334c2c66affSColin Finck skip("Could not start helper service\n");
335c2c66affSColin Finck }
336c2c66affSColin Finck DeleteService(service_handle);
337c2c66affSColin Finck }
338c2c66affSColin Finck CloseServiceHandle(scm_handle);
339c2c66affSColin Finck }
340c2c66affSColin Finck
START_TEST(NtApphelpCacheControl)341c2c66affSColin Finck START_TEST(NtApphelpCacheControl)
342c2c66affSColin Finck {
343c2c66affSColin Finck char **argv;
344c2c66affSColin Finck int argc;
345c2c66affSColin Finck
346c2c66affSColin Finck pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
347c2c66affSColin Finck if (!pRegisterServiceCtrlHandlerExA)
348c2c66affSColin Finck {
349c2c66affSColin Finck win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
350c2c66affSColin Finck return;
351c2c66affSColin Finck }
3526fe9441dSSerge Gautherie
3536fe9441dSSerge Gautherie pNtApphelpCacheControl = (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtApphelpCacheControl");
3546fe9441dSSerge Gautherie if (!pNtApphelpCacheControl)
3556fe9441dSSerge Gautherie {
3566fe9441dSSerge Gautherie win_skip("NtApphelpCacheControl not available, skipping tests\n");
3576fe9441dSSerge Gautherie return;
3586fe9441dSSerge Gautherie }
3596fe9441dSSerge Gautherie
360c2c66affSColin Finck argc = winetest_get_mainargs(&argv);
361c2c66affSColin Finck if(argc < 3)
362c2c66affSColin Finck {
363c2c66affSColin Finck RunTest();
364c2c66affSColin Finck }
365c2c66affSColin Finck else
366c2c66affSColin Finck {
367c2c66affSColin Finck SERVICE_TABLE_ENTRYA servtbl[] = {
368c2c66affSColin Finck {service_name, service_main},
369c2c66affSColin Finck {NULL, NULL}
370c2c66affSColin Finck };
371c2c66affSColin Finck service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
372c2c66affSColin Finck StartServiceCtrlDispatcherA(servtbl);
373c2c66affSColin Finck Sleep(50);
374c2c66affSColin Finck CloseHandle(service_stop_event);
375c2c66affSColin Finck }
376c2c66affSColin Finck }
377c2c66affSColin Finck
378c2c66affSColin Finck
379