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
);
35 static inline void service_trace(const char *msg
, ...)
40 va_start(valist
, msg
);
41 StringCbVPrintfA(buf
, sizeof(buf
), msg
, valist
);
44 send_msg("TRACE", buf
);
47 static void service_ok(int cnd
, const char *msg
, ...)
52 va_start(valist
, msg
);
53 StringCbVPrintfA(buf
, sizeof(buf
), msg
, valist
);
56 send_msg(cnd
? "OK" : "FAIL", buf
);
59 static VOID WINAPI
service_handler(DWORD ctrl
)
61 SERVICE_STATUS status
;
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;
72 case SERVICE_CONTROL_STOP
:
73 status
.dwCurrentState
= SERVICE_STOP_PENDING
;
74 status
.dwControlsAccepted
= 0;
75 SetServiceStatus(service_handle
, &status
);
77 status
.dwCurrentState
= SERVICE_RUNNING
;
78 SetServiceStatus(service_handle
, &status
);
82 static void service_main_common(void)
84 SERVICE_STATUS status
;
87 service_handle
= RegisterServiceCtrlHandlerW(service_nameW
, service_handler
);
88 service_ok(service_handle
!= NULL
, "RegisterServiceCtrlHandler failed: %lu\n", GetLastError());
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());
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());
111 * A/W argument layout XP/2003:
112 * [argv array of pointers][parameter 1][parameter 2]...[service name]
114 * A/W argument layout Vista:
115 * [argv array of pointers][align to 8 bytes][parameter 1][parameter 2]...[service name]
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]...
121 * Space for parameters and service name is always enough to store
122 * the WCHAR version including null terminator.
125 static void WINAPI
service_mainA(DWORD service_argc
, char **service_argv
)
129 char *next_arg_aligned
;
131 service_ok(service_argc
== argc
- 3, "service_argc = %d, expected %d", service_argc
, argc
- 3);
132 if (service_argc
== argc
- 3)
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
++)
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);
153 service_main_common();
156 static void WINAPI
service_mainW(DWORD service_argc
, WCHAR
**service_argv
)
161 WCHAR
*next_arg_aligned
;
163 service_ok(service_argc
== argc
- 3, "service_argc = %d, expected %d", service_argc
, argc
- 3);
164 if (service_argc
== argc
- 3)
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
++)
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);
186 service_main_common();
189 static void service_process(BOOLEAN unicode
)
193 SERVICE_TABLE_ENTRYA servtblA
[] =
195 { service_nameA
, service_mainA
},
198 SERVICE_TABLE_ENTRYW servtblW
[] =
200 { service_nameW
, service_mainW
},
204 res
= WaitNamedPipeW(named_pipe_name
, NMPWAIT_USE_DEFAULT_WAIT
);
208 pipe_handle
= CreateFileW(named_pipe_name
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
209 if (pipe_handle
== INVALID_HANDLE_VALUE
)
212 //service_trace("Starting...");
215 res
= StartServiceCtrlDispatcherW(servtblW
);
216 service_ok(res
, "StartServiceCtrlDispatcherW failed: %lu\n", GetLastError());
220 res
= StartServiceCtrlDispatcherA(servtblA
);
221 service_ok(res
, "StartServiceCtrlDispatcherA failed: %lu\n", GetLastError());
224 CloseHandle(pipe_handle
);
227 static SC_HANDLE
register_service(PCWSTR extra_args
)
229 WCHAR service_cmd
[MAX_PATH
+150];
232 GetModuleFileNameW(NULL
, service_cmd
, MAX_PATH
);
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
);
238 trace("service_cmd \"%ls\"\n", service_cmd
);
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
)
245 skip("Not enough access right to create service\n");
249 ok(service
!= NULL
, "CreateService failed: %lu\n", GetLastError());
253 static DWORD WINAPI
pipe_thread(void *arg
)
259 res
= ConnectNamedPipe(pipe_handle
, NULL
);
260 ok(res
|| GetLastError() == ERROR_PIPE_CONNECTED
, "ConnectNamedPipe failed: %lu\n", GetLastError());
264 res
= ReadFile(pipe_handle
, buf
, sizeof(buf
), &read
, NULL
);
267 ok(GetLastError() == ERROR_BROKEN_PIPE
|| GetLastError() == ERROR_INVALID_HANDLE
,
268 "ReadFile failed: %lu\n", GetLastError());
272 if (!strncmp(buf
, "TRACE:", 6))
274 trace("service trace: %s\n", buf
+6);
276 else if (!strncmp(buf
, "OK:", 3))
278 ok(1, "service: %s\n", buf
+3);
280 else if (!strncmp(buf
, "FAIL:", 5))
282 ok(0, "service: %s\n", buf
+5);
286 ok(0, "malformed service message: %s\n", buf
);
290 DisconnectNamedPipe(pipe_handle
);
291 //trace("pipe disconnected\n");
295 static void test_startA(SC_HANDLE service_handle
, int service_argc
, const char **service_argv
)
297 SERVICE_STATUS status
;
300 res
= StartServiceA(service_handle
, service_argc
, service_argv
);
301 ok(res
, "StartService failed: %lu\n", GetLastError());
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
);
315 static void test_startW(SC_HANDLE service_handle
, int service_argc
, const WCHAR
**service_argv
)
317 SERVICE_STATUS status
;
320 res
= StartServiceW(service_handle
, service_argc
, service_argv
);
321 ok(res
, "StartService failed: %lu\n", GetLastError());
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
);
335 static void test_runner(BOOLEAN unicode
, PCWSTR extra_args
, int service_argc
, void *service_argv
)
338 SC_HANDLE service_handle
;
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
);
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
)
352 thread
= CreateThread(NULL
, 0, pipe_thread
, NULL
, 0, NULL
);
353 ok(thread
!= NULL
, "CreateThread failed: %lu\n", GetLastError());
357 service_handle
= register_service(extra_args
);
361 //trace("starting...\n");
364 test_startW(service_handle
, service_argc
, service_argv
);
366 test_startA(service_handle
, service_argc
, service_argv
);
368 res
= DeleteService(service_handle
);
369 ok(res
, "DeleteService failed: %lu\n", GetLastError());
371 CloseServiceHandle(service_handle
);
373 ok(WaitForSingleObject(thread
, 10000) == WAIT_OBJECT_0
, "Timeout waiting for thread\n");
375 CloseHandle(pipe_handle
);
378 START_TEST(ServiceArgs
)
380 argc
= winetest_get_mainargs(&argv
);
382 scm_handle
= OpenSCManagerW(NULL
, NULL
, GENERIC_ALL
);
383 ok(scm_handle
!= NULL
, "OpenSCManager failed: %lu\n", GetLastError());
386 skip("Failed to open service control manager. Skipping test\n");
392 char *service_argvA
[10];
393 WCHAR
*service_argvW
[10];
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
471 service_process(TRUE
);
474 CloseServiceHandle(scm_handle
);