resend: advapi32 service functions

Alexander Yaworsky yaworsky at migusoft.ru
Sat Jun 19 02:48:59 CDT 2004


Hello

my apologize for incorrect prev. post

ChangeLog:
Improved functionality of service functions.

Index: dlls/advapi32/service.c
===================================================================
RCS file: /home/wine/wine/dlls/advapi32/service.c,v
retrieving revision 1.45
diff -u -r1.45 service.c
--- dlls/advapi32/service.c 23 Apr 2004 21:32:34 -0000 1.45
+++ dlls/advapi32/service.c 19 Jun 2004 07:21:29 -0000
@@ -34,16 +34,130 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(advapi);
 
-static DWORD   start_dwNumServiceArgs;
-static LPWSTR *start_lpServiceArgVectors;
+#define LOCK_OBJECT_SIZE 256 * sizeof(WCHAR) /* max service name length */
+
+/* structure of shared memory of service */
+typedef struct
+{
+    DWORD code; /* request code on enter and reply error code on exit */
+    SERVICE_STATUS status;
+    DWORD argc;
+    WCHAR argdata[4000];
+} SERVICE_SHMEM;
 
-static const WCHAR _ServiceStartDataW[]  = {'A','D','V','A','P','I','_','S',
-                                            'e','r','v','i','c','e','S','t',
-                                            'a','r','t','D','a','t','a',0};
 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};
+static const WCHAR  szWaitServiceStartW[]  = {'A','D','V','A','P','I','_','W',
+                                              'a','i','t','S','e','r','v','i',
+                                              'c','e','S','t','a','r','t',0};
+/* shared object names */
+static const WCHAR  szServiceObjectNameFmtW[] = {'A','D','V','A','P','I','_',
+                                                 '%','s','_','%','s',0};
+static const WCHAR  szShMemNameW[] = {'S','H','M','E','M',0};
+static const WCHAR  szMutexNameW[] = {'M','U','X',0};
+static const WCHAR  szRequestEventNameW[] = {'R','E','Q','E','V','T',0};
+static const WCHAR  szReplyEventNameW[] = {'R','E','P','L','Y','E','V','T',0};
+
+/******************************************************************************
+ * SC_HANDLEs
+ * they just keep service name
+ */
+
+typedef enum { SC_HTYPE_MANAGER, SC_HTYPE_SERVICE } SC_HANDLE_TYPE;
 
+struct sc_handle;
+
+struct sc_manager
+{
+    /* paranoidal error handling for insane applications:
+     * if SCM handle has been closed before any related service handle,
+     * leave SCM handle alive
+     */
+    LONG ref_count;
+};
+
+struct sc_service
+{
+    struct sc_handle *sc_manager;
+    WCHAR service_name[256];
+};
+
+struct sc_handle
+{
+    SC_HANDLE_TYPE htype;
+    HKEY hKey;
+    union
+    {
+ struct sc_manager manager;
+ struct sc_service service;
+    } hdata;
+};
+
+static
+struct sc_handle*
+alloc_sc_handle( SC_HANDLE_TYPE htype, LPCWSTR lpServiceName )
+{
+    struct sc_handle *retval;
+
+    retval = HeapAlloc( GetProcessHeap(), 0, sizeof(struct sc_handle) );
+    if( retval != NULL )
+    {
+ retval->htype = htype;
+ retval->hKey = NULL;
+ if( SC_HTYPE_SERVICE == htype )
+ {
+     strncpyW( retval->hdata.service.service_name, lpServiceName, 256 );
+     retval->hdata.service.service_name[ 255 ] = 0;
+ }
+    }
+    return retval;
+}
+
+static
+void
+free_sc_handle( struct sc_handle* handle )
+{
+    if( SC_HTYPE_MANAGER == handle->htype )
+    {
+ if( 0 != InterlockedDecrement( &handle->hdata.manager.ref_count ) )
+     /* let it be */
+     return;
+    }
+    if( SC_HTYPE_SERVICE == handle->htype )
+    {
+ struct sc_handle *h = handle->hdata.service.sc_manager;
+ if( 0 == InterlockedDecrement( &h->hdata.manager.ref_count ) )
+ {
+     /* it's time to really close SCM handle */
+     if( h->hKey != NULL )
+  RegCloseKey( h->hKey );
+     HeapFree( GetProcessHeap(), 0, h );
+ }
+    }
+    if( handle->hKey != NULL )
+ RegCloseKey( handle->hKey );
+    HeapFree( GetProcessHeap(), 0, handle );
+}
+
+static
+void
+init_sc_manager( struct sc_handle* handle, HKEY hKey )
+{
+    handle->hKey = hKey;
+    handle->hdata.manager.ref_count = 1;
+}
+
+static
+void
+init_sc_service( struct sc_handle* handle, HKEY hKey, struct sc_handle* sc_manager )
+{
+    handle->hKey = hKey;
+    InterlockedIncrement( &sc_manager->hdata.manager.ref_count );
+    handle->hdata.service.sc_manager = sc_manager;
+}
 
 /******************************************************************************
  * EnumServicesStatusA [ADVAPI32.@]
@@ -76,62 +190,277 @@
 }
 
 /******************************************************************************
- * StartServiceCtrlDispatcherA [ADVAPI32.@]
+ * service thread params; service_thread_func for StartServiceCtrlDispatcher
  */
-BOOL WINAPI
-StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent )
+
+typedef struct
 {
-    LPSERVICE_MAIN_FUNCTIONA fpMain;
-    HANDLE wait;
-    DWORD  dwNumServiceArgs ;
-    LPWSTR *lpArgVecW;
-    LPSTR  *lpArgVecA;
-    int i;
+    WCHAR service_name[ 256 ];
+    HANDLE shmem, mutex, request_event, reply_event;
+    SERVICE_SHMEM *shmem_ptr;
+    LPSERVICE_MAIN_FUNCTIONW func;
+    LPHANDLER_FUNCTION handler;
+    LONG terminated;
+    LPWSTR argv[ 64 ];  /* FIXME: use dynamic allocation */
+    CHAR argv0_a[ 256 ];
+} SERVICE_THREAD_PARAMS;
+
+/* FIXME: use service thread list for SERVICE_WIN32_SHARE_PROCESS
+ * instead of single global variable?
+ */
+static SERVICE_THREAD_PARAMS *service_thread_params;
+
+static DWORD WINAPI
+service_thread_func( LPVOID arg )
+{
+    SERVICE_THREAD_PARAMS *params = arg;
+
+    params->func( params->shmem_ptr->argc, params->argv );
+
+    /* signal to request handler in StartServiceCtrlDispatcher */
+    params->terminated = 1;
+    SetEvent( params->request_event );
+
+    return 0;
+}
+
+/******************************************************************************
+ * _StartServiceCtrlDispatcher
+ */
+static BOOL WINAPI
+do_StartServiceCtrlDispatcher( LPSERVICE_TABLE_ENTRYW servent, int unicode )
+{
+    SERVICE_THREAD_PARAMS *params;
+    WCHAR object_name[ MAX_PATH ];
+    LPWSTR argptr;
+    HANDLE ss_wait;
+    LPSERVICE_MAIN_FUNCTIONW fpMain;
+    HANDLE service_thread;
+    DWORD lasterr, i;
+    BOOL ret = FALSE;
 
     TRACE("(%p)\n", servent);
-    wait = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, _ServiceStartDataW);
-    if(wait == 0)
+
+    params = HeapAlloc( GetProcessHeap(),
+                        HEAP_ZERO_MEMORY, sizeof(SERVICE_THREAD_PARAMS) );
+    if( NULL == params )
+      return FALSE;
+
+    /* open locking shared memory object and read service name */
+    params->shmem = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, szSCMLock );
+    if( NULL == params->shmem )
     {
-        ERR("Couldn't find wait semaphore\n");
-        ERR("perhaps you need to start services using StartService\n");
-        return FALSE;
+        lasterr = ERROR_FAILED_SERVICE_CONTROLLER_CONNECT;
+        goto done;
+    }
+    argptr = MapViewOfFile( params->shmem, FILE_MAP_ALL_ACCESS,
+                            0, 0, LOCK_OBJECT_SIZE );
+    if( NULL == argptr )
+    {
+        lasterr = GetLastError();
+        goto done;
     }
