2 * PROJECT: ReactOS API Tests
3 * LICENSE: LGPL - See COPYING.LIB in the top level directory
4 * PURPOSE: Tests for SHIM engine caching.
5 * PROGRAMMER: Mark Jansen
14 RegisterShimCacheWithHandle
= 128,
15 RegisterShimCacheWithoutHandle
= 129,
18 static NTSTATUS (NTAPI
*pNtApphelpCacheControl
)(APPHELPCACHESERVICECLASS
, PAPPHELP_CACHE_SERVICE_LOOKUP
);
20 NTSTATUS
CallCacheControl(UNICODE_STRING
* PathName
, BOOLEAN WithMapping
, APPHELPCACHESERVICECLASS Service
)
22 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry
= { {0} };
24 CacheEntry
.ImageName
= *PathName
;
27 OBJECT_ATTRIBUTES LocalObjectAttributes
;
28 IO_STATUS_BLOCK IoStatusBlock
;
29 InitializeObjectAttributes(&LocalObjectAttributes
, PathName
,
30 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
31 Status
= NtOpenFile(&CacheEntry
.ImageHandle
,
32 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
| FILE_READ_DATA
| FILE_EXECUTE
,
33 &LocalObjectAttributes
, &IoStatusBlock
,
34 FILE_SHARE_READ
| FILE_SHARE_DELETE
,
35 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
);
36 ok_ntstatus(Status
, STATUS_SUCCESS
);
40 CacheEntry
.ImageHandle
= INVALID_HANDLE_VALUE
;
42 Status
= pNtApphelpCacheControl(Service
, &CacheEntry
);
43 if (CacheEntry
.ImageHandle
!= INVALID_HANDLE_VALUE
)
44 NtClose(CacheEntry
.ImageHandle
);
48 int InitEnv(UNICODE_STRING
* PathName
)
50 NTSTATUS Status
= CallCacheControl(PathName
, FALSE
, ApphelpCacheServiceRemove
);
51 if (Status
== STATUS_INVALID_PARAMETER
)
53 /* Windows Vista+ has a different layout for APPHELP_CACHE_SERVICE_LOOKUP */
56 ok(Status
== STATUS_SUCCESS
|| Status
== STATUS_NOT_FOUND
,
57 "Wrong value for Status, expected: SUCCESS or NOT_FOUND, got: 0x%lx\n",
62 void CheckValidation(UNICODE_STRING
* PathName
)
64 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry
= { {0} };
67 /* Validate the handling of a NULL pointer */
68 Status
= pNtApphelpCacheControl(ApphelpCacheServiceRemove
, NULL
);
69 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
70 Status
= pNtApphelpCacheControl(ApphelpCacheServiceLookup
, NULL
);
71 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
73 /* Validate the handling of a NULL pointer inside the struct */
74 Status
= pNtApphelpCacheControl(ApphelpCacheServiceRemove
, &CacheEntry
);
75 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
76 Status
= pNtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
77 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
79 /* Just call the dump function */
80 Status
= pNtApphelpCacheControl(ApphelpCacheServiceDump
, NULL
);
81 ok_ntstatus(Status
, STATUS_SUCCESS
);
83 /* Validate the handling of an invalid handle inside the struct */
84 CacheEntry
.ImageName
= *PathName
;
85 CacheEntry
.ImageHandle
= (HANDLE
)2;
86 Status
= pNtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
87 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
89 /* Validate the handling of an invalid service number */
90 Status
= pNtApphelpCacheControl(999, NULL
);
91 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
92 Status
= pNtApphelpCacheControl(999, &CacheEntry
);
93 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
96 static BOOLEAN
RequestAddition(SC_HANDLE service_handle
, BOOLEAN WithMapping
)
98 SERVICE_STATUS Status
;
99 ControlService(service_handle
, WithMapping
? RegisterShimCacheWithHandle
:
100 RegisterShimCacheWithoutHandle
, &Status
);
101 /* TODO: how to get a return code from the service? */
105 static void RunApphelpCacheControlTests(SC_HANDLE service_handle
)
107 WCHAR szPath
[MAX_PATH
];
108 UNICODE_STRING ntPath
;
111 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry
;
113 GetModuleFileNameW(NULL
, szPath
, sizeof(szPath
) / sizeof(szPath
[0]));
114 Result
= RtlDosPathNameToNtPathName_U(szPath
, &ntPath
, NULL
, NULL
);
115 ok(Result
== TRUE
, "RtlDosPathNameToNtPathName_U\n");
116 if (!InitEnv(&ntPath
))
118 skip("NtApphelpCacheControl expects a different structure layout\n");
121 /* At this point we have made sure that our binary is not present in the cache,
122 and that the NtApphelpCacheControl function expects the struct layout we use. */
123 CheckValidation(&ntPath
);
125 /* We expect not to find it */
126 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
127 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
128 Status
= CallCacheControl(&ntPath
, FALSE
, ApphelpCacheServiceLookup
);
129 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
131 /* First we add our process without a file handle (so it will be registered without file info) */
132 RequestAddition(service_handle
, FALSE
);
134 /* now we try to find it without validating file info */
135 Status
= CallCacheControl(&ntPath
, FALSE
, ApphelpCacheServiceLookup
);
136 ok_ntstatus(Status
, STATUS_SUCCESS
);
137 /* when validating file info the cache notices the file is wrong, so it is dropped from the cache */
138 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
139 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
140 /* making the second check without info also fail. */
141 Status
= CallCacheControl(&ntPath
, FALSE
, ApphelpCacheServiceLookup
);
142 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
145 /* Now we add the file with file info */
146 RequestAddition(service_handle
, TRUE
);
148 /* so both checks should succeed */
149 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
150 ok_ntstatus(Status
, STATUS_SUCCESS
);
151 Status
= CallCacheControl(&ntPath
, FALSE
, ApphelpCacheServiceLookup
);
152 ok_ntstatus(Status
, STATUS_SUCCESS
);
154 /* We know the file is in the cache now (assuming previous tests succeeded,
155 let's test invalid handle behavior */
156 CacheEntry
.ImageName
= ntPath
;
157 CacheEntry
.ImageHandle
= 0;
158 Status
= pNtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
159 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
161 /* re-add it for the next test */
162 RequestAddition(service_handle
, TRUE
);
163 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
164 ok_ntstatus(Status
, STATUS_SUCCESS
);
165 CacheEntry
.ImageHandle
= (HANDLE
)1;
166 Status
= pNtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
167 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
170 RequestAddition(service_handle
, TRUE
);
171 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
172 ok_ntstatus(Status
, STATUS_SUCCESS
);
173 CacheEntry
.ImageHandle
= (HANDLE
)0x80000000;
174 Status
= pNtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
175 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
177 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath
.Buffer
);
181 /* Most service related code was taken from services_winetest:service and modified for usage here
182 The rest came from MSDN */
184 static SERVICE_STATUS_HANDLE (WINAPI
*pRegisterServiceCtrlHandlerExA
)(LPCSTR
,LPHANDLER_FUNCTION_EX
,LPVOID
);
185 static char service_name
[100] = "apphelp_test_service";
186 static HANDLE service_stop_event
;
187 static SERVICE_STATUS_HANDLE service_status
;
189 static BOOLEAN
RegisterInShimCache(BOOLEAN WithMapping
)
191 WCHAR szPath
[MAX_PATH
];
192 UNICODE_STRING ntPath
;
195 GetModuleFileNameW(NULL
, szPath
, sizeof(szPath
) / sizeof(szPath
[0]));
196 Result
= RtlDosPathNameToNtPathName_U(szPath
, &ntPath
, NULL
, NULL
);
199 DbgPrint("RegisterInShimCache: RtlDosPathNameToNtPathName_U failed\n");
203 Status
= CallCacheControl(&ntPath
, WithMapping
, ApphelpCacheServiceUpdate
);
204 if (!NT_SUCCESS(Status
))
206 DbgPrint("RegisterInShimCache: CallCacheControl failed\n");
207 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath
.Buffer
);
210 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath
.Buffer
);
215 static DWORD WINAPI
service_handler(DWORD ctrl
, DWORD event_type
, void *event_data
, void *context
)
217 SERVICE_STATUS status
= {0};
218 status
.dwServiceType
= SERVICE_WIN32
;
219 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
223 case SERVICE_CONTROL_STOP
:
224 case SERVICE_CONTROL_SHUTDOWN
:
225 status
.dwCurrentState
= SERVICE_STOP_PENDING
;
226 status
.dwControlsAccepted
= 0;
227 SetServiceStatus(service_status
, &status
);
228 SetEvent(service_stop_event
);
230 case RegisterShimCacheWithHandle
:
231 if (!RegisterInShimCache(TRUE
))
233 /* TODO: how should we communicate a failure? */
236 case RegisterShimCacheWithoutHandle
:
237 if (!RegisterInShimCache(FALSE
))
239 /* TODO: how should we communicate a failure? */
243 DbgPrint("Unhandled: %d\n", ctrl
);
246 status
.dwCurrentState
= SERVICE_RUNNING
;
247 SetServiceStatus(service_status
, &status
);
251 static void WINAPI
service_main(DWORD argc
, char **argv
)
253 SERVICE_STATUS status
= {0};
254 service_status
= pRegisterServiceCtrlHandlerExA(service_name
, service_handler
, NULL
);
258 status
.dwServiceType
= SERVICE_WIN32
;
259 status
.dwCurrentState
= SERVICE_RUNNING
;
260 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
261 SetServiceStatus(service_status
, &status
);
263 WaitForSingleObject(service_stop_event
, INFINITE
);
265 status
.dwCurrentState
= SERVICE_STOPPED
;
266 status
.dwControlsAccepted
= 0;
267 SetServiceStatus(service_status
, &status
);
270 static SC_HANDLE
InstallService(SC_HANDLE scm_handle
)
272 char service_cmd
[MAX_PATH
+150], *ptr
;
275 ptr
= service_cmd
+ GetModuleFileNameA(NULL
, service_cmd
, MAX_PATH
);
276 strcpy(ptr
, " NtApphelpCacheControl service");
279 service
= CreateServiceA(scm_handle
, service_name
, service_name
, GENERIC_ALL
,
280 SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_IGNORE
,
281 service_cmd
, NULL
, NULL
, NULL
, NULL
, NULL
);
284 skip("Could not create helper service\n");
290 static void WaitService(SC_HANDLE service_handle
, DWORD Status
, SERVICE_STATUS_PROCESS
* ssp
)
293 DWORD dwStartTime
= GetTickCount();
294 while (ssp
->dwCurrentState
!= Status
)
297 if (!QueryServiceStatusEx(service_handle
, SC_STATUS_PROCESS_INFO
,
298 (LPBYTE
)ssp
, sizeof(SERVICE_STATUS_PROCESS
), &dwBytesNeeded
))
300 ok(0, "QueryServiceStatusEx failed waiting for %lu\n", Status
);
303 if ((GetTickCount() - dwStartTime
) > 1000)
305 ok(0, "Timeout waiting for (%lu) from service, is: %lu.\n",
306 Status
, ssp
->dwCurrentState
);
312 static void RunTest()
314 SC_HANDLE scm_handle
= OpenSCManagerA(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
315 SC_HANDLE service_handle
= InstallService(scm_handle
);
318 SERVICE_STATUS_PROCESS ssp
= {0};
319 BOOL res
= StartServiceA(service_handle
, 0, NULL
);
322 WaitService(service_handle
, SERVICE_RUNNING
, &ssp
);
323 RunApphelpCacheControlTests(service_handle
);
324 ControlService(service_handle
, SERVICE_CONTROL_STOP
, (LPSERVICE_STATUS
)&ssp
);
325 WaitService(service_handle
, SERVICE_STOPPED
, &ssp
);
329 skip("Could not start helper service\n");
331 DeleteService(service_handle
);
333 CloseServiceHandle(scm_handle
);
336 START_TEST(NtApphelpCacheControl
)
341 pRegisterServiceCtrlHandlerExA
= (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
342 if (!pRegisterServiceCtrlHandlerExA
)
344 win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
348 pNtApphelpCacheControl
= (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtApphelpCacheControl");
349 if (!pNtApphelpCacheControl
)
351 win_skip("NtApphelpCacheControl not available, skipping tests\n");
355 argc
= winetest_get_mainargs(&argv
);
362 SERVICE_TABLE_ENTRYA servtbl
[] = {
363 {service_name
, service_main
},
366 service_stop_event
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
367 StartServiceCtrlDispatcherA(servtbl
);
369 CloseHandle(service_stop_event
);