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
12 #define WIN32_NO_STATUS
17 RegisterShimCacheWithHandle
= 128,
18 RegisterShimCacheWithoutHandle
= 129,
22 NTSTATUS
CallCacheControl(UNICODE_STRING
* PathName
, BOOLEAN WithMapping
, APPHELPCACHESERVICECLASS Service
)
24 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry
= { {0} };
26 CacheEntry
.ImageName
= *PathName
;
29 OBJECT_ATTRIBUTES LocalObjectAttributes
;
30 IO_STATUS_BLOCK IoStatusBlock
;
31 InitializeObjectAttributes(&LocalObjectAttributes
, PathName
,
32 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
33 Status
= NtOpenFile(&CacheEntry
.ImageHandle
,
34 SYNCHRONIZE
| FILE_READ_ATTRIBUTES
| FILE_READ_DATA
| FILE_EXECUTE
,
35 &LocalObjectAttributes
, &IoStatusBlock
,
36 FILE_SHARE_READ
| FILE_SHARE_DELETE
,
37 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
);
38 ok_ntstatus(Status
, STATUS_SUCCESS
);
42 CacheEntry
.ImageHandle
= INVALID_HANDLE_VALUE
;
44 Status
= NtApphelpCacheControl(Service
, &CacheEntry
);
45 if (CacheEntry
.ImageHandle
!= INVALID_HANDLE_VALUE
)
46 NtClose(CacheEntry
.ImageHandle
);
50 int InitEnv(UNICODE_STRING
* PathName
)
52 NTSTATUS Status
= CallCacheControl(PathName
, FALSE
, ApphelpCacheServiceRemove
);
53 if (Status
== STATUS_INVALID_PARAMETER
)
55 /* Windows Vista+ has a different layout for APPHELP_CACHE_SERVICE_LOOKUP */
58 ok(Status
== STATUS_SUCCESS
|| Status
== STATUS_NOT_FOUND
,
59 "Wrong value for Status, expected: SUCCESS or NOT_FOUND, got: 0x%lx\n",
64 void CheckValidation(UNICODE_STRING
* PathName
)
66 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry
= { {0} };
69 /* Validate the handling of a NULL pointer */
70 Status
= NtApphelpCacheControl(ApphelpCacheServiceRemove
, NULL
);
71 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
72 Status
= NtApphelpCacheControl(ApphelpCacheServiceLookup
, NULL
);
73 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
75 /* Validate the handling of a NULL pointer inside the struct */
76 Status
= NtApphelpCacheControl(ApphelpCacheServiceRemove
, &CacheEntry
);
77 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
78 Status
= NtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
79 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
81 /* Just call the dump function */
82 Status
= NtApphelpCacheControl(ApphelpCacheServiceDump
, NULL
);
83 ok_ntstatus(Status
, STATUS_SUCCESS
);
85 /* Validate the handling of an invalid handle inside the struct */
86 CacheEntry
.ImageName
= *PathName
;
87 CacheEntry
.ImageHandle
= (HANDLE
)2;
88 Status
= NtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
89 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
91 /* Validate the handling of an invalid service number */
92 Status
= NtApphelpCacheControl(999, NULL
);
93 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
94 Status
= NtApphelpCacheControl(999, &CacheEntry
);
95 ok_ntstatus(Status
, STATUS_INVALID_PARAMETER
);
98 static BOOLEAN
RequestAddition(SC_HANDLE service_handle
, BOOLEAN WithMapping
)
100 SERVICE_STATUS Status
;
101 ControlService(service_handle
, WithMapping
? RegisterShimCacheWithHandle
:
102 RegisterShimCacheWithoutHandle
, &Status
);
103 /* TODO: how to get a return code from the service? */
107 static void RunApphelpCacheControlTests(SC_HANDLE service_handle
)
109 WCHAR szPath
[MAX_PATH
];
110 UNICODE_STRING ntPath
;
113 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry
;
115 GetModuleFileNameW(NULL
, szPath
, sizeof(szPath
) / sizeof(szPath
[0]));
116 Result
= RtlDosPathNameToNtPathName_U(szPath
, &ntPath
, NULL
, NULL
);
117 ok(Result
== TRUE
, "RtlDosPathNameToNtPathName_U\n");
118 if (!InitEnv(&ntPath
))
120 skip("NtApphelpCacheControl expects a different structure layout\n");
123 /* At this point we have made sure that our binary is not present in the cache,
124 and that the NtApphelpCacheControl function expects the struct layout we use. */
125 CheckValidation(&ntPath
);
127 /* We expect not to find it */
128 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
129 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
130 Status
= CallCacheControl(&ntPath
, FALSE
, ApphelpCacheServiceLookup
);
131 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
133 /* First we add our process without a file handle (so it will be registered without file info) */
134 RequestAddition(service_handle
, FALSE
);
136 /* now we try to find it without validating file info */
137 Status
= CallCacheControl(&ntPath
, FALSE
, ApphelpCacheServiceLookup
);
138 ok_ntstatus(Status
, STATUS_SUCCESS
);
139 /* when validating file info the cache notices the file is wrong, so it is dropped from the cache */
140 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
141 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
142 /* making the second check without info also fail. */
143 Status
= CallCacheControl(&ntPath
, FALSE
, ApphelpCacheServiceLookup
);
144 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
147 /* Now we add the file with file info */
148 RequestAddition(service_handle
, TRUE
);
150 /* so both checks should succeed */
151 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
152 ok_ntstatus(Status
, STATUS_SUCCESS
);
153 Status
= CallCacheControl(&ntPath
, FALSE
, ApphelpCacheServiceLookup
);
154 ok_ntstatus(Status
, STATUS_SUCCESS
);
156 /* We know the file is in the cache now (assuming previous tests succeeded,
157 let's test invalid handle behavior */
158 CacheEntry
.ImageName
= ntPath
;
159 CacheEntry
.ImageHandle
= 0;
160 Status
= NtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
161 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
163 /* re-add it for the next test */
164 RequestAddition(service_handle
, TRUE
);
165 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
166 ok_ntstatus(Status
, STATUS_SUCCESS
);
167 CacheEntry
.ImageHandle
= (HANDLE
)1;
168 Status
= NtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
169 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
172 RequestAddition(service_handle
, TRUE
);
173 Status
= CallCacheControl(&ntPath
, TRUE
, ApphelpCacheServiceLookup
);
174 ok_ntstatus(Status
, STATUS_SUCCESS
);
175 CacheEntry
.ImageHandle
= (HANDLE
)0x80000000;
176 Status
= NtApphelpCacheControl(ApphelpCacheServiceLookup
, &CacheEntry
);
177 ok_ntstatus(Status
, STATUS_NOT_FOUND
);
179 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath
.Buffer
);
183 /* Most service related code was taken from services_winetest:service and modified for usage here
184 The rest came from MSDN */
186 static SERVICE_STATUS_HANDLE (WINAPI
*pRegisterServiceCtrlHandlerExA
)(LPCSTR
,LPHANDLER_FUNCTION_EX
,LPVOID
);
187 static char service_name
[100] = "apphelp_test_service";
188 static HANDLE service_stop_event
;
189 static SERVICE_STATUS_HANDLE service_status
;
191 static BOOLEAN
RegisterInShimCache(BOOLEAN WithMapping
)
193 WCHAR szPath
[MAX_PATH
];
194 UNICODE_STRING ntPath
;
197 GetModuleFileNameW(NULL
, szPath
, sizeof(szPath
) / sizeof(szPath
[0]));
198 Result
= RtlDosPathNameToNtPathName_U(szPath
, &ntPath
, NULL
, NULL
);
201 DbgPrint("RegisterInShimCache: RtlDosPathNameToNtPathName_U failed\n");
205 Status
= CallCacheControl(&ntPath
, WithMapping
, ApphelpCacheServiceUpdate
);
206 if (!NT_SUCCESS(Status
))
208 DbgPrint("RegisterInShimCache: CallCacheControl failed\n");
209 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath
.Buffer
);
212 RtlFreeHeap(RtlGetProcessHeap(), 0, ntPath
.Buffer
);
217 static DWORD WINAPI
service_handler(DWORD ctrl
, DWORD event_type
, void *event_data
, void *context
)
219 SERVICE_STATUS status
= {0};
220 status
.dwServiceType
= SERVICE_WIN32
;
221 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
225 case SERVICE_CONTROL_STOP
:
226 case SERVICE_CONTROL_SHUTDOWN
:
227 status
.dwCurrentState
= SERVICE_STOP_PENDING
;
228 status
.dwControlsAccepted
= 0;
229 SetServiceStatus(service_status
, &status
);
230 SetEvent(service_stop_event
);
232 case RegisterShimCacheWithHandle
:
233 if (!RegisterInShimCache(TRUE
))
235 /* TODO: how should we communicate a failure? */
238 case RegisterShimCacheWithoutHandle
:
239 if (!RegisterInShimCache(FALSE
))
241 /* TODO: how should we communicate a failure? */
245 DbgPrint("Unhandled: %d\n", ctrl
);
248 status
.dwCurrentState
= SERVICE_RUNNING
;
249 SetServiceStatus(service_status
, &status
);
253 static void WINAPI
service_main(DWORD argc
, char **argv
)
255 SERVICE_STATUS status
= {0};
256 service_status
= pRegisterServiceCtrlHandlerExA(service_name
, service_handler
, NULL
);
260 status
.dwServiceType
= SERVICE_WIN32
;
261 status
.dwCurrentState
= SERVICE_RUNNING
;
262 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
263 SetServiceStatus(service_status
, &status
);
265 WaitForSingleObject(service_stop_event
, INFINITE
);
267 status
.dwCurrentState
= SERVICE_STOPPED
;
268 status
.dwControlsAccepted
= 0;
269 SetServiceStatus(service_status
, &status
);
272 static SC_HANDLE
InstallService(SC_HANDLE scm_handle
)
274 char service_cmd
[MAX_PATH
+150], *ptr
;
277 ptr
= service_cmd
+ GetModuleFileNameA(NULL
, service_cmd
, MAX_PATH
);
278 strcpy(ptr
, " NtApphelpCacheControl service");
281 service
= CreateServiceA(scm_handle
, service_name
, service_name
, GENERIC_ALL
,
282 SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_IGNORE
,
283 service_cmd
, NULL
, NULL
, NULL
, NULL
, NULL
);
286 skip("Could not create helper service\n");
292 static void WaitService(SC_HANDLE service_handle
, DWORD Status
, SERVICE_STATUS_PROCESS
* ssp
)
295 DWORD dwStartTime
= GetTickCount();
296 while (ssp
->dwCurrentState
!= Status
)
299 if (!QueryServiceStatusEx(service_handle
, SC_STATUS_PROCESS_INFO
,
300 (LPBYTE
)ssp
, sizeof(SERVICE_STATUS_PROCESS
), &dwBytesNeeded
))
302 ok(0, "QueryServiceStatusEx failed waiting for %lu\n", Status
);
305 if ((GetTickCount() - dwStartTime
) > 1000)
307 ok(0, "Timeout waiting for (%lu) from service, is: %lu.\n",
308 Status
, ssp
->dwCurrentState
);
314 static void RunTest()
316 SC_HANDLE scm_handle
= OpenSCManagerA(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
317 SC_HANDLE service_handle
= InstallService(scm_handle
);
320 SERVICE_STATUS_PROCESS ssp
= {0};
321 BOOL res
= StartServiceA(service_handle
, 0, NULL
);
324 WaitService(service_handle
, SERVICE_RUNNING
, &ssp
);
325 RunApphelpCacheControlTests(service_handle
);
326 ControlService(service_handle
, SERVICE_CONTROL_STOP
, (LPSERVICE_STATUS
)&ssp
);
327 WaitService(service_handle
, SERVICE_STOPPED
, &ssp
);
331 skip("Could not start helper service\n");
333 DeleteService(service_handle
);
335 CloseServiceHandle(scm_handle
);
338 START_TEST(NtApphelpCacheControl
)
343 pRegisterServiceCtrlHandlerExA
= (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
344 if (!pRegisterServiceCtrlHandlerExA
)
346 win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
349 argc
= winetest_get_mainargs(&argv
);
356 SERVICE_TABLE_ENTRYA servtbl
[] = {
357 {service_name
, service_main
},
360 service_stop_event
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
361 StartServiceCtrlDispatcherA(servtbl
);
363 CloseHandle(service_stop_event
);