+    strcpyW( params->service_name, argptr );
+    UnmapViewOfFile( argptr );
+    CloseHandle( params->shmem );
+    params->shmem = NULL;
 
-    dwNumServiceArgs = start_dwNumServiceArgs;
-    lpArgVecW        = start_lpServiceArgVectors;
+    /* create events and mutex; mutex is initially owned */
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szRequestEventNameW, params->service_name );
+    params->request_event = CreateEventW( NULL, FALSE, FALSE, object_name );
+    if( NULL == params->request_event )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+    if( ERROR_ALREADY_EXISTS == GetLastError() )
+    {
+        lasterr = ERROR_SERVICE_ALREADY_RUNNING;
+        goto done;
+    }
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szReplyEventNameW, params->service_name );
+    params->reply_event = CreateEventW( NULL, FALSE, FALSE, object_name );
+    if( NULL == params->reply_event )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szMutexNameW, params->service_name );
+    params->mutex = CreateMutexW( NULL, TRUE, object_name );
+    if( NULL == params->mutex )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
 
-    ReleaseSemaphore(wait, 1, NULL);
+    /* open service shared memory */
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szShMemNameW, params->service_name );
+    params->shmem = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, object_name );
+    if( NULL == params->shmem )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+    params->shmem_ptr = MapViewOfFile( params->shmem, FILE_MAP_ALL_ACCESS,
+                                       0, 0, sizeof(SERVICE_SHMEM) );
+    if( NULL == params->shmem_ptr )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
 
-    /* Convert the Unicode arg vectors back to ASCII */
-    if(dwNumServiceArgs)
-        lpArgVecA = (LPSTR*) HeapAlloc( GetProcessHeap(), 0,
-                                   dwNumServiceArgs*sizeof(LPSTR) );
+    /* init argvectors */
+    if( unicode )
+    {
+        LPWSTR argptr = &params->shmem_ptr->argdata[0];
+        for( i = 1; i <= params->shmem_ptr->argc; i++ )
+        {
+            if( i == sizeof(params->argv) / sizeof(params->argv[0]) - 1 )
+            {
+                /* FIXME: use dynamic allocation */
+                params->shmem_ptr->argc = i;
+                break;
+            }
+            params->argv[ i ] = argptr;
+            argptr += strlenW( argptr ) + 1;
+        }
+        params->argv[ 0 ] = &params->service_name[0];
+        params->argv[ i ] = NULL;
+    }
     else
-        lpArgVecA = NULL;
+    {
+        LPSTR argptr = (LPSTR) &params->shmem_ptr->argdata[0];
+        LPSTR buf;
 
-    for(i=0; i<dwNumServiceArgs; i++)
-        lpArgVecA[i]=HEAP_strdupWtoA(GetProcessHeap(), 0, lpArgVecW[i]);
+        buf = HeapAlloc( GetProcessHeap(), 0,
+                         sizeof(params->shmem_ptr->argdata) );
+        if( NULL == buf )
+        {
+            params->shmem_ptr->argc = 0;
+            i = 1;
+        }
+        else
+        {
+            DWORD n = sizeof(params->shmem_ptr->argdata);
+            LPWSTR src = params->shmem_ptr->argdata;
+            LPSTR dest = buf;
+     for( i = 0; i < params->shmem_ptr->argc; i++ )
+            {
+                DWORD len = strlenW( src ) + 1;
+                WideCharToMultiByte( CP_ACP, 0, src, -1, dest, n, NULL, NULL );
+                src += len;
+                n -= len;
+                dest += strlen( dest ) + 1;
+            }
+            memcpy( params->shmem_ptr->argdata, buf,
+                    sizeof(params->shmem_ptr->argdata) );
+            HeapFree( GetProcessHeap(), 0, buf );
+            for( i = 1; i <= params->shmem_ptr->argc; i++ )
+            {
+                if( i == sizeof(params->argv) / sizeof(params->argv[0]) - 1 )
+                {
+                    /* FIXME: use dynamic allocation */
+                    params->shmem_ptr->argc = i;
+                    break;
+                }
+                params->argv[ i ] = (LPWSTR) argptr;
+                argptr += strlen( argptr ) + 1;
+            }
+        }
+        WideCharToMultiByte( CP_ACP, 0, params->service_name, -1,
+                             params->argv0_a, 256, NULL, NULL );
+        params->argv[ 0 ] = (LPWSTR) &params->argv0_a[0];
+        params->argv[ i ] = NULL;
+        TRACE("argc=%d argv[0]=%s\n", (int) params->shmem_ptr->argc, (LPSTR) params->argv[0]);
+    }
+    params->shmem_ptr->argc++;
+
+    /* init status according to docs for StartService */
+    params->shmem_ptr->status.dwCurrentState = SERVICE_START_PENDING;
+    params->shmem_ptr->status.dwControlsAccepted = 0;
+    params->shmem_ptr->status.dwCheckPoint = 0;
+    params->shmem_ptr->status.dwWaitHint = 2000;
 
