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