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
);
174 CacheEntry
.ImageHandle
= (HANDLE
)0x8000000000000000ULL
;
176 CacheEntry
.ImageHandle
= (HANDLE
)0x80000000;
178 Status
= pNtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
179 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
181 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath
.Buffer
);
185 /* Most service related code was taken from services_winetest:service and modified for usage here
186 The rest came from MSDN */
188 static SERVICE_STATUS_HANDLE (WINAPI
*pRegisterServiceCtrlHandlerExA
)(LPCSTR
,LPHANDLER_FUNCTION_EX
,LPVOID
);
189 static char service_name
[100] = "apphelp_test_service";
190 static HANDLE service_stop_event
;
191 static SERVICE_STATUS_HANDLE service_status
;
193 static BOOLEAN
RegisterInShimCache(BOOLEAN WithMapping
)
195 WCHAR szPath
[MAX_PATH
];
196 UNICODE_STRING ntPath
;
199 GetModuleFileNameW(NULL
, szPath
, sizeof(szPath
) / sizeof(szPath
[0]));
200 Result
= RtlDosPathNameToNtPathName_U(szPath
, &ntPath
, NULL
, NULL
);
203 DbgPrint("RegisterInShimCache: RtlDosPathNameToNtPathName_U failed\n");
207 Status
= CallCacheControl(&ntPath
, WithMapping
, ApphelpCacheServiceUpdate
);
208 if (!NT_SUCCESS(Status
))
210 DbgPrint("RegisterInShimCache: CallCacheControl failed\n");
211 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath
.Buffer
);
214 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath
.Buffer
);
219 static DWORD WINAPI
service_handler(DWORD ctrl
, DWORD event_type
, void *event_data
, void *context
)
221 SERVICE_STATUS status
= {0};
222 status
.dwServiceType
= SERVICE_WIN32
;
223 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
227 case SERVICE_CONTROL_STOP
:
228 case SERVICE_CONTROL_SHUTDOWN
:
229 status
.dwCurrentState
= SERVICE_STOP_PENDING
;
230 status
.dwControlsAccepted
= 0;
231 SetServiceStatus(service_status
, &status
);
232 SetEvent(service_stop_event
);
234 case RegisterShimCacheWithHandle
:
235 if (!RegisterInShimCache(TRUE
))
237 /* TODO: how should we communicate a failure? */
240 case RegisterShimCacheWithoutHandle
:
241 if (!RegisterInShimCache(FALSE
))
243 /* TODO: how should we communicate a failure? */
247 DbgPrint("Unhandled: %d\n", ctrl
);
250 status
.dwCurrentState
= SERVICE_RUNNING
;
251 SetServiceStatus(service_status
, &status
);
255 static void WINAPI
service_main(DWORD argc
, char **argv
)
257 SERVICE_STATUS status
= {0};
258 service_status
= pRegisterServiceCtrlHandlerExA(service_name
, service_handler
, NULL
);
262 status
.dwServiceType
= SERVICE_WIN32
;
263 status
.dwCurrentState
= SERVICE_RUNNING
;
264 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
265 SetServiceStatus(service_status
, &status
);
267 WaitForSingleObject(service_stop_event
, INFINITE
);
269 status
.dwCurrentState
= SERVICE_STOPPED
;
270 status
.dwControlsAccepted
= 0;
271 SetServiceStatus(service_status
, &status
);
274 static SC_HANDLE
InstallService(SC_HANDLE scm_handle
)
276 char service_cmd
[MAX_PATH
+150], *ptr
;
279 ptr
= service_cmd
+ GetModuleFileNameA(NULL
, service_cmd
, MAX_PATH
);
280 strcpy(ptr
, " NtApphelpCacheControl service");
283 service
= CreateServiceA(scm_handle
, service_name
, service_name
, GENERIC_ALL
,
284 SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_IGNORE
,
285 service_cmd
, NULL
, NULL
, NULL
, NULL
, NULL
);
288 skip("Could not create helper service\n");
294 static void WaitService(SC_HANDLE service_handle
, DWORD Status
, SERVICE_STATUS_PROCESS
* ssp
)
297 DWORD dwStartTime
= GetTickCount();
298 while (ssp
->dwCurrentState
!= Status
)
301 if (!QueryServiceStatusEx(service_handle
, SC_STATUS_PROCESS_INFO
,
302 (LPBYTE
)ssp
, sizeof(SERVICE_STATUS_PROCESS
), &dwBytesNeeded
))
304 ok(0, "QueryServiceStatusEx failed waiting for %lu\n", Status
);
307 if ((GetTickCount() - dwStartTime
) > 1000)
309 ok(0, "Timeout waiting for (%lu) from service, is: %lu.\n",
310 Status
, ssp
->dwCurrentState
);
316 static void RunTest()
318 SC_HANDLE scm_handle
= OpenSCManagerA(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
319 SC_HANDLE service_handle
= InstallService(scm_handle
);
322 SERVICE_STATUS_PROCESS ssp
= {0};
323 BOOL res
= StartServiceA(service_handle
, 0, NULL
);
326 WaitService(service_handle
, SERVICE_RUNNING
, &ssp
);
327 RunApphelpCacheControlTests(service_handle
);
328 ControlService(service_handle
, SERVICE_CONTROL_STOP
, (LPSERVICE_STATUS
)&ssp
);
329 WaitService(service_handle
, SERVICE_STOPPED
, &ssp
);
333 skip("Could not start helper service\n");
335 DeleteService(service_handle
);
337 CloseServiceHandle(scm_handle
);
340 START_TEST(NtApphelpCacheControl
)
345 pRegisterServiceCtrlHandlerExA
= (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
346 if (!pRegisterServiceCtrlHandlerExA
)
348 win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
352 pNtApphelpCacheControl
= (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtApphelpCacheControl");
353 if (!pNtApphelpCacheControl
)
355 win_skip("NtApphelpCacheControl not available, skipping tests\n");
359 argc
= winetest_get_mainargs(&argv
);
366 SERVICE_TABLE_ENTRYA servtbl
[] = {
367 {service_name
, service_main
},
370 service_stop_event
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
371 StartServiceCtrlDispatcherA(servtbl
);
373 CloseHandle(service_stop_event
);