-    /* FIXME: should we blindly start all services? */
-    while (servent->lpServiceName) {
-        TRACE("%s at %p)\n", debugstr_a(servent->lpServiceName),servent);
-        fpMain = servent->lpServiceProc;
+    /* try to start the service */
 
-        /* try to start the service */
-        fpMain( dwNumServiceArgs, lpArgVecA);
+    /* FIXME: find entry by name for SERVICE_WIN32_SHARE_PROCESS */
+    fpMain = servent->lpServiceProc;
 
-        servent++;
+    params->func = fpMain;
+    params->handler = NULL;
+    params->terminated = 0;
+    service_thread = CreateThread( NULL, 0, service_thread_func, params, 0, &i );
+    if( NULL == service_thread )
+    {
+        lasterr = GetLastError();
+        goto done;
     }
 
-    if(dwNumServiceArgs)
+    /* signal event for StartService */
+    ss_wait = OpenEventW( EVENT_ALL_ACCESS, FALSE, szWaitServiceStartW );
+    if( NULL != ss_wait )
     {
-        /* free arg strings */
-        for(i=0; i<dwNumServiceArgs; i++)
-            HeapFree(GetProcessHeap(), 0, lpArgVecA[i]);
-        HeapFree(GetProcessHeap(), 0, lpArgVecA);
+        SetEvent( ss_wait );
+        CloseHandle( ss_wait );
     }
 
-    return TRUE;
+    /* ready to accept requests */
+    ReleaseMutex( params->mutex );
+
+    /* FIXME: multiple service threads in SERVICE_WIN32_SHARE_PROCESS */
+    service_thread_params = params;
+    for(;;)
+    {
+        WaitForSingleObject( params->request_event, INFINITE );
+
+        if( params->terminated )
+        {
+          break;
+        }
+
+        if( NULL == params->handler )
+        {
+          params->shmem_ptr->code = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
+        }
+        else
+        {
+          params->handler( params->shmem_ptr->code );
+          params->shmem_ptr->code = 0;
+        }
+
+        SetEvent( params->reply_event );
+    }
+
+    CloseHandle( service_thread );
+    lasterr = 0;
+    ret = TRUE;
+
+    /* close mutex first to make it abandoned for status requests
+     * that may occur in parallel
+     */
+done:
+    if( params->mutex != NULL ) CloseHandle( params->mutex );
+    if( params->shmem_ptr != NULL ) UnmapViewOfFile( params->shmem_ptr );
+    if( params->shmem != NULL ) CloseHandle( params->shmem );
+    if( params->reply_event != NULL ) CloseHandle( params->reply_event );
+    if( params->request_event != NULL ) CloseHandle( params->request_event );
+    HeapFree( GetProcessHeap(), 0, params );
+    SetLastError( lasterr );
+    return ret;
+}
+
+/******************************************************************************
+ * StartServiceCtrlDispatcherA [ADVAPI32.@]
+ */
+BOOL WINAPI
+StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent )
+{
+    return do_StartServiceCtrlDispatcher( (LPSERVICE_TABLE_ENTRYW) servent,
+                                          0 /* not unicode */ );
 }
 
 /******************************************************************************
@@ -143,37 +472,7 @@
 BOOL WINAPI
 StartServiceCtrlDispatcherW( LPSERVICE_TABLE_ENTRYW servent )
 {
-    LPSERVICE_MAIN_FUNCTIONW fpMain;
-    HANDLE wait;
-    DWORD  dwNumServiceArgs ;
-    LPWSTR *lpServiceArgVectors ;
-
-    TRACE("(%p)\n", servent);
-    wait = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, _ServiceStartDataW);
-    if(wait == 0)
-    {
-        ERR("Couldn't find wait semaphore\n");
-        ERR("perhaps you need to start services using StartService\n");
-        return FALSE;
-    }
-
-    dwNumServiceArgs    = start_dwNumServiceArgs;
-    lpServiceArgVectors = start_lpServiceArgVectors;
-
-    ReleaseSemaphore(wait, 1, NULL);
-
-    /* FIXME: should we blindly start all services? */
-    while (servent->lpServiceName) {
-        TRACE("%s at %p)\n", debugstr_w(servent->lpServiceName),servent);
-        fpMain = servent->lpServiceProc;
-
-        /* try to start the service */
-        fpMain( dwNumServiceArgs, lpServiceArgVectors);
-
-        servent++;
-    }
-
-    return TRUE;
+    return do_StartServiceCtrlDispatcher( servent, 1 /* unicode */ );
 }
 
 /******************************************************************************
@@ -181,8 +480,29 @@
  */
 LPVOID WINAPI LockServiceDatabase (SC_HANDLE hSCManager)
 {
-        FIXME("%p\n",hSCManager);
-        return (SC_HANDLE)0xcacacafe;
+    HANDLE hLock;
+
+    TRACE("%p\n",hSCManager);
+
+    /* FIXME: what to do with hSCManager?
+     * local SCM is always assumed here
+     */
+    hLock = CreateFileMappingW( INVALID_HANDLE_VALUE,
+                                NULL,
+                                PAGE_READWRITE,
+                                0, LOCK_OBJECT_SIZE,
+                                szSCMLock );
+    if( hLock != NULL
+        && GetLastError() == ERROR_ALREADY_EXISTS )
+    {
+        CloseHandle( hLock );
+        hLock = NULL;
+        SetLastError( ERROR_SERVICE_DATABASE_LOCKED );
+    }
+
+    TRACE("returning %p\n", hLock);
+
+    return hLock;
 }
 
 /******************************************************************************
@@ -190,8 +510,9 @@
  */
 BOOL WINAPI UnlockServiceDatabase (LPVOID ScLock)
 {
-        FIXME(": %p\n",ScLock);
- return TRUE;
+    TRACE("%p\n",ScLock);
+
+    return CloseHandle( (HANDLE) ScLock );
 }
 
 /******************************************************************************
@@ -200,8 +521,14 @@
 SERVICE_STATUS_HANDLE WINAPI
 RegisterServiceCtrlHandlerA( LPCSTR lpServiceName,
                              LPHANDLER_FUNCTION lpfHandler )
-{ FIXME("%s %p\n", lpServiceName, lpfHandler);
- return 0xcacacafe;
+{
+    SERVICE_STATUS_HANDLE ret;
+    UNICODE_STRING lpServiceNameW;
+
+    RtlCreateUnicodeStringFromAsciiz (&lpServiceNameW,lpServiceName);
+    ret = RegisterServiceCtrlHandlerW( lpServiceNameW.Buffer, lpfHandler );
+    RtlFreeUnicodeString(&lpServiceNameW);
+    return ret;
 }
 
 /******************************************************************************
@@ -214,8 +541,14 @@
 SERVICE_STATUS_HANDLE WINAPI
 RegisterServiceCtrlHandlerW( LPCWSTR lpServiceName,
                              LPHANDLER_FUNCTION lpfHandler )
-{ FIXME("%s %p\n", debugstr_w(lpServiceName), lpfHandler);
- return 0xcacacafe;
+{
+    TRACE("%s, %p\n",debugstr_w(lpServiceName),lpfHandler);
+    
+    /* FIXME: find service thread by service name
+     * (implement SERVICE_WIN32_SHARE_PROCESS)
+     */
+    service_thread_params->handler = lpfHandler;
+    return 0xcacacafe;
 }
 
 /******************************************************************************
@@ -226,16 +559,32 @@
  *   lpStatus []
  */
 BOOL WINAPI
