[SERVICES]
[reactos.git] / reactos / base / system / services / rpcserver.c
index cc45ff5..a42bc8f 100644 (file)
@@ -11,7 +11,6 @@
 /* INCLUDES ****************************************************************/
 
 #include "services.h"
-#include "svcctl_s.h"
 
 #define NDEBUG
 #include <debug.h>
@@ -78,6 +77,7 @@ typedef struct _SERVICE_HANDLE
    SERVICE_STOP | \
    SERVICE_START)
 
+#define TAG_ARRAY_SIZE 32
 
 /* VARIABLES ***************************************************************/
 
@@ -91,7 +91,7 @@ static GENERIC_MAPPING
 ScmServiceMapping = {SERVICE_READ,
                      SERVICE_WRITE,
                      SERVICE_EXECUTE,
-                     SC_MANAGER_ALL_ACCESS};
+                     SERVICE_ALL_ACCESS};
 
 
 /* FUNCTIONS ***************************************************************/
@@ -144,18 +144,18 @@ ScmCreateManagerHandle(LPWSTR lpDatabaseName,
 
     if (_wcsicmp(lpDatabaseName, SERVICES_FAILED_DATABASEW) == 0)
     {
-        DPRINT("Database %S, does not exist\n",lpDatabaseName);
+        DPRINT("Database %S, does not exist\n", lpDatabaseName);
         return ERROR_DATABASE_DOES_NOT_EXIST;
     }
     else if (_wcsicmp(lpDatabaseName, SERVICES_ACTIVE_DATABASEW) != 0)
     {
-        DPRINT("Invalid Database name %S.\n",lpDatabaseName);
+        DPRINT("Invalid Database name %S.\n", lpDatabaseName);
         return ERROR_INVALID_NAME;
     }
 
-    Ptr = (MANAGER_HANDLE*) HeapAlloc(GetProcessHeap(),
+    Ptr = HeapAlloc(GetProcessHeap(),
                     HEAP_ZERO_MEMORY,
-                    sizeof(MANAGER_HANDLE) + (wcslen(lpDatabaseName) + 1) * sizeof(WCHAR));
+                    FIELD_OFFSET(MANAGER_HANDLE, DatabaseName[wcslen(lpDatabaseName) + 1]));
     if (Ptr == NULL)
         return ERROR_NOT_ENOUGH_MEMORY;
 
@@ -175,7 +175,7 @@ ScmCreateServiceHandle(PSERVICE lpServiceEntry,
 {
     PSERVICE_HANDLE Ptr;
 
-    Ptr = (SERVICE_HANDLE*) HeapAlloc(GetProcessHeap(),
+    Ptr = HeapAlloc(GetProcessHeap(),
                     HEAP_ZERO_MEMORY,
                     sizeof(SERVICE_HANDLE));
     if (Ptr == NULL)
@@ -264,651 +264,830 @@ ScmCheckAccess(SC_HANDLE Handle,
 DWORD
 ScmAssignNewTag(PSERVICE lpService)
 {
-    /* FIXME */
-    DPRINT("Assigning new tag to service %S\n", lpService->lpServiceName);
-    lpService->dwTag = 0;
-    return ERROR_SUCCESS;
-}
+    HKEY hKey = NULL;
+    DWORD dwError;
+    DWORD dwGroupTagCount = 0;
+    PDWORD pdwGroupTags = NULL;
+    DWORD dwFreeTag = 0;
+    DWORD dwTagUsedBase = 1;
+    BOOLEAN TagUsed[TAG_ARRAY_SIZE];
+    INT nTagOffset;
+    DWORD i;
+    DWORD cbDataSize;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
 
+    ASSERT(lpService != NULL);
+    ASSERT(lpService->lpGroup != NULL);
 
-/* Internal recursive function */
-/* Need to search for every dependency on every service */
-static DWORD
-Int_EnumDependentServicesW(HKEY hServicesKey,
-                           PSERVICE lpService,
-                           DWORD dwServiceState,
-                           PSERVICE *lpServices,
-                           LPDWORD pcbBytesNeeded,
-                           LPDWORD lpServicesReturned)
-{
-    DWORD dwError = ERROR_SUCCESS;
-    WCHAR szNameBuf[MAX_PATH];
-    WCHAR szValueBuf[MAX_PATH];
-    WCHAR *lpszNameBuf = szNameBuf;
-    WCHAR *lpszValueBuf = szValueBuf;
-    DWORD dwSize;
-    DWORD dwNumSubKeys;
-    DWORD dwIteration;
-    PSERVICE lpCurrentService;
-    HKEY hServiceEnumKey;
-    DWORD dwCurrentServiceState = SERVICE_ACTIVE;
-    DWORD dwDependServiceStrPtr = 0;
-    DWORD dwRequiredSize = 0;
+    dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                            L"System\\CurrentControlSet\\Control\\GroupOrderList",
+                            0,
+                            KEY_READ,
+                            &hKey);
 
-    /* Get the number of service keys */
-    dwError = RegQueryInfoKeyW(hServicesKey,
-                               NULL,
-                               NULL,
-                               NULL,
-                               &dwNumSubKeys,
-                               NULL,
-                               NULL,
-                               NULL,
+    if (dwError != ERROR_SUCCESS)
+        goto findFreeTag;
+
+    /* query value length */
+    cbDataSize = 0;
+    dwError = RegQueryValueExW(hKey,
+                               lpService->lpGroup->szGroupName,
                                NULL,
                                NULL,
                                NULL,
-                               NULL);
-    if (dwError != ERROR_SUCCESS)
+                               &cbDataSize);
+
+    if (dwError != ERROR_SUCCESS && dwError != ERROR_MORE_DATA)
+        goto findFreeTag;
+
+    pdwGroupTags = HeapAlloc(GetProcessHeap(), 0, cbDataSize);
+    if (!pdwGroupTags)
     {
-        DPRINT("ERROR! Unable to get number of services keys.\n");
-        return dwError;
+        dwError = ERROR_NOT_ENOUGH_MEMORY;
+        goto cleanup;
     }
 
-    /* Iterate the service keys to see if another service depends on the this service */
-    for (dwIteration = 0; dwIteration < dwNumSubKeys; dwIteration++)
-    {
-        dwSize = MAX_PATH;
-        dwError = RegEnumKeyExW(hServicesKey,
-                                dwIteration,
-                                lpszNameBuf,
-                                &dwSize,
-                                NULL,
-                                NULL,
-                                NULL,
-                                NULL);
-        if (dwError != ERROR_SUCCESS)
-            return dwError;
+    dwError = RegQueryValueExW(hKey,
+                               lpService->lpGroup->szGroupName,
+                               NULL,
+                               NULL,
+                               (LPBYTE)pdwGroupTags,
+                               &cbDataSize);
 
-        /* Open the Service key */
-        dwError = RegOpenKeyExW(hServicesKey,
-                                lpszNameBuf,
-                                0,
-                                KEY_READ,
-                                &hServiceEnumKey);
-        if (dwError != ERROR_SUCCESS)
-            return dwError;
+    if (dwError != ERROR_SUCCESS)
+        goto findFreeTag;
 
-        dwSize = MAX_PATH;
+    if (cbDataSize < sizeof(pdwGroupTags[0]))
+        goto findFreeTag;
 
-        /* Check for the DependOnService Value */
-        dwError = RegQueryValueExW(hServiceEnumKey,
-                                   L"DependOnService",
-                                   NULL,
-                                   NULL,
-                                   (LPBYTE)lpszValueBuf,
-                                   &dwSize);
+    dwGroupTagCount = min(pdwGroupTags[0], cbDataSize / sizeof(pdwGroupTags[0]) - 1);
 
-        /* FIXME: Handle load order. */
+findFreeTag:
+    do
+    {
+        /* mark all tags as unused */
+        for (i = 0; i < TAG_ARRAY_SIZE; i++)
+            TagUsed[i] = FALSE;
 
-        /* If the service found has a DependOnService value */
-        if (dwError == ERROR_SUCCESS)
+        /* mark tags in GroupOrderList as used */
+        for (i = 1; i <= dwGroupTagCount; i++)
         {
-            dwDependServiceStrPtr = 0;
+            nTagOffset = pdwGroupTags[i] - dwTagUsedBase;
+            if (nTagOffset >= 0 && nTagOffset < TAG_ARRAY_SIZE)
+                TagUsed[nTagOffset] = TRUE;
+        }
 
-            /* Can be more than one Dependencies in the DependOnService string */
-            while (wcslen(lpszValueBuf + dwDependServiceStrPtr) > 0)
+        /* mark tags in service list as used */
+        ServiceEntry = lpService->ServiceListEntry.Flink;
+        while (ServiceEntry != &lpService->ServiceListEntry)
+        {
+            ASSERT(ServiceEntry != NULL);
+            CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
+            if (CurrentService->lpGroup == lpService->lpGroup)
             {
-                if (_wcsicmp(lpszValueBuf + dwDependServiceStrPtr, lpService->lpServiceName) == 0)
-                {
-                    /* Get the current enumed service pointer */
-                    lpCurrentService = ScmGetServiceEntryByName(lpszNameBuf);
-
-                    /* Check for valid Service */
-                    if (!lpCurrentService)
-                    {
-                        /* This should never happen! */
-                        DPRINT("This should not happen at this point, report to Developer\n");
-                        return ERROR_NOT_FOUND;
-                    }
-
-                    /* Determine state the service is in */
-                    if (lpCurrentService->Status.dwCurrentState == SERVICE_STOPPED)
-                        dwCurrentServiceState = SERVICE_INACTIVE;
-
-                    /* If the ServiceState matches that requested or searching for SERVICE_STATE_ALL */
-                    if ((dwCurrentServiceState == dwServiceState) ||
-                        (dwServiceState == SERVICE_STATE_ALL))
-                    {
-                        /* Calculate the required size */
-                        dwRequiredSize += sizeof(SERVICE_STATUS);
-                        dwRequiredSize += ((wcslen(lpCurrentService->lpServiceName) + 1) * sizeof(WCHAR));
-                        dwRequiredSize += ((wcslen(lpCurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
-
-                        /* Add the size for service name and display name pointers */
-                        dwRequiredSize += (2 * sizeof(PVOID));
-
-                        /* increase the BytesNeeded size */
-                        *pcbBytesNeeded = *pcbBytesNeeded + dwRequiredSize;
+                nTagOffset = CurrentService->dwTag - dwTagUsedBase;
+                if (nTagOffset >= 0 && nTagOffset < TAG_ARRAY_SIZE)
+                    TagUsed[nTagOffset] = TRUE;
+            }
 
-                        /* Don't fill callers buffer yet, as MSDN read that the last service with dependency
-                           comes first */
+            ServiceEntry = ServiceEntry->Flink;
+        }
 
-                        /* Recursive call to check for its dependencies */
-                        Int_EnumDependentServicesW(hServicesKey,
-                                                   lpCurrentService,
-                                                   dwServiceState,
-                                                   lpServices,
-                                                   pcbBytesNeeded,
-                                                   lpServicesReturned);
+        /* find unused tag, if any */
+        for (i = 0; i < TAG_ARRAY_SIZE; i++)
+        {
+            if (!TagUsed[i])
+            {
+                dwFreeTag = dwTagUsedBase + i;
+                break;
+            }
+        }
 
-                        /* If the lpServices is valid set the service pointer */
-                        if (lpServices)
-                            lpServices[*lpServicesReturned] = lpCurrentService;
+        dwTagUsedBase += TAG_ARRAY_SIZE;
+    } while (!dwFreeTag);
 
-                        *lpServicesReturned = *lpServicesReturned + 1;
-                    }
-                }
+cleanup:
+    if (pdwGroupTags)
+        HeapFree(GetProcessHeap(), 0, pdwGroupTags);
 
-                dwDependServiceStrPtr += (wcslen(lpszValueBuf + dwDependServiceStrPtr) + 1);
-            }
-        }
-        else if (*pcbBytesNeeded)
-        {
-            dwError = ERROR_SUCCESS;
-        }
+    if (hKey)
+        RegCloseKey(hKey);
 
-        RegCloseKey(hServiceEnumKey);
+    if (dwFreeTag)
+    {
+        lpService->dwTag = dwFreeTag;
+        DPRINT("Assigning new tag %lu to service %S in group %S\n",
+               lpService->dwTag, lpService->lpServiceName, lpService->lpGroup->szGroupName);
+        dwError = ERROR_SUCCESS;
+    }
+    else
+    {
+        DPRINT1("Failed to assign new tag to service %S, error=%lu\n",
+                lpService->lpServiceName, dwError);
     }
 
     return dwError;
 }
 
 
-/* Function 0 */
-DWORD RCloseServiceHandle(
-    LPSC_RPC_HANDLE hSCObject)
+/* Create a path suitable for the bootloader out of the full path */
+DWORD
+ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
 {
-    PMANAGER_HANDLE hManager;
-    PSERVICE_HANDLE hService;
-    PSERVICE lpService;
-    HKEY hServicesKey;
-    DWORD dwError;
-    DWORD pcbBytesNeeded = 0;
-    DWORD dwServicesReturned = 0;
+    DWORD ServiceNameLen, BufferSize, ExpandedLen;
+    WCHAR Dest;
+    WCHAR *Expanded;
+    UNICODE_STRING NtPathName, SystemRoot, LinkTarget;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    NTSTATUS Status;
+    HANDLE SymbolicLinkHandle;
 
-    DPRINT("RCloseServiceHandle() called\n");
+    DPRINT("ScmConvertToBootPathName %S\n", CanonName);
 
-    DPRINT("hSCObject = %p\n", *hSCObject);
+    if (!RelativeName)
+        return ERROR_INVALID_PARAMETER;
 
-    if (*hSCObject == 0)
-        return ERROR_INVALID_HANDLE;
+    *RelativeName = NULL;
 
-    hManager = ScmGetServiceManagerFromHandle(*hSCObject);
-    hService = ScmGetServiceFromHandle(*hSCObject);
+    ServiceNameLen = wcslen(CanonName);
 
-    if (hManager != NULL)
+    /* First check, if it's already good */
+    if (ServiceNameLen > 12 &&
+        !_wcsnicmp(L"\\SystemRoot\\", CanonName, 12))
     {
-        DPRINT("Found manager handle\n");
-
-        /* FIXME: add handle cleanup code */
+        *RelativeName = HeapAlloc(GetProcessHeap(),
+                                  HEAP_ZERO_MEMORY,
+                                  (ServiceNameLen + 1) * sizeof(WCHAR));
+        if (*RelativeName == NULL)
+        {
+            DPRINT("Error allocating memory for boot driver name!\n");
+            return ERROR_NOT_ENOUGH_MEMORY;
+        }
 
-        HeapFree(GetProcessHeap(), 0, hManager);
-        hManager = NULL;
+        /* Copy it */
+        wcscpy(*RelativeName, CanonName);
 
-        DPRINT("RCloseServiceHandle() done\n");
+        DPRINT("Bootdriver name %S\n", *RelativeName);
         return ERROR_SUCCESS;
     }
-    else if (hService != NULL)
+
+    /* If it has %SystemRoot% prefix, substitute it to \System*/
+    if (ServiceNameLen > 13 &&
+        !_wcsnicmp(L"%SystemRoot%\\", CanonName, 13))
     {
-        DPRINT("Found service handle\n");
+        /* There is no +sizeof(wchar_t) because the name is less by 1 wchar */
+        *RelativeName = HeapAlloc(GetProcessHeap(),
+                                  HEAP_ZERO_MEMORY,
+                                  ServiceNameLen * sizeof(WCHAR));
 
-        /* Get the pointer to the service record */
-        lpService = hService->ServiceEntry;
+        if (*RelativeName == NULL)
+        {
+            DPRINT("Error allocating memory for boot driver name!\n");
+            return ERROR_NOT_ENOUGH_MEMORY;
+        }
 
-        /* FIXME: add handle cleanup code */
+        /* Copy it */
+        wcscpy(*RelativeName, L"\\SystemRoot\\");
+        wcscat(*RelativeName, CanonName + 13);
 
-        /* Free the handle */
-        HeapFree(GetProcessHeap(), 0, hService);
-        hService = NULL;
+        DPRINT("Bootdriver name %S\n", *RelativeName);
+        return ERROR_SUCCESS;
+    }
 
-        ASSERT(lpService->dwRefCount > 0);
+    /* Get buffer size needed for expanding env strings */
+    BufferSize = ExpandEnvironmentStringsW(L"%SystemRoot%\\", &Dest, 1);
 
-        lpService->dwRefCount--;
-        DPRINT("CloseServiceHandle - lpService->dwRefCount %u\n",
-               lpService->dwRefCount);
+    if (BufferSize <= 1)
+    {
+        DPRINT("Error during a call to ExpandEnvironmentStringsW()\n");
+        return ERROR_INVALID_ENVIRONMENT;
+    }
 
-        if (lpService->dwRefCount == 0)
-        {
-            /* If this service has been marked for deletion */
-            if (lpService->bDeleted)
-            {
-                /* Open the Services Reg key */
-                dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
-                                        L"System\\CurrentControlSet\\Services",
-                                        0,
-                                        KEY_SET_VALUE | KEY_READ,
-                                        &hServicesKey);
-                if (dwError != ERROR_SUCCESS)
-                {
-                    DPRINT("Failed to open services key\n");
-                    return dwError;
-                }
+    /* Allocate memory, since the size is known now */
+    Expanded = HeapAlloc(GetProcessHeap(),
+                         HEAP_ZERO_MEMORY,
+                         (BufferSize + 1) * sizeof(WCHAR));
+    if (!Expanded)
+    {
+        DPRINT("Error allocating memory for boot driver name!\n");
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
 
-                /* Call the internal function with NULL, just to get bytes we need */
-                Int_EnumDependentServicesW(hServicesKey,
-                                           lpService,
-                                           SERVICE_ACTIVE,
-                                           NULL,
-                                           &pcbBytesNeeded,
-                                           &dwServicesReturned);
+    /* Expand it */
+    if (ExpandEnvironmentStringsW(L"%SystemRoot%\\", Expanded, BufferSize) >
+        BufferSize)
+    {
+        DPRINT("Error during a call to ExpandEnvironmentStringsW()\n");
+        HeapFree(GetProcessHeap(), 0, Expanded);
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
 
-                /* if pcbBytesNeeded returned a value then there are services running that are dependent on this service*/
-                if (pcbBytesNeeded)
-                {
-                    DPRINT("Deletion failed due to running dependencies.\n");
-                    RegCloseKey(hServicesKey);
-                    return ERROR_SUCCESS;
-                }
+    /* Convert to NT-style path */
+    if (!RtlDosPathNameToNtPathName_U(Expanded, &NtPathName, NULL, NULL))
+    {
+        DPRINT("Error during a call to RtlDosPathNameToNtPathName_U()\n");
+        return ERROR_INVALID_ENVIRONMENT;
+    }
 
-                /* There are no references and no runnning dependencies,
-                   it is now safe to delete the service */
+    DPRINT("Converted to NT-style %wZ\n", &NtPathName);
 
-                /* Delete the Service Key */
-                dwError = RegDeleteKeyW(hServicesKey,
-                                        lpService->lpServiceName);
+    /* No need to keep the dos-path anymore */
+    HeapFree(GetProcessHeap(), 0, Expanded);
 
-                RegCloseKey(hServicesKey);
+    /* Copy it to the allocated place */
+    Expanded = HeapAlloc(GetProcessHeap(),
+                         HEAP_ZERO_MEMORY,
+                         NtPathName.Length + sizeof(UNICODE_NULL));
+    if (!Expanded)
+    {
+        DPRINT("Error allocating memory for boot driver name!\n");
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
 
-                if (dwError != ERROR_SUCCESS)
-                {
-                    DPRINT("Failed to Delete the Service Registry key\n");
-                    return dwError;
-                }
+    ExpandedLen = NtPathName.Length / sizeof(WCHAR);
+    wcsncpy(Expanded, NtPathName.Buffer, ExpandedLen);
+    Expanded[ExpandedLen] = UNICODE_NULL;
 
-                /* Delete the Service */
-                ScmDeleteServiceRecord(lpService);
-            }
+    if (ServiceNameLen > ExpandedLen &&
+        !_wcsnicmp(Expanded, CanonName, ExpandedLen))
+    {
+        /* Only \SystemRoot\ is missing */
+        *RelativeName = HeapAlloc(GetProcessHeap(),
+                                  HEAP_ZERO_MEMORY,
+                                  (ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR));
+        if (*RelativeName == NULL)
+        {
+            DPRINT("Error allocating memory for boot driver name!\n");
+            HeapFree(GetProcessHeap(), 0, Expanded);
+            return ERROR_NOT_ENOUGH_MEMORY;
         }
 
-        DPRINT("RCloseServiceHandle() done\n");
+        wcscpy(*RelativeName, L"\\SystemRoot\\");
+        wcscat(*RelativeName, CanonName + ExpandedLen);
+
+        RtlFreeUnicodeString(&NtPathName);
         return ERROR_SUCCESS;
     }
 
-    DPRINT("Invalid handle tag (Tag %lx)\n", hManager->Handle.Tag);
+    /* The most complex case starts here */
+    RtlInitUnicodeString(&SystemRoot, L"\\SystemRoot");
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &SystemRoot,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
 
-    return ERROR_INVALID_HANDLE;
+    /* Open this symlink */
+    Status = NtOpenSymbolicLinkObject(&SymbolicLinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
+
+    if (NT_SUCCESS(Status))
+    {
+        LinkTarget.Length = 0;
+        LinkTarget.MaximumLength = 0;
+
+        DPRINT("Opened symbolic link object\n");
+
+        Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
+        if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL)
+        {
+            /* Check if required buffer size is sane */
+            if (BufferSize > 0xFFFD)
+            {
+                DPRINT("Too large buffer required\n");
+
+                if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
+                HeapFree(GetProcessHeap(), 0, Expanded);
+                return ERROR_NOT_ENOUGH_MEMORY;
+            }
+
+            /* Alloc the string */
+            LinkTarget.Length = (USHORT)BufferSize;
+            LinkTarget.MaximumLength = LinkTarget.Length + sizeof(UNICODE_NULL);
+            LinkTarget.Buffer = HeapAlloc(GetProcessHeap(),
+                                          HEAP_ZERO_MEMORY,
+                                          LinkTarget.MaximumLength);
+            if (!LinkTarget.Buffer)
+            {
+                DPRINT("Unable to alloc buffer\n");
+                if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
+                HeapFree(GetProcessHeap(), 0, Expanded);
+                return ERROR_NOT_ENOUGH_MEMORY;
+            }
+
+            /* Do a real query now */
+            Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
+            if (NT_SUCCESS(Status))
+            {
+                DPRINT("LinkTarget: %wZ\n", &LinkTarget);
+
+                ExpandedLen = LinkTarget.Length / sizeof(WCHAR);
+                if ((ServiceNameLen > ExpandedLen) &&
+                    !_wcsnicmp(LinkTarget.Buffer, CanonName, ExpandedLen))
+                {
+                    *RelativeName = HeapAlloc(GetProcessHeap(),
+                                              HEAP_ZERO_MEMORY,
+                                              (ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR));
+
+                    if (*RelativeName == NULL)
+                    {
+                        DPRINT("Unable to alloc buffer\n");
+                        if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
+                        HeapFree(GetProcessHeap(), 0, Expanded);
+                        RtlFreeUnicodeString(&NtPathName);
+                        return ERROR_NOT_ENOUGH_MEMORY;
+                    }
+
+                    /* Copy it over, substituting the first part
+                       with SystemRoot */
+                    wcscpy(*RelativeName, L"\\SystemRoot\\");
+                    wcscat(*RelativeName, CanonName+ExpandedLen+1);
+
+                    /* Cleanup */
+                    if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
+                    HeapFree(GetProcessHeap(), 0, Expanded);
+                    RtlFreeUnicodeString(&NtPathName);
+
+                    /* Return success */
+                    return ERROR_SUCCESS;
+                }
+                else
+                {
+                    if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
+                    HeapFree(GetProcessHeap(), 0, Expanded);
+                    RtlFreeUnicodeString(&NtPathName);
+                    return ERROR_INVALID_PARAMETER;
+                }
+            }
+            else
+            {
+                DPRINT("Error, Status = %08X\n", Status);
+                if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
+                HeapFree(GetProcessHeap(), 0, Expanded);
+                RtlFreeUnicodeString(&NtPathName);
+                return ERROR_INVALID_PARAMETER;
+            }
+        }
+        else
+        {
+            DPRINT("Error, Status = %08X\n", Status);
+            if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
+            HeapFree(GetProcessHeap(), 0, Expanded);
+            RtlFreeUnicodeString(&NtPathName);
+            return ERROR_INVALID_PARAMETER;
+        }
+    }
+    else
+    {
+        /* Failure */
+        DPRINT("Error, Status = %08X\n", Status);
+        HeapFree(GetProcessHeap(), 0, Expanded);
+        return ERROR_INVALID_PARAMETER;
+    }
 }
 
 
-/* Function 1 */
-DWORD RControlService(
-    SC_RPC_HANDLE hService,
-    DWORD dwControl,
-    LPSERVICE_STATUS lpServiceStatus)
+DWORD
+ScmCanonDriverImagePath(DWORD dwStartType,
+                        const wchar_t *lpServiceName,
+                        wchar_t **lpCanonName)
 {
-    PSERVICE_HANDLE hSvc;
-    PSERVICE lpService;
-    ACCESS_MASK DesiredAccess;
-    DWORD dwError = ERROR_SUCCESS;
-    DWORD pcbBytesNeeded = 0;
-    DWORD dwServicesReturned = 0;
-    DWORD dwControlsAccepted;
-    DWORD dwCurrentState;
-    HKEY hServicesKey = NULL;
+    DWORD ServiceNameLen, Result;
+    UNICODE_STRING NtServiceName;
+    WCHAR *RelativeName;
+    const WCHAR *SourceName = lpServiceName;
 
-    DPRINT("RControlService() called\n");
+    /* Calculate the length of the service's name */
+    ServiceNameLen = wcslen(lpServiceName);
 
-    if (ScmShutdown)
-        return ERROR_SHUTDOWN_IN_PROGRESS;
+    /* 12 is wcslen(L"\\SystemRoot\\") */
+    if (ServiceNameLen > 12 &&
+        !_wcsnicmp(L"\\SystemRoot\\", lpServiceName, 12))
+    {
+        /* SystemRoot prefix is already included */
+        *lpCanonName = HeapAlloc(GetProcessHeap(),
+                                 HEAP_ZERO_MEMORY,
+                                 (ServiceNameLen + 1) * sizeof(WCHAR));
 
-    /* Check the service handle */
-    hSvc = ScmGetServiceFromHandle(hService);
-    if (hSvc == NULL)
+        if (*lpCanonName == NULL)
+        {
+            DPRINT("Error allocating memory for canonized service name!\n");
+            return ERROR_NOT_ENOUGH_MEMORY;
+        }
+
+        /* If it's a boot-time driver, it must be systemroot relative */
+        if (dwStartType == SERVICE_BOOT_START)
+            SourceName += 12;
+
+        /* Copy it */
+        wcscpy(*lpCanonName, SourceName);
+
+        DPRINT("Canonicalized name %S\n", *lpCanonName);
+        return NO_ERROR;
+    }
+
+    /* Check if it has %SystemRoot% (len=13) */
+    if (ServiceNameLen > 13 &&
+        !_wcsnicmp(L"%SystemRoot%\\", lpServiceName, 13))
     {
-        DPRINT1("Invalid service handle!\n");
-        return ERROR_INVALID_HANDLE;
+        /* Substitute %SystemRoot% with \\SystemRoot\\ */
+        *lpCanonName = HeapAlloc(GetProcessHeap(),
+                                 HEAP_ZERO_MEMORY,
+                                 (ServiceNameLen + 1) * sizeof(WCHAR));
+
+        if (*lpCanonName == NULL)
+        {
+            DPRINT("Error allocating memory for canonized service name!\n");
+            return ERROR_NOT_ENOUGH_MEMORY;
+        }
+
+        /* If it's a boot-time driver, it must be systemroot relative */
+        if (dwStartType == SERVICE_BOOT_START)
+            wcscpy(*lpCanonName, L"\\SystemRoot\\");
+
+        wcscat(*lpCanonName, lpServiceName + 13);
+
+        DPRINT("Canonicalized name %S\n", *lpCanonName);
+        return NO_ERROR;
     }
 
+    /* Check if it's a relative path name */
+    if (lpServiceName[0] != L'\\' && lpServiceName[1] != L':')
+    {
+        *lpCanonName = HeapAlloc(GetProcessHeap(),
+                                 HEAP_ZERO_MEMORY,
+                                 (ServiceNameLen + 1) * sizeof(WCHAR));
 
-    /* Check the service entry point */
-    lpService = hSvc->ServiceEntry;
-    if (lpService == NULL)
+        if (*lpCanonName == NULL)
+        {
+            DPRINT("Error allocating memory for canonized service name!\n");
+            return ERROR_NOT_ENOUGH_MEMORY;
+        }
+
+        /* Just copy it over without changing */
+        wcscpy(*lpCanonName, lpServiceName);
+
+        return NO_ERROR;
+    }
+
+    /* It seems to be a DOS path, convert it */
+    if (!RtlDosPathNameToNtPathName_U(lpServiceName, &NtServiceName, NULL, NULL))
     {
-        DPRINT1("lpService == NULL!\n"); 
-        return ERROR_INVALID_HANDLE;
+        DPRINT("RtlDosPathNameToNtPathName_U() failed!\n");
+        return ERROR_INVALID_PARAMETER;
     }
 
-    /* Check access rights */
-    switch (dwControl)
+    *lpCanonName = HeapAlloc(GetProcessHeap(),
+                             HEAP_ZERO_MEMORY,
+                             NtServiceName.Length + sizeof(WCHAR));
+
+    if (*lpCanonName == NULL)
     {
-        case SERVICE_CONTROL_STOP:
-            DesiredAccess = SERVICE_STOP;
-            break;
+        DPRINT("Error allocating memory for canonized service name!\n");
+        RtlFreeUnicodeString(&NtServiceName);
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
 
-        case SERVICE_CONTROL_PAUSE:
-        case SERVICE_CONTROL_CONTINUE:
-            DesiredAccess = SERVICE_PAUSE_CONTINUE;
-            break;
+    /* Copy the string */
+    wcsncpy(*lpCanonName, NtServiceName.Buffer, NtServiceName.Length / sizeof(WCHAR));
 
-        case SERVICE_INTERROGATE:
-            DesiredAccess = SERVICE_INTERROGATE;
-            break;
+    /* The unicode string is not needed anymore */
+    RtlFreeUnicodeString(&NtServiceName);
 
-        default:
-            if (dwControl >= 128 && dwControl <= 255)
-                DesiredAccess = SERVICE_USER_DEFINED_CONTROL;
-            else
-                DesiredAccess = SERVICE_QUERY_CONFIG |
-                                SERVICE_CHANGE_CONFIG |
-                                SERVICE_QUERY_STATUS |
-                                SERVICE_START |
-                                SERVICE_PAUSE_CONTINUE;
-            break;
+    if (dwStartType != SERVICE_BOOT_START)
+    {
+        DPRINT("Canonicalized name %S\n", *lpCanonName);
+        return NO_ERROR;
     }
 
-    if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
-                                  DesiredAccess))
-        return ERROR_ACCESS_DENIED;
+    /* The service is boot-started, so must be relative */
+    Result = ScmConvertToBootPathName(*lpCanonName, &RelativeName);
+    if (Result)
+    {
+        /* There is a problem, free name and return */
+        HeapFree(GetProcessHeap(), 0, *lpCanonName);
+        DPRINT("Error converting named!\n");
+        return Result;
+    }
 
-    if (dwControl == SERVICE_CONTROL_STOP)
+    ASSERT(RelativeName);
+
+    /* Copy that string */
+    wcscpy(*lpCanonName, RelativeName + 12);
+
+    /* Free the allocated buffer */
+    HeapFree(GetProcessHeap(), 0, RelativeName);
+
+    DPRINT("Canonicalized name %S\n", *lpCanonName);
+
+    /* Success */
+    return NO_ERROR;
+}
+
+
+/* Internal recursive function */
+/* Need to search for every dependency on every service */
+static DWORD
+Int_EnumDependentServicesW(HKEY hServicesKey,
+                           PSERVICE lpService,
+                           DWORD dwServiceState,
+                           PSERVICE *lpServices,
+                           LPDWORD pcbBytesNeeded,
+                           LPDWORD lpServicesReturned)
+{
+    DWORD dwError = ERROR_SUCCESS;
+    WCHAR szNameBuf[MAX_PATH];
+    WCHAR szValueBuf[MAX_PATH];
+    WCHAR *lpszNameBuf = szNameBuf;
+    WCHAR *lpszValueBuf = szValueBuf;
+    DWORD dwSize;
+    DWORD dwNumSubKeys;
+    DWORD dwIteration;
+    PSERVICE lpCurrentService;
+    HKEY hServiceEnumKey;
+    DWORD dwCurrentServiceState = SERVICE_ACTIVE;
+    DWORD dwDependServiceStrPtr = 0;
+    DWORD dwRequiredSize = 0;
+
+    /* Get the number of service keys */
+    dwError = RegQueryInfoKeyW(hServicesKey,
+                               NULL,
+                               NULL,
+                               NULL,
+                               &dwNumSubKeys,
+                               NULL,
+                               NULL,
+                               NULL,
+                               NULL,
+                               NULL,
+                               NULL,
+                               NULL);
+    if (dwError != ERROR_SUCCESS)
     {
-        /* Check if the service has dependencies running as windows
-           doesn't stop a service that does */
+        DPRINT("ERROR! Unable to get number of services keys.\n");
+        return dwError;
+    }
 
-        /* Open the Services Reg key */
-        dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
-                                L"System\\CurrentControlSet\\Services",
+    /* Iterate the service keys to see if another service depends on the this service */
+    for (dwIteration = 0; dwIteration < dwNumSubKeys; dwIteration++)
+    {
+        dwSize = MAX_PATH;
+        dwError = RegEnumKeyExW(hServicesKey,
+                                dwIteration,
+                                lpszNameBuf,
+                                &dwSize,
+                                NULL,
+                                NULL,
+                                NULL,
+                                NULL);
+        if (dwError != ERROR_SUCCESS)
+            return dwError;
+
+        /* Open the Service key */
+        dwError = RegOpenKeyExW(hServicesKey,
+                                lpszNameBuf,
                                 0,
                                 KEY_READ,
-                                &hServicesKey);
+                                &hServiceEnumKey);
         if (dwError != ERROR_SUCCESS)
-        {
-            DPRINT("Failed to open services key\n");
             return dwError;
-        }
 
-        /* Call the internal function with NULL, just to get bytes we need */
-        Int_EnumDependentServicesW(hServicesKey,
-                                   lpService,
-                                   SERVICE_ACTIVE,
-                                   NULL,
-                                   &pcbBytesNeeded,
-                                   &dwServicesReturned);
-
-        RegCloseKey(hServicesKey);
+        dwSize = MAX_PATH;
 
-        /* If pcbBytesNeeded is not zero then there are services running that
-           are dependent on this service */
-        if (pcbBytesNeeded != 0)
-        {
-            DPRINT("Service has running dependencies. Failed to stop service.\n");
-            return ERROR_DEPENDENT_SERVICES_RUNNING;
-        }
-    }
+        /* Check for the DependOnService Value */
+        dwError = RegQueryValueExW(hServiceEnumKey,
+                                   L"DependOnService",
+                                   NULL,
+                                   NULL,
+                                   (LPBYTE)lpszValueBuf,
+                                   &dwSize);
 
-    if (lpService->Status.dwServiceType & SERVICE_DRIVER)
-    {
-        /* Send control code to the driver */
-        dwError = ScmControlDriver(lpService,
-                                   dwControl,
-                                   lpServiceStatus);
-    }
-    else
-    {
-        dwControlsAccepted = lpService->Status.dwControlsAccepted;
-        dwCurrentState = lpService->Status.dwCurrentState;
+        /* FIXME: Handle load order. */
 
-        /* Check the current state before sending a control request */
-        switch (dwCurrentState)
+        /* If the service found has a DependOnService value */
+        if (dwError == ERROR_SUCCESS)
         {
-            case SERVICE_STOP_PENDING:
-            case SERVICE_STOPPED:
-                return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
+            dwDependServiceStrPtr = 0;
 
-            case SERVICE_START_PENDING:
-                switch (dwControl)
+            /* Can be more than one Dependencies in the DependOnService string */
+            while (wcslen(lpszValueBuf + dwDependServiceStrPtr) > 0)
+            {
+                if (_wcsicmp(lpszValueBuf + dwDependServiceStrPtr, lpService->lpServiceName) == 0)
                 {
-                    case SERVICE_CONTROL_STOP:
-                        break;
+                    /* Get the current enumed service pointer */
+                    lpCurrentService = ScmGetServiceEntryByName(lpszNameBuf);
 
-                    case SERVICE_CONTROL_INTERROGATE:
-                        RtlCopyMemory(lpServiceStatus,
-                                      &lpService->Status,
-                                      sizeof(SERVICE_STATUS));
-                        return ERROR_SUCCESS;
+                    /* Check for valid Service */
+                    if (!lpCurrentService)
+                    {
+                        /* This should never happen! */
+                        DPRINT("This should not happen at this point, report to Developer\n");
+                        return ERROR_NOT_FOUND;
+                    }
 
-                    default:
-                        return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
-                }
-                break;
-        }
+                    /* Determine state the service is in */
+                    if (lpCurrentService->Status.dwCurrentState == SERVICE_STOPPED)
+                        dwCurrentServiceState = SERVICE_INACTIVE;
 
-        /* Check if the control code is acceptable to the service */
-        switch (dwControl)
-        {
-            case SERVICE_CONTROL_STOP:
-                if ((dwControlsAccepted & SERVICE_ACCEPT_STOP) == 0)
-                    return ERROR_INVALID_SERVICE_CONTROL;
-                break;
+                    /* If the ServiceState matches that requested or searching for SERVICE_STATE_ALL */
+                    if ((dwCurrentServiceState == dwServiceState) ||
+                        (dwServiceState == SERVICE_STATE_ALL))
+                    {
+                        /* Calculate the required size */
+                        dwRequiredSize += sizeof(SERVICE_STATUS);
+                        dwRequiredSize += ((wcslen(lpCurrentService->lpServiceName) + 1) * sizeof(WCHAR));
+                        dwRequiredSize += ((wcslen(lpCurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
 
-            case SERVICE_CONTROL_PAUSE:
-            case SERVICE_CONTROL_CONTINUE:
-                if ((dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE) == 0)
-                    return ERROR_INVALID_SERVICE_CONTROL;
-                break;
-        }
+                        /* Add the size for service name and display name pointers */
+                        dwRequiredSize += (2 * sizeof(PVOID));
 
-        /* Send control code to the service */
-        dwError = ScmControlService(lpService,
-                                    dwControl);
+                        /* increase the BytesNeeded size */
+                        *pcbBytesNeeded = *pcbBytesNeeded + dwRequiredSize;
 
-        /* Return service status information */
-        RtlCopyMemory(lpServiceStatus,
-                      &lpService->Status,
-                      sizeof(SERVICE_STATUS));
-    }
+                        /* Don't fill callers buffer yet, as MSDN read that the last service with dependency
+                           comes first */
 
-    if ((dwError == ERROR_SUCCESS) && (pcbBytesNeeded))
-        dwError = ERROR_DEPENDENT_SERVICES_RUNNING;
+                        /* Recursive call to check for its dependencies */
+                        Int_EnumDependentServicesW(hServicesKey,
+                                                   lpCurrentService,
+                                                   dwServiceState,
+                                                   lpServices,
+                                                   pcbBytesNeeded,
+                                                   lpServicesReturned);
 
-    if (dwError == ERROR_SUCCESS &&
-        dwControl == SERVICE_CONTROL_STOP && 
-        lpServiceStatus->dwCurrentState == SERVICE_STOPPED)
-    {
-        lpService->ProcessId = 0; /* FIXME */
-        lpService->ThreadId = 0;
-    }
+                        /* If the lpServices is valid set the service pointer */
+                        if (lpServices)
+                            lpServices[*lpServicesReturned] = lpCurrentService;
+
+                        *lpServicesReturned = *lpServicesReturned + 1;
+                    }
+                }
+
+                dwDependServiceStrPtr += (wcslen(lpszValueBuf + dwDependServiceStrPtr) + 1);
+            }
+        }
+        else if (*pcbBytesNeeded)
+        {
+            dwError = ERROR_SUCCESS;
+        }
 
+        RegCloseKey(hServiceEnumKey);
+    }
 
     return dwError;
 }
 
 
-/* Function 2 */
-DWORD RDeleteService(
-    SC_RPC_HANDLE hService)
+/* Function 0 */
+DWORD RCloseServiceHandle(
+    LPSC_RPC_HANDLE hSCObject)
 {
-    PSERVICE_HANDLE hSvc;
+    PMANAGER_HANDLE hManager;
+    PSERVICE_HANDLE hService;
     PSERVICE lpService;
+    HKEY hServicesKey;
     DWORD dwError;
+    DWORD pcbBytesNeeded = 0;
+    DWORD dwServicesReturned = 0;
 
-    DPRINT("RDeleteService() called\n");
-
-    if (ScmShutdown)
-        return ERROR_SHUTDOWN_IN_PROGRESS;
-
-    hSvc = ScmGetServiceFromHandle(hService);
-    if (hSvc == NULL)
-    {
-        DPRINT1("Invalid service handle!\n");
-        return ERROR_INVALID_HANDLE;
-    }
+    DPRINT("RCloseServiceHandle() called\n");
 
-    if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
-                                  DELETE))
-        return ERROR_ACCESS_DENIED;
+    DPRINT("hSCObject = %p\n", *hSCObject);
 
-    lpService = hSvc->ServiceEntry;
-    if (lpService == NULL)
-    {
-        DPRINT("lpService == NULL!\n");
+    if (*hSCObject == 0)
         return ERROR_INVALID_HANDLE;
-    }
 
-    /* FIXME: Acquire service database lock exclusively */
+    hManager = ScmGetServiceManagerFromHandle(*hSCObject);
+    hService = ScmGetServiceFromHandle(*hSCObject);
 
-    if (lpService->bDeleted)
+    if (hManager != NULL)
     {
-        DPRINT("The service has already been marked for delete!\n");
-        return ERROR_SERVICE_MARKED_FOR_DELETE;
-    }
-
-    /* Mark service for delete */
-    lpService->bDeleted = TRUE;
-
-    dwError = ScmMarkServiceForDelete(lpService);
-
-    /* FIXME: Release service database lock */
-
-    DPRINT("RDeleteService() done\n");
-
-    return dwError;
-}
-
+        DPRINT("Found manager handle\n");
 
-/* Function 3 */
-DWORD RLockServiceDatabase(
-    SC_RPC_HANDLE hSCManager,
-    LPSC_RPC_LOCK lpLock)
-{
-    PMANAGER_HANDLE hMgr;
+        /* FIXME: add handle cleanup code */
 
-    DPRINT("RLockServiceDatabase() called\n");
+        HeapFree(GetProcessHeap(), 0, hManager);
+        hManager = NULL;
 
-    *lpLock = 0;
+        *hSCObject = NULL;
 
-    hMgr = ScmGetServiceManagerFromHandle(hSCManager);
-    if (hMgr == NULL)
-    {
-        DPRINT1("Invalid service manager handle!\n");
-        return ERROR_INVALID_HANDLE;
+        DPRINT("RCloseServiceHandle() done\n");
+        return ERROR_SUCCESS;
     }
+    else if (hService != NULL)
+    {
+        DPRINT("Found service handle\n");
 
-    if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess,
-                                  SC_MANAGER_LOCK))
-        return ERROR_ACCESS_DENIED;
-
-//    return ScmLockDatabase(0, hMgr->0xC, hLock);
-
-    /* FIXME: Lock the database */
-    *lpLock = (SC_RPC_LOCK)0x12345678; /* Dummy! */
-
-    return ERROR_SUCCESS;
-}
+        /* Lock the service database exlusively */
+        ScmLockDatabaseExclusive();
 
+        /* Get the pointer to the service record */
+        lpService = hService->ServiceEntry;
 
-/* Function 4 */
-DWORD RQueryServiceObjectSecurity(
-    SC_RPC_HANDLE hService,
-    SECURITY_INFORMATION dwSecurityInformation,
-    LPBYTE lpSecurityDescriptor,
-    DWORD cbBufSize,
-    LPBOUNDED_DWORD_256K pcbBytesNeeded)
-{
-    PSERVICE_HANDLE hSvc;
-    PSERVICE lpService;
-    ULONG DesiredAccess = 0;
-    NTSTATUS Status;
-    DWORD dwBytesNeeded;
-    DWORD dwError;
+        /* FIXME: add handle cleanup code */
 
+        /* Free the handle */
+        HeapFree(GetProcessHeap(), 0, hService);
+        hService = NULL;
 
-    SECURITY_DESCRIPTOR ObjectDescriptor;
+        ASSERT(lpService->dwRefCount > 0);
 
-    DPRINT("RQueryServiceObjectSecurity() called\n");
+        lpService->dwRefCount--;
+        DPRINT("CloseServiceHandle - lpService->dwRefCount %u\n",
+               lpService->dwRefCount);
 
-    hSvc = ScmGetServiceFromHandle(hService);
-    if (hSvc == NULL)
-    {
-        DPRINT1("Invalid service handle!\n");
-        return ERROR_INVALID_HANDLE;
-    }
+        if (lpService->dwRefCount == 0)
+        {
+            /* If this service has been marked for deletion */
+            if (lpService->bDeleted)
+            {
+                /* Open the Services Reg key */
+                dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                                        L"System\\CurrentControlSet\\Services",
+                                        0,
+                                        KEY_SET_VALUE | KEY_READ,
+                                        &hServicesKey);
+                if (dwError != ERROR_SUCCESS)
+                {
+                    DPRINT("Failed to open services key\n");
+                    ScmUnlockDatabase();
+                    return dwError;
+                }
 
-    if (dwSecurityInformation & (DACL_SECURITY_INFORMATION |
-                                 GROUP_SECURITY_INFORMATION |
-                                 OWNER_SECURITY_INFORMATION))
-        DesiredAccess |= READ_CONTROL;
+                /* Call the internal function with NULL, just to get bytes we need */
+                Int_EnumDependentServicesW(hServicesKey,
+                                           lpService,
+                                           SERVICE_ACTIVE,
+                                           NULL,
+                                           &pcbBytesNeeded,
+                                           &dwServicesReturned);
 
-    if (dwSecurityInformation & SACL_SECURITY_INFORMATION)
-        DesiredAccess |= ACCESS_SYSTEM_SECURITY;
+                /* if pcbBytesNeeded returned a value then there are services running that are dependent on this service */
+                if (pcbBytesNeeded)
+                {
+                    DPRINT("Deletion failed due to running dependencies.\n");
+                    RegCloseKey(hServicesKey);
+                    ScmUnlockDatabase();
+                    return ERROR_SUCCESS;
+                }
 
-    if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
-                                  DesiredAccess))
-    {
-        DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
-        return ERROR_ACCESS_DENIED;
-    }
+                /* There are no references and no runnning dependencies,
+                   it is now safe to delete the service */
 
-    lpService = hSvc->ServiceEntry;
-    if (lpService == NULL)
-    {
-        DPRINT("lpService == NULL!\n");
-        return ERROR_INVALID_HANDLE;
-    }
+                /* Delete the Service Key */
+                dwError = RegDeleteKeyW(hServicesKey,
+                                        lpService->lpServiceName);
 
-    /* FIXME: Lock the service list */
+                RegCloseKey(hServicesKey);
 
-    /* hack */
-    Status = RtlCreateSecurityDescriptor(&ObjectDescriptor, SECURITY_DESCRIPTOR_REVISION);
+                if (dwError != ERROR_SUCCESS)
+                {
+                    DPRINT("Failed to Delete the Service Registry key\n");
+                    ScmUnlockDatabase();
+                    return dwError;
+                }
 
-    Status = RtlQuerySecurityObject(&ObjectDescriptor  /* lpService->lpSecurityDescriptor */,
-                                    dwSecurityInformation,
-                                    (PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
-                                    cbBufSize,
-                                    &dwBytesNeeded);
+                /* Delete the Service */
+                ScmDeleteServiceRecord(lpService);
+            }
+        }
 
-    /* FIXME: Unlock the service list */
+        ScmUnlockDatabase();
 
-    if (NT_SUCCESS(Status))
-    {
-        *pcbBytesNeeded = dwBytesNeeded;
-        dwError = STATUS_SUCCESS;
-    }
-    else if (Status == STATUS_BUFFER_TOO_SMALL)
-    {
-        *pcbBytesNeeded = dwBytesNeeded;
-        dwError = ERROR_INSUFFICIENT_BUFFER;
-    }
-    else if (Status == STATUS_BAD_DESCRIPTOR_FORMAT)
-    {
-        dwError = ERROR_GEN_FAILURE;
-    }
-    else
-    {
-        dwError = RtlNtStatusToDosError(Status);
+        *hSCObject = NULL;
+
+        DPRINT("RCloseServiceHandle() done\n");
+        return ERROR_SUCCESS;
     }
 
-    return dwError;
+    DPRINT("Invalid handle tag (Tag %lx)\n", hManager->Handle.Tag);
+
+    return ERROR_INVALID_HANDLE;
 }
 
 
-/* Function 5 */
-DWORD RSetServiceObjectSecurity(
+/* Function 1 */
+DWORD RControlService(
     SC_RPC_HANDLE hService,
-    DWORD dwSecurityInformation,
-    LPBYTE lpSecurityDescriptor,
-    DWORD dwSecuityDescriptorSize)
+    DWORD dwControl,
+    LPSERVICE_STATUS lpServiceStatus)
 {
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService;
-    ULONG DesiredAccess = 0;
-    /* HANDLE hToken = NULL; */
-    HKEY hServiceKey;
-    /* NTSTATUS Status; */
-    DWORD dwError;
+    ACCESS_MASK DesiredAccess;
+    DWORD dwError = ERROR_SUCCESS;
+    DWORD pcbBytesNeeded = 0;
+    DWORD dwServicesReturned = 0;
+    DWORD dwControlsAccepted;
+    DWORD dwCurrentState;
+    HKEY hServicesKey = NULL;
 
-    DPRINT("RSetServiceObjectSecurity() called\n");
+    DPRINT("RControlService() called\n");
+
+    if (ScmShutdown)
+        return ERROR_SHUTDOWN_IN_PROGRESS;
 
+    /* Check the service handle */
     hSvc = ScmGetServiceFromHandle(hService);
     if (hSvc == NULL)
     {
@@ -916,112 +1095,162 @@ DWORD RSetServiceObjectSecurity(
         return ERROR_INVALID_HANDLE;
     }
 
-    if (dwSecurityInformation == 0 ||
-        dwSecurityInformation & ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
-        | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION))
-        return ERROR_INVALID_PARAMETER;
-
-    if (!RtlValidSecurityDescriptor((PSECURITY_DESCRIPTOR)lpSecurityDescriptor))
-        return ERROR_INVALID_PARAMETER;
-
-    if (dwSecurityInformation & SACL_SECURITY_INFORMATION)
-        DesiredAccess |= ACCESS_SYSTEM_SECURITY;
+    /* Check the service entry point */
+    lpService = hSvc->ServiceEntry;
+    if (lpService == NULL)
+    {
+        DPRINT1("lpService == NULL!\n");
+        return ERROR_INVALID_HANDLE;
+    }
 
-    if (dwSecurityInformation & DACL_SECURITY_INFORMATION)
-        DesiredAccess |= WRITE_DAC;
+    /* Check access rights */
+    switch (dwControl)
+    {
+        case SERVICE_CONTROL_STOP:
+            DesiredAccess = SERVICE_STOP;
+            break;
 
-    if (dwSecurityInformation & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION))
-        DesiredAccess |= WRITE_OWNER;
+        case SERVICE_CONTROL_PAUSE:
+        case SERVICE_CONTROL_CONTINUE:
+            DesiredAccess = SERVICE_PAUSE_CONTINUE;
+            break;
 
-    if ((dwSecurityInformation & OWNER_SECURITY_INFORMATION) &&
-        (((PISECURITY_DESCRIPTOR)lpSecurityDescriptor)->Owner == NULL))
-        return ERROR_INVALID_PARAMETER;
+        case SERVICE_CONTROL_INTERROGATE:
+            DesiredAccess = SERVICE_INTERROGATE;
+            break;
 
-    if ((dwSecurityInformation & GROUP_SECURITY_INFORMATION) &&
-        (((PISECURITY_DESCRIPTOR)lpSecurityDescriptor)->Group == NULL))
-        return ERROR_INVALID_PARAMETER;
+        default:
+            if (dwControl >= 128 && dwControl <= 255)
+                DesiredAccess = SERVICE_USER_DEFINED_CONTROL;
+            else
+                return ERROR_INVALID_PARAMETER;
+            break;
+    }
 
     if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
                                   DesiredAccess))
