1 /*
2  * Copyright 2012 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <stdarg.h>
20 
21 #include <windef.h>
22 #include <winsvc.h>
23 #include <stdio.h>
24 #include <winbase.h>
25 #include <winuser.h>
26 
27 #include "wine/test.h"
28 
29 static SERVICE_STATUS_HANDLE (WINAPI *pRegisterServiceCtrlHandlerExA)(LPCSTR,LPHANDLER_FUNCTION_EX,LPVOID);
30 
31 static HANDLE pipe_handle = INVALID_HANDLE_VALUE;
32 static char service_name[100], named_pipe_name[100];
33 static SERVICE_STATUS_HANDLE service_handle;
34 
35 /* Service process global variables */
36 static HANDLE service_stop_event;
37 
38 static void send_msg(const char *type, const char *msg)
39 {
40     DWORD written = 0;
41     char buf[512];
42 
43     sprintf(buf, "%s:%s", type, msg);
44     WriteFile(pipe_handle, buf, strlen(buf)+1, &written, NULL);
45 }
46 
47 static inline void service_trace(const char *msg)
48 {
49     send_msg("TRACE", msg);
50 }
51 
52 static inline void service_event(const char *event)
53 {
54     send_msg("EVENT", event);
55 }
56 
57 static void service_ok(int cnd, const char *msg, ...)
58 {
59    va_list valist;
60    char buf[512];
61 
62     va_start(valist, msg);
63     vsprintf(buf, msg, valist);
64     va_end(valist);
65 
66     send_msg(cnd ? "OK" : "FAIL", buf);
67 }
68 
69 static void test_winstation(void)
70 {
71     HWINSTA winstation;
72     USEROBJECTFLAGS flags;
73     BOOL r;
74 
75     winstation = GetProcessWindowStation();
76     service_ok(winstation != NULL, "winstation = NULL\n");
77 
78     r = GetUserObjectInformationA(winstation, UOI_FLAGS, &flags, sizeof(flags), NULL);
79     service_ok(r, "GetUserObjectInformation(UOI_NAME) failed: %u\n", GetLastError());
80     service_ok(!(flags.dwFlags & WSF_VISIBLE), "winstation has flags %x\n", flags.dwFlags);
81 }
82 
83 /*
84  * Test creating window in a service process. Although services run in non-interactive,
85  * they may create windows that will never be visible.
86  */
87 static void test_create_window(void)
88 {
89     DWORD style;
90     ATOM class;
91     HWND hwnd;
92     BOOL r;
93 
94     static WNDCLASSEXA wndclass = {
95         sizeof(WNDCLASSEXA),
96         0,
97         DefWindowProcA,
98         0, 0, NULL, NULL, NULL, NULL, NULL,
99         "service_test",
100         NULL
101     };
102 
103     hwnd = GetDesktopWindow();
104     service_ok(IsWindow(hwnd), "GetDesktopWindow returned invalid window %p\n", hwnd);
105 
106     class = RegisterClassExA(&wndclass);
107     service_ok(class, "RegisterClassFailed\n");
108 
109     hwnd = CreateWindowA("service_test", "service_test",
110             WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
111             515, 530, NULL, NULL, NULL, NULL);
112     service_ok(hwnd != NULL, "CreateWindow failed: %u\n", GetLastError());
113 
114     style = GetWindowLongW(hwnd, GWL_STYLE);
115     service_ok(!(style & WS_VISIBLE), "style = %x, expected invisible\n", style);
116 
117     r = ShowWindow(hwnd, SW_SHOW);
118     service_ok(!r, "ShowWindow returned %x\n", r);
119 
120     style = GetWindowLongW(hwnd, GWL_STYLE);
121     service_ok(style & WS_VISIBLE, "style = %x, expected visible\n", style);
122 
123     r = ShowWindow(hwnd, SW_SHOW);
124     service_ok(r, "ShowWindow returned %x\n", r);
125 
126     r = DestroyWindow(hwnd);
127     service_ok(r, "DestroyWindow failed: %08x\n", GetLastError());
128 }
129 
130 static DWORD WINAPI service_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
131 {
132     SERVICE_STATUS status;
133 
134     status.dwServiceType             = SERVICE_WIN32;
135     status.dwControlsAccepted        = SERVICE_ACCEPT_STOP;
136     status.dwWin32ExitCode           = 0;
137     status.dwServiceSpecificExitCode = 0;
138     status.dwCheckPoint              = 0;
139     status.dwWaitHint                = 0;
140 
141     switch(ctrl)
142     {
143     case SERVICE_CONTROL_STOP:
144     case SERVICE_CONTROL_SHUTDOWN:
145         service_event("STOP");
146         status.dwCurrentState     = SERVICE_STOP_PENDING;
147         status.dwControlsAccepted = 0;
148         SetServiceStatus(service_handle, &status);
149         SetEvent(service_stop_event);
150         return NO_ERROR;
151     case 128:
152         test_winstation();
153         test_create_window();
154         service_event("CUSTOM");
155         return 0xdeadbeef;
156     default:
157         status.dwCurrentState = SERVICE_RUNNING;
158         SetServiceStatus( service_handle, &status );
159         return NO_ERROR;
160     }
161 }
162 
163 static void WINAPI service_main(DWORD argc, char **argv)
164 {
165     SERVICE_STATUS status;
166     char buf[64];
167     BOOL res;
168 
169     service_ok(argc == 3, "argc = %u, expected 3\n", argc);
170     service_ok(!strcmp(argv[0], service_name), "argv[0] = '%s', expected '%s'\n", argv[0], service_name);
171     service_ok(!strcmp(argv[1], "param1"), "argv[1] = '%s', expected 'param1'\n", argv[1]);
172     service_ok(!strcmp(argv[2], "param2"), "argv[2] = '%s', expected 'param2'\n", argv[2]);
173 
174     buf[0] = 0;
175     GetEnvironmentVariableA("PATHEXT", buf, sizeof(buf));
176     service_ok(buf[0], "did not find PATHEXT environment variable\n");
177 
178     service_handle = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL);
179     service_ok(service_handle != NULL, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
180     if(!service_handle)
181         return;
182 
183     status.dwServiceType             = SERVICE_WIN32;
184     status.dwCurrentState            = SERVICE_RUNNING;
185     status.dwControlsAccepted        = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
186     status.dwWin32ExitCode           = 0;
187     status.dwServiceSpecificExitCode = 0;
188     status.dwCheckPoint              = 0;
189     status.dwWaitHint                = 10000;
190     res = SetServiceStatus(service_handle, &status);
191     service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %u\n", GetLastError());
192 
193     service_event("RUNNING");
194 
195     WaitForSingleObject(service_stop_event, INFINITE);
196 
197     status.dwCurrentState     = SERVICE_STOPPED;
198     status.dwControlsAccepted = 0;
199     res = SetServiceStatus(service_handle, &status);
200     service_ok(res, "SetServiceStatus(SERVICE_STOPPED) failed: %u\n", GetLastError());
201 }
202 
203 static void service_process(void (WINAPI *p_service_main)(DWORD, char **))
204 {
205     BOOL res;
206 
207     SERVICE_TABLE_ENTRYA servtbl[] = {
208         {service_name, p_service_main},
209         {NULL, NULL}
210     };
211 
212     res = WaitNamedPipeA(named_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
213     if(!res)
214         return;
215 
216     pipe_handle = CreateFileA(named_pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
217     if(pipe_handle == INVALID_HANDLE_VALUE)
218         return;
219 
220     service_trace("Starting...\n");
221 
222     service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
223     service_ok(service_stop_event != NULL, "Could not create event: %u\n", GetLastError());
224     if(!service_stop_event)
225         return;
226 
227     res = StartServiceCtrlDispatcherA(servtbl);
228     service_ok(res, "StartServiceCtrlDispatcher failed: %u\n", GetLastError());
229 
230     /* Let service thread terminate */
231     Sleep(50);
232 
233     CloseHandle(service_stop_event);
234     CloseHandle(pipe_handle);
235 }
236 
237 static DWORD WINAPI no_stop_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
238 {
239     SERVICE_STATUS status;
240 
241     status.dwServiceType             = SERVICE_WIN32;
242     status.dwControlsAccepted        = SERVICE_ACCEPT_STOP;
243     status.dwWin32ExitCode           = 0;
244     status.dwServiceSpecificExitCode = 0;
245     status.dwCheckPoint              = 0;
246     status.dwWaitHint                = 0;
247 
248     switch(ctrl)
249     {
250         case SERVICE_CONTROL_STOP:
251         case SERVICE_CONTROL_SHUTDOWN:
252             service_event("STOP");
253             status.dwCurrentState     = SERVICE_STOPPED;
254             status.dwControlsAccepted = 0;
255             SetServiceStatus(service_handle, &status);
256             SetEvent(service_stop_event);
257             return NO_ERROR;
258         default:
259             status.dwCurrentState = SERVICE_RUNNING;
260             SetServiceStatus( service_handle, &status );
261             return NO_ERROR;
262     }
263 }
264 
265 static void WINAPI no_stop_main(DWORD argc, char **argv)
266 {
267     SERVICE_STATUS status;
268     BOOL res;
269 
270     service_ok(argc == 1, "argc = %u, expected 1\n", argc);
271     service_ok(!strcmp(argv[0], service_name), "argv[0] = '%s', expected '%s'\n", argv[0], service_name);
272 
273     service_handle = pRegisterServiceCtrlHandlerExA(service_name, no_stop_handler, NULL);
274     service_ok(service_handle != NULL, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
275     if(!service_handle)
276         return;
277 
278     status.dwServiceType             = SERVICE_WIN32;
279     status.dwCurrentState            = SERVICE_RUNNING;
280     status.dwControlsAccepted        = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
281     status.dwWin32ExitCode           = 0;
282     status.dwServiceSpecificExitCode = 0;
283     status.dwCheckPoint              = 0;
284     status.dwWaitHint                = 10000;
285     res = SetServiceStatus(service_handle, &status);
286     service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %u\n", GetLastError());
287 
288     service_event("RUNNING");
289 }
290 
291 /* Test process global variables */
292 static SC_HANDLE scm_handle;
293 
294 static char current_event[32];
295 static HANDLE event_handle = INVALID_HANDLE_VALUE;
296 static CRITICAL_SECTION event_cs;
297 
298 static SC_HANDLE register_service(const char *test_name)
299 {
300     char service_cmd[MAX_PATH+150], *ptr;
301     SC_HANDLE service;
302 
303     ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH);
304 
305     /* If the file doesn't exist, assume we're using built-in exe and append .so to the path */
306     if(GetFileAttributesA(service_cmd) == INVALID_FILE_ATTRIBUTES) {
307         strcpy(ptr, ".so");
308         ptr += 3;
309     }
310 
311     strcpy(ptr, " service ");
312     ptr += strlen(ptr);
313     sprintf(ptr, "%s ", test_name);
314     ptr += strlen(ptr);
315     strcpy(ptr, service_name);
316 
317     trace("service_cmd \"%s\"\n", service_cmd);
318 
319     service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL,
320                              SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
321                              service_cmd, NULL, NULL, NULL, NULL, NULL);
322     if(!service && GetLastError() == ERROR_ACCESS_DENIED) {
323         skip("Not enough access right to create service\n");
324         return NULL;
325     }
326 
327     ok(service != NULL, "CreateService failed: %u\n", GetLastError());
328     return service;
329 }
330 
331 static void expect_event(const char *event_name)
332 {
333     char evt[32];
334     DWORD res;
335 
336     trace("waiting for %s\n", event_name);
337 
338     res = WaitForSingleObject(event_handle, 30000);
339     ok(res == WAIT_OBJECT_0, "WaitForSingleObject failed: %u\n", res);
340     if(res != WAIT_OBJECT_0)
341         return;
342 
343     EnterCriticalSection(&event_cs);
344     strcpy(evt, current_event);
345     *current_event = 0;
346     LeaveCriticalSection(&event_cs);
347 
348     ok(!strcmp(evt, event_name), "Unexpected event: %s, expected %s\n", evt, event_name);
349 }
350 
351 static DWORD WINAPI pipe_thread(void *arg)
352 {
353     char buf[512], *ptr;
354     DWORD read;
355     BOOL res;
356 
357     res = ConnectNamedPipe(pipe_handle, NULL);
358     ok(res || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed: %u\n", GetLastError());
359 
360     while(1) {
361         res = ReadFile(pipe_handle, buf, sizeof(buf), &read, NULL);
362         if(!res) {
363             ok(GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_INVALID_HANDLE,
364                "ReadFile failed: %u\n", GetLastError());
365             break;
366         }
367 
368         for(ptr = buf; ptr < buf+read; ptr += strlen(ptr)+1) {
369             if(!strncmp(ptr, "TRACE:", 6)) {
370                 trace("service trace: %s", ptr+6);
371             }else if(!strncmp(ptr, "OK:", 3)) {
372                 ok(1, "service: %s", ptr+3);
373             }else if(!strncmp(ptr, "FAIL:", 5)) {
374                 ok(0, "service: %s", ptr+5);
375             }else if(!strncmp(ptr, "EVENT:", 6)) {
376                 trace("service event: %s\n", ptr+6);
377                 EnterCriticalSection(&event_cs);
378                 ok(!current_event[0], "event %s still queued\n", current_event);
379                 strcpy(current_event, ptr+6);
380                 LeaveCriticalSection(&event_cs);
381                 SetEvent(event_handle);
382             }else {
383                 ok(0, "malformed service message: %s\n", ptr);
384             }
385         }
386     }
387 
388     DisconnectNamedPipe(pipe_handle);
389     trace("pipe disconnected\n");
390     return 0;
391 }
392 
393 static void test_service(void)
394 {
395     static const char *argv[2] = {"param1", "param2"};
396     SC_HANDLE service_handle = register_service("simple_service");
397     SERVICE_STATUS_PROCESS status2;
398     SERVICE_STATUS status;
399     DWORD bytes;
400     BOOL res;
401 
402     if(!service_handle)
403         return;
404 
405     trace("starting...\n");
406     res = StartServiceA(service_handle, 2, argv);
407     ok(res, "StartService failed: %u\n", GetLastError());
408     if(!res) {
409         DeleteService(service_handle);
410         CloseServiceHandle(service_handle);
411         return;
412     }
413     expect_event("RUNNING");
414 
415     res = QueryServiceStatus(service_handle, &status);
416     ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
417     todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
418     ok(status.dwCurrentState == SERVICE_RUNNING, "status.dwCurrentState = %x\n", status.dwCurrentState);
419     ok(status.dwControlsAccepted == (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN),
420             "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
421     ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
422     ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
423             status.dwServiceSpecificExitCode);
424     ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
425     todo_wine ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
426 
427     res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
428     ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
429     ok(status2.dwCurrentState == SERVICE_RUNNING, "status2.dwCurrentState = %x\n", status2.dwCurrentState);
430     ok(status2.dwProcessId != 0, "status2.dwProcessId = %d\n", status2.dwProcessId);
431 
432     res = ControlService(service_handle, 128, &status);
433     ok(res, "ControlService failed: %u\n", GetLastError());
434     expect_event("CUSTOM");
435 
436     res = ControlService(service_handle, SERVICE_CONTROL_STOP, &status);
437     ok(res, "ControlService failed: %u\n", GetLastError());
438     expect_event("STOP");
439 
440     res = DeleteService(service_handle);
441     ok(res, "DeleteService failed: %u\n", GetLastError());
442 
443     CloseServiceHandle(service_handle);
444 }
445 
446 static inline void test_no_stop(void)
447 {
448     SC_HANDLE service_handle = register_service("no_stop");
449     SERVICE_STATUS_PROCESS status2;
450     SERVICE_STATUS status;
451     DWORD bytes;
452     BOOL res;
453 
454     if(!service_handle)
455         return;
456 
457     trace("starting...\n");
458     res = StartServiceA(service_handle, 0, NULL);
459     ok(res, "StartService failed: %u\n", GetLastError());
460     if(!res) {
461         DeleteService(service_handle);
462         CloseServiceHandle(service_handle);
463         return;
464     }
465     expect_event("RUNNING");
466 
467     /* Let service thread terminate */
468     Sleep(1000);
469 
470     res = QueryServiceStatus(service_handle, &status);
471     ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
472     todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
473     ok(status.dwCurrentState == SERVICE_RUNNING, "status.dwCurrentState = %x\n", status.dwCurrentState);
474     ok(status.dwControlsAccepted == (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN),
475             "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
476     ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
477     ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
478             status.dwServiceSpecificExitCode);
479     ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
480     todo_wine ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
481 
482     res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
483     ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
484     ok(status2.dwCurrentState == SERVICE_RUNNING, "status2.dwCurrentState = %x\n", status2.dwCurrentState);
485     ok(status2.dwProcessId != 0, "status2.dwProcessId = %d\n", status2.dwProcessId);
486 
487     res = ControlService(service_handle, SERVICE_CONTROL_STOP, &status);
488     ok(res, "ControlService failed: %u\n", GetLastError());
489     expect_event("STOP");
490 
491     res = QueryServiceStatus(service_handle, &status);
492     ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
493     todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
494     ok(status.dwCurrentState==SERVICE_STOPPED || status.dwCurrentState==SERVICE_STOP_PENDING,
495             "status.dwCurrentState = %x\n", status.dwCurrentState);
496     ok(status.dwControlsAccepted == 0, "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
497     ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
498     ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
499             status.dwServiceSpecificExitCode);
500     ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
501     ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
502 
503     res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
504     ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
505     ok(status2.dwProcessId == 0 || broken(status2.dwProcessId != 0),
506        "status2.dwProcessId = %d\n", status2.dwProcessId);
507 
508     res = DeleteService(service_handle);
509     ok(res, "DeleteService failed: %u\n", GetLastError());
510 
511     res = QueryServiceStatus(service_handle, &status);
512     ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
513     todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
514     ok(status.dwCurrentState==SERVICE_STOPPED || status.dwCurrentState==SERVICE_STOP_PENDING,
515             "status.dwCurrentState = %x\n", status.dwCurrentState);
516     ok(status.dwControlsAccepted == 0, "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
517     ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
518     ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
519             status.dwServiceSpecificExitCode);
520     ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
521     ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
522 
523     res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
524     ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
525     ok(status2.dwProcessId == 0 || broken(status2.dwProcessId != 0),
526        "status2.dwProcessId = %d\n", status2.dwProcessId);
527 
528     CloseServiceHandle(service_handle);
529 
530     res = QueryServiceStatus(service_handle, &status);
531     ok(!res, "QueryServiceStatus should have failed\n");
532     ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError = %d\n", GetLastError());
533 }
534 
535 static void test_runner(void (*p_run_test)(void))
536 {
537     HANDLE thread;
538 
539     sprintf(service_name, "WineTestService%d", GetTickCount());
540     trace("service_name: %s\n", service_name);
541     sprintf(named_pipe_name, "\\\\.\\pipe\\%s_pipe", service_name);
542 
543     pipe_handle = CreateNamedPipeA(named_pipe_name, PIPE_ACCESS_INBOUND,
544                                    PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 10, 2048, 2048, 10000, NULL);
545     ok(pipe_handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %u\n", GetLastError());
546     if(pipe_handle == INVALID_HANDLE_VALUE)
547         return;
548 
549     event_handle = CreateEventA(NULL, FALSE, FALSE, NULL);
550     ok(event_handle != INVALID_HANDLE_VALUE, "CreateEvent failed: %u\n", GetLastError());
551     if(event_handle == INVALID_HANDLE_VALUE)
552         return;
553 
554     thread = CreateThread(NULL, 0, pipe_thread, NULL, 0, NULL);
555     ok(thread != NULL, "CreateThread failed: %u\n", GetLastError());
556     if(!thread)
557         return;
558 
559     p_run_test();
560 
561     WaitForSingleObject(thread, INFINITE);
562     CloseHandle(event_handle);
563     CloseHandle(pipe_handle);
564     CloseHandle(thread);
565 }
566 
567 START_TEST(service)
568 {
569     char **argv;
570     int argc;
571 
572     InitializeCriticalSection(&event_cs);
573 
574     pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
575     if(!pRegisterServiceCtrlHandlerExA) {
576         win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
577         return;
578     }
579 
580     scm_handle = OpenSCManagerA(NULL, NULL, GENERIC_ALL);
581     ok(scm_handle != NULL || GetLastError() == ERROR_ACCESS_DENIED, "OpenSCManager failed: %u\n", GetLastError());
582     if(!scm_handle) {
583         skip("OpenSCManager failed, skipping tests\n");
584         return;
585     }
586 
587     argc = winetest_get_mainargs(&argv);
588 
589     if(argc < 3) {
590         test_runner(test_service);
591         test_runner(test_no_stop);
592     }else {
593         strcpy(service_name, argv[3]);
594         sprintf(named_pipe_name, "\\\\.\\pipe\\%s_pipe", service_name);
595 
596         if(!strcmp(argv[2], "simple_service"))
597             service_process(service_main);
598         else if(!strcmp(argv[2], "no_stop"))
599             service_process(no_stop_main);
600     }
601 
602     CloseServiceHandle(scm_handle);
603 }
604