-SetServiceStatus( SERVICE_STATUS_HANDLE hService, LPSERVICE_STATUS lpStatus )
-{ FIXME("0x%lx %p\n",hService, lpStatus);
- TRACE("\tType:%lx\n",lpStatus->dwServiceType);
- TRACE("\tState:%lx\n",lpStatus->dwCurrentState);
- TRACE("\tControlAccepted:%lx\n",lpStatus->dwControlsAccepted);
- TRACE("\tExitCode:%lx\n",lpStatus->dwWin32ExitCode);
- TRACE("\tServiceExitCode:%lx\n",lpStatus->dwServiceSpecificExitCode);
- TRACE("\tCheckPoint:%lx\n",lpStatus->dwCheckPoint);
- TRACE("\tWaitHint:%lx\n",lpStatus->dwWaitHint);
- return TRUE;
+SetServiceStatus( SERVICE_STATUS_HANDLE hStatus, LPSERVICE_STATUS lpStatus )
+{
+    DWORD r;
+
+    TRACE("\tType:%lx\n",lpStatus->dwServiceType);
+    TRACE("\tState:%lx\n",lpStatus->dwCurrentState);
+    TRACE("\tControlAccepted:%lx\n",lpStatus->dwControlsAccepted);
+    TRACE("\tExitCode:%lx\n",lpStatus->dwWin32ExitCode);
+    TRACE("\tServiceExitCode:%lx\n",lpStatus->dwServiceSpecificExitCode);
+    TRACE("\tCheckPoint:%lx\n",lpStatus->dwCheckPoint);
+    TRACE("\tWaitHint:%lx\n",lpStatus->dwWaitHint);
+
+    /* FIXME: find service thread data by status handle
+     * (implement SERVICE_WIN32_SHARE_PROCESS)
+     */
+
+    /* acquire mutex; note that mutex may already be owned
+     * when service handles control request
+     */
+    r = WaitForSingleObject( service_thread_params->mutex, 0 );
+    memcpy( &service_thread_params->shmem_ptr->status,
+            lpStatus, sizeof(SERVICE_STATUS) );
+    if( WAIT_OBJECT_0 == r || WAIT_ABANDONED == r )
+        ReleaseMutex( service_thread_params->mutex );
+
+    return TRUE;
 }
 
 /******************************************************************************
@@ -275,6 +624,7 @@
 SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName,
                                  DWORD dwDesiredAccess )
 {
+    struct sc_handle *retval;
     HKEY hReg, hKey = NULL;
     LONG r;
 
@@ -287,16 +637,22 @@
      * docs, but what if it isn't?
      */
 
+    retval = alloc_sc_handle( SC_HTYPE_MANAGER, NULL );
+    if( NULL == retval )
+      return NULL;
+      
     r = RegConnectRegistryW(lpMachineName,HKEY_LOCAL_MACHINE,&hReg);
     if (r==ERROR_SUCCESS)
     {
-        r = RegOpenKeyExW(hReg, szServiceManagerKey,0, dwDesiredAccess, &hKey );
+        r = RegOpenKeyExW(hReg, szServiceManagerKey,0, KEY_ALL_ACCESS, &hKey );
         RegCloseKey( hReg );
     }
+    
+    init_sc_manager( retval, hKey );
 
-    TRACE("returning %p\n", hKey);
+    TRACE("returning %p\n", retval);
 
-    return hKey;
+    return (SC_HANDLE) retval;
 }
 
 
@@ -332,8 +688,112 @@
 BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl,
                             LPSERVICE_STATUS lpServiceStatus )
 {
-    FIXME("(%p,%ld,%p): stub\n",hService,dwControl,lpServiceStatus);
-    return TRUE;
+    struct sc_handle *hsvc = hService;
+    WCHAR object_name[ MAX_PATH ];
+    HANDLE shmem = NULL, mutex = NULL;
+    HANDLE request_event = NULL, reply_event = NULL;
+    SERVICE_SHMEM *shmem_ptr = NULL;
+    DWORD lasterr, r;
+    BOOL ret = FALSE, mutex_acquired = FALSE;
+
+    /* open and acquire mutex */
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szMutexNameW, hsvc->hdata.service.service_name );
+    mutex = OpenMutexW( MUTEX_ALL_ACCESS, FALSE, object_name );
+    if( NULL == mutex )
+    {
+        SetLastError( ERROR_SERVICE_NOT_ACTIVE );
+        return FALSE;
+    }
+    r = WaitForSingleObject( mutex, 30000 );
+    if( WAIT_FAILED == r )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+    if( WAIT_TIMEOUT == r )
+    {
+        lasterr = ERROR_SERVICE_REQUEST_TIMEOUT;
+        goto done;
+    }
+    if( WAIT_ABANDONED == r )
+    {
+        lasterr = ERROR_SERVICE_NOT_ACTIVE;
+        goto done;
+    }
+    mutex_acquired = TRUE;
+
+    /* open events and shared memory */
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szRequestEventNameW, hsvc->hdata.service.service_name );
+    request_event = OpenEventW( EVENT_ALL_ACCESS, FALSE, object_name );
+    if( NULL == request_event )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szReplyEventNameW, hsvc->hdata.service.service_name );
+    reply_event = OpenEventW( EVENT_ALL_ACCESS, FALSE, object_name );
+    if( NULL == reply_event )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szShMemNameW, hsvc->hdata.service.service_name );
+    shmem = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, object_name );
+    if( NULL == shmem )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+    shmem_ptr = MapViewOfFile( shmem, FILE_MAP_ALL_ACCESS,
+                               0, 0, sizeof(SERVICE_SHMEM) );
+    if( NULL == shmem_ptr )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+
+    /* send request */
+    /* FIXME: check dwControl against controls accepted */
+    shmem_ptr->code = dwControl;
+    SetEvent( request_event );
+
+    /* wait for reply */
+    r = WaitForSingleObject( reply_event, 30000 );
+    if( WAIT_FAILED == r )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+    if( WAIT_TIMEOUT == r )
+    {
+        lasterr = ERROR_SERVICE_REQUEST_TIMEOUT;
+        goto done;
+    }
+
+    if( shmem_ptr->code != 0 )
+    {
+        lasterr = shmem_ptr->code;
+        goto done;
+    }
+
+    /* allright */
+    memcpy( lpServiceStatus, &shmem_ptr->status, sizeof(SERVICE_STATUS) );
+    ret = TRUE;
+    lasterr = 0;
+
+done:
+    if( shmem_ptr != NULL ) UnmapViewOfFile( shmem_ptr );
+    if( shmem != NULL ) CloseHandle( shmem );
+    if( reply_event != NULL ) CloseHandle( reply_event );
+    if( request_event != NULL ) CloseHandle( request_event );
+    if( mutex_acquired ) ReleaseMutex( mutex );
+    if( mutex != NULL ) CloseHandle( mutex );
+    SetLastError( lasterr );
+    return ret;
 }
 
 
@@ -349,12 +809,13 @@
  *  Success: TRUE
  *  Failure: FALSE
  */
+
 BOOL WINAPI
 CloseServiceHandle( SC_HANDLE hSCObject )
 {
     TRACE("(%p)\n", hSCObject);
 
-    RegCloseKey(hSCObject);
+    free_sc_handle( (struct sc_handle*) hSCObject );
 
     return TRUE;
 }
@@ -389,7 +850,6 @@
     return ret;
 }
 
