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