-    {
-        DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
         return ERROR_ACCESS_DENIED;
-    }
 
-    lpService = hSvc->ServiceEntry;
-    if (lpService == NULL)
-    {
-        DPRINT("lpService == NULL!\n");
-        return ERROR_INVALID_HANDLE;
-    }
+    /* Return the current service status information */
+    RtlCopyMemory(lpServiceStatus,
+                  &lpService->Status,
+                  sizeof(SERVICE_STATUS));
 
-    if (lpService->bDeleted)
-        return ERROR_SERVICE_MARKED_FOR_DELETE;
+    if (dwControl == SERVICE_CONTROL_STOP)
+    {
+        /* Check if the service has dependencies running as windows
+           doesn't stop a service that does */
 
-#if 0
-    RpcImpersonateClient(NULL);
+        /* Open the Services Reg key */
+        dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                                L"System\\CurrentControlSet\\Services",
+                                0,
+                                KEY_READ,
+                                &hServicesKey);
+        if (dwError != ERROR_SUCCESS)
+        {
+            DPRINT("Failed to open services key\n");
+            return dwError;
+        }
 
-    Status = NtOpenThreadToken(NtCurrentThread(),
-                               8,
-                               TRUE,
-                               &hToken);
-    if (!NT_SUCCESS(Status))
-        return RtlNtStatusToDosError(Status); 
+        /* Call the internal function with NULL, just to get bytes we need */
+        Int_EnumDependentServicesW(hServicesKey,
+                                   lpService,
+                                   SERVICE_ACTIVE,
+                                   NULL,
+                                   &pcbBytesNeeded,
+                                   &dwServicesReturned);
 