-
 /******************************************************************************
  * OpenServiceW [ADVAPI32.@]
  *
@@ -398,19 +858,30 @@
 SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
                                DWORD dwDesiredAccess)
 {
+    struct sc_handle *hscm = hSCManager;
+    struct sc_handle *retval;
     HKEY hKey;
     long r;
 
     TRACE("(%p,%p,%ld)\n",hSCManager, lpServiceName,
           dwDesiredAccess);
 
-    r = RegOpenKeyExW(hSCManager, lpServiceName, 0, KEY_ALL_ACCESS, &hKey );
+    retval = alloc_sc_handle( SC_HTYPE_SERVICE, lpServiceName );
+    if( NULL == retval )
+ return NULL;
+
+    r = RegOpenKeyExW( hscm->hKey, lpServiceName, 0, KEY_ALL_ACCESS, &hKey );
     if (r!=ERROR_SUCCESS)
-        return 0;
+    {
+ free_sc_handle( retval );
+        return NULL;
+    }
+    
+    init_sc_service( retval, hKey, hscm );
 
-    TRACE("returning %p\n",hKey);
+    TRACE("returning %p\n",retval);
 
-    return hKey;
+    return (SC_HANDLE) retval;
 }
 
 /******************************************************************************
@@ -425,6 +896,8 @@
                   LPCWSTR lpDependencies, LPCWSTR lpServiceStartName,
                   LPCWSTR lpPassword )
 {
+    struct sc_handle *hscm = hSCManager;
+    struct sc_handle *retval;
     HKEY hKey;
     LONG r;
     DWORD dp;
@@ -439,41 +912,47 @@
     FIXME("%p %s %s\n", hSCManager, 
           debugstr_w(lpServiceName), debugstr_w(lpDisplayName));
 
-    r = RegCreateKeyExW(hSCManager, lpServiceName, 0, NULL,
+    retval = alloc_sc_handle( SC_HTYPE_SERVICE, lpServiceName );
+    if( NULL == retval )
+ return NULL;
+
+    r = RegCreateKeyExW(hscm->hKey, lpServiceName, 0, NULL,
                        REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp);
     if (r!=ERROR_SUCCESS)
-        return 0;
+        goto failure;
 
     if (dp != REG_CREATED_NEW_KEY)
-        return 0;
+        goto failure;
+
+    init_sc_service( retval, hKey, hscm );
 
     if(lpDisplayName)
     {
         r = RegSetValueExW(hKey, szDisplayName, 0, REG_SZ, (LPBYTE)lpDisplayName,
                            (strlenW(lpDisplayName)+1)*sizeof(WCHAR) );
         if (r!=ERROR_SUCCESS)
-            return 0;
+            goto failure;
     }
 
     r = RegSetValueExW(hKey, szType, 0, REG_DWORD, (LPVOID)&dwServiceType, sizeof (DWORD) );
     if (r!=ERROR_SUCCESS)
-        return 0;
+        goto failure;
 
     r = RegSetValueExW(hKey, szStart, 0, REG_DWORD, (LPVOID)&dwStartType, sizeof (DWORD) );
     if (r!=ERROR_SUCCESS)
-        return 0;
+        goto failure;
 
     r = RegSetValueExW(hKey, szError, 0, REG_DWORD,
                            (LPVOID)&dwErrorControl, sizeof (DWORD) );
     if (r!=ERROR_SUCCESS)
-        return 0;
+        goto failure;
 
     if(lpBinaryPathName)
     {
         r = RegSetValueExW(hKey, szImagePath, 0, REG_SZ, (LPBYTE)lpBinaryPathName,
                            (strlenW(lpBinaryPathName)+1)*sizeof(WCHAR) );
         if (r!=ERROR_SUCCESS)
-            return 0;
+            goto failure;
     }
 
     if(lpLoadOrderGroup)
@@ -481,7 +960,7 @@
         r = RegSetValueExW(hKey, szGroup, 0, REG_SZ, (LPBYTE)lpLoadOrderGroup,
                            (strlenW(lpLoadOrderGroup)+1)*sizeof(WCHAR) );
         if (r!=ERROR_SUCCESS)
-            return 0;
+            goto failure;
     }
 
     if(lpDependencies)
@@ -496,7 +975,7 @@
         r = RegSetValueExW(hKey, szDependencies, 0, REG_MULTI_SZ,
                            (LPBYTE)lpDependencies, len );
         if (r!=ERROR_SUCCESS)
-            return 0;
+            goto failure;
     }
 
     if(lpPassword)
@@ -509,7 +988,11 @@
         FIXME("Don't know how to add a ServiceStartName for a service.\n");
     }
 
-    return hKey;
+    return (SC_HANDLE) retval;
+    
+failure:
+    free_sc_handle( retval );
+    return NULL;
 }
 
 
@@ -608,63 +1091,31 @@
  */
 BOOL WINAPI DeleteService( SC_HANDLE hService )
 {
+    struct sc_handle *hsvc = hService;
     WCHAR valname[MAX_PATH+1];
     INT index = 0;
     LONG rc;
-    DWORD value = 0x1;
     DWORD size;
-    HKEY hKey;
-
-    static const WCHAR szDeleted[] = {'D','e','l','e','t','e','d',0};
 
     FIXME("(%p): stub\n",hService);
    
     size = MAX_PATH+1; 
     /* Clean out the values */
-    rc = RegEnumValueW(hService, index, valname,&size,0,0,0,0);
+    rc = RegEnumValueW(hsvc->hKey, index, valname,&size,0,0,0,0);
     while (rc == ERROR_SUCCESS)
     {
-        RegDeleteValueW(hService,valname);
+        RegDeleteValueW(hsvc->hKey,valname);
         index++;
         size = MAX_PATH+1; 
-        rc = RegEnumValueW(hService, index, valname, &size,0,0,0,0);
+        rc = RegEnumValueW(hsvc->hKey, index, valname, &size,0,0,0,0);
     }
 
-    /* tag for deletion */
-    RegSetValueExW(hService, szDeleted, 0, REG_DWORD, (LPVOID)&value, 
-                    sizeof (DWORD) );
+    RegCloseKey(hsvc->hKey);
+    hsvc->hKey = NULL;
 
-    RegCloseKey(hService);
-
-    /* find and delete the key */
-    rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szServiceManagerKey,0,
-                        KEY_ALL_ACCESS, &hKey );
-    index = 0;
-    size = MAX_PATH+1; 
-    rc = RegEnumKeyExW(hKey,0, valname, &size, 0, 0, 0, 0);
-    while (rc == ERROR_SUCCESS)
-    {
-        HKEY checking;
-        rc = RegOpenKeyExW(hKey,valname,0,KEY_ALL_ACCESS,&checking);
-        if (rc == ERROR_SUCCESS)
-        {
-            DWORD deleted = 0;
-            DWORD size = sizeof(DWORD);
-            rc = RegQueryValueExW(checking, szDeleted , NULL, NULL,
-                                  (LPVOID)&deleted, &size);
-            if (deleted)
-            {
-                RegDeleteValueW(checking,szDeleted);
-                RegDeleteKeyW(hKey,valname);
-            }
-            else
-                index ++;
-            RegCloseKey(checking);
-        }
-        size = MAX_PATH+1; 
-        rc = RegEnumKeyExW(hKey, index, valname, &size, 0, 0, 0, 0);
-    }
-    RegCloseKey(hKey);
+    /* delete the key */
+    RegDeleteKeyW(hsvc->hdata.service.sc_manager->hKey,
+                  hsvc->hdata.service.service_name);
 
     return TRUE;
 }
@@ -684,8 +1135,6 @@
  *  - 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.
  *
