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>
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];
19 /* Test process global variables */
20 static SC_HANDLE scm_handle
;
21 static SERVICE_STATUS_HANDLE service_handle
;
23 static void send_msg(const char *type
, const char *msg
)
28 StringCbPrintfA(buf
, sizeof(buf
), "%s:%s", type
, msg
);
29 WriteFile(pipe_handle
, buf
, strlen(buf
)+1, &written
, NULL
);
33 static inline void service_trace(const char *msg
, ...)
38 va_start(valist
, msg
);
39 StringCbVPrintfA(buf
, sizeof(buf
), msg
, valist
);
42 send_msg("TRACE", buf
);
46 static void service_ok(int cnd
, const char *msg
, ...)
51 va_start(valist
, msg
);
52 StringCbVPrintfA(buf
, sizeof(buf
), msg
, valist
);
55 send_msg(cnd
? "OK" : "FAIL", buf
);
58 static VOID WINAPI
service_handler(DWORD ctrl
)
60 SERVICE_STATUS status
;
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;
71 case SERVICE_CONTROL_STOP
:
72 status
.dwCurrentState
= SERVICE_STOP_PENDING
;
73 status
.dwControlsAccepted
= 0;
74 SetServiceStatus(service_handle
, &status
);
76 status
.dwCurrentState
= SERVICE_RUNNING
;
77 SetServiceStatus(service_handle
, &status
);
81 static void service_main_common(void)
83 SERVICE_STATUS status
;
86 service_handle
= RegisterServiceCtrlHandlerW(service_nameW
, service_handler
);
87 service_ok(service_handle
!= NULL
, "RegisterServiceCtrlHandler failed: %lu\n", GetLastError());
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());
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());
110 * A/W argument layout XP/2003:
111 * [argv array of pointers][parameter 1][parameter 2]...[service name]
113 * A/W argument layout Vista:
114 * [argv array of pointers][align to 8 bytes][parameter 1][parameter 2]...[service name]
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]...
120 * Space for parameters and service name is always enough to store
121 * the WCHAR version including null terminator.
124 static void WINAPI
service_mainA(DWORD service_argc
, char **service_argv
)
128 char *next_arg_aligned
;
130 service_ok(service_argc
== argc
- 3, "service_argc = %d, expected %d", service_argc
, argc
- 3);
131 if (service_argc
== argc
- 3)
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
++)
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);
152 service_main_common();
155 static void WINAPI
service_mainW(DWORD service_argc
, WCHAR
**service_argv
)
160 WCHAR
*next_arg_aligned
;
162 service_ok(service_argc
== argc
- 3, "service_argc = %d, expected %d", service_argc
, argc
- 3);
163 if (service_argc
== argc
- 3)
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
++)
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);
185 service_main_common();
188 static void service_process(BOOLEAN unicode
)
192 SERVICE_TABLE_ENTRYA servtblA
[] =
194 { service_nameA
, service_mainA
},
197 SERVICE_TABLE_ENTRYW servtblW
[] =
199 { service_nameW
, service_mainW
},
203 res
= WaitNamedPipeW(named_pipe_name
, NMPWAIT_USE_DEFAULT_WAIT
);
207 pipe_handle
= CreateFileW(named_pipe_name
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
208 if (pipe_handle
== INVALID_HANDLE_VALUE
)
211 //service_trace("Starting...");
214 res
= StartServiceCtrlDispatcherW(servtblW
);
215 service_ok(res
, "StartServiceCtrlDispatcherW failed: %lu\n", GetLastError());
219 res
= StartServiceCtrlDispatcherA(servtblA
);
220 service_ok(res
, "StartServiceCtrlDispatcherA failed: %lu\n", GetLastError());
223 CloseHandle(pipe_handle
);
226 static SC_HANDLE
register_service(PCWSTR extra_args
)
228 WCHAR service_cmd
[MAX_PATH
+150];
231 GetModuleFileNameW(NULL
, service_cmd
, MAX_PATH
);
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
);
237 trace("service_cmd \"%ls\"\n", service_cmd
);
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
)
244 skip("Not enough access right to create service\n");
248 ok(service
!= NULL
, "CreateService failed: %lu\n", GetLastError());
252 static DWORD WINAPI
pipe_thread(void *arg
)
258 res
= ConnectNamedPipe(pipe_handle
, NULL
);
259 ok(res
|| GetLastError() == ERROR_PIPE_CONNECTED
, "ConnectNamedPipe failed: %lu\n", GetLastError());
263 res
= ReadFile(pipe_handle
, buf
, sizeof(buf
), &read
, NULL
);
266 ok(GetLastError() == ERROR_BROKEN_PIPE
|| GetLastError() == ERROR_INVALID_HANDLE
,
267 "ReadFile failed: %lu\n", GetLastError());
271 if (!strncmp(buf
, "TRACE:", 6))
273 trace("service trace: %s\n", buf
+6);
275 else if (!strncmp(buf
, "OK:", 3))
277 ok(1, "service: %s\n", buf
+3);
279 else if (!strncmp(buf
, "FAIL:", 5))
281 ok(0, "service: %s\n", buf
+5);
285 ok(0, "malformed service message: %s\n", buf
);
289 DisconnectNamedPipe(pipe_handle
);
290 //trace("pipe disconnected\n");
294 static void test_startA(SC_HANDLE service_handle
, int service_argc
, const char **service_argv
)
296 SERVICE_STATUS status
;
299 res
= StartServiceA(service_handle
, service_argc
, service_argv
);
300 ok(res
, "StartService failed: %lu\n", GetLastError());
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
);
314 static void test_startW(SC_HANDLE service_handle
, int service_argc
, const WCHAR
**service_argv
)
316 SERVICE_STATUS status
;
319 res
= StartServiceW(service_handle
, service_argc
, service_argv
);
320 ok(res
, "StartService failed: %lu\n", GetLastError());
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
);
334 static void test_runner(BOOLEAN unicode
, PCWSTR extra_args
, int service_argc
, void *service_argv
)
337 SC_HANDLE service_handle
;
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
);
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
)
351 thread
= CreateThread(NULL
, 0, pipe_thread
, NULL
, 0, NULL
);
352 ok(thread
!= NULL
, "CreateThread failed: %lu\n", GetLastError());
356 service_handle
= register_service(extra_args
);
360 //trace("starting...\n");
363 test_startW(service_handle
, service_argc
, service_argv
);
365 test_startA(service_handle
, service_argc
, service_argv
);
367 res
= DeleteService(service_handle
);
368 ok(res
, "DeleteService failed: %lu\n", GetLastError());
370 CloseServiceHandle(service_handle
);
372 ok(WaitForSingleObject(thread
, 10000) == WAIT_OBJECT_0
, "Timeout waiting for thread\n");
378 if (pipe_handle
!= INVALID_HANDLE_VALUE
)
379 CloseHandle(pipe_handle
);
382 START_TEST(ServiceArgs
)
384 argc
= winetest_get_mainargs(&argv
);
386 scm_handle
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
387 ok(scm_handle
!= NULL
, "OpenSCManager failed: %lu\n", GetLastError());
390 skip("Failed to open service control manager. Skipping test\n");
396 char *service_argvA
[10];
397 WCHAR
*service_argvW
[10];
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
475 service_process(TRUE
);
478 CloseServiceHandle(scm_handle
);