[SHELL32_APITEST]: Add more tests to see how IShellLink::Get/SetIconLocation interact...
[reactos.git] / rostests / apitests / advapi32 / ServiceArgs.c
1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory
4 * PURPOSE: Test for service arguments
5 * PROGRAMMER: Jacek Caban for CodeWeavers
6 * Thomas Faber <thomas.faber@reactos.org>
7 */
8
9 #include <apitest.h>
10 #include <winnls.h>
11 #include <winsvc.h>
12 #include <strsafe.h>
13
14 static char **argv;
15 static int argc;
16
17 static HANDLE pipe_handle = INVALID_HANDLE_VALUE;
18 static char service_nameA[100];
19 static WCHAR service_nameW[100];
20 static WCHAR named_pipe_name[100];
21
22 /* Test process global variables */
23 static SC_HANDLE scm_handle;
24 static SERVICE_STATUS_HANDLE service_handle;
25
26 static void send_msg(const char *type, const char *msg)
27 {
28 DWORD written = 0;
29 char buf[512];
30
31 StringCbPrintfA(buf, sizeof(buf), "%s:%s", type, msg);
32 WriteFile(pipe_handle, buf, strlen(buf)+1, &written, NULL);
33 }
34
35 static inline void service_trace(const char *msg, ...)
36 {
37 va_list valist;
38 char buf[512];
39
40 va_start(valist, msg);
41 StringCbVPrintfA(buf, sizeof(buf), msg, valist);
42 va_end(valist);
43
44 send_msg("TRACE", buf);
45 }
46
47 static void service_ok(int cnd, const char *msg, ...)
48 {
49 va_list valist;
50 char buf[512];
51
52 va_start(valist, msg);
53 StringCbVPrintfA(buf, sizeof(buf), msg, valist);
54 va_end(valist);
55
56 send_msg(cnd ? "OK" : "FAIL", buf);
57 }
58
59 static VOID WINAPI service_handler(DWORD ctrl)
60 {
61 SERVICE_STATUS status;
62
63 status.dwServiceType = SERVICE_WIN32;
64 status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
65 status.dwWin32ExitCode = 0;
66 status.dwServiceSpecificExitCode = 0;
67 status.dwCheckPoint = 0;
68 status.dwWaitHint = 0;
69
70 switch(ctrl)
71 {
72 case SERVICE_CONTROL_STOP:
73 status.dwCurrentState = SERVICE_STOP_PENDING;
74 status.dwControlsAccepted = 0;
75 SetServiceStatus(service_handle, &status);
76 default:
77 status.dwCurrentState = SERVICE_RUNNING;
78 SetServiceStatus(service_handle, &status);
79 }
80 }
81
82 static void service_main_common(void)
83 {
84 SERVICE_STATUS status;
85 BOOL res;
86
87 service_handle = RegisterServiceCtrlHandlerW(service_nameW, service_handler);
88 service_ok(service_handle != NULL, "RegisterServiceCtrlHandler failed: %lu\n", GetLastError());
89 if (!service_handle)
90 return;
91
92 status.dwServiceType = SERVICE_WIN32;
93 status.dwCurrentState = SERVICE_RUNNING;
94 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
95 status.dwWin32ExitCode = 0;
96 status.dwServiceSpecificExitCode = 0;
97 status.dwCheckPoint = 0;
98 status.dwWaitHint = 10000;
99 res = SetServiceStatus(service_handle, &status);
100 service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %lu", GetLastError());
101
102 Sleep(100);
103
104 status.dwCurrentState = SERVICE_STOPPED;
105 status.dwControlsAccepted = 0;
106 res = SetServiceStatus(service_handle, &status);
107 service_ok(res, "SetServiceStatus(SERVICE_STOPPED) failed: %lu", GetLastError());
108 }
109
110 /*
111 * A/W argument layout XP/2003:
112 * [argv array of pointers][parameter 1][parameter 2]...[service name]
113 *
114 * A/W argument layout Vista:
115 * [argv array of pointers][align to 8 bytes][parameter 1][parameter 2]...[service name]
116 *
117 * A/W argument layout Win7/8:
118 * [argv array of pointers][service name]
119 * [parameter 1][align to 4 bytes][parameter 2][align to 4 bytes]...
120 *
121 * Space for parameters and service name is always enough to store
122 * the WCHAR version including null terminator.
123 */
124
125 static void WINAPI service_mainA(DWORD service_argc, char **service_argv)
126 {
127 int i;
128 char *next_arg;
129 char *next_arg_aligned;
130
131 service_ok(service_argc == argc - 3, "service_argc = %d, expected %d", service_argc, argc - 3);
132 if (service_argc == argc - 3)
133 {
134 service_ok(!strcmp(service_argv[0], service_nameA), "service_argv[0] = %s, expected %s", service_argv[0], service_nameA);
135 service_ok(service_argv[0] == (char *)&service_argv[service_argc] ||
136 service_argv[1] == (char *)&service_argv[service_argc] ||
137 service_argv[1] == (char *)(((ULONG_PTR)&service_argv[service_argc] + 7) & ~7), "service_argv[0] = %p, [1] = %p, expected one of them to be %p", service_argv[0], service_argv[1], &service_argv[service_argc]);
138 //service_trace("service_argv[0] = %p", service_argv[0]);
139 next_arg_aligned = next_arg = NULL;
140 for (i = 1; i < service_argc; i++)
141 {
142 //service_trace("service_argv[%d] = %p", i, service_argv[i]);
143 service_ok(!strcmp(service_argv[i], argv[i + 3]), "service_argv[%d] = %s, expected %s", i, service_argv[i], argv[i + 3]);
144 service_ok(next_arg == NULL ||
145 service_argv[i] == next_arg ||
146 service_argv[i] == next_arg_aligned, "service_argv[%d] = %p, expected %p or %p", i, service_argv[i], next_arg, next_arg_aligned);
147 next_arg = service_argv[i];
148 next_arg += 2 * (strlen(next_arg) + 1);
149 next_arg_aligned = (char *)(((ULONG_PTR)next_arg + 3) & ~3);
150 }
151 }
152
153 service_main_common();
154 }
155
156 static void WINAPI service_mainW(DWORD service_argc, WCHAR **service_argv)
157 {
158 int i;
159 WCHAR argW[32];
160 WCHAR *next_arg;
161 WCHAR *next_arg_aligned;
162
163 service_ok(service_argc == argc - 3, "service_argc = %d, expected %d", service_argc, argc - 3);
164 if (service_argc == argc - 3)
165 {
166 service_ok(!wcscmp(service_argv[0], service_nameW), "service_argv[0] = %ls, expected %ls", service_argv[0], service_nameW);
167 service_ok(service_argv[0] == (WCHAR *)&service_argv[service_argc] ||
168 service_argv[1] == (WCHAR *)&service_argv[service_argc] ||
169 service_argv[1] == (WCHAR *)(((ULONG_PTR)&service_argv[service_argc] + 7) & ~7), "service_argv[0] = %p, [1] = %p, expected one of them to be %p", service_argv[0], service_argv[1], &service_argv[service_argc]);
170 //service_trace("service_argv[0] = %p", service_argv[0]);
171 next_arg_aligned = next_arg = NULL;
172 for (i = 1; i < service_argc; i++)
173 {
174 //service_trace("service_argv[%d] = %p", i, service_argv[i]);
175 MultiByteToWideChar(CP_ACP, 0, argv[i + 3], -1, argW, _countof(argW));
176 service_ok(!wcscmp(service_argv[i], argW), "service_argv[%d] = %ls, expected %ls", i, service_argv[i], argW);
177 service_ok(next_arg == NULL ||
178 service_argv[i] == next_arg ||
179 service_argv[i] == next_arg_aligned, "service_argv[%d] = %p, expected %p or %p", i, service_argv[i], next_arg, next_arg_aligned);
180 next_arg = service_argv[i];
181 next_arg += wcslen(next_arg) + 1;
182 next_arg_aligned = (WCHAR *)(((ULONG_PTR)next_arg + 3) & ~3);
183 }
184 }
185
186 service_main_common();
187 }
188
189 static void service_process(BOOLEAN unicode)
190 {
191 BOOL res;
192
193 SERVICE_TABLE_ENTRYA servtblA[] =
194 {
195 { service_nameA, service_mainA },
196 { NULL, NULL }
197 };
198 SERVICE_TABLE_ENTRYW servtblW[] =
199 {
200 { service_nameW, service_mainW },
201 { NULL, NULL }
202 };
203
204 res = WaitNamedPipeW(named_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
205 if (!res)
206 return;
207
208 pipe_handle = CreateFileW(named_pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
209 if (pipe_handle == INVALID_HANDLE_VALUE)
210 return;
211
212 //service_trace("Starting...");
213 if (unicode)
214 {
215 res = StartServiceCtrlDispatcherW(servtblW);
216 service_ok(res, "StartServiceCtrlDispatcherW failed: %lu\n", GetLastError());
217 }
218 else
219 {
220 res = StartServiceCtrlDispatcherA(servtblA);
221 service_ok(res, "StartServiceCtrlDispatcherA failed: %lu\n", GetLastError());
222 }
223
224 CloseHandle(pipe_handle);
225 }
226
227 static SC_HANDLE register_service(PCWSTR extra_args)
228 {
229 WCHAR service_cmd[MAX_PATH+150];
230 SC_HANDLE service;
231
232 GetModuleFileNameW(NULL, service_cmd, MAX_PATH);
233
234 StringCbCatW(service_cmd, sizeof(service_cmd), L" ServiceArgs ");
235 StringCbCatW(service_cmd, sizeof(service_cmd), service_nameW);
236 StringCbCatW(service_cmd, sizeof(service_cmd), extra_args);
237
238 trace("service_cmd \"%ls\"\n", service_cmd);
239
240 service = CreateServiceW(scm_handle, service_nameW, service_nameW, GENERIC_ALL,
241 SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
242 service_cmd, NULL, NULL, NULL, NULL, NULL);
243 if (!service && GetLastError() == ERROR_ACCESS_DENIED)
244 {
245 skip("Not enough access right to create service\n");
246 return NULL;
247 }
248
249 ok(service != NULL, "CreateService failed: %lu\n", GetLastError());
250 return service;
251 }
252
253 static DWORD WINAPI pipe_thread(void *arg)
254 {
255 char buf[512];
256 DWORD read;
257 BOOL res;
258
259 res = ConnectNamedPipe(pipe_handle, NULL);
260 ok(res || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed: %lu\n", GetLastError());
261
262 while (1)
263 {
264 res = ReadFile(pipe_handle, buf, sizeof(buf), &read, NULL);
265 if (!res)
266 {
267 ok(GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_INVALID_HANDLE,
268 "ReadFile failed: %lu\n", GetLastError());
269 break;
270 }
271
272 if (!strncmp(buf, "TRACE:", 6))
273 {
274 trace("service trace: %s\n", buf+6);
275 }
276 else if (!strncmp(buf, "OK:", 3))
277 {
278 ok(1, "service: %s\n", buf+3);
279 }
280 else if (!strncmp(buf, "FAIL:", 5))
281 {
282 ok(0, "service: %s\n", buf+5);
283 }
284 else
285 {
286 ok(0, "malformed service message: %s\n", buf);
287 }
288 }
289
290 DisconnectNamedPipe(pipe_handle);
291 //trace("pipe disconnected\n");
292 return 0;
293 }
294
295 static void test_startA(SC_HANDLE service_handle, int service_argc, const char **service_argv)
296 {
297 SERVICE_STATUS status;
298 BOOL res;
299
300 res = StartServiceA(service_handle, service_argc, service_argv);
301 ok(res, "StartService failed: %lu\n", GetLastError());
302 if (!res)
303 return;
304
305 do
306 {
307 Sleep(100);
308 ZeroMemory(&status, sizeof(status));
309 res = QueryServiceStatus(service_handle, &status);
310 } while (res && status.dwCurrentState != SERVICE_STOPPED);
311 ok(res, "QueryServiceStatus failed: %lu\n", GetLastError());
312 ok(status.dwCurrentState == SERVICE_STOPPED, "status.dwCurrentState = %lx\n", status.dwCurrentState);
313 }
314
315 static void test_startW(SC_HANDLE service_handle, int service_argc, const WCHAR **service_argv)
316 {
317 SERVICE_STATUS status;
318 BOOL res;
319
320 res = StartServiceW(service_handle, service_argc, service_argv);
321 ok(res, "StartService failed: %lu\n", GetLastError());
322 if (!res)
323 return;
324
325 do
326 {
327 Sleep(100);
328 ZeroMemory(&status, sizeof(status));
329 res = QueryServiceStatus(service_handle, &status);
330 } while (res && status.dwCurrentState != SERVICE_STOPPED);
331 ok(res, "QueryServiceStatus failed: %lu\n", GetLastError());
332 ok(status.dwCurrentState == SERVICE_STOPPED, "status.dwCurrentState = %lx\n", status.dwCurrentState);
333 }
334
335 static void test_runner(BOOLEAN unicode, PCWSTR extra_args, int service_argc, void *service_argv)
336 {
337 HANDLE thread;
338 SC_HANDLE service_handle;
339 BOOL res;
340
341 StringCbPrintfW(service_nameW, sizeof(service_nameW), L"WineTestService%lu", GetTickCount());
342 WideCharToMultiByte(CP_ACP, 0, service_nameW, -1, service_nameA, _countof(service_nameA), NULL, NULL);
343 //trace("service_name: %ls\n", service_nameW);
344 StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW);
345
346 pipe_handle = CreateNamedPipeW(named_pipe_name, PIPE_ACCESS_INBOUND,
347 PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT, 10, 2048, 2048, 10000, NULL);
348 ok(pipe_handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError());
349 if (pipe_handle == INVALID_HANDLE_VALUE)
350 return;
351
352 thread = CreateThread(NULL, 0, pipe_thread, NULL, 0, NULL);
353 ok(thread != NULL, "CreateThread failed: %lu\n", GetLastError());
354 if (!thread)
355 return;
356
357 service_handle = register_service(extra_args);
358 if (!service_handle)
359 return;
360
361 //trace("starting...\n");
362
363 if (unicode)
364 test_startW(service_handle, service_argc, service_argv);
365 else
366 test_startA(service_handle, service_argc, service_argv);
367
368 res = DeleteService(service_handle);
369 ok(res, "DeleteService failed: %lu\n", GetLastError());
370
371 CloseServiceHandle(service_handle);
372
373 ok(WaitForSingleObject(thread, 10000) == WAIT_OBJECT_0, "Timeout waiting for thread\n");
374 CloseHandle(thread);
375 CloseHandle(pipe_handle);
376 }
377
378 START_TEST(ServiceArgs)
379 {
380 argc = winetest_get_mainargs(&argv);
381
382 scm_handle = OpenSCManagerW(NULL, NULL, GENERIC_ALL);
383 ok(scm_handle != NULL, "OpenSCManager failed: %lu\n", GetLastError());
384 if (!scm_handle)
385 {
386 skip("Failed to open service control manager. Skipping test\n");
387 return;
388 }
389
390 if (argc < 3)
391 {
392 char *service_argvA[10];
393 WCHAR *service_argvW[10];
394
395 test_runner(FALSE, L" A", 0, NULL);
396 test_runner(FALSE, L" W", 0, NULL);
397 test_runner(TRUE, L" A", 0, NULL);
398 test_runner(TRUE, L" W", 0, NULL);
399
400 service_argvA[0] = "x";
401 service_argvW[0] = L"x";
402 test_runner(FALSE, L" A x", 1, service_argvA);
403 test_runner(FALSE, L" W x", 1, service_argvA);
404 test_runner(TRUE, L" A x", 1, service_argvW);
405 test_runner(TRUE, L" W x", 1, service_argvW);
406
407 service_argvA[1] = "y";
408 service_argvW[1] = L"y";
409 test_runner(FALSE, L" A x y", 2, service_argvA);
410 test_runner(FALSE, L" W x y", 2, service_argvA);
411 test_runner(TRUE, L" A x y", 2, service_argvW);
412 test_runner(TRUE, L" W x y", 2, service_argvW);
413
414 service_argvA[0] = "ab";
415 service_argvW[0] = L"ab";
416 test_runner(FALSE, L" A ab y", 2, service_argvA);
417 test_runner(FALSE, L" W ab y", 2, service_argvA);
418 test_runner(TRUE, L" A ab y", 2, service_argvW);
419 test_runner(TRUE, L" W ab y", 2, service_argvW);
420
421 service_argvA[0] = "abc";
422 service_argvW[0] = L"abc";
423 test_runner(FALSE, L" A abc y", 2, service_argvA);
424 test_runner(FALSE, L" W abc y", 2, service_argvA);
425 test_runner(TRUE, L" A abc y", 2, service_argvW);
426 test_runner(TRUE, L" W abc y", 2, service_argvW);
427
428 service_argvA[0] = "abcd";
429 service_argvW[0] = L"abcd";
430 test_runner(FALSE, L" A abcd y", 2, service_argvA);
431 test_runner(FALSE, L" W abcd y", 2, service_argvA);
432 test_runner(TRUE, L" A abcd y", 2, service_argvW);
433 test_runner(TRUE, L" W abcd y", 2, service_argvW);
434
435 service_argvA[0] = "abcde";
436 service_argvW[0] = L"abcde";
437 test_runner(FALSE, L" A abcde y", 2, service_argvA);
438 test_runner(FALSE, L" W abcde y", 2, service_argvA);
439 test_runner(TRUE, L" A abcde y", 2, service_argvW);
440 test_runner(TRUE, L" W abcde y", 2, service_argvW);
441
442 service_argvA[0] = "abcdef";
443 service_argvW[0] = L"abcdef";
444 test_runner(FALSE, L" A abcdef y", 2, service_argvA);
445 test_runner(FALSE, L" W abcdef y", 2, service_argvA);
446 test_runner(TRUE, L" A abcdef y", 2, service_argvW);
447 test_runner(TRUE, L" W abcdef y", 2, service_argvW);
448
449 service_argvA[0] = "abcdefg";
450 service_argvW[0] = L"abcdefg";
451 test_runner(FALSE, L" A abcdefg y", 2, service_argvA);
452 test_runner(FALSE, L" W abcdefg y", 2, service_argvA);
453 test_runner(TRUE, L" A abcdefg y", 2, service_argvW);
454 test_runner(TRUE, L" W abcdefg y", 2, service_argvW);
455
456 service_argvA[0] = "";
457 service_argvW[0] = L"";
458 test_runner(FALSE, L" A \"\" y", 2, service_argvA);
459 test_runner(FALSE, L" W \"\" y", 2, service_argvA);
460 test_runner(TRUE, L" A \"\" y", 2, service_argvW);
461 test_runner(TRUE, L" W \"\" y", 2, service_argvW);
462 }
463 else
464 {
465 strcpy(service_nameA, argv[2]);
466 MultiByteToWideChar(CP_ACP, 0, service_nameA, -1, service_nameW, _countof(service_nameW));
467 StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW);
468 if (!strcmp(argv[3], "A"))
469 service_process(FALSE);
470 else
471 service_process(TRUE);
472 }
473
474 CloseServiceHandle(scm_handle);
475 }