@@ -737,63 +1186,145 @@
 StartServiceW( SC_HANDLE hService, DWORD dwNumServiceArgs,
                  LPCWSTR *lpServiceArgVectors )
 {
-    static const WCHAR  _WaitServiceStartW[]  = {'A','D','V','A','P','I','_','W',
-                                                'a','i','t','S','e','r','v','i',
-                                                'c','e','S','t','a','r','t',0};
     static const WCHAR  _ImagePathW[]  = {'I','m','a','g','e','P','a','t','h',0};
-                                                
+    static const WCHAR  _TypeW[]  = {'T','y','p','e',0};
+
+    struct sc_handle *hsvc = hService;
     WCHAR path[MAX_PATH],str[MAX_PATH];
-    DWORD type,size;
+    HANDLE hLock, wait;
+    LPWSTR shmem_lock = NULL;
+    LPWSTR argptr, endptr;
+    HANDLE hSvcMem = NULL;
+    SERVICE_SHMEM *shmem_service = NULL;
+    DWORD ServiceType, type, size, lasterr, i;
     long r;
-    HANDLE data,wait;
     PROCESS_INFORMATION procinfo;
     STARTUPINFOW startupinfo;
+    BOOL ret = FALSE;
+    
     TRACE("(%p,%ld,%p)\n",hService,dwNumServiceArgs,
           lpServiceArgVectors);
 
     size = sizeof(str);
-    r = RegQueryValueExW(hService, _ImagePathW, NULL, &type, (LPVOID)str, &size);
+    r = RegQueryValueExW(hsvc->hKey, _ImagePathW, NULL, &type, (LPVOID)str, &size);
     if (r!=ERROR_SUCCESS)
         return FALSE;
     ExpandEnvironmentStringsW(str,path,sizeof(path));
 
-    TRACE("Starting service %s\n", debugstr_w(path) );
-
-    data = CreateSemaphoreW(NULL,1,1,_ServiceStartDataW);
-    if (!data)
+    size = sizeof(DWORD);
+    r = RegQueryValueExW(hsvc->hKey, _TypeW,
+                         NULL, &type, (LPVOID) &ServiceType, &size);
+    if (r!=ERROR_SUCCESS || type != REG_DWORD)
     {
-        ERR("Couldn't create data semaphore\n");
+        ERR("Couldn't read service type; r=%ld, type=%lu\n", r, type);
         return FALSE;
     }
-    wait = CreateSemaphoreW(NULL,0,1,_WaitServiceStartW);
-    if (!wait)
+
+    /* FIXME: what about interactive services? */
+    
+    ServiceType &= ~SERVICE_INTERACTIVE_PROCESS;
+
+    if( SERVICE_WIN32_OWN_PROCESS != ServiceType
+        && SERVICE_WIN32_SHARE_PROCESS != ServiceType )
     {
-        ERR("Couldn't create wait semaphore\n");
+        ERR("Invalid service type %lu\n",ServiceType);
+        /* FIXME: what error code should be here? */
+        SetLastError(ERROR_ACCESS_DENIED);
         return FALSE;
     }
 
+    /* lock service database and get pointer to shared memory */
+    hLock = LockServiceDatabase( hsvc->hdata.service.sc_manager );
+    if( NULL == hLock )
+        return FALSE;
+
     /*
-     * FIXME: lpServiceArgsVectors need to be stored and returned to
-     *        the service when it calls StartServiceCtrlDispatcher
-     *
-     * Chuck these in a global (yuk) so we can pass them to
-     * another process - address space separation will break this.
+     * FIXME: start dependent services
      */
 
-    r = WaitForSingleObject(data,INFINITE);
+    shmem_lock = MapViewOfFile( hLock, FILE_MAP_ALL_ACCESS,
+                                0, 0, LOCK_OBJECT_SIZE );
+    if( NULL == shmem_lock )
+    {
+        ERR("Couldn't map shared memory\n");
+        lasterr = GetLastError();
+        goto done;
+    }
+
+    /* set argv[0] (service name) for service thread */
+    strcpyW( shmem_lock, hsvc->hdata.service.service_name );
+
+    /* create dedicated shared memory object for service */
+    snprintfW( str, MAX_PATH, szServiceObjectNameFmtW,
+               szShMemNameW, shmem_lock );
+    hSvcMem = CreateFileMappingW( INVALID_HANDLE_VALUE,
+                                  NULL,
+                                  PAGE_READWRITE,
+                                  0, sizeof(SERVICE_SHMEM),
+                                  str );
+    if( NULL == hSvcMem )
+    {
+        ERR("Couldn't create shared memory object\n");
+        lasterr = GetLastError();
+        goto done;
+    }
+    if( GetLastError() == ERROR_ALREADY_EXISTS )
+    {
+        lasterr = ERROR_SERVICE_ALREADY_RUNNING;
+        goto done;
+    }
+    shmem_service = MapViewOfFile( hSvcMem, FILE_MAP_ALL_ACCESS,
+                                   0, 0, sizeof(SERVICE_SHMEM) );
+    if( NULL == shmem_service )
+    {
+        ERR("Couldn't map shared memory\n");
+        lasterr = GetLastError();
+        goto done;
+    }
+
+    /* copy service args */
+    shmem_service->argc = dwNumServiceArgs;
+    argptr = &shmem_service->argdata[0];
+    endptr = argptr + sizeof(shmem_service->argdata)
+                      / sizeof(shmem_service->argdata[0]);
+    for( i = 0; i < dwNumServiceArgs; i++ )
+    {
+        int len = strlenW( argptr ) + 1;
+        if( argptr + len >= endptr )
+        {
+            /* FIXME: use dynamic allocation */
+            shmem_service->argc = i;
+            break;
+        }
+        strcpyW( argptr, lpServiceArgVectors[ i ] );
+        argptr += len;
+    }
 
-    if( r == WAIT_FAILED)
-        return FALSE;
+    if( SERVICE_WIN32_SHARE_PROCESS == ServiceType )
+    {
+        /*
+         * FIXME: implement SERVICE_WIN32_SHARE_PROCESS startup.
+         * We need CreateRemoteThread for this.
+         */
+        FIXME(": SERVICE_WIN32_SHARE_PROCESS\n");
+ lasterr = ERROR_ACCESS_DENIED;
+        goto done;
+    }
 
-    FIXME("problematic because of address space separation.\n");
-    start_dwNumServiceArgs    = dwNumServiceArgs;
-    start_lpServiceArgVectors = (LPWSTR *)lpServiceArgVectors;
+    /* create wait object for process startup */
+    wait = CreateEventW(NULL,FALSE,FALSE,szWaitServiceStartW);
+    if( NULL == wait )
+    {
+        ERR("Couldn't create wait event\n");
+        lasterr = GetLastError();
+        goto done;
+    }
 
     ZeroMemory(&startupinfo,sizeof(STARTUPINFOW));
     startupinfo.cb = sizeof(STARTUPINFOW);
 
-    r = CreateProcessW(path,
-                   NULL,
+    r = CreateProcessW(NULL,  /* name of executable module */
+                   path,  /* command line string */
                    NULL,  /* process security attribs */
                    NULL,  /* thread security attribs */
                    FALSE, /* inherit handles */
@@ -802,23 +1333,44 @@
                    NULL,  /* current directory */
                    &startupinfo,  /* startup info */
                    &procinfo); /* process info */
-
     if(r == FALSE)
     {
         ERR("Couldn't start process\n");
-        /* ReleaseSemaphore(data, 1, NULL);
-        return FALSE; */
+        lasterr = GetLastError();
+        CloseHandle( wait );
+        goto done;
     }
+    CloseHandle( procinfo.hThread );
 
     /* docs for StartServiceCtrlDispatcher say this should be 30 sec */
     r = WaitForSingleObject(wait,30000);
-
-    ReleaseSemaphore(data, 1, NULL);
-
-    if( r == WAIT_FAILED)
-        return FALSE;
-
-    return TRUE;
+    if( WAIT_FAILED == r )
+    {
+        lasterr = GetLastError();
+        CloseHandle( procinfo.hProcess );
+        goto done;
+    }
+    if( WAIT_TIMEOUT == r )
+    {
+        TerminateProcess( procinfo.hProcess, 1 );
+        CloseHandle( procinfo.hProcess );
+        lasterr = ERROR_SERVICE_REQUEST_TIMEOUT;
+        goto done;
+    }
+    CloseHandle( procinfo.hProcess );
+    CloseHandle( wait );
+
+    /* allright */
+    lasterr = 0;
+    ret = TRUE;
+
+done:
+    if( shmem_service != NULL ) UnmapViewOfFile( shmem_service );
+    if( hSvcMem != NULL ) CloseHandle( hSvcMem );
+    if( shmem_lock != NULL ) UnmapViewOfFile( shmem_lock );
+    UnlockServiceDatabase( hLock );
+    SetLastError( lasterr );
+    return ret;
 }
 
 /******************************************************************************
@@ -832,30 +1384,91 @@
 BOOL WINAPI
 QueryServiceStatus( SC_HANDLE hService, LPSERVICE_STATUS lpservicestatus )
 {
-    LONG r;
+    struct sc_handle *hsvc = hService;
+    WCHAR object_name[ MAX_PATH ];
+    HANDLE mutex, shmem = NULL;
+    SERVICE_SHMEM *shmem_ptr = NULL;
+    DWORD lasterr, r;
     DWORD type, val, size;
+    BOOL ret = FALSE, mutex_acquired = FALSE;
 
-    FIXME("(%p,%p) partial\n",hService,lpservicestatus);
+    /* try to open mutex */
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szMutexNameW, hsvc->hdata.service.service_name );
+    mutex = OpenMutexW( MUTEX_ALL_ACCESS, FALSE, object_name );
+    if( NULL == mutex )
+    {
+stopped:
+        /* service stopped */
+        /* read the service type from the registry */
+        size = sizeof(val);
+        r = RegQueryValueExA(hsvc->hKey, "Type", NULL, &type, (LPBYTE)&val, &size);
+        if(type!=REG_DWORD)
+        {
+            ERR("invalid Type\n");
+            return FALSE;
+        }
+        lpservicestatus->dwServiceType = val;
+        lpservicestatus->dwCurrentState            = SERVICE_STOPPED;
+        lpservicestatus->dwControlsAccepted        = 0;
+        lpservicestatus->dwWin32ExitCode           = NO_ERROR;
+        lpservicestatus->dwServiceSpecificExitCode = 0;
+        lpservicestatus->dwCheckPoint              = 0;
+        lpservicestatus->dwWaitHint                = 0;
+
+        return TRUE;
+    }
 
