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>
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];
22 /* Test process global variables */
23 static SC_HANDLE scm_handle
;
24 static SERVICE_STATUS_HANDLE service_handle
;
26 static void send_msg(const char *type
, const char *msg
)
31 StringCbPrintfA(buf
, sizeof(buf
), "%s:%s", type
, msg
);
32 WriteFile(pipe_handle
, buf
, strlen(buf
)+1, &written
, NULL
);
36 static inline void service_trace(const char *msg
, ...)
41 va_start(valist
, msg
);
42 StringCbVPrintfA(buf
, sizeof(buf
), msg
, valist
);
45 send_msg("TRACE", buf
);
49 static void service_ok(int cnd
, const char *msg
, ...)
54 va_start(valist
, msg
);
55 StringCbVPrintfA(buf
, sizeof(buf
), msg
, valist
);
58 send_msg(cnd
? "OK" : "FAIL", buf
);
61 static VOID WINAPI
service_handler(DWORD ctrl
)
63 SERVICE_STATUS status
;
65 status
.dwServiceType
= SERVICE_WIN32
;
66 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
67 status
.dwWin32ExitCode
= 0;
68 status
.dwServiceSpecificExitCode
= 0;
69 status
.dwCheckPoint
= 0;
70 status
.dwWaitHint
= 0;
74 case SERVICE_CONTROL_STOP
:
75 status
.dwCurrentState
= SERVICE_STOP_PENDING
;
76 status
.dwControlsAccepted
= 0;
77 SetServiceStatus(service_handle
, &status
);
79 status
.dwCurrentState
= SERVICE_RUNNING
;
80 SetServiceStatus(service_handle
, &status
);
84 static void service_main_common(void)
86 SERVICE_STATUS status
;
89 service_handle
= RegisterServiceCtrlHandlerW(service_nameW
, service_handler
);
90 service_ok(service_handle
!= NULL
, "RegisterServiceCtrlHandler failed: %lu\n", GetLastError());
94 status
.dwServiceType
= SERVICE_WIN32
;
95 status
.dwCurrentState
= SERVICE_RUNNING
;
96 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
97 status
.dwWin32ExitCode
= 0;
98 status
.dwServiceSpecificExitCode
= 0;
99 status
.dwCheckPoint
= 0;
100 status
.dwWaitHint
= 10000;
101 res
= SetServiceStatus(service_handle
, &status
);
102 service_ok(res
, "SetServiceStatus(SERVICE_RUNNING) failed: %lu", GetLastError());
106 status
.dwCurrentState
= SERVICE_STOPPED
;
107 status
.dwControlsAccepted
= 0;
108 res
= SetServiceStatus(service_handle
, &status
);
109 service_ok(res
, "SetServiceStatus(SERVICE_STOPPED) failed: %lu", GetLastError());
113 * A/W argument layout XP/2003:
114 * [argv array of pointers][parameter 1][parameter 2]...[service name]
116 * A/W argument layout Vista:
117 * [argv array of pointers][align to 8 bytes][parameter 1][parameter 2]...[service name]
119 * A/W argument layout Win7/8:
120 * [argv array of pointers][service name]
121 * [parameter 1][align to 4 bytes][parameter 2][align to 4 bytes]...
123 * Space for parameters and service name is always enough to store
124 * the WCHAR version including null terminator.
127 static void WINAPI
service_mainA(DWORD service_argc
, char **service_argv
)
131 char *next_arg_aligned
;
133 service_ok(service_argc
== argc
- 3, "service_argc = %d, expected %d", service_argc
, argc
- 3);
134 if (service_argc
== argc
- 3)
136 service_ok(!strcmp(service_argv
[0], service_nameA
), "service_argv[0] = %s, expected %s", service_argv
[0], service_nameA
);
137 service_ok(service_argv
[0] == (char *)&service_argv
[service_argc
] ||
138 service_argv
[1] == (char *)&service_argv
[service_argc
] ||
139 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
]);
140 //service_trace("service_argv[0] = %p", service_argv[0]);
141 next_arg_aligned
= next_arg
= NULL
;
142 for (i
= 1; i
< service_argc
; i
++)
144 //service_trace("service_argv[%d] = %p", i, service_argv[i]);
145 service_ok(!strcmp(service_argv
[i
], argv
[i
+ 3]), "service_argv[%d] = %s, expected %s", i
, service_argv
[i
], argv
[i
+ 3]);
146 service_ok(next_arg
== NULL
||
147 service_argv
[i
] == next_arg
||
148 service_argv
[i
] == next_arg_aligned
, "service_argv[%d] = %p, expected %p or %p", i
, service_argv
[i
], next_arg
, next_arg_aligned
);
149 next_arg
= service_argv
[i
];
150 next_arg
+= 2 * (strlen(next_arg
) + 1);
151 next_arg_aligned
= (char *)(((ULONG_PTR
)next_arg
+ 3) & ~3);
155 service_main_common();
158 static void WINAPI
service_mainW(DWORD service_argc
, WCHAR
**service_argv
)
163 WCHAR
*next_arg_aligned
;
165 service_ok(service_argc
== argc
- 3, "service_argc = %d, expected %d", service_argc
, argc
- 3);
166 if (service_argc
== argc
- 3)
168 service_ok(!wcscmp(service_argv
[0], service_nameW
), "service_argv[0] = %ls, expected %ls", service_argv
[0], service_nameW
);
169 service_ok(service_argv
[0] == (WCHAR
*)&service_argv
[service_argc
] ||
170 service_argv
[1] == (WCHAR
*)&service_argv
[service_argc
] ||
171 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
]);
172 //service_trace("service_argv[0] = %p", service_argv[0]);
173 next_arg_aligned
= next_arg
= NULL
;
174 for (i
= 1; i
< service_argc
; i
++)
176 //service_trace("service_argv[%d] = %p", i, service_argv[i]);
177 MultiByteToWideChar(CP_ACP
, 0, argv
[i
+ 3], -1, argW
, _countof(argW
));
178 service_ok(!wcscmp(service_argv
[i
], argW
), "service_argv[%d] = %ls, expected %ls", i
, service_argv
[i
], argW
);
179 service_ok(next_arg
== NULL
||
180 service_argv
[i
] == next_arg
||
181 service_argv
[i
] == next_arg_aligned
, "service_argv[%d] = %p, expected %p or %p", i
, service_argv
[i
], next_arg
, next_arg_aligned
);
182 next_arg
= service_argv
[i
];
183 next_arg
+= wcslen(next_arg
) + 1;
184 next_arg_aligned
= (WCHAR
*)(((ULONG_PTR
)next_arg
+ 3) & ~3);
188 service_main_common();
191 static void service_process(BOOLEAN unicode
)
195 SERVICE_TABLE_ENTRYA servtblA
[] =
197 { service_nameA
, service_mainA
},
200 SERVICE_TABLE_ENTRYW servtblW
[] =
202 { service_nameW
, service_mainW
},
206 res
= WaitNamedPipeW(named_pipe_name
, NMPWAIT_USE_DEFAULT_WAIT
);
210 pipe_handle
= CreateFileW(named_pipe_name
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
211 if (pipe_handle
== INVALID_HANDLE_VALUE
)
214 //service_trace("Starting...");
217 res
= StartServiceCtrlDispatcherW(servtblW
);
218 service_ok(res
, "StartServiceCtrlDispatcherW failed: %lu\n", GetLastError());
222 res
= StartServiceCtrlDispatcherA(servtblA
);
223 service_ok(res
, "StartServiceCtrlDispatcherA failed: %lu\n", GetLastError());
226 CloseHandle(pipe_handle
);
229 static SC_HANDLE
register_service(PCWSTR extra_args
)
231 WCHAR service_cmd
[MAX_PATH
+150];
234 GetModuleFileNameW(NULL
, service_cmd
, MAX_PATH
);
236 StringCbCatW(service_cmd
, sizeof(service_cmd
), L
" ServiceArgs ");
237 StringCbCatW(service_cmd
, sizeof(service_cmd
), service_nameW
);
238 StringCbCatW(service_cmd
, sizeof(service_cmd
), extra_args
);
240 trace("service_cmd \"%ls\"\n", service_cmd
);
242 service
= CreateServiceW(scm_handle
, service_nameW
, service_nameW
, GENERIC_ALL
,
243 SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_IGNORE
,
244 service_cmd
, NULL
, NULL
, NULL
, NULL
, NULL
);
245 if (!service
&& GetLastError() == ERROR_ACCESS_DENIED
)
247 skip("Not enough access right to create service\n");
251 ok(service
!= NULL
, "CreateService failed: %lu\n", GetLastError());
255 static DWORD WINAPI
pipe_thread(void *arg
)
261 res
= ConnectNamedPipe(pipe_handle
, NULL
);
262 ok(res
|| GetLastError() == ERROR_PIPE_CONNECTED
, "ConnectNamedPipe failed: %lu\n", GetLastError());
266 res
= ReadFile(pipe_handle
, buf
, sizeof(buf
), &read
, NULL
);
269 ok(GetLastError() == ERROR_BROKEN_PIPE
|| GetLastError() == ERROR_INVALID_HANDLE
,
270 "ReadFile failed: %lu\n", GetLastError());
274 if (!strncmp(buf
, "TRACE:", 6))
276 trace("service trace: %s\n", buf
+6);
278 else if (!strncmp(buf
, "OK:", 3))
280 ok(1, "service: %s\n", buf
+3);
282 else if (!strncmp(buf
, "FAIL:", 5))
284 ok(0, "service: %s\n", buf
+5);
288 ok(0, "malformed service message: %s\n", buf
);
292 DisconnectNamedPipe(pipe_handle
);
293 //trace("pipe disconnected\n");
297 static void test_startA(SC_HANDLE service_handle
, int service_argc
, const char **service_argv
)
299 SERVICE_STATUS status
;
302 res
= StartServiceA(service_handle
, service_argc
, service_argv
);
303 ok(res
, "StartService failed: %lu\n", GetLastError());
310 ZeroMemory(&status
, sizeof(status
));
311 res
= QueryServiceStatus(service_handle
, &status
);
312 } while (res
&& status
.dwCurrentState
!= SERVICE_STOPPED
);
313 ok(res
, "QueryServiceStatus failed: %lu\n", GetLastError());
314 ok(status
.dwCurrentState
== SERVICE_STOPPED
, "status.dwCurrentState = %lx\n", status
.dwCurrentState
);
317 static void test_startW(SC_HANDLE service_handle
, int service_argc
, const WCHAR
**service_argv
)
319 SERVICE_STATUS status
;
322 res
= StartServiceW(service_handle
, service_argc
, service_argv
);
323 ok(res
, "StartService failed: %lu\n", GetLastError());
330 ZeroMemory(&status
, sizeof(status
));
331 res
= QueryServiceStatus(service_handle
, &status
);
332 } while (res
&& status
.dwCurrentState
!= SERVICE_STOPPED
);
333 ok(res
, "QueryServiceStatus failed: %lu\n", GetLastError());
334 ok(status
.dwCurrentState
== SERVICE_STOPPED
, "status.dwCurrentState = %lx\n", status
.dwCurrentState
);
337 static void test_runner(BOOLEAN unicode
, PCWSTR extra_args
, int service_argc
, void *service_argv
)
340 SC_HANDLE service_handle
;
343 StringCbPrintfW(service_nameW
, sizeof(service_nameW
), L
"WineTestService%lu", GetTickCount());
344 WideCharToMultiByte(CP_ACP
, 0, service_nameW
, -1, service_nameA
, _countof(service_nameA
), NULL
, NULL
);
345 //trace("service_name: %ls\n", service_nameW);
346 StringCbPrintfW(named_pipe_name
, sizeof(named_pipe_name
), L
"\\\\.\\pipe\\%ls_pipe", service_nameW
);
348 pipe_handle
= CreateNamedPipeW(named_pipe_name
, PIPE_ACCESS_INBOUND
,
349 PIPE_TYPE_MESSAGE
|PIPE_READMODE_MESSAGE
|PIPE_WAIT
, 10, 2048, 2048, 10000, NULL
);
350 ok(pipe_handle
!= INVALID_HANDLE_VALUE
, "CreateNamedPipe failed: %lu\n", GetLastError());
351 if (pipe_handle
== INVALID_HANDLE_VALUE
)
354 thread
= CreateThread(NULL
, 0, pipe_thread
, NULL
, 0, NULL
);
355 ok(thread
!= NULL
, "CreateThread failed: %lu\n", GetLastError());
359 service_handle
= register_service(extra_args
);
363 //trace("starting...\n");
366 test_startW(service_handle
, service_argc
, service_argv
);
368 test_startA(service_handle
, service_argc
, service_argv
);
370 res
= DeleteService(service_handle
);
371 ok(res
, "DeleteService failed: %lu\n", GetLastError());
373 CloseServiceHandle(service_handle
);
375 ok(WaitForSingleObject(thread
, 10000) == WAIT_OBJECT_0
, "Timeout waiting for thread\n");
377 CloseHandle(pipe_handle
);
380 START_TEST(ServiceArgs
)
382 argc
= winetest_get_mainargs(&argv
);
384 scm_handle
= OpenSCManagerW(NULL
, NULL
, GENERIC_ALL
);
385 ok(scm_handle
!= NULL
, "OpenSCManager failed: %lu\n", GetLastError());
388 skip("Failed to open service control manager. Skipping test\n");
394 char *service_argvA
[10];
395 WCHAR
*service_argvW
[10];
397 test_runner(FALSE
, L
" A", 0, NULL
);
398 test_runner(FALSE
, L
" W", 0, NULL
);
399 test_runner(TRUE
, L
" A", 0, NULL
);
400 test_runner(TRUE
, L
" W", 0, NULL
);
402 service_argvA
[0] = "x";
403 service_argvW
[0] = L
"x";
404 test_runner(FALSE
, L
" A x", 1, service_argvA
);
405 test_runner(FALSE
, L
" W x", 1, service_argvA
);
406 test_runner(TRUE
, L
" A x", 1, service_argvW
);
407 test_runner(TRUE
, L
" W x", 1, service_argvW
);
409 service_argvA
[1] = "y";
410 service_argvW
[1] = L
"y";
411 test_runner(FALSE
, L
" A x y", 2, service_argvA
);
412 test_runner(FALSE
, L
" W x y", 2, service_argvA
);
413 test_runner(TRUE
, L
" A x y", 2, service_argvW
);
414 test_runner(TRUE
, L
" W x y", 2, service_argvW
);
416 service_argvA
[0] = "ab";
417 service_argvW
[0] = L
"ab";
418 test_runner(FALSE
, L
" A ab y", 2, service_argvA
);
419 test_runner(FALSE
, L
" W ab y", 2, service_argvA
);
420 test_runner(TRUE
, L
" A ab y", 2, service_argvW
);
421 test_runner(TRUE
, L
" W ab y", 2, service_argvW
);
423 service_argvA
[0] = "abc";
424 service_argvW
[0] = L
"abc";
425 test_runner(FALSE
, L
" A abc y", 2, service_argvA
);
426 test_runner(FALSE
, L
" W abc y", 2, service_argvA
);
427 test_runner(TRUE
, L
" A abc y", 2, service_argvW
);
428 test_runner(TRUE
, L
" W abc y", 2, service_argvW
);
430 service_argvA
[0] = "abcd";
431 service_argvW
[0] = L
"abcd";
432 test_runner(FALSE
, L
" A abcd y", 2, service_argvA
);
433 test_runner(FALSE
, L
" W abcd y", 2, service_argvA
);
434 test_runner(TRUE
, L
" A abcd y", 2, service_argvW
);
435 test_runner(TRUE
, L
" W abcd y", 2, service_argvW
);
437 service_argvA
[0] = "abcde";
438 service_argvW
[0] = L
"abcde";
439 test_runner(FALSE
, L
" A abcde y", 2, service_argvA
);
440 test_runner(FALSE
, L
" W abcde y", 2, service_argvA
);
441 test_runner(TRUE
, L
" A abcde y", 2, service_argvW
);
442 test_runner(TRUE
, L
" W abcde y", 2, service_argvW
);
444 service_argvA
[0] = "abcdef";
445 service_argvW
[0] = L
"abcdef";
446 test_runner(FALSE
, L
" A abcdef y", 2, service_argvA
);
447 test_runner(FALSE
, L
" W abcdef y", 2, service_argvA
);
448 test_runner(TRUE
, L
" A abcdef y", 2, service_argvW
);
449 test_runner(TRUE
, L
" W abcdef y", 2, service_argvW
);
451 service_argvA
[0] = "abcdefg";
452 service_argvW
[0] = L
"abcdefg";
453 test_runner(FALSE
, L
" A abcdefg y", 2, service_argvA
);
454 test_runner(FALSE
, L
" W abcdefg y", 2, service_argvA
);
455 test_runner(TRUE
, L
" A abcdefg y", 2, service_argvW
);
456 test_runner(TRUE
, L
" W abcdefg y", 2, service_argvW
);
458 service_argvA
[0] = "";
459 service_argvW
[0] = L
"";
460 test_runner(FALSE
, L
" A \"\" y", 2, service_argvA
);
461 test_runner(FALSE
, L
" W \"\" y", 2, service_argvA
);
462 test_runner(TRUE
, L
" A \"\" y", 2, service_argvW
);
463 test_runner(TRUE
, L
" W \"\" y", 2, service_argvW
);
467 strcpy(service_nameA
, argv
[2]);
468 MultiByteToWideChar(CP_ACP
, 0, service_nameA
, -1, service_nameW
, _countof(service_nameW
));
469 StringCbPrintfW(named_pipe_name
, sizeof(named_pipe_name
), L
"\\\\.\\pipe\\%ls_pipe", service_nameW
);
470 if (!strcmp(argv
[3], "A"))
471 service_process(FALSE
);
473 service_process(TRUE
);
476 CloseServiceHandle(scm_handle
);