-    RpcRevertToSelf();
+        RegCloseKey(hServicesKey);
 
-    /* FIXME: Lock service database */
+        /* If pcbBytesNeeded is not zero then there are services running that
+           are dependent on this service */
+        if (pcbBytesNeeded != 0)
+        {
+            DPRINT("Service has running dependencies. Failed to stop service.\n");
+            return ERROR_DEPENDENT_SERVICES_RUNNING;
+        }
+    }
 
-    Status = RtlSetSecurityObject(dwSecurityInformation,
-                                  (PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
-                                  &lpService->lpSecurityDescriptor,
-                                  &ScmServiceMapping,
-                                  hToken);
-    if (!NT_SUCCESS(Status))
+    if (lpService->Status.dwServiceType & SERVICE_DRIVER)
     {
-        dwError = RtlNtStatusToDosError(Status);
-        goto Done;
+        /* Send control code to the driver */
+        dwError = ScmControlDriver(lpService,
+                                   dwControl,
+                                   lpServiceStatus);
     }
-#endif
+    else
+    {
+        dwControlsAccepted = lpService->Status.dwControlsAccepted;
+        dwCurrentState = lpService->Status.dwCurrentState;
 
-    dwError = ScmOpenServiceKey(lpService->lpServiceName,
-                                READ_CONTROL | KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
-                                &hServiceKey);
-    if (dwError != ERROR_SUCCESS)
-        goto Done;
+        /* Return ERROR_SERVICE_NOT_ACTIVE if the service has not been started */
+        if (lpService->lpImage == NULL || dwCurrentState == SERVICE_STOPPED)
+            return ERROR_SERVICE_NOT_ACTIVE;
 
-    UNIMPLEMENTED;
-    dwError = ERROR_SUCCESS;
-//    dwError = ScmWriteSecurityDescriptor(hServiceKey,
-//                                         lpService->lpSecurityDescriptor);
+        /* Check the current state before sending a control request */
+        switch (dwCurrentState)
+        {
+            case SERVICE_STOP_PENDING:
+            case SERVICE_STOPPED:
+                return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
 
-    RegFlushKey(hServiceKey);
-    RegCloseKey(hServiceKey);
+            case SERVICE_START_PENDING:
+                switch (dwControl)
+                {
+                    case SERVICE_CONTROL_STOP:
+                        break;
 
-Done:
+                    case SERVICE_CONTROL_INTERROGATE:
+                        RtlCopyMemory(lpServiceStatus,
+                                      &lpService->Status,
+                                      sizeof(SERVICE_STATUS));
+                        return ERROR_SUCCESS;
 
-#if 0
-    if (hToken != NULL)
-        NtClose(hToken);
-#endif
+                    default:
+                        return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
+                }
+                break;
+        }
+
+        /* Check if the control code is acceptable to the service */
+        switch (dwControl)
+        {
+            case SERVICE_CONTROL_STOP:
+                if ((dwControlsAccepted & SERVICE_ACCEPT_STOP) == 0)
+                    return ERROR_INVALID_SERVICE_CONTROL;
+                break;
+
+            case SERVICE_CONTROL_PAUSE:
+            case SERVICE_CONTROL_CONTINUE:
+                if ((dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE) == 0)
+                    return ERROR_INVALID_SERVICE_CONTROL;
+                break;
+        }
 
-    /* FIXME: Unlock service database */
+        /* Send control code to the service */
+        dwError = ScmControlService(lpService,
+                                    dwControl);
 
-    DPRINT("RSetServiceObjectSecurity() done (Error %lu)\n", dwError);
+        /* Return service status information */
+        RtlCopyMemory(lpServiceStatus,
+                      &lpService->Status,
+                      sizeof(SERVICE_STATUS));
+    }
 
     return dwError;
 }
 
 
-/* Function 6 */
-DWORD RQueryServiceStatus(
-    SC_RPC_HANDLE hService,
-    LPSERVICE_STATUS lpServiceStatus)
+/* Function 2 */
+DWORD RDeleteService(
+    SC_RPC_HANDLE hService)
 {
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService;
+    DWORD dwError;
 
-    DPRINT("RQueryServiceStatus() called\n");
+    DPRINT("RDeleteService() called\n");
 
     if (ScmShutdown)
         return ERROR_SHUTDOWN_IN_PROGRESS;
@@ -1034,11 +1263,8 @@ DWORD RQueryServiceStatus(
     }
 
     if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
-                                  SERVICE_QUERY_STATUS))
-    {
-        DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
+                                  DELETE))
         return ERROR_ACCESS_DENIED;
-    }
 
     lpService = hSvc->ServiceEntry;
     if (lpService == NULL)
@@ -1047,170 +1273,166 @@ DWORD RQueryServiceStatus(
         return ERROR_INVALID_HANDLE;
     }
 
-    ScmLockDatabaseShared();
+    /* Lock the service database exclusively */
+    ScmLockDatabaseExclusive();
 
-    /* Return service status information */
-    RtlCopyMemory(lpServiceStatus,
-                  &lpService->Status,
-                  sizeof(SERVICE_STATUS));
+    if (lpService->bDeleted)
+    {
+        DPRINT("The service has already been marked for delete!\n");
+        dwError = ERROR_SERVICE_MARKED_FOR_DELETE;
+        goto Done;
+    }
 
+    /* Mark service for delete */
+    lpService->bDeleted = TRUE;
+
+    dwError = ScmMarkServiceForDelete(lpService);
+
+Done:;
+    /* Unlock the service database */
     ScmUnlockDatabase();
 
-    return ERROR_SUCCESS;
+    DPRINT("RDeleteService() done\n");
+
+    return dwError;
 }
 
 
-static BOOL
-ScmIsValidServiceState(DWORD dwCurrentState)
+/* Function 3 */
+DWORD RLockServiceDatabase(
+    SC_RPC_HANDLE hSCManager,
+    LPSC_RPC_LOCK lpLock)
 {
-    switch (dwCurrentState)
-    {
-        case SERVICE_STOPPED:
-        case SERVICE_START_PENDING:
-        case SERVICE_STOP_PENDING:
-        case SERVICE_RUNNING:
-        case SERVICE_CONTINUE_PENDING:
-        case SERVICE_PAUSE_PENDING:
-        case SERVICE_PAUSED:
-            return TRUE;
+    PMANAGER_HANDLE hMgr;
 
-        default:
-            return FALSE;
+    DPRINT("RLockServiceDatabase() called\n");
+
+    *lpLock = 0;
+
+    hMgr = ScmGetServiceManagerFromHandle(hSCManager);
+    if (hMgr == NULL)
+    {
+        DPRINT1("Invalid service manager handle!\n");
+        return ERROR_INVALID_HANDLE;
     }
+
+    if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess,
+                                  SC_MANAGER_LOCK))
+        return ERROR_ACCESS_DENIED;
+
+//    return ScmLockDatabase(0, hMgr->0xC, hLock);
+
+    /* FIXME: Lock the database */
+    *lpLock = (SC_RPC_LOCK)0x12345678; /* Dummy! */
+
+    return ERROR_SUCCESS;
 }
 
 
-/* Function 7 */
-DWORD RSetServiceStatus(
-    RPC_SERVICE_STATUS_HANDLE hServiceStatus,
-    LPSERVICE_STATUS lpServiceStatus)
+/* Function 4 */
+DWORD RQueryServiceObjectSecurity(
+    SC_RPC_HANDLE hService,
+    SECURITY_INFORMATION dwSecurityInformation,
+    LPBYTE lpSecurityDescriptor,
+    DWORD cbBufSize,
+    LPBOUNDED_DWORD_256K pcbBytesNeeded)
 {
+    PSERVICE_HANDLE hSvc;
     PSERVICE lpService;
+    ULONG DesiredAccess = 0;
+    NTSTATUS Status;
+    DWORD dwBytesNeeded;
+    DWORD dwError;
 
-    DPRINT("RSetServiceStatus() called\n");
-    DPRINT("hServiceStatus = %p\n", hServiceStatus);
-    DPRINT("dwServiceType = %lu\n", lpServiceStatus->dwServiceType);
-    DPRINT("dwCurrentState = %lu\n", lpServiceStatus->dwCurrentState);
-    DPRINT("dwControlsAccepted = %lu\n", lpServiceStatus->dwControlsAccepted);
-    DPRINT("dwWin32ExitCode = %lu\n", lpServiceStatus->dwWin32ExitCode);
-    DPRINT("dwServiceSpecificExitCode = %lu\n", lpServiceStatus->dwServiceSpecificExitCode);
-    DPRINT("dwCheckPoint = %lu\n", lpServiceStatus->dwCheckPoint);
-    DPRINT("dwWaitHint = %lu\n", lpServiceStatus->dwWaitHint);
 
-    if (hServiceStatus == 0)
-    {
-        DPRINT("hServiceStatus == NULL!\n");
-        return ERROR_INVALID_HANDLE;
-    }
+    SECURITY_DESCRIPTOR ObjectDescriptor;
 
-    lpService = (PSERVICE)hServiceStatus;
-    if (lpService == NULL)
+    DPRINT("RQueryServiceObjectSecurity() called\n");
+
+    hSvc = ScmGetServiceFromHandle(hService);
+    if (hSvc == NULL)
     {
-        DPRINT("lpService == NULL!\n");
+        DPRINT1("Invalid service handle!\n");
         return ERROR_INVALID_HANDLE;
     }
 
-    /* Check current state */
-    if (!ScmIsValidServiceState(lpServiceStatus->dwCurrentState))
-    {
-        DPRINT("Invalid service state!\n");
-        return ERROR_INVALID_DATA;
-    }
+    if (dwSecurityInformation & (DACL_SECURITY_INFORMATION |
+                                 GROUP_SECURITY_INFORMATION |
+                                 OWNER_SECURITY_INFORMATION))
+        DesiredAccess |= READ_CONTROL;
 
-    /* Check service type */
-    if (!(lpServiceStatus->dwServiceType & SERVICE_WIN32) &&
-         (lpServiceStatus->dwServiceType & SERVICE_DRIVER))
+    if (dwSecurityInformation & SACL_SECURITY_INFORMATION)
+        DesiredAccess |= ACCESS_SYSTEM_SECURITY;
+
+    if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
+                                  DesiredAccess))
     {
-        DPRINT("Invalid service type!\n");
-        return ERROR_INVALID_DATA;
+        DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
+        return ERROR_ACCESS_DENIED;
     }
 
-    /* Check accepted controls */
-    if (lpServiceStatus->dwControlsAccepted & ~0xFF)
+    lpService = hSvc->ServiceEntry;
+    if (lpService == NULL)
     {
-        DPRINT("Invalid controls accepted!\n");
-        return ERROR_INVALID_DATA;
+        DPRINT("lpService == NULL!\n");
+        return ERROR_INVALID_HANDLE;
     }
 
-    ScmLockDatabaseExclusive();
-
-    RtlCopyMemory(&lpService->Status,
-                  lpServiceStatus,
-                  sizeof(SERVICE_STATUS));
-
-    ScmUnlockDatabase();
-
-    DPRINT("Set %S to %lu\n", lpService->lpDisplayName, lpService->Status.dwCurrentState);
-    DPRINT("RSetServiceStatus() done\n");
-
-    return ERROR_SUCCESS;
-}
+    /* Lock the service database */
+    ScmLockDatabaseShared();
 
 
-/* Function 8 */
-DWORD RUnlockServiceDatabase(
-    LPSC_RPC_LOCK Lock)
-{
-    UNIMPLEMENTED;
-    return ERROR_SUCCESS;
-}
+    /* hack */
+    Status = RtlCreateSecurityDescriptor(&ObjectDescriptor, SECURITY_DESCRIPTOR_REVISION);
 