-    /* read the service type from the registry */
-    size = sizeof(val);
-    r = RegQueryValueExA(hService, "Type", NULL, &type, (LPBYTE)&val, &size);
-    if(type!=REG_DWORD)
+    /*  acquire mutex */
+    r = WaitForSingleObject( mutex, 30000 );
+    if( WAIT_FAILED == r )
     {
-        ERR("invalid Type\n");
-        return FALSE;
+        lasterr = GetLastError();
+        goto done;
     }
-    lpservicestatus->dwServiceType = val;
-    /* FIXME: how are these determined or read from the registry? */
-    /* SERVICE: unavailable=0, stopped=1, starting=2, running=3? */;
-    lpservicestatus->dwCurrentState            = 1;
-    lpservicestatus->dwControlsAccepted        = 0;
-    lpservicestatus->dwWin32ExitCode           = NO_ERROR;
-    lpservicestatus->dwServiceSpecificExitCode = 0;
-    lpservicestatus->dwCheckPoint              = 0;
-    lpservicestatus->dwWaitHint                = 0;
+    if( WAIT_TIMEOUT == r )
+    {
+        lasterr = ERROR_SERVICE_REQUEST_TIMEOUT;
+        goto done;
+    }
+    mutex_acquired = TRUE;
 
-    return TRUE;
+    if( WAIT_ABANDONED == r )
+    {
+        lasterr = ERROR_SERVICE_NOT_ACTIVE;
+        CloseHandle( mutex );
+        goto stopped;
+    }
+
+    /* open shared memory */
+    snprintfW( object_name, MAX_PATH, szServiceObjectNameFmtW,
+               szShMemNameW, hsvc->hdata.service.service_name );
+    shmem = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, object_name );
+    if( NULL == shmem )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+    shmem_ptr = MapViewOfFile( shmem, FILE_MAP_ALL_ACCESS,
+                               0, 0, sizeof(SERVICE_SHMEM) );
+    if( NULL == shmem_ptr )
+    {
+        lasterr = GetLastError();
+        goto done;
+    }
+
+    /* allright */
+    memcpy( lpservicestatus, &shmem_ptr->status, sizeof(SERVICE_STATUS) );
+    ret = TRUE;
+    lasterr = 0;
+
+done:
+    if( shmem_ptr != NULL ) UnmapViewOfFile( shmem_ptr );
+    if( shmem != NULL ) CloseHandle( shmem );
+    if( mutex_acquired ) ReleaseMutex( mutex );
+    CloseHandle( mutex );
+    SetLastError( lasterr );
+    return ret;
 }
 
 /******************************************************************************
@@ -891,9 +1504,135 @@
                      LPQUERY_SERVICE_CONFIGA lpServiceConfig,
                      DWORD cbBufSize, LPDWORD pcbBytesNeeded)
 {
-    FIXME("%p %p %ld %p\n", hService, lpServiceConfig,
+    static const CHAR szDisplayName[] = {
+        'D','i','s','p','l','a','y','N','a','m','e', 0 };
+    static const CHAR szType[] = {'T','y','p','e',0};
+    static const CHAR szStart[] = {'S','t','a','r','t',0};
+    static const CHAR szError[] = {
+        'E','r','r','o','r','C','o','n','t','r','o','l', 0};
+    static const CHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0};
+    static const CHAR szGroup[] = {'G','r','o','u','p',0};
+    static const CHAR szDependencies[] = { 
+        'D','e','p','e','n','d','e','n','c','i','e','s',0};
+    struct sc_handle *hsvc = hService;
+    CHAR str[ MAX_PATH ], path[ MAX_PATH ];
+    LONG r;
+    DWORD type, val, sz, total, n;
+    LPBYTE p;
+
+    TRACE("%p %p %ld %p\n", hService, lpServiceConfig,
            cbBufSize, pcbBytesNeeded);
-    return FALSE;
+
+    /* calculate the size required first */
+    total = sizeof (QUERY_SERVICE_CONFIGA);
+
+    sz = sizeof(str);
+    r = RegQueryValueExA( hsvc->hKey, szImagePath, 0, &type, str, &sz );
+    if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
+    {
+ ExpandEnvironmentStringsA(str,path,sizeof(path));
+        total += strlen(path) + 1;
+    }
+    else
+    {
+ /* FIXME: set last error */
+ return FALSE;
+    }
+
+    /* FIXME: ImagePath is required, how about other members? */
+    sz = 0;
+    r = RegQueryValueExA( hsvc->hKey, szGroup, 0, &type, NULL, &sz );
+    if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
+        total += sz;
+
+    sz = 0;
+    r = RegQueryValueExA( hsvc->hKey, szDependencies, 0, &type, NULL, &sz );
+    if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) )
+        total += sz;
+
+    sz = 0;
+    r = RegQueryValueExA( hsvc->hKey, szStart, 0, &type, NULL, &sz );
+    if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
+        total += sz;
+
+    sz = 0;
+    r = RegQueryValueExA( hsvc->hKey, szDisplayName, 0, &type, NULL, &sz );
+    if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
+        total += sz;
+
+    /* if there's not enough memory, return an error */
+    if( total > *pcbBytesNeeded )
+    {
+        *pcbBytesNeeded = total;
+        SetLastError( ERROR_INSUFFICIENT_BUFFER );
+        return FALSE;
+    }
+
+    *pcbBytesNeeded = total;
+    ZeroMemory( lpServiceConfig, total );
+
+    sz = sizeof val;
+    r = RegQueryValueExA( hsvc->hKey, szType, 0, &type, (LPBYTE)&val, &sz );
+    if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
+        lpServiceConfig->dwServiceType = val;
+
+    sz = sizeof val;
+    r = RegQueryValueExA( hsvc->hKey, szStart, 0, &type, (LPBYTE)&val, &sz );
+    if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
+        lpServiceConfig->dwStartType = val;
+
+    sz = sizeof val;
+    r = RegQueryValueExA( hsvc->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_CONFIGA);
+
+    sz = sizeof(str);
+    r = RegQueryValueExA( hsvc->hKey, szImagePath, 0, &type, str, &sz );
+    if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
+    {
+ ExpandEnvironmentStringsA(str,path,sizeof(path));
+ strncpy( (LPSTR) p, path, n );
+ *(p + n - 1) = 0;
+        lpServiceConfig->lpBinaryPathName = (LPSTR) p;
+ sz = strlen( path ) + 1;
+        p += sz;
+        n -= sz;
+    }
+    else
+    {
+ /* FIXME: set last error */
+ return FALSE;
+    }
+
+    sz = n;
+    r = RegQueryValueExA( hsvc->hKey, szGroup, 0, &type, p, &sz );
+    if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
+    {
+        lpServiceConfig->lpLoadOrderGroup = (LPSTR) p;
+        p += sz;
+        n -= sz;
+    }
+
+    sz = n;
+    r = RegQueryValueExA( hsvc->hKey, szDependencies, 0, &type, p, &sz );
+    if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
+    {
+        lpServiceConfig->lpDependencies = (LPSTR) 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;
 }
 
 /******************************************************************************
@@ -914,6 +1653,8 @@
     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};
+    struct sc_handle *hsvc = hService;
+    WCHAR str[ MAX_PATH ], path[ MAX_PATH ];
     LONG r;
     DWORD type, val, sz, total, n;
     LPBYTE p;
@@ -924,28 +1665,37 @@
     /* calculate the size required first */
     total = sizeof (QUERY_SERVICE_CONFIGW);
 
