[PATCH] advapi32: Move service functions into sechost.

Zebediah Figura z.figura12 at gmail.com
Tue Apr 28 21:44:00 CDT 2020


Based on a patch by Micah N Gorrell.

Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
 dlls/advapi32/Makefile.in             |    1 -
 dlls/advapi32/advapi32.spec           |   70 +-
 dlls/advapi32/service.c               | 2370 +------------------------
 dlls/sechost/Makefile.in              |    5 +
 dlls/sechost/sechost.spec             |   70 +-
 dlls/sechost/service.c                | 1969 ++++++++++++++++++++
 dlls/{advapi32 => sechost}/svcctl.idl |    0
 7 files changed, 2089 insertions(+), 2396 deletions(-)
 create mode 100644 dlls/sechost/service.c
 rename dlls/{advapi32 => sechost}/svcctl.idl (100%)

diff --git a/dlls/advapi32/Makefile.in b/dlls/advapi32/Makefile.in
index 33dbe851c9f..e70b1a7a07a 100644
--- a/dlls/advapi32/Makefile.in
+++ b/dlls/advapi32/Makefile.in
@@ -19,6 +19,5 @@ C_SRCS = \
 	service.c \
 	wmi.c
 
-IDL_SRCS = svcctl.idl
 
 RC_SRCS = version.rc
diff --git a/dlls/advapi32/advapi32.spec b/dlls/advapi32/advapi32.spec
index cb3e856ce98..cd4dedac74d 100644
--- a/dlls/advapi32/advapi32.spec
+++ b/dlls/advapi32/advapi32.spec
@@ -89,10 +89,10 @@
 @ stdcall BuildTrusteeWithSidA(ptr ptr)
 @ stdcall BuildTrusteeWithSidW(ptr ptr)
 # @ stub CancelOverlappedAccess
-@ stdcall ChangeServiceConfig2A(long long ptr)
-@ stdcall ChangeServiceConfig2W(long long ptr)
-@ stdcall ChangeServiceConfigA(long long long long wstr str ptr str str str str)
-@ stdcall ChangeServiceConfigW(long long long long wstr wstr ptr wstr wstr wstr wstr)
+@ stdcall -import ChangeServiceConfig2A(long long ptr)
+@ stdcall -import ChangeServiceConfig2W(long long ptr)
+@ stdcall -import ChangeServiceConfigA(long long long long wstr str ptr str str str str)
+@ stdcall -import ChangeServiceConfigW(long long long long wstr wstr ptr wstr wstr wstr wstr)
 # @ stub CheckForHiberboot
 @ stdcall -import CheckTokenMembership(long ptr ptr)
 @ stdcall ClearEventLogA (long str)
@@ -100,12 +100,12 @@
 # @ stub CloseCodeAuthzLevel
 @ stdcall CloseEncryptedFileRaw(ptr)
 @ stdcall CloseEventLog (long)
-@ stdcall CloseServiceHandle(long)
+@ stdcall -import CloseServiceHandle(long)
 # @ stub CloseThreadWaitChainSession
 @ stdcall -import CloseTrace(int64)
 @ stdcall CommandLineFromMsiDescriptor(wstr ptr ptr)
 # @ stub ComputeAccessTokenFromCodeAuthzLevel
-@ stdcall ControlService(long long ptr)
+@ stdcall -import ControlService(long long ptr)
 # @ stub ControlServiceExA
 # @ stub ControlServiceExW
 @ stdcall -import ControlTraceA(int64 str ptr long)
@@ -143,8 +143,8 @@
 @ stdcall CreateProcessWithLogonW(wstr wstr wstr long wstr wstr long ptr wstr ptr ptr)
 @ stdcall CreateProcessWithTokenW(long long wstr wstr long ptr wstr ptr ptr)
 @ stdcall -import CreateRestrictedToken(long long long ptr long ptr long ptr ptr)
-@ stdcall CreateServiceA(long str str long long long long str str ptr str str str)
-@ stdcall CreateServiceW(long wstr wstr long long long long wstr wstr ptr wstr wstr wstr)
+@ stdcall -import CreateServiceA(long str str long long long long str str ptr str str str)
+@ stdcall -import CreateServiceW(long wstr wstr long long long long wstr wstr ptr wstr wstr wstr)
 # @ stub CreateTraceInstanceId
 @ stdcall -import CreateWellKnownSid(long ptr ptr ptr)
 # @ stub CredBackupCredentials
@@ -235,7 +235,7 @@
 @ stdcall DecryptFileA(str long)
 @ stdcall DecryptFileW(wstr long)
 @ stdcall -import DeleteAce(ptr long)
-@ stdcall DeleteService(long)
+@ stdcall -import DeleteService(long)
 @ stdcall DeregisterEventSource(long)
 @ stdcall -import DestroyPrivateObjectSecurity(ptr)
 # @ stub DuplicateEncryptionInfoFile
@@ -271,13 +271,13 @@
 # @ stub EncryptedFileKeyInfo
 # @ stub EncryptionDisable
 @ stdcall EnumDependentServicesA(long long ptr long ptr ptr)
-@ stdcall EnumDependentServicesW(long long ptr long ptr ptr)
+@ stdcall -import EnumDependentServicesW(long long ptr long ptr ptr)
 @ stdcall -import EnumDynamicTimeZoneInformation(long ptr)
 @ stub EnumServiceGroupA
 @ stub EnumServiceGroupW
 @ stdcall EnumServicesStatusA (long long long ptr long ptr ptr ptr)
 @ stdcall EnumServicesStatusExA(long long long long ptr long ptr ptr ptr str)
-@ stdcall EnumServicesStatusExW(long long long long ptr long ptr ptr ptr wstr)
+@ stdcall -import EnumServicesStatusExW(long long long long ptr long ptr ptr ptr wstr)
 @ stdcall EnumServicesStatusW (long long long ptr long ptr ptr ptr)
 @ stdcall EnumerateTraceGuids(ptr long ptr)
 # @ stub EnumerateTraceGuidsEx
@@ -361,9 +361,9 @@
 @ stdcall GetSecurityInfoExA (long long long str str ptr ptr ptr ptr)
 @ stdcall GetSecurityInfoExW (long long long wstr wstr ptr ptr ptr ptr)
 @ stdcall GetServiceDisplayNameA(ptr str ptr ptr)
-@ stdcall GetServiceDisplayNameW(ptr wstr ptr ptr)
+@ stdcall -import GetServiceDisplayNameW(ptr wstr ptr ptr)
 @ stdcall GetServiceKeyNameA(long str ptr ptr)
-@ stdcall GetServiceKeyNameW(long wstr ptr ptr)
+@ stdcall -import GetServiceKeyNameW(long wstr ptr ptr)
 @ stdcall -import GetSidIdentifierAuthority(ptr)
 @ stdcall -import GetSidLengthRequired(long)
 @ stdcall -import GetSidSubAuthority(ptr long)
@@ -526,7 +526,7 @@
 @ stdcall NotifyChangeEventLog (long long)
 # @ stub NotifyServiceStatusChange
 # @ stub NotifyServiceStatusChangeA
-@ stdcall NotifyServiceStatusChangeW(ptr long ptr)
+@ stdcall -import NotifyServiceStatusChangeW(ptr long ptr)
 # @ stub NpGetUserName
 @ stdcall ObjectCloseAuditAlarmA(str ptr long)
 @ stdcall -import ObjectCloseAuditAlarmW(wstr ptr long)
@@ -543,10 +543,10 @@
 @ stdcall OpenEventLogA (str str)
 @ stdcall OpenEventLogW (wstr wstr)
 @ stdcall -import OpenProcessToken(long long ptr)
-@ stdcall OpenSCManagerA(str str long)
-@ stdcall OpenSCManagerW(wstr wstr long)
-@ stdcall OpenServiceA(long str long)
-@ stdcall OpenServiceW(long wstr long)
+@ stdcall -import OpenSCManagerA(str str long)
+@ stdcall -import OpenSCManagerW(wstr wstr long)
+@ stdcall -import OpenServiceA(long str long)
+@ stdcall -import OpenServiceW(long wstr long)
 @ stdcall -import OpenThreadToken(long long long ptr)
 # @ stub OpenThreadWaitChainSession
 @ stdcall -ret64 OpenTraceA(ptr)
@@ -593,16 +593,16 @@
 # @ stub QueryLocalUserServiceName
 # @ stub QueryRecoveryAgentsOnEncryptedFile
 # @ stub QuerySecurityAccessMask
-@ stdcall QueryServiceConfig2A(long long ptr long ptr)
-@ stdcall QueryServiceConfig2W(long long ptr long ptr)
-@ stdcall QueryServiceConfigA(long ptr long ptr)
-@ stdcall QueryServiceConfigW(long ptr long ptr)
+@ stdcall -import QueryServiceConfig2A(long long ptr long ptr)
+@ stdcall -import QueryServiceConfig2W(long long ptr long ptr)
+@ stdcall -import QueryServiceConfigA(long ptr long ptr)
+@ stdcall -import QueryServiceConfigW(long ptr long ptr)
 # @ stub QueryServiceDynamicInformation
 @ stdcall QueryServiceLockStatusA(long ptr long ptr)
 @ stdcall QueryServiceLockStatusW(long ptr long ptr)
-@ stdcall QueryServiceObjectSecurity(long long ptr long ptr)
-@ stdcall QueryServiceStatus(long ptr)
-@ stdcall QueryServiceStatusEx (long long ptr long ptr)
+@ stdcall -import QueryServiceObjectSecurity(long long ptr long ptr)
+@ stdcall -import QueryServiceStatus(long ptr)
+@ stdcall -import QueryServiceStatusEx (long long ptr long ptr)
 # @ stub QueryTraceA
 @ stdcall QueryTraceW(int64 wstr ptr)
 # @ stub QueryUserServiceName
@@ -697,10 +697,10 @@
 @ stdcall RegisterEventSourceA(str str)
 @ stdcall RegisterEventSourceW(wstr wstr)
 # @ stub RegisterIdleTask
-@ stdcall RegisterServiceCtrlHandlerA(str ptr)
-@ stdcall RegisterServiceCtrlHandlerExA(str ptr ptr)
-@ stdcall RegisterServiceCtrlHandlerExW(wstr ptr ptr)
-@ stdcall RegisterServiceCtrlHandlerW(wstr ptr)
+@ stdcall -import RegisterServiceCtrlHandlerA(str ptr)
+@ stdcall -import RegisterServiceCtrlHandlerExA(str ptr ptr)
+@ stdcall -import RegisterServiceCtrlHandlerExW(wstr ptr ptr)
+@ stdcall -import RegisterServiceCtrlHandlerW(wstr ptr)
 @ stdcall RegisterTraceGuidsA(ptr ptr ptr long ptr str str ptr) ntdll.EtwRegisterTraceGuidsA
 @ stdcall RegisterTraceGuidsW(ptr ptr ptr long ptr wstr wstr ptr) ntdll.EtwRegisterTraceGuidsW
 @ stdcall RegisterWaitChainCOMCallback(ptr ptr)
@@ -761,17 +761,17 @@
 # @ stub SetSecurityInfoExA
 # @ stub SetSecurityInfoExW
 @ stdcall SetServiceBits(long long long long)
-@ stdcall SetServiceObjectSecurity(long long ptr)
-@ stdcall SetServiceStatus(long ptr)
+@ stdcall -import SetServiceObjectSecurity(long long ptr)
+@ stdcall -import SetServiceStatus(long ptr)
 @ stdcall -import SetThreadToken(ptr ptr)
 @ stdcall -import SetTokenInformation(long long ptr long)
 # @ stub SetTraceCallback
 # @ stub SetUserFileEncryptionKey
 # @ stub SetUserFileEncryptionKeyEx
-@ stdcall StartServiceA(long long ptr)
-@ stdcall StartServiceCtrlDispatcherA(ptr)
-@ stdcall StartServiceCtrlDispatcherW(ptr)
-@ stdcall StartServiceW(long long ptr)
+@ stdcall -import StartServiceA(long long ptr)
+@ stdcall -import StartServiceCtrlDispatcherA(ptr)
+@ stdcall -import StartServiceCtrlDispatcherW(ptr)
+@ stdcall -import StartServiceW(long long ptr)
 @ stdcall -import StartTraceA(ptr str ptr)
 @ stdcall -import StartTraceW(ptr wstr ptr)
 @ stdcall StopTraceA(int64 str ptr)
diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index eb195d84d05..2146ddfb186 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -20,1661 +20,34 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#include "config.h"
-#include "wine/port.h"
-
-#include <stdarg.h>
-#include <string.h>
-#include <time.h>
-#include <assert.h>
-
-#define NONAMELESSUNION
-
-#include "ntstatus.h"
-#define WIN32_NO_STATUS
-#include "windef.h"
-#include "winbase.h"
-#include "winsvc.h"
-#include "winerror.h"
-#include "winreg.h"
-#include "wine/unicode.h"
-#include "wine/debug.h"
-#include "winternl.h"
-#include "lmcons.h"
-#include "lmserver.h"
-
-#include "svcctl.h"
-
-#include "advapi32_misc.h"
-
-#include "wine/exception.h"
-#include "wine/list.h"
-
-WINE_DEFAULT_DEBUG_CHANNEL(service);
-
-void  __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
-{
-    return heap_alloc(len);
-}
-
-void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
-{
-    heap_free(ptr);
-}
-
-typedef struct service_data_t
-{
-    LPHANDLER_FUNCTION_EX handler;
-    LPVOID context;
-    HANDLE thread;
-    SC_HANDLE handle;
-    SC_HANDLE full_access_handle;
-    BOOL unicode : 1;
-    union {
-        LPSERVICE_MAIN_FUNCTIONA a;
-        LPSERVICE_MAIN_FUNCTIONW w;
-    } proc;
-    LPWSTR args;
-    WCHAR name[1];
-} service_data;
-
-typedef struct dispatcher_data_t
-{
-    SC_HANDLE manager;
-    HANDLE pipe;
-} dispatcher_data;
-
-typedef struct notify_data_t {
-    SC_HANDLE service;
-    SC_RPC_NOTIFY_PARAMS params;
-    SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2 cparams;
-    SC_NOTIFY_RPC_HANDLE notify_handle;
-    SERVICE_NOTIFYW *notify_buffer;
-    HANDLE calling_thread, ready_evt;
-    struct list entry;
-} notify_data;
-
-static struct list notify_list = LIST_INIT(notify_list);
-
-static CRITICAL_SECTION service_cs;
-static CRITICAL_SECTION_DEBUG service_cs_debug =
-{
-    0, 0, &service_cs,
-    { &service_cs_debug.ProcessLocksList, 
-      &service_cs_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": service_cs") }
-};
-static CRITICAL_SECTION service_cs = { &service_cs_debug, -1, 0, 0, 0, 0 };
-
-static service_data **services;
-static unsigned int nb_services;
-static HANDLE service_event;
-static BOOL stop_service;
-
-extern HANDLE CDECL __wine_make_process_system(void);
-
-static inline LPWSTR SERV_dupmulti(LPCSTR str)
-{
-    UINT len = 0, n = 0;
-    LPWSTR wstr;
-
-    if( !str )
-        return NULL;
-    do {
-        len += MultiByteToWideChar( CP_ACP, 0, &str[n], -1, NULL, 0 );
-        n += (strlen( &str[n] ) + 1);
-    } while (str[n]);
-    len++;
-    n++;
-
-    wstr = heap_alloc( len*sizeof (WCHAR) );
-    MultiByteToWideChar( CP_ACP, 0, str, n, wstr, len );
-    return wstr;
-}
-
-static inline DWORD multisz_cb(LPCWSTR wmultisz)
-{
-    const WCHAR *wptr = wmultisz;
-
-    if (wmultisz == NULL)
-        return 0;
-
-    while (*wptr)
-        wptr += lstrlenW(wptr)+1;
-    return (wptr - wmultisz + 1)*sizeof(WCHAR);
-}
-
-/******************************************************************************
- * RPC connection with services.exe
- */
-static handle_t rpc_wstr_bind(RPC_WSTR str)
-{
-    WCHAR transport[] = SVCCTL_TRANSPORT;
-    WCHAR endpoint[] = SVCCTL_ENDPOINT;
-    RPC_WSTR binding_str;
-    RPC_STATUS status;
-    handle_t rpc_handle;
-
-    status = RpcStringBindingComposeW(NULL, transport, str, endpoint, NULL, &binding_str);
-    if (status != RPC_S_OK)
-    {
-        ERR("RpcStringBindingComposeW failed (%d)\n", (DWORD)status);
-        return NULL;
-    }
-
-    status = RpcBindingFromStringBindingW(binding_str, &rpc_handle);
-    RpcStringFreeW(&binding_str);
-
-    if (status != RPC_S_OK)
-    {
-        ERR("Couldn't connect to services.exe: error code %u\n", (DWORD)status);
-        return NULL;
-    }
-
-    return rpc_handle;
-}
-
-static handle_t rpc_cstr_bind(RPC_CSTR str)
-{
-    RPC_CSTR transport = (RPC_CSTR)SVCCTL_TRANSPORTA;
-    RPC_CSTR endpoint = (RPC_CSTR)SVCCTL_ENDPOINTA;
-    RPC_CSTR binding_str;
-    RPC_STATUS status;
-    handle_t rpc_handle;
-
-    status = RpcStringBindingComposeA(NULL, transport, str, endpoint, NULL, &binding_str);
-    if (status != RPC_S_OK)
-    {
-        ERR("RpcStringBindingComposeW failed (%d)\n", (DWORD)status);
-        return NULL;
-    }
-
-    status = RpcBindingFromStringBindingA(binding_str, &rpc_handle);
-    RpcStringFreeA(&binding_str);
-
-    if (status != RPC_S_OK)
-    {
-        ERR("Couldn't connect to services.exe: error code %u\n", (DWORD)status);
-        return NULL;
-    }
-
-    return rpc_handle;
-}
-
-DECLSPEC_HIDDEN handle_t __RPC_USER MACHINE_HANDLEA_bind(MACHINE_HANDLEA MachineName)
-{
-    return rpc_cstr_bind((RPC_CSTR)MachineName);
-}
-
-DECLSPEC_HIDDEN void __RPC_USER MACHINE_HANDLEA_unbind(MACHINE_HANDLEA MachineName, handle_t h)
-{
-    RpcBindingFree(&h);
-}
-
-DECLSPEC_HIDDEN handle_t __RPC_USER MACHINE_HANDLEW_bind(MACHINE_HANDLEW MachineName)
-{
-    return rpc_wstr_bind((RPC_WSTR)MachineName);
-}
-
-DECLSPEC_HIDDEN void __RPC_USER MACHINE_HANDLEW_unbind(MACHINE_HANDLEW MachineName, handle_t h)
-{
-    RpcBindingFree(&h);
-}
-
-DECLSPEC_HIDDEN handle_t __RPC_USER SVCCTL_HANDLEW_bind(SVCCTL_HANDLEW MachineName)
-{
-    return rpc_wstr_bind((RPC_WSTR)MachineName);
-}
-
-DECLSPEC_HIDDEN void __RPC_USER SVCCTL_HANDLEW_unbind(SVCCTL_HANDLEW MachineName, handle_t h)
-{
-    RpcBindingFree(&h);
-}
-
-static LONG WINAPI rpc_filter(EXCEPTION_POINTERS *eptr)
-{
-    return I_RpcExceptionFilter(eptr->ExceptionRecord->ExceptionCode);
-}
-
-static DWORD map_exception_code(DWORD exception_code)
-{
-    switch (exception_code)
-    {
-    case RPC_X_NULL_REF_POINTER:
-        return ERROR_INVALID_ADDRESS;
-    case RPC_X_ENUM_VALUE_OUT_OF_RANGE:
-    case RPC_X_BYTE_COUNT_TOO_SMALL:
-        return ERROR_INVALID_PARAMETER;
-    case RPC_S_INVALID_BINDING:
-    case RPC_X_SS_IN_NULL_CONTEXT:
-        return ERROR_INVALID_HANDLE;
-    default:
-        return exception_code;
-    }
-}
-
-/******************************************************************************
- * Service IPC functions
- */
-static LPWSTR service_get_pipe_name(void)
-{
-    static const WCHAR format[] = { '\\','\\','.','\\','p','i','p','e','\\',
-        'n','e','t','\\','N','t','C','o','n','t','r','o','l','P','i','p','e','%','u',0};
-    static const WCHAR service_current_key_str[] = { 'S','Y','S','T','E','M','\\',
-        'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
-        'C','o','n','t','r','o','l','\\',
-        'S','e','r','v','i','c','e','C','u','r','r','e','n','t',0};
-    LPWSTR name;
-    DWORD len;
-    HKEY service_current_key;
-    DWORD service_current;
-    LONG ret;
-    DWORD type;
-
-    ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, service_current_key_str, 0,
-        KEY_QUERY_VALUE, &service_current_key);
-    if (ret != ERROR_SUCCESS)
-        return NULL;
-    len = sizeof(service_current);
-    ret = RegQueryValueExW(service_current_key, NULL, NULL, &type,
-        (BYTE *)&service_current, &len);
-    RegCloseKey(service_current_key);
-    if (ret != ERROR_SUCCESS || type != REG_DWORD)
-        return NULL;
-    len = ARRAY_SIZE(format) + 10 /* strlenW("4294967295") */;
-    name = heap_alloc(len * sizeof(WCHAR));
-    if (!name)
-        return NULL;
-    snprintfW(name, len, format, service_current);
-    return name;
-}
-
-static HANDLE service_open_pipe(void)
-{
-    LPWSTR szPipe = service_get_pipe_name();
-    HANDLE handle = INVALID_HANDLE_VALUE;
-
-    do {
-        handle = CreateFileW(szPipe, GENERIC_READ|GENERIC_WRITE,
-                         0, NULL, OPEN_ALWAYS, 0, NULL);
-        if (handle != INVALID_HANDLE_VALUE)
-            break;
-        if (GetLastError() != ERROR_PIPE_BUSY)
-            break;
-    } while (WaitNamedPipeW(szPipe, NMPWAIT_USE_DEFAULT_WAIT));
-    heap_free(szPipe);
-
-    return handle;
-}
-
-static service_data *find_service_by_name( const WCHAR *name )
-{
-    unsigned int i;
-
-    if (nb_services == 1)  /* only one service (FIXME: should depend on OWN_PROCESS etc.) */
-        return services[0];
-    for (i = 0; i < nb_services; i++)
-        if (!strcmpiW( name, services[i]->name )) return services[i];
-    return NULL;
-}
-
-/******************************************************************************
- * service_thread
- *
- * Call into the main service routine provided by StartServiceCtrlDispatcher.
- */
-static DWORD WINAPI service_thread(LPVOID arg)
-{
-    service_data *info = arg;
-    LPWSTR str = info->args;
-    DWORD argc = 0, len = 0;
-
-    TRACE("%p\n", arg);
-
-    while (str[len])
-    {
-        len += strlenW(&str[len]) + 1;
-        argc++;
-    }
-    len++;
-
-    if (info->unicode)
-    {
-        LPWSTR *argv, p;
-
-        argv = heap_alloc((argc+1)*sizeof(LPWSTR));
-        for (argc=0, p=str; *p; p += strlenW(p) + 1)
-            argv[argc++] = p;
-        argv[argc] = NULL;
-
-        info->proc.w(argc, argv);
-        heap_free(argv);
-    }
-    else
-    {
-        LPSTR strA, *argv, p;
-        DWORD lenA;
-        
-        lenA = WideCharToMultiByte(CP_ACP,0, str, len, NULL, 0, NULL, NULL);
-        strA = heap_alloc(lenA);
-        WideCharToMultiByte(CP_ACP,0, str, len, strA, lenA, NULL, NULL);
-
-        argv = heap_alloc((argc+1)*sizeof(LPSTR));
-        for (argc=0, p=strA; *p; p += strlen(p) + 1)
-            argv[argc++] = p;
-        argv[argc] = NULL;
-
-        info->proc.a(argc, argv);
-        heap_free(argv);
-        heap_free(strA);
-    }
-    return 0;
-}
-
-/******************************************************************************
- * service_handle_start
- */
-static DWORD service_handle_start(service_data *service, const void *data, DWORD data_size)
-{
-    DWORD count = data_size / sizeof(WCHAR);
-
-    if (service->thread)
-    {
-        WARN("service is not stopped\n");
-        return ERROR_SERVICE_ALREADY_RUNNING;
-    }
-
-    heap_free(service->args);
-    service->args = heap_alloc((count + 2) * sizeof(WCHAR));
-    if (count) memcpy( service->args, data, count * sizeof(WCHAR) );
-    service->args[count++] = 0;
-    service->args[count++] = 0;
-
-    service->thread = CreateThread( NULL, 0, service_thread,
-                                    service, 0, NULL );
-    SetEvent( service_event );  /* notify the main loop */
-    return 0;
-}
-
-/******************************************************************************
- * service_handle_control
- */
-static DWORD service_handle_control(service_data *service, DWORD control, const void *data, DWORD data_size)
-{
-    DWORD ret = ERROR_INVALID_SERVICE_CONTROL;
-
-    TRACE("%s control %u data %p data_size %u\n", debugstr_w(service->name), control, data, data_size);
-
-    if (control == SERVICE_CONTROL_START)
-        ret = service_handle_start(service, data, data_size);
-    else if (service->handler)
-        ret = service->handler(control, 0, (void *)data, service->context);
-    return ret;
-}
-
-/******************************************************************************
- * service_control_dispatcher
- */
-static DWORD WINAPI service_control_dispatcher(LPVOID arg)
-{
-    dispatcher_data *disp = arg;
-
-    /* dispatcher loop */
-    while (1)
-    {
-        service_data *service;
-        service_start_info info;
-        BYTE *data = NULL;
-        WCHAR *name;
-        BOOL r;
-        DWORD data_size = 0, count, result;
-
-        r = ReadFile( disp->pipe, &info, FIELD_OFFSET(service_start_info,data), &count, NULL );
-        if (!r)
-        {
-            if (GetLastError() != ERROR_BROKEN_PIPE)
-                ERR( "pipe read failed error %u\n", GetLastError() );
-            break;
-        }
-        if (count != FIELD_OFFSET(service_start_info,data))
-        {
-            ERR( "partial pipe read %u\n", count );
-            break;
-        }
-        if (count < info.total_size)
-        {
-            data_size = info.total_size - FIELD_OFFSET(service_start_info,data);
-            data = heap_alloc( data_size );
-            r = ReadFile( disp->pipe, data, data_size, &count, NULL );
-            if (!r)
-            {
-                if (GetLastError() != ERROR_BROKEN_PIPE)
-                    ERR( "pipe read failed error %u\n", GetLastError() );
-                heap_free( data );
-                break;
-            }
-            if (count != data_size)
-            {
-                ERR( "partial pipe read %u/%u\n", count, data_size );
-                heap_free( data );
-                break;
-            }
-        }
-
-        EnterCriticalSection( &service_cs );
-
-        /* validate service name */
-        name = (WCHAR *)data;
-        if (!info.name_size || data_size < info.name_size * sizeof(WCHAR) || name[info.name_size - 1])
-        {
-            ERR( "got request without valid service name\n" );
-            result = ERROR_INVALID_PARAMETER;
-            goto done;
-        }
-
-        if (info.magic != SERVICE_PROTOCOL_MAGIC)
-        {
-            ERR( "received invalid request for service %s\n", debugstr_w(name) );
-            result = ERROR_INVALID_PARAMETER;
-            goto done;
-        }
-
-        /* find the service */
-        if (!(service = find_service_by_name( name )))
-        {
-            FIXME( "got request for unknown service %s\n", debugstr_w(name) );
-            result = ERROR_INVALID_PARAMETER;
-            goto done;
-        }
-
-        if (!service->handle)
-        {
-            if (!(service->handle = OpenServiceW( disp->manager, name, SERVICE_SET_STATUS )) ||
-                !(service->full_access_handle = OpenServiceW( disp->manager, name,
-                        GENERIC_READ|GENERIC_WRITE )))
-                FIXME( "failed to open service %s\n", debugstr_w(name) );
-        }
-
-        data_size -= info.name_size * sizeof(WCHAR);
-        result = service_handle_control(service, info.control, data_size ?
-                                        &data[info.name_size * sizeof(WCHAR)] : NULL, data_size);
-
-    done:
-        LeaveCriticalSection( &service_cs );
-        WriteFile( disp->pipe, &result, sizeof(result), &count, NULL );
-        heap_free( data );
-    }
-
-    CloseHandle( disp->pipe );
-    CloseServiceHandle( disp->manager );
-    heap_free( disp );
-    return 1;
-}
-
-/* wait for services which accept this type of message to become STOPPED */
-static void handle_shutdown_msg(DWORD msg, DWORD accept)
-{
-    SERVICE_STATUS st;
-    SERVICE_PRESHUTDOWN_INFO spi;
-    DWORD i, n = 0, sz, timeout = 2000;
-    ULONGLONG stop_time;
-    BOOL res, done = TRUE;
-    SC_HANDLE *wait_handles = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SC_HANDLE) * nb_services );
-
-    EnterCriticalSection( &service_cs );
-    for (i = 0; i < nb_services; i++)
-    {
-        res = QueryServiceStatus( services[i]->full_access_handle, &st );
-        if (!res || st.dwCurrentState == SERVICE_STOPPED || !(st.dwControlsAccepted & accept))
-            continue;
-
-        done = FALSE;
-
-        if (accept == SERVICE_ACCEPT_PRESHUTDOWN)
-        {
-            res = QueryServiceConfig2W( services[i]->full_access_handle, SERVICE_CONFIG_PRESHUTDOWN_INFO,
-                    (LPBYTE)&spi, sizeof(spi), &sz );
-            if (res)
-            {
-                FIXME( "service should be able to delay shutdown\n" );
-                timeout = max( spi.dwPreshutdownTimeout, timeout );
-            }
-        }
-
-        service_handle_control( services[i], msg, NULL, 0 );
-        wait_handles[n++] = services[i]->full_access_handle;
-    }
-    LeaveCriticalSection( &service_cs );
-
-    /* FIXME: these timeouts should be more generous, but we can't currently delay prefix shutdown */
-    timeout = min( timeout, 3000 );
-    stop_time = GetTickCount64() + timeout;
-
-    while (!done && GetTickCount64() < stop_time)
-    {
-        done = TRUE;
-        for (i = 0; i < n; i++)
-        {
-            res = QueryServiceStatus( wait_handles[i], &st );
-            if (!res || st.dwCurrentState == SERVICE_STOPPED)
-                continue;
-
-            done = FALSE;
-            Sleep( 100 );
-            break;
-        }
-    }
-
-    HeapFree( GetProcessHeap(), 0, wait_handles );
-}
-
-/******************************************************************************
- * service_run_main_thread
- */
-static BOOL service_run_main_thread(void)
-{
-    DWORD i, n, ret;
-    HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
-    UINT wait_services[MAXIMUM_WAIT_OBJECTS];
-    dispatcher_data *disp = heap_alloc( sizeof(*disp) );
-
-    disp->manager = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
-    if (!disp->manager)
-    {
-        ERR("failed to open service manager error %u\n", GetLastError());
-        heap_free( disp );
-        return FALSE;
-    }
-
-    disp->pipe = service_open_pipe();
-    if (disp->pipe == INVALID_HANDLE_VALUE)
-    {
-        WARN("failed to create control pipe error %u\n", GetLastError());
-        CloseServiceHandle( disp->manager );
-        heap_free( disp );
-        SetLastError( ERROR_FAILED_SERVICE_CONTROLLER_CONNECT );
-        return FALSE;
-    }
-
-    service_event = CreateEventW( NULL, FALSE, FALSE, NULL );
-    stop_service  = FALSE;
-
-    /* FIXME: service_control_dispatcher should be merged into the main thread */
-    wait_handles[0] = __wine_make_process_system();
-    wait_handles[1] = CreateThread( NULL, 0, service_control_dispatcher, disp, 0, NULL );
-    wait_handles[2] = service_event;
-
-    TRACE("Starting %d services running as process %d\n",
-          nb_services, GetCurrentProcessId());
-
-    /* wait for all the threads to pack up and exit */
-    while (!stop_service)
-    {
-        EnterCriticalSection( &service_cs );
-        for (i = 0, n = 3; i < nb_services && n < MAXIMUM_WAIT_OBJECTS; i++)
-        {
-            if (!services[i]->thread) continue;
-            wait_services[n] = i;
-            wait_handles[n++] = services[i]->thread;
-        }
-        LeaveCriticalSection( &service_cs );
-
-        ret = WaitForMultipleObjects( n, wait_handles, FALSE, INFINITE );
-        if (!ret)  /* system process event */
-        {
-            handle_shutdown_msg(SERVICE_CONTROL_PRESHUTDOWN, SERVICE_ACCEPT_PRESHUTDOWN);
-            handle_shutdown_msg(SERVICE_CONTROL_SHUTDOWN, SERVICE_ACCEPT_SHUTDOWN);
-            ExitProcess(0);
-        }
-        else if (ret == 1)
-        {
-            TRACE( "control dispatcher exited, shutting down\n" );
-            /* FIXME: we should maybe send a shutdown control to running services */
-            ExitProcess(0);
-        }
-        else if (ret == 2)
-        {
-            continue;  /* rebuild the list */
-        }
-        else if (ret < n)
-        {
-            i = wait_services[ret];
-            EnterCriticalSection( &service_cs );
-            CloseHandle( services[i]->thread );
-            services[i]->thread = NULL;
-            LeaveCriticalSection( &service_cs );
-        }
-        else return FALSE;
-    }
-
-    return TRUE;
-}
-
-/******************************************************************************
- * StartServiceCtrlDispatcherA [ADVAPI32.@]
- *
- * See StartServiceCtrlDispatcherW.
- */
-BOOL WINAPI StartServiceCtrlDispatcherA( const SERVICE_TABLE_ENTRYA *servent )
-{
-    service_data *info;
-    unsigned int i;
-
-    TRACE("%p\n", servent);
-
-    if (nb_services)
-    {
-        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
-        return FALSE;
-    }
-    while (servent[nb_services].lpServiceName) nb_services++;
-    if (!nb_services)
-    {
-        SetLastError( ERROR_INVALID_PARAMETER );
-        return FALSE;
-    }
-
-    services = heap_alloc( nb_services * sizeof(*services) );
-
-    for (i = 0; i < nb_services; i++)
-    {
-        DWORD len = MultiByteToWideChar(CP_ACP, 0, servent[i].lpServiceName, -1, NULL, 0);
-        DWORD sz = FIELD_OFFSET( service_data, name[len] );
-        info = heap_alloc_zero( sz );
-        MultiByteToWideChar(CP_ACP, 0, servent[i].lpServiceName, -1, info->name, len);
-        info->proc.a = servent[i].lpServiceProc;
-        info->unicode = FALSE;
-        services[i] = info;
-    }
-
-    return service_run_main_thread();
-}
-
-/******************************************************************************
- * StartServiceCtrlDispatcherW [ADVAPI32.@]
- *
- *  Connects a process containing one or more services to the service control
- * manager.
- *
- * PARAMS
- *   servent [I]  A list of the service names and service procedures
- *
- * RETURNS
- *  Success: TRUE.
- *  Failure: FALSE.
- */
-BOOL WINAPI StartServiceCtrlDispatcherW( const SERVICE_TABLE_ENTRYW *servent )
-{
-    service_data *info;
-    unsigned int i;
-
-    TRACE("%p\n", servent);
-
-    if (nb_services)
-    {
-        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
-        return FALSE;
-    }
-    while (servent[nb_services].lpServiceName) nb_services++;
-    if (!nb_services)
-    {
-        SetLastError( ERROR_INVALID_PARAMETER );
-        return FALSE;
-    }
-
-    services = heap_alloc( nb_services * sizeof(*services) );
-
-    for (i = 0; i < nb_services; i++)
-    {
-        DWORD len = strlenW(servent[i].lpServiceName) + 1;
-        DWORD sz = FIELD_OFFSET( service_data, name[len] );
-        info = heap_alloc_zero( sz );
-        strcpyW(info->name, servent[i].lpServiceName);
-        info->proc.w = servent[i].lpServiceProc;
-        info->unicode = TRUE;
-        services[i] = info;
-    }
-
-    return service_run_main_thread();
-}
-
-/******************************************************************************
- * LockServiceDatabase  [ADVAPI32.@]
- */
-SC_LOCK WINAPI LockServiceDatabase( SC_HANDLE manager )
-{
-    /* this function is a no-op in Vista and above */
-    TRACE("%p\n", manager);
-    return (SC_LOCK)0xdeadbeef;
-}
-
-/******************************************************************************
- * UnlockServiceDatabase  [ADVAPI32.@]
- */
-BOOL WINAPI UnlockServiceDatabase( SC_LOCK lock )
-{
-    /* this function is a no-op in Vista and above */
-    TRACE("%p\n", lock);
-    return TRUE;
-}
-
-/******************************************************************************
- * SetServiceStatus [ADVAPI32.@]
- *
- * PARAMS
- *   hService []
- *   lpStatus []
- */
-BOOL WINAPI
-SetServiceStatus( SERVICE_STATUS_HANDLE hService, LPSERVICE_STATUS lpStatus )
-{
-    DWORD err;
-
-    TRACE("%p %x %x %x %x %x %x %x\n", hService,
-          lpStatus->dwServiceType, lpStatus->dwCurrentState,
-          lpStatus->dwControlsAccepted, lpStatus->dwWin32ExitCode,
-          lpStatus->dwServiceSpecificExitCode, lpStatus->dwCheckPoint,
-          lpStatus->dwWaitHint);
-
-    __TRY
-    {
-        err = svcctl_SetServiceStatus( hService, lpStatus );
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-    if (err != ERROR_SUCCESS)
-    {
-        SetLastError(err);
-        return FALSE;
-    }
-
-    if (lpStatus->dwCurrentState == SERVICE_STOPPED)
-    {
-        unsigned int i, count = 0;
-        EnterCriticalSection( &service_cs );
-        for (i = 0; i < nb_services; i++)
-        {
-            if (services[i]->handle == (SC_HANDLE)hService) continue;
-            if (services[i]->thread) count++;
-        }
-        if (!count)
-        {
-            stop_service = TRUE;
-            SetEvent( service_event );  /* notify the main loop */
-        }
-        LeaveCriticalSection( &service_cs );
-    }
-
-    return TRUE;
-}
-
-
-/******************************************************************************
- * OpenSCManagerA [ADVAPI32.@]
- *
- * Establish a connection to the service control manager and open its database.
- *
- * PARAMS
- *   lpMachineName   [I] Pointer to machine name string
- *   lpDatabaseName  [I] Pointer to database name string
- *   dwDesiredAccess [I] Type of access
- *
- * RETURNS
- *   Success: A Handle to the service control manager database
- *   Failure: NULL
- */
-SC_HANDLE WINAPI OpenSCManagerA( LPCSTR lpMachineName, LPCSTR lpDatabaseName,
-                                 DWORD dwDesiredAccess )
-{
-    LPWSTR machineW, databaseW;
-    SC_HANDLE ret;
-
-    machineW = strdupAW(lpMachineName);
-    databaseW = strdupAW(lpDatabaseName);
-    ret = OpenSCManagerW(machineW, databaseW, dwDesiredAccess);
-    heap_free(databaseW);
-    heap_free(machineW);
-    return ret;
-}
-
-/******************************************************************************
- * OpenSCManagerW [ADVAPI32.@]
- *
- * See OpenSCManagerA.
- */
-static DWORD SERV_OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName,
-                           DWORD dwDesiredAccess, SC_HANDLE *handle )
-{
-    DWORD r;
-
-    TRACE("(%s,%s,0x%08x)\n", debugstr_w(lpMachineName),
-          debugstr_w(lpDatabaseName), dwDesiredAccess);
-
-    __TRY
-    {
-        r = svcctl_OpenSCManagerW(lpMachineName, lpDatabaseName, dwDesiredAccess, (SC_RPC_HANDLE *)handle);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        r = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    if (r!=ERROR_SUCCESS)
-        *handle = 0;
-
-    TRACE("returning %p\n", *handle);
-    return r;
-}
-
-SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName,
-                                 DWORD dwDesiredAccess )
-{
-    SC_HANDLE handle = 0;
-    DWORD r;
-
-    r = SERV_OpenSCManagerW(lpMachineName, lpDatabaseName, dwDesiredAccess, &handle);
-    if (r!=ERROR_SUCCESS)
-        SetLastError(r);
-    return handle;
-}
-
-/******************************************************************************
- * ControlService [ADVAPI32.@]
- *
- * Send a control code to a service.
- *
- * PARAMS
- *   hService        [I] Handle of the service control manager database
- *   dwControl       [I] Control code to send (SERVICE_CONTROL_* flags from "winsvc.h")
- *   lpServiceStatus [O] Destination for the status of the service, if available
- *
- * RETURNS
- *   Success: TRUE.
- *   Failure: FALSE.
- *
- * BUGS
- *   Unlike M$' implementation, control requests are not serialized and may be
- *   processed asynchronously.
- */
-BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl,
-                            LPSERVICE_STATUS lpServiceStatus )
-{
-    DWORD err;
-
-    TRACE("%p %d %p\n", hService, dwControl, lpServiceStatus);
-
-    __TRY
-    {
-        err = svcctl_ControlService(hService, dwControl, lpServiceStatus);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-    if (err != ERROR_SUCCESS)
-    {
-        SetLastError(err);
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-/******************************************************************************
- * CloseServiceHandle [ADVAPI32.@]
- * 
- * Close a handle to a service or the service control manager database.
- *
- * PARAMS
- *   hSCObject [I] Handle to service or service control manager database
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- */
-BOOL WINAPI
-CloseServiceHandle( SC_HANDLE hSCObject )
-{
-    DWORD err;
-
-    TRACE("%p\n", hSCObject);
-
-    __TRY
-    {
-        err = svcctl_CloseServiceHandle((SC_RPC_HANDLE *)&hSCObject);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    if (err != ERROR_SUCCESS)
-    {
-        SetLastError(err);
-        return FALSE;
-    }
-    return TRUE;
-}
-
-
-/******************************************************************************
- * OpenServiceA [ADVAPI32.@]
- *
- * Open a handle to a service.
- *
- * PARAMS
- *   hSCManager      [I] Handle of the service control manager database
- *   lpServiceName   [I] Name of the service to open
- *   dwDesiredAccess [I] Access required to the service
- *
- * RETURNS
- *    Success: Handle to the service
- *    Failure: NULL
- */
-SC_HANDLE WINAPI OpenServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
-                               DWORD dwDesiredAccess )
-{
-    LPWSTR lpServiceNameW;
-    SC_HANDLE ret;
-
-    TRACE("%p %s 0x%08x\n", hSCManager, debugstr_a(lpServiceName), dwDesiredAccess);
-
-    lpServiceNameW = strdupAW(lpServiceName);
-    ret = OpenServiceW( hSCManager, lpServiceNameW, dwDesiredAccess);
-    heap_free(lpServiceNameW);
-    return ret;
-}
-
-
-/******************************************************************************
- * OpenServiceW [ADVAPI32.@]
- *
- * See OpenServiceA.
- */
-static DWORD SERV_OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
-                         DWORD dwDesiredAccess, SC_HANDLE *handle )
-{
-    DWORD err;
-
-    TRACE("%p %s 0x%08x\n", hSCManager, debugstr_w(lpServiceName), dwDesiredAccess);
-
-    if (!hSCManager)
-        return ERROR_INVALID_HANDLE;
-
-    __TRY
-    {
-        err = svcctl_OpenServiceW(hSCManager, lpServiceName, dwDesiredAccess, (SC_RPC_HANDLE *)handle);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    if (err != ERROR_SUCCESS)
-        *handle = 0;
-
-    TRACE("returning %p\n", *handle);
-    return err;
-}
-
-SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
-                               DWORD dwDesiredAccess)
-{
-    SC_HANDLE handle = 0;
-    DWORD err;
-
-    err = SERV_OpenServiceW(hSCManager, lpServiceName, dwDesiredAccess, &handle);
-    if (err != ERROR_SUCCESS)
-        SetLastError(err);
-    return handle;
-}
-
-/******************************************************************************
- * CreateServiceW [ADVAPI32.@]
- */
-SC_HANDLE WINAPI
-CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
-                  LPCWSTR lpDisplayName, DWORD dwDesiredAccess,
-                  DWORD dwServiceType, DWORD dwStartType,
-                  DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
-                  LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
-                  LPCWSTR lpDependencies, LPCWSTR lpServiceStartName,
-                  LPCWSTR lpPassword )
-{
-    SC_HANDLE handle = 0;
-    DWORD err;
-    SIZE_T passwdlen;
-
-    TRACE("%p %s %s\n", hSCManager, 
-          debugstr_w(lpServiceName), debugstr_w(lpDisplayName));
-
-    if (!hSCManager)
-    {
-        SetLastError( ERROR_INVALID_HANDLE );
-        return 0;
-    }
-
-    if (lpPassword)
-        passwdlen = (strlenW(lpPassword) + 1) * sizeof(WCHAR);
-    else
-        passwdlen = 0;
-
-    __TRY
-    {
-        BOOL is_wow64;
-
-        IsWow64Process(GetCurrentProcess(), &is_wow64);
-
-        if (is_wow64)
-            err = svcctl_CreateServiceWOW64W(hSCManager, lpServiceName,
-                    lpDisplayName, dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
-                    lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, (const BYTE*)lpDependencies,
-                    multisz_cb(lpDependencies), lpServiceStartName, (const BYTE*)lpPassword, passwdlen,
-                    (SC_RPC_HANDLE *)&handle);
-        else
-            err = svcctl_CreateServiceW(hSCManager, lpServiceName,
-                    lpDisplayName, dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
-                    lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, (const BYTE*)lpDependencies,
-                    multisz_cb(lpDependencies), lpServiceStartName, (const BYTE*)lpPassword, passwdlen,
-                    (SC_RPC_HANDLE *)&handle);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    if (err != ERROR_SUCCESS)
-    {
-        SetLastError(err);
-        handle = 0;
-    }
-    return handle;
-}
-
-
-/******************************************************************************
- * CreateServiceA [ADVAPI32.@]
- */
-SC_HANDLE WINAPI
-CreateServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
-                  LPCSTR lpDisplayName, DWORD dwDesiredAccess,
-                  DWORD dwServiceType, DWORD dwStartType,
-                  DWORD dwErrorControl, LPCSTR lpBinaryPathName,
-                  LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
-                  LPCSTR lpDependencies, LPCSTR lpServiceStartName,
-                  LPCSTR lpPassword )
-{
-    LPWSTR lpServiceNameW, lpDisplayNameW, lpBinaryPathNameW,
-        lpLoadOrderGroupW, lpDependenciesW, lpServiceStartNameW, lpPasswordW;
-    SC_HANDLE r;
-
-    TRACE("%p %s %s\n", hSCManager,
-          debugstr_a(lpServiceName), debugstr_a(lpDisplayName));
-
-    lpServiceNameW = strdupAW( lpServiceName );
-    lpDisplayNameW = strdupAW( lpDisplayName );
-    lpBinaryPathNameW = strdupAW( lpBinaryPathName );
-    lpLoadOrderGroupW = strdupAW( lpLoadOrderGroup );
-    lpDependenciesW = SERV_dupmulti( lpDependencies );
-    lpServiceStartNameW = strdupAW( lpServiceStartName );
-    lpPasswordW = strdupAW( lpPassword );
-
-    r = CreateServiceW( hSCManager, lpServiceNameW, lpDisplayNameW,
-            dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
-            lpBinaryPathNameW, lpLoadOrderGroupW, lpdwTagId,
-            lpDependenciesW, lpServiceStartNameW, lpPasswordW );
-
-    heap_free( lpServiceNameW );
-    heap_free( lpDisplayNameW );
-    heap_free( lpBinaryPathNameW );
-    heap_free( lpLoadOrderGroupW );
-    heap_free( lpDependenciesW );
-    heap_free( lpServiceStartNameW );
-    heap_free( lpPasswordW );
-
-    return r;
-}
-
-
-/******************************************************************************
- * DeleteService [ADVAPI32.@]
- *
- * Delete a service from the service control manager database.
- *
- * PARAMS
- *    hService [I] Handle of the service to delete
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE
- */
-BOOL WINAPI DeleteService( SC_HANDLE hService )
-{
-    DWORD err;
-
-    TRACE("%p\n", hService);
-
-    __TRY
-    {
-        err = svcctl_DeleteService(hService);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-    if (err != 0)
-    {
-        SetLastError(err);
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-
-/******************************************************************************
- * StartServiceA [ADVAPI32.@]
- *
- * Start a service
- *
- * PARAMS
- *   hService            [I] Handle of service
- *   dwNumServiceArgs    [I] Number of arguments
- *   lpServiceArgVectors [I] Address of array of argument strings
- *
- * NOTES
- *  - NT implements this function using an obscure RPC call.
- *  - You might need to do a "setenv SystemRoot \\WINNT" in your .cshrc
- *    to get things like "%SystemRoot%\\System32\\service.exe" to load.
- *  - This will only work for shared address space. How should the service
- *    args be transferred when address spaces are separated?
- *  - Can only start one service at a time.
- *  - Has no concept of privilege.
- *
- * RETURNS
- *   Success: TRUE.
- *   Failure: FALSE
- */
-BOOL WINAPI StartServiceA( SC_HANDLE hService, DWORD dwNumServiceArgs,
-                           LPCSTR *lpServiceArgVectors )
-{
-    LPWSTR *lpwstr=NULL;
-    unsigned int i;
-    BOOL r;
-
-    TRACE("(%p,%d,%p)\n",hService,dwNumServiceArgs,lpServiceArgVectors);
-
-    if (dwNumServiceArgs)
-        lpwstr = heap_alloc( dwNumServiceArgs*sizeof(LPWSTR) );
-
-    for(i=0; i<dwNumServiceArgs; i++)
-        lpwstr[i]=strdupAW(lpServiceArgVectors[i]);
-
-    r = StartServiceW(hService, dwNumServiceArgs, (LPCWSTR *)lpwstr);
-
-    if (dwNumServiceArgs)
-    {
-        for(i=0; i<dwNumServiceArgs; i++)
-            heap_free(lpwstr[i]);
-        heap_free(lpwstr);
-    }
-
-    return r;
-}
-
-
-/******************************************************************************
- * StartServiceW [ADVAPI32.@]
- * 
- * See StartServiceA.
- */
-BOOL WINAPI StartServiceW(SC_HANDLE hService, DWORD dwNumServiceArgs,
-                          LPCWSTR *lpServiceArgVectors)
-{
-    DWORD err;
-
-    TRACE("%p %d %p\n", hService, dwNumServiceArgs, lpServiceArgVectors);
-
-    __TRY
-    {
-        err = svcctl_StartServiceW(hService, dwNumServiceArgs, lpServiceArgVectors);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-    if (err != ERROR_SUCCESS)
-    {
-        SetLastError(err);
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-/******************************************************************************
- * QueryServiceStatus [ADVAPI32.@]
- *
- * PARAMS
- *   hService        [I] Handle to service to get information about
- *   lpservicestatus [O] buffer to receive the status information for the service
- *
- */
-BOOL WINAPI QueryServiceStatus(SC_HANDLE hService,
-                               LPSERVICE_STATUS lpservicestatus)
-{
-    SERVICE_STATUS_PROCESS SvcStatusData;
-    BOOL ret;
-    DWORD dummy;
-
-    TRACE("%p %p\n", hService, lpservicestatus);
-
-    if (!hService)
-    {
-        SetLastError(ERROR_INVALID_HANDLE);
-        return FALSE;
-    }
-    if (!lpservicestatus)
-    {
-        SetLastError(ERROR_INVALID_ADDRESS);
-        return FALSE;
-    }
-
-    ret = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&SvcStatusData,
-                                sizeof(SERVICE_STATUS_PROCESS), &dummy);
-    if (ret) memcpy(lpservicestatus, &SvcStatusData, sizeof(SERVICE_STATUS)) ;
-    return ret;
-}
-
-
-/******************************************************************************
- * QueryServiceStatusEx [ADVAPI32.@]
- *
- * Get information about a service.
- *
- * PARAMS
- *   hService       [I] Handle to service to get information about
- *   InfoLevel      [I] Level of information to get
- *   lpBuffer       [O] Destination for requested information
- *   cbBufSize      [I] Size of lpBuffer in bytes
- *   pcbBytesNeeded [O] Destination for number of bytes needed, if cbBufSize is too small
- *
- * RETURNS
- *  Success: TRUE
- *  FAILURE: FALSE
- */
-BOOL WINAPI QueryServiceStatusEx(SC_HANDLE hService, SC_STATUS_TYPE InfoLevel,
-                        LPBYTE lpBuffer, DWORD cbBufSize,
-                        LPDWORD pcbBytesNeeded)
-{
-    DWORD err;
-
-    TRACE("%p %d %p %d %p\n", hService, InfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded);
-
-    if (InfoLevel != SC_STATUS_PROCESS_INFO)
-    {
-        err = ERROR_INVALID_LEVEL;
-    }
-    else if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
-    {
-        *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
-        err = ERROR_INSUFFICIENT_BUFFER;
-    }
-    else
-    {
-        __TRY
-        {
-            err = svcctl_QueryServiceStatusEx(hService, InfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded);
-        }
-        __EXCEPT(rpc_filter)
-        {
-            err = map_exception_code(GetExceptionCode());
-        }
-        __ENDTRY
-    }
-    if (err != ERROR_SUCCESS)
-    {
-        SetLastError(err);
-        return FALSE;
-    }
-    return TRUE;
-}
-
-/******************************************************************************
- * QueryServiceConfigA [ADVAPI32.@]
- */
-BOOL WINAPI QueryServiceConfigA( SC_HANDLE hService, LPQUERY_SERVICE_CONFIGA config,
-                                 DWORD size, LPDWORD needed )
-{
-    DWORD n;
-    LPSTR p, buffer;
-    BOOL ret;
-    QUERY_SERVICE_CONFIGW *configW;
-
-    TRACE("%p %p %d %p\n", hService, config, size, needed);
-
-    if (!(buffer = heap_alloc( 2 * size )))
-    {
-        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
-        return FALSE;
-    }
-    configW = (QUERY_SERVICE_CONFIGW *)buffer;
-    ret = QueryServiceConfigW( hService, configW, 2 * size, needed );
-    if (!ret) goto done;
-
-    config->dwServiceType      = configW->dwServiceType;
-    config->dwStartType        = configW->dwStartType;
-    config->dwErrorControl     = configW->dwErrorControl;
-    config->lpBinaryPathName   = NULL;
-    config->lpLoadOrderGroup   = NULL;
-    config->dwTagId            = configW->dwTagId;
-    config->lpDependencies     = NULL;
-    config->lpServiceStartName = NULL;
-    config->lpDisplayName      = NULL;
-
-    p = (LPSTR)(config + 1);
-    n = size - sizeof(*config);
-    ret = FALSE;
-
-#define MAP_STR(str) \
-    do { \
-        if (configW->str) \
-        { \
-            DWORD sz = WideCharToMultiByte( CP_ACP, 0, configW->str, -1, p, n, NULL, NULL ); \
-            if (!sz) goto done; \
-            config->str = p; \
-            p += sz; \
-            n -= sz; \
-        } \
-    } while (0)
-
-    MAP_STR( lpBinaryPathName );
-    MAP_STR( lpLoadOrderGroup );
-    MAP_STR( lpDependencies );
-    MAP_STR( lpServiceStartName );
-    MAP_STR( lpDisplayName );
-#undef MAP_STR
-
-    *needed = p - (LPSTR)config;
-    ret = TRUE;
-
-done:
-    heap_free( buffer );
-    return ret;
-}
-
-static DWORD move_string_to_buffer(BYTE **buf, LPWSTR *string_ptr)
-{
-    DWORD cb;
-
-    if (!*string_ptr)
-    {
-        cb = sizeof(WCHAR);
-        memset(*buf, 0, cb);
-    }
-    else
-    {
-        cb = (strlenW(*string_ptr) + 1)*sizeof(WCHAR);
-        memcpy(*buf, *string_ptr, cb);
-        MIDL_user_free(*string_ptr);
-    }
-
-    *string_ptr = (LPWSTR)*buf;
-    *buf += cb;
-
-    return cb;
-}
-
-static DWORD size_string(LPCWSTR string)
-{
-    return (string ? (strlenW(string) + 1)*sizeof(WCHAR) : sizeof(WCHAR));
-}
-
-/******************************************************************************
- * QueryServiceConfigW [ADVAPI32.@]
- */
-BOOL WINAPI 
-QueryServiceConfigW( SC_HANDLE hService,
-                     LPQUERY_SERVICE_CONFIGW lpServiceConfig,
-                     DWORD cbBufSize, LPDWORD pcbBytesNeeded)
-{
-    QUERY_SERVICE_CONFIGW config;
-    DWORD total;
-    DWORD err;
-    BYTE *bufpos;
-
-    TRACE("%p %p %d %p\n", hService, lpServiceConfig,
-           cbBufSize, pcbBytesNeeded);
-
-    memset(&config, 0, sizeof(config));
-
-    __TRY
-    {
-        err = svcctl_QueryServiceConfigW(hService, &config, cbBufSize, pcbBytesNeeded);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    if (err != ERROR_SUCCESS)
-    {
-        TRACE("services.exe: error %u\n", err);
-        SetLastError(err);
-        return FALSE;
-    }
-
-    /* calculate the size required first */
-    total = sizeof (QUERY_SERVICE_CONFIGW);
-    total += size_string(config.lpBinaryPathName);
-    total += size_string(config.lpLoadOrderGroup);
-    total += size_string(config.lpDependencies);
-    total += size_string(config.lpServiceStartName);
-    total += size_string(config.lpDisplayName);
-
-    *pcbBytesNeeded = total;
-
-    /* if there's not enough memory, return an error */
-    if( total > cbBufSize )
-    {
-        SetLastError( ERROR_INSUFFICIENT_BUFFER );
-        MIDL_user_free(config.lpBinaryPathName);
-        MIDL_user_free(config.lpLoadOrderGroup);
-        MIDL_user_free(config.lpDependencies);
-        MIDL_user_free(config.lpServiceStartName);
-        MIDL_user_free(config.lpDisplayName);
-        return FALSE;
-    }
-
-    *lpServiceConfig = config;
-    bufpos = ((BYTE *)lpServiceConfig) + sizeof(QUERY_SERVICE_CONFIGW);
-    move_string_to_buffer(&bufpos, &lpServiceConfig->lpBinaryPathName);
-    move_string_to_buffer(&bufpos, &lpServiceConfig->lpLoadOrderGroup);
-    move_string_to_buffer(&bufpos, &lpServiceConfig->lpDependencies);
-    move_string_to_buffer(&bufpos, &lpServiceConfig->lpServiceStartName);
-    move_string_to_buffer(&bufpos, &lpServiceConfig->lpDisplayName);
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winsvc.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
 
-    TRACE("Image path           = %s\n", debugstr_w(lpServiceConfig->lpBinaryPathName) );
-    TRACE("Group                = %s\n", debugstr_w(lpServiceConfig->lpLoadOrderGroup) );
-    TRACE("Dependencies         = %s\n", debugstr_w(lpServiceConfig->lpDependencies) );
-    TRACE("Service account name = %s\n", debugstr_w(lpServiceConfig->lpServiceStartName) );
-    TRACE("Display name         = %s\n", debugstr_w(lpServiceConfig->lpDisplayName) );
+#include "advapi32_misc.h"
 
-    return TRUE;
-}
+WINE_DEFAULT_DEBUG_CHANNEL(service);
 
 /******************************************************************************
- * QueryServiceConfig2A [ADVAPI32.@]
- *
- * Note
- *   observed under win2k:
- *   The functions QueryServiceConfig2A and QueryServiceConfig2W return the same
- *   required buffer size (in byte) at least for dwLevel SERVICE_CONFIG_DESCRIPTION
+ * LockServiceDatabase  [ADVAPI32.@]
  */
-BOOL WINAPI QueryServiceConfig2A(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffer,
-                                 DWORD size, LPDWORD needed)
+SC_LOCK WINAPI LockServiceDatabase( SC_HANDLE manager )
 {
-    BOOL ret;
-    LPBYTE bufferW = NULL;
-
-    TRACE("%p %u %p %u %p\n", hService, dwLevel, buffer, size, needed);
-
-    if(buffer && size)
-        bufferW = heap_alloc(size);
-
-    ret = QueryServiceConfig2W(hService, dwLevel, bufferW, size, needed);
-    if(!ret) goto cleanup;
-
-    switch(dwLevel) {
-        case SERVICE_CONFIG_DESCRIPTION:
-            if (buffer && bufferW) {
-                LPSERVICE_DESCRIPTIONA configA = (LPSERVICE_DESCRIPTIONA) buffer;
-                LPSERVICE_DESCRIPTIONW configW = (LPSERVICE_DESCRIPTIONW) bufferW;
-                if (configW->lpDescription && (size > sizeof(SERVICE_DESCRIPTIONA))) {
-                    DWORD sz;
-                    configA->lpDescription = (LPSTR)(configA + 1);
-                    sz = WideCharToMultiByte( CP_ACP, 0, configW->lpDescription, -1,
-                             configA->lpDescription, size - sizeof(SERVICE_DESCRIPTIONA), NULL, NULL );
-                    if (!sz) {
-                        FIXME("WideCharToMultiByte failed for configW->lpDescription\n");
-                        ret = FALSE;
-                        configA->lpDescription = NULL;
-                    }
-                }
-                else configA->lpDescription = NULL;
-            }
-            break;
-        case SERVICE_CONFIG_PRESHUTDOWN_INFO:
-            if (buffer && bufferW && *needed<=size)
-                memcpy(buffer, bufferW, *needed);
-            break;
-        default:
-            FIXME("conversation W->A not implemented for level %d\n", dwLevel);
-            ret = FALSE;
-            break;
-    }
-
-cleanup:
-    heap_free( bufferW);
-    return ret;
+    /* this function is a no-op in Vista and above */
+    TRACE("%p\n", manager);
+    return (SC_LOCK)0xdeadbeef;
 }
 
 /******************************************************************************
- * QueryServiceConfig2W [ADVAPI32.@]
- *
- * See QueryServiceConfig2A.
+ * UnlockServiceDatabase  [ADVAPI32.@]
  */
-BOOL WINAPI QueryServiceConfig2W(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffer,
-                                 DWORD size, LPDWORD needed)
+BOOL WINAPI UnlockServiceDatabase( SC_LOCK lock )
 {
-    BYTE *bufptr;
-    DWORD err;
-
-    TRACE("%p %u %p %u %p\n", hService, dwLevel, buffer, size, needed);
-
-    if (!buffer && size)
-    {
-        SetLastError(ERROR_INVALID_ADDRESS);
-        return FALSE;
-    }
-
-    switch (dwLevel)
-    {
-    case SERVICE_CONFIG_DESCRIPTION:
-        if (!(bufptr = heap_alloc( size )))
-        {
-            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
-            return FALSE;
-        }
-        break;
-
-    case SERVICE_CONFIG_PRESHUTDOWN_INFO:
-        bufptr = buffer;
-        break;
-
-    default:
-        FIXME("Level %d not implemented\n", dwLevel);
-        SetLastError(ERROR_INVALID_LEVEL);
-        return FALSE;
-    }
-
-    if (!needed)
-    {
-        if (dwLevel == SERVICE_CONFIG_DESCRIPTION) heap_free(bufptr);
-        SetLastError(ERROR_INVALID_ADDRESS);
-        return FALSE;
-    }
-
-    __TRY
-    {
-        err = svcctl_QueryServiceConfig2W(hService, dwLevel, bufptr, size, needed);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    switch (dwLevel)
-    {
-    case SERVICE_CONFIG_DESCRIPTION:
-    {
-        SERVICE_DESCRIPTIONW *desc = (SERVICE_DESCRIPTIONW *)buffer;
-        struct service_description *s = (struct service_description *)bufptr;
-
-        if (err != ERROR_SUCCESS && err != ERROR_INSUFFICIENT_BUFFER)
-        {
-            heap_free( bufptr );
-            SetLastError( err );
-            return FALSE;
-        }
-
-        /* adjust for potentially larger SERVICE_DESCRIPTIONW structure */
-        if (*needed == sizeof(*s)) *needed = sizeof(*desc);
-        else
-            *needed = *needed - FIELD_OFFSET(struct service_description, description) + sizeof(*desc);
-
-        if (size < *needed)
-        {
-            heap_free( bufptr );
-            SetLastError( ERROR_INSUFFICIENT_BUFFER );
-            return FALSE;
-        }
-        if (desc)
-        {
-            if (!s->size) desc->lpDescription = NULL;
-            else
-            {
-                desc->lpDescription = (WCHAR *)(desc + 1);
-                memcpy( desc->lpDescription, s->description, s->size );
-            }
-        }
-        heap_free( bufptr );
-        break;
-    }
-    case SERVICE_CONFIG_PRESHUTDOWN_INFO:
-        if (err != ERROR_SUCCESS)
-        {
-            SetLastError( err );
-            return FALSE;
-        }
-        break;
-
-    default:
-        break;
-    }
-
+    /* this function is a no-op in Vista and above */
+    TRACE("%p\n", lock);
     return TRUE;
 }
 
@@ -1871,158 +244,41 @@ EnumServicesStatusExA( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD st
             heap_free( servicesW );
             return FALSE;
         }
-        MultiByteToWideChar( CP_ACP, 0, group, -1, groupW, len * sizeof(WCHAR) );
-    }
-
-    ret = EnumServicesStatusExW( hmngr, level, type, state, (BYTE *)servicesW, sz,
-                                 needed, returned, resume_handle, groupW );
-    if (!ret) goto done;
-
-    p = (char *)services + *returned * sizeof(ENUM_SERVICE_STATUS_PROCESSA);
-    n = size - (p - (char *)services);
-    ret = FALSE;
-    for (i = 0; i < *returned; i++)
-    {
-        sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpServiceName, -1, p, n, NULL, NULL );
-        if (!sz) goto done;
-        services[i].lpServiceName = p;
-        p += sz;
-        n -= sz;
-        if (servicesW[i].lpDisplayName)
-        {
-            sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpDisplayName, -1, p, n, NULL, NULL );
-            if (!sz) goto done;
-            services[i].lpDisplayName = p;
-            p += sz;
-            n -= sz;
-        }
-        else services[i].lpDisplayName = NULL;
-        services[i].ServiceStatusProcess = servicesW[i].ServiceStatusProcess;
-    }
-
-    ret = TRUE;
-
-done:
-    heap_free( servicesW );
-    heap_free( groupW );
-    return ret;
-}
-
-/******************************************************************************
- * EnumServicesStatusExW [ADVAPI32.@]
- */
-BOOL WINAPI
-EnumServicesStatusExW( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD state,
-                       LPBYTE buffer, DWORD size, LPDWORD needed, LPDWORD returned,
-                       LPDWORD resume_handle, LPCWSTR group )
-{
-    DWORD err, i, offset, buflen, count, total_size = 0;
-    ENUM_SERVICE_STATUS_PROCESSW *services = (ENUM_SERVICE_STATUS_PROCESSW *)buffer;
-    struct enum_service_status_process *entry;
-    const WCHAR *str;
-    BYTE *buf;
-
-    TRACE("%p %u 0x%x 0x%x %p %u %p %p %p %s\n", hmngr, level, type, state, buffer,
-          size, needed, returned, resume_handle, debugstr_w(group));
-
-    if (level != SC_ENUM_PROCESS_INFO)
-    {
-        SetLastError( ERROR_INVALID_LEVEL );
-        return FALSE;
-    }
-    if (!hmngr)
-    {
-        SetLastError( ERROR_INVALID_HANDLE );
-        return FALSE;
-    }
-    if (!needed || !returned)
-    {
-        SetLastError( ERROR_INVALID_ADDRESS );
-        return FALSE;
-    }
-
-    /* make sure we pass a valid pointer */
-    buflen = max( size, sizeof(*services) );
-    if (!(buf = heap_alloc( buflen )))
-    {
-        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
-        return FALSE;
-    }
-
-    __TRY
-    {
-        err = svcctl_EnumServicesStatusExW( hmngr, SC_ENUM_PROCESS_INFO, type, state, buf, buflen, needed,
-                                            &count, resume_handle, group );
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code( GetExceptionCode() );
-    }
-    __ENDTRY
-
-    *returned = 0;
-    if (err != ERROR_SUCCESS)
-    {
-        /* double the needed size to fit the potentially larger ENUM_SERVICE_STATUS_PROCESSW */
-        if (err == ERROR_MORE_DATA) *needed *= 2;
-        heap_free( buf );
-        SetLastError( err );
-        return FALSE;
-    }
-
-    entry = (struct enum_service_status_process *)buf;
-    for (i = 0; i < count; i++)
-    {
-        total_size += sizeof(*services);
-        if (entry->service_name)
-        {
-            str = (const WCHAR *)(buf + entry->service_name);
-            total_size += (strlenW( str ) + 1) * sizeof(WCHAR);
-        }
-        if (entry->display_name)
-        {
-            str = (const WCHAR *)(buf + entry->display_name);
-            total_size += (strlenW( str ) + 1) * sizeof(WCHAR);
-        }
-        entry++;
-    }
-
-    if (total_size > size)
-    {
-        heap_free( buf );
-        *needed = total_size;
-        SetLastError( ERROR_MORE_DATA );
-        return FALSE;
+        MultiByteToWideChar( CP_ACP, 0, group, -1, groupW, len * sizeof(WCHAR) );
     }
 
-    offset = count * sizeof(*services);
-    entry = (struct enum_service_status_process *)buf;
-    for (i = 0; i < count; i++)
-    {
-        DWORD str_size;
-        str = (const WCHAR *)(buf + entry->service_name);
-        str_size = (strlenW( str ) + 1) * sizeof(WCHAR);
-        services[i].lpServiceName = (WCHAR *)((char *)services + offset);
-        memcpy( services[i].lpServiceName, str, str_size );
-        offset += str_size;
+    ret = EnumServicesStatusExW( hmngr, level, type, state, (BYTE *)servicesW, sz,
+                                 needed, returned, resume_handle, groupW );
+    if (!ret) goto done;
 
-        if (!entry->display_name) services[i].lpDisplayName = NULL;
-        else
+    p = (char *)services + *returned * sizeof(ENUM_SERVICE_STATUS_PROCESSA);
+    n = size - (p - (char *)services);
+    ret = FALSE;
+    for (i = 0; i < *returned; i++)
+    {
+        sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpServiceName, -1, p, n, NULL, NULL );
+        if (!sz) goto done;
+        services[i].lpServiceName = p;
+        p += sz;
+        n -= sz;
+        if (servicesW[i].lpDisplayName)
         {
-            str = (const WCHAR *)(buf + entry->display_name);
-            str_size = (strlenW( str ) + 1) * sizeof(WCHAR);
-            services[i].lpDisplayName = (WCHAR *)((char *)services + offset);
-            memcpy( services[i].lpDisplayName, str, str_size );
-            offset += str_size;
+            sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpDisplayName, -1, p, n, NULL, NULL );
+            if (!sz) goto done;
+            services[i].lpDisplayName = p;
+            p += sz;
+            n -= sz;
         }
-        services[i].ServiceStatusProcess = entry->service_status_process;
-        entry++;
+        else services[i].lpDisplayName = NULL;
+        services[i].ServiceStatusProcess = servicesW[i].ServiceStatusProcess;
     }
 
-    heap_free( buf );
-    *needed = 0;
-    *returned = count;
-    return TRUE;
+    ret = TRUE;
+
+done:
+    heap_free( servicesW );
+    heap_free( groupW );
+    return ret;
 }
 
 /******************************************************************************
@@ -2071,59 +327,6 @@ cleanup:
     return ret;
 }
 
-/******************************************************************************
- * GetServiceKeyNameW [ADVAPI32.@]
- */
-BOOL WINAPI GetServiceKeyNameW( SC_HANDLE hSCManager, LPCWSTR lpDisplayName,
-                                LPWSTR lpServiceName, LPDWORD lpcchBuffer )
-{
-    DWORD err;
-    WCHAR buffer[2];
-    DWORD size;
-
-    TRACE("%p %s %p %p\n", hSCManager,
-          debugstr_w(lpDisplayName), lpServiceName, lpcchBuffer);
-
-    if (!hSCManager)
-    {
-        SetLastError( ERROR_INVALID_HANDLE );
-        return FALSE;
-    }
-
-    /* provide a buffer if the caller didn't */
-    if (!lpServiceName || *lpcchBuffer < 2)
-    {
-        lpServiceName = buffer;
-        /* A size of 1 would be enough, but tests show that Windows returns 2,
-         * probably because of a WCHAR/bytes mismatch in their code.
-         */
-        *lpcchBuffer = 2;
-    }
-
-    /* RPC call takes size excluding nul-terminator, whereas *lpcchBuffer
-     * includes the nul-terminator on input. */
-    size = *lpcchBuffer - 1;
-
-    __TRY
-    {
-        err = svcctl_GetServiceKeyNameW(hSCManager, lpDisplayName, lpServiceName,
-                                        &size);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    /* The value of *lpcchBuffer excludes nul-terminator on output. */
-    if (err == ERROR_SUCCESS || err == ERROR_INSUFFICIENT_BUFFER)
-        *lpcchBuffer = size;
-
-    if (err)
-        SetLastError(err);
-    return err == ERROR_SUCCESS;
-}
-
 /******************************************************************************
  * QueryServiceLockStatusA [ADVAPI32.@]
  */
@@ -2195,284 +398,6 @@ cleanup:
     return ret;
 }
 
-/******************************************************************************
- * GetServiceDisplayNameW  [ADVAPI32.@]
- */
-BOOL WINAPI GetServiceDisplayNameW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
-  LPWSTR lpDisplayName, LPDWORD lpcchBuffer)
-{
-    DWORD err;
-    DWORD size;
-    WCHAR buffer[2];
-
-    TRACE("%p %s %p %p\n", hSCManager,
-          debugstr_w(lpServiceName), lpDisplayName, lpcchBuffer);
-
-    if (!hSCManager)
-    {
-        SetLastError( ERROR_INVALID_HANDLE );
-        return FALSE;
-    }
-
-    /* provide a buffer if the caller didn't */
-    if (!lpDisplayName || *lpcchBuffer < 2)
-    {
-        lpDisplayName = buffer;
-        /* A size of 1 would be enough, but tests show that Windows returns 2,
-         * probably because of a WCHAR/bytes mismatch in their code.
-         */
-        *lpcchBuffer = 2;
-    }
-
-    /* RPC call takes size excluding nul-terminator, whereas *lpcchBuffer
-     * includes the nul-terminator on input. */
-    size = *lpcchBuffer - 1;
-
-    __TRY
-    {
-        err = svcctl_GetServiceDisplayNameW(hSCManager, lpServiceName, lpDisplayName,
-                                            &size);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    /* The value of *lpcchBuffer excludes nul-terminator on output. */
-    if (err == ERROR_SUCCESS || err == ERROR_INSUFFICIENT_BUFFER)
-        *lpcchBuffer = size;
-
-    if (err)
-        SetLastError(err);
-    return err == ERROR_SUCCESS;
-}
-
-/******************************************************************************
- * ChangeServiceConfigW  [ADVAPI32.@]
- */
-BOOL WINAPI ChangeServiceConfigW( SC_HANDLE hService, DWORD dwServiceType,
-  DWORD dwStartType, DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
-  LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCWSTR lpDependencies,
-  LPCWSTR lpServiceStartName, LPCWSTR lpPassword, LPCWSTR lpDisplayName)
-{
-    DWORD cb_pwd;
-    DWORD err;
-
-    TRACE("%p %d %d %d %s %s %p %p %s %s %s\n",
-          hService, dwServiceType, dwStartType, dwErrorControl, 
-          debugstr_w(lpBinaryPathName), debugstr_w(lpLoadOrderGroup),
-          lpdwTagId, lpDependencies, debugstr_w(lpServiceStartName),
-          debugstr_w(lpPassword), debugstr_w(lpDisplayName) );
-
-    cb_pwd = lpPassword ? (strlenW(lpPassword) + 1)*sizeof(WCHAR) : 0;
-
-    __TRY
-    {
-        err = svcctl_ChangeServiceConfigW(hService, dwServiceType,
-                dwStartType, dwErrorControl, lpBinaryPathName, lpLoadOrderGroup, lpdwTagId,
-                (const BYTE *)lpDependencies, multisz_cb(lpDependencies), lpServiceStartName,
-                (const BYTE *)lpPassword, cb_pwd, lpDisplayName);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    if (err != ERROR_SUCCESS)
-        SetLastError(err);
-
-    return err == ERROR_SUCCESS;
-}
-
-/******************************************************************************
- * ChangeServiceConfigA  [ADVAPI32.@]
- */
-BOOL WINAPI ChangeServiceConfigA( SC_HANDLE hService, DWORD dwServiceType,
-  DWORD dwStartType, DWORD dwErrorControl, LPCSTR lpBinaryPathName,
-  LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCSTR lpDependencies,
-  LPCSTR lpServiceStartName, LPCSTR lpPassword, LPCSTR lpDisplayName)
-{
-    LPWSTR wBinaryPathName, wLoadOrderGroup, wDependencies;
-    LPWSTR wServiceStartName, wPassword, wDisplayName;
-    BOOL r;
-
-    TRACE("%p %d %d %d %s %s %p %p %s %s %s\n",
-          hService, dwServiceType, dwStartType, dwErrorControl, 
-          debugstr_a(lpBinaryPathName), debugstr_a(lpLoadOrderGroup),
-          lpdwTagId, lpDependencies, debugstr_a(lpServiceStartName),
-          debugstr_a(lpPassword), debugstr_a(lpDisplayName) );
-
-    wBinaryPathName = strdupAW( lpBinaryPathName );
-    wLoadOrderGroup = strdupAW( lpLoadOrderGroup );
-    wDependencies = SERV_dupmulti( lpDependencies );
-    wServiceStartName = strdupAW( lpServiceStartName );
-    wPassword = strdupAW( lpPassword );
-    wDisplayName = strdupAW( lpDisplayName );
-
-    r = ChangeServiceConfigW( hService, dwServiceType,
-            dwStartType, dwErrorControl, wBinaryPathName,
-            wLoadOrderGroup, lpdwTagId, wDependencies,
-            wServiceStartName, wPassword, wDisplayName);
-
-    heap_free( wBinaryPathName );
-    heap_free( wLoadOrderGroup );
-    heap_free( wDependencies );
-    heap_free( wServiceStartName );
-    heap_free( wPassword );
-    heap_free( wDisplayName );
-
-    return r;
-}
-
-/******************************************************************************
- * ChangeServiceConfig2A  [ADVAPI32.@]
- */
-BOOL WINAPI ChangeServiceConfig2A( SC_HANDLE hService, DWORD dwInfoLevel, 
-    LPVOID lpInfo)
-{
-    BOOL r = FALSE;
-
-    TRACE("%p %d %p\n",hService, dwInfoLevel, lpInfo);
-
-    if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
-    {
-        LPSERVICE_DESCRIPTIONA sd = lpInfo;
-        SERVICE_DESCRIPTIONW sdw;
-
-        sdw.lpDescription = strdupAW( sd->lpDescription );
-
-        r = ChangeServiceConfig2W( hService, dwInfoLevel, &sdw );
-
-        heap_free( sdw.lpDescription );
-    }
-    else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
-    {
-        LPSERVICE_FAILURE_ACTIONSA fa = lpInfo;
-        SERVICE_FAILURE_ACTIONSW faw;
-
-        faw.dwResetPeriod = fa->dwResetPeriod;
-        faw.lpRebootMsg = strdupAW( fa->lpRebootMsg );
-        faw.lpCommand = strdupAW( fa->lpCommand );
-        faw.cActions = fa->cActions;
-        faw.lpsaActions = fa->lpsaActions;
-
-        r = ChangeServiceConfig2W( hService, dwInfoLevel, &faw );
-
-        heap_free( faw.lpRebootMsg );
-        heap_free( faw.lpCommand );
-    }
-    else if (dwInfoLevel == SERVICE_CONFIG_PRESHUTDOWN_INFO)
-    {
-        r = ChangeServiceConfig2W( hService, dwInfoLevel, lpInfo);
-    }
-    else
-        SetLastError( ERROR_INVALID_PARAMETER );
-
-    return r;
-}
-
-/******************************************************************************
- * ChangeServiceConfig2W  [ADVAPI32.@]
- */
-BOOL WINAPI ChangeServiceConfig2W( SC_HANDLE hService, DWORD dwInfoLevel, 
-    LPVOID lpInfo)
-{
-    SERVICE_RPC_REQUIRED_PRIVILEGES_INFO rpc_privinfo;
-    DWORD err;
-
-    __TRY
-    {
-        SC_RPC_CONFIG_INFOW info;
-
-        info.dwInfoLevel = dwInfoLevel;
-        if (dwInfoLevel == SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO)
-        {
-            SERVICE_REQUIRED_PRIVILEGES_INFOW *privinfo = lpInfo;
-            WCHAR *p;
-
-            for (p = privinfo->pmszRequiredPrivileges; *p; p += strlenW(p) + 1);
-            rpc_privinfo.cbRequiredPrivileges =
-                (p - privinfo->pmszRequiredPrivileges + 1) * sizeof(WCHAR);
-            rpc_privinfo.pRequiredPrivileges = (BYTE *)privinfo->pmszRequiredPrivileges;
-            info.u.privinfo = &rpc_privinfo;
-        }
-        else
-            info.u.descr = lpInfo;
-        err = svcctl_ChangeServiceConfig2W( hService, info );
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    if (err != ERROR_SUCCESS)
-        SetLastError(err);
-
-    return err == ERROR_SUCCESS;
-}
-
-static NTSTATUS SERV_QueryServiceObjectSecurity(SC_HANDLE hService,
-       SECURITY_INFORMATION dwSecurityInformation,
-       PSECURITY_DESCRIPTOR lpSecurityDescriptor,
-       DWORD cbBufSize, LPDWORD pcbBytesNeeded)
-{
-    SECURITY_DESCRIPTOR descriptor;
-    NTSTATUS status;
-    DWORD size;
-    ACL acl;
-
-    FIXME("%p %d %p %u %p - semi-stub\n", hService, dwSecurityInformation,
-          lpSecurityDescriptor, cbBufSize, pcbBytesNeeded);
-
-    if (dwSecurityInformation != DACL_SECURITY_INFORMATION)
-        FIXME("information %d not supported\n", dwSecurityInformation);
-
-    InitializeSecurityDescriptor(&descriptor, SECURITY_DESCRIPTOR_REVISION);
-
-    InitializeAcl(&acl, sizeof(ACL), ACL_REVISION);
-    SetSecurityDescriptorDacl(&descriptor, TRUE, &acl, TRUE);
-
-    size = cbBufSize;
-    status = RtlMakeSelfRelativeSD(&descriptor, lpSecurityDescriptor, &size);
-    *pcbBytesNeeded = size;
-    return status;
-}
-
-/******************************************************************************
- * QueryServiceObjectSecurity [ADVAPI32.@]
- */
-BOOL WINAPI QueryServiceObjectSecurity(SC_HANDLE hService,
-       SECURITY_INFORMATION dwSecurityInformation,
-       PSECURITY_DESCRIPTOR lpSecurityDescriptor,
-       DWORD cbBufSize, LPDWORD pcbBytesNeeded)
-{
-    NTSTATUS status = SERV_QueryServiceObjectSecurity(hService, dwSecurityInformation, lpSecurityDescriptor,
-                                                      cbBufSize, pcbBytesNeeded);
-    if (status != STATUS_SUCCESS)
-    {
-        SetLastError(RtlNtStatusToDosError(status));
-        return FALSE;
-    }
-    return TRUE;
-}
-
-/******************************************************************************
- * SetServiceObjectSecurity [ADVAPI32.@]
- *
- * NOTES
- *  - SetSecurityInfo should be updated to call this function once it's implemented.
- */
-BOOL WINAPI SetServiceObjectSecurity(SC_HANDLE hService,
-       SECURITY_INFORMATION dwSecurityInformation,
-       PSECURITY_DESCRIPTOR lpSecurityDescriptor)
-{
-    FIXME("%p %d %p\n", hService, dwSecurityInformation, lpSecurityDescriptor);
-    return TRUE;
-}
-
 /******************************************************************************
  * SetServiceBits [ADVAPI32.@]
  */
@@ -2486,69 +411,6 @@ BOOL WINAPI SetServiceBits( SERVICE_STATUS_HANDLE hServiceStatus,
     return TRUE;
 }
 
-/* thunk for calling the RegisterServiceCtrlHandler handler function */
-static DWORD WINAPI ctrl_handler_thunk( DWORD control, DWORD type, void *data, void *context )
-{
-    LPHANDLER_FUNCTION func = context;
-
-    func( control );
-    return ERROR_SUCCESS;
-}
-
-/******************************************************************************
- * RegisterServiceCtrlHandlerA [ADVAPI32.@]
- */
-SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerA( LPCSTR name, LPHANDLER_FUNCTION handler )
-{
-    return RegisterServiceCtrlHandlerExA( name, ctrl_handler_thunk, handler );
-}
-
-/******************************************************************************
- * RegisterServiceCtrlHandlerW [ADVAPI32.@]
- */
-SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerW( LPCWSTR name, LPHANDLER_FUNCTION handler )
-{
-    return RegisterServiceCtrlHandlerExW( name, ctrl_handler_thunk, handler );
-}
-
-/******************************************************************************
- * RegisterServiceCtrlHandlerExA [ADVAPI32.@]
- */
-SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExA( LPCSTR name, LPHANDLER_FUNCTION_EX handler, LPVOID context )
-{
-    LPWSTR nameW;
-    SERVICE_STATUS_HANDLE ret;
-
-    nameW = strdupAW(name);
-    ret = RegisterServiceCtrlHandlerExW( nameW, handler, context );
-    heap_free( nameW );
-    return ret;
-}
-
-/******************************************************************************
- * RegisterServiceCtrlHandlerExW [ADVAPI32.@]
- */
-SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExW( LPCWSTR lpServiceName,
-        LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext )
-{
-    service_data *service;
-    SC_HANDLE hService = 0;
-
-    TRACE("%s %p %p\n", debugstr_w(lpServiceName), lpHandlerProc, lpContext);
-
-    EnterCriticalSection( &service_cs );
-    if ((service = find_service_by_name( lpServiceName )))
-    {
-        service->handler = lpHandlerProc;
-        service->context = lpContext;
-        hService = service->handle;
-    }
-    LeaveCriticalSection( &service_cs );
-
-    if (!hService) SetLastError( ERROR_SERVICE_DOES_NOT_EXIST );
-    return (SERVICE_STATUS_HANDLE)hService;
-}
-
 /******************************************************************************
  * EnumDependentServicesA [ADVAPI32.@]
  */
@@ -2562,145 +424,3 @@ BOOL WINAPI EnumDependentServicesA( SC_HANDLE hService, DWORD dwServiceState,
     *lpServicesReturned = 0;
     return TRUE;
 }
-
-/******************************************************************************
- * EnumDependentServicesW [ADVAPI32.@]
- */
-BOOL WINAPI EnumDependentServicesW( SC_HANDLE hService, DWORD dwServiceState,
-                                    LPENUM_SERVICE_STATUSW lpServices, DWORD cbBufSize,
-                                    LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned )
-{
-    FIXME("%p 0x%08x %p 0x%08x %p %p - stub\n", hService, dwServiceState,
-          lpServices, cbBufSize, pcbBytesNeeded, lpServicesReturned);
-
-    *lpServicesReturned = 0;
-    return TRUE;
-}
-
-static DWORD WINAPI notify_thread(void *user)
-{
-    DWORD err;
-    notify_data *data = user;
-    SC_RPC_NOTIFY_PARAMS_LIST *list = NULL;
-    SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2 *cparams;
-    BOOL dummy;
-
-    __TRY
-    {
-        /* GetNotifyResults blocks until there is an event */
-        err = svcctl_GetNotifyResults(data->notify_handle, &list);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    EnterCriticalSection( &service_cs );
-
-    list_remove(&data->entry);
-
-    LeaveCriticalSection( &service_cs );
-
-    if (err == ERROR_SUCCESS && list)
-    {
-        cparams = list->NotifyParamsArray[0].u.params;
-
-        data->notify_buffer->dwNotificationStatus = cparams->dwNotificationStatus;
-        memcpy(&data->notify_buffer->ServiceStatus, &cparams->ServiceStatus,
-                sizeof(SERVICE_STATUS_PROCESS));
-        data->notify_buffer->dwNotificationTriggered = cparams->dwNotificationTriggered;
-        data->notify_buffer->pszServiceNames = NULL;
-
-        QueueUserAPC((PAPCFUNC)data->notify_buffer->pfnNotifyCallback,
-                data->calling_thread, (ULONG_PTR)data->notify_buffer);
-
-        HeapFree(GetProcessHeap(), 0, list);
-    }
-    else
-        WARN("GetNotifyResults server call failed: %u\n", err);
-
-
-    __TRY
-    {
-        err = svcctl_CloseNotifyHandle(&data->notify_handle, &dummy);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    if (err != ERROR_SUCCESS)
-        WARN("CloseNotifyHandle server call failed: %u\n", err);
-
-    CloseHandle(data->calling_thread);
-    HeapFree(GetProcessHeap(), 0, data);
-
-    return 0;
-}
-
-/******************************************************************************
- * NotifyServiceStatusChangeW [ADVAPI32.@]
- */
-DWORD WINAPI NotifyServiceStatusChangeW(SC_HANDLE hService, DWORD dwNotifyMask,
-        SERVICE_NOTIFYW *pNotifyBuffer)
-{
-    DWORD err;
-    BOOL b_dummy = FALSE;
-    GUID g_dummy = {0};
-    notify_data *data;
-
-    TRACE("%p 0x%x %p\n", hService, dwNotifyMask, pNotifyBuffer);
-
-    data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data));
-    if (!data)
-        return ERROR_NOT_ENOUGH_MEMORY;
-
-    data->service = hService;
-    data->notify_buffer = pNotifyBuffer;
-    if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
-            GetCurrentProcess(), &data->calling_thread, 0, FALSE,
-            DUPLICATE_SAME_ACCESS))
-    {
-        ERR("DuplicateHandle failed: %u\n", GetLastError());
-        HeapFree(GetProcessHeap(), 0, data);
-        return ERROR_NOT_ENOUGH_MEMORY;
-    }
-
-    data->params.dwInfoLevel = 2;
-    data->params.u.params = &data->cparams;
-
-    data->cparams.dwNotifyMask = dwNotifyMask;
-
-    EnterCriticalSection( &service_cs );
-
-    __TRY
-    {
-        err = svcctl_NotifyServiceStatusChange(hService, data->params,
-                &g_dummy, &g_dummy, &b_dummy, &data->notify_handle);
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code(GetExceptionCode());
-    }
-    __ENDTRY
-
-    if (err != ERROR_SUCCESS)
-    {
-        WARN("NotifyServiceStatusChange server call failed: %u\n", err);
-        LeaveCriticalSection( &service_cs );
-        CloseHandle(data->calling_thread);
-        CloseHandle(data->ready_evt);
-        HeapFree(GetProcessHeap(), 0, data);
-        return err;
-    }
-
-    CloseHandle(CreateThread(NULL, 0, &notify_thread, data, 0, NULL));
-
-    list_add_tail(&notify_list, &data->entry);
-
-    LeaveCriticalSection( &service_cs );
-
-    return ERROR_SUCCESS;
-}
diff --git a/dlls/sechost/Makefile.in b/dlls/sechost/Makefile.in
index 9f573ccc2f9..eb98d04859c 100644
--- a/dlls/sechost/Makefile.in
+++ b/dlls/sechost/Makefile.in
@@ -1,8 +1,13 @@
 MODULE    = sechost.dll
 IMPORTLIB = sechost
 IMPORTS   = kernelbase
+DELAYIMPORTS = rpcrt4
 
 EXTRADLLFLAGS = -mno-cygwin
 
 C_SRCS = \
+	service.c \
 	trace.c
+
+IDL_SRCS = \
+	svcctl.idl
diff --git a/dlls/sechost/sechost.spec b/dlls/sechost/sechost.spec
index f54de38c289..df6af1646df 100644
--- a/dlls/sechost/sechost.spec
+++ b/dlls/sechost/sechost.spec
@@ -19,13 +19,13 @@
 @ stub BuildSecurityDescriptorForSharingAccessEx
 @ stub CapabilityCheck
 @ stub CapabilityCheckForSingleSessionSku
-@ stdcall ChangeServiceConfig2A(long long ptr) advapi32.ChangeServiceConfig2A
-@ stdcall ChangeServiceConfig2W(long long ptr) advapi32.ChangeServiceConfig2W
-@ stdcall ChangeServiceConfigA(long long long long wstr str ptr str str str str) advapi32.ChangeServiceConfigA
-@ stdcall ChangeServiceConfigW(long long long long wstr wstr ptr wstr wstr wstr wstr) advapi32.ChangeServiceConfigW
-@ stdcall CloseServiceHandle(long) advapi32.CloseServiceHandle
+@ stdcall ChangeServiceConfig2A(long long ptr)
+@ stdcall ChangeServiceConfig2W(long long ptr)
+@ stdcall ChangeServiceConfigA(long long long long wstr str ptr str str str str)
+@ stdcall ChangeServiceConfigW(long long long long wstr wstr ptr wstr wstr wstr wstr)
+@ stdcall CloseServiceHandle(long)
 @ stdcall CloseTrace(int64)
-@ stdcall ControlService(long long ptr) advapi32.ControlService
+@ stdcall ControlService(long long ptr)
 @ stub ControlServiceExA
 @ stub ControlServiceExW
 @ stdcall ControlTraceA(int64 str ptr long)
@@ -38,9 +38,9 @@
 @ stub ConvertStringSDToSDRootDomainW
 @ stdcall ConvertStringSecurityDescriptorToSecurityDescriptorW(wstr long ptr ptr) advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
 @ stdcall ConvertStringSidToSidW(ptr ptr) advapi32.ConvertStringSidToSidW
-@ stdcall CreateServiceA(long str str long long long long str str ptr str str str) advapi32.CreateServiceA
+@ stdcall CreateServiceA(long str str long long long long str str ptr str str str)
 @ stub CreateServiceEx
-@ stdcall CreateServiceW(long wstr wstr long long long long wstr wstr ptr wstr wstr wstr) advapi32.CreateServiceW
+@ stdcall CreateServiceW(long wstr wstr long long long long wstr wstr ptr wstr wstr wstr)
 @ stub CredBackupCredentials
 @ stdcall CredDeleteA(str long long) advapi32.CredDeleteA
 @ stdcall CredDeleteW(wstr long long) advapi32.CredDeleteW
@@ -84,10 +84,10 @@
 @ stub CredpDecodeCredential
 @ stub CredpEncodeCredential
 @ stub CredpEncodeSecret
-@ stdcall DeleteService(long) advapi32.DeleteService
+@ stdcall DeleteService(long)
 @ stdcall EnableTraceEx2(int64 ptr long long int64 int64 long ptr)
-@ stdcall EnumDependentServicesW(long long ptr long ptr ptr) advapi32.EnumDependentServicesW
-@ stdcall EnumServicesStatusExW(long long long long ptr long ptr ptr ptr wstr) advapi32.EnumServicesStatusExW
+@ stdcall EnumDependentServicesW(long long ptr long ptr ptr)
+@ stdcall EnumServicesStatusExW(long long long long ptr long ptr ptr ptr wstr)
 @ stub EnumerateIdentityProviders
 @ stub EnumerateTraceGuidsEx
 @ stub EtwQueryRealtimeConsumer
@@ -99,8 +99,8 @@
 @ stub GetIdentityProviderInfoByGUID
 @ stub GetIdentityProviderInfoByName
 @ stub GetServiceDirectory
-@ stdcall GetServiceDisplayNameW(ptr wstr ptr ptr) advapi32.GetServiceDisplayNameW
-@ stdcall GetServiceKeyNameW(long wstr ptr ptr) advapi32.GetServiceKeyNameW
+@ stdcall GetServiceDisplayNameW(ptr wstr ptr ptr)
+@ stdcall GetServiceKeyNameW(long wstr ptr ptr)
 @ stub GetServiceRegistryStateKey
 @ stub I_QueryTagInformation
 @ stub I_RegisterSvchostNotificationCallback
@@ -159,45 +159,45 @@
 @ stdcall LsaStorePrivateData(ptr ptr ptr) advapi32.LsaStorePrivateData
 @ stub NotifyServiceStatusChange
 @ stub NotifyServiceStatusChangeA
-@ stdcall NotifyServiceStatusChangeW(ptr long ptr) advapi32.NotifyServiceStatusChangeW
-@ stdcall OpenSCManagerA(str str long) advapi32.OpenSCManagerA
-@ stdcall OpenSCManagerW(wstr wstr long) advapi32.OpenSCManagerW
-@ stdcall OpenServiceA(long str long) advapi32.OpenServiceA
-@ stdcall OpenServiceW(long wstr long) advapi32.OpenServiceW
+@ stdcall NotifyServiceStatusChangeW(ptr long ptr)
+@ stdcall OpenSCManagerA(str str long)
+@ stdcall OpenSCManagerW(wstr wstr long)
+@ stdcall OpenServiceA(long str long)
+@ stdcall OpenServiceW(long wstr long)
 @ stdcall -ret64 OpenTraceW(ptr)
 @ stdcall ProcessTrace(ptr long ptr ptr)
 @ stdcall QueryAllTracesA(ptr long ptr)
 @ stdcall QueryAllTracesW(ptr long ptr)
 @ stub QueryLocalUserServiceName
-@ stdcall QueryServiceConfig2A(long long ptr long ptr) advapi32.QueryServiceConfig2A
-@ stdcall QueryServiceConfig2W(long long ptr long ptr) advapi32.QueryServiceConfig2W
-@ stdcall QueryServiceConfigA(long ptr long ptr) advapi32.QueryServiceConfigA
-@ stdcall QueryServiceConfigW(long ptr long ptr) advapi32.QueryServiceConfigW
+@ stdcall QueryServiceConfig2A(long long ptr long ptr)
+@ stdcall QueryServiceConfig2W(long long ptr long ptr)
+@ stdcall QueryServiceConfigA(long ptr long ptr)
+@ stdcall QueryServiceConfigW(long ptr long ptr)
 @ stub QueryServiceDynamicInformation
-@ stdcall QueryServiceObjectSecurity(long long ptr long ptr) advapi32.QueryServiceObjectSecurity
-@ stdcall QueryServiceStatus(long ptr) advapi32.QueryServiceStatus
-@ stdcall QueryServiceStatusEx(long long ptr long ptr) advapi32.QueryServiceStatusEx
+@ stdcall QueryServiceObjectSecurity(long long ptr long ptr)
+@ stdcall QueryServiceStatus(long ptr)
+@ stdcall QueryServiceStatusEx(long long ptr long ptr)
 @ stub QueryTraceProcessingHandle
 @ stub QueryTransientObjectSecurityDescriptor
 @ stub QueryUserServiceName
 @ stub QueryUserServiceNameForContext
-@ stdcall RegisterServiceCtrlHandlerA(str ptr) advapi32.RegisterServiceCtrlHandlerA
-@ stdcall RegisterServiceCtrlHandlerExA(str ptr ptr) advapi32.RegisterServiceCtrlHandlerExA
-@ stdcall RegisterServiceCtrlHandlerExW(wstr ptr ptr) advapi32.RegisterServiceCtrlHandlerExW
-@ stdcall RegisterServiceCtrlHandlerW(wstr ptr) advapi32.RegisterServiceCtrlHandlerW
+@ stdcall RegisterServiceCtrlHandlerA(str ptr)
+@ stdcall RegisterServiceCtrlHandlerExA(str ptr ptr)
+@ stdcall RegisterServiceCtrlHandlerExW(wstr ptr ptr)
+@ stdcall RegisterServiceCtrlHandlerW(wstr ptr)
 @ stdcall RegisterTraceGuidsA(ptr ptr ptr long ptr str str ptr) advapi32.RegisterTraceGuidsA
 @ stub ReleaseIdentityProviderEnumContext
 @ stub RemoveTraceCallback
 @ stub RpcClientCapabilityCheck
 @ stub SetLocalRpcServerInterfaceSecurity
 @ stub SetLocalRpcServerProtseqSecurity
-@ stdcall SetServiceObjectSecurity(long long ptr) advapi32.SetServiceObjectSecurity
-@ stdcall SetServiceStatus(long ptr) advapi32.SetServiceStatus
+@ stdcall SetServiceObjectSecurity(long long ptr)
+@ stdcall SetServiceStatus(long ptr)
 @ stub SetTraceCallback
-@ stdcall StartServiceA(long long ptr) advapi32.StartServiceA
-@ stdcall StartServiceCtrlDispatcherA(ptr) advapi32.StartServiceCtrlDispatcherA
-@ stdcall StartServiceCtrlDispatcherW(ptr) advapi32.StartServiceCtrlDispatcherW
-@ stdcall StartServiceW(long long ptr) advapi32.StartServiceW
+@ stdcall StartServiceA(long long ptr)
+@ stdcall StartServiceCtrlDispatcherA(ptr)
+@ stdcall StartServiceCtrlDispatcherW(ptr)
+@ stdcall StartServiceW(long long ptr)
 @ stdcall StartTraceA(ptr str ptr)
 @ stdcall StartTraceW(ptr wstr ptr)
 @ stdcall StopTraceW(int64 wstr ptr)
diff --git a/dlls/sechost/service.c b/dlls/sechost/service.c
new file mode 100644
index 00000000000..7a4211631e2
--- /dev/null
+++ b/dlls/sechost/service.c
@@ -0,0 +1,1969 @@
+/*
+ * Service control API
+ *
+ * Copyright 1995 Sven Verdoolaege
+ * Copyright 2005 Mike McCormack
+ * Copyright 2007 Rolf Kalbermatter
+ *
+ * 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
+ */
+
+#define NONAMELESSUNION
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winsvc.h"
+#include "winternl.h"
+
+#include "wine/debug.h"
+#include "wine/exception.h"
+#include "wine/heap.h"
+#include "wine/list.h"
+
+#include "svcctl.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(service);
+
+struct notify_data
+{
+    SC_HANDLE service;
+    SC_RPC_NOTIFY_PARAMS params;
+    SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2 cparams;
+    SC_NOTIFY_RPC_HANDLE notify_handle;
+    SERVICE_NOTIFYW *notify_buffer;
+    HANDLE calling_thread, ready_evt;
+    struct list entry;
+};
+
+static struct list notify_list = LIST_INIT(notify_list);
+
+static CRITICAL_SECTION service_cs;
+static CRITICAL_SECTION_DEBUG service_cs_debug =
+{
+    0, 0, &service_cs,
+    { &service_cs_debug.ProcessLocksList,
+      &service_cs_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": service_cs") }
+};
+static CRITICAL_SECTION service_cs = { &service_cs_debug, -1, 0, 0, 0, 0 };
+
+struct service_data
+{
+    LPHANDLER_FUNCTION_EX handler;
+    void *context;
+    HANDLE thread;
+    SC_HANDLE handle;
+    SC_HANDLE full_access_handle;
+    unsigned int unicode : 1;
+    union
+    {
+        LPSERVICE_MAIN_FUNCTIONA a;
+        LPSERVICE_MAIN_FUNCTIONW w;
+    } proc;
+    WCHAR *args;
+    WCHAR name[1];
+};
+
+struct dispatcher_data
+{
+    SC_HANDLE manager;
+    HANDLE pipe;
+};
+
+static struct service_data **services;
+static unsigned int nb_services;
+static HANDLE service_event;
+static BOOL stop_service;
+
+extern HANDLE CDECL __wine_make_process_system(void);
+
+static WCHAR *heap_strdupAtoW( const char *src )
+{
+    WCHAR *dst = NULL;
+    if (src)
+    {
+        DWORD len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 );
+        if ((dst = heap_alloc( len * sizeof(WCHAR) ))) MultiByteToWideChar( CP_ACP, 0, src, -1, dst, len );
+    }
+    return dst;
+}
+
+static WCHAR *heap_strdup_multi_AtoW( const char *src )
+{
+    WCHAR *dst = NULL;
+    const char *p = src;
+    DWORD len;
+
+    if (!src) return NULL;
+
+    while (*p) p += strlen(p) + 1;
+    for (p = src; *p; p += strlen(p) + 1);
+    p++; /* final null */
+    len = MultiByteToWideChar( CP_ACP, 0, src, p - src, NULL, 0 );
+    if ((dst = heap_alloc( len * sizeof(WCHAR) ))) MultiByteToWideChar( CP_ACP, 0, src, p - src, dst, len );
+    return dst;
+}
+
+static inline DWORD multisz_size( const WCHAR *str )
+{
+    const WCHAR *p = str;
+
+    if (!str) return 0;
+
+    while (*p) p += wcslen(p) + 1;
+    return (p - str + 1) * sizeof(WCHAR);
+}
+
+void  __RPC_FAR * __RPC_USER MIDL_user_allocate( SIZE_T len )
+{
+    return heap_alloc(len);
+}
+
+void __RPC_USER MIDL_user_free( void __RPC_FAR *ptr )
+{
+    heap_free(ptr);
+}
+
+static LONG WINAPI rpc_filter( EXCEPTION_POINTERS *eptr )
+{
+    return I_RpcExceptionFilter( eptr->ExceptionRecord->ExceptionCode );
+}
+
+static DWORD map_exception_code( DWORD exception_code )
+{
+    switch (exception_code)
+    {
+    case RPC_X_NULL_REF_POINTER:
+        return ERROR_INVALID_ADDRESS;
+    case RPC_X_ENUM_VALUE_OUT_OF_RANGE:
+    case RPC_X_BYTE_COUNT_TOO_SMALL:
+        return ERROR_INVALID_PARAMETER;
+    case RPC_S_INVALID_BINDING:
+    case RPC_X_SS_IN_NULL_CONTEXT:
+        return ERROR_INVALID_HANDLE;
+    default:
+        return exception_code;
+    }
+}
+
+static handle_t rpc_wstr_bind( RPC_WSTR str )
+{
+    WCHAR transport[] = SVCCTL_TRANSPORT;
+    WCHAR endpoint[] = SVCCTL_ENDPOINT;
+    RPC_WSTR binding_str;
+    RPC_STATUS status;
+    handle_t rpc_handle;
+
+    status = RpcStringBindingComposeW( NULL, transport, str, endpoint, NULL, &binding_str );
+    if (status != RPC_S_OK)
+    {
+        ERR("RpcStringBindingComposeW failed, error %d\n", status);
+        return NULL;
+    }
+
+    status = RpcBindingFromStringBindingW( binding_str, &rpc_handle );
+    RpcStringFreeW( &binding_str );
+
+    if (status != RPC_S_OK)
+    {
+        ERR("Couldn't connect to services.exe, error %d\n", status);
+        return NULL;
+    }
+
+    return rpc_handle;
+}
+
+static handle_t rpc_cstr_bind(RPC_CSTR str)
+{
+    RPC_CSTR transport = (RPC_CSTR)SVCCTL_TRANSPORTA;
+    RPC_CSTR endpoint = (RPC_CSTR)SVCCTL_ENDPOINTA;
+    RPC_CSTR binding_str;
+    RPC_STATUS status;
+    handle_t rpc_handle;
+
+    status = RpcStringBindingComposeA( NULL, transport, str, endpoint, NULL, &binding_str );
+    if (status != RPC_S_OK)
+    {
+        ERR("RpcStringBindingComposeA failed, error %d\n", status);
+        return NULL;
+    }
+
+    status = RpcBindingFromStringBindingA( binding_str, &rpc_handle );
+    RpcStringFreeA( &binding_str );
+
+    if (status != RPC_S_OK)
+    {
+        ERR("Couldn't connect to services.exe, error %d\n", status);
+        return NULL;
+    }
+
+    return rpc_handle;
+}
+
+DECLSPEC_HIDDEN handle_t __RPC_USER MACHINE_HANDLEA_bind( MACHINE_HANDLEA name )
+{
+    return rpc_cstr_bind( (RPC_CSTR)name );
+}
+
+DECLSPEC_HIDDEN void __RPC_USER MACHINE_HANDLEA_unbind( MACHINE_HANDLEA name, handle_t h )
+{
+    RpcBindingFree( &h );
+}
+
+DECLSPEC_HIDDEN handle_t __RPC_USER MACHINE_HANDLEW_bind( MACHINE_HANDLEW name )
+{
+    return rpc_wstr_bind( (RPC_WSTR)name );
+}
+
+DECLSPEC_HIDDEN void __RPC_USER MACHINE_HANDLEW_unbind( MACHINE_HANDLEW name, handle_t h )
+{
+    RpcBindingFree( &h );
+}
+
+DECLSPEC_HIDDEN handle_t __RPC_USER SVCCTL_HANDLEW_bind( SVCCTL_HANDLEW name )
+{
+    return rpc_wstr_bind( (RPC_WSTR)name );
+}
+
+DECLSPEC_HIDDEN void __RPC_USER SVCCTL_HANDLEW_unbind( SVCCTL_HANDLEW name, handle_t h )
+{
+    RpcBindingFree( &h );
+}
+
+static BOOL set_error( DWORD err )
+{
+    if (err) SetLastError( err );
+    return !err;
+}
+
+/******************************************************************************
+ *     OpenSCManagerA   (sechost.@)
+ */
+SC_HANDLE WINAPI DECLSPEC_HOTPATCH OpenSCManagerA( const char *machine, const char *database, DWORD access )
+{
+    WCHAR *machineW, *databaseW;
+    SC_HANDLE ret;
+
+    machineW = heap_strdupAtoW( machine );
+    databaseW = heap_strdupAtoW( database );
+    ret = OpenSCManagerW( machineW, databaseW, access );
+    heap_free( databaseW );
+    heap_free( machineW );
+    return ret;
+}
+
+/******************************************************************************
+ *     OpenSCManagerW   (sechost.@)
+ */
+SC_HANDLE WINAPI DECLSPEC_HOTPATCH OpenSCManagerW( const WCHAR *machine, const WCHAR *database, DWORD access )
+{
+    SC_RPC_HANDLE handle = NULL;
+    DWORD err;
+
+    TRACE( "%s %s %#x\n", debugstr_w(machine), debugstr_w(database), access );
+
+    __TRY
+    {
+        err = svcctl_OpenSCManagerW( machine, database, access, &handle );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    if (!err) return handle;
+    SetLastError( err );
+    return NULL;
+}
+
+/******************************************************************************
+ *     OpenServiceA   (sechost.@)
+ */
+SC_HANDLE WINAPI DECLSPEC_HOTPATCH OpenServiceA( SC_HANDLE manager, const char *name, DWORD access )
+{
+    WCHAR *nameW;
+    SC_HANDLE ret;
+
+    TRACE( "%p %s %#x\n", manager, debugstr_a(name), access );
+
+    nameW = heap_strdupAtoW( name );
+    ret = OpenServiceW( manager, nameW, access );
+    heap_free( nameW );
+    return ret;
+}
+
+/******************************************************************************
+ *     OpenServiceW   (sechost.@)
+ */
+SC_HANDLE WINAPI DECLSPEC_HOTPATCH OpenServiceW( SC_HANDLE manager, const WCHAR *name, DWORD access )
+{
+    SC_RPC_HANDLE handle = NULL;
+    DWORD err;
+
+    TRACE( "%p %s %#x\n", manager, debugstr_w(name), access );
+
+    if (!manager)
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return NULL;
+    }
+
+    __TRY
+    {
+        err = svcctl_OpenServiceW( manager, name, access, &handle );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    if (!err) return handle;
+    SetLastError( err );
+    return 0;
+}
+
+/******************************************************************************
+ *     CreateServiceA   (sechost.@)
+ */
+SC_HANDLE WINAPI DECLSPEC_HOTPATCH CreateServiceA( SC_HANDLE manager, const char *name, const char *display_name,
+                                                   DWORD access, DWORD service_type, DWORD start_type,
+                                                   DWORD error_control, const char *path, const char *group,
+                                                   DWORD *tag, const char *dependencies, const char *username,
+                                                   const char *password )
+{
+    WCHAR *nameW, *display_nameW, *pathW, *groupW, *dependenciesW, *usernameW, *passwordW;
+    SC_HANDLE handle;
+
+    TRACE( "%p %s %s\n", manager, debugstr_a(name), debugstr_a(display_name) );
+
+    nameW = heap_strdupAtoW( name );
+    display_nameW = heap_strdupAtoW( display_name );
+    pathW = heap_strdupAtoW( path );
+    groupW = heap_strdupAtoW( group );
+    dependenciesW = heap_strdupAtoW( dependencies );
+    usernameW = heap_strdupAtoW( username );
+    passwordW = heap_strdupAtoW( password );
+
+    handle = CreateServiceW( manager, nameW, display_nameW, access, service_type, start_type, error_control,
+                             pathW, groupW, tag, dependenciesW, usernameW, passwordW );
+
+    heap_free( nameW );
+    heap_free( display_nameW );
+    heap_free( pathW );
+    heap_free( groupW );
+    heap_free( dependenciesW );
+    heap_free( usernameW );
+    heap_free( passwordW );
+
+    return handle;
+}
+
+/******************************************************************************
+ *     CreateServiceW   (sechost.@)
+ */
+SC_HANDLE WINAPI DECLSPEC_HOTPATCH CreateServiceW( SC_HANDLE manager, const WCHAR *name, const WCHAR *display_name,
+                                                   DWORD access, DWORD service_type, DWORD start_type,
+                                                   DWORD error_control, const WCHAR *path, const WCHAR *group,
+                                                   DWORD *tag, const WCHAR *dependencies, const WCHAR *username,
+                                                   const WCHAR *password )
+{
+    SC_RPC_HANDLE handle = NULL;
+    DWORD err;
+    SIZE_T password_size = 0;
+
+    TRACE( "%p %s %s\n", manager, debugstr_w(name), debugstr_w(display_name) );
+
+    if (!manager)
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return 0;
+    }
+
+    if (password) password_size = (wcslen(password) + 1) * sizeof(WCHAR);
+
+    __TRY
+    {
+        BOOL is_wow64;
+
+        if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)
+            err = svcctl_CreateServiceWOW64W( manager, name, display_name, access, service_type, start_type,
+                                              error_control, path, group, tag, (const BYTE *)dependencies,
+                                              multisz_size( dependencies ), username, (const BYTE *)password,
+                                              password_size, &handle );
+        else
+            err = svcctl_CreateServiceW( manager, name, display_name, access, service_type, start_type,
+                                         error_control, path, group, tag, (const BYTE *)dependencies,
+                                         multisz_size( dependencies ), username, (const BYTE *)password,
+                                         password_size, &handle );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    if (!err) return handle;
+    SetLastError( err );
+    return NULL;
+}
+
+/******************************************************************************
+ *     DeleteService   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH DeleteService( SC_HANDLE service )
+{
+    DWORD err;
+
+    TRACE( "%p\n", service );
+
+    __TRY
+    {
+        err = svcctl_DeleteService( service );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    return set_error( err );
+}
+
+/******************************************************************************
+ *     CloseServiceHandle   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH CloseServiceHandle( SC_HANDLE handle )
+{
+    DWORD err;
+
+    TRACE( "%p\n", handle );
+
+    __TRY
+    {
+        err = svcctl_CloseServiceHandle( (SC_RPC_HANDLE *)&handle );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    return set_error( err );
+}
+
+/******************************************************************************
+ *     ChangeServiceConfig2A   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH ChangeServiceConfig2A( SC_HANDLE service, DWORD level, void *info)
+{
+    BOOL r = FALSE;
+
+    TRACE( "%p %d %p\n", service, level, info );
+
+    if (level == SERVICE_CONFIG_DESCRIPTION)
+    {
+        SERVICE_DESCRIPTIONA *sd = info;
+        SERVICE_DESCRIPTIONW sdw;
+
+        sdw.lpDescription = heap_strdupAtoW( sd->lpDescription );
+
+        r = ChangeServiceConfig2W( service, level, &sdw );
+
+        heap_free( sdw.lpDescription );
+    }
+    else if (level == SERVICE_CONFIG_FAILURE_ACTIONS)
+    {
+        SERVICE_FAILURE_ACTIONSA *fa = info;
+        SERVICE_FAILURE_ACTIONSW faw;
+
+        faw.dwResetPeriod = fa->dwResetPeriod;
+        faw.lpRebootMsg = heap_strdupAtoW( fa->lpRebootMsg );
+        faw.lpCommand = heap_strdupAtoW( fa->lpCommand );
+        faw.cActions = fa->cActions;
+        faw.lpsaActions = fa->lpsaActions;
+
+        r = ChangeServiceConfig2W( service, level, &faw );
+
+        heap_free( faw.lpRebootMsg );
+        heap_free( faw.lpCommand );
+    }
+    else if (level == SERVICE_CONFIG_PRESHUTDOWN_INFO)
+    {
+        r = ChangeServiceConfig2W( service, level, info );
+    }
+    else
+        SetLastError( ERROR_INVALID_PARAMETER );
+
+    return r;
+}
+
+/******************************************************************************
+ *     ChangeServiceConfig2W   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH ChangeServiceConfig2W( SC_HANDLE service, DWORD level, void *info )
+{
+    SERVICE_RPC_REQUIRED_PRIVILEGES_INFO rpc_privinfo;
+    DWORD err;
+
+    __TRY
+    {
+        SC_RPC_CONFIG_INFOW rpc_info;
+
+        rpc_info.dwInfoLevel = level;
+        if (level == SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO)
+        {
+            SERVICE_REQUIRED_PRIVILEGES_INFOW *privinfo = info;
+
+            rpc_privinfo.cbRequiredPrivileges = multisz_size( privinfo->pmszRequiredPrivileges );
+            rpc_privinfo.pRequiredPrivileges = (BYTE *)privinfo->pmszRequiredPrivileges;
+            rpc_info.u.privinfo = &rpc_privinfo;
+        }
+        else
+            rpc_info.u.descr = info;
+        err = svcctl_ChangeServiceConfig2W( service, rpc_info );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    return set_error( err );
+}
+
+/******************************************************************************
+ *     ChangeServiceConfigA   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH ChangeServiceConfigA( SC_HANDLE service, DWORD service_type, DWORD start_type,
+                                                    DWORD error_control, const char *path, const char *group,
+                                                    DWORD *tag, const char *dependencies, const char *username,
+                                                    const char *password, const char *display_name )
+{
+    WCHAR *pathW, *groupW, *dependenciesW, *usernameW, *passwordW, *display_nameW;
+    BOOL r;
+
+    TRACE( "%p %d %d %d %s %s %p %p %s %s %s\n", service, service_type, start_type,
+           error_control, debugstr_a(path), debugstr_a(group), tag, dependencies,
+           debugstr_a(username), debugstr_a(password), debugstr_a(display_name) );
+
+    pathW = heap_strdupAtoW( path );
+    groupW = heap_strdupAtoW( group );
+    dependenciesW = heap_strdup_multi_AtoW( dependencies );
+    usernameW = heap_strdupAtoW( username );
+    passwordW = heap_strdupAtoW( password );
+    display_nameW = heap_strdupAtoW( display_name );
+
+    r = ChangeServiceConfigW( service, service_type, start_type, error_control, pathW,
+                              groupW, tag, dependenciesW, usernameW, passwordW, display_nameW );
+
+    heap_free( pathW );
+    heap_free( groupW );
+    heap_free( dependenciesW );
+    heap_free( usernameW );
+    heap_free( passwordW );
+    heap_free( display_nameW );
+
+    return r;
+}
+
+/******************************************************************************
+ *     ChangeServiceConfigW   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH ChangeServiceConfigW( SC_HANDLE service, DWORD service_type, DWORD start_type,
+                                                    DWORD error_control, const WCHAR *path, const WCHAR *group,
+                                                    DWORD *tag, const WCHAR *dependencies, const WCHAR *username,
+                                                    const WCHAR *password, const WCHAR *display_name )
+{
+    DWORD password_size;
+    DWORD err;
+
+    TRACE( "%p %d %d %d %s %s %p %p %s %s %s\n", service, service_type, start_type,
+           error_control, debugstr_w(path), debugstr_w(group), tag, dependencies,
+           debugstr_w(username), debugstr_w(password), debugstr_w(display_name) );
+
+    password_size = password ? (wcslen(password) + 1) * sizeof(WCHAR) : 0;
+
+    __TRY
+    {
+        err = svcctl_ChangeServiceConfigW( service, service_type, start_type, error_control, path, group, tag,
+                                           (const BYTE *)dependencies, multisz_size(dependencies), username,
+                                           (const BYTE *)password, password_size, display_name );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    return set_error( err );
+}
+
+/******************************************************************************
+ *     QueryServiceConfigA   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH QueryServiceConfigA( SC_HANDLE service, QUERY_SERVICE_CONFIGA *config,
+                                                   DWORD size, DWORD *ret_size )
+{
+    DWORD n;
+    char *p, *buffer;
+    BOOL ret;
+    QUERY_SERVICE_CONFIGW *configW;
+
+    TRACE( "%p %p %d %p\n", service, config, size, ret_size );
+
+    if (!(buffer = heap_alloc( 2 * size ))) return set_error( ERROR_NOT_ENOUGH_MEMORY );
+    configW = (QUERY_SERVICE_CONFIGW *)buffer;
+    ret = QueryServiceConfigW( service, configW, 2 * size, ret_size );
+    if (!ret) goto done;
+
+    config->dwServiceType      = configW->dwServiceType;
+    config->dwStartType        = configW->dwStartType;
+    config->dwErrorControl     = configW->dwErrorControl;
+    config->lpBinaryPathName   = NULL;
+    config->lpLoadOrderGroup   = NULL;
+    config->dwTagId            = configW->dwTagId;
+    config->lpDependencies     = NULL;
+    config->lpServiceStartName = NULL;
+    config->lpDisplayName      = NULL;
+
+    p = (char *)(config + 1);
+    n = size - sizeof(*config);
+    ret = FALSE;
+
+#define MAP_STR(str) \
+    do { \
+        if (configW->str) \
+        { \
+            DWORD sz = WideCharToMultiByte( CP_ACP, 0, configW->str, -1, p, n, NULL, NULL ); \
+            if (!sz) goto done; \
+            config->str = p; \
+            p += sz; \
+            n -= sz; \
+        } \
+    } while (0)
+
+    MAP_STR( lpBinaryPathName );
+    MAP_STR( lpLoadOrderGroup );
+    MAP_STR( lpDependencies );
+    MAP_STR( lpServiceStartName );
+    MAP_STR( lpDisplayName );
+#undef MAP_STR
+
+    *ret_size = p - (char *)config;
+    ret = TRUE;
+
+done:
+    heap_free( buffer );
+    return ret;
+}
+
+static DWORD move_string_to_buffer(BYTE **buf, WCHAR **string_ptr)
+{
+    DWORD cb;
+
+    if (!*string_ptr)
+    {
+        cb = sizeof(WCHAR);
+        memset(*buf, 0, cb);
+    }
+    else
+    {
+        cb = (wcslen( *string_ptr ) + 1) * sizeof(WCHAR);
+        memcpy(*buf, *string_ptr, cb);
+        MIDL_user_free( *string_ptr );
+    }
+
+    *string_ptr = (WCHAR *)*buf;
+    *buf += cb;
+
+    return cb;
+}
+
+static DWORD size_string( const WCHAR *string )
+{
+    return (string ? (wcslen( string ) + 1) * sizeof(WCHAR) : sizeof(WCHAR));
+}
+
+/******************************************************************************
+ *     QueryServiceConfigW   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH QueryServiceConfigW( SC_HANDLE service, QUERY_SERVICE_CONFIGW *ret_config,
+                                                   DWORD size, DWORD *ret_size )
+{
+    QUERY_SERVICE_CONFIGW config;
+    DWORD total;
+    DWORD err;
+    BYTE *bufpos;
+
+    TRACE( "%p %p %d %p\n", service, ret_config, size, ret_size );
+
+    memset(&config, 0, sizeof(config));
+
+    __TRY
+    {
+        err = svcctl_QueryServiceConfigW( service, &config, size, ret_size );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    if (err) return set_error( err );
+
+    /* calculate the size required first */
+    total = sizeof(QUERY_SERVICE_CONFIGW);
+    total += size_string( config.lpBinaryPathName );
+    total += size_string( config.lpLoadOrderGroup );
+    total += size_string( config.lpDependencies );
+    total += size_string( config.lpServiceStartName );
+    total += size_string( config.lpDisplayName );
+
+    *ret_size = total;
+
+    /* if there's not enough memory, return an error */
+    if (size < total)
+    {
+        SetLastError( ERROR_INSUFFICIENT_BUFFER );
+        MIDL_user_free( config.lpBinaryPathName );
+        MIDL_user_free( config.lpLoadOrderGroup );
+        MIDL_user_free( config.lpDependencies );
+        MIDL_user_free( config.lpServiceStartName );
+        MIDL_user_free( config.lpDisplayName );
+        return FALSE;
+    }
+
+    *ret_config = config;
+    bufpos = ((BYTE *)ret_config) + sizeof(QUERY_SERVICE_CONFIGW);
+    move_string_to_buffer( &bufpos, &ret_config->lpBinaryPathName );
+    move_string_to_buffer( &bufpos, &ret_config->lpLoadOrderGroup );
+    move_string_to_buffer( &bufpos, &ret_config->lpDependencies );
+    move_string_to_buffer( &bufpos, &ret_config->lpServiceStartName );
+    move_string_to_buffer( &bufpos, &ret_config->lpDisplayName );
+
+    TRACE( "Image path           = %s\n", debugstr_w( ret_config->lpBinaryPathName ) );
+    TRACE( "Group                = %s\n", debugstr_w( ret_config->lpLoadOrderGroup ) );
+    TRACE( "Dependencies         = %s\n", debugstr_w( ret_config->lpDependencies ) );
+    TRACE( "Service account name = %s\n", debugstr_w( ret_config->lpServiceStartName ) );
+    TRACE( "Display name         = %s\n", debugstr_w( ret_config->lpDisplayName ) );
+
+    return TRUE;
+}
+
+/******************************************************************************
+ *     QueryServiceConfig2A   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH QueryServiceConfig2A( SC_HANDLE service, DWORD level, BYTE *buffer,
+                                                    DWORD size, DWORD *ret_size )
+{
+    BYTE *bufferW = NULL;
+
+    TRACE( "%p %u %p %u %p\n", service, level, buffer, size, ret_size );
+
+    if (buffer && size)
+        bufferW = heap_alloc( size );
+
+    if (!QueryServiceConfig2W( service, level, bufferW, size, ret_size ))
+    {
+        heap_free( bufferW );
+        return FALSE;
+    }
+
+    switch (level)
+    {
+        case SERVICE_CONFIG_DESCRIPTION:
+            if (buffer && bufferW) {
+                SERVICE_DESCRIPTIONA *configA = (SERVICE_DESCRIPTIONA *)buffer;
+                SERVICE_DESCRIPTIONW *configW = (SERVICE_DESCRIPTIONW *)bufferW;
+                if (configW->lpDescription && size > sizeof(SERVICE_DESCRIPTIONA))
+                {
+                    configA->lpDescription = (char *)(configA + 1);
+                    WideCharToMultiByte( CP_ACP, 0, configW->lpDescription, -1, configA->lpDescription,
+                                         size - sizeof(SERVICE_DESCRIPTIONA), NULL, NULL );
+                }
+                else configA->lpDescription = NULL;
+            }
+            break;
+        case SERVICE_CONFIG_PRESHUTDOWN_INFO:
+            if (buffer && bufferW && *ret_size <= size)
+                memcpy(buffer, bufferW, *ret_size);
+            break;
+        default:
+            FIXME("conversion W->A not implemented for level %d\n", level);
+            heap_free( bufferW );
+            return FALSE;
+    }
+
+    heap_free( bufferW );
+    return TRUE;
+}
+
+/******************************************************************************
+ *     QueryServiceConfig2W   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH QueryServiceConfig2W( SC_HANDLE service, DWORD level, BYTE *buffer,
+                                                    DWORD size, DWORD *ret_size )
+{
+    BYTE *bufptr;
+    DWORD err;
+
+    TRACE( "%p %u %p %u %p\n", service, level, buffer, size, ret_size );
+
+    if (!buffer && size)
+    {
+        SetLastError(ERROR_INVALID_ADDRESS);
+        return FALSE;
+    }
+
+    switch (level)
+    {
+    case SERVICE_CONFIG_DESCRIPTION:
+        if (!(bufptr = heap_alloc( size )))
+        {
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            return FALSE;
+        }
+        break;
+
+    case SERVICE_CONFIG_PRESHUTDOWN_INFO:
+        bufptr = buffer;
+        break;
+
+    default:
+        FIXME("Level %d not implemented\n", level);
+        SetLastError(ERROR_INVALID_LEVEL);
+        return FALSE;
+    }
+
+    if (!ret_size)
+    {
+        if (level == SERVICE_CONFIG_DESCRIPTION) heap_free( bufptr );
+        SetLastError(ERROR_INVALID_ADDRESS);
+        return FALSE;
+    }
+
+    __TRY
+    {
+        err = svcctl_QueryServiceConfig2W( service, level, bufptr, size, ret_size );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    switch (level)
+    {
+    case SERVICE_CONFIG_DESCRIPTION:
+    {
+        SERVICE_DESCRIPTIONW *desc = (SERVICE_DESCRIPTIONW *)buffer;
+        struct service_description *s = (struct service_description *)bufptr;
+
+        if (err != ERROR_SUCCESS && err != ERROR_INSUFFICIENT_BUFFER)
+        {
+            heap_free( bufptr );
+            SetLastError( err );
+            return FALSE;
+        }
+
+        /* adjust for potentially larger SERVICE_DESCRIPTIONW structure */
+        if (*ret_size == sizeof(*s))
+            *ret_size = sizeof(*desc);
+        else
+            *ret_size = *ret_size - FIELD_OFFSET(struct service_description, description) + sizeof(*desc);
+
+        if (size < *ret_size)
+        {
+            heap_free( bufptr );
+            SetLastError( ERROR_INSUFFICIENT_BUFFER );
+            return FALSE;
+        }
+        if (desc)
+        {
+            if (!s->size) desc->lpDescription = NULL;
+            else
+            {
+                desc->lpDescription = (WCHAR *)(desc + 1);
+                memcpy( desc->lpDescription, s->description, s->size );
+            }
+        }
+        heap_free( bufptr );
+        break;
+    }
+    case SERVICE_CONFIG_PRESHUTDOWN_INFO:
+        return set_error( err );
+
+    default:
+        break;
+    }
+
+    return TRUE;
+}
+
+/******************************************************************************
+ *     GetServiceDisplayNameW   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH GetServiceDisplayNameW( SC_HANDLE manager, const WCHAR *service,
+                                                      WCHAR *display_name, DWORD *len )
+{
+    DWORD err;
+    DWORD size;
+    WCHAR buffer[2];
+
+    TRACE( "%p %s %p %p\n", manager, debugstr_w(service), display_name, len );
+
+    if (!manager)
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return FALSE;
+    }
+
+    /* provide a buffer if the caller didn't */
+    if (!display_name || *len < sizeof(WCHAR))
+    {
+        display_name = buffer;
+        /* A size of 1 would be enough, but tests show that Windows returns 2,
+         * probably because of a WCHAR/bytes mismatch in their code. */
+        *len = 2;
+    }
+
+    /* RPC call takes size excluding nul-terminator, whereas *len
+     * includes the nul-terminator on input. */
+    size = *len - 1;
+
+    __TRY
+    {
+        err = svcctl_GetServiceDisplayNameW( manager, service, display_name, &size );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    /* The value of *len excludes nul-terminator on output. */
+    if (err == ERROR_SUCCESS || err == ERROR_INSUFFICIENT_BUFFER)
+        *len = size;
+    return set_error( err );
+}
+
+/******************************************************************************
+ *     GetServiceKeyNameW   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH GetServiceKeyNameW( SC_HANDLE manager, const WCHAR *display_name,
+                                                  WCHAR *key_name, DWORD *len )
+{
+    DWORD err;
+    WCHAR buffer[2];
+    DWORD size;
+
+    TRACE( "%p %s %p %p\n", manager, debugstr_w(display_name), key_name, len );
+
+    if (!manager)
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return FALSE;
+    }
+
+    /* provide a buffer if the caller didn't */
+    if (!key_name || *len < 2)
+    {
+        key_name = buffer;
+        /* A size of 1 would be enough, but tests show that Windows returns 2,
+         * probably because of a WCHAR/bytes mismatch in their code.
+         */
+        *len = 2;
+    }
+
+    /* RPC call takes size excluding nul-terminator, whereas *len
+     * includes the nul-terminator on input. */
+    size = *len - 1;
+
+    __TRY
+    {
+        err = svcctl_GetServiceKeyNameW( manager, display_name, key_name, &size );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    /* The value of *lpcchBuffer excludes nul-terminator on output. */
+    if (err == ERROR_SUCCESS || err == ERROR_INSUFFICIENT_BUFFER)
+        *len = size;
+    return set_error( err );
+}
+
+/******************************************************************************
+ *     StartServiceA   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH StartServiceA( SC_HANDLE service, DWORD argc, const char **argv )
+{
+    WCHAR **argvW = NULL;
+    DWORD i;
+    BOOL r;
+
+    if (argc)
+        argvW = heap_alloc( argc * sizeof(WCHAR) );
+
+    for (i = 0; i < argc; i++)
+        argvW[i] = heap_strdupAtoW( argv[i] );
+
+    r = StartServiceW( service, argc, (const WCHAR **)argvW );
+
+    for (i = 0; i < argc; i++)
+        heap_free( argvW[i] );
+    heap_free( argv );
+    return r;
+}
+
+
+/******************************************************************************
+ *     StartServiceW   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH StartServiceW( SC_HANDLE service, DWORD argc, const WCHAR **argv )
+{
+    DWORD err;
+
+    TRACE( "%p %u %p\n", service, argc, argv );
+
+    __TRY
+    {
+        err = svcctl_StartServiceW( service, argc, argv );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    return set_error( err );
+}
+
+/******************************************************************************
+ *     ControlService   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH ControlService( SC_HANDLE service, DWORD control, SERVICE_STATUS *status )
+{
+    DWORD err;
+
+    TRACE( "%p %d %p\n", service, control, status );
+
+    __TRY
+    {
+        err = svcctl_ControlService( service, control, status );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    return set_error( err );
+}
+
+/******************************************************************************
+ *     QueryServiceStatus   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH QueryServiceStatus( SC_HANDLE service, SERVICE_STATUS *status )
+{
+    SERVICE_STATUS_PROCESS process_status;
+    BOOL ret;
+    DWORD size;
+
+    TRACE( "%p %p\n", service, status );
+
+    if (!service) return set_error( ERROR_INVALID_HANDLE );
+    if (!status) return set_error( ERROR_INVALID_ADDRESS );
+
+    ret = QueryServiceStatusEx( service, SC_STATUS_PROCESS_INFO, (BYTE *)&process_status,
+                                sizeof(SERVICE_STATUS_PROCESS), &size );
+    if (ret) memcpy(status, &process_status, sizeof(SERVICE_STATUS) );
+    return ret;
+}
+
+/******************************************************************************
+ *     QueryServiceStatusEx   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH QueryServiceStatusEx( SC_HANDLE service, SC_STATUS_TYPE level,
+                                                    BYTE *buffer, DWORD size, DWORD *ret_size )
+{
+    DWORD err;
+
+    TRACE( "%p %d %p %d %p\n", service, level, buffer, size, ret_size );
+
+    if (level != SC_STATUS_PROCESS_INFO) return set_error( ERROR_INVALID_LEVEL );
+
+    if (size < sizeof(SERVICE_STATUS_PROCESS))
+    {
+        *ret_size = sizeof(SERVICE_STATUS_PROCESS);
+        return set_error( ERROR_INSUFFICIENT_BUFFER );
+    }
+
+    __TRY
+    {
+        err = svcctl_QueryServiceStatusEx( service, level, buffer, size, ret_size );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    return set_error( err );
+}
+
+/******************************************************************************
+ *     EnumServicesStatusExW   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH EnumServicesStatusExW( SC_HANDLE manager, SC_ENUM_TYPE level, DWORD type, DWORD state,
+                                                     BYTE *buffer, DWORD size, DWORD *needed, DWORD *returned,
+                                                     DWORD *resume_handle, const WCHAR *group )
+{
+    DWORD err, i, offset, buflen, count, total_size = 0;
+    ENUM_SERVICE_STATUS_PROCESSW *services = (ENUM_SERVICE_STATUS_PROCESSW *)buffer;
+    struct enum_service_status_process *entry;
+    const WCHAR *str;
+    BYTE *buf;
+
+    TRACE( "%p %u 0x%x 0x%x %p %u %p %p %p %s\n", manager, level, type, state, buffer,
+           size, needed, returned, resume_handle, debugstr_w(group) );
+
+    if (level != SC_ENUM_PROCESS_INFO) return set_error( ERROR_INVALID_LEVEL );
+    if (!manager) return set_error( ERROR_INVALID_HANDLE );
+    if (!needed || !returned) return set_error( ERROR_INVALID_ADDRESS );
+
+    /* make sure we pass a valid pointer */
+    buflen = max( size, sizeof(*services) );
+    if (!(buf = heap_alloc( buflen ))) return set_error( ERROR_NOT_ENOUGH_MEMORY );
+
+    __TRY
+    {
+        err = svcctl_EnumServicesStatusExW( manager, SC_ENUM_PROCESS_INFO, type, state, buf, buflen, needed,
+                                            &count, resume_handle, group );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    *returned = 0;
+    if (err != ERROR_SUCCESS)
+    {
+        /* double the needed size to fit the potentially larger ENUM_SERVICE_STATUS_PROCESSW */
+        if (err == ERROR_MORE_DATA) *needed *= 2;
+        heap_free( buf );
+        SetLastError( err );
+        return FALSE;
+    }
+
+    entry = (struct enum_service_status_process *)buf;
+    for (i = 0; i < count; i++)
+    {
+        total_size += sizeof(*services);
+        if (entry->service_name)
+        {
+            str = (const WCHAR *)(buf + entry->service_name);
+            total_size += (wcslen( str ) + 1) * sizeof(WCHAR);
+        }
+        if (entry->display_name)
+        {
+            str = (const WCHAR *)(buf + entry->display_name);
+            total_size += (wcslen( str ) + 1) * sizeof(WCHAR);
+        }
+        entry++;
+    }
+
+    if (total_size > size)
+    {
+        heap_free( buf );
+        *needed = total_size;
+        SetLastError( ERROR_MORE_DATA );
+        return FALSE;
+    }
+
+    offset = count * sizeof(*services);
+    entry = (struct enum_service_status_process *)buf;
+    for (i = 0; i < count; i++)
+    {
+        DWORD str_size;
+        str = (const WCHAR *)(buf + entry->service_name);
+        str_size = (wcslen( str ) + 1) * sizeof(WCHAR);
+        services[i].lpServiceName = (WCHAR *)((char *)services + offset);
+        memcpy( services[i].lpServiceName, str, str_size );
+        offset += str_size;
+
+        if (!entry->display_name) services[i].lpDisplayName = NULL;
+        else
+        {
+            str = (const WCHAR *)(buf + entry->display_name);
+            str_size = (wcslen( str ) + 1) * sizeof(WCHAR);
+            services[i].lpDisplayName = (WCHAR *)((char *)services + offset);
+            memcpy( services[i].lpDisplayName, str, str_size );
+            offset += str_size;
+        }
+        services[i].ServiceStatusProcess = entry->service_status_process;
+        entry++;
+    }
+
+    heap_free( buf );
+    *needed = 0;
+    *returned = count;
+    return TRUE;
+}
+
+/******************************************************************************
+ *     EnumDependentServicesW   (sechost.@)
+ */
+BOOL WINAPI EnumDependentServicesW( SC_HANDLE hService, DWORD dwServiceState,
+                                    LPENUM_SERVICE_STATUSW lpServices, DWORD cbBufSize,
+                                    LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned )
+{
+    FIXME("%p 0x%08x %p 0x%08x %p %p - stub\n", hService, dwServiceState,
+          lpServices, cbBufSize, pcbBytesNeeded, lpServicesReturned);
+
+    *lpServicesReturned = 0;
+    return TRUE;
+}
+
+/******************************************************************************
+ *     QueryServiceObjectSecurity   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH QueryServiceObjectSecurity( SC_HANDLE service, SECURITY_INFORMATION type,
+                                                          PSECURITY_DESCRIPTOR ret_descriptor, DWORD size, DWORD *ret_size )
+{
+    SECURITY_DESCRIPTOR descriptor;
+    NTSTATUS status;
+    ACL acl;
+
+    FIXME( "%p %d %p %u %p - semi-stub\n", service, type, ret_descriptor, size, ret_size );
+
+    if (type != DACL_SECURITY_INFORMATION)
+        FIXME("information %d not supported\n", type);
+
+    InitializeSecurityDescriptor( &descriptor, SECURITY_DESCRIPTOR_REVISION );
+
+    InitializeAcl( &acl, sizeof(ACL), ACL_REVISION );
+    SetSecurityDescriptorDacl( &descriptor, TRUE, &acl, TRUE );
+
+    status = RtlMakeSelfRelativeSD( &descriptor, ret_descriptor, &size );
+    *ret_size = size;
+
+    return set_error( RtlNtStatusToDosError( status ) );
+}
+
+/******************************************************************************
+ *     SetServiceObjectSecurity   (sechost.@)
+ */
+BOOL WINAPI SetServiceObjectSecurity(SC_HANDLE hService,
+       SECURITY_INFORMATION dwSecurityInformation,
+       PSECURITY_DESCRIPTOR lpSecurityDescriptor)
+{
+    FIXME("%p %d %p\n", hService, dwSecurityInformation, lpSecurityDescriptor);
+    return TRUE;
+}
+
+static DWORD WINAPI notify_thread(void *user)
+{
+    DWORD err;
+    struct notify_data *data = user;
+    SC_RPC_NOTIFY_PARAMS_LIST *list = NULL;
+    SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2 *cparams;
+    BOOL dummy;
+
+    __TRY
+    {
+        /* GetNotifyResults blocks until there is an event */
+        err = svcctl_GetNotifyResults(data->notify_handle, &list);
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code(GetExceptionCode());
+    }
+    __ENDTRY
+
+    EnterCriticalSection( &service_cs );
+
+    list_remove(&data->entry);
+
+    LeaveCriticalSection( &service_cs );
+
+    if (err == ERROR_SUCCESS && list)
+    {
+        cparams = list->NotifyParamsArray[0].u.params;
+
+        data->notify_buffer->dwNotificationStatus = cparams->dwNotificationStatus;
+        memcpy(&data->notify_buffer->ServiceStatus, &cparams->ServiceStatus,
+                sizeof(SERVICE_STATUS_PROCESS));
+        data->notify_buffer->dwNotificationTriggered = cparams->dwNotificationTriggered;
+        data->notify_buffer->pszServiceNames = NULL;
+
+        QueueUserAPC((PAPCFUNC)data->notify_buffer->pfnNotifyCallback,
+                data->calling_thread, (ULONG_PTR)data->notify_buffer);
+
+        HeapFree(GetProcessHeap(), 0, list);
+    }
+    else
+        WARN("GetNotifyResults server call failed: %u\n", err);
+
+
+    __TRY
+    {
+        err = svcctl_CloseNotifyHandle(&data->notify_handle, &dummy);
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code(GetExceptionCode());
+    }
+    __ENDTRY
+
+    if (err != ERROR_SUCCESS)
+        WARN("CloseNotifyHandle server call failed: %u\n", err);
+
+    CloseHandle(data->calling_thread);
+    HeapFree(GetProcessHeap(), 0, data);
+
+    return 0;
+}
+
+/******************************************************************************
+ *     NotifyServiceStatusChangeW   (sechost.@)
+ */
+DWORD WINAPI DECLSPEC_HOTPATCH NotifyServiceStatusChangeW( SC_HANDLE service, DWORD mask,
+                                                           SERVICE_NOTIFYW *notify_buffer )
+{
+    DWORD err;
+    BOOL b_dummy = FALSE;
+    GUID g_dummy = {0};
+    struct notify_data *data;
+
+    TRACE( "%p 0x%x %p\n", service, mask, notify_buffer );
+
+    if (!(data = heap_alloc_zero( sizeof(*data) )))
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    data->service = service;
+    data->notify_buffer = notify_buffer;
+    if (!DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
+                          &data->calling_thread, 0, FALSE, DUPLICATE_SAME_ACCESS ))
+    {
+        ERR("DuplicateHandle failed: %u\n", GetLastError());
+        heap_free( data );
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
+
+    data->params.dwInfoLevel = 2;
+    data->params.u.params = &data->cparams;
+
+    data->cparams.dwNotifyMask = mask;
+
+    EnterCriticalSection( &service_cs );
+
+    __TRY
+    {
+        err = svcctl_NotifyServiceStatusChange( service, data->params, &g_dummy,
+                                                &g_dummy, &b_dummy, &data->notify_handle );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    if (err != ERROR_SUCCESS)
+    {
+        WARN("NotifyServiceStatusChange server call failed: %u\n", err);
+        LeaveCriticalSection( &service_cs );
+        CloseHandle( data->calling_thread );
+        CloseHandle( data->ready_evt );
+        heap_free( data );
+        return err;
+    }
+
+    CloseHandle( CreateThread( NULL, 0, &notify_thread, data, 0, NULL ) );
+
+    list_add_tail( &notify_list, &data->entry );
+
+    LeaveCriticalSection( &service_cs );
+
+    return ERROR_SUCCESS;
+}
+
+/* thunk for calling the RegisterServiceCtrlHandler handler function */
+static DWORD WINAPI ctrl_handler_thunk( DWORD control, DWORD type, void *data, void *context )
+{
+    LPHANDLER_FUNCTION func = context;
+
+    func( control );
+    return ERROR_SUCCESS;
+}
+
+/******************************************************************************
+ *     RegisterServiceCtrlHandlerA   (sechost.@)
+ */
+SERVICE_STATUS_HANDLE WINAPI DECLSPEC_HOTPATCH RegisterServiceCtrlHandlerA(
+        const char *name, LPHANDLER_FUNCTION handler )
+{
+    return RegisterServiceCtrlHandlerExA( name, ctrl_handler_thunk, handler );
+}
+
+/******************************************************************************
+ *     RegisterServiceCtrlHandlerW   (sechost.@)
+ */
+SERVICE_STATUS_HANDLE WINAPI DECLSPEC_HOTPATCH RegisterServiceCtrlHandlerW(
+        const WCHAR *name, LPHANDLER_FUNCTION handler )
+{
+    return RegisterServiceCtrlHandlerExW( name, ctrl_handler_thunk, handler );
+}
+
+/******************************************************************************
+ *     RegisterServiceCtrlHandlerExA   (sechost.@)
+ */
+SERVICE_STATUS_HANDLE WINAPI DECLSPEC_HOTPATCH RegisterServiceCtrlHandlerExA(
+        const char *name, LPHANDLER_FUNCTION_EX handler, void *context )
+{
+    WCHAR *nameW;
+    SERVICE_STATUS_HANDLE ret;
+
+    nameW = heap_strdupAtoW( name );
+    ret = RegisterServiceCtrlHandlerExW( nameW, handler, context );
+    heap_free( nameW );
+    return ret;
+}
+
+static struct service_data *find_service_by_name( const WCHAR *name )
+{
+    unsigned int i;
+
+    if (nb_services == 1)  /* only one service (FIXME: should depend on OWN_PROCESS etc.) */
+        return services[0];
+    for (i = 0; i < nb_services; i++)
+        if (!wcsicmp( name, services[i]->name )) return services[i];
+    return NULL;
+}
+
+/******************************************************************************
+ *     RegisterServiceCtrlHandlerExW   (sechost.@)
+ */
+SERVICE_STATUS_HANDLE WINAPI DECLSPEC_HOTPATCH RegisterServiceCtrlHandlerExW(
+        const WCHAR *name, LPHANDLER_FUNCTION_EX handler, void *context )
+{
+    struct service_data *service;
+    SC_HANDLE handle = 0;
+
+    TRACE( "%s %p %p\n", debugstr_w(name), handler, context );
+
+    EnterCriticalSection( &service_cs );
+    if ((service = find_service_by_name( name )))
+    {
+        service->handler = handler;
+        service->context = context;
+        handle = service->handle;
+    }
+    LeaveCriticalSection( &service_cs );
+
+    if (!handle) SetLastError( ERROR_SERVICE_DOES_NOT_EXIST );
+    return (SERVICE_STATUS_HANDLE)handle;
+}
+
+/******************************************************************************
+ *     SetServiceStatus   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH SetServiceStatus( SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status )
+{
+    DWORD err;
+
+    TRACE( "%p %#x %#x %#x %#x %#x %#x %#x\n", service, status->dwServiceType,
+           status->dwCurrentState, status->dwControlsAccepted, status->dwWin32ExitCode,
+           status->dwServiceSpecificExitCode, status->dwCheckPoint, status->dwWaitHint );
+
+    __TRY
+    {
+        err = svcctl_SetServiceStatus( service, status );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    if (!set_error( err ))
+        return FALSE;
+
+    if (status->dwCurrentState == SERVICE_STOPPED)
+    {
+        unsigned int i, count = 0;
+        EnterCriticalSection( &service_cs );
+        for (i = 0; i < nb_services; i++)
+        {
+            if (services[i]->handle == (SC_HANDLE)service) continue;
+            if (services[i]->thread) count++;
+        }
+        if (!count)
+        {
+            stop_service = TRUE;
+            SetEvent( service_event );  /* notify the main loop */
+        }
+        LeaveCriticalSection( &service_cs );
+    }
+
+    return TRUE;
+}
+
+static WCHAR *service_get_pipe_name(void)
+{
+    static const WCHAR format[] = L"\\\\.\\pipe\\net\\NtControlPipe%u";
+    WCHAR *name;
+    DWORD len;
+    HKEY service_current_key;
+    DWORD service_current;
+    LONG ret;
+    DWORD type;
+
+    ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+                         L"SYSTEM\\CurrentControlSet\\Control\\ServiceCurrent",
+                         0, KEY_QUERY_VALUE, &service_current_key );
+    if (ret != ERROR_SUCCESS)
+        return NULL;
+
+    len = sizeof(service_current);
+    ret = RegQueryValueExW( service_current_key, NULL, NULL, &type,
+                            (BYTE *)&service_current, &len );
+    RegCloseKey(service_current_key);
+    if (ret != ERROR_SUCCESS || type != REG_DWORD)
+        return NULL;
+
+    len = ARRAY_SIZE(format) + 10 /* strlenW("4294967295") */;
+    name = heap_alloc(len * sizeof(WCHAR));
+    if (!name)
+        return NULL;
+
+    swprintf( name, len, format, service_current );
+    return name;
+}
+
+static HANDLE service_open_pipe(void)
+{
+    WCHAR *pipe_name = service_get_pipe_name();
+    HANDLE handle = INVALID_HANDLE_VALUE;
+
+    do
+    {
+        handle = CreateFileW( pipe_name, GENERIC_READ|GENERIC_WRITE,
+                              0, NULL, OPEN_ALWAYS, 0, NULL );
+        if (handle != INVALID_HANDLE_VALUE)
+            break;
+        if (GetLastError() != ERROR_PIPE_BUSY)
+            break;
+    } while (WaitNamedPipeW( pipe_name, NMPWAIT_USE_DEFAULT_WAIT ));
+    heap_free(pipe_name);
+
+    return handle;
+}
+
+static DWORD WINAPI service_thread( void *arg )
+{
+    struct service_data *info = arg;
+    WCHAR *str = info->args;
+    DWORD argc = 0, len = 0;
+
+    TRACE("%p\n", arg);
+
+    while (str[len])
+    {
+        len += wcslen( &str[len] ) + 1;
+        argc++;
+    }
+    len++;
+
+    if (info->unicode)
+    {
+        WCHAR **argv, *p;
+
+        argv = heap_alloc( (argc+1)*sizeof(*argv) );
+        for (argc = 0, p = str; *p; p += wcslen( p ) + 1)
+            argv[argc++] = p;
+        argv[argc] = NULL;
+
+        info->proc.w( argc, argv );
+        heap_free( argv );
+    }
+    else
+    {
+        char *strA, **argv, *p;
+        DWORD lenA;
+
+        lenA = WideCharToMultiByte( CP_ACP,0, str, len, NULL, 0, NULL, NULL );
+        strA = heap_alloc(lenA);
+        WideCharToMultiByte(CP_ACP,0, str, len, strA, lenA, NULL, NULL);
+
+        argv = heap_alloc( (argc+1)*sizeof(*argv) );
+        for (argc = 0, p = strA; *p; p += strlen( p ) + 1)
+            argv[argc++] = p;
+        argv[argc] = NULL;
+
+        info->proc.a( argc, argv );
+        heap_free( argv );
+        heap_free( strA );
+    }
+    return 0;
+}
+
+static DWORD service_handle_start( struct service_data *service, const void *data, DWORD data_size )
+{
+    DWORD count = data_size / sizeof(WCHAR);
+
+    if (service->thread)
+    {
+        WARN("service is not stopped\n");
+        return ERROR_SERVICE_ALREADY_RUNNING;
+    }
+
+    heap_free( service->args );
+    service->args = heap_alloc( (count + 2) * sizeof(WCHAR) );
+    if (count) memcpy( service->args, data, count * sizeof(WCHAR) );
+    service->args[count++] = 0;
+    service->args[count++] = 0;
+
+    service->thread = CreateThread( NULL, 0, service_thread,
+                                    service, 0, NULL );
+    SetEvent( service_event );  /* notify the main loop */
+    return 0;
+}
+
+static DWORD service_handle_control( struct service_data *service, DWORD control, const void *data, DWORD data_size )
+{
+    DWORD ret = ERROR_INVALID_SERVICE_CONTROL;
+
+    TRACE( "%s control %u data %p data_size %u\n", debugstr_w(service->name), control, data, data_size );
+
+    if (control == SERVICE_CONTROL_START)
+        ret = service_handle_start( service, data, data_size );
+    else if (service->handler)
+        ret = service->handler( control, 0, (void *)data, service->context );
+    return ret;
+}
+
+static DWORD WINAPI service_control_dispatcher( void *arg )
+{
+    struct dispatcher_data *disp = arg;
+
+    /* dispatcher loop */
+    while (1)
+    {
+        struct service_data *service;
+        service_start_info info;
+        BYTE *data = NULL;
+        WCHAR *name;
+        BOOL r;
+        DWORD data_size = 0, count, result;
+
+        r = ReadFile( disp->pipe, &info, FIELD_OFFSET(service_start_info,data), &count, NULL );
+        if (!r)
+        {
+            if (GetLastError() != ERROR_BROKEN_PIPE)
+                ERR( "pipe read failed error %u\n", GetLastError() );
+            break;
+        }
+        if (count != FIELD_OFFSET(service_start_info,data))
+        {
+            ERR( "partial pipe read %u\n", count );
+            break;
+        }
+        if (count < info.total_size)
+        {
+            data_size = info.total_size - FIELD_OFFSET(service_start_info,data);
+            data = heap_alloc( data_size );
+            r = ReadFile( disp->pipe, data, data_size, &count, NULL );
+            if (!r)
+            {
+                if (GetLastError() != ERROR_BROKEN_PIPE)
+                    ERR( "pipe read failed error %u\n", GetLastError() );
+                heap_free( data );
+                break;
+            }
+            if (count != data_size)
+            {
+                ERR( "partial pipe read %u/%u\n", count, data_size );
+                heap_free( data );
+                break;
+            }
+        }
+
+        EnterCriticalSection( &service_cs );
+
+        /* validate service name */
+        name = (WCHAR *)data;
+        if (!info.name_size || data_size < info.name_size * sizeof(WCHAR) || name[info.name_size - 1])
+        {
+            ERR( "got request without valid service name\n" );
+            result = ERROR_INVALID_PARAMETER;
+            goto done;
+        }
+
+        if (info.magic != SERVICE_PROTOCOL_MAGIC)
+        {
+            ERR( "received invalid request for service %s\n", debugstr_w(name) );
+            result = ERROR_INVALID_PARAMETER;
+            goto done;
+        }
+
+        /* find the service */
+        if (!(service = find_service_by_name( name )))
+        {
+            FIXME( "got request for unknown service %s\n", debugstr_w(name) );
+            result = ERROR_INVALID_PARAMETER;
+            goto done;
+        }
+
+        if (!service->handle)
+        {
+            if (!(service->handle = OpenServiceW( disp->manager, name, SERVICE_SET_STATUS )) ||
+                !(service->full_access_handle = OpenServiceW( disp->manager, name,
+                        GENERIC_READ|GENERIC_WRITE )))
+                FIXME( "failed to open service %s\n", debugstr_w(name) );
+        }
+
+        data_size -= info.name_size * sizeof(WCHAR);
+        result = service_handle_control(service, info.control, data_size ?
+                                        &data[info.name_size * sizeof(WCHAR)] : NULL, data_size);
+
+    done:
+        LeaveCriticalSection( &service_cs );
+        WriteFile( disp->pipe, &result, sizeof(result), &count, NULL );
+        heap_free( data );
+    }
+
+    CloseHandle( disp->pipe );
+    CloseServiceHandle( disp->manager );
+    heap_free( disp );
+    return 1;
+}
+
+/* wait for services which accept this type of message to become STOPPED */
+static void handle_shutdown_msg(DWORD msg, DWORD accept)
+{
+    SERVICE_STATUS st;
+    SERVICE_PRESHUTDOWN_INFO spi;
+    DWORD i, n = 0, sz, timeout = 2000;
+    ULONGLONG stop_time;
+    BOOL res, done = TRUE;
+    SC_HANDLE *wait_handles = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SC_HANDLE) * nb_services );
+
+    EnterCriticalSection( &service_cs );
+    for (i = 0; i < nb_services; i++)
+    {
+        res = QueryServiceStatus( services[i]->full_access_handle, &st );
+        if (!res || st.dwCurrentState == SERVICE_STOPPED || !(st.dwControlsAccepted & accept))
+            continue;
+
+        done = FALSE;
+
+        if (accept == SERVICE_ACCEPT_PRESHUTDOWN)
+        {
+            res = QueryServiceConfig2W( services[i]->full_access_handle, SERVICE_CONFIG_PRESHUTDOWN_INFO,
+                                        (BYTE *)&spi, sizeof(spi), &sz );
+            if (res)
+            {
+                FIXME( "service should be able to delay shutdown\n" );
+                timeout = max( spi.dwPreshutdownTimeout, timeout );
+            }
+        }
+
+        service_handle_control( services[i], msg, NULL, 0 );
+        wait_handles[n++] = services[i]->full_access_handle;
+    }
+    LeaveCriticalSection( &service_cs );
+
+    /* FIXME: these timeouts should be more generous, but we can't currently delay prefix shutdown */
+    timeout = min( timeout, 3000 );
+    stop_time = GetTickCount64() + timeout;
+
+    while (!done && GetTickCount64() < stop_time)
+    {
+        done = TRUE;
+        for (i = 0; i < n; i++)
+        {
+            res = QueryServiceStatus( wait_handles[i], &st );
+            if (!res || st.dwCurrentState == SERVICE_STOPPED)
+                continue;
+
+            done = FALSE;
+            Sleep( 100 );
+            break;
+        }
+    }
+
+    HeapFree( GetProcessHeap(), 0, wait_handles );
+}
+
+static BOOL service_run_main_thread(void)
+{
+    DWORD i, n, ret;
+    HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
+    UINT wait_services[MAXIMUM_WAIT_OBJECTS];
+    struct dispatcher_data *disp = heap_alloc( sizeof(*disp) );
+
+    disp->manager = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
+    if (!disp->manager)
+    {
+        ERR("failed to open service manager error %u\n", GetLastError());
+        heap_free( disp );
+        return FALSE;
+    }
+
+    disp->pipe = service_open_pipe();
+    if (disp->pipe == INVALID_HANDLE_VALUE)
+    {
+        WARN("failed to create control pipe error %u\n", GetLastError());
+        CloseServiceHandle( disp->manager );
+        heap_free( disp );
+        SetLastError( ERROR_FAILED_SERVICE_CONTROLLER_CONNECT );
+        return FALSE;
+    }
+
+    service_event = CreateEventW( NULL, FALSE, FALSE, NULL );
+    stop_service  = FALSE;
+
+    /* FIXME: service_control_dispatcher should be merged into the main thread */
+    wait_handles[0] = __wine_make_process_system();
+    wait_handles[1] = CreateThread( NULL, 0, service_control_dispatcher, disp, 0, NULL );
+    wait_handles[2] = service_event;
+
+    TRACE("Starting %d services running as process %d\n",
+          nb_services, GetCurrentProcessId());
+
+    /* wait for all the threads to pack up and exit */
+    while (!stop_service)
+    {
+        EnterCriticalSection( &service_cs );
+        for (i = 0, n = 3; i < nb_services && n < MAXIMUM_WAIT_OBJECTS; i++)
+        {
+            if (!services[i]->thread) continue;
+            wait_services[n] = i;
+            wait_handles[n++] = services[i]->thread;
+        }
+        LeaveCriticalSection( &service_cs );
+
+        ret = WaitForMultipleObjects( n, wait_handles, FALSE, INFINITE );
+        if (!ret)  /* system process event */
+        {
+            handle_shutdown_msg(SERVICE_CONTROL_PRESHUTDOWN, SERVICE_ACCEPT_PRESHUTDOWN);
+            handle_shutdown_msg(SERVICE_CONTROL_SHUTDOWN, SERVICE_ACCEPT_SHUTDOWN);
+            ExitProcess(0);
+        }
+        else if (ret == 1)
+        {
+            TRACE( "control dispatcher exited, shutting down\n" );
+            /* FIXME: we should maybe send a shutdown control to running services */
+            ExitProcess(0);
+        }
+        else if (ret == 2)
+        {
+            continue;  /* rebuild the list */
+        }
+        else if (ret < n)
+        {
+            i = wait_services[ret];
+            EnterCriticalSection( &service_cs );
+            CloseHandle( services[i]->thread );
+            services[i]->thread = NULL;
+            LeaveCriticalSection( &service_cs );
+        }
+        else return FALSE;
+    }
+
+    return TRUE;
+}
+
+/******************************************************************************
+ *     StartServiceCtrlDispatcherA   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH StartServiceCtrlDispatcherA( const SERVICE_TABLE_ENTRYA *servent )
+{
+    struct service_data *info;
+    unsigned int i;
+
+    TRACE("%p\n", servent);
+
+    if (nb_services)
+    {
+        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
+        return FALSE;
+    }
+    while (servent[nb_services].lpServiceName) nb_services++;
+    if (!nb_services)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    services = heap_alloc( nb_services * sizeof(*services) );
+
+    for (i = 0; i < nb_services; i++)
+    {
+        DWORD len = MultiByteToWideChar( CP_ACP, 0, servent[i].lpServiceName, -1, NULL, 0 );
+        DWORD sz = FIELD_OFFSET( struct service_data, name[len] );
+        info = heap_alloc_zero( sz );
+        MultiByteToWideChar( CP_ACP, 0, servent[i].lpServiceName, -1, info->name, len );
+        info->proc.a = servent[i].lpServiceProc;
+        info->unicode = FALSE;
+        services[i] = info;
+    }
+
+    return service_run_main_thread();
+}
+
+/******************************************************************************
+ *     StartServiceCtrlDispatcherW   (sechost.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH StartServiceCtrlDispatcherW( const SERVICE_TABLE_ENTRYW *servent )
+{
+    struct service_data *info;
+    unsigned int i;
+
+    TRACE("%p\n", servent);
+
+    if (nb_services)
+    {
+        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
+        return FALSE;
+    }
+    while (servent[nb_services].lpServiceName) nb_services++;
+    if (!nb_services)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    services = heap_alloc( nb_services * sizeof(*services) );
+
+    for (i = 0; i < nb_services; i++)
+    {
+        DWORD len = wcslen( servent[i].lpServiceName ) + 1;
+        DWORD sz = FIELD_OFFSET( struct service_data, name[len] );
+        info = heap_alloc_zero( sz );
+        wcscpy( info->name, servent[i].lpServiceName );
+        info->proc.w = servent[i].lpServiceProc;
+        info->unicode = TRUE;
+        services[i] = info;
+    }
+
+    return service_run_main_thread();
+}
diff --git a/dlls/advapi32/svcctl.idl b/dlls/sechost/svcctl.idl
similarity index 100%
rename from dlls/advapi32/svcctl.idl
rename to dlls/sechost/svcctl.idl
-- 
2.26.2




More information about the wine-devel mailing list