2012-06-15 15:35:19 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2012 Jacek Caban for CodeWeavers
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2017-02-10 21:57:18 +01:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2013-10-15 22:08:39 +02:00
|
|
|
#include <windef.h>
|
|
|
|
#include <winsvc.h>
|
2012-06-15 15:35:19 +02:00
|
|
|
#include <stdio.h>
|
2017-02-09 15:13:09 +01:00
|
|
|
#include <winbase.h>
|
|
|
|
#include <winuser.h>
|
2012-06-15 15:35:19 +02:00
|
|
|
|
|
|
|
#include "wine/test.h"
|
|
|
|
|
|
|
|
static SERVICE_STATUS_HANDLE (WINAPI *pRegisterServiceCtrlHandlerExA)(LPCSTR,LPHANDLER_FUNCTION_EX,LPVOID);
|
|
|
|
|
|
|
|
static HANDLE pipe_handle = INVALID_HANDLE_VALUE;
|
2020-02-11 19:09:33 +01:00
|
|
|
static char service_name[100], named_pipe_name[114];
|
2012-06-15 15:35:19 +02:00
|
|
|
static SERVICE_STATUS_HANDLE service_handle;
|
|
|
|
|
|
|
|
/* Service process global variables */
|
|
|
|
static HANDLE service_stop_event;
|
|
|
|
|
2018-10-02 16:27:42 +02:00
|
|
|
static int monitor_count;
|
|
|
|
|
2012-06-15 15:35:19 +02:00
|
|
|
static void send_msg(const char *type, const char *msg)
|
|
|
|
{
|
|
|
|
DWORD written = 0;
|
|
|
|
char buf[512];
|
|
|
|
|
|
|
|
sprintf(buf, "%s:%s", type, msg);
|
|
|
|
WriteFile(pipe_handle, buf, strlen(buf)+1, &written, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void service_trace(const char *msg)
|
|
|
|
{
|
|
|
|
send_msg("TRACE", msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void service_event(const char *event)
|
|
|
|
{
|
|
|
|
send_msg("EVENT", event);
|
|
|
|
}
|
|
|
|
|
2019-04-24 12:10:19 +02:00
|
|
|
static void WINAPIV service_ok(int cnd, const char *msg, ...)
|
2012-06-15 15:35:19 +02:00
|
|
|
{
|
2019-04-24 12:10:19 +02:00
|
|
|
__ms_va_list valist;
|
|
|
|
char buf[512];
|
2012-06-15 15:35:19 +02:00
|
|
|
|
2019-04-24 12:10:19 +02:00
|
|
|
__ms_va_start(valist, msg);
|
2012-06-15 15:35:19 +02:00
|
|
|
vsprintf(buf, msg, valist);
|
2019-04-24 12:10:19 +02:00
|
|
|
__ms_va_end(valist);
|
2012-06-15 15:35:19 +02:00
|
|
|
|
|
|
|
send_msg(cnd ? "OK" : "FAIL", buf);
|
|
|
|
}
|
|
|
|
|
2017-02-09 15:13:09 +01:00
|
|
|
static void test_winstation(void)
|
|
|
|
{
|
|
|
|
HWINSTA winstation;
|
|
|
|
USEROBJECTFLAGS flags;
|
|
|
|
BOOL r;
|
|
|
|
|
|
|
|
winstation = GetProcessWindowStation();
|
|
|
|
service_ok(winstation != NULL, "winstation = NULL\n");
|
|
|
|
|
|
|
|
r = GetUserObjectInformationA(winstation, UOI_FLAGS, &flags, sizeof(flags), NULL);
|
|
|
|
service_ok(r, "GetUserObjectInformation(UOI_NAME) failed: %u\n", GetLastError());
|
|
|
|
service_ok(!(flags.dwFlags & WSF_VISIBLE), "winstation has flags %x\n", flags.dwFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test creating window in a service process. Although services run in non-interactive,
|
|
|
|
* they may create windows that will never be visible.
|
|
|
|
*/
|
|
|
|
static void test_create_window(void)
|
|
|
|
{
|
|
|
|
DWORD style;
|
|
|
|
ATOM class;
|
|
|
|
HWND hwnd;
|
|
|
|
BOOL r;
|
|
|
|
|
|
|
|
static WNDCLASSEXA wndclass = {
|
|
|
|
sizeof(WNDCLASSEXA),
|
|
|
|
0,
|
|
|
|
DefWindowProcA,
|
|
|
|
0, 0, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
"service_test",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
hwnd = GetDesktopWindow();
|
|
|
|
service_ok(IsWindow(hwnd), "GetDesktopWindow returned invalid window %p\n", hwnd);
|
|
|
|
|
|
|
|
class = RegisterClassExA(&wndclass);
|
|
|
|
service_ok(class, "RegisterClassFailed\n");
|
|
|
|
|
|
|
|
hwnd = CreateWindowA("service_test", "service_test",
|
|
|
|
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
|
|
515, 530, NULL, NULL, NULL, NULL);
|
|
|
|
service_ok(hwnd != NULL, "CreateWindow failed: %u\n", GetLastError());
|
|
|
|
|
|
|
|
style = GetWindowLongW(hwnd, GWL_STYLE);
|
|
|
|
service_ok(!(style & WS_VISIBLE), "style = %x, expected invisible\n", style);
|
|
|
|
|
|
|
|
r = ShowWindow(hwnd, SW_SHOW);
|
|
|
|
service_ok(!r, "ShowWindow returned %x\n", r);
|
|
|
|
|
|
|
|
style = GetWindowLongW(hwnd, GWL_STYLE);
|
|
|
|
service_ok(style & WS_VISIBLE, "style = %x, expected visible\n", style);
|
|
|
|
|
|
|
|
r = ShowWindow(hwnd, SW_SHOW);
|
|
|
|
service_ok(r, "ShowWindow returned %x\n", r);
|
|
|
|
|
|
|
|
r = DestroyWindow(hwnd);
|
|
|
|
service_ok(r, "DestroyWindow failed: %08x\n", GetLastError());
|
|
|
|
}
|
|
|
|
|
2018-10-02 16:27:42 +02:00
|
|
|
static BOOL CALLBACK monitor_enum_proc(HMONITOR hmon, HDC hdc, LPRECT lprc, LPARAM lparam)
|
|
|
|
{
|
2021-08-23 05:30:11 +02:00
|
|
|
static const RECT expected_rect = {0, 0, 1024, 768};
|
2018-10-02 16:27:42 +02:00
|
|
|
BOOL r;
|
|
|
|
MONITORINFOEXA mi;
|
|
|
|
|
|
|
|
service_ok(hmon != NULL, "Unexpected hmon=%#x\n", hmon);
|
|
|
|
|
|
|
|
monitor_count++;
|
|
|
|
|
|
|
|
mi.cbSize = sizeof(mi);
|
|
|
|
|
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
r = GetMonitorInfoA(NULL, (MONITORINFO*)&mi);
|
|
|
|
service_ok(GetLastError() == ERROR_INVALID_MONITOR_HANDLE, "Unexpected GetLastError: %#x.\n", GetLastError());
|
|
|
|
service_ok(!r, "GetMonitorInfo with NULL HMONITOR succeeded.\n");
|
|
|
|
|
|
|
|
r = GetMonitorInfoA(hmon, (MONITORINFO*)&mi);
|
|
|
|
service_ok(r, "GetMonitorInfo failed.\n");
|
|
|
|
|
2021-08-23 05:30:11 +02:00
|
|
|
service_ok(EqualRect(lprc, &expected_rect), "Unexpected rect: %s\n", wine_dbgstr_rect(lprc));
|
|
|
|
service_ok(EqualRect(&mi.rcMonitor, &expected_rect), "Unexpected rcMonitor: %s\n",
|
|
|
|
wine_dbgstr_rect(&mi.rcMonitor));
|
|
|
|
service_ok(EqualRect(&mi.rcWork, &expected_rect), "Unexpected rcWork: %s\n",
|
|
|
|
wine_dbgstr_rect(&mi.rcWork));
|
|
|
|
service_ok(!strcmp(mi.szDevice, "WinDisc"), "Unexpected szDevice received: %s\n", mi.szDevice);
|
2018-10-02 16:27:42 +02:00
|
|
|
|
|
|
|
service_ok(mi.dwFlags == MONITORINFOF_PRIMARY, "Unexpected secondary monitor info.\n");
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2018-10-22 03:17:06 +02:00
|
|
|
/* query monitor information, even in non-interactive services */
|
2018-10-02 16:27:42 +02:00
|
|
|
static void test_monitors(void)
|
|
|
|
{
|
|
|
|
BOOL r;
|
|
|
|
|
|
|
|
r = EnumDisplayMonitors(0, 0, monitor_enum_proc, 0);
|
|
|
|
service_ok(r, "EnumDisplayMonitors failed.\n");
|
|
|
|
service_ok(monitor_count == 1, "Callback got called less or more than once. %d\n", monitor_count);
|
|
|
|
}
|
|
|
|
|
2012-06-15 15:35:19 +02:00
|
|
|
static DWORD WINAPI service_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
|
|
|
|
{
|
|
|
|
SERVICE_STATUS status;
|
|
|
|
|
|
|
|
status.dwServiceType = SERVICE_WIN32;
|
|
|
|
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
|
|
|
status.dwWin32ExitCode = 0;
|
|
|
|
status.dwServiceSpecificExitCode = 0;
|
|
|
|
status.dwCheckPoint = 0;
|
|
|
|
status.dwWaitHint = 0;
|
|
|
|
|
|
|
|
switch(ctrl)
|
|
|
|
{
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
|
|
service_event("STOP");
|
|
|
|
status.dwCurrentState = SERVICE_STOP_PENDING;
|
|
|
|
status.dwControlsAccepted = 0;
|
|
|
|
SetServiceStatus(service_handle, &status);
|
|
|
|
SetEvent(service_stop_event);
|
|
|
|
return NO_ERROR;
|
2016-08-10 10:42:35 +02:00
|
|
|
case 128:
|
2017-02-09 15:13:09 +01:00
|
|
|
test_winstation();
|
|
|
|
test_create_window();
|
2018-10-02 16:27:42 +02:00
|
|
|
test_monitors();
|
2016-08-10 10:42:35 +02:00
|
|
|
service_event("CUSTOM");
|
|
|
|
return 0xdeadbeef;
|
2012-06-15 15:35:19 +02:00
|
|
|
default:
|
|
|
|
status.dwCurrentState = SERVICE_RUNNING;
|
|
|
|
SetServiceStatus( service_handle, &status );
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WINAPI service_main(DWORD argc, char **argv)
|
|
|
|
{
|
|
|
|
SERVICE_STATUS status;
|
2016-09-01 07:39:21 +02:00
|
|
|
char buf[64];
|
2012-06-15 15:35:19 +02:00
|
|
|
BOOL res;
|
|
|
|
|
2016-08-31 08:45:26 +02:00
|
|
|
service_ok(argc == 3, "argc = %u, expected 3\n", argc);
|
|
|
|
service_ok(!strcmp(argv[0], service_name), "argv[0] = '%s', expected '%s'\n", argv[0], service_name);
|
|
|
|
service_ok(!strcmp(argv[1], "param1"), "argv[1] = '%s', expected 'param1'\n", argv[1]);
|
|
|
|
service_ok(!strcmp(argv[2], "param2"), "argv[2] = '%s', expected 'param2'\n", argv[2]);
|
2012-06-15 16:37:27 +02:00
|
|
|
|
2016-09-01 07:39:21 +02:00
|
|
|
buf[0] = 0;
|
|
|
|
GetEnvironmentVariableA("PATHEXT", buf, sizeof(buf));
|
|
|
|
service_ok(buf[0], "did not find PATHEXT environment variable\n");
|
|
|
|
|
2012-06-15 15:35:19 +02:00
|
|
|
service_handle = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL);
|
|
|
|
service_ok(service_handle != NULL, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
|
|
|
|
if(!service_handle)
|
|
|
|
return;
|
|
|
|
|
|
|
|
status.dwServiceType = SERVICE_WIN32;
|
|
|
|
status.dwCurrentState = SERVICE_RUNNING;
|
|
|
|
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
|
|
status.dwWin32ExitCode = 0;
|
|
|
|
status.dwServiceSpecificExitCode = 0;
|
|
|
|
status.dwCheckPoint = 0;
|
|
|
|
status.dwWaitHint = 10000;
|
|
|
|
res = SetServiceStatus(service_handle, &status);
|
2016-03-03 06:24:59 +01:00
|
|
|
service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %u\n", GetLastError());
|
2012-06-15 15:35:19 +02:00
|
|
|
|
|
|
|
service_event("RUNNING");
|
|
|
|
|
|
|
|
WaitForSingleObject(service_stop_event, INFINITE);
|
|
|
|
|
|
|
|
status.dwCurrentState = SERVICE_STOPPED;
|
|
|
|
status.dwControlsAccepted = 0;
|
|
|
|
res = SetServiceStatus(service_handle, &status);
|
2016-03-03 06:24:59 +01:00
|
|
|
service_ok(res, "SetServiceStatus(SERVICE_STOPPED) failed: %u\n", GetLastError());
|
2012-06-15 15:35:19 +02:00
|
|
|
}
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
static void service_process(void (WINAPI *p_service_main)(DWORD, char **))
|
2012-06-15 15:35:19 +02:00
|
|
|
{
|
|
|
|
BOOL res;
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
SERVICE_TABLE_ENTRYA servtbl[] = {
|
|
|
|
{service_name, p_service_main},
|
2012-06-15 15:35:19 +02:00
|
|
|
{NULL, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
res = WaitNamedPipeA(named_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
|
|
|
|
if(!res)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pipe_handle = CreateFileA(named_pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if(pipe_handle == INVALID_HANDLE_VALUE)
|
|
|
|
return;
|
|
|
|
|
2016-03-03 06:24:59 +01:00
|
|
|
service_trace("Starting...\n");
|
2012-06-15 15:35:19 +02:00
|
|
|
|
|
|
|
service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
|
|
|
|
service_ok(service_stop_event != NULL, "Could not create event: %u\n", GetLastError());
|
|
|
|
if(!service_stop_event)
|
|
|
|
return;
|
|
|
|
|
|
|
|
res = StartServiceCtrlDispatcherA(servtbl);
|
|
|
|
service_ok(res, "StartServiceCtrlDispatcher failed: %u\n", GetLastError());
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
/* Let service thread terminate */
|
|
|
|
Sleep(50);
|
|
|
|
|
2012-06-15 15:35:19 +02:00
|
|
|
CloseHandle(service_stop_event);
|
2012-10-22 15:00:47 +02:00
|
|
|
CloseHandle(pipe_handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
static DWORD WINAPI no_stop_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
|
|
|
|
{
|
|
|
|
SERVICE_STATUS status;
|
|
|
|
|
|
|
|
status.dwServiceType = SERVICE_WIN32;
|
|
|
|
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
|
|
|
status.dwWin32ExitCode = 0;
|
|
|
|
status.dwServiceSpecificExitCode = 0;
|
|
|
|
status.dwCheckPoint = 0;
|
|
|
|
status.dwWaitHint = 0;
|
|
|
|
|
|
|
|
switch(ctrl)
|
|
|
|
{
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
|
|
service_event("STOP");
|
|
|
|
status.dwCurrentState = SERVICE_STOPPED;
|
|
|
|
status.dwControlsAccepted = 0;
|
|
|
|
SetServiceStatus(service_handle, &status);
|
|
|
|
SetEvent(service_stop_event);
|
|
|
|
return NO_ERROR;
|
|
|
|
default:
|
|
|
|
status.dwCurrentState = SERVICE_RUNNING;
|
|
|
|
SetServiceStatus( service_handle, &status );
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WINAPI no_stop_main(DWORD argc, char **argv)
|
|
|
|
{
|
|
|
|
SERVICE_STATUS status;
|
|
|
|
BOOL res;
|
|
|
|
|
2016-08-31 08:45:26 +02:00
|
|
|
service_ok(argc == 1, "argc = %u, expected 1\n", argc);
|
|
|
|
service_ok(!strcmp(argv[0], service_name), "argv[0] = '%s', expected '%s'\n", argv[0], service_name);
|
2012-10-22 15:00:47 +02:00
|
|
|
|
|
|
|
service_handle = pRegisterServiceCtrlHandlerExA(service_name, no_stop_handler, NULL);
|
|
|
|
service_ok(service_handle != NULL, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
|
|
|
|
if(!service_handle)
|
|
|
|
return;
|
|
|
|
|
|
|
|
status.dwServiceType = SERVICE_WIN32;
|
|
|
|
status.dwCurrentState = SERVICE_RUNNING;
|
|
|
|
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
|
|
status.dwWin32ExitCode = 0;
|
|
|
|
status.dwServiceSpecificExitCode = 0;
|
|
|
|
status.dwCheckPoint = 0;
|
|
|
|
status.dwWaitHint = 10000;
|
|
|
|
res = SetServiceStatus(service_handle, &status);
|
2016-03-03 06:24:59 +01:00
|
|
|
service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %u\n", GetLastError());
|
2012-10-22 15:00:47 +02:00
|
|
|
|
|
|
|
service_event("RUNNING");
|
2012-06-15 15:35:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Test process global variables */
|
|
|
|
static SC_HANDLE scm_handle;
|
|
|
|
|
|
|
|
static char current_event[32];
|
|
|
|
static HANDLE event_handle = INVALID_HANDLE_VALUE;
|
|
|
|
static CRITICAL_SECTION event_cs;
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
static SC_HANDLE register_service(const char *test_name)
|
2012-06-15 15:35:19 +02:00
|
|
|
{
|
|
|
|
char service_cmd[MAX_PATH+150], *ptr;
|
|
|
|
SC_HANDLE service;
|
|
|
|
|
|
|
|
ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH);
|
|
|
|
|
|
|
|
/* If the file doesn't exist, assume we're using built-in exe and append .so to the path */
|
|
|
|
if(GetFileAttributesA(service_cmd) == INVALID_FILE_ATTRIBUTES) {
|
|
|
|
strcpy(ptr, ".so");
|
|
|
|
ptr += 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(ptr, " service ");
|
|
|
|
ptr += strlen(ptr);
|
2012-10-22 15:00:47 +02:00
|
|
|
sprintf(ptr, "%s ", test_name);
|
|
|
|
ptr += strlen(ptr);
|
2012-06-15 15:35:19 +02:00
|
|
|
strcpy(ptr, service_name);
|
|
|
|
|
|
|
|
trace("service_cmd \"%s\"\n", service_cmd);
|
|
|
|
|
|
|
|
service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL,
|
|
|
|
SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
|
|
|
|
service_cmd, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
if(!service && GetLastError() == ERROR_ACCESS_DENIED) {
|
|
|
|
skip("Not enough access right to create service\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ok(service != NULL, "CreateService failed: %u\n", GetLastError());
|
|
|
|
return service;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void expect_event(const char *event_name)
|
|
|
|
{
|
|
|
|
char evt[32];
|
|
|
|
DWORD res;
|
|
|
|
|
|
|
|
trace("waiting for %s\n", event_name);
|
|
|
|
|
|
|
|
res = WaitForSingleObject(event_handle, 30000);
|
|
|
|
ok(res == WAIT_OBJECT_0, "WaitForSingleObject failed: %u\n", res);
|
|
|
|
if(res != WAIT_OBJECT_0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
EnterCriticalSection(&event_cs);
|
|
|
|
strcpy(evt, current_event);
|
|
|
|
*current_event = 0;
|
|
|
|
LeaveCriticalSection(&event_cs);
|
|
|
|
|
|
|
|
ok(!strcmp(evt, event_name), "Unexpected event: %s, expected %s\n", evt, event_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static DWORD WINAPI pipe_thread(void *arg)
|
|
|
|
{
|
2016-08-31 08:45:26 +02:00
|
|
|
char buf[512], *ptr;
|
2012-06-15 15:35:19 +02:00
|
|
|
DWORD read;
|
|
|
|
BOOL res;
|
|
|
|
|
|
|
|
res = ConnectNamedPipe(pipe_handle, NULL);
|
|
|
|
ok(res || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed: %u\n", GetLastError());
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
res = ReadFile(pipe_handle, buf, sizeof(buf), &read, NULL);
|
|
|
|
if(!res) {
|
|
|
|
ok(GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_INVALID_HANDLE,
|
|
|
|
"ReadFile failed: %u\n", GetLastError());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(ptr = buf; ptr < buf+read; ptr += strlen(ptr)+1) {
|
|
|
|
if(!strncmp(ptr, "TRACE:", 6)) {
|
2016-03-03 06:24:59 +01:00
|
|
|
trace("service trace: %s", ptr+6);
|
2012-06-15 15:35:19 +02:00
|
|
|
}else if(!strncmp(ptr, "OK:", 3)) {
|
2016-03-03 06:24:59 +01:00
|
|
|
ok(1, "service: %s", ptr+3);
|
2012-10-22 15:00:47 +02:00
|
|
|
}else if(!strncmp(ptr, "FAIL:", 5)) {
|
2016-03-03 06:24:59 +01:00
|
|
|
ok(0, "service: %s", ptr+5);
|
2012-06-15 15:35:19 +02:00
|
|
|
}else if(!strncmp(ptr, "EVENT:", 6)) {
|
|
|
|
trace("service event: %s\n", ptr+6);
|
|
|
|
EnterCriticalSection(&event_cs);
|
|
|
|
ok(!current_event[0], "event %s still queued\n", current_event);
|
|
|
|
strcpy(current_event, ptr+6);
|
|
|
|
LeaveCriticalSection(&event_cs);
|
|
|
|
SetEvent(event_handle);
|
|
|
|
}else {
|
|
|
|
ok(0, "malformed service message: %s\n", ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DisconnectNamedPipe(pipe_handle);
|
|
|
|
trace("pipe disconnected\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
static void test_service(void)
|
2012-06-15 15:35:19 +02:00
|
|
|
{
|
2016-08-31 08:45:26 +02:00
|
|
|
static const char *argv[2] = {"param1", "param2"};
|
2012-10-22 15:00:47 +02:00
|
|
|
SC_HANDLE service_handle = register_service("simple_service");
|
2016-08-11 07:21:49 +02:00
|
|
|
SERVICE_STATUS_PROCESS status2;
|
2012-06-15 15:35:19 +02:00
|
|
|
SERVICE_STATUS status;
|
2016-08-11 07:21:49 +02:00
|
|
|
DWORD bytes;
|
2012-06-15 15:35:19 +02:00
|
|
|
BOOL res;
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
if(!service_handle)
|
2012-06-15 15:35:19 +02:00
|
|
|
return;
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
trace("starting...\n");
|
2016-08-31 08:45:26 +02:00
|
|
|
res = StartServiceA(service_handle, 2, argv);
|
2012-10-22 15:00:47 +02:00
|
|
|
ok(res, "StartService failed: %u\n", GetLastError());
|
|
|
|
if(!res) {
|
|
|
|
DeleteService(service_handle);
|
2013-05-26 15:31:51 +02:00
|
|
|
CloseServiceHandle(service_handle);
|
2012-06-15 15:35:19 +02:00
|
|
|
return;
|
2012-10-22 15:00:47 +02:00
|
|
|
}
|
|
|
|
expect_event("RUNNING");
|
2012-06-15 15:35:19 +02:00
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
res = QueryServiceStatus(service_handle, &status);
|
|
|
|
ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
|
2019-07-04 00:29:35 +02:00
|
|
|
ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
|
2012-10-22 15:00:47 +02:00
|
|
|
ok(status.dwCurrentState == SERVICE_RUNNING, "status.dwCurrentState = %x\n", status.dwCurrentState);
|
|
|
|
ok(status.dwControlsAccepted == (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN),
|
|
|
|
"status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
|
|
|
|
ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
|
|
|
|
ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
|
|
|
|
status.dwServiceSpecificExitCode);
|
|
|
|
ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
|
|
|
|
todo_wine ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
|
|
|
|
|
2016-08-11 07:21:49 +02:00
|
|
|
res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
|
|
|
|
ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
|
|
|
|
ok(status2.dwCurrentState == SERVICE_RUNNING, "status2.dwCurrentState = %x\n", status2.dwCurrentState);
|
|
|
|
ok(status2.dwProcessId != 0, "status2.dwProcessId = %d\n", status2.dwProcessId);
|
|
|
|
|
2016-08-10 10:42:35 +02:00
|
|
|
res = ControlService(service_handle, 128, &status);
|
|
|
|
ok(res, "ControlService failed: %u\n", GetLastError());
|
|
|
|
expect_event("CUSTOM");
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
res = ControlService(service_handle, SERVICE_CONTROL_STOP, &status);
|
|
|
|
ok(res, "ControlService failed: %u\n", GetLastError());
|
|
|
|
expect_event("STOP");
|
|
|
|
|
|
|
|
res = DeleteService(service_handle);
|
|
|
|
ok(res, "DeleteService failed: %u\n", GetLastError());
|
|
|
|
|
|
|
|
CloseServiceHandle(service_handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void test_no_stop(void)
|
|
|
|
{
|
|
|
|
SC_HANDLE service_handle = register_service("no_stop");
|
2016-08-11 07:21:49 +02:00
|
|
|
SERVICE_STATUS_PROCESS status2;
|
2012-10-22 15:00:47 +02:00
|
|
|
SERVICE_STATUS status;
|
2016-08-11 07:21:49 +02:00
|
|
|
DWORD bytes;
|
2012-10-22 15:00:47 +02:00
|
|
|
BOOL res;
|
2012-06-15 15:35:19 +02:00
|
|
|
|
|
|
|
if(!service_handle)
|
|
|
|
return;
|
|
|
|
|
|
|
|
trace("starting...\n");
|
|
|
|
res = StartServiceA(service_handle, 0, NULL);
|
|
|
|
ok(res, "StartService failed: %u\n", GetLastError());
|
2012-10-22 15:00:47 +02:00
|
|
|
if(!res) {
|
|
|
|
DeleteService(service_handle);
|
2013-05-26 15:31:51 +02:00
|
|
|
CloseServiceHandle(service_handle);
|
2012-06-15 15:35:19 +02:00
|
|
|
return;
|
2012-10-22 15:00:47 +02:00
|
|
|
}
|
2012-06-15 15:35:19 +02:00
|
|
|
expect_event("RUNNING");
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
/* Let service thread terminate */
|
|
|
|
Sleep(1000);
|
|
|
|
|
|
|
|
res = QueryServiceStatus(service_handle, &status);
|
|
|
|
ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
|
2019-07-04 00:29:35 +02:00
|
|
|
ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
|
2012-10-22 15:00:47 +02:00
|
|
|
ok(status.dwCurrentState == SERVICE_RUNNING, "status.dwCurrentState = %x\n", status.dwCurrentState);
|
|
|
|
ok(status.dwControlsAccepted == (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN),
|
|
|
|
"status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
|
|
|
|
ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
|
|
|
|
ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
|
|
|
|
status.dwServiceSpecificExitCode);
|
|
|
|
ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
|
|
|
|
todo_wine ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
|
|
|
|
|
2016-08-11 07:21:49 +02:00
|
|
|
res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
|
|
|
|
ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
|
|
|
|
ok(status2.dwCurrentState == SERVICE_RUNNING, "status2.dwCurrentState = %x\n", status2.dwCurrentState);
|
|
|
|
ok(status2.dwProcessId != 0, "status2.dwProcessId = %d\n", status2.dwProcessId);
|
|
|
|
|
2012-06-15 15:35:19 +02:00
|
|
|
res = ControlService(service_handle, SERVICE_CONTROL_STOP, &status);
|
|
|
|
ok(res, "ControlService failed: %u\n", GetLastError());
|
|
|
|
expect_event("STOP");
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
res = QueryServiceStatus(service_handle, &status);
|
|
|
|
ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
|
2019-07-04 00:29:35 +02:00
|
|
|
ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
|
2012-10-22 15:00:47 +02:00
|
|
|
ok(status.dwCurrentState==SERVICE_STOPPED || status.dwCurrentState==SERVICE_STOP_PENDING,
|
|
|
|
"status.dwCurrentState = %x\n", status.dwCurrentState);
|
|
|
|
ok(status.dwControlsAccepted == 0, "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
|
|
|
|
ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
|
|
|
|
ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
|
|
|
|
status.dwServiceSpecificExitCode);
|
|
|
|
ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
|
|
|
|
ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
|
|
|
|
|
2016-08-11 07:21:49 +02:00
|
|
|
res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
|
|
|
|
ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
|
|
|
|
ok(status2.dwProcessId == 0 || broken(status2.dwProcessId != 0),
|
|
|
|
"status2.dwProcessId = %d\n", status2.dwProcessId);
|
|
|
|
|
2012-06-15 15:35:19 +02:00
|
|
|
res = DeleteService(service_handle);
|
|
|
|
ok(res, "DeleteService failed: %u\n", GetLastError());
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
res = QueryServiceStatus(service_handle, &status);
|
|
|
|
ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
|
2019-07-04 00:29:35 +02:00
|
|
|
ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
|
2012-10-22 15:00:47 +02:00
|
|
|
ok(status.dwCurrentState==SERVICE_STOPPED || status.dwCurrentState==SERVICE_STOP_PENDING,
|
|
|
|
"status.dwCurrentState = %x\n", status.dwCurrentState);
|
|
|
|
ok(status.dwControlsAccepted == 0, "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
|
|
|
|
ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
|
|
|
|
ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
|
|
|
|
status.dwServiceSpecificExitCode);
|
|
|
|
ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
|
|
|
|
ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
|
|
|
|
|
2016-08-11 07:21:49 +02:00
|
|
|
res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
|
|
|
|
ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
|
|
|
|
ok(status2.dwProcessId == 0 || broken(status2.dwProcessId != 0),
|
|
|
|
"status2.dwProcessId = %d\n", status2.dwProcessId);
|
|
|
|
|
2012-06-15 15:35:19 +02:00
|
|
|
CloseServiceHandle(service_handle);
|
2012-10-22 15:00:47 +02:00
|
|
|
|
|
|
|
res = QueryServiceStatus(service_handle, &status);
|
|
|
|
ok(!res, "QueryServiceStatus should have failed\n");
|
|
|
|
ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError = %d\n", GetLastError());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_runner(void (*p_run_test)(void))
|
|
|
|
{
|
|
|
|
HANDLE thread;
|
|
|
|
|
|
|
|
sprintf(service_name, "WineTestService%d", GetTickCount());
|
|
|
|
trace("service_name: %s\n", service_name);
|
|
|
|
sprintf(named_pipe_name, "\\\\.\\pipe\\%s_pipe", service_name);
|
|
|
|
|
|
|
|
pipe_handle = CreateNamedPipeA(named_pipe_name, PIPE_ACCESS_INBOUND,
|
2018-10-05 15:49:21 +02:00
|
|
|
PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT, 10, 2048, 2048, 10000, NULL);
|
2012-10-22 15:00:47 +02:00
|
|
|
ok(pipe_handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %u\n", GetLastError());
|
|
|
|
if(pipe_handle == INVALID_HANDLE_VALUE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
event_handle = CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
|
|
ok(event_handle != INVALID_HANDLE_VALUE, "CreateEvent failed: %u\n", GetLastError());
|
|
|
|
if(event_handle == INVALID_HANDLE_VALUE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
thread = CreateThread(NULL, 0, pipe_thread, NULL, 0, NULL);
|
|
|
|
ok(thread != NULL, "CreateThread failed: %u\n", GetLastError());
|
|
|
|
if(!thread)
|
|
|
|
return;
|
|
|
|
|
|
|
|
p_run_test();
|
|
|
|
|
|
|
|
WaitForSingleObject(thread, INFINITE);
|
|
|
|
CloseHandle(event_handle);
|
|
|
|
CloseHandle(pipe_handle);
|
2018-01-11 16:47:04 +01:00
|
|
|
CloseHandle(thread);
|
2012-06-15 15:35:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
START_TEST(service)
|
|
|
|
{
|
|
|
|
char **argv;
|
|
|
|
int argc;
|
|
|
|
|
2016-03-03 06:25:16 +01:00
|
|
|
InitializeCriticalSection(&event_cs);
|
|
|
|
|
2012-06-15 15:35:19 +02:00
|
|
|
pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
|
|
|
|
if(!pRegisterServiceCtrlHandlerExA) {
|
|
|
|
win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scm_handle = OpenSCManagerA(NULL, NULL, GENERIC_ALL);
|
2014-04-08 11:20:27 +02:00
|
|
|
ok(scm_handle != NULL || GetLastError() == ERROR_ACCESS_DENIED, "OpenSCManager failed: %u\n", GetLastError());
|
|
|
|
if(!scm_handle) {
|
|
|
|
skip("OpenSCManager failed, skipping tests\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
argc = winetest_get_mainargs(&argv);
|
2012-06-15 15:35:19 +02:00
|
|
|
|
|
|
|
if(argc < 3) {
|
2012-10-22 15:00:47 +02:00
|
|
|
test_runner(test_service);
|
|
|
|
test_runner(test_no_stop);
|
2012-06-15 15:35:19 +02:00
|
|
|
}else {
|
2012-10-22 15:00:47 +02:00
|
|
|
strcpy(service_name, argv[3]);
|
2012-06-15 15:35:19 +02:00
|
|
|
sprintf(named_pipe_name, "\\\\.\\pipe\\%s_pipe", service_name);
|
|
|
|
|
2012-10-22 15:00:47 +02:00
|
|
|
if(!strcmp(argv[2], "simple_service"))
|
|
|
|
service_process(service_main);
|
|
|
|
else if(!strcmp(argv[2], "no_stop"))
|
|
|
|
service_process(no_stop_main);
|
2012-06-15 15:35:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CloseServiceHandle(scm_handle);
|
|
|
|
}
|