-    sz = 0;
-    r = RegQueryValueExW( hService, szImagePath, 0, &type, NULL, &sz );
-    if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
-        total += sz;
+    sz = sizeof(str);
+    r = RegQueryValueExW( hsvc->hKey, szImagePath, 0, &type, (LPBYTE) str, &sz );
+    if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
+    {
+ ExpandEnvironmentStringsW(str,path,sizeof(path));
+        total += sizeof(WCHAR) * (strlenW(path) + 1);
+    }
+    else
+    {
+ /* FIXME: set last error */
+ return FALSE;
+    }
 
+    /* FIXME: ImagePath is required, how about other members? */
     sz = 0;
-    r = RegQueryValueExW( hService, szGroup, 0, &type, NULL, &sz );
+    r = RegQueryValueExW( hsvc->hKey, szGroup, 0, &type, NULL, &sz );
     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
         total += sz;
 
     sz = 0;
-    r = RegQueryValueExW( hService, szDependencies, 0, &type, NULL, &sz );
+    r = RegQueryValueExW( hsvc->hKey, szDependencies, 0, &type, NULL, &sz );
     if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) )
         total += sz;
 
     sz = 0;
-    r = RegQueryValueExW( hService, szStart, 0, &type, NULL, &sz );
+    r = RegQueryValueExW( hsvc->hKey, szStart, 0, &type, NULL, &sz );
     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
         total += sz;
 
     sz = 0;
-    r = RegQueryValueExW( hService, szDisplayName, 0, &type, NULL, &sz );
+    r = RegQueryValueExW( hsvc->hKey, szDisplayName, 0, &type, NULL, &sz );
     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
         total += sz;
 
@@ -961,17 +1711,17 @@
     ZeroMemory( lpServiceConfig, total );
 
     sz = sizeof val;
-    r = RegQueryValueExW( hService, szType, 0, &type, (LPBYTE)&val, &sz );
+    r = RegQueryValueExW( hsvc->hKey, szType, 0, &type, (LPBYTE)&val, &sz );
     if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
         lpServiceConfig->dwServiceType = val;
 
     sz = sizeof val;
-    r = RegQueryValueExW( hService, szStart, 0, &type, (LPBYTE)&val, &sz );
+    r = RegQueryValueExW( hsvc->hKey, szStart, 0, &type, (LPBYTE)&val, &sz );
     if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
         lpServiceConfig->dwStartType = val;
 
     sz = sizeof val;
-    r = RegQueryValueExW( hService, szError, 0, &type, (LPBYTE)&val, &sz );
+    r = RegQueryValueExW( hsvc->hKey, szError, 0, &type, (LPBYTE)&val, &sz );
     if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
         lpServiceConfig->dwErrorControl = val;
 
@@ -979,17 +1729,26 @@
     p = (LPBYTE) &lpServiceConfig[1];
     n = total - sizeof (QUERY_SERVICE_CONFIGW);
 
-    sz = n;
-    r = RegQueryValueExW( hService, szImagePath, 0, &type, p, &sz );
-    if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
-    {
+    sz = sizeof(str);
+    r = RegQueryValueExW( hsvc->hKey, szImagePath, 0, &type, (LPBYTE) str, &sz );
+    if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
+    {
+ ExpandEnvironmentStringsW(str,path,sizeof(path));
+ strncpyW( (LPWSTR) p, path, n >> 1 );
+ *(LPWSTR) (p + n - sizeof(WCHAR)) = 0;
         lpServiceConfig->lpBinaryPathName = (LPWSTR) p;
+ sz = sizeof(WCHAR) * (strlenW( path ) + 1);
         p += sz;
         n -= sz;
     }
+    else
+    {
+ /* FIXME: set last error */
+ return FALSE;
+    }
 
     sz = n;
-    r = RegQueryValueExW( hService, szGroup, 0, &type, p, &sz );
+    r = RegQueryValueExW( hsvc->hKey, szGroup, 0, &type, p, &sz );
     if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
     {
         lpServiceConfig->lpLoadOrderGroup = (LPWSTR) p;
@@ -998,7 +1757,7 @@
     }
 
     sz = n;
-    r = RegQueryValueExW( hService, szDependencies, 0, &type, p, &sz );
+    r = RegQueryValueExW( hsvc->hKey, szDependencies, 0, &type, p, &sz );
     if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
     {
         lpServiceConfig->lpDependencies = (LPWSTR) p;
@@ -1063,6 +1822,8 @@
 BOOL WINAPI ChangeServiceConfig2W( SC_HANDLE hService, DWORD dwInfoLevel, 
     LPVOID lpInfo)
 {
+    struct sc_handle *hsvc = hService;
+
     if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
     {
         static const WCHAR szDescription[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
@@ -1071,9 +1832,9 @@
         {
             TRACE("Setting Description to %s\n",debugstr_w(sd->lpDescription));
             if (sd->lpDescription[0] == 0)
-                RegDeleteValueW(hService,szDescription);
+                RegDeleteValueW(hsvc->hKey,szDescription);
             else
-                RegSetValueExW(hService, szDescription, 0, REG_SZ,
+                RegSetValueExW(hsvc->hKey, szDescription, 0, REG_SZ,
                                         (LPVOID)sd->lpDescription,
                                  sizeof(WCHAR)*(strlenW(sd->lpDescription)+1));
         }




More information about the wine-patches mailing list