+    Status = RtlQuerySecurityObject(&ObjectDescriptor  /* lpService->lpSecurityDescriptor */,
+                                    dwSecurityInformation,
+                                    (PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
+                                    cbBufSize,
+                                    &dwBytesNeeded);
 
-/* Function 9 */
-DWORD RNotifyBootConfigStatus(
-    SVCCTL_HANDLEW lpMachineName,
-    DWORD BootAcceptable)
-{
-    UNIMPLEMENTED;
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
+    /* Unlock the service database */
+    ScmUnlockDatabase();
 
+    if (NT_SUCCESS(Status))
+    {
+        *pcbBytesNeeded = dwBytesNeeded;
+        dwError = STATUS_SUCCESS;
+    }
+    else if (Status == STATUS_BUFFER_TOO_SMALL)
+    {
+        *pcbBytesNeeded = dwBytesNeeded;
+        dwError = ERROR_INSUFFICIENT_BUFFER;
+    }
+    else if (Status == STATUS_BAD_DESCRIPTOR_FORMAT)
+    {
+        dwError = ERROR_GEN_FAILURE;
+    }
+    else
+    {
+        dwError = RtlNtStatusToDosError(Status);
+    }
 
-/* Function 10 */
-DWORD RI_ScSetServiceBitsW(
-    RPC_SERVICE_STATUS_HANDLE hServiceStatus,
-    DWORD dwServiceBits,
-    int bSetBitsOn,
-    int bUpdateImmediately,
-    wchar_t *lpString)
-{
-    UNIMPLEMENTED;
-    return ERROR_CALL_NOT_IMPLEMENTED;
+    return dwError;
 }
 
 
-/* Function 11 */
-DWORD RChangeServiceConfigW(
+/* Function 5 */
+DWORD RSetServiceObjectSecurity(
     SC_RPC_HANDLE hService,
-    DWORD dwServiceType,
-    DWORD dwStartType,
-    DWORD dwErrorControl,
-    LPWSTR lpBinaryPathName,
-    LPWSTR lpLoadOrderGroup,
-    LPDWORD lpdwTagId,
-    LPBYTE lpDependencies,
-    DWORD dwDependSize,
-    LPWSTR lpServiceStartName,
-    LPBYTE lpPassword,
-    DWORD dwPwSize,
-    LPWSTR lpDisplayName)
+    DWORD dwSecurityInformation,
+    LPBYTE lpSecurityDescriptor,
+    DWORD dwSecuityDescriptorSize)
 {
-    DWORD dwError = ERROR_SUCCESS;
     PSERVICE_HANDLE hSvc;
-    PSERVICE lpService = NULL;
-    HKEY hServiceKey = NULL;
-    LPWSTR lpDisplayNameW = NULL;
-
-    DPRINT("RChangeServiceConfigW() called\n");
-    DPRINT("dwServiceType = %lu\n", dwServiceType);
-    DPRINT("dwStartType = %lu\n", dwStartType);
-    DPRINT("dwErrorControl = %lu\n", dwErrorControl);
-    DPRINT("lpBinaryPathName = %S\n", lpBinaryPathName);
-    DPRINT("lpLoadOrderGroup = %S\n", lpLoadOrderGroup);
-    DPRINT("lpDisplayName = %S\n", lpDisplayName);
+    PSERVICE lpService;
+    ULONG DesiredAccess = 0;
+    /* HANDLE hToken = NULL; */
+    HKEY hServiceKey;
+    /* NTSTATUS Status; */
+    DWORD dwError;
 
-    if (ScmShutdown)
-        return ERROR_SHUTDOWN_IN_PROGRESS;
+    DPRINT("RSetServiceObjectSecurity() called\n");
 
     hSvc = ScmGetServiceFromHandle(hService);
     if (hSvc == NULL)
@@ -1219,8 +1441,33 @@ DWORD RChangeServiceConfigW(
         return ERROR_INVALID_HANDLE;
     }
 
+    if (dwSecurityInformation == 0 ||
+        dwSecurityInformation & ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
+        | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION))
+        return ERROR_INVALID_PARAMETER;
+
+    if (!RtlValidSecurityDescriptor((PSECURITY_DESCRIPTOR)lpSecurityDescriptor))
+        return ERROR_INVALID_PARAMETER;
+
+    if (dwSecurityInformation & SACL_SECURITY_INFORMATION)
+        DesiredAccess |= ACCESS_SYSTEM_SECURITY;
+
+    if (dwSecurityInformation & DACL_SECURITY_INFORMATION)
+        DesiredAccess |= WRITE_DAC;
+
+    if (dwSecurityInformation & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION))
+        DesiredAccess |= WRITE_OWNER;
+
+    if ((dwSecurityInformation & OWNER_SECURITY_INFORMATION) &&
+        (((PISECURITY_DESCRIPTOR)lpSecurityDescriptor)->Owner == NULL))
+        return ERROR_INVALID_PARAMETER;
+
+    if ((dwSecurityInformation & GROUP_SECURITY_INFORMATION) &&
+        (((PISECURITY_DESCRIPTOR)lpSecurityDescriptor)->Group == NULL))
+        return ERROR_INVALID_PARAMETER;
+
     if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
-                                  SERVICE_CHANGE_CONFIG))
+                                  DesiredAccess))
     {
         DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
         return ERROR_ACCESS_DENIED;
@@ -1233,562 +1480,497 @@ DWORD RChangeServiceConfigW(
         return ERROR_INVALID_HANDLE;
     }
 
-    /* FIXME: Lock database exclusively */
-
     if (lpService->bDeleted)
-    {
-        /* FIXME: Unlock database */
-        DPRINT("The service has already been marked for delete!\n");
         return ERROR_SERVICE_MARKED_FOR_DELETE;
-    }
-
-    /* Open the service key */
-    dwError = ScmOpenServiceKey(lpService->szServiceName,
-                                KEY_SET_VALUE,
-                                &hServiceKey);
-    if (dwError != ERROR_SUCCESS)
-        goto done;
 
-    /* Write service data to the registry */
-    /* Set the display name */
-    if (lpDisplayName != NULL && *lpDisplayName != 0)
-    {
-        RegSetValueExW(hServiceKey,
-                       L"DisplayName",
-                       0,
-                       REG_SZ,
-                       (LPBYTE)lpDisplayName,
-                       (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
+#if 0
+    RpcImpersonateClient(NULL);
 
-        /* Update the display name */
-        lpDisplayNameW = (LPWSTR)HeapAlloc(GetProcessHeap(),
-                                           0,
-                                           (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
-        if (lpDisplayNameW == NULL)
-        {
-            dwError = ERROR_NOT_ENOUGH_MEMORY;
-            goto done;
-        }
+    Status = NtOpenThreadToken(NtCurrentThread(),
+                               8,
+                               TRUE,
+                               &hToken);
+    if (!NT_SUCCESS(Status))
+        return RtlNtStatusToDosError(Status);
 
-        if (lpService->lpDisplayName != lpService->lpServiceName)
-            HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
+    RpcRevertToSelf();
+#endif
 
-        lpService->lpDisplayName = lpDisplayNameW;
-    }
+    /* Lock the service database exclusive */
+    ScmLockDatabaseExclusive();
 
-    if (dwServiceType != SERVICE_NO_CHANGE)
+#if 0
+    Status = RtlSetSecurityObject(dwSecurityInformation,
+                                  (PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
+                                  &lpService->lpSecurityDescriptor,
+                                  &ScmServiceMapping,
+                                  hToken);
+    if (!NT_SUCCESS(Status))
     {
-        /* Set the service type */
-        dwError = RegSetValueExW(hServiceKey,
-                                 L"Type",
-                                 0,
-                                 REG_DWORD,
-                                 (LPBYTE)&dwServiceType,
-                                 sizeof(DWORD));
-        if (dwError != ERROR_SUCCESS)
-            goto done;
-
-        lpService->Status.dwServiceType = dwServiceType;
+        dwError = RtlNtStatusToDosError(Status);
+        goto Done;
     }
+#endif
 
-    if (dwStartType != SERVICE_NO_CHANGE)
-    {
-        /* Set the start value */
-        dwError = RegSetValueExW(hServiceKey,
-                                 L"Start",
-                                 0,
-                                 REG_DWORD,
-                                 (LPBYTE)&dwStartType,
-                                 sizeof(DWORD));
-        if (dwError != ERROR_SUCCESS)
-            goto done;
+    dwError = ScmOpenServiceKey(lpService->lpServiceName,
+                                READ_CONTROL | KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
+                                &hServiceKey);
+    if (dwError != ERROR_SUCCESS)
+        goto Done;
 
-        lpService->dwStartType = dwStartType;
-    }
+    UNIMPLEMENTED;
+    dwError = ERROR_SUCCESS;
+//    dwError = ScmWriteSecurityDescriptor(hServiceKey,
+//                                         lpService->lpSecurityDescriptor);
 
-    if (dwErrorControl != SERVICE_NO_CHANGE)
-    {
-        /* Set the error control value */
-        dwError = RegSetValueExW(hServiceKey,
-                                 L"ErrorControl",
-                                 0,
-                                 REG_DWORD,
-                                 (LPBYTE)&dwErrorControl,
-                                 sizeof(DWORD));
-        if (dwError != ERROR_SUCCESS)
-            goto done;
+    RegFlushKey(hServiceKey);
+    RegCloseKey(hServiceKey);
 
-        lpService->dwErrorControl = dwErrorControl;
-    }
+Done:
 
 #if 0
-    /* FIXME: set the new ImagePath value */
-
-    /* Set the image path */
-    if (dwServiceType & SERVICE_WIN32)
-    {
-        if (lpBinaryPathName != NULL && *lpBinaryPathName != 0)
-        {
-            dwError = RegSetValueExW(hServiceKey,
-                                     L"ImagePath",
-                                     0,
-                                     REG_EXPAND_SZ,
-                                     (LPBYTE)lpBinaryPathName,
-                                     (wcslen(lpBinaryPathName) + 1) * sizeof(WCHAR));
-            if (dwError != ERROR_SUCCESS)
-                goto done;
-        }
-    }
-    else if (dwServiceType & SERVICE_DRIVER)
-    {
-        if (lpImagePath != NULL && *lpImagePath != 0)
-        {
-            dwError = RegSetValueExW(hServiceKey,
-                                     L"ImagePath",
-                                     0,
-                                     REG_EXPAND_SZ,
-                                     (LPBYTE)lpImagePath,
-                                     (wcslen(lpImagePath) + 1) *sizeof(WCHAR));
-            if (dwError != ERROR_SUCCESS)
-                goto done;
-        }
-    }
+    if (hToken != NULL)
+        NtClose(hToken);
 #endif
 
-    /* Set the group name */
-    if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
-    {
-        dwError = RegSetValueExW(hServiceKey,
-                                 L"Group",
-                                 0,
-                                 REG_SZ,
-                                 (LPBYTE)lpLoadOrderGroup,
-                                 (wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR));
-        if (dwError != ERROR_SUCCESS)
-            goto done;
+    /* Unlock service database */
+    ScmUnlockDatabase();
 
-        dwError = ScmSetServiceGroup(lpService,
-                                     lpLoadOrderGroup);
-        if (dwError != ERROR_SUCCESS)
-            goto done;
-    }
+    DPRINT("RSetServiceObjectSecurity() done (Error %lu)\n", dwError);
 
-    if (lpdwTagId != NULL)
-    {
-        dwError = ScmAssignNewTag(lpService);
-        if (dwError != ERROR_SUCCESS)
-            goto done;
+    return dwError;
+}
 
-        dwError = RegSetValueExW(hServiceKey,
-                                 L"Tag",
-                                 0,
-                                 REG_DWORD,
-                                 (LPBYTE)&lpService->dwTag,
-                                 sizeof(DWORD));
-        if (dwError != ERROR_SUCCESS)
-            goto done;
 
-        *lpdwTagId = lpService->dwTag;
-    }
+/* Function 6 */
+DWORD RQueryServiceStatus(
+    SC_RPC_HANDLE hService,
+    LPSERVICE_STATUS lpServiceStatus)
+{
+    PSERVICE_HANDLE hSvc;
+    PSERVICE lpService;
 
-    /* Write dependencies */
-    if (lpDependencies != NULL && *lpDependencies != 0)
+    DPRINT("RQueryServiceStatus() called\n");
+
+    if (ScmShutdown)
+        return ERROR_SHUTDOWN_IN_PROGRESS;
+
+    hSvc = ScmGetServiceFromHandle(hService);
+    if (hSvc == NULL)
     {
-        dwError = ScmWriteDependencies(hServiceKey,
-                                       (LPWSTR)lpDependencies,
-                                       dwDependSize);
-        if (dwError != ERROR_SUCCESS)
-            goto done;
+        DPRINT1("Invalid service handle!\n");
+        return ERROR_INVALID_HANDLE;
     }
 
-    if (lpPassword != NULL)
+    if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
+                                  SERVICE_QUERY_STATUS))
     {
-        /* FIXME: Write password */
+        DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
+        return ERROR_ACCESS_DENIED;
     }
 
-    /* FIXME: Unlock database */
-
-done:
-    if (hServiceKey != NULL)
-        RegCloseKey(hServiceKey);
-
-    DPRINT("RChangeServiceConfigW() done (Error %lu)\n", dwError);
+    lpService = hSvc->ServiceEntry;
+    if (lpService == NULL)
+    {
+        DPRINT("lpService == NULL!\n");
+        return ERROR_INVALID_HANDLE;
+    }
 
-    return dwError;
-}
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
 
+    /* Return service status information */
+    RtlCopyMemory(lpServiceStatus,
+                  &lpService->Status,
+                  sizeof(SERVICE_STATUS));
 
-/* Create a path suitable for the bootloader out of the full path */
-DWORD
-ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
-{
-    DWORD ServiceNameLen, BufferSize, ExpandedLen;
-    WCHAR Dest;
-    WCHAR *Expanded;
-    UNICODE_STRING NtPathName, SystemRoot, LinkTarget;
-    OBJECT_ATTRIBUTES ObjectAttributes;
-    NTSTATUS Status;
-    HANDLE SymbolicLinkHandle;
+    /* Unlock the service database */
+    ScmUnlockDatabase();
 
-    DPRINT("ScmConvertToBootPathName %S\n", CanonName);
+    return ERROR_SUCCESS;
+}
 
-    ServiceNameLen = wcslen(CanonName);
 
-    /* First check, if it's already good */
-    if (ServiceNameLen > 12 &&
-        !_wcsnicmp(L"\\SystemRoot\\", CanonName, 12))
+static BOOL
+ScmIsValidServiceState(DWORD dwCurrentState)
+{
+    switch (dwCurrentState)
     {
-        *RelativeName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR));
-        if (*RelativeName == NULL)
-        {
-            DPRINT("Error allocating memory for boot driver name!\n");
-            return ERROR_NOT_ENOUGH_MEMORY;
-        }
-
-        /* Copy it */
-        wcscpy(*RelativeName, CanonName);
+        case SERVICE_STOPPED:
+        case SERVICE_START_PENDING:
+        case SERVICE_STOP_PENDING:
+        case SERVICE_RUNNING:
+        case SERVICE_CONTINUE_PENDING:
+        case SERVICE_PAUSE_PENDING:
+        case SERVICE_PAUSED:
+            return TRUE;
 
-        DPRINT("Bootdriver name %S\n", *RelativeName);
-        return ERROR_SUCCESS;
+        default:
+            return FALSE;
     }
+}
 
-    /* If it has %SystemRoot% prefix, substitute it to \System*/
-    if (ServiceNameLen > 13 &&
-        !_wcsnicmp(L"%SystemRoot%\\", CanonName, 13))
-    {
-        /* There is no +sizeof(wchar_t) because the name is less by 1 wchar */
-        *RelativeName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR));
 
-        if (*RelativeName == NULL)
-        {
-            DPRINT("Error allocating memory for boot driver name!\n");
-            return ERROR_NOT_ENOUGH_MEMORY;
-        }
+/* Function 7 */
+DWORD RSetServiceStatus(
+    RPC_SERVICE_STATUS_HANDLE hServiceStatus,
+    LPSERVICE_STATUS lpServiceStatus)
+{
+    PSERVICE lpService;
+    DWORD dwPreviousState;
+    LPCWSTR lpErrorStrings[2];
+    WCHAR szErrorBuffer[32];
 
-        /* Copy it */
-        wcscpy(*RelativeName, L"\\SystemRoot\\");
-        wcscat(*RelativeName, CanonName + 13);
+    DPRINT("RSetServiceStatus() called\n");
+    DPRINT("hServiceStatus = %p\n", hServiceStatus);
+    DPRINT("dwServiceType = %lu\n", lpServiceStatus->dwServiceType);
+    DPRINT("dwCurrentState = %lu\n", lpServiceStatus->dwCurrentState);
+    DPRINT("dwControlsAccepted = %lu\n", lpServiceStatus->dwControlsAccepted);
+    DPRINT("dwWin32ExitCode = %lu\n", lpServiceStatus->dwWin32ExitCode);
+    DPRINT("dwServiceSpecificExitCode = %lu\n", lpServiceStatus->dwServiceSpecificExitCode);
+    DPRINT("dwCheckPoint = %lu\n", lpServiceStatus->dwCheckPoint);
+    DPRINT("dwWaitHint = %lu\n", lpServiceStatus->dwWaitHint);
 
-        DPRINT("Bootdriver name %S\n", *RelativeName);
-        return ERROR_SUCCESS;
+    if (hServiceStatus == 0)
+    {
+        DPRINT("hServiceStatus == NULL!\n");
+        return ERROR_INVALID_HANDLE;
     }
 
-    /* Get buffer size needed for expanding env strings */
-    BufferSize = ExpandEnvironmentStringsW(L"%SystemRoot%\\", &Dest, 1);
-
-    if (BufferSize <= 1)
+    lpService = (PSERVICE)hServiceStatus;
+    if (lpService == NULL)
     {
-        DPRINT("Error during a call to ExpandEnvironmentStringsW()\n");
-        return ERROR_INVALID_ENVIRONMENT;
+        DPRINT("lpService == NULL!\n");
+        return ERROR_INVALID_HANDLE;
     }
 
-    /* Allocate memory, since the size is known now */
-    Expanded = LocalAlloc(LMEM_ZEROINIT, BufferSize * sizeof(WCHAR) + sizeof(WCHAR));
-    if (!Expanded)
+    /* Check current state */
+    if (!ScmIsValidServiceState(lpServiceStatus->dwCurrentState))
     {
-        DPRINT("Error allocating memory for boot driver name!\n");
-        return ERROR_NOT_ENOUGH_MEMORY;
+        DPRINT("Invalid service state!\n");
+        return ERROR_INVALID_DATA;
     }
 
-    /* Expand it */
-    if (ExpandEnvironmentStringsW(L"%SystemRoot%\\", Expanded, BufferSize) >
-        BufferSize)
+    /* Check service type */
+    if (!(lpServiceStatus->dwServiceType & SERVICE_WIN32) &&
+         (lpServiceStatus->dwServiceType & SERVICE_DRIVER))
     {
-        DPRINT("Error during a call to ExpandEnvironmentStringsW()\n");
-        LocalFree(Expanded);
-        return ERROR_NOT_ENOUGH_MEMORY;
+        DPRINT("Invalid service type!\n");
+        return ERROR_INVALID_DATA;
     }
 
-    /* Convert to NY-style path */
-    if (!RtlDosPathNameToNtPathName_U(Expanded, &NtPathName, NULL, NULL))
+    /* Check accepted controls */
+    if (lpServiceStatus->dwControlsAccepted & ~0xFF)
     {
-        DPRINT("Error during a call to RtlDosPathNameToNtPathName_U()\n");
-        return ERROR_INVALID_ENVIRONMENT;
+        DPRINT("Invalid controls accepted!\n");
+        return ERROR_INVALID_DATA;
     }
 
-    DPRINT("Converted to NT-style %wZ\n", &NtPathName);
+    /* Lock the service database exclusively */
+    ScmLockDatabaseExclusive();
 
-    /* No need to keep the dos-path anymore */
-    LocalFree(Expanded);
+    /* Save the current service state */
+    dwPreviousState = lpService->Status.dwCurrentState;
 
-    /* Copy it to the allocated place */
-    Expanded = LocalAlloc(LMEM_ZEROINIT, NtPathName.Length + sizeof(WCHAR));
-    if (!Expanded)
-    {
-            DPRINT("Error allocating memory for boot driver name!\n");
-            return ERROR_NOT_ENOUGH_MEMORY;
-    }
+    RtlCopyMemory(&lpService->Status,
+                  lpServiceStatus,
+                  sizeof(SERVICE_STATUS));
 
-    ExpandedLen = NtPathName.Length / sizeof(WCHAR);
-    wcsncpy(Expanded, NtPathName.Buffer, ExpandedLen);
-    Expanded[ExpandedLen] = 0;
+    /* Unlock the service database */
+    ScmUnlockDatabase();
 
-    if (ServiceNameLen > ExpandedLen &&
-        !_wcsnicmp(Expanded, CanonName, ExpandedLen))
+    /* Log a failed service stop */
+    if ((lpServiceStatus->dwCurrentState == SERVICE_STOPPED) &&
+        (dwPreviousState != SERVICE_STOPPED))
     {
-        /* Only \SystemRoot\ is missing */
-        *RelativeName = LocalAlloc(LMEM_ZEROINIT,
-            (ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR));
-        if (*RelativeName == NULL)
+        if (lpServiceStatus->dwWin32ExitCode != ERROR_SUCCESS)
         {
-            DPRINT("Error allocating memory for boot driver name!\n");
-            LocalFree(Expanded);
-            return ERROR_NOT_ENOUGH_MEMORY;
-        }
-
-        wcscpy(*RelativeName, L"\\SystemRoot\\");
-        wcscat(*RelativeName, CanonName + ExpandedLen);
+            swprintf(szErrorBuffer, L"%lu", lpServiceStatus->dwWin32ExitCode);
+            lpErrorStrings[0] = lpService->lpDisplayName;
+            lpErrorStrings[1] = szErrorBuffer;
 
-        RtlFreeUnicodeString(&NtPathName);
-        return ERROR_SUCCESS;
+            ScmLogError(EVENT_SERVICE_EXIT_FAILED,
+                        2,
+                        lpErrorStrings);
+        }
     }
 
-    /* The most complex case starts here */
-    RtlInitUnicodeString(&SystemRoot, L"\\SystemRoot");
-    InitializeObjectAttributes(&ObjectAttributes,
-                               &SystemRoot,
-                               OBJ_CASE_INSENSITIVE,
-                               NULL,
-                               NULL);
-
-    /* Open this symlink */
-    Status = NtOpenSymbolicLinkObject(&SymbolicLinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
-
-    if (NT_SUCCESS(Status))
-    {
-        LinkTarget.Length = 0;
-        LinkTarget.MaximumLength = 0;
-
-        DPRINT("Opened symbolic link object\n");
+    DPRINT("Set %S to %lu\n", lpService->lpDisplayName, lpService->Status.dwCurrentState);
+    DPRINT("RSetServiceStatus() done\n");
 
-        Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
-        if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL)
-        {
-            /* Check if required buffer size is sane */
-            if (BufferSize > 0xFFFD)
-            {
-                DPRINT("Too large buffer required\n");
-                *RelativeName = 0;
+    return ERROR_SUCCESS;
+}
 
-                if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-                LocalFree(Expanded);
-                return ERROR_NOT_ENOUGH_MEMORY;
-            }
 
-            /* Alloc the string */
-            LinkTarget.Buffer = LocalAlloc(LMEM_ZEROINIT, BufferSize + sizeof(WCHAR));
-            if (!LinkTarget.Buffer)
-            {
-                DPRINT("Unable to alloc buffer\n");
-                if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-                LocalFree(Expanded);
-                return ERROR_NOT_ENOUGH_MEMORY;
-            }
+/* Function 8 */
+DWORD RUnlockServiceDatabase(
+    LPSC_RPC_LOCK Lock)
+{
+    UNIMPLEMENTED;
+    return ERROR_SUCCESS;
+}
 
-            /* Do a real query now */
-            LinkTarget.Length = BufferSize;
-            LinkTarget.MaximumLength = LinkTarget.Length + sizeof(WCHAR);
 
-            Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
-            if (NT_SUCCESS(Status))
-            {
-                DPRINT("LinkTarget: %wZ\n", &LinkTarget);
+/* Function 9 */
+DWORD RNotifyBootConfigStatus(
+    SVCCTL_HANDLEW lpMachineName,
+    DWORD BootAcceptable)
+{
+    DPRINT1("RNotifyBootConfigStatus(%p %lu) called\n", lpMachineName, BootAcceptable);
+    return ERROR_SUCCESS;
 
-                ExpandedLen = LinkTarget.Length / sizeof(WCHAR);
-                if ((ServiceNameLen > ExpandedLen) &&
-                    !_wcsnicmp(LinkTarget.Buffer, CanonName, ExpandedLen))
-                {
-                    *RelativeName = LocalAlloc(LMEM_ZEROINIT,
-                       (ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR));
+//    UNIMPLEMENTED;
+//    return ERROR_CALL_NOT_IMPLEMENTED;
+}
 
-                    if (*RelativeName == NULL)
-                    {
-                        DPRINT("Unable to alloc buffer\n");
-                        if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-                        LocalFree(Expanded);
-                        RtlFreeUnicodeString(&NtPathName);
-                        return ERROR_NOT_ENOUGH_MEMORY;
-                    }
 
-                    /* Copy it over, substituting the first part
-                       with SystemRoot */
-                    wcscpy(*RelativeName, L"\\SystemRoot\\");
-                    wcscat(*RelativeName, CanonName+ExpandedLen+1);
+/* Function 10 */
+DWORD RI_ScSetServiceBitsW(
+    RPC_SERVICE_STATUS_HANDLE hServiceStatus,
+    DWORD dwServiceBits,
+    int bSetBitsOn,
+    int bUpdateImmediately,
+    wchar_t *lpString)
+{
+    UNIMPLEMENTED;
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
 
-                    /* Cleanup */
-                    if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-                    LocalFree(Expanded);
-                    RtlFreeUnicodeString(&NtPathName);
 
-                    /* Return success */
-                    return ERROR_SUCCESS;
-                }
-                else
-                {
-                    if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-                    LocalFree(Expanded);
-                    RtlFreeUnicodeString(&NtPathName);
-                    return ERROR_INVALID_PARAMETER;
-                }
-            }
-            else
-            {
-                DPRINT("Error, Status = %08X\n", Status);
-                if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-                LocalFree(Expanded);
-                RtlFreeUnicodeString(&NtPathName);
-                return ERROR_INVALID_PARAMETER;
-            }
-        }
-        else
-        {
-            DPRINT("Error, Status = %08X\n", Status);
-            if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-            LocalFree(Expanded);
-            RtlFreeUnicodeString(&NtPathName);
-            return ERROR_INVALID_PARAMETER;
-        }
-    }
-    else
+/* Function 11 */
+DWORD RChangeServiceConfigW(
+    SC_RPC_HANDLE hService,
+    DWORD dwServiceType,
+    DWORD dwStartType,
+    DWORD dwErrorControl,
+    LPWSTR lpBinaryPathName,
+    LPWSTR lpLoadOrderGroup,
+    LPDWORD lpdwTagId,
+    LPBYTE lpDependencies,
+    DWORD dwDependSize,
+    LPWSTR lpServiceStartName,
+    LPBYTE lpPassword,
+    DWORD dwPwSize,
+    LPWSTR lpDisplayName)
+{
+    DWORD dwError = ERROR_SUCCESS;
+    PSERVICE_HANDLE hSvc;
+    PSERVICE lpService = NULL;
+    HKEY hServiceKey = NULL;
+    LPWSTR lpDisplayNameW = NULL;
+    LPWSTR lpImagePathW = NULL;
+
+    DPRINT("RChangeServiceConfigW() called\n");
+    DPRINT("dwServiceType = %lu\n", dwServiceType);
+    DPRINT("dwStartType = %lu\n", dwStartType);
+    DPRINT("dwErrorControl = %lu\n", dwErrorControl);
+    DPRINT("lpBinaryPathName = %S\n", lpBinaryPathName);
+    DPRINT("lpLoadOrderGroup = %S\n", lpLoadOrderGroup);
+    DPRINT("lpDisplayName = %S\n", lpDisplayName);
+
+    if (ScmShutdown)
+        return ERROR_SHUTDOWN_IN_PROGRESS;
+
+    hSvc = ScmGetServiceFromHandle(hService);
+    if (hSvc == NULL)
     {
-        DPRINT("Error, Status = %08X\n", Status);
-        LocalFree(Expanded);
-        return ERROR_INVALID_PARAMETER;
+        DPRINT1("Invalid service handle!\n");
+        return ERROR_INVALID_HANDLE;
     }
 
-    /* Failure */
-    *RelativeName = NULL;
-    return ERROR_INVALID_PARAMETER;
-}
+    if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
+                                  SERVICE_CHANGE_CONFIG))
+    {
+        DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
+        return ERROR_ACCESS_DENIED;
+    }
 
-DWORD
-ScmCanonDriverImagePath(DWORD dwStartType,
-                        const wchar_t *lpServiceName,
-                        wchar_t **lpCanonName)
-{
-    DWORD ServiceNameLen, Result;
-    UNICODE_STRING NtServiceName;
-    WCHAR *RelativeName;
-    const WCHAR *SourceName = lpServiceName;
+    lpService = hSvc->ServiceEntry;
+    if (lpService == NULL)
+    {
+        DPRINT("lpService == NULL!\n");
+        return ERROR_INVALID_HANDLE;
+    }
 
-    /* Calculate the length of the service's name */
-    ServiceNameLen = wcslen(lpServiceName);
+    /* Lock the service database exclusively */
+    ScmLockDatabaseExclusive();
 
-    /* 12 is wcslen(L"\\SystemRoot\\") */
-    if (ServiceNameLen > 12 &&
-        !_wcsnicmp(L"\\SystemRoot\\", lpServiceName, 12))
+    if (lpService->bDeleted)
     {
-        /* SystemRoot prefix is already included */
+        DPRINT("The service has already been marked for delete!\n");
+        dwError = ERROR_SERVICE_MARKED_FOR_DELETE;
+        goto done;
+    }
 
-        *lpCanonName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR));
+    /* Open the service key */
+    dwError = ScmOpenServiceKey(lpService->szServiceName,
+                                KEY_SET_VALUE,
+                                &hServiceKey);
+    if (dwError != ERROR_SUCCESS)
+        goto done;
 
