| /* |
| * Win32 advapi functions |
| * |
| * 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 |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #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" |
| |
| #include "svcctl.h" |
| |
| #include "wine/exception.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(service); |
| |
| 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 }; |
| |
| void __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len) |
| { |
| return HeapAlloc(GetProcessHeap(), 0, len); |
| } |
| |
| void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr) |
| { |
| HeapFree(GetProcessHeap(), 0, ptr); |
| } |
| |
| typedef struct service_data_t |
| { |
| LPHANDLER_FUNCTION_EX handler; |
| LPVOID context; |
| HANDLE thread; |
| SC_HANDLE handle; |
| BOOL unicode : 1; |
| 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 **services; |
| static unsigned int nb_services; |
| static HANDLE service_event; |
| |
| extern HANDLE CDECL __wine_make_process_system(void); |
| |
| /****************************************************************************** |
| * String management functions (same behaviour as strdup) |
| * NOTE: the caller of those functions is responsible for calling HeapFree |
| * in order to release the memory allocated by those 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 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 |
| */ |
| |
| handle_t __RPC_USER MACHINE_HANDLEW_bind(MACHINE_HANDLEW MachineName) |
| { |
| WCHAR transport[] = SVCCTL_TRANSPORT; |
| WCHAR endpoint[] = SVCCTL_ENDPOINT; |
| RPC_WSTR binding_str; |
| RPC_STATUS status; |
| handle_t rpc_handle; |
| |
| status = RpcStringBindingComposeW(NULL, transport, (RPC_WSTR)MachineName, 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; |
| } |
| |
| void __RPC_USER MACHINE_HANDLEW_unbind(MACHINE_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 = sizeof(format)/sizeof(WCHAR) + 10 /* strlenW("4294967295") */; |
| name = HeapAlloc(GetProcessHeap(), 0, 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_WAIT_FOREVER)); |
| HeapFree(GetProcessHeap(), 0, 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 = 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 DWORD service_handle_start(service_data *service, const WCHAR *data, DWORD count) |
| { |
| TRACE("%s argsize %u\n", debugstr_w(service->name), count); |
| |
| if (service->thread) |
| { |
| WARN("service is not stopped\n"); |
| return ERROR_SERVICE_ALREADY_RUNNING; |
| } |
| |
| HeapFree(GetProcessHeap(), 0, service->args); |
| service->args = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR)); |
| memcpy( service->args, data, count * sizeof(WCHAR) ); |
| 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(const service_data *service, DWORD dwControl) |
| { |
| DWORD ret = ERROR_INVALID_SERVICE_CONTROL; |
| |
| TRACE("%s control %u\n", debugstr_w(service->name), dwControl); |
| |
| if (service->handler) |
| ret = service->handler(dwControl, 0, NULL, service->context); |
| return ret; |
| } |
| |
| /****************************************************************************** |
| * service_control_dispatcher |
| */ |
| static DWORD WINAPI service_control_dispatcher(LPVOID arg) |
| { |
| SC_HANDLE manager; |
| HANDLE pipe; |
| |
| if (!(manager = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT ))) |
| { |
| ERR("failed to open service manager error %u\n", GetLastError()); |
| return 0; |
| } |
| |
| pipe = service_open_pipe(); |
| |
| if (pipe==INVALID_HANDLE_VALUE) |
| { |
| ERR("failed to create control pipe error = %d\n", GetLastError()); |
| return 0; |
| } |
| |
| /* dispatcher loop */ |
| while (1) |
| { |
| service_data *service; |
| service_start_info info; |
| WCHAR *data = NULL; |
| BOOL r; |
| DWORD data_size = 0, count, result; |
| |
| r = ReadFile( 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 = HeapAlloc( GetProcessHeap(), 0, data_size ); |
| r = ReadFile( pipe, data, data_size, &count, NULL ); |
| if (!r) |
| { |
| if (GetLastError() != ERROR_BROKEN_PIPE) |
| ERR( "pipe read failed error %u\n", GetLastError() ); |
| break; |
| } |
| if (count != data_size) |
| { |
| ERR( "partial pipe read %u/%u\n", count, data_size ); |
| break; |
| } |
| } |
| |
| /* find the service */ |
| |
| if (!(service = find_service_by_name( data ))) |
| { |
| FIXME( "got request %u for unknown service %s\n", info.cmd, debugstr_w(data)); |
| result = ERROR_INVALID_PARAMETER; |
| goto done; |
| } |
| |
| TRACE( "got request %u for service %s\n", info.cmd, debugstr_w(data) ); |
| |
| /* handle the request */ |
| switch (info.cmd) |
| { |
| case WINESERV_STARTINFO: |
| if (!service->handle) |
| { |
| if (!(service->handle = OpenServiceW( manager, data, SERVICE_SET_STATUS ))) |
| FIXME( "failed to open service %s\n", debugstr_w(data) ); |
| } |
| result = service_handle_start(service, data + info.name_size, |
| data_size / sizeof(WCHAR) - info.name_size ); |
| break; |
| case WINESERV_SENDCONTROL: |
| result = service_handle_control(service, info.control); |
| break; |
| default: |
| ERR("received invalid command %u\n", info.cmd); |
| result = ERROR_INVALID_PARAMETER; |
| break; |
| } |
| |
| done: |
| WriteFile(pipe, &result, sizeof(result), &count, NULL); |
| HeapFree( GetProcessHeap(), 0, data ); |
| } |
| |
| CloseHandle(pipe); |
| CloseServiceHandle( manager ); |
| return 1; |
| } |
| |
| /****************************************************************************** |
| * 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]; |
| |
| service_event = CreateEventW( NULL, FALSE, FALSE, NULL ); |
| |
| /* 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, NULL, 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 */ |
| for (;;) |
| { |
| 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 */ |
| { |
| TRACE( "last user process exited, shutting down\n" ); |
| /* FIXME: we should maybe send a shutdown control to running services */ |
| 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) |
| { |
| services[wait_services[ret]]->thread = 0; |
| CloseHandle( wait_handles[ret] ); |
| if (n == 4) return TRUE; /* it was the last running thread */ |
| } |
| else return FALSE; |
| } |
| } |
| |
| /****************************************************************************** |
| * StartServiceCtrlDispatcherA [ADVAPI32.@] |
| * |
| * See StartServiceCtrlDispatcherW. |
| */ |
| BOOL WINAPI StartServiceCtrlDispatcherA( const SERVICE_TABLE_ENTRYA *servent ) |
| { |
| service_data *info; |
| unsigned int i; |
| BOOL ret = TRUE; |
| |
| TRACE("%p\n", servent); |
| |
| if (nb_services) |
| { |
| SetLastError( ERROR_SERVICE_ALREADY_RUNNING ); |
| return FALSE; |
| } |
| while (servent[nb_services].lpServiceName) nb_services++; |
| services = HeapAlloc( GetProcessHeap(), 0, 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 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz ); |
| MultiByteToWideChar(CP_ACP, 0, servent[i].lpServiceName, -1, info->name, len); |
| info->proc.a = servent[i].lpServiceProc; |
| info->unicode = FALSE; |
| services[i] = info; |
| } |
| |
| service_run_main_thread(); |
| |
| 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( const SERVICE_TABLE_ENTRYW *servent ) |
| { |
| service_data *info; |
| unsigned int i; |
| BOOL ret = TRUE; |
| |
| TRACE("%p\n", servent); |
| |
| if (nb_services) |
| { |
| SetLastError( ERROR_SERVICE_ALREADY_RUNNING ); |
| return FALSE; |
| } |
| while (servent[nb_services].lpServiceName) nb_services++; |
| services = HeapAlloc( GetProcessHeap(), 0, 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 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz ); |
| strcpyW(info->name, servent[i].lpServiceName); |
| info->proc.w = servent[i].lpServiceProc; |
| info->unicode = TRUE; |
| services[i] = info; |
| } |
| |
| service_run_main_thread(); |
| |
| return ret; |
| } |
| |
| /****************************************************************************** |
| * LockServiceDatabase [ADVAPI32.@] |
| */ |
| SC_LOCK WINAPI LockServiceDatabase (SC_HANDLE hSCManager) |
| { |
| SC_RPC_LOCK hLock = NULL; |
| DWORD err; |
| |
| TRACE("%p\n",hSCManager); |
| |
| __TRY |
| { |
| err = svcctl_LockServiceDatabase(hSCManager, &hLock); |
| } |
| __EXCEPT(rpc_filter) |
| { |
| err = map_exception_code(GetExceptionCode()); |
| } |
| __ENDTRY |
| if (err != ERROR_SUCCESS) |
| { |
| SetLastError(err); |
| return NULL; |
| } |
| return hLock; |
| } |
| |
| /****************************************************************************** |
| * UnlockServiceDatabase [ADVAPI32.@] |
| */ |
| BOOL WINAPI UnlockServiceDatabase (SC_LOCK ScLock) |
| { |
| DWORD err; |
| SC_RPC_LOCK hRpcLock = ScLock; |
| |
| TRACE("%p\n",ScLock); |
| |
| __TRY |
| { |
| err = svcctl_UnlockServiceDatabase(&hRpcLock); |
| } |
| __EXCEPT(rpc_filter) |
| { |
| err = map_exception_code(GetExceptionCode()); |
| } |
| __ENDTRY |
| if (err != ERROR_SUCCESS) |
| { |
| SetLastError(err); |
| return FALSE; |
| } |
| 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) |
| CloseServiceHandle((SC_HANDLE)hService); |
| |
| 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 lpMachineNameW, lpDatabaseNameW; |
| SC_HANDLE ret; |
| |
| lpMachineNameW = SERV_dup(lpMachineName); |
| lpDatabaseNameW = SERV_dup(lpDatabaseName); |
| ret = OpenSCManagerW(lpMachineNameW, lpDatabaseNameW, dwDesiredAccess); |
| HeapFree(GetProcessHeap(), 0, lpDatabaseNameW); |
| HeapFree(GetProcessHeap(), 0, lpMachineNameW); |
| return ret; |
| } |
| |
| /****************************************************************************** |
| * OpenSCManagerW [ADVAPI32.@] |
| * |
| * See OpenSCManagerA. |
| */ |
| SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName, |
| DWORD dwDesiredAccess ) |
| { |
| SC_HANDLE handle; |
| LONG 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) |
| { |
| SetLastError( r ); |
| handle = 0; |
| } |
| |
| TRACE("returning %p\n", handle); |
| 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 %d\n", hSCManager, debugstr_a(lpServiceName), dwDesiredAccess); |
| |
| lpServiceNameW = SERV_dup(lpServiceName); |
| ret = OpenServiceW( hSCManager, lpServiceNameW, dwDesiredAccess); |
| HeapFree(GetProcessHeap(), 0, lpServiceNameW); |
| return ret; |
| } |
| |
| |
| /****************************************************************************** |
| * OpenServiceW [ADVAPI32.@] |
| * |
| * See OpenServiceA. |
| */ |
| SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName, |
| DWORD dwDesiredAccess) |
| { |
| SC_HANDLE handle; |
| DWORD err; |
| |
| TRACE("%p %s %d\n", hSCManager, debugstr_w(lpServiceName), dwDesiredAccess); |
| |
| if (!hSCManager) |
| { |
| SetLastError( ERROR_INVALID_HANDLE ); |
| return 0; |
| } |
| |
| __TRY |
| { |
| err = svcctl_OpenServiceW(hSCManager, lpServiceName, dwDesiredAccess, (SC_RPC_HANDLE *)&handle); |
| } |
| __EXCEPT(rpc_filter) |
| { |
| err = map_exception_code(GetExceptionCode()); |
| } |
| __ENDTRY |
| |
| if (err != ERROR_SUCCESS) |
| { |
| SetLastError(err); |
| handle = 0; |
| } |
| |
| TRACE("returning %p\n",handle); |
| 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; |
| 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 |
| { |
| 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 = 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 ); |
| |
| HeapFree( GetProcessHeap(), 0, lpServiceNameW ); |
| HeapFree( GetProcessHeap(), 0, lpDisplayNameW ); |
| HeapFree( GetProcessHeap(), 0, lpBinaryPathNameW ); |
| HeapFree( GetProcessHeap(), 0, lpLoadOrderGroupW ); |
| HeapFree( GetProcessHeap(), 0, lpDependenciesW ); |
| HeapFree( GetProcessHeap(), 0, lpServiceStartNameW ); |
| HeapFree( GetProcessHeap(), 0, 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; |
| |
| __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 = 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++) |
| HeapFree(GetProcessHeap(), 0, lpwstr[i]); |
| HeapFree(GetProcessHeap(), 0, 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); |
| |
| 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); |
| |
| __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 = HeapAlloc( GetProcessHeap(), 0, 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: |
| HeapFree( GetProcessHeap(), 0, 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); |
| } |
| __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); |
| |
| 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) ); |
| |
| return TRUE; |
| } |
| |
| /****************************************************************************** |
| * 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 |
| */ |
| BOOL WINAPI QueryServiceConfig2A(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffer, |
| DWORD size, LPDWORD needed) |
| { |
| BOOL ret; |
| LPBYTE bufferW = NULL; |
| |
| if(buffer && size) |
| bufferW = HeapAlloc( GetProcessHeap(), 0, 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; |
| default: |
| FIXME("conversation W->A not implemented for level %d\n", dwLevel); |
| ret = FALSE; |
| break; |
| } |
| |
| cleanup: |
| HeapFree( GetProcessHeap(), 0, bufferW); |
| return ret; |
| } |
| |
| /****************************************************************************** |
| * QueryServiceConfig2W [ADVAPI32.@] |
| */ |
| BOOL WINAPI QueryServiceConfig2W(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffer, |
| DWORD size, LPDWORD needed) |
| { |
| DWORD err; |
| |
| if(dwLevel != SERVICE_CONFIG_DESCRIPTION) { |
| if((dwLevel == SERVICE_CONFIG_DELAYED_AUTO_START_INFO) || |
| (dwLevel == SERVICE_CONFIG_FAILURE_ACTIONS) || |
| (dwLevel == SERVICE_CONFIG_FAILURE_ACTIONS_FLAG) || |
| (dwLevel == SERVICE_CONFIG_PRESHUTDOWN_INFO) || |
| (dwLevel == SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO) || |
| (dwLevel == SERVICE_CONFIG_SERVICE_SID_INFO)) |
| FIXME("Level %d not implemented\n", dwLevel); |
| SetLastError(ERROR_INVALID_LEVEL); |
| return FALSE; |
| } |
| |
| if(!buffer && size) { |
| SetLastError(ERROR_INVALID_ADDRESS); |
| return FALSE; |
| } |
| |
| TRACE("%p 0x%d %p 0x%d %p\n", hService, dwLevel, buffer, size, needed); |
| |
| __TRY |
| { |
| err = svcctl_QueryServiceConfig2W(hService, dwLevel, buffer, size, needed); |
| } |
| __EXCEPT(rpc_filter) |
| { |
| err = map_exception_code(GetExceptionCode()); |
| } |
| __ENDTRY |
| |
| if (err != ERROR_SUCCESS) |
| { |
| SetLastError( err ); |
| return FALSE; |
| } |
| |
| switch (dwLevel) |
| { |
| case SERVICE_CONFIG_DESCRIPTION: |
| if (buffer) |
| { |
| SERVICE_DESCRIPTIONW *descr = (SERVICE_DESCRIPTIONW *)buffer; |
| if (descr->lpDescription) /* make it an absolute pointer */ |
| descr->lpDescription = (WCHAR *)(buffer + (ULONG_PTR)descr->lpDescription); |
| break; |
| } |
| } |
| |
| 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=%x state=%x %p %x %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=%x state=%x %p %x %p %p %p\n", hSCManager, |
| dwServiceType, dwServiceState, lpServices, cbBufSize, |
| pcbBytesNeeded, lpServicesReturned, lpResumeHandle); |
| SetLastError (ERROR_ACCESS_DENIED); |
| return FALSE; |
| } |
| |
| /****************************************************************************** |
| * EnumServicesStatusExA [ADVAPI32.@] |
| */ |
| BOOL WINAPI |
| EnumServicesStatusExA(SC_HANDLE hSCManager, SC_ENUM_TYPE InfoLevel, DWORD dwServiceType, |
| DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, |
| LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, LPCSTR pszGroupName) |
| { |
| FIXME("%p level=%d type=%x state=%x %p %x %p %p %p %s\n", hSCManager, InfoLevel, |
| dwServiceType, dwServiceState, lpServices, cbBufSize, |
| pcbBytesNeeded, lpServicesReturned, lpResumeHandle, debugstr_a(pszGroupName)); |
| if (lpServicesReturned) *lpServicesReturned = 0; |
| SetLastError (ERROR_ACCESS_DENIED); |
| return FALSE; |
| } |
| |
| /****************************************************************************** |
| * EnumServicesStatusExW [ADVAPI32.@] |
| */ |
| BOOL WINAPI |
| EnumServicesStatusExW(SC_HANDLE hSCManager, SC_ENUM_TYPE InfoLevel, DWORD dwServiceType, |
| DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, |
| LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, LPCWSTR pszGroupName) |
| { |
| FIXME("%p level=%d type=%x state=%x %p %x %p %p %p %s\n", hSCManager, InfoLevel, |
| dwServiceType, dwServiceState, lpServices, cbBufSize, |
| pcbBytesNeeded, lpServicesReturned, lpResumeHandle, debugstr_w(pszGroupName)); |
| SetLastError (ERROR_ACCESS_DENIED); |
| return FALSE; |
| } |
| |
| /****************************************************************************** |
| * GetServiceKeyNameA [ADVAPI32.@] |
| */ |
| BOOL WINAPI GetServiceKeyNameA( SC_HANDLE hSCManager, LPCSTR lpDisplayName, |
| LPSTR lpServiceName, LPDWORD lpcchBuffer ) |
| { |
| LPWSTR lpDisplayNameW, lpServiceNameW; |
| DWORD sizeW; |
| BOOL ret = FALSE; |
| |
| TRACE("%p %s %p %p\n", hSCManager, |
| debugstr_a(lpDisplayName), lpServiceName, lpcchBuffer); |
| |
| lpDisplayNameW = SERV_dup(lpDisplayName); |
| if (lpServiceName) |
| lpServiceNameW = HeapAlloc(GetProcessHeap(), 0, *lpcchBuffer * sizeof(WCHAR)); |
| else |
| lpServiceNameW = NULL; |
| |
| sizeW = *lpcchBuffer; |
| if (!GetServiceKeyNameW(hSCManager, lpDisplayNameW, lpServiceNameW, &sizeW)) |
| { |
| if (lpServiceName && *lpcchBuffer) |
| lpServiceName[0] = 0; |
| *lpcchBuffer = sizeW*2; /* we can only provide an upper estimation of string length */ |
| goto cleanup; |
| } |
| |
| if (!WideCharToMultiByte(CP_ACP, 0, lpServiceNameW, (sizeW + 1), lpServiceName, |
| *lpcchBuffer, NULL, NULL )) |
| { |
| if (*lpcchBuffer && lpServiceName) |
| lpServiceName[0] = 0; |
| *lpcchBuffer = WideCharToMultiByte(CP_ACP, 0, lpServiceNameW, -1, NULL, 0, NULL, NULL); |
| goto cleanup; |
| } |
| |
| /* lpcchBuffer not updated - same as in GetServiceDisplayNameA */ |
| ret = TRUE; |
| |
| cleanup: |
| HeapFree(GetProcessHeap(), 0, lpServiceNameW); |
| HeapFree(GetProcessHeap(), 0, lpDisplayNameW); |
| 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 0; |
| } |
| |
| /* 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.@] |
| */ |
| BOOL WINAPI QueryServiceLockStatusA( SC_HANDLE hSCManager, |
| LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus, |
| DWORD cbBufSize, LPDWORD pcbBytesNeeded) |
| { |
| FIXME("%p %p %08x %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 %08x %p\n", hSCManager, lpLockStatus, cbBufSize, pcbBytesNeeded); |
| |
| return FALSE; |
| } |
| |
| /****************************************************************************** |
| * GetServiceDisplayNameA [ADVAPI32.@] |
| */ |
| BOOL WINAPI GetServiceDisplayNameA( SC_HANDLE hSCManager, LPCSTR lpServiceName, |
| LPSTR lpDisplayName, LPDWORD lpcchBuffer) |
| { |
| LPWSTR lpServiceNameW, lpDisplayNameW; |
| DWORD sizeW; |
| BOOL ret = FALSE; |
| |
| TRACE("%p %s %p %p\n", hSCManager, |
| debugstr_a(lpServiceName), lpDisplayName, lpcchBuffer); |
| |
| lpServiceNameW = SERV_dup(lpServiceName); |
| if (lpDisplayName) |
| lpDisplayNameW = HeapAlloc(GetProcessHeap(), 0, *lpcchBuffer * sizeof(WCHAR)); |
| else |
| lpDisplayNameW = NULL; |
| |
| sizeW = *lpcchBuffer; |
| if (!GetServiceDisplayNameW(hSCManager, lpServiceNameW, lpDisplayNameW, &sizeW)) |
| { |
| if (lpDisplayName && *lpcchBuffer) |
| lpDisplayName[0] = 0; |
| *lpcchBuffer = sizeW*2; /* we can only provide an upper estimation of string length */ |
| goto cleanup; |
| } |
| |
| if (!WideCharToMultiByte(CP_ACP, 0, lpDisplayNameW, (sizeW + 1), lpDisplayName, |
| *lpcchBuffer, NULL, NULL )) |
| { |
| if (*lpcchBuffer && lpDisplayName) |
| lpDisplayName[0] = 0; |
| *lpcchBuffer = WideCharToMultiByte(CP_ACP, 0, lpDisplayNameW, -1, NULL, 0, NULL, NULL); |
| goto cleanup; |
| } |
| |
| /* probably due to a bug GetServiceDisplayNameA doesn't modify lpcchBuffer on success. |
| * (but if the function succeeded it means that is a good upper estimation of the size) */ |
| ret = TRUE; |
| |
| cleanup: |
| HeapFree(GetProcessHeap(), 0, lpDisplayNameW); |
| HeapFree(GetProcessHeap(), 0, lpServiceNameW); |
| 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 0; |
| } |
| |
| /* 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 = 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); |
| |
| HeapFree( GetProcessHeap(), 0, wBinaryPathName ); |
| HeapFree( GetProcessHeap(), 0, wLoadOrderGroup ); |
| HeapFree( GetProcessHeap(), 0, wDependencies ); |
| HeapFree( GetProcessHeap(), 0, wServiceStartName ); |
| HeapFree( GetProcessHeap(), 0, wPassword ); |
| HeapFree( GetProcessHeap(), 0, 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 = SERV_dup( sd->lpDescription ); |
| |
| r = ChangeServiceConfig2W( hService, dwInfoLevel, &sdw ); |
| |
| HeapFree( GetProcessHeap(), 0, sdw.lpDescription ); |
| } |
| else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS) |
| { |
| LPSERVICE_FAILURE_ACTIONSA fa = 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 ); |
| |
| HeapFree( GetProcessHeap(), 0, faw.lpRebootMsg ); |
| HeapFree( GetProcessHeap(), 0, faw.lpCommand ); |
| } |
| else |
| SetLastError( ERROR_INVALID_PARAMETER ); |
| |
| return r; |
| } |
| |
| /****************************************************************************** |
| * ChangeServiceConfig2W [ADVAPI32.@] |
| */ |
| BOOL WINAPI ChangeServiceConfig2W( SC_HANDLE hService, DWORD dwInfoLevel, |
| LPVOID lpInfo) |
| { |
| DWORD err; |
| |
| __TRY |
| { |
| err = svcctl_ChangeServiceConfig2W( hService, dwInfoLevel, lpInfo ); |
| } |
| __EXCEPT(rpc_filter) |
| { |
| err = map_exception_code(GetExceptionCode()); |
| } |
| __ENDTRY |
| |
| if (err != ERROR_SUCCESS) |
| SetLastError(err); |
| |
| return err == ERROR_SUCCESS; |
| } |
| |
| /****************************************************************************** |
| * QueryServiceObjectSecurity [ADVAPI32.@] |
| */ |
| BOOL WINAPI QueryServiceObjectSecurity(SC_HANDLE hService, |
| SECURITY_INFORMATION dwSecurityInformation, |
| PSECURITY_DESCRIPTOR lpSecurityDescriptor, |
| DWORD cbBufSize, LPDWORD pcbBytesNeeded) |
| { |
| SECURITY_DESCRIPTOR descriptor; |
| DWORD size; |
| BOOL succ; |
| 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; |
| succ = MakeSelfRelativeSD(&descriptor, lpSecurityDescriptor, &size); |
| *pcbBytesNeeded = size; |
| return succ; |
| } |
| |
| /****************************************************************************** |
| * SetServiceObjectSecurity [ADVAPI32.@] |
| */ |
| BOOL WINAPI SetServiceObjectSecurity(SC_HANDLE hService, |
| SECURITY_INFORMATION dwSecurityInformation, |
| PSECURITY_DESCRIPTOR lpSecurityDescriptor) |
| { |
| FIXME("%p %d %p\n", hService, dwSecurityInformation, lpSecurityDescriptor); |
| return TRUE; |
| } |
| |
| /****************************************************************************** |
| * SetServiceBits [ADVAPI32.@] |
| */ |
| BOOL WINAPI SetServiceBits( SERVICE_STATUS_HANDLE hServiceStatus, |
| DWORD dwServiceBits, |
| BOOL bSetBitsOn, |
| BOOL bUpdateImmediately) |
| { |
| FIXME("%p %08x %x %x\n", hServiceStatus, dwServiceBits, |
| bSetBitsOn, bUpdateImmediately); |
| 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 = SERV_dup(name); |
| ret = RegisterServiceCtrlHandlerExW( nameW, handler, context ); |
| HeapFree( GetProcessHeap(), 0, 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; |
| BOOL found = FALSE; |
| |
| 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; |
| found = TRUE; |
| } |
| LeaveCriticalSection( &service_cs ); |
| |
| if (!found) SetLastError(ERROR_SERVICE_DOES_NOT_EXIST); |
| |
| return (SERVICE_STATUS_HANDLE)hService; |
| } |
| |
| /****************************************************************************** |
| * EnumDependentServicesA [ADVAPI32.@] |
| */ |
| BOOL WINAPI EnumDependentServicesA( SC_HANDLE hService, DWORD dwServiceState, |
| LPENUM_SERVICE_STATUSA 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; |
| } |
| |
| /****************************************************************************** |
| * 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; |
| } |