|  | /* | 
|  | * Win32 advapi functions | 
|  | * | 
|  | * Copyright 1995 Sven Verdoolaege | 
|  | * Copyright 2005 Mike McCormack | 
|  | * | 
|  | * 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 | 
|  | */ | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <string.h> | 
|  | #include <time.h> | 
|  | #include <assert.h> | 
|  |  | 
|  | #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" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(advapi); | 
|  |  | 
|  | static const WCHAR szServiceManagerKey[] = { 'S','y','s','t','e','m','\\', | 
|  | 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', | 
|  | 'S','e','r','v','i','c','e','s','\\',0 }; | 
|  | static const WCHAR  szSCMLock[] = {'A','D','V','A','P','I','_','S','C','M', | 
|  | 'L','O','C','K',0}; | 
|  |  | 
|  | typedef struct service_start_info_t | 
|  | { | 
|  | DWORD cmd; | 
|  | DWORD size; | 
|  | WCHAR str[1]; | 
|  | } service_start_info; | 
|  |  | 
|  | #define WINESERV_STARTINFO   1 | 
|  | #define WINESERV_GETSTATUS   2 | 
|  | #define WINESERV_SENDCONTROL 3 | 
|  |  | 
|  | typedef struct service_data_t | 
|  | { | 
|  | struct service_data_t *next; | 
|  | union { | 
|  | LPHANDLER_FUNCTION handler; | 
|  | LPHANDLER_FUNCTION_EX handler_ex; | 
|  | } handler; | 
|  | LPVOID context; | 
|  | SERVICE_STATUS status; | 
|  | HANDLE thread; | 
|  | BOOL unicode : 1; | 
|  | BOOL extended : 1; /* uses handler_ex instead of handler? */ | 
|  | union { | 
|  | LPSERVICE_MAIN_FUNCTIONA a; | 
|  | LPSERVICE_MAIN_FUNCTIONW w; | 
|  | } proc; | 
|  | LPWSTR args; | 
|  | WCHAR name[1]; | 
|  | } service_data; | 
|  |  | 
|  | 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 *service_list; | 
|  |  | 
|  | /****************************************************************************** | 
|  | * SC_HANDLEs | 
|  | */ | 
|  |  | 
|  | #define MAX_SERVICE_NAME 256 | 
|  |  | 
|  | typedef enum { SC_HTYPE_MANAGER, SC_HTYPE_SERVICE } SC_HANDLE_TYPE; | 
|  |  | 
|  | struct sc_handle; | 
|  | typedef VOID (*sc_handle_destructor)(struct sc_handle *); | 
|  |  | 
|  | struct sc_handle | 
|  | { | 
|  | SC_HANDLE_TYPE htype; | 
|  | DWORD ref_count; | 
|  | sc_handle_destructor destroy; | 
|  | }; | 
|  |  | 
|  | struct sc_manager       /* service control manager handle */ | 
|  | { | 
|  | struct sc_handle hdr; | 
|  | HKEY   hkey;   /* handle to services database in the registry */ | 
|  | }; | 
|  |  | 
|  | struct sc_service       /* service handle */ | 
|  | { | 
|  | struct sc_handle hdr; | 
|  | HKEY   hkey;          /* handle to service entry in the registry (under hkey) */ | 
|  | struct sc_manager *scm;  /* pointer to SCM handle */ | 
|  | WCHAR  name[1]; | 
|  | }; | 
|  |  | 
|  | static void *sc_handle_alloc(SC_HANDLE_TYPE htype, DWORD size, | 
|  | sc_handle_destructor destroy) | 
|  | { | 
|  | struct sc_handle *hdr; | 
|  |  | 
|  | hdr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ); | 
|  | if (hdr) | 
|  | { | 
|  | hdr->htype = htype; | 
|  | hdr->ref_count = 1; | 
|  | hdr->destroy = destroy; | 
|  | } | 
|  | TRACE("sc_handle type=%d -> %p\n", htype, hdr); | 
|  | return hdr; | 
|  | } | 
|  |  | 
|  | static void *sc_handle_get_handle_data(SC_HANDLE handle, DWORD htype) | 
|  | { | 
|  | struct sc_handle *hdr = (struct sc_handle *) handle; | 
|  |  | 
|  | if (!hdr) | 
|  | return NULL; | 
|  | if (hdr->htype != htype) | 
|  | return NULL; | 
|  | return hdr; | 
|  | } | 
|  |  | 
|  | static void sc_handle_free(struct sc_handle* hdr) | 
|  | { | 
|  | if (!hdr) | 
|  | return; | 
|  | if (--hdr->ref_count) | 
|  | return; | 
|  | hdr->destroy(hdr); | 
|  | HeapFree(GetProcessHeap(), 0, hdr); | 
|  | } | 
|  |  | 
|  | static void sc_handle_destroy_manager(struct sc_handle *handle) | 
|  | { | 
|  | struct sc_manager *mgr = (struct sc_manager*) handle; | 
|  |  | 
|  | TRACE("destroying SC Manager %p\n", mgr); | 
|  | if (mgr->hkey) | 
|  | RegCloseKey(mgr->hkey); | 
|  | } | 
|  |  | 
|  | static void sc_handle_destroy_service(struct sc_handle *handle) | 
|  | { | 
|  | struct sc_service *svc = (struct sc_service*) handle; | 
|  |  | 
|  | TRACE("destroying service %p\n", svc); | 
|  | if (svc->hkey) | 
|  | RegCloseKey(svc->hkey); | 
|  | svc->hkey = NULL; | 
|  | sc_handle_free(&svc->scm->hdr); | 
|  | svc->scm = NULL; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * String management functions | 
|  | */ | 
|  | static inline LPWSTR SERV_dup( LPCSTR str ) | 
|  | { | 
|  | UINT len; | 
|  | LPWSTR wstr; | 
|  |  | 
|  | if( !str ) | 
|  | return NULL; | 
|  | len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); | 
|  | wstr = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) ); | 
|  | MultiByteToWideChar( CP_ACP, 0, str, -1, wstr, len ); | 
|  | return wstr; | 
|  | } | 
|  |  | 
|  | 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 = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) ); | 
|  | MultiByteToWideChar( CP_ACP, 0, str, n, wstr, len ); | 
|  | return wstr; | 
|  | } | 
|  |  | 
|  | static inline VOID SERV_free( LPWSTR wstr ) | 
|  | { | 
|  | HeapFree( GetProcessHeap(), 0, wstr ); | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * registry access functions and data | 
|  | */ | 
|  | static const WCHAR szDisplayName[] = { | 
|  | 'D','i','s','p','l','a','y','N','a','m','e', 0 }; | 
|  | static const WCHAR szType[] = {'T','y','p','e',0}; | 
|  | static const WCHAR szStart[] = {'S','t','a','r','t',0}; | 
|  | static const WCHAR szError[] = { | 
|  | 'E','r','r','o','r','C','o','n','t','r','o','l', 0}; | 
|  | static const WCHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0}; | 
|  | static const WCHAR szGroup[] = {'G','r','o','u','p',0}; | 
|  | static const WCHAR szDependencies[] = { | 
|  | 'D','e','p','e','n','d','e','n','c','i','e','s',0}; | 
|  | static const WCHAR szDependOnService[] = { | 
|  | 'D','e','p','e','n','d','O','n','S','e','r','v','i','c','e',0}; | 
|  |  | 
|  | struct reg_value { | 
|  | DWORD type; | 
|  | DWORD size; | 
|  | LPCWSTR name; | 
|  | LPCVOID data; | 
|  | }; | 
|  |  | 
|  | static inline void service_set_value( struct reg_value *val, | 
|  | DWORD type, LPCWSTR name, LPCVOID data, DWORD size ) | 
|  | { | 
|  | val->name = name; | 
|  | val->type = type; | 
|  | val->data = data; | 
|  | val->size = size; | 
|  | } | 
|  |  | 
|  | static inline void service_set_dword( struct reg_value *val, | 
|  | LPCWSTR name, DWORD *data ) | 
|  | { | 
|  | service_set_value( val, REG_DWORD, name, data, sizeof (DWORD)); | 
|  | } | 
|  |  | 
|  | static inline void service_set_string( struct reg_value *val, | 
|  | LPCWSTR name, LPCWSTR string ) | 
|  | { | 
|  | DWORD len = (lstrlenW(string)+1) * sizeof (WCHAR); | 
|  | service_set_value( val, REG_SZ, name, string, len ); | 
|  | } | 
|  |  | 
|  | static inline void service_set_multi_string( struct reg_value *val, | 
|  | LPCWSTR name, LPCWSTR string ) | 
|  | { | 
|  | DWORD len = 0; | 
|  |  | 
|  | /* determine the length of a double null terminated multi string */ | 
|  | do { | 
|  | len += (lstrlenW( &string[ len ] )+1); | 
|  | } while ( string[ len++ ] ); | 
|  |  | 
|  | len *= sizeof (WCHAR); | 
|  | service_set_value( val, REG_MULTI_SZ, name, string, len ); | 
|  | } | 
|  |  | 
|  | static inline LONG service_write_values( HKEY hKey, | 
|  | struct reg_value *val, int n ) | 
|  | { | 
|  | LONG r = ERROR_SUCCESS; | 
|  | int i; | 
|  |  | 
|  | for( i=0; i<n; i++ ) | 
|  | { | 
|  | r = RegSetValueExW(hKey, val[i].name, 0, val[i].type, | 
|  | (const BYTE*)val[i].data, val[i].size ); | 
|  | if( r != ERROR_SUCCESS ) | 
|  | break; | 
|  | } | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * Service IPC functions | 
|  | */ | 
|  | static LPWSTR service_get_pipe_name(LPWSTR service) | 
|  | { | 
|  | static const WCHAR prefix[] = { '\\','\\','.','\\','p','i','p','e','\\', | 
|  | '_','_','w','i','n','e','s','e','r','v','i','c','e','_',0}; | 
|  | LPWSTR name; | 
|  | DWORD len; | 
|  |  | 
|  | len = sizeof prefix + strlenW(service)*sizeof(WCHAR); | 
|  | name = HeapAlloc(GetProcessHeap(), 0, len); | 
|  | strcpyW(name, prefix); | 
|  | strcatW(name, service); | 
|  | return name; | 
|  | } | 
|  |  | 
|  | static HANDLE service_open_pipe(LPWSTR service) | 
|  | { | 
|  | LPWSTR szPipe = service_get_pipe_name( service ); | 
|  | 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_WAIT_FOREVER)); | 
|  | SERV_free(szPipe); | 
|  |  | 
|  | return handle; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_get_event_handle | 
|  | */ | 
|  | static HANDLE service_get_event_handle(LPWSTR service) | 
|  | { | 
|  | static const WCHAR prefix[] = { | 
|  | '_','_','w','i','n','e','s','e','r','v','i','c','e','_',0}; | 
|  | LPWSTR name; | 
|  | DWORD len; | 
|  | HANDLE handle; | 
|  |  | 
|  | len = sizeof prefix + strlenW(service)*sizeof(WCHAR); | 
|  | name = HeapAlloc(GetProcessHeap(), 0, len); | 
|  | strcpyW(name, prefix); | 
|  | strcatW(name, service); | 
|  | handle = CreateEventW(NULL, TRUE, FALSE, name); | 
|  | SERV_free(name); | 
|  | return handle; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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++; | 
|  | } | 
|  |  | 
|  | if (!argc) | 
|  | { | 
|  | if (info->unicode) | 
|  | info->proc.w(0, NULL); | 
|  | else | 
|  | info->proc.a(0, NULL); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (info->unicode) | 
|  | { | 
|  | LPWSTR *argv, p; | 
|  |  | 
|  | argv = HeapAlloc(GetProcessHeap(), 0, (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); | 
|  | HeapFree(GetProcessHeap(), 0, argv); | 
|  | } | 
|  | else | 
|  | { | 
|  | LPSTR strA, *argv, p; | 
|  | DWORD lenA; | 
|  |  | 
|  | lenA = WideCharToMultiByte(CP_ACP,0, str, len, NULL, 0, NULL, NULL); | 
|  | strA = HeapAlloc(GetProcessHeap(), 0, lenA); | 
|  | WideCharToMultiByte(CP_ACP,0, str, len, strA, lenA, NULL, NULL); | 
|  |  | 
|  | argv = HeapAlloc(GetProcessHeap(), 0, (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); | 
|  | HeapFree(GetProcessHeap(), 0, argv); | 
|  | HeapFree(GetProcessHeap(), 0, strA); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_handle_start | 
|  | */ | 
|  | static BOOL service_handle_start(HANDLE pipe, service_data *service, DWORD count) | 
|  | { | 
|  | DWORD read = 0, result = 0; | 
|  | LPWSTR args; | 
|  | BOOL r; | 
|  |  | 
|  | TRACE("%p %p %ld\n", pipe, service, count); | 
|  |  | 
|  | args = HeapAlloc(GetProcessHeap(), 0, count*sizeof(WCHAR)); | 
|  | r = ReadFile(pipe, args, count*sizeof(WCHAR), &read, NULL); | 
|  | if (!r || count!=read/sizeof(WCHAR) || args[count-1]) | 
|  | { | 
|  | ERR("pipe read failed r = %d count = %ld/%ld args[n-1]=%s\n", | 
|  | r, count, read/sizeof(WCHAR), debugstr_wn(args, count)); | 
|  | goto end; | 
|  | } | 
|  |  | 
|  | if (service->thread) | 
|  | { | 
|  | ERR("service is not stopped\n"); | 
|  | goto end; | 
|  | } | 
|  |  | 
|  | if (service->args) | 
|  | SERV_free(service->args); | 
|  | service->args = args; | 
|  | args = NULL; | 
|  | service->thread = CreateThread( NULL, 0, service_thread, | 
|  | service, 0, NULL ); | 
|  |  | 
|  | end: | 
|  | HeapFree(GetProcessHeap(), 0, args); | 
|  | WriteFile( pipe, &result, sizeof result, &read, NULL ); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_send_start_message | 
|  | */ | 
|  | static BOOL service_send_start_message(HANDLE pipe, LPCWSTR *argv, DWORD argc) | 
|  | { | 
|  | DWORD i, len, count, result; | 
|  | service_start_info *ssi; | 
|  | LPWSTR p; | 
|  | BOOL r; | 
|  |  | 
|  | TRACE("%p %p %ld\n", pipe, argv, argc); | 
|  |  | 
|  | /* calculate how much space do we need to send the startup info */ | 
|  | len = 1; | 
|  | for (i=0; i<argc; i++) | 
|  | len += strlenW(argv[i])+1; | 
|  |  | 
|  | ssi = HeapAlloc(GetProcessHeap(),0,sizeof *ssi + (len-1)*sizeof(WCHAR)); | 
|  | ssi->cmd = WINESERV_STARTINFO; | 
|  | ssi->size = len; | 
|  |  | 
|  | /* copy service args into a single buffer*/ | 
|  | p = &ssi->str[0]; | 
|  | for (i=0; i<argc; i++) | 
|  | { | 
|  | strcpyW(p, argv[i]); | 
|  | p += strlenW(p) + 1; | 
|  | } | 
|  | *p=0; | 
|  |  | 
|  | r = WriteFile(pipe, ssi, sizeof *ssi + len*sizeof(WCHAR), &count, NULL); | 
|  | if (r) | 
|  | r = ReadFile(pipe, &result, sizeof result, &count, NULL); | 
|  |  | 
|  | HeapFree(GetProcessHeap(),0,ssi); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_handle_get_status | 
|  | */ | 
|  | static BOOL service_handle_get_status(HANDLE pipe, service_data *service) | 
|  | { | 
|  | DWORD count = 0; | 
|  | TRACE("\n"); | 
|  | return WriteFile(pipe, &service->status, | 
|  | sizeof service->status, &count, NULL); | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_get_status | 
|  | */ | 
|  | static BOOL service_get_status(HANDLE pipe, LPSERVICE_STATUS status) | 
|  | { | 
|  | DWORD cmd[2], count = 0; | 
|  | BOOL r; | 
|  |  | 
|  | cmd[0] = WINESERV_GETSTATUS; | 
|  | cmd[1] = 0; | 
|  | r = WriteFile( pipe, cmd, sizeof cmd, &count, NULL ); | 
|  | if (!r || count != sizeof cmd) | 
|  | { | 
|  | ERR("service protocol error - failed to write pipe!\n"); | 
|  | return r; | 
|  | } | 
|  | r = ReadFile( pipe, status, sizeof *status, &count, NULL ); | 
|  | if (!r || count != sizeof *status) | 
|  | ERR("service protocol error - failed to read pipe " | 
|  | "r = %d  count = %ld!\n", r, count); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_send_control | 
|  | */ | 
|  | static BOOL service_send_control(HANDLE pipe, DWORD dwControl, DWORD *result) | 
|  | { | 
|  | DWORD cmd[2], count = 0; | 
|  | BOOL r; | 
|  |  | 
|  | cmd[0] = WINESERV_SENDCONTROL; | 
|  | cmd[1] = dwControl; | 
|  | r = WriteFile(pipe, cmd, sizeof cmd, &count, NULL); | 
|  | if (!r || count != sizeof cmd) | 
|  | { | 
|  | ERR("service protocol error - failed to write pipe!\n"); | 
|  | return r; | 
|  | } | 
|  | r = ReadFile(pipe, result, sizeof *result, &count, NULL); | 
|  | if (!r || count != sizeof *result) | 
|  | ERR("service protocol error - failed to read pipe " | 
|  | "r = %d  count = %ld!\n", r, count); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_accepts_control | 
|  | */ | 
|  | static BOOL service_accepts_control(service_data *service, DWORD dwControl) | 
|  | { | 
|  | DWORD a = service->status.dwControlsAccepted; | 
|  |  | 
|  | switch (dwControl) | 
|  | { | 
|  | case SERVICE_CONTROL_INTERROGATE: | 
|  | return TRUE; | 
|  | case SERVICE_CONTROL_STOP: | 
|  | if (a&SERVICE_ACCEPT_STOP) | 
|  | return TRUE; | 
|  | break; | 
|  | case SERVICE_CONTROL_SHUTDOWN: | 
|  | if (a&SERVICE_ACCEPT_SHUTDOWN) | 
|  | return TRUE; | 
|  | break; | 
|  | case SERVICE_CONTROL_PAUSE: | 
|  | case SERVICE_CONTROL_CONTINUE: | 
|  | if (a&SERVICE_ACCEPT_PAUSE_CONTINUE) | 
|  | return TRUE; | 
|  | break; | 
|  | case SERVICE_CONTROL_PARAMCHANGE: | 
|  | if (a&SERVICE_ACCEPT_PARAMCHANGE) | 
|  | return TRUE; | 
|  | break; | 
|  | case SERVICE_CONTROL_NETBINDADD: | 
|  | case SERVICE_CONTROL_NETBINDREMOVE: | 
|  | case SERVICE_CONTROL_NETBINDENABLE: | 
|  | case SERVICE_CONTROL_NETBINDDISABLE: | 
|  | if (a&SERVICE_ACCEPT_NETBINDCHANGE) | 
|  | return TRUE; | 
|  | } | 
|  | if (!service->extended) | 
|  | return FALSE; | 
|  | switch (dwControl) | 
|  | { | 
|  | case SERVICE_CONTROL_HARDWAREPROFILECHANGE: | 
|  | if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE) | 
|  | return TRUE; | 
|  | break; | 
|  | case SERVICE_CONTROL_POWEREVENT: | 
|  | if (a&SERVICE_ACCEPT_POWEREVENT) | 
|  | return TRUE; | 
|  | break; | 
|  | case SERVICE_CONTROL_SESSIONCHANGE: | 
|  | if (a&SERVICE_ACCEPT_SESSIONCHANGE) | 
|  | return TRUE; | 
|  | break; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_handle_control | 
|  | */ | 
|  | static BOOL service_handle_control(HANDLE pipe, service_data *service, | 
|  | DWORD dwControl) | 
|  | { | 
|  | DWORD count, ret = ERROR_INVALID_SERVICE_CONTROL; | 
|  |  | 
|  | TRACE("received control %ld\n", dwControl); | 
|  |  | 
|  | if (service_accepts_control(service, dwControl)) | 
|  | { | 
|  | if (service->extended && service->handler.handler_ex) | 
|  | { | 
|  | service->handler.handler_ex(dwControl, 0, NULL, service->context); | 
|  | ret = ERROR_SUCCESS; | 
|  | } | 
|  | else if (service->handler.handler) | 
|  | { | 
|  | service->handler.handler(dwControl); | 
|  | ret = ERROR_SUCCESS; | 
|  | } | 
|  | } | 
|  | return WriteFile(pipe, &ret, sizeof ret, &count, NULL); | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_reap_thread | 
|  | */ | 
|  | static DWORD service_reap_thread(service_data *service) | 
|  | { | 
|  | DWORD exitcode = 0; | 
|  |  | 
|  | if (!service->thread) | 
|  | return 0; | 
|  | GetExitCodeThread(service->thread, &exitcode); | 
|  | if (exitcode!=STILL_ACTIVE) | 
|  | { | 
|  | CloseHandle(service->thread); | 
|  | service->thread = 0; | 
|  | } | 
|  | return exitcode; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_control_dispatcher | 
|  | */ | 
|  | static DWORD WINAPI service_control_dispatcher(LPVOID arg) | 
|  | { | 
|  | service_data *service = arg; | 
|  | LPWSTR name; | 
|  | HANDLE pipe, event; | 
|  |  | 
|  | TRACE("%p %s\n", service, debugstr_w(service->name)); | 
|  |  | 
|  | /* create a pipe to talk to the rest of the world with */ | 
|  | name = service_get_pipe_name(service->name); | 
|  | pipe = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX, | 
|  | PIPE_TYPE_BYTE|PIPE_WAIT, 1, 256, 256, 10000, NULL ); | 
|  | SERV_free(name); | 
|  |  | 
|  | /* let the process who started us know we've tried to create a pipe */ | 
|  | event = service_get_event_handle(service->name); | 
|  | SetEvent(event); | 
|  | CloseHandle(event); | 
|  |  | 
|  | if (pipe==INVALID_HANDLE_VALUE) | 
|  | { | 
|  | ERR("failed to create pipe for %s, error = %ld\n", | 
|  | debugstr_w(service->name), GetLastError()); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* dispatcher loop */ | 
|  | while (1) | 
|  | { | 
|  | BOOL r; | 
|  | DWORD count, req[2] = {0,0}; | 
|  |  | 
|  | r = ConnectNamedPipe(pipe, NULL); | 
|  | if (!r && GetLastError() != ERROR_PIPE_CONNECTED) | 
|  | { | 
|  | ERR("pipe connect failed\n"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | r = ReadFile( pipe, &req, sizeof req, &count, NULL ); | 
|  | if (!r || count!=sizeof req) | 
|  | { | 
|  | ERR("pipe read failed\n"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | service_reap_thread(service); | 
|  |  | 
|  | /* handle the request */ | 
|  | switch (req[0]) | 
|  | { | 
|  | case WINESERV_STARTINFO: | 
|  | service_handle_start(pipe, service, req[1]); | 
|  | break; | 
|  | case WINESERV_GETSTATUS: | 
|  | service_handle_get_status(pipe, service); | 
|  | break; | 
|  | case WINESERV_SENDCONTROL: | 
|  | service_handle_control(pipe, service, req[1]); | 
|  | break; | 
|  | default: | 
|  | ERR("received invalid command %ld length %ld\n", req[0], req[1]); | 
|  | } | 
|  |  | 
|  | FlushFileBuffers(pipe); | 
|  | DisconnectNamedPipe(pipe); | 
|  | } | 
|  |  | 
|  | CloseHandle(pipe); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_run_threads | 
|  | */ | 
|  | static BOOL service_run_threads(void) | 
|  | { | 
|  | service_data *service; | 
|  | DWORD count = 0, n = 0; | 
|  | HANDLE *handles; | 
|  |  | 
|  | EnterCriticalSection( &service_cs ); | 
|  |  | 
|  | /* count how many services there are */ | 
|  | for (service = service_list; service; service = service->next) | 
|  | count++; | 
|  |  | 
|  | TRACE("starting %ld pipe listener threads\n", count); | 
|  |  | 
|  | handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE)*count); | 
|  |  | 
|  | for (n=0, service = service_list; service; service = service->next, n++) | 
|  | handles[n] = CreateThread( NULL, 0, service_control_dispatcher, | 
|  | service, 0, NULL ); | 
|  | assert(n==count); | 
|  |  | 
|  | LeaveCriticalSection( &service_cs ); | 
|  |  | 
|  | /* wait for all the threads to pack up and exit */ | 
|  | WaitForMultipleObjectsEx(count, handles, TRUE, INFINITE, FALSE); | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, handles); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * StartServiceCtrlDispatcherA [ADVAPI32.@] | 
|  | * | 
|  | * See StartServiceCtrlDispatcherW. | 
|  | */ | 
|  | BOOL WINAPI StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent ) | 
|  | { | 
|  | service_data *info; | 
|  | DWORD sz, len; | 
|  | BOOL ret = TRUE; | 
|  |  | 
|  | TRACE("%p\n", servent); | 
|  |  | 
|  | EnterCriticalSection( &service_cs ); | 
|  | while (servent->lpServiceName) | 
|  | { | 
|  | LPSTR name = servent->lpServiceName; | 
|  |  | 
|  | len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0); | 
|  | sz = len*sizeof(WCHAR) + sizeof *info; | 
|  | info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz ); | 
|  | MultiByteToWideChar(CP_ACP, 0, name, -1, info->name, len); | 
|  | info->proc.a = servent->lpServiceProc; | 
|  | info->unicode = FALSE; | 
|  |  | 
|  | /* insert into the list */ | 
|  | info->next = service_list; | 
|  | service_list = info; | 
|  |  | 
|  | servent++; | 
|  | } | 
|  | LeaveCriticalSection( &service_cs ); | 
|  |  | 
|  | service_run_threads(); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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( LPSERVICE_TABLE_ENTRYW servent ) | 
|  | { | 
|  | service_data *info; | 
|  | DWORD sz, len; | 
|  | BOOL ret = TRUE; | 
|  |  | 
|  | TRACE("%p\n", servent); | 
|  |  | 
|  | EnterCriticalSection( &service_cs ); | 
|  | while (servent->lpServiceName) | 
|  | { | 
|  | LPWSTR name = servent->lpServiceName; | 
|  |  | 
|  | len = strlenW(name); | 
|  | sz = len*sizeof(WCHAR) + sizeof *info; | 
|  | info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz ); | 
|  | strcpyW(info->name, name); | 
|  | info->proc.w = servent->lpServiceProc; | 
|  | info->unicode = TRUE; | 
|  |  | 
|  | /* insert into the list */ | 
|  | info->next = service_list; | 
|  | service_list = info; | 
|  |  | 
|  | servent++; | 
|  | } | 
|  | LeaveCriticalSection( &service_cs ); | 
|  |  | 
|  | service_run_threads(); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * LockServiceDatabase  [ADVAPI32.@] | 
|  | */ | 
|  | SC_LOCK WINAPI LockServiceDatabase (SC_HANDLE hSCManager) | 
|  | { | 
|  | HANDLE ret; | 
|  |  | 
|  | TRACE("%p\n",hSCManager); | 
|  |  | 
|  | ret = CreateSemaphoreW( NULL, 1, 1, szSCMLock ); | 
|  | if( ret && GetLastError() == ERROR_ALREADY_EXISTS ) | 
|  | { | 
|  | CloseHandle( ret ); | 
|  | ret = NULL; | 
|  | SetLastError( ERROR_SERVICE_DATABASE_LOCKED ); | 
|  | } | 
|  |  | 
|  | TRACE("returning %p\n", ret); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * UnlockServiceDatabase  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI UnlockServiceDatabase (SC_LOCK ScLock) | 
|  | { | 
|  | TRACE("%p\n",ScLock); | 
|  |  | 
|  | return CloseHandle( ScLock ); | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * RegisterServiceCtrlHandlerA [ADVAPI32.@] | 
|  | */ | 
|  | SERVICE_STATUS_HANDLE WINAPI | 
|  | RegisterServiceCtrlHandlerA( LPCSTR lpServiceName, LPHANDLER_FUNCTION lpfHandler ) | 
|  | { | 
|  | LPWSTR lpServiceNameW; | 
|  | SERVICE_STATUS_HANDLE ret; | 
|  |  | 
|  | lpServiceNameW = SERV_dup(lpServiceName); | 
|  | ret = RegisterServiceCtrlHandlerW( lpServiceNameW, lpfHandler ); | 
|  | SERV_free(lpServiceNameW); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * RegisterServiceCtrlHandlerW [ADVAPI32.@] | 
|  | * | 
|  | * PARAMS | 
|  | *   lpServiceName [] | 
|  | *   lpfHandler    [] | 
|  | */ | 
|  | SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerW( LPCWSTR lpServiceName, | 
|  | LPHANDLER_FUNCTION lpfHandler ) | 
|  | { | 
|  | service_data *service; | 
|  |  | 
|  | EnterCriticalSection( &service_cs ); | 
|  | for(service = service_list; service; service = service->next) | 
|  | if(!strcmpW(lpServiceName, service->name)) | 
|  | break; | 
|  | if (service) | 
|  | service->handler.handler = lpfHandler; | 
|  | LeaveCriticalSection( &service_cs ); | 
|  |  | 
|  | return (SERVICE_STATUS_HANDLE)service; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * SetServiceStatus [ADVAPI32.@] | 
|  | * | 
|  | * PARAMS | 
|  | *   hService [] | 
|  | *   lpStatus [] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | SetServiceStatus( SERVICE_STATUS_HANDLE hService, LPSERVICE_STATUS lpStatus ) | 
|  | { | 
|  | service_data *service; | 
|  | BOOL r = TRUE; | 
|  |  | 
|  | TRACE("%p %lx %lx %lx %lx %lx %lx %lx\n", hService, | 
|  | lpStatus->dwServiceType, lpStatus->dwCurrentState, | 
|  | lpStatus->dwControlsAccepted, lpStatus->dwWin32ExitCode, | 
|  | lpStatus->dwServiceSpecificExitCode, lpStatus->dwCheckPoint, | 
|  | lpStatus->dwWaitHint); | 
|  |  | 
|  | EnterCriticalSection( &service_cs ); | 
|  | for (service = service_list; service; service = service->next) | 
|  | if(service == (service_data*)hService) | 
|  | break; | 
|  | if (service) | 
|  | { | 
|  | memcpy( &service->status, lpStatus, sizeof(SERVICE_STATUS) ); | 
|  | TRACE("Set service status to %ld\n",service->status.dwCurrentState); | 
|  | } | 
|  | else | 
|  | r = FALSE; | 
|  | LeaveCriticalSection( &service_cs ); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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 lpMachineNameW, lpDatabaseNameW; | 
|  | SC_HANDLE ret; | 
|  |  | 
|  | lpMachineNameW = SERV_dup(lpMachineName); | 
|  | lpDatabaseNameW = SERV_dup(lpDatabaseName); | 
|  | ret = OpenSCManagerW(lpMachineNameW, lpDatabaseNameW, dwDesiredAccess); | 
|  | SERV_free(lpDatabaseNameW); | 
|  | SERV_free(lpMachineNameW); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * OpenSCManagerW [ADVAPI32.@] | 
|  | * | 
|  | * See OpenSCManagerA. | 
|  | */ | 
|  | SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName, | 
|  | DWORD dwDesiredAccess ) | 
|  | { | 
|  | struct sc_manager *manager; | 
|  | HKEY hReg; | 
|  | LONG r; | 
|  |  | 
|  | TRACE("(%s,%s,0x%08lx)\n", debugstr_w(lpMachineName), | 
|  | debugstr_w(lpDatabaseName), dwDesiredAccess); | 
|  |  | 
|  | if( lpDatabaseName && lpDatabaseName[0] ) | 
|  | { | 
|  | if( strcmpiW( lpDatabaseName, SERVICES_ACTIVE_DATABASEW ) == 0 ) | 
|  | { | 
|  | /* noop, all right */ | 
|  | } | 
|  | else if( strcmpiW( lpDatabaseName, SERVICES_FAILED_DATABASEW ) == 0 ) | 
|  | { | 
|  | SetLastError( ERROR_DATABASE_DOES_NOT_EXIST ); | 
|  | return NULL; | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError( ERROR_INVALID_NAME ); | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | manager = sc_handle_alloc( SC_HTYPE_MANAGER, sizeof (struct sc_manager), | 
|  | sc_handle_destroy_manager ); | 
|  | if (!manager) | 
|  | return NULL; | 
|  |  | 
|  | r = RegConnectRegistryW(lpMachineName,HKEY_LOCAL_MACHINE,&hReg); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  |  | 
|  | r = RegOpenKeyExW(hReg, szServiceManagerKey, | 
|  | 0, KEY_ALL_ACCESS, &manager->hkey); | 
|  | RegCloseKey( hReg ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  |  | 
|  | TRACE("returning %p\n", manager); | 
|  |  | 
|  | return (SC_HANDLE) &manager->hdr; | 
|  |  | 
|  | error: | 
|  | sc_handle_free( &manager->hdr ); | 
|  | SetLastError( r); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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 ) | 
|  | { | 
|  | struct sc_service *hsvc; | 
|  | BOOL ret = FALSE; | 
|  | HANDLE handle; | 
|  |  | 
|  | TRACE("%p %ld %p\n", hService, dwControl, lpServiceStatus); | 
|  |  | 
|  | hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | 
|  | if (!hsvc) | 
|  | { | 
|  | SetLastError( ERROR_INVALID_HANDLE ); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | ret = QueryServiceStatus(hService, lpServiceStatus); | 
|  | if (!ret) | 
|  | { | 
|  | ERR("failed to query service status\n"); | 
|  | SetLastError(ERROR_SERVICE_NOT_ACTIVE); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | switch (lpServiceStatus->dwCurrentState) | 
|  | { | 
|  | case SERVICE_STOPPED: | 
|  | SetLastError(ERROR_SERVICE_NOT_ACTIVE); | 
|  | return FALSE; | 
|  | case SERVICE_START_PENDING: | 
|  | if (dwControl==SERVICE_CONTROL_STOP) | 
|  | break; | 
|  | /* fall thru */ | 
|  | case SERVICE_STOP_PENDING: | 
|  | SetLastError(ERROR_SERVICE_CANNOT_ACCEPT_CTRL); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | handle = service_open_pipe(hsvc->name); | 
|  | if (handle!=INVALID_HANDLE_VALUE) | 
|  | { | 
|  | DWORD result = ERROR_SUCCESS; | 
|  | ret = service_send_control(handle, dwControl, &result); | 
|  | CloseHandle(handle); | 
|  | if (result!=ERROR_SUCCESS) | 
|  | { | 
|  | SetLastError(result); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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 ) | 
|  | { | 
|  | TRACE("%p\n", hSCObject); | 
|  |  | 
|  | sc_handle_free( (struct sc_handle*) hSCObject ); | 
|  |  | 
|  | 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 %ld\n", hSCManager, debugstr_a(lpServiceName), dwDesiredAccess); | 
|  |  | 
|  | lpServiceNameW = SERV_dup(lpServiceName); | 
|  | ret = OpenServiceW( hSCManager, lpServiceNameW, dwDesiredAccess); | 
|  | SERV_free(lpServiceNameW); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * OpenServiceW [ADVAPI32.@] | 
|  | * | 
|  | * See OpenServiceA. | 
|  | */ | 
|  | SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName, | 
|  | DWORD dwDesiredAccess) | 
|  | { | 
|  | struct sc_manager *hscm; | 
|  | struct sc_service *hsvc; | 
|  | HKEY hKey; | 
|  | long r; | 
|  | DWORD len; | 
|  |  | 
|  | TRACE("%p %s %ld\n", hSCManager, debugstr_w(lpServiceName), dwDesiredAccess); | 
|  |  | 
|  | if (!lpServiceName) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_ADDRESS); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | hscm = sc_handle_get_handle_data( hSCManager, SC_HTYPE_MANAGER ); | 
|  | if (!hscm) | 
|  | { | 
|  | SetLastError( ERROR_INVALID_HANDLE ); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | r = RegOpenKeyExW( hscm->hkey, lpServiceName, 0, KEY_ALL_ACCESS, &hKey ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | { | 
|  | SetLastError( ERROR_SERVICE_DOES_NOT_EXIST ); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | len = strlenW(lpServiceName)+1; | 
|  | hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, | 
|  | sizeof (struct sc_service) + len*sizeof(WCHAR), | 
|  | sc_handle_destroy_service ); | 
|  | if (!hsvc) | 
|  | return NULL; | 
|  | strcpyW( hsvc->name, lpServiceName ); | 
|  | hsvc->hkey = hKey; | 
|  |  | 
|  | /* add reference to SCM handle */ | 
|  | hscm->hdr.ref_count++; | 
|  | hsvc->scm = hscm; | 
|  |  | 
|  | TRACE("returning %p\n",hsvc); | 
|  |  | 
|  | return (SC_HANDLE) &hsvc->hdr; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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 ) | 
|  | { | 
|  | struct sc_manager *hscm; | 
|  | struct sc_service *hsvc = NULL; | 
|  | HKEY hKey; | 
|  | LONG r; | 
|  | DWORD dp, len; | 
|  | struct reg_value val[10]; | 
|  | int n = 0; | 
|  |  | 
|  | TRACE("%p %s %s\n", hSCManager, | 
|  | debugstr_w(lpServiceName), debugstr_w(lpDisplayName)); | 
|  |  | 
|  | hscm = sc_handle_get_handle_data( hSCManager, SC_HTYPE_MANAGER ); | 
|  | if (!hscm) | 
|  | { | 
|  | SetLastError( ERROR_INVALID_HANDLE ); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | r = RegCreateKeyExW(hscm->hkey, lpServiceName, 0, NULL, | 
|  | REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | return NULL; | 
|  |  | 
|  | if (dp != REG_CREATED_NEW_KEY) | 
|  | { | 
|  | SetLastError(ERROR_SERVICE_EXISTS); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | if( lpDisplayName ) | 
|  | service_set_string( &val[n++], szDisplayName, lpDisplayName ); | 
|  |  | 
|  | service_set_dword( &val[n++], szType, &dwServiceType ); | 
|  | service_set_dword( &val[n++], szStart, &dwStartType ); | 
|  | service_set_dword( &val[n++], szError, &dwErrorControl ); | 
|  |  | 
|  | if( lpBinaryPathName ) | 
|  | service_set_string( &val[n++], szImagePath, lpBinaryPathName ); | 
|  |  | 
|  | if( lpLoadOrderGroup ) | 
|  | service_set_string( &val[n++], szGroup, lpLoadOrderGroup ); | 
|  |  | 
|  | if( lpDependencies ) | 
|  | service_set_multi_string( &val[n++], szDependencies, lpDependencies ); | 
|  |  | 
|  | if( lpPassword ) | 
|  | FIXME("Don't know how to add a Password for a service.\n"); | 
|  |  | 
|  | if( lpServiceStartName ) | 
|  | service_set_string( &val[n++], szDependOnService, lpServiceStartName ); | 
|  |  | 
|  | r = service_write_values( hKey, val, n ); | 
|  | if( r != ERROR_SUCCESS ) | 
|  | goto error; | 
|  |  | 
|  | len = strlenW(lpServiceName)+1; | 
|  | len = sizeof (struct sc_service) + len*sizeof(WCHAR); | 
|  | hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, len, sc_handle_destroy_service ); | 
|  | if( !hsvc ) | 
|  | goto error; | 
|  | lstrcpyW( hsvc->name, lpServiceName ); | 
|  | hsvc->hkey = hKey; | 
|  | hsvc->scm = hscm; | 
|  | hscm->hdr.ref_count++; | 
|  |  | 
|  | return (SC_HANDLE) &hsvc->hdr; | 
|  |  | 
|  | error: | 
|  | RegCloseKey( hKey ); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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 = SERV_dup( lpServiceName ); | 
|  | lpDisplayNameW = SERV_dup( lpDisplayName ); | 
|  | lpBinaryPathNameW = SERV_dup( lpBinaryPathName ); | 
|  | lpLoadOrderGroupW = SERV_dup( lpLoadOrderGroup ); | 
|  | lpDependenciesW = SERV_dupmulti( lpDependencies ); | 
|  | lpServiceStartNameW = SERV_dup( lpServiceStartName ); | 
|  | lpPasswordW = SERV_dup( lpPassword ); | 
|  |  | 
|  | r = CreateServiceW( hSCManager, lpServiceNameW, lpDisplayNameW, | 
|  | dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl, | 
|  | lpBinaryPathNameW, lpLoadOrderGroupW, lpdwTagId, | 
|  | lpDependenciesW, lpServiceStartNameW, lpPasswordW ); | 
|  |  | 
|  | SERV_free( lpServiceNameW ); | 
|  | SERV_free( lpDisplayNameW ); | 
|  | SERV_free( lpBinaryPathNameW ); | 
|  | SERV_free( lpLoadOrderGroupW ); | 
|  | SERV_free( lpDependenciesW ); | 
|  | SERV_free( lpServiceStartNameW ); | 
|  | SERV_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 ) | 
|  | { | 
|  | struct sc_service *hsvc; | 
|  | HKEY hKey; | 
|  | WCHAR valname[MAX_PATH+1]; | 
|  | INT index = 0; | 
|  | LONG rc; | 
|  | DWORD size; | 
|  |  | 
|  | hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | 
|  | if (!hsvc) | 
|  | { | 
|  | SetLastError( ERROR_INVALID_HANDLE ); | 
|  | return FALSE; | 
|  | } | 
|  | hKey = hsvc->hkey; | 
|  |  | 
|  | size = MAX_PATH+1; | 
|  | /* Clean out the values */ | 
|  | rc = RegEnumValueW(hKey, index, valname,&size,0,0,0,0); | 
|  | while (rc == ERROR_SUCCESS) | 
|  | { | 
|  | RegDeleteValueW(hKey,valname); | 
|  | index++; | 
|  | size = MAX_PATH+1; | 
|  | rc = RegEnumValueW(hKey, index, valname, &size,0,0,0,0); | 
|  | } | 
|  |  | 
|  | RegCloseKey(hKey); | 
|  | hsvc->hkey = NULL; | 
|  |  | 
|  | /* delete the key */ | 
|  | RegDeleteKeyW(hsvc->scm->hkey, hsvc->name); | 
|  |  | 
|  | 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,%ld,%p)\n",hService,dwNumServiceArgs,lpServiceArgVectors); | 
|  |  | 
|  | if (dwNumServiceArgs) | 
|  | lpwstr = HeapAlloc( GetProcessHeap(), 0, | 
|  | dwNumServiceArgs*sizeof(LPWSTR) ); | 
|  |  | 
|  | for(i=0; i<dwNumServiceArgs; i++) | 
|  | lpwstr[i]=SERV_dup(lpServiceArgVectors[i]); | 
|  |  | 
|  | r = StartServiceW(hService, dwNumServiceArgs, (LPCWSTR *)lpwstr); | 
|  |  | 
|  | if (dwNumServiceArgs) | 
|  | { | 
|  | for(i=0; i<dwNumServiceArgs; i++) | 
|  | SERV_free(lpwstr[i]); | 
|  | HeapFree(GetProcessHeap(), 0, lpwstr); | 
|  | } | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_start_process    [INTERNAL] | 
|  | */ | 
|  | static DWORD service_start_process(struct sc_service *hsvc) | 
|  | { | 
|  | static const WCHAR _ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0}; | 
|  | PROCESS_INFORMATION pi; | 
|  | STARTUPINFOW si; | 
|  | LPWSTR path = NULL, str; | 
|  | DWORD type, size, ret; | 
|  | HANDLE handles[2]; | 
|  | BOOL r; | 
|  |  | 
|  | /* read the executable path from memory */ | 
|  | size = 0; | 
|  | ret = RegQueryValueExW(hsvc->hkey, _ImagePathW, NULL, &type, NULL, &size); | 
|  | if (ret!=ERROR_SUCCESS) | 
|  | return FALSE; | 
|  | str = HeapAlloc(GetProcessHeap(),0,size); | 
|  | ret = RegQueryValueExW(hsvc->hkey, _ImagePathW, NULL, &type, (LPBYTE)str, &size); | 
|  | if (ret==ERROR_SUCCESS) | 
|  | { | 
|  | size = ExpandEnvironmentStringsW(str,NULL,0); | 
|  | path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR)); | 
|  | ExpandEnvironmentStringsW(str,path,size); | 
|  | } | 
|  | HeapFree(GetProcessHeap(),0,str); | 
|  | if (!path) | 
|  | return FALSE; | 
|  |  | 
|  | /* wait for the process to start and set an event or terminate */ | 
|  | handles[0] = service_get_event_handle( hsvc->name ); | 
|  | ZeroMemory(&si, sizeof(STARTUPINFOW)); | 
|  | si.cb = sizeof(STARTUPINFOW); | 
|  | r = CreateProcessW(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); | 
|  | if (r) | 
|  | { | 
|  | handles[1] = pi.hProcess; | 
|  | ret = WaitForMultipleObjectsEx(2, handles, FALSE, 30000, FALSE); | 
|  | if(ret != WAIT_OBJECT_0) | 
|  | { | 
|  | SetLastError(ERROR_IO_PENDING); | 
|  | r = FALSE; | 
|  | } | 
|  |  | 
|  | CloseHandle( pi.hThread ); | 
|  | CloseHandle( pi.hProcess ); | 
|  | } | 
|  | CloseHandle( handles[0] ); | 
|  | HeapFree(GetProcessHeap(),0,path); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static BOOL service_wait_for_startup(SC_HANDLE hService) | 
|  | { | 
|  | DWORD i; | 
|  | SERVICE_STATUS status; | 
|  | BOOL r = FALSE; | 
|  |  | 
|  | TRACE("%p\n", hService); | 
|  |  | 
|  | for (i=0; i<30; i++) | 
|  | { | 
|  | status.dwCurrentState = 0; | 
|  | r = QueryServiceStatus(hService, &status); | 
|  | if (!r) | 
|  | break; | 
|  | if (status.dwCurrentState == SERVICE_RUNNING) | 
|  | { | 
|  | TRACE("Service started successfully\n"); | 
|  | break; | 
|  | } | 
|  | r = FALSE; | 
|  | Sleep(1000); | 
|  | } | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * StartServiceW [ADVAPI32.@] | 
|  | * | 
|  | * See StartServiceA. | 
|  | */ | 
|  | BOOL WINAPI StartServiceW(SC_HANDLE hService, DWORD dwNumServiceArgs, | 
|  | LPCWSTR *lpServiceArgVectors) | 
|  | { | 
|  | struct sc_service *hsvc; | 
|  | BOOL r = FALSE; | 
|  | SC_LOCK hLock; | 
|  | HANDLE handle = INVALID_HANDLE_VALUE; | 
|  |  | 
|  | TRACE("%p %ld %p\n", hService, dwNumServiceArgs, lpServiceArgVectors); | 
|  |  | 
|  | hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | 
|  | if (!hsvc) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_HANDLE); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | hLock = LockServiceDatabase((SC_HANDLE)hsvc->scm); | 
|  | if (!hLock) | 
|  | return r; | 
|  |  | 
|  | handle = service_open_pipe(hsvc->name); | 
|  | if (handle==INVALID_HANDLE_VALUE) | 
|  | { | 
|  | /* start the service process */ | 
|  | if (service_start_process(hsvc)) | 
|  | handle = service_open_pipe(hsvc->name); | 
|  | } | 
|  |  | 
|  | if (handle != INVALID_HANDLE_VALUE) | 
|  | { | 
|  | service_send_start_message(handle, lpServiceArgVectors, dwNumServiceArgs); | 
|  | CloseHandle(handle); | 
|  | r = TRUE; | 
|  | } | 
|  |  | 
|  | UnlockServiceDatabase( hLock ); | 
|  |  | 
|  | TRACE("returning %d\n", r); | 
|  |  | 
|  | if (r) | 
|  | service_wait_for_startup(hService); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceStatus [ADVAPI32.@] | 
|  | * | 
|  | * PARAMS | 
|  | *   hService        [] | 
|  | *   lpservicestatus [] | 
|  | * | 
|  | */ | 
|  | BOOL WINAPI QueryServiceStatus(SC_HANDLE hService, | 
|  | LPSERVICE_STATUS lpservicestatus) | 
|  | { | 
|  | struct sc_service *hsvc; | 
|  | DWORD size, type, val; | 
|  | HANDLE pipe; | 
|  | LONG r; | 
|  |  | 
|  | TRACE("%p %p\n", hService, lpservicestatus); | 
|  |  | 
|  | hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | 
|  | if (!hsvc) | 
|  | { | 
|  | SetLastError( ERROR_INVALID_HANDLE ); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | pipe = service_open_pipe(hsvc->name); | 
|  | if (pipe != INVALID_HANDLE_VALUE) | 
|  | { | 
|  | r = service_get_status(pipe, lpservicestatus); | 
|  | CloseHandle(pipe); | 
|  | if (r) | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | TRACE("Failed to read service status\n"); | 
|  |  | 
|  | /* read the service type from the registry */ | 
|  | size = sizeof(val); | 
|  | r = RegQueryValueExA(hsvc->hkey, "Type", NULL, &type, (LPBYTE)&val, &size); | 
|  | if(r!=ERROR_SUCCESS || type!=REG_DWORD) | 
|  | val = 0; | 
|  |  | 
|  | lpservicestatus->dwServiceType = val; | 
|  | lpservicestatus->dwCurrentState            = SERVICE_STOPPED;  /* stopped */ | 
|  | lpservicestatus->dwControlsAccepted        = 0; | 
|  | lpservicestatus->dwWin32ExitCode           = ERROR_SERVICE_NEVER_STARTED; | 
|  | lpservicestatus->dwServiceSpecificExitCode = 0; | 
|  | lpservicestatus->dwCheckPoint              = 0; | 
|  | lpservicestatus->dwWaitHint                = 0; | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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) | 
|  | { | 
|  | FIXME("stub\n"); | 
|  | SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceConfigA [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | QueryServiceConfigA( SC_HANDLE hService, | 
|  | LPQUERY_SERVICE_CONFIGA lpServiceConfig, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded) | 
|  | { | 
|  | static const CHAR szDisplayName[] = "DisplayName"; | 
|  | static const CHAR szType[] = "Type"; | 
|  | static const CHAR szStart[] = "Start"; | 
|  | static const CHAR szError[] = "ErrorControl"; | 
|  | static const CHAR szImagePath[] = "ImagePath"; | 
|  | static const CHAR szGroup[] = "Group"; | 
|  | static const CHAR szDependencies[] = "Dependencies"; | 
|  | struct sc_service *hsvc; | 
|  | HKEY hKey; | 
|  | CHAR str_buffer[ MAX_PATH ]; | 
|  | LONG r; | 
|  | DWORD type, val, sz, total, n; | 
|  | LPSTR p; | 
|  |  | 
|  | TRACE("%p %p %ld %p\n", hService, lpServiceConfig, | 
|  | cbBufSize, pcbBytesNeeded); | 
|  |  | 
|  | hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | 
|  | if (!hsvc) | 
|  | { | 
|  | SetLastError( ERROR_INVALID_HANDLE ); | 
|  | return FALSE; | 
|  | } | 
|  | hKey = hsvc->hkey; | 
|  |  | 
|  | /* calculate the size required first */ | 
|  | total = sizeof (QUERY_SERVICE_CONFIGA); | 
|  |  | 
|  | sz = sizeof(str_buffer); | 
|  | r = RegQueryValueExA( hKey, szImagePath, 0, &type, (LPBYTE)str_buffer, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) ) | 
|  | { | 
|  | sz = ExpandEnvironmentStringsA(str_buffer,NULL,0); | 
|  | if( 0 == sz ) return FALSE; | 
|  |  | 
|  | total += sz; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* FIXME: set last error */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExA( hKey, szGroup, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExA( hKey, szDependencies, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExA( hKey, szStart, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExA( hKey, szDisplayName, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | *pcbBytesNeeded = total; | 
|  |  | 
|  | /* if there's not enough memory, return an error */ | 
|  | if( total > cbBufSize ) | 
|  | { | 
|  | SetLastError( ERROR_INSUFFICIENT_BUFFER ); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | ZeroMemory( lpServiceConfig, total ); | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExA( hKey, szType, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwServiceType = val; | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExA( hKey, szStart, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwStartType = val; | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExA( hKey, szError, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwErrorControl = val; | 
|  |  | 
|  | /* now do the strings */ | 
|  | p = (LPSTR) &lpServiceConfig[1]; | 
|  | n = total - sizeof (QUERY_SERVICE_CONFIGA); | 
|  |  | 
|  | sz = sizeof(str_buffer); | 
|  | r = RegQueryValueExA( hKey, szImagePath, 0, &type, (LPBYTE)str_buffer, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) ) | 
|  | { | 
|  | sz = ExpandEnvironmentStringsA(str_buffer, p, n); | 
|  | if( 0 == sz || sz > n ) return FALSE; | 
|  |  | 
|  | lpServiceConfig->lpBinaryPathName = p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* FIXME: set last error */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | sz = n; | 
|  | r = RegQueryValueExA( hKey, szGroup, 0, &type, (LPBYTE)p, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) | 
|  | { | 
|  | lpServiceConfig->lpLoadOrderGroup = p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  |  | 
|  | sz = n; | 
|  | r = RegQueryValueExA( hKey, szDependencies, 0, &type, (LPBYTE)p, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) | 
|  | { | 
|  | lpServiceConfig->lpDependencies = p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  |  | 
|  | if( n < 0 ) | 
|  | ERR("Buffer overflow!\n"); | 
|  |  | 
|  | TRACE("Image path = %s\n", lpServiceConfig->lpBinaryPathName ); | 
|  | TRACE("Group      = %s\n", lpServiceConfig->lpLoadOrderGroup ); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceConfigW [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | QueryServiceConfigW( SC_HANDLE hService, | 
|  | LPQUERY_SERVICE_CONFIGW lpServiceConfig, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded) | 
|  | { | 
|  | WCHAR str_buffer[ MAX_PATH ]; | 
|  | LONG r; | 
|  | DWORD type, val, sz, total, n; | 
|  | LPBYTE p; | 
|  | HKEY hKey; | 
|  | struct sc_service *hsvc; | 
|  |  | 
|  | TRACE("%p %p %ld %p\n", hService, lpServiceConfig, | 
|  | cbBufSize, pcbBytesNeeded); | 
|  |  | 
|  | hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | 
|  | if (!hsvc) | 
|  | { | 
|  | SetLastError( ERROR_INVALID_HANDLE ); | 
|  | return FALSE; | 
|  | } | 
|  | hKey = hsvc->hkey; | 
|  |  | 
|  | /* calculate the size required first */ | 
|  | total = sizeof (QUERY_SERVICE_CONFIGW); | 
|  |  | 
|  | sz = sizeof(str_buffer); | 
|  | r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) ) | 
|  | { | 
|  | sz = ExpandEnvironmentStringsW(str_buffer,NULL,0); | 
|  | if( 0 == sz ) return FALSE; | 
|  |  | 
|  | total += sizeof(WCHAR) * sz; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* FIXME: set last error */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExW( hKey, szGroup, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExW( hKey, szDependencies, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) ) | 
|  | total += sz; | 
|  | else | 
|  | total += sizeof(WCHAR); | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExW( hKey, szStart, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExW( hKey, szDisplayName, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | *pcbBytesNeeded = total; | 
|  |  | 
|  | /* if there's not enough memory, return an error */ | 
|  | if( total > cbBufSize ) | 
|  | { | 
|  | SetLastError( ERROR_INSUFFICIENT_BUFFER ); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | ZeroMemory( lpServiceConfig, total ); | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExW( hKey, szType, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwServiceType = val; | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExW( hKey, szStart, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwStartType = val; | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExW( hKey, szError, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwErrorControl = val; | 
|  |  | 
|  | /* now do the strings */ | 
|  | p = (LPBYTE) &lpServiceConfig[1]; | 
|  | n = total - sizeof (QUERY_SERVICE_CONFIGW); | 
|  |  | 
|  | sz = sizeof(str_buffer); | 
|  | r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) ) | 
|  | { | 
|  | sz = ExpandEnvironmentStringsW(str_buffer, (LPWSTR) p, n); | 
|  | sz *= sizeof(WCHAR); | 
|  | if( 0 == sz || sz > n ) return FALSE; | 
|  |  | 
|  | lpServiceConfig->lpBinaryPathName = (LPWSTR) p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* FIXME: set last error */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | sz = n; | 
|  | r = RegQueryValueExW( hKey, szGroup, 0, &type, p, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) | 
|  | { | 
|  | lpServiceConfig->lpLoadOrderGroup = (LPWSTR) p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  |  | 
|  | sz = n; | 
|  | r = RegQueryValueExW( hKey, szDependencies, 0, &type, p, &sz ); | 
|  | lpServiceConfig->lpDependencies = (LPWSTR) p; | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) | 
|  | { | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  | else | 
|  | { | 
|  | *(WCHAR *) p = 0; | 
|  | p += sizeof(WCHAR); | 
|  | n -= sizeof(WCHAR); | 
|  | } | 
|  |  | 
|  | if( n < 0 ) | 
|  | ERR("Buffer overflow!\n"); | 
|  |  | 
|  | TRACE("Image path = %s\n", debugstr_w(lpServiceConfig->lpBinaryPathName) ); | 
|  | TRACE("Group      = %s\n", debugstr_w(lpServiceConfig->lpLoadOrderGroup) ); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * EnumServicesStatusA [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | EnumServicesStatusA( SC_HANDLE hSCManager, DWORD dwServiceType, | 
|  | DWORD dwServiceState, LPENUM_SERVICE_STATUSA lpServices, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded, | 
|  | LPDWORD lpServicesReturned, LPDWORD lpResumeHandle ) | 
|  | { | 
|  | FIXME("%p type=%lx state=%lx %p %lx %p %p %p\n", hSCManager, | 
|  | dwServiceType, dwServiceState, lpServices, cbBufSize, | 
|  | pcbBytesNeeded, lpServicesReturned,  lpResumeHandle); | 
|  | SetLastError (ERROR_ACCESS_DENIED); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * EnumServicesStatusW [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | EnumServicesStatusW( SC_HANDLE hSCManager, DWORD dwServiceType, | 
|  | DWORD dwServiceState, LPENUM_SERVICE_STATUSW lpServices, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded, | 
|  | LPDWORD lpServicesReturned, LPDWORD lpResumeHandle ) | 
|  | { | 
|  | FIXME("%p type=%lx state=%lx %p %lx %p %p %p\n", hSCManager, | 
|  | dwServiceType, dwServiceState, lpServices, cbBufSize, | 
|  | pcbBytesNeeded, lpServicesReturned,  lpResumeHandle); | 
|  | SetLastError (ERROR_ACCESS_DENIED); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * GetServiceKeyNameA [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI GetServiceKeyNameA( SC_HANDLE hSCManager, LPCSTR lpDisplayName, | 
|  | LPSTR lpServiceName, LPDWORD lpcchBuffer ) | 
|  | { | 
|  | FIXME("%p %s %p %p\n", hSCManager, debugstr_a(lpDisplayName), lpServiceName, lpcchBuffer); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * GetServiceKeyNameW [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI GetServiceKeyNameW( SC_HANDLE hSCManager, LPCWSTR lpDisplayName, | 
|  | LPWSTR lpServiceName, LPDWORD lpcchBuffer ) | 
|  | { | 
|  | FIXME("%p %s %p %p\n", hSCManager, debugstr_w(lpDisplayName), lpServiceName, lpcchBuffer); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceLockStatusA [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI QueryServiceLockStatusA( SC_HANDLE hSCManager, | 
|  | LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded) | 
|  | { | 
|  | FIXME("%p %p %08lx %p\n", hSCManager, lpLockStatus, cbBufSize, pcbBytesNeeded); | 
|  |  | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceLockStatusW [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI QueryServiceLockStatusW( SC_HANDLE hSCManager, | 
|  | LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded) | 
|  | { | 
|  | FIXME("%p %p %08lx %p\n", hSCManager, lpLockStatus, cbBufSize, pcbBytesNeeded); | 
|  |  | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * GetServiceDisplayNameA  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI GetServiceDisplayNameA( SC_HANDLE hSCManager, LPCSTR lpServiceName, | 
|  | LPSTR lpDisplayName, LPDWORD lpcchBuffer) | 
|  | { | 
|  | FIXME("%p %s %p %p\n", hSCManager, | 
|  | debugstr_a(lpServiceName), lpDisplayName, lpcchBuffer); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * GetServiceDisplayNameW  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI GetServiceDisplayNameW( SC_HANDLE hSCManager, LPCWSTR lpServiceName, | 
|  | LPWSTR lpDisplayName, LPDWORD lpcchBuffer) | 
|  | { | 
|  | FIXME("%p %s %p %p\n", hSCManager, | 
|  | debugstr_w(lpServiceName), lpDisplayName, lpcchBuffer); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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) | 
|  | { | 
|  | struct reg_value val[10]; | 
|  | struct sc_service *hsvc; | 
|  | DWORD r = ERROR_SUCCESS; | 
|  | HKEY hKey; | 
|  | int n = 0; | 
|  |  | 
|  | TRACE("%p %ld %ld %ld %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) ); | 
|  |  | 
|  | hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | 
|  | if (!hsvc) | 
|  | { | 
|  | SetLastError( ERROR_INVALID_HANDLE ); | 
|  | return FALSE; | 
|  | } | 
|  | hKey = hsvc->hkey; | 
|  |  | 
|  | if( dwServiceType != SERVICE_NO_CHANGE ) | 
|  | service_set_dword( &val[n++], szType, &dwServiceType ); | 
|  |  | 
|  | if( dwStartType != SERVICE_NO_CHANGE ) | 
|  | service_set_dword( &val[n++], szStart, &dwStartType ); | 
|  |  | 
|  | if( dwErrorControl != SERVICE_NO_CHANGE ) | 
|  | service_set_dword( &val[n++], szError, &dwErrorControl ); | 
|  |  | 
|  | if( lpBinaryPathName ) | 
|  | service_set_string( &val[n++], szImagePath, lpBinaryPathName ); | 
|  |  | 
|  | if( lpLoadOrderGroup ) | 
|  | service_set_string( &val[n++], szGroup, lpLoadOrderGroup ); | 
|  |  | 
|  | if( lpDependencies ) | 
|  | service_set_multi_string( &val[n++], szDependencies, lpDependencies ); | 
|  |  | 
|  | if( lpPassword ) | 
|  | FIXME("ignoring password\n"); | 
|  |  | 
|  | if( lpServiceStartName ) | 
|  | service_set_string( &val[n++], szDependOnService, lpServiceStartName ); | 
|  |  | 
|  | r = service_write_values( hsvc->hkey, val, n ); | 
|  |  | 
|  | return (r == ERROR_SUCCESS) ? TRUE : FALSE ; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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 %ld %ld %ld %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 = SERV_dup( lpBinaryPathName ); | 
|  | wLoadOrderGroup = SERV_dup( lpLoadOrderGroup ); | 
|  | wDependencies = SERV_dupmulti( lpDependencies ); | 
|  | wServiceStartName = SERV_dup( lpServiceStartName ); | 
|  | wPassword = SERV_dup( lpPassword ); | 
|  | wDisplayName = SERV_dup( lpDisplayName ); | 
|  |  | 
|  | r = ChangeServiceConfigW( hService, dwServiceType, | 
|  | dwStartType, dwErrorControl, wBinaryPathName, | 
|  | wLoadOrderGroup, lpdwTagId, wDependencies, | 
|  | wServiceStartName, wPassword, wDisplayName); | 
|  |  | 
|  | SERV_free( wBinaryPathName ); | 
|  | SERV_free( wLoadOrderGroup ); | 
|  | SERV_free( wDependencies ); | 
|  | SERV_free( wServiceStartName ); | 
|  | SERV_free( wPassword ); | 
|  | SERV_free( wDisplayName ); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * ChangeServiceConfig2A  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI ChangeServiceConfig2A( SC_HANDLE hService, DWORD dwInfoLevel, | 
|  | LPVOID lpInfo) | 
|  | { | 
|  | BOOL r = FALSE; | 
|  |  | 
|  | TRACE("%p %ld %p\n",hService, dwInfoLevel, lpInfo); | 
|  |  | 
|  | if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION) | 
|  | { | 
|  | LPSERVICE_DESCRIPTIONA sd = (LPSERVICE_DESCRIPTIONA) lpInfo; | 
|  | SERVICE_DESCRIPTIONW sdw; | 
|  |  | 
|  | sdw.lpDescription = SERV_dup( sd->lpDescription ); | 
|  |  | 
|  | r = ChangeServiceConfig2W( hService, dwInfoLevel, &sdw ); | 
|  |  | 
|  | SERV_free( sdw.lpDescription ); | 
|  | } | 
|  | else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS) | 
|  | { | 
|  | LPSERVICE_FAILURE_ACTIONSA fa = (LPSERVICE_FAILURE_ACTIONSA) lpInfo; | 
|  | SERVICE_FAILURE_ACTIONSW faw; | 
|  |  | 
|  | faw.dwResetPeriod = fa->dwResetPeriod; | 
|  | faw.lpRebootMsg = SERV_dup( fa->lpRebootMsg ); | 
|  | faw.lpCommand = SERV_dup( fa->lpCommand ); | 
|  | faw.cActions = fa->cActions; | 
|  | faw.lpsaActions = fa->lpsaActions; | 
|  |  | 
|  | r = ChangeServiceConfig2W( hService, dwInfoLevel, &faw ); | 
|  |  | 
|  | SERV_free( faw.lpRebootMsg ); | 
|  | SERV_free( faw.lpCommand ); | 
|  | } | 
|  | else | 
|  | SetLastError( ERROR_INVALID_PARAMETER ); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * ChangeServiceConfig2W  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI ChangeServiceConfig2W( SC_HANDLE hService, DWORD dwInfoLevel, | 
|  | LPVOID lpInfo) | 
|  | { | 
|  | HKEY hKey; | 
|  | struct sc_service *hsvc; | 
|  |  | 
|  | hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | 
|  | if (!hsvc) | 
|  | { | 
|  | SetLastError( ERROR_INVALID_HANDLE ); | 
|  | return FALSE; | 
|  | } | 
|  | hKey = hsvc->hkey; | 
|  |  | 
|  | if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION) | 
|  | { | 
|  | static const WCHAR szDescription[] = {'D','e','s','c','r','i','p','t','i','o','n',0}; | 
|  | LPSERVICE_DESCRIPTIONW sd = (LPSERVICE_DESCRIPTIONW)lpInfo; | 
|  | if (sd->lpDescription) | 
|  | { | 
|  | TRACE("Setting Description to %s\n",debugstr_w(sd->lpDescription)); | 
|  | if (sd->lpDescription[0] == 0) | 
|  | RegDeleteValueW(hKey,szDescription); | 
|  | else | 
|  | RegSetValueExW(hKey, szDescription, 0, REG_SZ, | 
|  | (LPVOID)sd->lpDescription, | 
|  | sizeof(WCHAR)*(strlenW(sd->lpDescription)+1)); | 
|  | } | 
|  | } | 
|  | else | 
|  | FIXME("STUB: %p %ld %p\n",hService, dwInfoLevel, lpInfo); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceObjectSecurity [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI QueryServiceObjectSecurity(SC_HANDLE hService, | 
|  | SECURITY_INFORMATION dwSecurityInformation, | 
|  | PSECURITY_DESCRIPTOR lpSecurityDescriptor, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded) | 
|  | { | 
|  | PACL pACL = NULL; | 
|  |  | 
|  | FIXME("%p %ld %p %lu %p\n", hService, dwSecurityInformation, | 
|  | lpSecurityDescriptor, cbBufSize, pcbBytesNeeded); | 
|  |  | 
|  | InitializeSecurityDescriptor(lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); | 
|  |  | 
|  | pACL = HeapAlloc( GetProcessHeap(), 0, sizeof(ACL) ); | 
|  | InitializeAcl(pACL, sizeof(ACL), ACL_REVISION); | 
|  | SetSecurityDescriptorDacl(lpSecurityDescriptor, TRUE, pACL, TRUE); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * SetServiceObjectSecurity [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI SetServiceObjectSecurity(SC_HANDLE hService, | 
|  | SECURITY_INFORMATION dwSecurityInformation, | 
|  | PSECURITY_DESCRIPTOR lpSecurityDescriptor) | 
|  | { | 
|  | FIXME("%p %ld %p\n", hService, dwSecurityInformation, lpSecurityDescriptor); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * SetServiceBits [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI SetServiceBits( SERVICE_STATUS_HANDLE hServiceStatus, | 
|  | DWORD dwServiceBits, | 
|  | BOOL bSetBitsOn, | 
|  | BOOL bUpdateImmediately) | 
|  | { | 
|  | FIXME("%p %08lx %x %x\n", hServiceStatus, dwServiceBits, | 
|  | bSetBitsOn, bUpdateImmediately); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExA( LPCSTR lpServiceName, | 
|  | LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext ) | 
|  | { | 
|  | FIXME("%s %p %p\n", debugstr_a(lpServiceName), lpHandlerProc, lpContext); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExW( LPCWSTR lpServiceName, | 
|  | LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext ) | 
|  | { | 
|  | service_data *service; | 
|  |  | 
|  | TRACE("%s %p %p\n", debugstr_w(lpServiceName), lpHandlerProc, lpContext); | 
|  |  | 
|  | EnterCriticalSection( &service_cs ); | 
|  | for(service = service_list; service; service = service->next) | 
|  | if(!strcmpW(lpServiceName, service->name)) | 
|  | break; | 
|  | if (service) | 
|  | { | 
|  | service->handler.handler_ex = lpHandlerProc; | 
|  | service->context = lpContext; | 
|  | service->extended = TRUE; | 
|  | } | 
|  | LeaveCriticalSection( &service_cs ); | 
|  |  | 
|  | return (SERVICE_STATUS_HANDLE)service; | 
|  | } |