-        if (*lpCanonName == NULL)
+    /* Write service data to the registry */
+    /* Set the display name */
+    if (lpDisplayName != NULL && *lpDisplayName != 0)
+    {
+        RegSetValueExW(hServiceKey,
+                       L"DisplayName",
+                       0,
+                       REG_SZ,
+                       (LPBYTE)lpDisplayName,
+                       (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
+
+        /* Update the display name */
+        lpDisplayNameW = HeapAlloc(GetProcessHeap(),
+                                   0,
+                                   (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
+        if (lpDisplayNameW == NULL)
         {
-            DPRINT("Error allocating memory for canonized service name!\n");
-            return ERROR_NOT_ENOUGH_MEMORY;
+            dwError = ERROR_NOT_ENOUGH_MEMORY;
+            goto done;
         }
 
-        /* If it's a boot-time driver, it must be systemroot relative */
-        if (dwStartType == SERVICE_BOOT_START)
-            SourceName += 12;
-
-        /* Copy it */
-        wcscpy(*lpCanonName, SourceName);
+        if (lpService->lpDisplayName != lpService->lpServiceName)
+            HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
 
-        DPRINT("Canonicalized name %S\n", *lpCanonName);
-        return NO_ERROR;
+        lpService->lpDisplayName = lpDisplayNameW;
     }
 
-    /* Check if it has %SystemRoot% (len=13) */
-    if (ServiceNameLen > 13 &&
-        !_wcsnicmp(L"%%SystemRoot%%\\", lpServiceName, 13))
+    if (dwServiceType != SERVICE_NO_CHANGE)
     {
-        /* Substitute %SystemRoot% with \\SystemRoot\\ */
-        *lpCanonName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR));
+        /* Set the service type */
+        dwError = RegSetValueExW(hServiceKey,
+                                 L"Type",
+                                 0,
+                                 REG_DWORD,
+                                 (LPBYTE)&dwServiceType,
+                                 sizeof(DWORD));
+        if (dwError != ERROR_SUCCESS)
+            goto done;
 
-        if (*lpCanonName == NULL)
-        {
-            DPRINT("Error allocating memory for canonized service name!\n");
-            return ERROR_NOT_ENOUGH_MEMORY;
-        }
+        lpService->Status.dwServiceType = dwServiceType;
+    }
 
-        /* If it's a boot-time driver, it must be systemroot relative */
-        if (dwStartType == SERVICE_BOOT_START)
-            wcscpy(*lpCanonName, L"\\SystemRoot\\");
+    if (dwStartType != SERVICE_NO_CHANGE)
+    {
+        /* Set the start value */
+        dwError = RegSetValueExW(hServiceKey,
+                                 L"Start",
+                                 0,
+                                 REG_DWORD,
+                                 (LPBYTE)&dwStartType,
+                                 sizeof(DWORD));
+        if (dwError != ERROR_SUCCESS)
+            goto done;
 
-        wcscat(*lpCanonName, lpServiceName + 13);
+        lpService->dwStartType = dwStartType;
+    }
 
-        DPRINT("Canonicalized name %S\n", *lpCanonName);
-        return NO_ERROR;
+    if (dwErrorControl != SERVICE_NO_CHANGE)
+    {
+        /* Set the error control value */
+        dwError = RegSetValueExW(hServiceKey,
+                                 L"ErrorControl",
+                                 0,
+                                 REG_DWORD,
+                                 (LPBYTE)&dwErrorControl,
+                                 sizeof(DWORD));
+        if (dwError != ERROR_SUCCESS)
+            goto done;
+
+        lpService->dwErrorControl = dwErrorControl;
     }
 
-    /* Check if it's a relative path name */
-    if (lpServiceName[0] != L'\\' && lpServiceName[1] != L':')
+    if (lpBinaryPathName != NULL && *lpBinaryPathName != 0)
     {
-        *lpCanonName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR));
+        /* Set the image path */
+        lpImagePathW = lpBinaryPathName;
 
-        if (*lpCanonName == NULL)
+        if (lpService->Status.dwServiceType & SERVICE_DRIVER)
         {
-            DPRINT("Error allocating memory for canonized service name!\n");
-            return ERROR_NOT_ENOUGH_MEMORY;
+            dwError = ScmCanonDriverImagePath(lpService->dwStartType,
+                                              lpBinaryPathName,
+                                              &lpImagePathW);
+
+            if (dwError != ERROR_SUCCESS)
+                goto done;
         }
 
-        /* Just copy it over without changing */
-        wcscpy(*lpCanonName, lpServiceName);
+        dwError = RegSetValueExW(hServiceKey,
+                                 L"ImagePath",
+                                 0,
+                                 REG_EXPAND_SZ,
+                                 (LPBYTE)lpImagePathW,
+                                 (wcslen(lpImagePathW) + 1) * sizeof(WCHAR));
 
-        return NO_ERROR;
+        if (lpImagePathW != lpBinaryPathName)
+            HeapFree(GetProcessHeap(), 0, lpImagePathW);
+
+        if (dwError != ERROR_SUCCESS)
+            goto done;
     }
 
-    /* It seems to be a DOS path, convert it */
-    if (!RtlDosPathNameToNtPathName_U(lpServiceName, &NtServiceName, NULL, NULL))
+    /* Set the group name */
+    if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
     {
-        DPRINT("RtlDosPathNameToNtPathName_U() failed!\n");
-        return ERROR_INVALID_PARAMETER;
-    }
+        dwError = RegSetValueExW(hServiceKey,
+                                 L"Group",
+                                 0,
+                                 REG_SZ,
+                                 (LPBYTE)lpLoadOrderGroup,
+                                 (wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR));
+        if (dwError != ERROR_SUCCESS)
+            goto done;
 
-    *lpCanonName = LocalAlloc(LMEM_ZEROINIT, NtServiceName.Length + sizeof(WCHAR));
+        dwError = ScmSetServiceGroup(lpService,
+                                     lpLoadOrderGroup);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
+    }
 
-    if (*lpCanonName == NULL)
+    if (lpdwTagId != NULL)
     {
-        DPRINT("Error allocating memory for canonized service name!\n");
-        RtlFreeUnicodeString(&NtServiceName);
-        return ERROR_NOT_ENOUGH_MEMORY;
-    }
+        dwError = ScmAssignNewTag(lpService);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
 
-    /* Copy the string */
-    wcsncpy(*lpCanonName, NtServiceName.Buffer, NtServiceName.Length / sizeof(WCHAR));
+        dwError = RegSetValueExW(hServiceKey,
+                                 L"Tag",
+                                 0,
+                                 REG_DWORD,
+                                 (LPBYTE)&lpService->dwTag,
+                                 sizeof(DWORD));
+        if (dwError != ERROR_SUCCESS)
+            goto done;
 
-    /* The unicode string is not needed anymore */
-    RtlFreeUnicodeString(&NtServiceName);
+        *lpdwTagId = lpService->dwTag;
+    }
 
-    if (dwStartType != SERVICE_BOOT_START)
+    /* Write dependencies */
+    if (lpDependencies != NULL && *lpDependencies != 0)
     {
-        DPRINT("Canonicalized name %S\n", *lpCanonName);
-        return NO_ERROR;
+        dwError = ScmWriteDependencies(hServiceKey,
+                                       (LPWSTR)lpDependencies,
+                                       dwDependSize);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
     }
 
-    /* The service is boot-started, so must be relative */
-    Result = ScmConvertToBootPathName(*lpCanonName, &RelativeName);
-    if (Result)
+    if (lpPassword != NULL)
     {
-        /* There is a problem, free name and return */
-        LocalFree(*lpCanonName);
-        DPRINT("Error converting named!\n");
-        return Result;
+        /* FIXME: Write password */
     }
 
-    ASSERT(RelativeName);
-
-    /* Copy that string */
-    wcscpy(*lpCanonName, RelativeName + 12);
+done:
+    if (hServiceKey != NULL)
+        RegCloseKey(hServiceKey);
 
-    /* Free the allocated buffer */
-    LocalFree(RelativeName);
+    /* Unlock the service database */
+    ScmUnlockDatabase();
 
-    DPRINT("Canonicalized name %S\n", *lpCanonName);
+    DPRINT("RChangeServiceConfigW() done (Error %lu)\n", dwError);
 
-    /* Success */
-    return NO_ERROR;
+    return dwError;
 }
 
 
