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