@@ -1828,6 +2010,7 @@ DWORD RCreateServiceW(
     DPRINT("dwErrorControl = %lu\n", dwErrorControl);
     DPRINT("lpBinaryPathName = %S\n", lpBinaryPathName);
     DPRINT("lpLoadOrderGroup = %S\n", lpLoadOrderGroup);
+    DPRINT("lpdwTagId = %p\n", lpdwTagId);
 
     if (ScmShutdown)
         return ERROR_SHUTDOWN_IN_PROGRESS;
@@ -1858,37 +2041,73 @@ DWORD RCreateServiceW(
         return ERROR_INVALID_PARAMETER;
     }
 
-    if ((dwServiceType == (SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS)) &&
-        (lpServiceStartName))
-    {
+    /* Check for invalid service type value */
+    if ((dwServiceType != SERVICE_KERNEL_DRIVER) &&
+        (dwServiceType != SERVICE_FILE_SYSTEM_DRIVER) &&
+        ((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS) &&
+        ((dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_SHARE_PROCESS))
+            return ERROR_INVALID_PARAMETER;
+
+    /* Check for invalid start type value */
+    if ((dwStartType != SERVICE_BOOT_START) &&
+        (dwStartType != SERVICE_SYSTEM_START) &&
+        (dwStartType != SERVICE_AUTO_START) &&
+        (dwStartType != SERVICE_DEMAND_START) &&
+        (dwStartType != SERVICE_DISABLED))
         return ERROR_INVALID_PARAMETER;
+
+    /* Only drivers can be boot start or system start services */
+    if ((dwStartType == SERVICE_BOOT_START) ||
+        (dwStartType == SERVICE_SYSTEM_START))
+    {
+        if ((dwServiceType != SERVICE_KERNEL_DRIVER) &&
+            (dwServiceType != SERVICE_FILE_SYSTEM_DRIVER))
+            return ERROR_INVALID_PARAMETER;
     }
 
-    if ((dwServiceType > SERVICE_WIN32_SHARE_PROCESS) &&
-        (dwServiceType != (SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS)) &&
-        (dwServiceType != (SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS)))
+    /* Check for invalid error control value */
+    if ((dwErrorControl != SERVICE_ERROR_IGNORE) &&
+        (dwErrorControl != SERVICE_ERROR_NORMAL) &&
+        (dwErrorControl != SERVICE_ERROR_SEVERE) &&
+        (dwErrorControl != SERVICE_ERROR_CRITICAL))
+        return ERROR_INVALID_PARAMETER;
+
+    if ((dwServiceType == (SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS)) &&
+        (lpServiceStartName))
     {
         return ERROR_INVALID_PARAMETER;
     }
 
-    if (dwStartType > SERVICE_DISABLED)
+    if (lpdwTagId && (!lpLoadOrderGroup || !*lpLoadOrderGroup))
     {
         return ERROR_INVALID_PARAMETER;
     }
 
+    /* Lock the service database exclusively */
+    ScmLockDatabaseExclusive();
+
     lpService = ScmGetServiceEntryByName(lpServiceName);
     if (lpService)
     {
-        /* check if it is marked for deletion */
+        /* Unlock the service database */
+        ScmUnlockDatabase();
+
+        /* Check if it is marked for deletion */
         if (lpService->bDeleted)
             return ERROR_SERVICE_MARKED_FOR_DELETE;
+
         /* Return Error exist */
         return ERROR_SERVICE_EXISTS;
     }
 
     if (lpDisplayName != NULL &&
         ScmGetServiceEntryByDisplayName(lpDisplayName) != NULL)
+    {
+        /* Unlock the service database */
+        ScmUnlockDatabase();
+
         return ERROR_DUPLICATE_SERVICE_NAME;
+    }
 
     if (dwServiceType & SERVICE_DRIVER)
     {
@@ -1903,6 +2122,9 @@ DWORD RCreateServiceW(
         if (dwStartType == SERVICE_BOOT_START ||
             dwStartType == SERVICE_SYSTEM_START)
         {
+            /* Unlock the service database */
+            ScmUnlockDatabase();
+
             return ERROR_INVALID_PARAMETER;
         }
     }
@@ -1923,7 +2145,7 @@ DWORD RCreateServiceW(
         *lpDisplayName != 0 &&
         _wcsicmp(lpService->lpDisplayName, lpDisplayName) != 0)
     {
-        lpService->lpDisplayName = (WCHAR*) HeapAlloc(GetProcessHeap(), 0,
+        lpService->lpDisplayName = HeapAlloc(GetProcessHeap(), 0,
                                              (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
         if (lpService->lpDisplayName == NULL)
         {
@@ -2052,7 +2274,7 @@ DWORD RCreateServiceW(
     if (lpDependencies != NULL && *lpDependencies != 0)
     {
         dwError = ScmWriteDependencies(hServiceKey,
-                                       (LPWSTR)lpDependencies,
+                                       (LPCWSTR)lpDependencies,
                                        dwDependSize);
         if (dwError != ERROR_SUCCESS)
             goto done;
@@ -2091,6 +2313,9 @@ DWORD RCreateServiceW(
     DPRINT("CreateService - lpService->dwRefCount %u\n", lpService->dwRefCount);
 
 done:;
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
     if (hServiceKey != NULL)
         RegCloseKey(hServiceKey);
 
@@ -2104,9 +2329,12 @@ done:;
     }
     else
     {
-        /* Release the display name buffer */
-        if (lpService->lpServiceName != NULL)
+        if (lpService != NULL &&
+            lpService->lpServiceName != NULL)
+        {
+            /* Release the display name buffer */
             HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
+        }
 
         if (hServiceHandle)
         {
@@ -2203,7 +2431,7 @@ DWORD REnumDependentServicesW(
                                 (dwServicesReturned + 1) * sizeof(PSERVICE));
     if (!lpServicesArray)
     {
-        DPRINT("Could not allocate a buffer!!\n");
+        DPRINT1("Could not allocate a buffer!!\n");
         dwError = ERROR_NOT_ENOUGH_MEMORY;
         goto Done;
     }
@@ -2273,202 +2501,16 @@ DWORD REnumServicesStatusW(
     LPBOUNDED_DWORD_256K lpServicesReturned,
     LPBOUNDED_DWORD_256K lpResumeHandle)
 {
-    PMANAGER_HANDLE hManager;
-    PSERVICE lpService;
-    DWORD dwError = ERROR_SUCCESS;
-    PLIST_ENTRY ServiceEntry;
-    PSERVICE CurrentService;
-    DWORD dwState;
-    DWORD dwRequiredSize;
-    DWORD dwServiceCount;
-    DWORD dwSize;
-    DWORD dwLastResumeCount = 0;
-    LPENUM_SERVICE_STATUSW lpStatusPtr;
-    LPWSTR lpStringPtr;
-
-    DPRINT("REnumServicesStatusW() called\n");
-
-    if (ScmShutdown)
-        return ERROR_SHUTDOWN_IN_PROGRESS;
-
-    hManager = ScmGetServiceManagerFromHandle(hSCManager);
-    if (hManager == NULL)
-    {
-        DPRINT1("Invalid service manager handle!\n");
-        return ERROR_INVALID_HANDLE;
-    }
-
-
-    *pcbBytesNeeded = 0;
-    *lpServicesReturned = 0;
-
-    if ((dwServiceType!=SERVICE_DRIVER) && (dwServiceType!=SERVICE_WIN32))
-    {
-        DPRINT("Not a valid Service Type!\n");
-        return ERROR_INVALID_PARAMETER;
-    }
-
-    if ((dwServiceState<SERVICE_ACTIVE) || (dwServiceState>SERVICE_STATE_ALL))
-    {
-        DPRINT("Not a valid Service State!\n");
-        return ERROR_INVALID_PARAMETER;
-    }
-
-    /* Check access rights */
-    if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
-                                  SC_MANAGER_ENUMERATE_SERVICE))
-    {
-        DPRINT("Insufficient access rights! 0x%lx\n",
-                hManager->Handle.DesiredAccess);
-        return ERROR_ACCESS_DENIED;
-    }
-
-    if (lpResumeHandle)
-        dwLastResumeCount = *lpResumeHandle;
-
-    /* FIXME: Lock the service list shared */
-
-    lpService = ScmGetServiceEntryByResumeCount(dwLastResumeCount);
-    if (lpService == NULL)
-    {
-        dwError = ERROR_SUCCESS;
-        goto Done;
-    }
-
-    dwRequiredSize = 0;
-    dwServiceCount = 0;
-
-    for (ServiceEntry = &lpService->ServiceListEntry;
-         ServiceEntry != &ServiceListHead;
-         ServiceEntry = ServiceEntry->Flink)
-    {
-        CurrentService = CONTAINING_RECORD(ServiceEntry,
-                                           SERVICE,
-                                           ServiceListEntry);
-
-        if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
-            continue;
-
-        dwState = SERVICE_ACTIVE;
-        if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
-            dwState = SERVICE_INACTIVE;
-
-        if ((dwState & dwServiceState) == 0)
-            continue;
-
-        dwSize = sizeof(ENUM_SERVICE_STATUSW) +
-                 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
-                 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
-
-        if (dwRequiredSize + dwSize > dwBufSize)
-        {
-            DPRINT("Service name: %S  no fit\n", CurrentService->lpServiceName);
-            break;
-        }
-
-        DPRINT("Service name: %S  fit\n", CurrentService->lpServiceName);
-        dwRequiredSize += dwSize;
-        dwServiceCount++;
-        dwLastResumeCount = CurrentService->dwResumeCount;
-    }
-
-    DPRINT("dwRequiredSize: %lu\n", dwRequiredSize);
-    DPRINT("dwServiceCount: %lu\n", dwServiceCount);
-
-    for (;
-         ServiceEntry != &ServiceListHead;
-         ServiceEntry = ServiceEntry->Flink)
-    {
-        CurrentService = CONTAINING_RECORD(ServiceEntry,
-                                           SERVICE,
-                                           ServiceListEntry);
-
-        if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
-            continue;
-
-        dwState = SERVICE_ACTIVE;
-        if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
-            dwState = SERVICE_INACTIVE;
-
-        if ((dwState & dwServiceState) == 0)
-            continue;
-
-        dwRequiredSize += (sizeof(ENUM_SERVICE_STATUSW) +
-                           ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
-                           ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR)));
-
-        dwError = ERROR_MORE_DATA;
-    }
-
-    DPRINT("*pcbBytesNeeded: %lu\n", dwRequiredSize);
-
-    if (lpResumeHandle)
-        *lpResumeHandle = dwLastResumeCount;
-
-    *lpServicesReturned = dwServiceCount;
-    *pcbBytesNeeded = dwRequiredSize;
-
-    lpStatusPtr = (LPENUM_SERVICE_STATUSW)lpBuffer;
-    lpStringPtr = (LPWSTR)((ULONG_PTR)lpBuffer +
-                           dwServiceCount * sizeof(ENUM_SERVICE_STATUSW));
-
-    dwRequiredSize = 0;
-    for (ServiceEntry = &lpService->ServiceListEntry;
-         ServiceEntry != &ServiceListHead;
-         ServiceEntry = ServiceEntry->Flink)
-    {
-        CurrentService = CONTAINING_RECORD(ServiceEntry,
-                                           SERVICE,
-                                           ServiceListEntry);
-
-        if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
-            continue;
-
-        dwState = SERVICE_ACTIVE;
-        if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
-            dwState = SERVICE_INACTIVE;
-
-        if ((dwState & dwServiceState) == 0)
-            continue;
-
-        dwSize = sizeof(ENUM_SERVICE_STATUSW) +
-                 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
-                 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
-
-        if (dwRequiredSize + dwSize > dwBufSize)
-            break;
-
-        /* Copy the service name */
-        wcscpy(lpStringPtr, CurrentService->lpServiceName);
-        lpStatusPtr->lpServiceName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
-        lpStringPtr += (wcslen(CurrentService->lpServiceName) + 1);
-
-        /* Copy the display name */
-        wcscpy(lpStringPtr, CurrentService->lpDisplayName);
-        lpStatusPtr->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
-        lpStringPtr += (wcslen(CurrentService->lpDisplayName) + 1);
-
-        /* Copy the status information */
-        memcpy(&lpStatusPtr->ServiceStatus,
-               &CurrentService->Status,
-               sizeof(SERVICE_STATUS));
-
-        lpStatusPtr++;
-        dwRequiredSize += dwSize;
-    }
-
-    if (dwError == 0) 
-    {
-        *pcbBytesNeeded = 0;
-        if (lpResumeHandle) *lpResumeHandle = 0;
-    }
-
-Done:;
-    /* FIXME: Unlock the service list */
-
-    DPRINT("REnumServicesStatusW() done (Error %lu)\n", dwError);
-
-    return dwError;
+    /* Enumerate all the services, not regarding of their group */
+    return REnumServiceGroupW(hSCManager,
+                              dwServiceType,
+                              dwServiceState,
+                              lpBuffer,
+                              dwBufSize,
+                              pcbBytesNeeded,
+                              lpServicesReturned,
+                              lpResumeHandle,
+                              NULL);
 }
 
 
@@ -2532,7 +2574,7 @@ DWORD ROpenServiceW(
     PSERVICE lpService;
     PMANAGER_HANDLE hManager;
     SC_HANDLE hHandle;
-    DWORD dwError;
+    DWORD dwError = ERROR_SUCCESS;
 
     DPRINT("ROpenServiceW() called\n");
     DPRINT("hSCManager = %p\n", hSCManager);
@@ -2556,14 +2598,16 @@ DWORD ROpenServiceW(
     if (!lpServiceName)
         return ERROR_INVALID_ADDRESS;
 
-    /* FIXME: Lock the service list */
+    /* Lock the service database exclusive */
+    ScmLockDatabaseExclusive();
 
     /* Get service database entry */
     lpService = ScmGetServiceEntryByName(lpServiceName);
     if (lpService == NULL)
     {
         DPRINT("Could not find a service!\n");
-        return ERROR_SERVICE_DOES_NOT_EXIST;
+        dwError = ERROR_SERVICE_DOES_NOT_EXIST;
+        goto Done;
     }
 
     /* Create a service handle */
@@ -2572,7 +2616,7 @@ DWORD ROpenServiceW(
     if (dwError != ERROR_SUCCESS)
     {
         DPRINT("ScmCreateServiceHandle() failed (Error %lu)\n", dwError);
-        return dwError;
+        goto Done;
     }
 
     /* Check the desired access */
@@ -2582,7 +2626,7 @@ DWORD ROpenServiceW(
     {
         DPRINT("ScmCheckAccess() failed (Error %lu)\n", dwError);
         HeapFree(GetProcessHeap(), 0, hHandle);
-        return dwError;
+        goto Done;
     }
 
     lpService->dwRefCount++;
@@ -2591,9 +2635,13 @@ DWORD ROpenServiceW(
     *lpServiceHandle = (SC_RPC_HANDLE)hHandle;
     DPRINT("*hService = %p\n", *lpServiceHandle);
 
+Done:;
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
     DPRINT("ROpenServiceW() done\n");
 
-    return ERROR_SUCCESS;
+    return dwError;
 }
 
 
@@ -2644,7 +2692,8 @@ DWORD RQueryServiceConfigW(
         return ERROR_INVALID_HANDLE;
     }
 
-    /* FIXME: Lock the service database shared */
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
 
     dwError = ScmOpenServiceKey(lpService->lpServiceName,
                                 KEY_READ,
@@ -2750,7 +2799,7 @@ DWORD RQueryServiceConfigW(
 
         lpConfig->lpDependencies = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
         if (lpDependencies != NULL)
-            lpStr += dwDependenciesLength * sizeof(WCHAR);
+            lpStr += dwDependenciesLength;
         else
             lpStr += (wcslen(lpStr) + 1);
 
@@ -2784,6 +2833,9 @@ DWORD RQueryServiceConfigW(
         *pcbBytesNeeded = dwRequiredSize;
 
 Done:;
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
     if (lpImagePath != NULL)
         HeapFree(GetProcessHeap(), 0, lpImagePath);
 
@@ -2796,8 +2848,6 @@ Done:;
     if (hServiceKey != NULL)
         RegCloseKey(hServiceKey);
 
-    /* FIXME: Unlock the service database */
-
     DPRINT("RQueryServiceConfigW() done\n");
 
     return dwError;
@@ -2825,8 +2875,17 @@ DWORD RStartServiceW(
     DWORD dwError = ERROR_SUCCESS;
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService = NULL;
+    DWORD i;
 
-    DPRINT("RStartServiceW() called\n");
+    DPRINT("RStartServiceW(%p %lu %p) called\n", hService, argc, argv);
+    DPRINT("  argc: %lu\n", argc);
+    if (argv != NULL)
+    {
+        for (i = 0; i < argc; i++)
+        {
+            DPRINT("  argv[%lu]: %S\n", i, argv[i]);
+        }
+    }
 
     if (ScmShutdown)
         return ERROR_SHUTDOWN_IN_PROGRESS;
@@ -2858,13 +2917,8 @@ DWORD RStartServiceW(
     if (lpService->bDeleted)
         return ERROR_SERVICE_MARKED_FOR_DELETE;
 
-    if (argv) {
-        UNIMPLEMENTED;
-        argv = NULL;
-    }
-
     /* Start the service */
-    dwError = ScmStartService(lpService, argc, (LPWSTR *)argv);
+    dwError = ScmStartService(lpService, argc, (LPWSTR*)argv);
 
     return dwError;
 }
@@ -3041,7 +3095,8 @@ DWORD RChangeServiceConfigA(
     PSERVICE lpService = NULL;
     HKEY hServiceKey = NULL;
     LPWSTR lpDisplayNameW = NULL;
-    // LPWSTR lpBinaryPathNameW = NULL;
+    LPWSTR lpBinaryPathNameW = NULL;
+    LPWSTR lpCanonicalImagePathW = NULL;
     LPWSTR lpLoadOrderGroupW = NULL;
     LPWSTR lpDependenciesW = NULL;
     // LPWSTR lpPasswordW = NULL;
@@ -3078,13 +3133,14 @@ DWORD RChangeServiceConfigA(
         return ERROR_INVALID_HANDLE;
     }
 
-    /* FIXME: Lock database exclusively */
+    /* Lock the service database exclusively */
+    ScmLockDatabaseExclusive();
 
     if (lpService->bDeleted)
     {
-        /* FIXME: Unlock database */
         DPRINT("The service has already been marked for delete!\n");
-        return ERROR_SERVICE_MARKED_FOR_DELETE;
+        dwError = ERROR_SERVICE_MARKED_FOR_DELETE;
+        goto done;
     }
 
     /* Open the service key */
@@ -3174,41 +3230,51 @@ DWORD RChangeServiceConfigA(
         lpService->dwErrorControl = dwErrorControl;
     }
 
-#if 0
-    /* FIXME: set the new ImagePath value */
-
-    /* Set the image path */
-    if (dwServiceType & SERVICE_WIN32)
+    if (lpBinaryPathName != NULL && *lpBinaryPathName != 0)
     {
-        if (lpBinaryPathName != NULL && *lpBinaryPathName != 0)
+        /* Set the image path */
+        lpBinaryPathNameW = HeapAlloc(GetProcessHeap(),
+                                      0,
+                                      (strlen(lpBinaryPathName) + 1) * sizeof(WCHAR));
+        if (lpBinaryPathNameW == NULL)
         {
-            lpBinaryPathNameW=HeapAlloc(GetProcessHeap(),0, (strlen(lpBinaryPathName)+1) * sizeof(WCHAR));
-            MultiByteToWideChar(CP_ACP, 0, lpBinaryPathName, -1, lpBinaryPathNameW, strlen(lpBinaryPathName)+1);
-            dwError = RegSetValueExW(hServiceKey,
-                                     L"ImagePath",
-                                     0,
-                                     REG_EXPAND_SZ,
-                                     (LPBYTE)lpBinaryPathNameW,
-                                     (wcslen(lpBinaryPathNameW) + 1) * sizeof(WCHAR));
-            if (dwError != ERROR_SUCCESS)
-                goto done;
+            dwError = ERROR_NOT_ENOUGH_MEMORY;
+            goto done;
         }
-    }
-    else if (dwServiceType & SERVICE_DRIVER)
-    {
-        if (lpImagePath != NULL && *lpImagePath != 0)
+
+        MultiByteToWideChar(CP_ACP,
+                            0,
+                            lpBinaryPathName,
+                            -1,
+                            lpBinaryPathNameW,
+                            strlen(lpBinaryPathName) + 1);
+
+        if (lpService->Status.dwServiceType & SERVICE_DRIVER)
         {
-            dwError = RegSetValueExW(hServiceKey,
-                                     L"ImagePath",
-                                     0,
-                                     REG_EXPAND_SZ,
-                                     (LPBYTE)lpImagePath,
-                                     (wcslen(lpImagePath) + 1) *sizeof(WCHAR));
+            dwError = ScmCanonDriverImagePath(lpService->dwStartType,
+                                              lpBinaryPathNameW,
+                                              &lpCanonicalImagePathW);
+
+            HeapFree(GetProcessHeap(), 0, lpBinaryPathNameW);
+
             if (dwError != ERROR_SUCCESS)
                 goto done;
+
+            lpBinaryPathNameW = lpCanonicalImagePathW;
         }
+
+        dwError = RegSetValueExW(hServiceKey,
+                                 L"ImagePath",
+                                 0,
+                                 REG_EXPAND_SZ,
+                                 (LPBYTE)lpBinaryPathNameW,
+                                 (wcslen(lpBinaryPathNameW) + 1) * sizeof(WCHAR));
+
+        HeapFree(GetProcessHeap(), 0, lpBinaryPathNameW);
+
+        if (dwError != ERROR_SUCCESS)
+            goto done;
     }
-#endif
 
     /* Set the group name */
     if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
@@ -3299,9 +3365,10 @@ DWORD RChangeServiceConfigA(
         /* FIXME: Write password */
     }
 
-    /* FIXME: Unlock database */
-
 done:
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
     if (hServiceKey != NULL)
         RegCloseKey(hServiceKey);
 
@@ -3340,7 +3407,7 @@ DWORD RCreateServiceA(
     DWORD dwDependenciesLength = 0;
     DWORD dwLength;
     int len;
-    LPSTR lpStr;
+    LPCSTR lpStr;
 
     if (lpServiceName)
     {
@@ -3392,7 +3459,7 @@ DWORD RCreateServiceA(
 
     if (lpDependencies)
     {
-        lpStr = (LPSTR)lpDependencies;
+        lpStr = (LPCSTR)lpDependencies;
         while (*lpStr)
         {
             dwLength = strlen(lpStr) + 1;
@@ -3407,7 +3474,7 @@ DWORD RCreateServiceA(
             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
             goto cleanup;
         }
-        MultiByteToWideChar(CP_ACP, 0, (LPSTR)lpDependencies, dwDependenciesLength, lpDependenciesW, dwDependenciesLength);
+        MultiByteToWideChar(CP_ACP, 0, (LPCSTR)lpDependencies, dwDependenciesLength, lpDependenciesW, dwDependenciesLength);
     }
 
     if (lpServiceStartName)
@@ -3660,7 +3727,7 @@ DWORD REnumServicesStatusA(
     lpStatusPtrA = (LPENUM_SERVICE_STATUSA)lpBuffer;
     lpStringPtrA = (LPSTR)((ULONG_PTR)lpBuffer +
                   *lpServicesReturned * sizeof(ENUM_SERVICE_STATUSA));
-    lpStringPtrW = (LPWSTR)((ULONG_PTR)lpStatusPtrW + 
+    lpStringPtrW = (LPWSTR)((ULONG_PTR)lpStatusPtrW +
                   *lpServicesReturned * sizeof(ENUM_SERVICE_STATUSW));
 
     for (dwServiceCount = 0; dwServiceCount < *lpServicesReturned; dwServiceCount++)
@@ -3705,98 +3772,344 @@ Done:;
     if (lpStatusPtrW)
         HeapFree(GetProcessHeap(), 0, lpStatusPtrW);
 
-    DPRINT("REnumServicesStatusA() done (Error %lu)\n", dwError);
+    DPRINT("REnumServicesStatusA() done (Error %lu)\n", dwError);
+
+    return dwError;
+}
+
+
+/* Function 27 */
+DWORD ROpenSCManagerA(
+    LPSTR lpMachineName,
+    LPSTR lpDatabaseName,
+    DWORD dwDesiredAccess,
+    LPSC_RPC_HANDLE lpScHandle)
+{
+    UNICODE_STRING MachineName;
+    UNICODE_STRING DatabaseName;
+    DWORD dwError;
+
+    DPRINT("ROpenSCManagerA() called\n");
+
+    if (lpMachineName)
+        RtlCreateUnicodeStringFromAsciiz(&MachineName,
+                                         lpMachineName);
+
+    if (lpDatabaseName)
+        RtlCreateUnicodeStringFromAsciiz(&DatabaseName,
+                                         lpDatabaseName);
+
+    dwError = ROpenSCManagerW(lpMachineName ? MachineName.Buffer : NULL,
+                              lpDatabaseName ? DatabaseName.Buffer : NULL,
+                              dwDesiredAccess,
+                              lpScHandle);
+
+    if (lpMachineName)
+        RtlFreeUnicodeString(&MachineName);
+
+    if (lpDatabaseName)
+        RtlFreeUnicodeString(&DatabaseName);
+
+    return dwError;
+}
+
+
+/* Function 28 */
+DWORD ROpenServiceA(
+    SC_RPC_HANDLE hSCManager,
+    LPSTR lpServiceName,
+    DWORD dwDesiredAccess,
+    LPSC_RPC_HANDLE lpServiceHandle)
+{
+    UNICODE_STRING ServiceName;
+    DWORD dwError;
+
+    DPRINT("ROpenServiceA() called\n");
+
+    if (lpServiceName)
+        RtlCreateUnicodeStringFromAsciiz(&ServiceName,
+                                         lpServiceName);
+
+    dwError = ROpenServiceW(hSCManager,
+                            lpServiceName ? ServiceName.Buffer : NULL,
+                            dwDesiredAccess,
+                            lpServiceHandle);
+
+    if (lpServiceName)
+        RtlFreeUnicodeString(&ServiceName);
+
+    return dwError;
+}
+
+
+/* Function 29 */
+DWORD RQueryServiceConfigA(
+    SC_RPC_HANDLE hService,
+    LPBYTE lpBuf, //LPQUERY_SERVICE_CONFIGA lpServiceConfig,
+    DWORD cbBufSize,
+    LPBOUNDED_DWORD_8K pcbBytesNeeded)
+{
+    LPQUERY_SERVICE_CONFIGA lpServiceConfig = (LPQUERY_SERVICE_CONFIGA)lpBuf;
+    DWORD dwError = ERROR_SUCCESS;
+    PSERVICE_HANDLE hSvc;
+    PSERVICE lpService = NULL;
+    HKEY hServiceKey = NULL;
+    LPWSTR lpImagePath = NULL;
+    LPWSTR lpServiceStartName = NULL;
+    LPWSTR lpDependencies = NULL;
+    DWORD dwDependenciesLength = 0;
+    DWORD dwRequiredSize;
+    LPQUERY_SERVICE_CONFIGA lpConfig = NULL;
+    CHAR lpEmptyString[]={0,0};
+    LPSTR lpStr;
+
+    DPRINT("RQueryServiceConfigA() called\n");
+
+    if (ScmShutdown)
+        return ERROR_SHUTDOWN_IN_PROGRESS;
+
+    hSvc = ScmGetServiceFromHandle(hService);
+    if (hSvc == NULL)
+    {
+        DPRINT1("Invalid service handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+
+    if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
+                                  SERVICE_QUERY_CONFIG))
+    {
+        DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
+        return ERROR_ACCESS_DENIED;
+    }
+
+    lpService = hSvc->ServiceEntry;
+    if (lpService == NULL)
+    {
+        DPRINT("lpService == NULL!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
+
+    dwError = ScmOpenServiceKey(lpService->lpServiceName,
+                                KEY_READ,
+                                &hServiceKey);
+    if (dwError != ERROR_SUCCESS)
+        goto Done;
+
+    /* Read the image path */
+    dwError = ScmReadString(hServiceKey,
+                            L"ImagePath",
+                            &lpImagePath);
+    if (dwError != ERROR_SUCCESS)
+        goto Done;
+
+    /* Read the service start name */
+    ScmReadString(hServiceKey,
+                  L"ObjectName",
+                  &lpServiceStartName);
+
+    /* Read the dependencies */
+    ScmReadDependencies(hServiceKey,
+                        &lpDependencies,
+                        &dwDependenciesLength);
+
+    dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGA);
+
+    if (lpImagePath != NULL)
+        dwRequiredSize += wcslen(lpImagePath) + 1;
+    else
+        dwRequiredSize += 2;
+
+    if ((lpService->lpGroup != NULL) && (lpService->lpGroup->lpGroupName != NULL))
+        dwRequiredSize += wcslen(lpService->lpGroup->lpGroupName) + 1;
+    else
+        dwRequiredSize += 2;
+
+    /* Add Dependencies length */
+    if (lpDependencies != NULL)
+        dwRequiredSize += dwDependenciesLength;
+    else
+        dwRequiredSize += 2;
+
+    if (lpServiceStartName != NULL)
+        dwRequiredSize += wcslen(lpServiceStartName) + 1;
+    else
+        dwRequiredSize += 2;
+
+    if (lpService->lpDisplayName != NULL)
+        dwRequiredSize += wcslen(lpService->lpDisplayName) + 1;
+    else
+        dwRequiredSize += 2;
+
+    if (lpServiceConfig == NULL || cbBufSize < dwRequiredSize)
+    {
+        dwError = ERROR_INSUFFICIENT_BUFFER;
+    }
+    else
+    {
+        lpConfig = (LPQUERY_SERVICE_CONFIGA)lpServiceConfig;
+        lpConfig->dwServiceType = lpService->Status.dwServiceType;
+        lpConfig->dwStartType = lpService->dwStartType;
+        lpConfig->dwErrorControl = lpService->dwErrorControl;
+        lpConfig->dwTagId = lpService->dwTag;
+
+        lpStr = (LPSTR)(lpServiceConfig + 1);
+
+        /* NOTE: Strings that are NULL for QUERY_SERVICE_CONFIG are pointers to empty strings.
+           Verified in WINXP */
+
+        if (lpImagePath)
+        {
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpImagePath,
+                                -1,
+                                lpStr,
+                                wcslen(lpImagePath) + 1,
+                                0,
+                                0);
+        }
+        else
+        {
+            strcpy(lpStr, lpEmptyString);
+        }
+
+        lpConfig->lpBinaryPathName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpStr += (strlen((LPSTR)lpStr) + 1);
+
+        if (lpService->lpGroup && lpService->lpGroup->lpGroupName)
+        {
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpService->lpGroup->lpGroupName,
+                                -1,
+                                lpStr,
+                                wcslen(lpService->lpGroup->lpGroupName) + 1,
+                                0,
+                                0);
+        }
+        else
+        {
+            strcpy(lpStr, lpEmptyString);
+        }
+
+        lpConfig->lpLoadOrderGroup = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpStr += (strlen(lpStr) + 1);
+
+        /* Append Dependencies */
+        if (lpDependencies)
+        {
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpDependencies,
+                                dwDependenciesLength,
+                                lpStr,
+                                dwDependenciesLength,
+                                0,
+                                0);
+        }
+        else
+        {
+            strcpy(lpStr, lpEmptyString);
+        }
+
+        lpConfig->lpDependencies = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        if (lpDependencies)
+            lpStr += dwDependenciesLength;
+        else
+            lpStr += (strlen(lpStr) + 1);
+
+        if (lpServiceStartName)
+        {
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpServiceStartName,
+                                -1,
+                                lpStr,
+                                wcslen(lpServiceStartName) + 1,
+                                0,
+                                0);
+        }
+        else
+        {
+            strcpy(lpStr, lpEmptyString);
+        }
+
+        lpConfig->lpServiceStartName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpStr += (strlen(lpStr) + 1);
 
-    return dwError;
-}
+        if (lpService->lpDisplayName)
+        {
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpService->lpDisplayName,
+                                -1,
+                                lpStr,
+                                wcslen(lpService->lpDisplayName) + 1,
+                                0,
+                                0);
+        }
+        else
+        {
+            strcpy(lpStr, lpEmptyString);
+        }
 
+        lpConfig->lpDisplayName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+    }
 
-/* Function 27 */
-DWORD ROpenSCManagerA(
-    LPSTR lpMachineName,
-    LPSTR lpDatabaseName,
-    DWORD dwDesiredAccess,
-    LPSC_RPC_HANDLE lpScHandle)
-{
-    UNICODE_STRING MachineName;
-    UNICODE_STRING DatabaseName;
-    DWORD dwError;
+    if (pcbBytesNeeded != NULL)
+        *pcbBytesNeeded = dwRequiredSize;
 
-    DPRINT("ROpenSCManagerA() called\n");
+Done:;
+    /* Unlock the service database */
+    ScmUnlockDatabase();
 
-    if (lpMachineName)
-        RtlCreateUnicodeStringFromAsciiz(&MachineName,
-                                         lpMachineName);
+    if (lpImagePath != NULL)
+        HeapFree(GetProcessHeap(), 0, lpImagePath);
 
-    if (lpDatabaseName)
-        RtlCreateUnicodeStringFromAsciiz(&DatabaseName,
-                                         lpDatabaseName);
+    if (lpServiceStartName != NULL)
+        HeapFree(GetProcessHeap(), 0, lpServiceStartName);
 
-    dwError = ROpenSCManagerW(lpMachineName ? MachineName.Buffer : NULL,
-                              lpDatabaseName ? DatabaseName.Buffer : NULL,
-                              dwDesiredAccess,
-                              lpScHandle);
+    if (lpDependencies != NULL)
+        HeapFree(GetProcessHeap(), 0, lpDependencies);
 
-    if (lpMachineName)
-        RtlFreeUnicodeString(&MachineName);
+    if (hServiceKey != NULL)
+        RegCloseKey(hServiceKey);
 
-    if (lpDatabaseName)
-        RtlFreeUnicodeString(&DatabaseName);
+    DPRINT("RQueryServiceConfigA() done\n");
 
     return dwError;
 }
 
 
-/* Function 28 */
-DWORD ROpenServiceA(
+/* Function 30 */
+DWORD RQueryServiceLockStatusA(
     SC_RPC_HANDLE hSCManager,
-    LPSTR lpServiceName,
-    DWORD dwDesiredAccess,
-    LPSC_RPC_HANDLE lpServiceHandle)
+    LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus,
+    DWORD cbBufSize,
+    LPBOUNDED_DWORD_4K pcbBytesNeeded)
 {
-    UNICODE_STRING ServiceName;
-    DWORD dwError;
-
-    DPRINT("ROpenServiceA() called\n");
-
-    if (lpServiceName)
-        RtlCreateUnicodeStringFromAsciiz(&ServiceName,
-                                         lpServiceName);
-
-    dwError = ROpenServiceW(hSCManager,
-                            lpServiceName ? ServiceName.Buffer : NULL,
-                            dwDesiredAccess,
-                            lpServiceHandle);
-
-    if (lpServiceName)
-        RtlFreeUnicodeString(&ServiceName);
-
-    return dwError;
+    UNIMPLEMENTED;
+    return ERROR_CALL_NOT_IMPLEMENTED;
 }
 
 
-/* Function 29 */
-DWORD RQueryServiceConfigA(
+/* Function 31 */
+DWORD RStartServiceA(
     SC_RPC_HANDLE hService,
-    LPBYTE lpBuf, //LPQUERY_SERVICE_CONFIGA lpServiceConfig,
-    DWORD cbBufSize,
-    LPBOUNDED_DWORD_8K pcbBytesNeeded)
+    DWORD argc,
+    LPSTRING_PTRSA argv)
 {
-    LPQUERY_SERVICE_CONFIGA lpServiceConfig = (LPQUERY_SERVICE_CONFIGA)lpBuf;
     DWORD dwError = ERROR_SUCCESS;
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService = NULL;
-    HKEY hServiceKey = NULL;
-    LPWSTR lpImagePath = NULL;
-    LPWSTR lpServiceStartName = NULL;
-    LPWSTR lpDependencies = NULL;
-    DWORD dwDependenciesLength = 0;
-    DWORD dwRequiredSize;
-    LPQUERY_SERVICE_CONFIGA lpConfig = NULL;
-    CHAR lpEmptyString[]={0,0};
-    LPSTR lpStr;
+    LPWSTR *lpVector = NULL;
+    DWORD i;
+    DWORD dwLength;
 
-    DPRINT("RQueryServiceConfigA() called\n");
+    DPRINT("RStartServiceA() called\n");
 
     if (ScmShutdown)
         return ERROR_SHUTDOWN_IN_PROGRESS;
@@ -3809,7 +4122,7 @@ DWORD RQueryServiceConfigA(
     }
 
     if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
-                                  SERVICE_QUERY_CONFIG))
+                                  SERVICE_START))
     {
         DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
         return ERROR_ACCESS_DENIED;
@@ -3822,476 +4135,522 @@ DWORD RQueryServiceConfigA(
         return ERROR_INVALID_HANDLE;
     }
 
-    /* FIXME: Lock the service database shared */
+    if (lpService->dwStartType == SERVICE_DISABLED)
+        return ERROR_SERVICE_DISABLED;
+
+    if (lpService->bDeleted)
+        return ERROR_SERVICE_MARKED_FOR_DELETE;
+
+    /* Build a Unicode argument vector */
+    if (argc > 0)
+    {
+        lpVector = HeapAlloc(GetProcessHeap(),
+                             HEAP_ZERO_MEMORY,
+                             argc * sizeof(LPWSTR));
+        if (lpVector == NULL)
+            return ERROR_NOT_ENOUGH_MEMORY;
+
+        for (i = 0; i < argc; i++)
+        {
+            dwLength = MultiByteToWideChar(CP_ACP,
+                                           0,
+                                           ((LPSTR*)argv)[i],
+                                           -1,
+                                           NULL,
+                                           0);
+
+            lpVector[i] = HeapAlloc(GetProcessHeap(),
+                                    HEAP_ZERO_MEMORY,
+                                    dwLength * sizeof(WCHAR));
+            if (lpVector[i] == NULL)
+            {
+                dwError = ERROR_NOT_ENOUGH_MEMORY;
+                goto done;
+            }
+
+            MultiByteToWideChar(CP_ACP,
+                                0,
+                                ((LPSTR*)argv)[i],
+                                -1,
+                                lpVector[i],
+                                dwLength);
+        }
+    }
+
+    /* Start the service */
+    dwError = ScmStartService(lpService, argc, lpVector);
 
-    dwError = ScmOpenServiceKey(lpService->lpServiceName,
-                                KEY_READ,
-                                &hServiceKey);
-    if (dwError != ERROR_SUCCESS)
-        goto Done;
+done:
+    /* Free the Unicode argument vector */
+    if (lpVector != NULL)
+    {
+        for (i = 0; i < argc; i++)
+        {
+            if (lpVector[i] != NULL)
+                HeapFree(GetProcessHeap(), 0, lpVector[i]);
+        }
+        HeapFree(GetProcessHeap(), 0, lpVector);
+    }
 
-    /* Read the image path */
-    dwError = ScmReadString(hServiceKey,
-                            L"ImagePath",
-                            &lpImagePath);
-    if (dwError != ERROR_SUCCESS)
-        goto Done;
+    return dwError;
+}
 
-    /* Read the service start name */
-    ScmReadString(hServiceKey,
-                  L"ObjectName",
-                  &lpServiceStartName);
 
-    /* Read the dependencies */
-    ScmReadDependencies(hServiceKey,
-                        &lpDependencies,
-                        &dwDependenciesLength);
+/* Function 32 */
+DWORD RGetServiceDisplayNameA(
+    SC_RPC_HANDLE hSCManager,
+    LPCSTR lpServiceName,
+    LPSTR lpDisplayName,
+    LPBOUNDED_DWORD_4K lpcchBuffer)
+{
+//    PMANAGER_HANDLE hManager;
+    PSERVICE lpService = NULL;
+    DWORD dwLength;
+    DWORD dwError;
+    LPWSTR lpServiceNameW;
 
-    dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGW);
+    DPRINT("RGetServiceDisplayNameA() called\n");
+    DPRINT("hSCManager = %p\n", hSCManager);
+    DPRINT("lpServiceName: %s\n", lpServiceName);
+    DPRINT("lpDisplayName: %p\n", lpDisplayName);
+    DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
 
-    if (lpImagePath != NULL)
-        dwRequiredSize += wcslen(lpImagePath) + 1;
-    else
-        dwRequiredSize += 2;
+//    hManager = (PMANAGER_HANDLE)hSCManager;
+//    if (hManager->Handle.Tag != MANAGER_TAG)
+//    {
+//        DPRINT("Invalid manager handle!\n");
+//        return ERROR_INVALID_HANDLE;
+//    }
 
-    if ((lpService->lpGroup != NULL) && (lpService->lpGroup->lpGroupName != NULL))
-        dwRequiredSize += wcslen(lpService->lpGroup->lpGroupName) + 1;
-    else
-        dwRequiredSize += 2;
+    if (lpServiceName != NULL)
+    {
+        dwLength = strlen(lpServiceName) + 1;
+        lpServiceNameW = HeapAlloc(GetProcessHeap(),
+                                   HEAP_ZERO_MEMORY,
+                                   dwLength * sizeof(WCHAR));
+        if (!lpServiceNameW)
+            return ERROR_NOT_ENOUGH_MEMORY;
 
-    /* Add Dependencies length */
-    if (lpDependencies != NULL)
-        dwRequiredSize += dwDependenciesLength;
-    else
-        dwRequiredSize += 2;
+        MultiByteToWideChar(CP_ACP,
+                            0,
+                            lpServiceName,
+                            -1,
+                            lpServiceNameW,
+                            dwLength);
 
-    if (lpServiceStartName != NULL)
-        dwRequiredSize += wcslen(lpServiceStartName) + 1;
-    else
-        dwRequiredSize += 2;
+        lpService = ScmGetServiceEntryByName(lpServiceNameW);
 
-    if (lpService->lpDisplayName != NULL)
-        dwRequiredSize += wcslen(lpService->lpDisplayName) + 1;
-    else
-        dwRequiredSize += 2;
+        HeapFree(GetProcessHeap(), 0, lpServiceNameW);
+    }
+
+    if (lpService == NULL)
+    {
+        DPRINT("Could not find a service!\n");
+
+        /* If the service could not be found and lpcchBuffer is 0, windows
+           puts null in lpDisplayName and puts 1 in lpcchBuffer */
+        if (*lpcchBuffer == 0)
+        {
+            *lpcchBuffer = 1;
+            if (lpDisplayName != NULL)
+            {
+                *lpDisplayName = '\0';
+            }
+        }
+        return ERROR_SERVICE_DOES_NOT_EXIST;
+    }
 
-    if (lpServiceConfig == NULL || cbBufSize < dwRequiredSize)
+    if (!lpService->lpDisplayName)
     {
-        dwError = ERROR_INSUFFICIENT_BUFFER;
+        dwLength = wcslen(lpService->lpServiceName);
+        if (lpDisplayName != NULL &&
+            *lpcchBuffer > dwLength)
+        {
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpService->lpServiceName,
+                                wcslen(lpService->lpServiceName),
+                                lpDisplayName,
+                                dwLength + 1,
+                                NULL,
+                                NULL);
+            return ERROR_SUCCESS;
+        }
     }
     else
     {
-        lpConfig = (LPQUERY_SERVICE_CONFIGA)lpServiceConfig;
-        lpConfig->dwServiceType = lpService->Status.dwServiceType;
-        lpConfig->dwStartType = lpService->dwStartType;
-        lpConfig->dwErrorControl = lpService->dwErrorControl;
-        lpConfig->dwTagId = lpService->dwTag;
-
-        lpStr = (LPSTR)(lpServiceConfig + 1);
-
-        /* NOTE: Strings that are NULL for QUERY_SERVICE_CONFIG are pointers to empty strings.
-          Verified in WINXP*/
-
-        if (lpImagePath)
+        dwLength = wcslen(lpService->lpDisplayName);
+        if (lpDisplayName != NULL &&
+            *lpcchBuffer > dwLength)
         {
             WideCharToMultiByte(CP_ACP,
                                 0,
-                                lpImagePath,
-                                -1,
-                                lpStr,
-                                wcslen(lpImagePath) + 1,
-                                0,
-                                0);
-        }
-        else
-        {
-            strcpy(lpStr, lpEmptyString);
+                                lpService->lpDisplayName,
+                                wcslen(lpService->lpDisplayName),
+                                lpDisplayName,
+                                dwLength + 1,
+                                NULL,
+                                NULL);
+            return ERROR_SUCCESS;
         }
+    }
 
-        lpConfig->lpBinaryPathName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
-        lpStr += (strlen((LPSTR)lpStr) + 1);
+    dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
 
-        if (lpService->lpGroup && lpService->lpGroup->lpGroupName)
-        {
-            WideCharToMultiByte(CP_ACP,
-                                0,
-                                lpService->lpGroup->lpGroupName,
-                                -1,
-                                lpStr,
-                                wcslen(lpService->lpGroup->lpGroupName) + 1,
-                                0,
-                                0);
-        }
-        else
-        {
-            strcpy(lpStr, lpEmptyString);
-        }
+    *lpcchBuffer = dwLength * 2;
 
-        lpConfig->lpLoadOrderGroup = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
-        lpStr += (strlen(lpStr) + 1);
+    return dwError;
+}
 
-        /* Append Dependencies */
-        if (lpDependencies)
-        {
-            WideCharToMultiByte(CP_ACP,
-                                0,
-                                lpDependencies,
-                                dwDependenciesLength,
-                                lpStr,
-                                dwDependenciesLength,
-                                0,
-                                0);
-        }
-        else
-        {
-            strcpy(lpStr, lpEmptyString);
-        }
 
-        lpConfig->lpDependencies = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
-        if (lpDependencies)
-            lpStr += dwDependenciesLength;
-        else
-            lpStr += (strlen(lpStr) + 1);
+/* Function 33 */
+DWORD RGetServiceKeyNameA(
+    SC_RPC_HANDLE hSCManager,
+    LPCSTR lpDisplayName,
+    LPSTR lpServiceName,
+    LPBOUNDED_DWORD_4K lpcchBuffer)
+{
+    PSERVICE lpService;
+    DWORD dwLength;
+    DWORD dwError;
+    LPWSTR lpDisplayNameW;
 
-        if (lpServiceStartName)
-        {
-            WideCharToMultiByte(CP_ACP,
-                                0,
-                                lpServiceStartName,
-                                -1,
-                                lpStr,
-                                wcslen(lpServiceStartName) + 1,
-                                0,
-                                0);
-        }
-        else
-        {
-            strcpy(lpStr, lpEmptyString);
-        }
+    DPRINT("RGetServiceKeyNameA() called\n");
+    DPRINT("hSCManager = %p\n", hSCManager);
+    DPRINT("lpDisplayName: %s\n", lpDisplayName);
+    DPRINT("lpServiceName: %p\n", lpServiceName);
+    DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
 
-        lpConfig->lpServiceStartName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
-        lpStr += (strlen(lpStr) + 1);
+    dwLength = strlen(lpDisplayName) + 1;
+    lpDisplayNameW = HeapAlloc(GetProcessHeap(),
+                               HEAP_ZERO_MEMORY,
+                               dwLength * sizeof(WCHAR));
+    if (!lpDisplayNameW)
+        return ERROR_NOT_ENOUGH_MEMORY;
 
-        if (lpService->lpDisplayName)
-        {
-            WideCharToMultiByte(CP_ACP,
-                                0,
-                                lpService->lpDisplayName,
-                                -1,
-                                lpStr,
-                                wcslen(lpService->lpDisplayName) + 1,
-                                0,
-                                0);
-        }
-        else
-        {
-            strcpy(lpStr, lpEmptyString);
-        }
+    MultiByteToWideChar(CP_ACP,
+                        0,
+                        lpDisplayName,
+                        -1,
+                        lpDisplayNameW,
+                        dwLength);
 
-        lpConfig->lpDisplayName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
-    }
+    lpService = ScmGetServiceEntryByDisplayName(lpDisplayNameW);
 
-    if (pcbBytesNeeded != NULL)
-        *pcbBytesNeeded = dwRequiredSize;
+    HeapFree(GetProcessHeap(), 0, lpDisplayNameW);
 
-Done:;
-    if (lpImagePath != NULL)
-        HeapFree(GetProcessHeap(), 0, lpImagePath);
+    if (lpService == NULL)
+    {
+        DPRINT("Could not find the service!\n");
 
-    if (lpServiceStartName != NULL)
-        HeapFree(GetProcessHeap(), 0, lpServiceStartName);
+        /* If the service could not be found and lpcchBuffer is 0,
+           put null in lpDisplayName and puts 1 in lpcchBuffer, verified WINXP. */
+        if (*lpcchBuffer == 0)
+        {
+            *lpcchBuffer = 1;
+            if (lpServiceName != NULL)
+            {
+                *lpServiceName = '\0';
+            }
+        }
 
-    if (lpDependencies != NULL)
-        HeapFree(GetProcessHeap(), 0, lpDependencies);
+        return ERROR_SERVICE_DOES_NOT_EXIST;
+    }
 
-    if (hServiceKey != NULL)
-        RegCloseKey(hServiceKey);
+    dwLength = wcslen(lpService->lpServiceName);
+    if (lpServiceName != NULL &&
+        *lpcchBuffer > dwLength)
+    {
+        WideCharToMultiByte(CP_ACP,
+                            0,
+                            lpService->lpServiceName,
+                            wcslen(lpService->lpServiceName),
+                            lpServiceName,
+                            dwLength + 1,
+                            NULL,
+                            NULL);
+        return ERROR_SUCCESS;
+    }
 
-    /* FIXME: Unlock the service database */
+    dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
 
-    DPRINT("RQueryServiceConfigA() done\n");
+    *lpcchBuffer = dwLength * 2;
 
     return dwError;
 }
 
 
-/* Function 30 */
-DWORD RQueryServiceLockStatusA(
+/* Function 34 */
+DWORD RI_ScGetCurrentGroupStateW(
     SC_RPC_HANDLE hSCManager,
-    LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus,
-    DWORD cbBufSize,
-    LPBOUNDED_DWORD_4K pcbBytesNeeded)
+    LPWSTR lpLoadOrderGroup,
+    LPDWORD lpState)
 {
     UNIMPLEMENTED;
     return ERROR_CALL_NOT_IMPLEMENTED;
 }
 
 
-/* Function 31 */
-DWORD RStartServiceA(
-    SC_RPC_HANDLE hService,
-    DWORD argc,
-    LPSTRING_PTRSA argv)
+/* Function 35 */
+DWORD REnumServiceGroupW(
+    SC_RPC_HANDLE hSCManager,
+    DWORD dwServiceType,
+    DWORD dwServiceState,
+    LPBYTE lpBuffer,
+    DWORD cbBufSize,
+    LPBOUNDED_DWORD_256K pcbBytesNeeded,
+    LPBOUNDED_DWORD_256K lpServicesReturned,
+    LPBOUNDED_DWORD_256K lpResumeIndex,
+    LPCWSTR pszGroupName)
 {
+    PMANAGER_HANDLE hManager;
+    PSERVICE lpService;
     DWORD dwError = ERROR_SUCCESS;
-    PSERVICE_HANDLE hSvc;
-    PSERVICE lpService = NULL;
+    PLIST_ENTRY ServiceEntry;
+    PSERVICE CurrentService;
+    DWORD dwState;
+    DWORD dwRequiredSize;
+    DWORD dwServiceCount;
+    DWORD dwSize;
+    DWORD dwLastResumeCount = 0;
+    LPENUM_SERVICE_STATUSW lpStatusPtr;
+    LPWSTR lpStringPtr;
 
-    DPRINT("RStartServiceA() called\n");
+    DPRINT("REnumServiceGroupW() called\n");
 
     if (ScmShutdown)
         return ERROR_SHUTDOWN_IN_PROGRESS;
 
-    hSvc = ScmGetServiceFromHandle(hService);
-    if (hSvc == NULL)
+    hManager = ScmGetServiceManagerFromHandle(hSCManager);
+    if (hManager == NULL)
+    {
+        DPRINT1("Invalid service manager handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+
+    *pcbBytesNeeded = 0;
+    *lpServicesReturned = 0;
+
+    if ((dwServiceType == 0) ||
+        ((dwServiceType & ~(SERVICE_DRIVER | SERVICE_WIN32)) != 0))
+    {
+        DPRINT("Not a valid Service Type!\n");
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if ((dwServiceState != SERVICE_ACTIVE) &&
+        (dwServiceState != SERVICE_INACTIVE) &&
+        (dwServiceState != SERVICE_STATE_ALL))
     {
-        DPRINT1("Invalid service handle!\n");
-        return ERROR_INVALID_HANDLE;
+        DPRINT("Not a valid Service State!\n");
+        return ERROR_INVALID_PARAMETER;
     }
 
-    if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
-                                  SERVICE_START))
+    /* Check access rights */
+    if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
+                                  SC_MANAGER_ENUMERATE_SERVICE))
     {
-        DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
+        DPRINT("Insufficient access rights! 0x%lx\n",
+                hManager->Handle.DesiredAccess);
         return ERROR_ACCESS_DENIED;
     }
 
-    lpService = hSvc->ServiceEntry;
+    if (lpResumeIndex)
+        dwLastResumeCount = *lpResumeIndex;
+
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
+
+    lpService = ScmGetServiceEntryByResumeCount(dwLastResumeCount);
     if (lpService == NULL)
     {
-        DPRINT("lpService == NULL!\n");
-        return ERROR_INVALID_HANDLE;
+        dwError = ERROR_SUCCESS;
+        goto Done;
     }
 
-    if (lpService->dwStartType == SERVICE_DISABLED)
-        return ERROR_SERVICE_DISABLED;
+    dwRequiredSize = 0;
+    dwServiceCount = 0;
 
-    if (lpService->bDeleted)
-        return ERROR_SERVICE_MARKED_FOR_DELETE;
+    for (ServiceEntry = &lpService->ServiceListEntry;
+         ServiceEntry != &ServiceListHead;
+         ServiceEntry = ServiceEntry->Flink)
+    {
+        CurrentService = CONTAINING_RECORD(ServiceEntry,
+                                           SERVICE,
+                                           ServiceListEntry);
 
-    /* FIXME: Convert argument vector to Unicode */
+        if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
+            continue;
 
-    /* Start the service */
-    dwError = ScmStartService(lpService, 0, NULL);
+        dwState = SERVICE_ACTIVE;
+        if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
+            dwState = SERVICE_INACTIVE;
 
-    /* FIXME: Free argument vector */
+        if ((dwState & dwServiceState) == 0)
+            continue;
 
-    return dwError;
-}
+        if (pszGroupName)
+        {
+            if (*pszGroupName == 0)
+            {
+                if (CurrentService->lpGroup != NULL)
+                    continue;
+            }
+            else
+            {
+                if ((CurrentService->lpGroup == NULL) ||
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
+                    continue;
+            }
+        }
 
+        dwSize = sizeof(ENUM_SERVICE_STATUSW) +
+                 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
 
-/* Function 32 */
-DWORD RGetServiceDisplayNameA(
-    SC_RPC_HANDLE hSCManager,
-    LPCSTR lpServiceName,
-    LPSTR lpDisplayName,
-    LPBOUNDED_DWORD_4K lpcchBuffer)
-{
-//    PMANAGER_HANDLE hManager;
-    PSERVICE lpService = NULL;
-    DWORD dwLength;
-    DWORD dwError;
-    LPWSTR lpServiceNameW;
+        if (dwRequiredSize + dwSize > cbBufSize)
+        {
+            DPRINT("Service name: %S  no fit\n", CurrentService->lpServiceName);
+            break;
+        }
 
-    DPRINT("RGetServiceDisplayNameA() called\n");
-    DPRINT("hSCManager = %p\n", hSCManager);
-    DPRINT("lpServiceName: %s\n", lpServiceName);
-    DPRINT("lpDisplayName: %p\n", lpDisplayName);
-    DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
+        DPRINT("Service name: %S  fit\n", CurrentService->lpServiceName);
+        dwRequiredSize += dwSize;
+        dwServiceCount++;
+        dwLastResumeCount = CurrentService->dwResumeCount;
+    }
 
-//    hManager = (PMANAGER_HANDLE)hSCManager;
-//    if (hManager->Handle.Tag != MANAGER_TAG)
-//    {
-//        DPRINT("Invalid manager handle!\n");
-//        return ERROR_INVALID_HANDLE;
-//    }
+    DPRINT("dwRequiredSize: %lu\n", dwRequiredSize);
+    DPRINT("dwServiceCount: %lu\n", dwServiceCount);
 
-    if (lpServiceName != NULL)
+    for (;
+         ServiceEntry != &ServiceListHead;
+         ServiceEntry = ServiceEntry->Flink)
     {
-        dwLength = strlen(lpServiceName) + 1;
-        lpServiceNameW = HeapAlloc(GetProcessHeap(),
-                                   HEAP_ZERO_MEMORY,
-                                   dwLength * sizeof(WCHAR));
-        if (!lpServiceNameW)
-            return ERROR_NOT_ENOUGH_MEMORY;
-
-        MultiByteToWideChar(CP_ACP,
-                            0,
-                            lpServiceName,
-                            -1,
-                            lpServiceNameW,
-                            dwLength);
+        CurrentService = CONTAINING_RECORD(ServiceEntry,
+                                           SERVICE,
+                                           ServiceListEntry);
 
-        lpService = ScmGetServiceEntryByName(lpServiceNameW);
+        if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
+            continue;
 
-        HeapFree(GetProcessHeap(), 0, lpServiceNameW);
-    }
+        dwState = SERVICE_ACTIVE;
+        if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
+            dwState = SERVICE_INACTIVE;
 
-    if (lpService == NULL)
-    {
-        DPRINT("Could not find a service!\n");
+        if ((dwState & dwServiceState) == 0)
+            continue;
 
-        /* If the service could not be found and lpcchBuffer is 0, windows
-           puts null in lpDisplayName and puts 1 in lpcchBuffer */
-        if (*lpcchBuffer == 0)
+        if (pszGroupName)
         {
-            *lpcchBuffer = 1;
-            if (lpDisplayName != NULL)
+            if (*pszGroupName == 0)
             {
-                *lpDisplayName = '\0';
+                if (CurrentService->lpGroup != NULL)
+                    continue;
+            }
+            else
+            {
+                if ((CurrentService->lpGroup == NULL) ||
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
+                    continue;
             }
         }
-        return ERROR_SERVICE_DOES_NOT_EXIST;
-    }
 
-    if (!lpService->lpDisplayName)
-    {
-        dwLength = wcslen(lpService->lpServiceName);
-        if (lpDisplayName != NULL &&
-            *lpcchBuffer > dwLength)
-        {
-            WideCharToMultiByte(CP_ACP,
-                                0,
-                                lpService->lpServiceName,
-                                wcslen(lpService->lpServiceName),
-                                lpDisplayName,
-                                dwLength + 1,
-                                NULL,
-                                NULL);
-            return ERROR_SUCCESS;
-        }
-    }
-    else
-    {
-        dwLength = wcslen(lpService->lpDisplayName);
-        if (lpDisplayName != NULL &&
-            *lpcchBuffer > dwLength)
-        {
-            WideCharToMultiByte(CP_ACP,
-                                0,
-                                lpService->lpDisplayName,
-                                wcslen(lpService->lpDisplayName),
-                                lpDisplayName,
-                                dwLength + 1,
-                                NULL,
-                                NULL);
-            return ERROR_SUCCESS;
-        }
+        dwRequiredSize += (sizeof(ENUM_SERVICE_STATUSW) +
+                           ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                           ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR)));
+
+        dwError = ERROR_MORE_DATA;
     }
 
-    dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
+    DPRINT("*pcbBytesNeeded: %lu\n", dwRequiredSize);
 
-    *lpcchBuffer = dwLength * 2;
+    if (lpResumeIndex)
+        *lpResumeIndex = dwLastResumeCount;
 
-    return dwError;
-}
+    *lpServicesReturned = dwServiceCount;
+    *pcbBytesNeeded = dwRequiredSize;
 
+    lpStatusPtr = (LPENUM_SERVICE_STATUSW)lpBuffer;
+    lpStringPtr = (LPWSTR)((ULONG_PTR)lpBuffer +
+                           dwServiceCount * sizeof(ENUM_SERVICE_STATUSW));
 
-/* Function 33 */
-DWORD RGetServiceKeyNameA(
-    SC_RPC_HANDLE hSCManager,
-    LPCSTR lpDisplayName,
-    LPSTR lpServiceName,
-    LPBOUNDED_DWORD_4K lpcchBuffer)
-{
-    PSERVICE lpService;
-    DWORD dwLength;
-    DWORD dwError;
-    LPWSTR lpDisplayNameW;
+    dwRequiredSize = 0;
+    for (ServiceEntry = &lpService->ServiceListEntry;
+         ServiceEntry != &ServiceListHead;
+         ServiceEntry = ServiceEntry->Flink)
+    {
+        CurrentService = CONTAINING_RECORD(ServiceEntry,
+                                           SERVICE,
+                                           ServiceListEntry);
 
-    DPRINT("RGetServiceKeyNameA() called\n");
-    DPRINT("hSCManager = %p\n", hSCManager);
-    DPRINT("lpDisplayName: %s\n", lpDisplayName);
-    DPRINT("lpServiceName: %p\n", lpServiceName);
-    DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
+        if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
+            continue;
 
-    dwLength = strlen(lpDisplayName) + 1;
-    lpDisplayNameW = HeapAlloc(GetProcessHeap(),
-                               HEAP_ZERO_MEMORY,
-                               dwLength * sizeof(WCHAR));
-    if (!lpDisplayNameW)
-        return ERROR_NOT_ENOUGH_MEMORY;
+        dwState = SERVICE_ACTIVE;
+        if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
+            dwState = SERVICE_INACTIVE;
 
-    MultiByteToWideChar(CP_ACP,
-                        0,
-                        lpDisplayName,
-                        -1,
-                        lpDisplayNameW,
-                        dwLength);
+        if ((dwState & dwServiceState) == 0)
+            continue;
+
+        if (pszGroupName)
+        {
+            if (*pszGroupName == 0)
+            {
+                if (CurrentService->lpGroup != NULL)
+                    continue;
+            }
+            else
+            {
+                if ((CurrentService->lpGroup == NULL) ||
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
+                    continue;
+            }
+        }
+
+        dwSize = sizeof(ENUM_SERVICE_STATUSW) +
+                 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
 
-    lpService = ScmGetServiceEntryByDisplayName(lpDisplayNameW);
+        if (dwRequiredSize + dwSize > cbBufSize)
+            break;
 
-    HeapFree(GetProcessHeap(), 0, lpDisplayNameW);
+        /* Copy the service name */
+        wcscpy(lpStringPtr, CurrentService->lpServiceName);
+        lpStatusPtr->lpServiceName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
+        lpStringPtr += (wcslen(CurrentService->lpServiceName) + 1);
 
-    if (lpService == NULL)
-    {
-        DPRINT("Could not find the service!\n");
+        /* Copy the display name */
+        wcscpy(lpStringPtr, CurrentService->lpDisplayName);
+        lpStatusPtr->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
+        lpStringPtr += (wcslen(CurrentService->lpDisplayName) + 1);
 
-        /* If the service could not be found and lpcchBuffer is 0,
-           put null in lpDisplayName and puts 1 in lpcchBuffer, verified WINXP. */
-        if (*lpcchBuffer == 0)
-        {
-            *lpcchBuffer = 1;
-            if (lpServiceName != NULL)
-            {
-                *lpServiceName = '\0';
-            }
-        }
+        /* Copy the status information */
+        memcpy(&lpStatusPtr->ServiceStatus,
+               &CurrentService->Status,
+               sizeof(SERVICE_STATUS));
 
-        return ERROR_SERVICE_DOES_NOT_EXIST;
+        lpStatusPtr++;
+        dwRequiredSize += dwSize;
     }
 
-    dwLength = wcslen(lpService->lpServiceName);
-    if (lpServiceName != NULL &&
-        *lpcchBuffer > dwLength)
+    if (dwError == ERROR_SUCCESS)
     {
-        WideCharToMultiByte(CP_ACP,
-                            0,
-                            lpService->lpServiceName,
-                            wcslen(lpService->lpServiceName),
-                            lpServiceName,
-                            dwLength + 1,
-                            NULL,
-                            NULL);
-        return ERROR_SUCCESS;
+        *pcbBytesNeeded = 0;
+        if (lpResumeIndex) *lpResumeIndex = 0;
     }
 
-    dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
+Done:
+    /* Unlock the service database */
+    ScmUnlockDatabase();
 
-    *lpcchBuffer = dwLength * 2;
+    DPRINT("REnumServiceGroupW() done (Error %lu)\n", dwError);
 
     return dwError;
 }
 
 
-/* Function 34 */
-DWORD RI_ScGetCurrentGroupStateW(
-    SC_RPC_HANDLE hSCManager,
-    LPWSTR lpLoadOrderGroup,
-    LPDWORD lpState)
-{
-    UNIMPLEMENTED;
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-
-/* Function 35 */
-DWORD REnumServiceGroupW(
-    SC_RPC_HANDLE hSCManager,
-    DWORD dwServiceType,
-    DWORD dwServiceState,
-    LPBYTE lpBuffer,
-    DWORD cbBufSize,
-    LPBOUNDED_DWORD_256K pcbBytesNeeded,
-    LPBOUNDED_DWORD_256K lpServicesReturned,
-    LPBOUNDED_DWORD_256K lpResumeIndex,
-    LPCWSTR pszGroupName)
-{
-    UNIMPLEMENTED;
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-
 //
 // WARNING: This function is untested
 //
@@ -4312,9 +4671,9 @@ DWORD RChangeServiceConfig2A(
     if (InfoW.dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
     {
         LPSERVICE_DESCRIPTIONW lpServiceDescriptonW;
-        LPSERVICE_DESCRIPTIONA lpServiceDescriptonA;
+        //LPSERVICE_DESCRIPTIONA lpServiceDescriptonA;
 
-        lpServiceDescriptonA = Info.psd;
+        //lpServiceDescriptonA = Info.psd;
 
         ///if (lpServiceDescriptonA &&
         ///lpServiceDescriptonA->lpDescription)
@@ -4322,8 +4681,8 @@ DWORD RChangeServiceConfig2A(
             dwLength = (strlen(Info.lpDescription) + 1) * sizeof(WCHAR);
 
             lpServiceDescriptonW = HeapAlloc(GetProcessHeap(),
-                                            0,
-                                            dwLength + sizeof(SERVICE_DESCRIPTIONW));
+                                             0,
+                                             dwLength + sizeof(SERVICE_DESCRIPTIONW));
             if (!lpServiceDescriptonW)
             {
                 return ERROR_NOT_ENOUGH_MEMORY;
@@ -4444,13 +4803,14 @@ DWORD RChangeServiceConfig2W(
         return ERROR_INVALID_HANDLE;
     }
 
-    /* FIXME: Lock database exclusively */
+    /* Lock the service database exclusively */
+    ScmLockDatabaseExclusive();
 
     if (lpService->bDeleted)
     {
-        /* FIXME: Unlock database */
         DPRINT("The service has already been marked for delete!\n");
-        return ERROR_SERVICE_MARKED_FOR_DELETE;
+        dwError = ERROR_SERVICE_MARKED_FOR_DELETE;
+        goto done;
     }
 
     /* Open the service key */
@@ -4465,19 +4825,17 @@ DWORD RChangeServiceConfig2W(
         LPSERVICE_DESCRIPTIONW lpServiceDescription;
 
         lpServiceDescription = (LPSERVICE_DESCRIPTIONW)Info.psd;
-        lpServiceDescription->lpDescription = (LPWSTR)((ULONG_PTR)lpServiceDescription + sizeof(LPSERVICE_DESCRIPTIONW));
 
         if (lpServiceDescription != NULL &&
             lpServiceDescription->lpDescription != NULL)
         {
             DPRINT("Setting value %S\n", lpServiceDescription->lpDescription);
-            RegSetValueExW(hServiceKey,
-                           L"Description",
-                           0,
-                           REG_SZ,
-                           (LPBYTE)lpServiceDescription->lpDescription,
-                           (wcslen(lpServiceDescription->lpDescription) + 1) * sizeof(WCHAR));
-
+            dwError = RegSetValueExW(hServiceKey,
+                                     L"Description",
+                                     0,
+                                     REG_SZ,
+                                     (LPBYTE)lpServiceDescription->lpDescription,
+                                     (wcslen(lpServiceDescription->lpDescription) + 1) * sizeof(WCHAR));
             if (dwError != ERROR_SUCCESS)
                 goto done;
         }
@@ -4490,7 +4848,9 @@ DWORD RChangeServiceConfig2W(
     }
 
 done:
-    /* FIXME: Unlock database */
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
     if (hServiceKey != NULL)
         RegCloseKey(hServiceKey);
 
@@ -4512,8 +4872,11 @@ DWORD RQueryServiceConfig2A(
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService = NULL;
     HKEY hServiceKey = NULL;
+    DWORD dwRequiredSize = 0;
+    DWORD dwType = 0;
     LPWSTR lpDescriptionW = NULL;
-    LPSTR lpDescription = NULL;
+    LPWSTR lpRebootMessageW = NULL;
+    LPWSTR lpFailureCommandW = NULL;
 
     DPRINT("RQueryServiceConfig2A() called hService %p dwInfoLevel %u, lpBuffer %p cbBufSize %u pcbBytesNeeded %p\n",
            hService, dwInfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded);
@@ -4545,7 +4908,8 @@ DWORD RQueryServiceConfig2A(
         return ERROR_INVALID_HANDLE;
     }
 
-    /* FIXME: Lock the service database shared */
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
 
     dwError = ScmOpenServiceKey(lpService->lpServiceName,
                                 KEY_READ,
@@ -4558,62 +4922,169 @@ DWORD RQueryServiceConfig2A(
         LPSERVICE_DESCRIPTIONA lpServiceDescription = (LPSERVICE_DESCRIPTIONA)lpBuffer;
         LPSTR lpStr;
 
-        *pcbBytesNeeded = sizeof(SERVICE_DESCRIPTIONA);
-
         dwError = ScmReadString(hServiceKey,
                                 L"Description",
                                 &lpDescriptionW);
+        if (dwError != ERROR_SUCCESS && dwError != ERROR_FILE_NOT_FOUND)
+            goto done;
+
+        *pcbBytesNeeded = sizeof(SERVICE_DESCRIPTIONA);
         if (dwError == ERROR_SUCCESS)
-        {
             *pcbBytesNeeded += ((wcslen(lpDescriptionW) + 1) * sizeof(WCHAR));
-        }
 
-        if (cbBufSize >= *pcbBytesNeeded)
+        if (cbBufSize < *pcbBytesNeeded)
         {
+            dwError = ERROR_INSUFFICIENT_BUFFER;
+            goto done;
+        }
 
-            if (dwError == ERROR_SUCCESS)
-            {
-                lpStr = (LPSTR)(lpServiceDescription + 1);
+        if (dwError == ERROR_SUCCESS)
+        {
+            lpStr = (LPSTR)(lpServiceDescription + 1);
 
-                WideCharToMultiByte(CP_ACP,
-                                    0,
-                                    lpDescriptionW,
-                                    -1,
-                                    lpStr,
-                                    wcslen(lpDescriptionW),
-                                    NULL,
-                                    NULL);
-                lpServiceDescription->lpDescription = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceDescription);
-            }
-            else
-            {
-                lpServiceDescription->lpDescription = NULL;
-                goto done;
-            }
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpDescriptionW,
+                                -1,
+                                lpStr,
+                                wcslen(lpDescriptionW),
+                                NULL,
+                                NULL);
+            lpServiceDescription->lpDescription = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceDescription);
         }
         else
         {
-            dwError = ERROR_INSUFFICIENT_BUFFER;
-            goto done;
+            lpServiceDescription->lpDescription = NULL;
+            dwError = ERROR_SUCCESS;
         }
     }
-    else if (dwInfoLevel & SERVICE_CONFIG_FAILURE_ACTIONS)
+    else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
     {
-        UNIMPLEMENTED;
-        dwError = ERROR_CALL_NOT_IMPLEMENTED;
-        goto done;
+        LPSERVICE_FAILURE_ACTIONSA lpFailureActions = (LPSERVICE_FAILURE_ACTIONSA)lpBuffer;
+        LPSTR lpStr;
+
+        /* Query value length */
+        dwRequiredSize = 0;
+        dwError = RegQueryValueExW(hServiceKey,
+                                   L"FailureActions",
+                                   NULL,
+                                   &dwType,
+                                   NULL,
+                                   &dwRequiredSize);
+        if (dwError != ERROR_SUCCESS && dwError != ERROR_MORE_DATA)
+            goto done;
+
+        if (dwType != REG_BINARY)
+        {
+            dwError = ERROR_UNSUPPORTED_TYPE;
+            goto done;
+        }
+
+        dwRequiredSize = max(sizeof(SERVICE_FAILURE_ACTIONSA), dwRequiredSize);
+
+        dwError = ScmReadString(hServiceKey,
+                                L"FailureCommand",
+                                &lpFailureCommandW);
+
+        dwError = ScmReadString(hServiceKey,
+                                L"RebootMessage",
+                                &lpRebootMessageW);
+
+        if (lpRebootMessageW)
+            dwRequiredSize += (wcslen(lpRebootMessageW) + 1) * sizeof(WCHAR);
+
+        if (lpFailureCommandW)
+            dwRequiredSize += (wcslen(lpFailureCommandW) + 1) * sizeof(WCHAR);
+
+        if (cbBufSize < dwRequiredSize)
+        {
+            *pcbBytesNeeded = dwRequiredSize;
+            dwError = ERROR_INSUFFICIENT_BUFFER;
+            goto done;
+        }
+
+        /* Now we can fill the buffer */
+        dwError = RegQueryValueExW(hServiceKey,
+                                   L"FailureActions",
+                                   NULL,
+                                   NULL,
+                                   (LPBYTE)lpFailureActions,
+                                   &dwRequiredSize);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
+
+        if ((dwRequiredSize < sizeof(SERVICE_FAILURE_ACTIONSA)) ||
+            (dwRequiredSize > cbBufSize))
+        {
+            dwError = ERROR_BUFFER_OVERFLOW;
+            goto done;
+        }
+
+        if (dwError == ERROR_SUCCESS)
+        {
+            lpFailureActions->cActions = min(lpFailureActions->cActions, (dwRequiredSize - FIELD_OFFSET(SERVICE_FAILURE_ACTIONSA, lpsaActions)) / sizeof(SC_ACTION));
+            lpFailureActions->lpsaActions = (lpFailureActions->cActions > 0) ? (LPSC_ACTION)(lpFailureActions + 1) : NULL;
+            lpStr = (LPSTR)((ULONG_PTR)(lpFailureActions + 1) + lpFailureActions->cActions * sizeof(SC_ACTION));
+        }
+        else
+        {
+            lpFailureActions->dwResetPeriod = 0;
+            lpFailureActions->cActions = 0;
+            lpFailureActions->lpsaActions = NULL;
+            lpStr = (LPSTR)(lpFailureActions + 1);
+        }
+
+        lpFailureActions->lpRebootMsg = NULL;
+        lpFailureActions->lpCommand = NULL;
+
+        if (lpRebootMessageW)
+        {
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpRebootMessageW,
+                                -1,
+                                lpStr,
+                                wcslen(lpRebootMessageW),
+                                NULL,
+                                NULL);
+            lpFailureActions->lpRebootMsg = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
+            lpStr += strlen(lpStr) + 1;
+        }
+
+        if (lpFailureCommandW)
+        {
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpFailureCommandW,
+                                -1,
+                                lpStr,
+                                wcslen(lpFailureCommandW),
+                                NULL,
+                                NULL);
+            lpFailureActions->lpCommand = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
+            /* lpStr += strlen(lpStr) + 1; */
+        }
+
+        dwError = ERROR_SUCCESS;
     }
 
 done:
-    if (lpDescription != NULL)
-        HeapFree(GetProcessHeap(), 0, lpDescription);
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
+    if (lpDescriptionW != NULL)
+        HeapFree(GetProcessHeap(), 0, lpDescriptionW);
+
+    if (lpRebootMessageW != NULL)
+        HeapFree(GetProcessHeap(), 0, lpRebootMessageW);
+
+    if (lpFailureCommandW != NULL)
+        HeapFree(GetProcessHeap(), 0, lpFailureCommandW);
 
     if (hServiceKey != NULL)
         RegCloseKey(hServiceKey);
 
-    /* FIXME: Unlock database */
-
-    DPRINT("RQueryServiceConfig2W() done (Error %lu)\n", dwError);
+    DPRINT("RQueryServiceConfig2A() done (Error %lu)\n", dwError);
 
     return dwError;
 }
@@ -4631,10 +5102,11 @@ DWORD RQueryServiceConfig2W(
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService = NULL;
     HKEY hServiceKey = NULL;
-    DWORD dwRequiredSize;
+    DWORD dwRequiredSize = 0;
+    DWORD dwType = 0;
     LPWSTR lpDescription = NULL;
-    LPWSTR lpFailureCommand = NULL;
     LPWSTR lpRebootMessage = NULL;
+    LPWSTR lpFailureCommand = NULL;
 
     DPRINT("RQueryServiceConfig2W() called\n");
 
@@ -4665,7 +5137,8 @@ DWORD RQueryServiceConfig2W(
         return ERROR_INVALID_HANDLE;
     }
 
-    /* FIXME: Lock the service database shared */
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
 
     dwError = ScmOpenServiceKey(lpService->lpServiceName,
                                 KEY_READ,
@@ -4681,28 +5154,54 @@ DWORD RQueryServiceConfig2W(
         dwError = ScmReadString(hServiceKey,
                                 L"Description",
                                 &lpDescription);
-        if (dwError != ERROR_SUCCESS)
+        if (dwError != ERROR_SUCCESS && dwError != ERROR_FILE_NOT_FOUND)
             goto done;
 
-        dwRequiredSize = sizeof(SERVICE_DESCRIPTIONW) + ((wcslen(lpDescription) + 1) * sizeof(WCHAR));
+        *pcbBytesNeeded = sizeof(SERVICE_DESCRIPTIONW);
+        if (dwError == ERROR_SUCCESS)
+            *pcbBytesNeeded += ((wcslen(lpDescription) + 1) * sizeof(WCHAR));
 
-        if (cbBufSize < dwRequiredSize)
+        if (cbBufSize < *pcbBytesNeeded)
         {
-            *pcbBytesNeeded = dwRequiredSize;
             dwError = ERROR_INSUFFICIENT_BUFFER;
             goto done;
         }
 
-        lpStr = (LPWSTR)(lpServiceDescription + 1);
-        wcscpy(lpStr, lpDescription);
-        lpServiceDescription->lpDescription = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceDescription);
+        if (dwError == ERROR_SUCCESS)
+        {
+            lpStr = (LPWSTR)(lpServiceDescription + 1);
+            wcscpy(lpStr, lpDescription);
+            lpServiceDescription->lpDescription = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceDescription);
+        }
+        else
+        {
+            lpServiceDescription->lpDescription = NULL;
+            dwError = ERROR_SUCCESS;
+        }
     }
     else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
     {
-        LPWSTR lpStr;
         LPSERVICE_FAILURE_ACTIONSW lpFailureActions = (LPSERVICE_FAILURE_ACTIONSW)lpBuffer;
+        LPWSTR lpStr;
 
-        UNIMPLEMENTED;
+        /* Query value length */
+        dwRequiredSize = 0;
+        dwError = RegQueryValueExW(hServiceKey,
+                                   L"FailureActions",
+                                   NULL,
+                                   &dwType,
+                                   NULL,
+                                   &dwRequiredSize);
+        if (dwError != ERROR_SUCCESS && dwError != ERROR_MORE_DATA)
+            goto done;
+
+        if (dwType != REG_BINARY)
+        {
+            dwError = ERROR_UNSUPPORTED_TYPE;
+            goto done;
+        }
+
+        dwRequiredSize = max(sizeof(SERVICE_FAILURE_ACTIONSW), dwRequiredSize);
 
         dwError = ScmReadString(hServiceKey,
                                 L"FailureCommand",
@@ -4712,14 +5211,12 @@ DWORD RQueryServiceConfig2W(
                                 L"RebootMessage",
                                 &lpRebootMessage);
 
-        dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW);
+        if (lpRebootMessage)
+            dwRequiredSize += (wcslen(lpRebootMessage) + 1) * sizeof(WCHAR);
 
         if (lpFailureCommand)
             dwRequiredSize += (wcslen(lpFailureCommand) + 1) * sizeof(WCHAR);
 
-        if (lpRebootMessage)
-            dwRequiredSize += (wcslen(lpRebootMessage) + 1) * sizeof(WCHAR);
-
         if (cbBufSize < dwRequiredSize)
         {
             *pcbBytesNeeded = dwRequiredSize;
@@ -4727,31 +5224,61 @@ DWORD RQueryServiceConfig2W(
             goto done;
         }
 
-        lpFailureActions->cActions = 0; 
-        lpFailureActions->dwResetPeriod = 0;
-        lpFailureActions->lpCommand = NULL;
+        /* Now we can fill the buffer */
+        dwError = RegQueryValueExW(hServiceKey,
+                                   L"FailureActions",
+                                   NULL,
+                                   NULL,
+                                   (LPBYTE)lpFailureActions,
+                                   &dwRequiredSize);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
+
+        if ((dwRequiredSize < sizeof(SERVICE_FAILURE_ACTIONSW)) ||
+            (dwRequiredSize > cbBufSize))
+        {
+            dwError = ERROR_BUFFER_OVERFLOW;
+            goto done;
+        }
+
+        if (dwError == ERROR_SUCCESS)
+        {
+            lpFailureActions->cActions = min(lpFailureActions->cActions, (dwRequiredSize - FIELD_OFFSET(SERVICE_FAILURE_ACTIONSW, lpsaActions)) / sizeof(SC_ACTION));
+            lpFailureActions->lpsaActions = (lpFailureActions->cActions > 0 ? (LPSC_ACTION)(lpFailureActions + 1) : NULL);
+            lpStr = (LPWSTR)((ULONG_PTR)(lpFailureActions + 1) + lpFailureActions->cActions * sizeof(SC_ACTION));
+        }
+        else
+        {
+            lpFailureActions->dwResetPeriod = 0;
+            lpFailureActions->cActions = 0;
+            lpFailureActions->lpsaActions = NULL;
+            lpStr = (LPWSTR)(lpFailureActions + 1);
+        }
+
         lpFailureActions->lpRebootMsg = NULL;
-        lpFailureActions->lpsaActions = NULL;
+        lpFailureActions->lpCommand   = NULL;
 
-        lpStr = (LPWSTR)(lpFailureActions + 1);
         if (lpRebootMessage)
         {
             wcscpy(lpStr, lpRebootMessage);
-            lpFailureActions->lpRebootMsg = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpRebootMessage);
-            lpStr += wcslen(lpRebootMessage) + 1;
+            lpFailureActions->lpRebootMsg = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
+            lpStr += wcslen(lpStr) + 1;
         }
 
         if (lpFailureCommand)
         {
             wcscpy(lpStr, lpFailureCommand);
-            lpFailureActions->lpCommand = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureCommand);
-            lpStr += wcslen(lpRebootMessage) + 1;
+            lpFailureActions->lpCommand = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
+            /* lpStr += wcslen(lpStr) + 1; */
         }
-        dwError = STATUS_SUCCESS;
-        goto done;
+
+        dwError = ERROR_SUCCESS;
     }
 
 done:
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
     if (lpDescription != NULL)
         HeapFree(GetProcessHeap(), 0, lpDescription);
 
@@ -4764,8 +5291,6 @@ done:
     if (hServiceKey != NULL)
         RegCloseKey(hServiceKey);
 
-    /* FIXME: Unlock database */
-
     DPRINT("RQueryServiceConfig2W() done (Error %lu)\n", dwError);
 
     return dwError;
@@ -4818,6 +5343,9 @@ DWORD RQueryServiceStatusEx(
         return ERROR_INVALID_HANDLE;
     }
 
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
+
     lpStatus = (LPSERVICE_STATUS_PROCESS)lpBuffer;
 
     /* Return service status information */
@@ -4825,9 +5353,12 @@ DWORD RQueryServiceStatusEx(
                   &lpService->Status,
                   sizeof(SERVICE_STATUS));
 
-    lpStatus->dwProcessId = lpService->ProcessId;      /* FIXME */
+    lpStatus->dwProcessId = (lpService->lpImage != NULL) ? lpService->lpImage->dwProcessId : 0; /* FIXME */
     lpStatus->dwServiceFlags = 0;                      /* FIXME */
 
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
     return ERROR_SUCCESS;
 }
 
@@ -4900,7 +5431,7 @@ DWORD REnumServicesStatusExA(
     lpStatusPtrA = (LPENUM_SERVICE_STATUS_PROCESSA)lpBuffer;
     lpStringPtrA = (LPSTR)((ULONG_PTR)lpBuffer +
                   *lpServicesReturned * sizeof(ENUM_SERVICE_STATUS_PROCESSA));
-    lpStringPtrW = (LPWSTR)((ULONG_PTR)lpStatusPtrW + 
+    lpStringPtrW = (LPWSTR)((ULONG_PTR)lpStatusPtrW +
                   *lpServicesReturned * sizeof(ENUM_SERVICE_STATUS_PROCESSW));
 
     for (dwServiceCount = 0; dwServiceCount < *lpServicesReturned; dwServiceCount++)
@@ -5000,13 +5531,16 @@ DWORD REnumServicesStatusExW(
     *pcbBytesNeeded = 0;
     *lpServicesReturned = 0;
 
-    if ((dwServiceType!=SERVICE_DRIVER) && (dwServiceType!=SERVICE_WIN32))
+    if ((dwServiceType == 0) ||
+        ((dwServiceType & ~(SERVICE_DRIVER | SERVICE_WIN32)) != 0))
     {
         DPRINT("Not a valid Service Type!\n");
         return ERROR_INVALID_PARAMETER;
     }
 
-    if ((dwServiceState<SERVICE_ACTIVE) || (dwServiceState>SERVICE_STATE_ALL))
+    if ((dwServiceState != SERVICE_ACTIVE) &&
+        (dwServiceState != SERVICE_INACTIVE) &&
+        (dwServiceState != SERVICE_STATE_ALL))
     {
         DPRINT("Not a valid Service State!\n");
         return ERROR_INVALID_PARAMETER;
@@ -5021,9 +5555,11 @@ DWORD REnumServicesStatusExW(
         return ERROR_ACCESS_DENIED;
     }
 
-    if (lpResumeIndex) dwLastResumeCount = *lpResumeIndex;
+    if (lpResumeIndex)
+        dwLastResumeCount = *lpResumeIndex;
 
-    /* Lock the service list shared */
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
 
     lpService = ScmGetServiceEntryByResumeCount(dwLastResumeCount);
     if (lpService == NULL)
@@ -5063,7 +5599,7 @@ DWORD REnumServicesStatusExW(
             else
             {
                 if ((CurrentService->lpGroup == NULL) ||
-                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
                     continue;
             }
         }
@@ -5118,7 +5654,7 @@ DWORD REnumServicesStatusExW(
             else
             {
                 if ((CurrentService->lpGroup == NULL) ||
-                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
                     continue;
             }
         }
@@ -5178,7 +5714,7 @@ DWORD REnumServicesStatusExW(
             else
             {
                 if ((CurrentService->lpGroup == NULL) ||
-                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
                     continue;
             }
         }
@@ -5205,7 +5741,8 @@ DWORD REnumServicesStatusExW(
             memcpy(&lpStatusPtr->ServiceStatusProcess,
                    &CurrentService->Status,
                    sizeof(SERVICE_STATUS));
-            lpStatusPtr->ServiceStatusProcess.dwProcessId = CurrentService->ProcessId; /* FIXME */
+            lpStatusPtr->ServiceStatusProcess.dwProcessId =
+                (CurrentService->lpImage != NULL) ? CurrentService->lpImage->dwProcessId : 0; /* FIXME */
             lpStatusPtr->ServiceStatusProcess.dwServiceFlags = 0; /* FIXME */
 
             lpStatusPtr++;
@@ -5217,7 +5754,7 @@ DWORD REnumServicesStatusExW(
         }
     }
 
-    if (dwError == 0) 
+    if (dwError == 0)
     {
         *pcbBytesNeeded = 0;
         if (lpResumeIndex)
@@ -5225,7 +5762,8 @@ DWORD REnumServicesStatusExW(
     }
 
 Done:;
-    /* Unlock the service list */
+    /* Unlock the service database */
+    ScmUnlockDatabase();
 
     DPRINT("REnumServicesStatusExW() done (Error %lu)\n", dwError);
 
@@ -5405,11 +5943,15 @@ void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
 
 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE hSCObject)
 {
+    /* Close the handle */
+    RCloseServiceHandle(&hSCObject);
 }
 
 
 void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK Lock)
 {
+    /* Unlock the database */
+    RUnlockServiceDatabase(&Lock);
 }