[MSIEXEC] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / base / system / services / rpcserver.c
index f36fa39..3465d4d 100644 (file)
@@ -12,6 +12,8 @@
 
 #include "services.h"
 
+#include <winnls.h>
+
 #define NDEBUG
 #include <debug.h>
 
@@ -19,6 +21,7 @@
 
 #define MANAGER_TAG 0x72674D68  /* 'hMgr' */
 #define SERVICE_TAG 0x63765368  /* 'hSvc' */
+#define INVALID_TAG 0xAABBCCDD
 
 typedef struct _SCMGR_HANDLE
 {
@@ -301,7 +304,7 @@ ScmAssignNewTag(PSERVICE lpService)
     if (dwError != ERROR_SUCCESS && dwError != ERROR_MORE_DATA)
         goto findFreeTag;
 
-    pdwGroupTags = HeapAlloc(GetProcessHeap(), 0, cbDataSize);
+    pdwGroupTags = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDataSize);
     if (!pdwGroupTags)
     {
         dwError = ERROR_NOT_ENOUGH_MEMORY;
@@ -395,7 +398,8 @@ cleanup:
 DWORD
 ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
 {
-    DWORD ServiceNameLen, BufferSize, ExpandedLen;
+    SIZE_T ServiceNameLen, ExpandedLen;
+    DWORD BufferSize;
     WCHAR Dest;
     WCHAR *Expanded;
     UNICODE_STRING NtPathName, SystemRoot, LinkTarget;
@@ -405,6 +409,11 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
 
     DPRINT("ScmConvertToBootPathName %S\n", CanonName);
 
+    if (!RelativeName)
+        return ERROR_INVALID_PARAMETER;
+
+    *RelativeName = NULL;
+
     ServiceNameLen = wcslen(CanonName);
 
     /* First check, if it's already good */
@@ -478,7 +487,7 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
         return ERROR_NOT_ENOUGH_MEMORY;
     }
 
-    /* Convert to NY-style path */
+    /* Convert to NT-style path */
     if (!RtlDosPathNameToNtPathName_U(Expanded, &NtPathName, NULL, NULL))
     {
         DPRINT("Error during a call to RtlDosPathNameToNtPathName_U()\n");
@@ -496,17 +505,21 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
                          NtPathName.Length + sizeof(UNICODE_NULL));
     if (!Expanded)
     {
-            DPRINT("Error allocating memory for boot driver name!\n");
-            return ERROR_NOT_ENOUGH_MEMORY;
+        DPRINT("Error allocating memory for boot driver name!\n");
+        RtlFreeUnicodeString(&NtPathName);
+        return ERROR_NOT_ENOUGH_MEMORY;
     }
 
     ExpandedLen = NtPathName.Length / sizeof(WCHAR);
     wcsncpy(Expanded, NtPathName.Buffer, ExpandedLen);
     Expanded[ExpandedLen] = UNICODE_NULL;
+    RtlFreeUnicodeString(&NtPathName);
 
     if (ServiceNameLen > ExpandedLen &&
         !_wcsnicmp(Expanded, CanonName, ExpandedLen))
     {
+        HeapFree(GetProcessHeap(), 0, Expanded);
+
         /* Only \SystemRoot\ is missing */
         *RelativeName = HeapAlloc(GetProcessHeap(),
                                   HEAP_ZERO_MEMORY,
@@ -514,17 +527,18 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
         if (*RelativeName == NULL)
         {
             DPRINT("Error allocating memory for boot driver name!\n");
-            HeapFree(GetProcessHeap(), 0, Expanded);
             return ERROR_NOT_ENOUGH_MEMORY;
         }
 
         wcscpy(*RelativeName, L"\\SystemRoot\\");
         wcscat(*RelativeName, CanonName + ExpandedLen);
 
-        RtlFreeUnicodeString(&NtPathName);
         return ERROR_SUCCESS;
     }
 
+    /* No longer need this */
+    HeapFree(GetProcessHeap(), 0, Expanded);
+
     /* The most complex case starts here */
     RtlInitUnicodeString(&SystemRoot, L"\\SystemRoot");
     InitializeObjectAttributes(&ObjectAttributes,
@@ -535,25 +549,20 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
 
     /* 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");
 
+        RtlInitEmptyUnicodeString(&LinkTarget, NULL, 0);
         Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
         if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL)
         {
             /* Check if required buffer size is sane */
-            if (BufferSize > 0xFFFD)
+            if (BufferSize > UNICODE_STRING_MAX_BYTES - sizeof(UNICODE_NULL))
             {
                 DPRINT("Too large buffer required\n");
-                *RelativeName = 0;
 
-                if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-                HeapFree(GetProcessHeap(), 0, Expanded);
+                NtClose(SymbolicLinkHandle);
                 return ERROR_NOT_ENOUGH_MEMORY;
             }
 
@@ -566,13 +575,13 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
             if (!LinkTarget.Buffer)
             {
                 DPRINT("Unable to alloc buffer\n");
-                if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-                HeapFree(GetProcessHeap(), 0, Expanded);
+                NtClose(SymbolicLinkHandle);
                 return ERROR_NOT_ENOUGH_MEMORY;
             }
 
             /* Do a real query now */
             Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
+            NtClose(SymbolicLinkHandle);
             if (NT_SUCCESS(Status))
             {
                 DPRINT("LinkTarget: %wZ\n", &LinkTarget);
@@ -588,9 +597,6 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
                     if (*RelativeName == NULL)
                     {
                         DPRINT("Unable to alloc buffer\n");
-                        if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
-                        HeapFree(GetProcessHeap(), 0, Expanded);
-                        RtlFreeUnicodeString(&NtPathName);
                         return ERROR_NOT_ENOUGH_MEMORY;
                     }
 
@@ -599,50 +605,33 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
                     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);
+            NtClose(SymbolicLinkHandle);
             return ERROR_INVALID_PARAMETER;
         }
     }
     else
     {
+        /* Failure */
         DPRINT("Error, Status = %08X\n", Status);
-        HeapFree(GetProcessHeap(), 0, Expanded);
         return ERROR_INVALID_PARAMETER;
     }
-
-    /* Failure */
-    *RelativeName = NULL;
-    return ERROR_INVALID_PARAMETER;
 }
 
 
@@ -651,7 +640,8 @@ ScmCanonDriverImagePath(DWORD dwStartType,
                         const wchar_t *lpServiceName,
                         wchar_t **lpCanonName)
 {
-    DWORD ServiceNameLen, Result;
+    DWORD Result;
+    SIZE_T ServiceNameLen;
     UNICODE_STRING NtServiceName;
     WCHAR *RelativeName;
     const WCHAR *SourceName = lpServiceName;
@@ -851,7 +841,7 @@ Int_EnumDependentServicesW(HKEY hServicesKey,
         if (dwError != ERROR_SUCCESS)
             return dwError;
 
-        dwSize = MAX_PATH;
+        dwSize = MAX_PATH * sizeof(WCHAR);
 
         /* Check for the DependOnService Value */
         dwError = RegQueryValueExW(hServiceEnumKey,
@@ -894,8 +884,8 @@ Int_EnumDependentServicesW(HKEY hServicesKey,
                     {
                         /* Calculate the required size */
                         dwRequiredSize += sizeof(SERVICE_STATUS);
-                        dwRequiredSize += ((wcslen(lpCurrentService->lpServiceName) + 1) * sizeof(WCHAR));
-                        dwRequiredSize += ((wcslen(lpCurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
+                        dwRequiredSize += (DWORD)((wcslen(lpCurrentService->lpServiceName) + 1) * sizeof(WCHAR));
+                        dwRequiredSize += (DWORD)((wcslen(lpCurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
 
                         /* Add the size for service name and display name pointers */
                         dwRequiredSize += (2 * sizeof(PVOID));
@@ -922,7 +912,7 @@ Int_EnumDependentServicesW(HKEY hServicesKey,
                     }
                 }
 
-                dwDependServiceStrPtr += (wcslen(lpszValueBuf + dwDependServiceStrPtr) + 1);
+                dwDependServiceStrPtr += (DWORD)(wcslen(lpszValueBuf + dwDependServiceStrPtr) + 1);
             }
         }
         else if (*pcbBytesNeeded)
@@ -963,7 +953,8 @@ DWORD RCloseServiceHandle(
     {
         DPRINT("Found manager handle\n");
 
-        /* FIXME: add handle cleanup code */
+        /* Make sure we don't access stale memory if someone tries to use this handle again. */
+        hManager->Handle.Tag = INVALID_TAG;
 
         HeapFree(GetProcessHeap(), 0, hManager);
         hManager = NULL;
@@ -977,13 +968,14 @@ DWORD RCloseServiceHandle(
     {
         DPRINT("Found service handle\n");
 
-        /* Lock the service database exlusively */
+        /* Lock the service database exclusively */
         ScmLockDatabaseExclusive();
 
         /* Get the pointer to the service record */
         lpService = hService->ServiceEntry;
 
-        /* FIXME: add handle cleanup code */
+        /* Make sure we don't access stale memory if someone tries to use this handle again. */
+        hService->Handle.Tag = INVALID_TAG;
 
         /* Free the handle */
         HeapFree(GetProcessHeap(), 0, hService);
@@ -1030,12 +1022,12 @@ DWORD RCloseServiceHandle(
                     return ERROR_SUCCESS;
                 }
 
-                /* There are no references and no runnning dependencies,
+                /* There are no references and no running dependencies,
                    it is now safe to delete the service */
 
                 /* Delete the Service Key */
-                dwError = RegDeleteKeyW(hServicesKey,
-                                        lpService->lpServiceName);
+                dwError = ScmDeleteRegKey(hServicesKey,
+                                          lpService->lpServiceName);
 
                 RegCloseKey(hServicesKey);
 
@@ -1080,6 +1072,9 @@ DWORD RControlService(
     DWORD dwControlsAccepted;
     DWORD dwCurrentState;
     HKEY hServicesKey = NULL;
+    LPCWSTR lpLogStrings[2];
+    WCHAR szLogBuffer[80];
+    UINT uID;
 
     DPRINT("RControlService() called\n");
 
@@ -1114,7 +1109,7 @@ DWORD RControlService(
             DesiredAccess = SERVICE_PAUSE_CONTINUE;
             break;
 
-        case SERVICE_INTERROGATE:
+        case SERVICE_CONTROL_INTERROGATE:
             DesiredAccess = SERVICE_INTERROGATE;
             break;
 
@@ -1237,8 +1232,39 @@ DWORD RControlService(
                       sizeof(SERVICE_STATUS));
     }
 
-    if ((dwError == ERROR_SUCCESS) && (pcbBytesNeeded))
-        dwError = ERROR_DEPENDENT_SERVICES_RUNNING;
+    if (dwError == ERROR_SUCCESS)
+    {
+            if (dwControl == SERVICE_CONTROL_STOP ||
+                dwControl == SERVICE_CONTROL_PAUSE ||
+                dwControl == SERVICE_CONTROL_CONTINUE)
+            {
+                /* Log a successful send control */
+
+                switch (dwControl)
+                {
+                    case SERVICE_CONTROL_STOP:
+                        uID = IDS_SERVICE_STOP;
+                        break;
+
+                    case SERVICE_CONTROL_PAUSE:
+                        uID = IDS_SERVICE_PAUSE;
+                        break;
+
+                    case SERVICE_CONTROL_CONTINUE:
+                        uID = IDS_SERVICE_RESUME;
+                        break;
+                }
+                LoadStringW(GetModuleHandle(NULL), uID, szLogBuffer, 80);
+
+                lpLogStrings[0] = lpService->lpDisplayName;
+                lpLogStrings[1] = szLogBuffer;
+
+                ScmLogEvent(EVENT_SERVICE_CONTROL_SUCCESS,
+                            EVENTLOG_INFORMATION_TYPE,
+                            2,
+                            lpLogStrings);
+            }
+    }
 
     return dwError;
 }
@@ -1290,7 +1316,7 @@ DWORD RDeleteService(
 
     dwError = ScmMarkServiceForDelete(lpService);
 
-Done:;
+Done:
     /* Unlock the service database */
     ScmUnlockDatabase();
 
@@ -1309,7 +1335,7 @@ DWORD RLockServiceDatabase(
 
     DPRINT("RLockServiceDatabase() called\n");
 
-    *lpLock = 0;
+    *lpLock = NULL;
 
     hMgr = ScmGetServiceManagerFromHandle(hSCManager);
     if (hMgr == NULL)
@@ -1322,12 +1348,7 @@ DWORD RLockServiceDatabase(
                                   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;
+    return ScmAcquireServiceStartLock(FALSE, lpLock);
 }
 
 
@@ -1346,9 +1367,6 @@ DWORD RQueryServiceObjectSecurity(
     DWORD dwBytesNeeded;
     DWORD dwError;
 
-
-    SECURITY_DESCRIPTOR ObjectDescriptor;
-
     DPRINT("RQueryServiceObjectSecurity() called\n");
 
     hSvc = ScmGetServiceFromHandle(hService);
@@ -1383,11 +1401,8 @@ DWORD RQueryServiceObjectSecurity(
     /* Lock the service database */
     ScmLockDatabaseShared();
 
-
-    /* hack */
-    Status = RtlCreateSecurityDescriptor(&ObjectDescriptor, SECURITY_DESCRIPTOR_REVISION);
-
-    Status = RtlQuerySecurityObject(&ObjectDescriptor  /* lpService->lpSecurityDescriptor */,
+    /* Retrieve the security descriptor */
+    Status = RtlQuerySecurityObject(lpService->pSecurityDescriptor,
                                     dwSecurityInformation,
                                     (PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
                                     cbBufSize,
@@ -1424,14 +1439,15 @@ DWORD RSetServiceObjectSecurity(
     SC_RPC_HANDLE hService,
     DWORD dwSecurityInformation,
     LPBYTE lpSecurityDescriptor,
-    DWORD dwSecuityDescriptorSize)
+    DWORD dwSecurityDescriptorSize)
 {
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService;
-    ULONG DesiredAccess = 0;
-    /* HANDLE hToken = NULL; */
-    HKEY hServiceKey;
-    /* NTSTATUS Status; */
+    ACCESS_MASK DesiredAccess = 0;
+    HANDLE hToken = NULL;
+    HKEY hServiceKey = NULL;
+    BOOL bDatabaseLocked = FALSE;
+    NTSTATUS Status;
     DWORD dwError;
 
     DPRINT("RSetServiceObjectSecurity() called\n");
@@ -1471,14 +1487,14 @@ DWORD RSetServiceObjectSecurity(
     if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
                                   DesiredAccess))
     {
-        DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
+        DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
         return ERROR_ACCESS_DENIED;
     }
 
     lpService = hSvc->ServiceEntry;
     if (lpService == NULL)
     {
-        DPRINT("lpService == NULL!\n");
+        DPRINT1("lpService == NULL!\n");
         return ERROR_INVALID_HANDLE;
     }
 
@@ -1498,13 +1514,10 @@ DWORD RSetServiceObjectSecurity(
     RpcRevertToSelf();
 #endif
 
-    /* Lock the service database exclusive */
-    ScmLockDatabaseExclusive();
-
-#if 0
+    /* Build the new security descriptor */
     Status = RtlSetSecurityObject(dwSecurityInformation,
                                   (PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
-                                  &lpService->lpSecurityDescriptor,
+                                  &lpService->pSecurityDescriptor,
                                   &ScmServiceMapping,
                                   hToken);
     if (!NT_SUCCESS(Status))
@@ -1512,31 +1525,34 @@ DWORD RSetServiceObjectSecurity(
         dwError = RtlNtStatusToDosError(Status);
         goto Done;
     }
-#endif
 
+    /* Lock the service database exclusive */
+    ScmLockDatabaseExclusive();
+    bDatabaseLocked = TRUE;
+
+    /* Open the service key */
     dwError = ScmOpenServiceKey(lpService->lpServiceName,
                                 READ_CONTROL | KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
                                 &hServiceKey);
     if (dwError != ERROR_SUCCESS)
         goto Done;
 
-    UNIMPLEMENTED;
-    dwError = ERROR_SUCCESS;
-//    dwError = ScmWriteSecurityDescriptor(hServiceKey,
-//                                         lpService->lpSecurityDescriptor);
+    /* Store the new security descriptor */
+    dwError = ScmWriteSecurityDescriptor(hServiceKey,
+                                         lpService->pSecurityDescriptor);
 
     RegFlushKey(hServiceKey);
-    RegCloseKey(hServiceKey);
 
 Done:
+    if (hServiceKey != NULL)
+        RegCloseKey(hServiceKey);
+
+    /* Unlock service database */
+    if (bDatabaseLocked == TRUE)
+        ScmUnlockDatabase();
 
-#if 0
     if (hToken != NULL)
         NtClose(hToken);
-#endif
-
-    /* Unlock service database */
-    ScmUnlockDatabase();
 
     DPRINT("RSetServiceObjectSecurity() done (Error %lu)\n", dwError);
 
@@ -1619,10 +1635,15 @@ DWORD RSetServiceStatus(
     LPSERVICE_STATUS lpServiceStatus)
 {
     PSERVICE lpService;
+    DWORD dwPreviousState;
+    DWORD dwPreviousType;
+    LPCWSTR lpLogStrings[2];
+    WCHAR szLogBuffer[80];
+    UINT uID;
 
     DPRINT("RSetServiceStatus() called\n");
-    DPRINT("hServiceStatus = %p\n", hServiceStatus);
-    DPRINT("dwServiceType = %lu\n", lpServiceStatus->dwServiceType);
+    DPRINT("hServiceStatus = %lu\n", hServiceStatus);
+    DPRINT("dwServiceType = 0x%lx\n", lpServiceStatus->dwServiceType);
     DPRINT("dwCurrentState = %lu\n", lpServiceStatus->dwCurrentState);
     DPRINT("dwControlsAccepted = %lu\n", lpServiceStatus->dwControlsAccepted);
     DPRINT("dwWin32ExitCode = %lu\n", lpServiceStatus->dwWin32ExitCode);
@@ -1637,11 +1658,6 @@ DWORD RSetServiceStatus(
     }
 
     lpService = (PSERVICE)hServiceStatus;
-    if (lpService == NULL)
-    {
-        DPRINT("lpService == NULL!\n");
-        return ERROR_INVALID_HANDLE;
-    }
 
     /* Check current state */
     if (!ScmIsValidServiceState(lpServiceStatus->dwCurrentState))
@@ -1665,16 +1681,81 @@ DWORD RSetServiceStatus(
         return ERROR_INVALID_DATA;
     }
 
+    /* Set the wait hint and check point only if the service is in a pending state,
+       otherwise they should be 0 */
+    if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED ||
+        lpServiceStatus->dwCurrentState == SERVICE_PAUSED ||
+        lpServiceStatus->dwCurrentState == SERVICE_RUNNING)
+    {
+        lpServiceStatus->dwWaitHint = 0;
+        lpServiceStatus->dwCheckPoint = 0;
+    }
+
     /* Lock the service database exclusively */
     ScmLockDatabaseExclusive();
 
+    /* Save the current service state */
+    dwPreviousState = lpService->Status.dwCurrentState;
+
+    /* Save the current service type */
+    dwPreviousType = lpService->Status.dwServiceType;
+
+    /* Update the service status */
     RtlCopyMemory(&lpService->Status,
                   lpServiceStatus,
                   sizeof(SERVICE_STATUS));
 
+    /* Restore the previous service type */
+    lpService->Status.dwServiceType = dwPreviousType;
+
     /* Unlock the service database */
     ScmUnlockDatabase();
 
+    if ((lpServiceStatus->dwCurrentState == SERVICE_STOPPED) &&
+        (dwPreviousState != SERVICE_STOPPED) &&
+        (lpServiceStatus->dwWin32ExitCode != ERROR_SUCCESS))
+    {
+        /* Log a failed service stop */
+        swprintf(szLogBuffer, L"%lu", lpServiceStatus->dwWin32ExitCode);
+        lpLogStrings[0] = lpService->lpDisplayName;
+        lpLogStrings[1] = szLogBuffer;
+
+        ScmLogEvent(EVENT_SERVICE_EXIT_FAILED,
+                    EVENTLOG_ERROR_TYPE,
+                    2,
+                    lpLogStrings);
+    }
+    else if (lpServiceStatus->dwCurrentState != dwPreviousState &&
+             (lpServiceStatus->dwCurrentState == SERVICE_STOPPED ||
+              lpServiceStatus->dwCurrentState == SERVICE_RUNNING ||
+              lpServiceStatus->dwCurrentState == SERVICE_PAUSED))
+    {
+        /* Log a successful service status change */
+        switch(lpServiceStatus->dwCurrentState)
+        {
+            case SERVICE_STOPPED:
+                uID = IDS_SERVICE_STOPPED;
+                break;
+
+            case SERVICE_RUNNING:
+                uID = IDS_SERVICE_RUNNING;
+                break;
+
+            case SERVICE_PAUSED:
+                uID = IDS_SERVICE_PAUSED;
+                break;
+        }
+
+        LoadStringW(GetModuleHandle(NULL), uID, szLogBuffer, 80);
+        lpLogStrings[0] = lpService->lpDisplayName;
+        lpLogStrings[1] = szLogBuffer;
+
+        ScmLogEvent(EVENT_SERVICE_STATUS_SUCCESS,
+                    EVENTLOG_INFORMATION_TYPE,
+                    2,
+                    lpLogStrings);
+    }
+
     DPRINT("Set %S to %lu\n", lpService->lpDisplayName, lpService->Status.dwCurrentState);
     DPRINT("RSetServiceStatus() done\n");
 
@@ -1686,8 +1767,8 @@ DWORD RSetServiceStatus(
 DWORD RUnlockServiceDatabase(
     LPSC_RPC_LOCK Lock)
 {
-    UNIMPLEMENTED;
-    return ERROR_SUCCESS;
+    DPRINT("RUnlockServiceDatabase(%p)\n", Lock);
+    return ScmReleaseServiceStartLock(Lock);
 }
 
 
@@ -1741,7 +1822,7 @@ DWORD RChangeServiceConfigW(
     LPWSTR lpImagePathW = NULL;
 
     DPRINT("RChangeServiceConfigW() called\n");
-    DPRINT("dwServiceType = %lu\n", dwServiceType);
+    DPRINT("dwServiceType = 0x%lx\n", dwServiceType);
     DPRINT("dwStartType = %lu\n", dwStartType);
     DPRINT("dwErrorControl = %lu\n", dwErrorControl);
     DPRINT("lpBinaryPathName = %S\n", lpBinaryPathName);
@@ -1765,6 +1846,40 @@ DWORD RChangeServiceConfigW(
         return ERROR_ACCESS_DENIED;
     }
 
+    /* Check for invalid service type value */
+    if ((dwServiceType != SERVICE_NO_CHANGE) &&
+        (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_NO_CHANGE) &&
+        (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;
+    }
+
+    /* Check for invalid error control value */
+    if ((dwErrorControl != SERVICE_NO_CHANGE) &&
+        (dwErrorControl != SERVICE_ERROR_IGNORE) &&
+        (dwErrorControl != SERVICE_ERROR_NORMAL) &&
+        (dwErrorControl != SERVICE_ERROR_SEVERE) &&
+        (dwErrorControl != SERVICE_ERROR_CRITICAL))
+        return ERROR_INVALID_PARAMETER;
+
     lpService = hSvc->ServiceEntry;
     if (lpService == NULL)
     {
@@ -1798,11 +1913,11 @@ DWORD RChangeServiceConfigW(
                        0,
                        REG_SZ,
                        (LPBYTE)lpDisplayName,
-                       (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
+                       (DWORD)((wcslen(lpDisplayName) + 1) * sizeof(WCHAR)));
 
         /* Update the display name */
         lpDisplayNameW = HeapAlloc(GetProcessHeap(),
-                                   0,
+                                   HEAP_ZERO_MEMORY,
                                    (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
         if (lpDisplayNameW == NULL)
         {
@@ -1810,6 +1925,7 @@ DWORD RChangeServiceConfigW(
             goto done;
         }
 
+        wcscpy(lpDisplayNameW, lpDisplayName);
         if (lpService->lpDisplayName != lpService->lpServiceName)
             HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
 
@@ -1881,7 +1997,7 @@ DWORD RChangeServiceConfigW(
                                  0,
                                  REG_EXPAND_SZ,
                                  (LPBYTE)lpImagePathW,
-                                 (wcslen(lpImagePathW) + 1) * sizeof(WCHAR));
+                                 (DWORD)((wcslen(lpImagePathW) + 1) * sizeof(WCHAR)));
 
         if (lpImagePathW != lpBinaryPathName)
             HeapFree(GetProcessHeap(), 0, lpImagePathW);
@@ -1898,7 +2014,7 @@ DWORD RChangeServiceConfigW(
                                  0,
                                  REG_SZ,
                                  (LPBYTE)lpLoadOrderGroup,
-                                 (wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR));
+                                 (DWORD)((wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR)));
         if (dwError != ERROR_SUCCESS)
             goto done;
 
@@ -1908,6 +2024,7 @@ DWORD RChangeServiceConfigW(
             goto done;
     }
 
+    /* Set the tag */
     if (lpdwTagId != NULL)
     {
         dwError = ScmAssignNewTag(lpService);
@@ -1938,7 +2055,27 @@ DWORD RChangeServiceConfigW(
 
     if (lpPassword != NULL)
     {
-        /* FIXME: Write password */
+        if (wcslen((LPWSTR)lpPassword) != 0)
+        {
+            /* FIXME: Decrypt the password */
+
+            /* Write the password */
+            dwError = ScmSetServicePassword(lpService->szServiceName,
+                                            (LPCWSTR)lpPassword);
+            if (dwError != ERROR_SUCCESS)
+                goto done;
+        }
+        else
+        {
+            /* Delete the password */
+            dwError = ScmSetServicePassword(lpService->szServiceName,
+                                            NULL);
+            if (dwError == ERROR_FILE_NOT_FOUND)
+                dwError = ERROR_SUCCESS;
+
+            if (dwError != ERROR_SUCCESS)
+                goto done;
+        }
     }
 
 done:
@@ -1985,7 +2122,7 @@ DWORD RCreateServiceW(
     DPRINT("lpServiceName = %S\n", lpServiceName);
     DPRINT("lpDisplayName = %S\n", lpDisplayName);
     DPRINT("dwDesiredAccess = %lx\n", dwDesiredAccess);
-    DPRINT("dwServiceType = %lu\n", dwServiceType);
+    DPRINT("dwServiceType = 0x%lx\n", dwServiceType);
     DPRINT("dwStartType = %lu\n", dwStartType);
     DPRINT("dwErrorControl = %lu\n", dwErrorControl);
     DPRINT("lpBinaryPathName = %S\n", lpBinaryPathName);
@@ -2055,7 +2192,11 @@ DWORD RCreateServiceW(
     if ((dwServiceType == (SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS)) &&
         (lpServiceStartName))
     {
-        return ERROR_INVALID_PARAMETER;
+        /* We allow LocalSystem to run interactive. */
+        if (wcsicmp(lpServiceStartName, L"LocalSystem"))
+        {
+            return ERROR_INVALID_PARAMETER;
+        }
     }
 
     if (lpdwTagId && (!lpLoadOrderGroup || !*lpLoadOrderGroup))
@@ -2125,7 +2266,8 @@ DWORD RCreateServiceW(
         *lpDisplayName != 0 &&
         _wcsicmp(lpService->lpDisplayName, lpDisplayName) != 0)
     {
-        lpService->lpDisplayName = HeapAlloc(GetProcessHeap(), 0,
+        lpService->lpDisplayName = HeapAlloc(GetProcessHeap(),
+                                             HEAP_ZERO_MEMORY,
                                              (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
         if (lpService->lpDisplayName == NULL)
         {
@@ -2152,6 +2294,14 @@ DWORD RCreateServiceW(
             goto done;
     }
 
+    /* Assign the default security descriptor */
+    if (dwServiceType & SERVICE_WIN32)
+    {
+        dwError = ScmCreateDefaultServiceSD(&lpService->pSecurityDescriptor);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
+    }
+
     /* Write service data to the registry */
     /* Create the service key */
     dwError = ScmCreateServiceKey(lpServiceName,
@@ -2168,7 +2318,7 @@ DWORD RCreateServiceW(
                        0,
                        REG_SZ,
                        (LPBYTE)lpDisplayName,
-                       (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
+                       (DWORD)((wcslen(lpDisplayName) + 1) * sizeof(WCHAR)));
     }
 
     /* Set the service type */
@@ -2209,7 +2359,7 @@ DWORD RCreateServiceW(
                                  0,
                                  REG_EXPAND_SZ,
                                  (LPBYTE)lpBinaryPathName,
-                                 (wcslen(lpBinaryPathName) + 1) * sizeof(WCHAR));
+                                 (DWORD)((wcslen(lpBinaryPathName) + 1) * sizeof(WCHAR)));
         if (dwError != ERROR_SUCCESS)
             goto done;
     }
@@ -2220,7 +2370,7 @@ DWORD RCreateServiceW(
                                  0,
                                  REG_EXPAND_SZ,
                                  (LPBYTE)lpImagePath,
-                                 (wcslen(lpImagePath) + 1) * sizeof(WCHAR));
+                                 (DWORD)((wcslen(lpImagePath) + 1) * sizeof(WCHAR)));
         if (dwError != ERROR_SUCCESS)
             goto done;
     }
@@ -2233,11 +2383,12 @@ DWORD RCreateServiceW(
                                  0,
                                  REG_SZ,
                                  (LPBYTE)lpLoadOrderGroup,
-                                 (wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR));
+                                 (DWORD)((wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR)));
         if (dwError != ERROR_SUCCESS)
             goto done;
     }
 
+    /* Set the service tag */
     if (lpdwTagId != NULL)
     {
         dwError = RegSetValueExW(hServiceKey,
@@ -2260,23 +2411,37 @@ DWORD RCreateServiceW(
             goto done;
     }
 
-    /* Write service start name */
+    /* Start name and password are only used by Win32 services */
     if (dwServiceType & SERVICE_WIN32)
     {
+        /* Write service start name */
         lpObjectName = (lpServiceStartName != NULL) ? (LPWSTR)lpServiceStartName : L"LocalSystem";
         dwError = RegSetValueExW(hServiceKey,
                                  L"ObjectName",
                                  0,
                                  REG_SZ,
                                  (LPBYTE)lpObjectName,
-                                 (wcslen(lpObjectName) + 1) * sizeof(WCHAR));
+                                 (DWORD)((wcslen(lpObjectName) + 1) * sizeof(WCHAR)));
         if (dwError != ERROR_SUCCESS)
             goto done;
-    }
 
-    if (lpPassword != NULL)
-    {
-        /* FIXME: Write password */
+        if (lpPassword != NULL && wcslen((LPWSTR)lpPassword) != 0)
+        {
+            /* FIXME: Decrypt the password */
+
+            /* Write the password */
+            dwError = ScmSetServicePassword(lpServiceName,
+                                            (LPCWSTR)lpPassword);
+            if (dwError != ERROR_SUCCESS)
+                goto done;
+        }
+
+DPRINT1("\n");
+        /* Write the security descriptor */
+        dwError = ScmWriteSecurityDescriptor(hServiceKey,
+                                             lpService->pSecurityDescriptor);
+        if (dwError != ERROR_SUCCESS)
+            goto done;
     }
 
     dwError = ScmCreateServiceHandle(lpService,
@@ -2292,7 +2457,7 @@ DWORD RCreateServiceW(
     lpService->dwRefCount = 1;
     DPRINT("CreateService - lpService->dwRefCount %u\n", lpService->dwRefCount);
 
-done:;
+done:
     /* Unlock the service database */
     ScmUnlockDatabase();
 
@@ -2407,7 +2572,7 @@ DWORD REnumDependentServicesW(
 
     /* Allocate memory for array of service pointers */
     lpServicesArray = HeapAlloc(GetProcessHeap(),
-                                0,
+                                HEAP_ZERO_MEMORY,
                                 (dwServicesReturned + 1) * sizeof(PSERVICE));
     if (!lpServicesArray)
     {
@@ -2430,7 +2595,7 @@ DWORD REnumDependentServicesW(
         goto Done;
     }
 
-    lpServicesPtr = (LPENUM_SERVICE_STATUSW) lpServices;
+    lpServicesPtr = (LPENUM_SERVICE_STATUSW)lpServices;
     lpStr = (LPWSTR)(lpServices + (dwServicesReturned * sizeof(ENUM_SERVICE_STATUSW)));
 
     /* Copy EnumDepenedentService to Buffer */
@@ -2453,7 +2618,7 @@ DWORD REnumDependentServicesW(
         lpServicesPtr->lpServiceName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServices);
         lpStr += (wcslen(lpService->lpServiceName) + 1);
 
-        lpServicesPtr ++;
+        lpServicesPtr++;
     }
 
     *lpServicesReturned = dwServicesReturned;
@@ -2481,258 +2646,67 @@ 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");
+    /* Enumerate all the services, not regarding of their group */
+    return REnumServiceGroupW(hSCManager,
+                              dwServiceType,
+                              dwServiceState,
+                              lpBuffer,
+                              dwBufSize,
+                              pcbBytesNeeded,
+                              lpServicesReturned,
+                              lpResumeHandle,
+                              NULL);
+}
 
-    if (ScmShutdown)
-        return ERROR_SHUTDOWN_IN_PROGRESS;
 
-    hManager = ScmGetServiceManagerFromHandle(hSCManager);
-    if (hManager == NULL)
-    {
-        DPRINT1("Invalid service manager handle!\n");
-        return ERROR_INVALID_HANDLE;
-    }
+/* Function 15 */
+DWORD ROpenSCManagerW(
+    LPWSTR lpMachineName,
+    LPWSTR lpDatabaseName,
+    DWORD dwDesiredAccess,
+    LPSC_RPC_HANDLE lpScHandle)
+{
+    DWORD dwError;
+    SC_HANDLE hHandle;
 
+    DPRINT("ROpenSCManagerW() called\n");
+    DPRINT("lpMachineName = %p\n", lpMachineName);
+    DPRINT("lpMachineName: %S\n", lpMachineName);
+    DPRINT("lpDataBaseName = %p\n", lpDatabaseName);
+    DPRINT("lpDataBaseName: %S\n", lpDatabaseName);
+    DPRINT("dwDesiredAccess = %x\n", dwDesiredAccess);
 
-    *pcbBytesNeeded = 0;
-    *lpServicesReturned = 0;
+    if (ScmShutdown)
+        return ERROR_SHUTDOWN_IN_PROGRESS;
 
-    if ((dwServiceType == 0) ||
-        ((dwServiceType & ~(SERVICE_DRIVER | SERVICE_WIN32)) != 0))
-    {
-        DPRINT("Not a valid Service Type!\n");
+    if (!lpScHandle)
         return ERROR_INVALID_PARAMETER;
-    }
 
-    if ((dwServiceState != SERVICE_ACTIVE) &&
-        (dwServiceState != SERVICE_INACTIVE) &&
-        (dwServiceState != SERVICE_STATE_ALL))
+    dwError = ScmCreateManagerHandle(lpDatabaseName,
+                                     &hHandle);
+    if (dwError != ERROR_SUCCESS)
     {
-        DPRINT("Not a valid Service State!\n");
-        return ERROR_INVALID_PARAMETER;
+        DPRINT("ScmCreateManagerHandle() failed (Error %lu)\n", dwError);
+        return dwError;
     }
 
-    /* Check access rights */
-    if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
-                                  SC_MANAGER_ENUMERATE_SERVICE))
+    /* Check the desired access */
+    dwError = ScmCheckAccess(hHandle,
+                             dwDesiredAccess | SC_MANAGER_CONNECT);
+    if (dwError != ERROR_SUCCESS)
     {
-        DPRINT("Insufficient access rights! 0x%lx\n",
-                hManager->Handle.DesiredAccess);
-        return ERROR_ACCESS_DENIED;
+        DPRINT("ScmCheckAccess() failed (Error %lu)\n", dwError);
+        HeapFree(GetProcessHeap(), 0, hHandle);
+        return dwError;
     }
 
-    if (lpResumeHandle)
-        dwLastResumeCount = *lpResumeHandle;
+    *lpScHandle = (SC_RPC_HANDLE)hHandle;
+    DPRINT("*hScm = %p\n", *lpScHandle);
 
-    /* Lock the service database shared */
-    ScmLockDatabaseShared();
+    DPRINT("ROpenSCManagerW() done\n");
 
-    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 == ERROR_SUCCESS)
-    {
-        *pcbBytesNeeded = 0;
-        if (lpResumeHandle) *lpResumeHandle = 0;
-    }
-
-Done:;
-    /* Unlock the service database */
-    ScmUnlockDatabase();
-
-    DPRINT("REnumServicesStatusW() done (Error %lu)\n", dwError);
-
-    return dwError;
-}
-
-
-/* Function 15 */
-DWORD ROpenSCManagerW(
-    LPWSTR lpMachineName,
-    LPWSTR lpDatabaseName,
-    DWORD dwDesiredAccess,
-    LPSC_RPC_HANDLE lpScHandle)
-{
-    DWORD dwError;
-    SC_HANDLE hHandle;
-
-    DPRINT("ROpenSCManagerW() called\n");
-    DPRINT("lpMachineName = %p\n", lpMachineName);
-    DPRINT("lpMachineName: %S\n", lpMachineName);
-    DPRINT("lpDataBaseName = %p\n", lpDatabaseName);
-    DPRINT("lpDataBaseName: %S\n", lpDatabaseName);
-    DPRINT("dwDesiredAccess = %x\n", dwDesiredAccess);
-
-    if (ScmShutdown)
-        return ERROR_SHUTDOWN_IN_PROGRESS;
-
-    if (!lpScHandle)
-        return ERROR_INVALID_PARAMETER;
-
-    dwError = ScmCreateManagerHandle(lpDatabaseName,
-                                     &hHandle);
-    if (dwError != ERROR_SUCCESS)
-    {
-        DPRINT("ScmCreateManagerHandle() failed (Error %lu)\n", dwError);
-        return dwError;
-    }
-
-    /* Check the desired access */
-    dwError = ScmCheckAccess(hHandle,
-                             dwDesiredAccess | SC_MANAGER_CONNECT);
-    if (dwError != ERROR_SUCCESS)
-    {
-        DPRINT("ScmCheckAccess() failed (Error %lu)\n", dwError);
-        HeapFree(GetProcessHeap(), 0, hHandle);
-        return dwError;
-    }
-
-    *lpScHandle = (SC_RPC_HANDLE)hHandle;
-    DPRINT("*hScm = %p\n", *lpScHandle);
-
-    DPRINT("ROpenSCManagerW() done\n");
-
-    return ERROR_SUCCESS;
-}
+    return ERROR_SUCCESS;
+}
 
 
 /* Function 16 */
@@ -2806,7 +2780,7 @@ DWORD ROpenServiceW(
     *lpServiceHandle = (SC_RPC_HANDLE)hHandle;
     DPRINT("*hService = %p\n", *lpServiceHandle);
 
-Done:;
+Done:
     /* Unlock the service database */
     ScmUnlockDatabase();
 
@@ -2833,7 +2807,6 @@ DWORD RQueryServiceConfigW(
     LPWSTR lpDependencies = NULL;
     DWORD dwDependenciesLength = 0;
     DWORD dwRequiredSize;
-    LPQUERY_SERVICE_CONFIGW lpConfig = NULL;
     WCHAR lpEmptyString[] = {0,0};
     LPWSTR lpStr;
 
@@ -2892,12 +2865,12 @@ DWORD RQueryServiceConfigW(
     dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGW);
 
     if (lpImagePath != NULL)
-        dwRequiredSize += ((wcslen(lpImagePath) + 1) * sizeof(WCHAR));
+        dwRequiredSize += (DWORD)((wcslen(lpImagePath) + 1) * sizeof(WCHAR));
     else
         dwRequiredSize += 2 * sizeof(WCHAR);
 
-    if (lpService->lpGroup != NULL)
-        dwRequiredSize += ((wcslen(lpService->lpGroup->lpGroupName) + 1) * sizeof(WCHAR));
+    if ((lpService->lpGroup != NULL) && (lpService->lpGroup->lpGroupName != NULL))
+        dwRequiredSize += (DWORD)((wcslen(lpService->lpGroup->lpGroupName) + 1) * sizeof(WCHAR));
     else
         dwRequiredSize += 2 * sizeof(WCHAR);
 
@@ -2907,12 +2880,12 @@ DWORD RQueryServiceConfigW(
         dwRequiredSize += 2 * sizeof(WCHAR);
 
     if (lpServiceStartName != NULL)
-        dwRequiredSize += ((wcslen(lpServiceStartName) + 1) * sizeof(WCHAR));
+        dwRequiredSize += (DWORD)((wcslen(lpServiceStartName) + 1) * sizeof(WCHAR));
     else
         dwRequiredSize += 2 * sizeof(WCHAR);
 
     if (lpService->lpDisplayName != NULL)
-        dwRequiredSize += ((wcslen(lpService->lpDisplayName) + 1) * sizeof(WCHAR));
+        dwRequiredSize += (DWORD)((wcslen(lpService->lpDisplayName) + 1) * sizeof(WCHAR));
     else
         dwRequiredSize += 2 * sizeof(WCHAR);
 
@@ -2922,13 +2895,12 @@ DWORD RQueryServiceConfigW(
     }
     else
     {
-        lpConfig = (LPQUERY_SERVICE_CONFIGW)lpServiceConfig;
-        lpConfig->dwServiceType = lpService->Status.dwServiceType;
-        lpConfig->dwStartType = lpService->dwStartType;
-        lpConfig->dwErrorControl = lpService->dwErrorControl;
-        lpConfig->dwTagId = lpService->dwTag;
+        lpServiceConfig->dwServiceType = lpService->Status.dwServiceType;
+        lpServiceConfig->dwStartType = lpService->dwStartType;
+        lpServiceConfig->dwErrorControl = lpService->dwErrorControl;
+        lpServiceConfig->dwTagId = lpService->dwTag;
 
-        lpStr = (LPWSTR)(lpConfig + 1);
+        lpStr = (LPWSTR)(lpServiceConfig + 1);
 
         /* Append the image path */
         if (lpImagePath != NULL)
@@ -2940,11 +2912,11 @@ DWORD RQueryServiceConfigW(
             wcscpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpBinaryPathName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpBinaryPathName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
         lpStr += (wcslen(lpStr) + 1);
 
         /* Append the group name */
-        if (lpService->lpGroup != NULL)
+        if ((lpService->lpGroup != NULL) && (lpService->lpGroup->lpGroupName != NULL))
         {
             wcscpy(lpStr, lpService->lpGroup->lpGroupName);
         }
@@ -2953,7 +2925,7 @@ DWORD RQueryServiceConfigW(
             wcscpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpLoadOrderGroup = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpLoadOrderGroup = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
         lpStr += (wcslen(lpStr) + 1);
 
         /* Append Dependencies */
@@ -2968,9 +2940,9 @@ DWORD RQueryServiceConfigW(
             wcscpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpDependencies = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpDependencies = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
         if (lpDependencies != NULL)
-            lpStr += dwDependenciesLength * sizeof(WCHAR);
+            lpStr += dwDependenciesLength;
         else
             lpStr += (wcslen(lpStr) + 1);
 
@@ -2984,7 +2956,7 @@ DWORD RQueryServiceConfigW(
             wcscpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpServiceStartName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpServiceStartName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
         lpStr += (wcslen(lpStr) + 1);
 
         /* Append the display name */
@@ -2997,13 +2969,13 @@ DWORD RQueryServiceConfigW(
             wcscpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
     }
 
     if (pcbBytesNeeded != NULL)
         *pcbBytesNeeded = dwRequiredSize;
 
-Done:;
+Done:
     /* Unlock the service database */
     ScmUnlockDatabase();
 
@@ -3028,12 +3000,41 @@ Done:;
 /* Function 18 */
 DWORD RQueryServiceLockStatusW(
     SC_RPC_HANDLE hSCManager,
-    LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus,
+    LPBYTE lpBuf, // LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus,
     DWORD cbBufSize,
     LPBOUNDED_DWORD_4K pcbBytesNeeded)
 {
-    UNIMPLEMENTED;
-    return ERROR_CALL_NOT_IMPLEMENTED;
+    LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus = (LPQUERY_SERVICE_LOCK_STATUSW)lpBuf;
+    PMANAGER_HANDLE hMgr;
+    DWORD dwRequiredSize;
+
+    if (!lpLockStatus || !pcbBytesNeeded)
+        return ERROR_INVALID_PARAMETER;
+
+    hMgr = ScmGetServiceManagerFromHandle(hSCManager);
+    if (hMgr == NULL)
+    {
+        DPRINT1("Invalid service manager handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+
+    if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess,
+                                  SC_MANAGER_QUERY_LOCK_STATUS))
+    {
+        DPRINT("Insufficient access rights! 0x%lx\n", hMgr->Handle.DesiredAccess);
+        return ERROR_ACCESS_DENIED;
+    }
+
+    /* FIXME: we need to compute instead the real length of the owner name */
+    dwRequiredSize = sizeof(QUERY_SERVICE_LOCK_STATUSW) + sizeof(WCHAR);
+    *pcbBytesNeeded = dwRequiredSize;
+
+    if (cbBufSize < dwRequiredSize)
+        return ERROR_INSUFFICIENT_BUFFER;
+
+    ScmQueryServiceLockStatusW(lpLockStatus);
+
+    return ERROR_SUCCESS;
 }
 
 
@@ -3046,6 +3047,8 @@ DWORD RStartServiceW(
     DWORD dwError = ERROR_SUCCESS;
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService = NULL;
+
+#ifndef NDEBUG
     DWORD i;
 
     DPRINT("RStartServiceW(%p %lu %p) called\n", hService, argc, argv);
@@ -3054,9 +3057,10 @@ DWORD RStartServiceW(
     {
         for (i = 0; i < argc; i++)
         {
-            DPRINT("  argv[%lu]: %S\n", i, argv[i]);
+            DPRINT("  argv[%lu]: %S\n", i, argv[i].StringPtr);
         }
     }
+#endif
 
     if (ScmShutdown)
         return ERROR_SHUTDOWN_IN_PROGRESS;
@@ -3133,7 +3137,7 @@ DWORD RGetServiceDisplayNameW(
             *lpcchBuffer = 2;
             if (lpDisplayName != NULL)
             {
-                *lpDisplayName = '\0';
+                *lpDisplayName = 0;
             }
         }
 
@@ -3142,7 +3146,7 @@ DWORD RGetServiceDisplayNameW(
 
     if (!lpService->lpDisplayName)
     {
-        dwLength = wcslen(lpService->lpServiceName);
+        dwLength = (DWORD)wcslen(lpService->lpServiceName);
 
         if (lpDisplayName != NULL &&
             *lpcchBuffer > dwLength)
@@ -3152,7 +3156,7 @@ DWORD RGetServiceDisplayNameW(
     }
     else
     {
-        dwLength = wcslen(lpService->lpDisplayName);
+        dwLength = (DWORD)wcslen(lpService->lpDisplayName);
 
         if (lpDisplayName != NULL &&
             *lpcchBuffer > dwLength)
@@ -3207,14 +3211,14 @@ DWORD RGetServiceKeyNameW(
             *lpcchBuffer = 2;
             if (lpServiceName != NULL)
             {
-                *lpServiceName = '\0';
+                *lpServiceName = 0;
             }
         }
 
         return ERROR_SERVICE_DOES_NOT_EXIST;
     }
 
-    dwLength = wcslen(lpService->lpServiceName);
+    dwLength = (DWORD)wcslen(lpService->lpServiceName);
 
     if (lpServiceName != NULL &&
         *lpcchBuffer > dwLength)
@@ -3254,7 +3258,7 @@ DWORD RChangeServiceConfigA(
     LPSTR lpBinaryPathName,
     LPSTR lpLoadOrderGroup,
     LPDWORD lpdwTagId,
-    LPSTR lpDependencies,
+    LPBYTE lpDependencies,
     DWORD dwDependSize,
     LPSTR lpServiceStartName,
     LPBYTE lpPassword,
@@ -3270,7 +3274,6 @@ DWORD RChangeServiceConfigA(
     LPWSTR lpCanonicalImagePathW = NULL;
     LPWSTR lpLoadOrderGroupW = NULL;
     LPWSTR lpDependenciesW = NULL;
-    // LPWSTR lpPasswordW = NULL;
 
     DPRINT("RChangeServiceConfigA() called\n");
     DPRINT("dwServiceType = %lu\n", dwServiceType);
@@ -3327,7 +3330,7 @@ DWORD RChangeServiceConfigA(
     {
         /* Set the display name */
         lpDisplayNameW = HeapAlloc(GetProcessHeap(),
-                                   0,
+                                   HEAP_ZERO_MEMORY,
                                    (strlen(lpDisplayName) + 1) * sizeof(WCHAR));
         if (lpDisplayNameW == NULL)
         {
@@ -3340,14 +3343,14 @@ DWORD RChangeServiceConfigA(
                             lpDisplayName,
                             -1,
                             lpDisplayNameW,
-                            strlen(lpDisplayName) + 1);
+                            (int)(strlen(lpDisplayName) + 1));
 
         RegSetValueExW(hServiceKey,
                        L"DisplayName",
                        0,
                        REG_SZ,
                        (LPBYTE)lpDisplayNameW,
-                       (wcslen(lpDisplayNameW) + 1) * sizeof(WCHAR));
+                       (DWORD)((wcslen(lpDisplayNameW) + 1) * sizeof(WCHAR)));
 
         /* Update lpService->lpDisplayName */
         if (lpService->lpDisplayName)
@@ -3405,7 +3408,7 @@ DWORD RChangeServiceConfigA(
     {
         /* Set the image path */
         lpBinaryPathNameW = HeapAlloc(GetProcessHeap(),
-                                      0,
+                                      HEAP_ZERO_MEMORY,
                                       (strlen(lpBinaryPathName) + 1) * sizeof(WCHAR));
         if (lpBinaryPathNameW == NULL)
         {
@@ -3418,7 +3421,7 @@ DWORD RChangeServiceConfigA(
                             lpBinaryPathName,
                             -1,
                             lpBinaryPathNameW,
-                            strlen(lpBinaryPathName) + 1);
+                            (int)(strlen(lpBinaryPathName) + 1));
 
         if (lpService->Status.dwServiceType & SERVICE_DRIVER)
         {
@@ -3439,7 +3442,7 @@ DWORD RChangeServiceConfigA(
                                  0,
                                  REG_EXPAND_SZ,
                                  (LPBYTE)lpBinaryPathNameW,
-                                 (wcslen(lpBinaryPathNameW) + 1) * sizeof(WCHAR));
+                                 (DWORD)((wcslen(lpBinaryPathNameW) + 1) * sizeof(WCHAR)));
 
         HeapFree(GetProcessHeap(), 0, lpBinaryPathNameW);
 
@@ -3451,7 +3454,7 @@ DWORD RChangeServiceConfigA(
     if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
     {
         lpLoadOrderGroupW = HeapAlloc(GetProcessHeap(),
-                                      0,
+                                      HEAP_ZERO_MEMORY,
                                       (strlen(lpLoadOrderGroup) + 1) * sizeof(WCHAR));
         if (lpLoadOrderGroupW == NULL)
         {
@@ -3464,14 +3467,14 @@ DWORD RChangeServiceConfigA(
                             lpLoadOrderGroup,
                             -1,
                             lpLoadOrderGroupW,
-                            strlen(lpLoadOrderGroup) + 1);
+                            (int)(strlen(lpLoadOrderGroup) + 1));
 
         dwError = RegSetValueExW(hServiceKey,
                                  L"Group",
                                  0,
                                  REG_SZ,
                                  (LPBYTE)lpLoadOrderGroupW,
-                                 (wcslen(lpLoadOrderGroupW) + 1) * sizeof(WCHAR));
+                                 (DWORD)((wcslen(lpLoadOrderGroupW) + 1) * sizeof(WCHAR)));
         if (dwError != ERROR_SUCCESS)
         {
             HeapFree(GetProcessHeap(), 0, lpLoadOrderGroupW);
@@ -3509,8 +3512,8 @@ DWORD RChangeServiceConfigA(
     if (lpDependencies != NULL && *lpDependencies != 0)
     {
         lpDependenciesW = HeapAlloc(GetProcessHeap(),
-                                    0,
-                                    (strlen(lpDependencies) + 1) * sizeof(WCHAR));
+                                    HEAP_ZERO_MEMORY,
+                                    (strlen((LPSTR)lpDependencies) + 1) * sizeof(WCHAR));
         if (lpDependenciesW == NULL)
         {
             dwError = ERROR_NOT_ENOUGH_MEMORY;
@@ -3519,21 +3522,44 @@ DWORD RChangeServiceConfigA(
 
         MultiByteToWideChar(CP_ACP,
                             0,
-                            lpDependencies,
+                            (LPSTR)lpDependencies,
                             dwDependSize,
                             lpDependenciesW,
-                            strlen(lpDependencies) + 1);
+                            (int)(strlen((LPSTR)lpDependencies) + 1));
 
         dwError = ScmWriteDependencies(hServiceKey,
                                        (LPWSTR)lpDependenciesW,
                                        dwDependSize);
 
         HeapFree(GetProcessHeap(), 0, lpDependenciesW);
+
+        if (dwError != ERROR_SUCCESS)
+            goto done;
     }
 
     if (lpPassword != NULL)
     {
-        /* FIXME: Write password */
+        if (wcslen((LPWSTR)lpPassword) != 0)
+        {
+            /* FIXME: Decrypt the password */
+
+            /* Write the password */
+            dwError = ScmSetServicePassword(lpService->szServiceName,
+                                            (LPCWSTR)lpPassword);
+            if (dwError != ERROR_SUCCESS)
+                goto done;
+        }
+        else
+        {
+            /* Delete the password */
+            dwError = ScmSetServicePassword(lpService->szServiceName,
+                                            NULL);
+            if (dwError == ERROR_FILE_NOT_FOUND)
+                dwError = ERROR_SUCCESS;
+
+            if (dwError != ERROR_SUCCESS)
+                goto done;
+        }
     }
 
 done:
@@ -3576,14 +3602,14 @@ DWORD RCreateServiceA(
     LPWSTR lpDependenciesW = NULL;
     LPWSTR lpServiceStartNameW = NULL;
     DWORD dwDependenciesLength = 0;
-    DWORD dwLength;
+    SIZE_T cchLength;
     int len;
     LPCSTR lpStr;
 
     if (lpServiceName)
     {
         len = MultiByteToWideChar(CP_ACP, 0, lpServiceName, -1, NULL, 0);
-        lpServiceNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        lpServiceNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
         if (!lpServiceNameW)
         {
             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -3595,7 +3621,7 @@ DWORD RCreateServiceA(
     if (lpDisplayName)
     {
         len = MultiByteToWideChar(CP_ACP, 0, lpDisplayName, -1, NULL, 0);
-        lpDisplayNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        lpDisplayNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
         if (!lpDisplayNameW)
         {
             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -3607,7 +3633,7 @@ DWORD RCreateServiceA(
     if (lpBinaryPathName)
     {
         len = MultiByteToWideChar(CP_ACP, 0, lpBinaryPathName, -1, NULL, 0);
-        lpBinaryPathNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        lpBinaryPathNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
         if (!lpBinaryPathNameW)
         {
             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -3619,7 +3645,7 @@ DWORD RCreateServiceA(
     if (lpLoadOrderGroup)
     {
         len = MultiByteToWideChar(CP_ACP, 0, lpLoadOrderGroup, -1, NULL, 0);
-        lpLoadOrderGroupW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        lpLoadOrderGroupW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
         if (!lpLoadOrderGroupW)
         {
             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -3633,13 +3659,13 @@ DWORD RCreateServiceA(
         lpStr = (LPCSTR)lpDependencies;
         while (*lpStr)
         {
-            dwLength = strlen(lpStr) + 1;
-            dwDependenciesLength += dwLength;
-            lpStr = lpStr + dwLength;
+            cchLength = strlen(lpStr) + 1;
+            dwDependenciesLength += (DWORD)cchLength;
+            lpStr = lpStr + cchLength;
         }
         dwDependenciesLength++;
 
-        lpDependenciesW = HeapAlloc(GetProcessHeap(), 0, dwDependenciesLength * sizeof(WCHAR));
+        lpDependenciesW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwDependenciesLength * sizeof(WCHAR));
         if (!lpDependenciesW)
         {
             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -3651,7 +3677,7 @@ DWORD RCreateServiceA(
     if (lpServiceStartName)
     {
         len = MultiByteToWideChar(CP_ACP, 0, lpServiceStartName, -1, NULL, 0);
-        lpServiceStartNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        lpServiceStartNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
         if (!lpServiceStartNameW)
         {
             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -3775,7 +3801,7 @@ DWORD REnumDependentServicesA(
 
     /* Allocate memory for array of service pointers */
     lpServicesArray = HeapAlloc(GetProcessHeap(),
-                                0,
+                                HEAP_ZERO_MEMORY,
                                 (dwServicesReturned + 1) * sizeof(PSERVICE));
     if (!lpServicesArray)
     {
@@ -3817,7 +3843,7 @@ DWORD REnumDependentServicesA(
                             lpService->lpDisplayName,
                             -1,
                             lpStr,
-                            wcslen(lpService->lpDisplayName),
+                            (int)wcslen(lpService->lpDisplayName),
                             0,
                             0);
         lpServicesPtr->lpDisplayName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServices);
@@ -3829,13 +3855,13 @@ DWORD REnumDependentServicesA(
                             lpService->lpServiceName,
                             -1,
                             lpStr,
-                            wcslen(lpService->lpServiceName),
+                            (int)wcslen(lpService->lpServiceName),
                             0,
                             0);
         lpServicesPtr->lpServiceName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServices);
         lpStr += strlen(lpStr) + 1;
 
-        lpServicesPtr ++;
+        lpServicesPtr++;
     }
 
     *lpServicesReturned = dwServicesReturned;
@@ -3864,6 +3890,7 @@ DWORD REnumServicesStatusA(
     LPBOUNDED_DWORD_256K lpResumeHandle)
 {
     LPENUM_SERVICE_STATUSW lpStatusPtrW = NULL;
+    LPENUM_SERVICE_STATUSW lpStatusPtrIncrW;
     LPENUM_SERVICE_STATUSA lpStatusPtrA = NULL;
     LPWSTR lpStringPtrW;
     LPSTR lpStringPtrA;
@@ -3872,6 +3899,11 @@ DWORD REnumServicesStatusA(
 
     DPRINT("REnumServicesStatusA() called\n");
 
+    if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
+    {
+        return ERROR_INVALID_ADDRESS;
+    }
+
     if ((dwBufSize > 0) && (lpBuffer))
     {
         lpStatusPtrW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufSize);
@@ -3895,6 +3927,7 @@ DWORD REnumServicesStatusA(
     if (*lpServicesReturned == 0)
         goto Done;
 
+    lpStatusPtrIncrW = lpStatusPtrW;
     lpStatusPtrA = (LPENUM_SERVICE_STATUSA)lpBuffer;
     lpStringPtrA = (LPSTR)((ULONG_PTR)lpBuffer +
                   *lpServicesReturned * sizeof(ENUM_SERVICE_STATUSA));
@@ -3909,7 +3942,7 @@ DWORD REnumServicesStatusA(
                             lpStringPtrW,
                             -1,
                             lpStringPtrA,
-                            wcslen(lpStringPtrW),
+                            (int)wcslen(lpStringPtrW),
                             0,
                             0);
 
@@ -3923,7 +3956,7 @@ DWORD REnumServicesStatusA(
                             lpStringPtrW,
                             -1,
                             lpStringPtrA,
-                            wcslen(lpStringPtrW),
+                            (int)wcslen(lpStringPtrW),
                             0,
                             0);
 
@@ -3933,13 +3966,14 @@ DWORD REnumServicesStatusA(
 
         /* Copy the status information */
         memcpy(&lpStatusPtrA->ServiceStatus,
-               &lpStatusPtrW->ServiceStatus,
+               &lpStatusPtrIncrW->ServiceStatus,
                sizeof(SERVICE_STATUS));
 
+        lpStatusPtrIncrW++;
         lpStatusPtrA++;
     }
 
-Done:;
+Done:
     if (lpStatusPtrW)
         HeapFree(GetProcessHeap(), 0, lpStatusPtrW);
 
@@ -4030,7 +4064,6 @@ DWORD RQueryServiceConfigA(
     LPWSTR lpDependencies = NULL;
     DWORD dwDependenciesLength = 0;
     DWORD dwRequiredSize;
-    LPQUERY_SERVICE_CONFIGA lpConfig = NULL;
     CHAR lpEmptyString[]={0,0};
     LPSTR lpStr;
 
@@ -4086,15 +4119,15 @@ DWORD RQueryServiceConfigA(
                         &lpDependencies,
                         &dwDependenciesLength);
 
-    dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGW);
+    dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGA);
 
     if (lpImagePath != NULL)
-        dwRequiredSize += wcslen(lpImagePath) + 1;
+        dwRequiredSize += (DWORD)(wcslen(lpImagePath) + 1);
     else
         dwRequiredSize += 2;
 
     if ((lpService->lpGroup != NULL) && (lpService->lpGroup->lpGroupName != NULL))
-        dwRequiredSize += wcslen(lpService->lpGroup->lpGroupName) + 1;
+        dwRequiredSize += (DWORD)(wcslen(lpService->lpGroup->lpGroupName) + 1);
     else
         dwRequiredSize += 2;
 
@@ -4105,12 +4138,12 @@ DWORD RQueryServiceConfigA(
         dwRequiredSize += 2;
 
     if (lpServiceStartName != NULL)
-        dwRequiredSize += wcslen(lpServiceStartName) + 1;
+        dwRequiredSize += (DWORD)(wcslen(lpServiceStartName) + 1);
     else
         dwRequiredSize += 2;
 
     if (lpService->lpDisplayName != NULL)
-        dwRequiredSize += wcslen(lpService->lpDisplayName) + 1;
+        dwRequiredSize += (DWORD)(wcslen(lpService->lpDisplayName) + 1);
     else
         dwRequiredSize += 2;
 
@@ -4120,16 +4153,15 @@ DWORD RQueryServiceConfigA(
     }
     else
     {
-        lpConfig = (LPQUERY_SERVICE_CONFIGA)lpServiceConfig;
-        lpConfig->dwServiceType = lpService->Status.dwServiceType;
-        lpConfig->dwStartType = lpService->dwStartType;
-        lpConfig->dwErrorControl = lpService->dwErrorControl;
-        lpConfig->dwTagId = lpService->dwTag;
+        lpServiceConfig->dwServiceType = lpService->Status.dwServiceType;
+        lpServiceConfig->dwStartType = lpService->dwStartType;
+        lpServiceConfig->dwErrorControl = lpService->dwErrorControl;
+        lpServiceConfig->dwTagId = lpService->dwTag;
 
         lpStr = (LPSTR)(lpServiceConfig + 1);
 
         /* NOTE: Strings that are NULL for QUERY_SERVICE_CONFIG are pointers to empty strings.
-          Verified in WINXP*/
+           Verified in WINXP */
 
         if (lpImagePath)
         {
@@ -4138,7 +4170,7 @@ DWORD RQueryServiceConfigA(
                                 lpImagePath,
                                 -1,
                                 lpStr,
-                                wcslen(lpImagePath) + 1,
+                                (int)(wcslen(lpImagePath) + 1),
                                 0,
                                 0);
         }
@@ -4147,7 +4179,7 @@ DWORD RQueryServiceConfigA(
             strcpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpBinaryPathName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpBinaryPathName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
         lpStr += (strlen((LPSTR)lpStr) + 1);
 
         if (lpService->lpGroup && lpService->lpGroup->lpGroupName)
@@ -4157,7 +4189,7 @@ DWORD RQueryServiceConfigA(
                                 lpService->lpGroup->lpGroupName,
                                 -1,
                                 lpStr,
-                                wcslen(lpService->lpGroup->lpGroupName) + 1,
+                                (int)(wcslen(lpService->lpGroup->lpGroupName) + 1),
                                 0,
                                 0);
         }
@@ -4166,7 +4198,7 @@ DWORD RQueryServiceConfigA(
             strcpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpLoadOrderGroup = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpLoadOrderGroup = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
         lpStr += (strlen(lpStr) + 1);
 
         /* Append Dependencies */
@@ -4186,7 +4218,7 @@ DWORD RQueryServiceConfigA(
             strcpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpDependencies = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpDependencies = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
         if (lpDependencies)
             lpStr += dwDependenciesLength;
         else
@@ -4199,7 +4231,7 @@ DWORD RQueryServiceConfigA(
                                 lpServiceStartName,
                                 -1,
                                 lpStr,
-                                wcslen(lpServiceStartName) + 1,
+                                (int)(wcslen(lpServiceStartName) + 1),
                                 0,
                                 0);
         }
@@ -4208,7 +4240,7 @@ DWORD RQueryServiceConfigA(
             strcpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpServiceStartName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpServiceStartName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
         lpStr += (strlen(lpStr) + 1);
 
         if (lpService->lpDisplayName)
@@ -4218,7 +4250,7 @@ DWORD RQueryServiceConfigA(
                                 lpService->lpDisplayName,
                                 -1,
                                 lpStr,
-                                wcslen(lpService->lpDisplayName) + 1,
+                                (int)(wcslen(lpService->lpDisplayName) + 1),
                                 0,
                                 0);
         }
@@ -4227,13 +4259,13 @@ DWORD RQueryServiceConfigA(
             strcpy(lpStr, lpEmptyString);
         }
 
-        lpConfig->lpDisplayName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
+        lpServiceConfig->lpDisplayName = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceConfig);
     }
 
     if (pcbBytesNeeded != NULL)
         *pcbBytesNeeded = dwRequiredSize;
 
-Done:;
+Done:
     /* Unlock the service database */
     ScmUnlockDatabase();
 
@@ -4258,12 +4290,41 @@ Done:;
 /* Function 30 */
 DWORD RQueryServiceLockStatusA(
     SC_RPC_HANDLE hSCManager,
-    LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus,
+    LPBYTE lpBuf, // LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus,
     DWORD cbBufSize,
     LPBOUNDED_DWORD_4K pcbBytesNeeded)
 {
-    UNIMPLEMENTED;
-    return ERROR_CALL_NOT_IMPLEMENTED;
+    LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus = (LPQUERY_SERVICE_LOCK_STATUSA)lpBuf;
+    PMANAGER_HANDLE hMgr;
+    DWORD dwRequiredSize;
+
+    if (!lpLockStatus || !pcbBytesNeeded)
+        return ERROR_INVALID_PARAMETER;
+
+    hMgr = ScmGetServiceManagerFromHandle(hSCManager);
+    if (hMgr == NULL)
+    {
+        DPRINT1("Invalid service manager handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+
+    if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess,
+                                  SC_MANAGER_QUERY_LOCK_STATUS))
+    {
+        DPRINT("Insufficient access rights! 0x%lx\n", hMgr->Handle.DesiredAccess);
+        return ERROR_ACCESS_DENIED;
+    }
+
+    /* FIXME: we need to compute instead the real length of the owner name */
+    dwRequiredSize = sizeof(QUERY_SERVICE_LOCK_STATUSA) + sizeof(CHAR);
+    *pcbBytesNeeded = dwRequiredSize;
+
+    if (cbBufSize < dwRequiredSize)
+        return ERROR_INSUFFICIENT_BUFFER;
+
+    ScmQueryServiceLockStatusA(lpLockStatus);
+
+    return ERROR_SUCCESS;
 }
 
 
@@ -4395,7 +4456,7 @@ DWORD RGetServiceDisplayNameA(
 
     if (lpServiceName != NULL)
     {
-        dwLength = strlen(lpServiceName) + 1;
+        dwLength = (DWORD)(strlen(lpServiceName) + 1);
         lpServiceNameW = HeapAlloc(GetProcessHeap(),
                                    HEAP_ZERO_MEMORY,
                                    dwLength * sizeof(WCHAR));
@@ -4425,7 +4486,7 @@ DWORD RGetServiceDisplayNameA(
             *lpcchBuffer = 1;
             if (lpDisplayName != NULL)
             {
-                *lpDisplayName = '\0';
+                *lpDisplayName = 0;
             }
         }
         return ERROR_SERVICE_DOES_NOT_EXIST;
@@ -4433,14 +4494,14 @@ DWORD RGetServiceDisplayNameA(
 
     if (!lpService->lpDisplayName)
     {
-        dwLength = wcslen(lpService->lpServiceName);
+        dwLength = (DWORD)wcslen(lpService->lpServiceName);
         if (lpDisplayName != NULL &&
             *lpcchBuffer > dwLength)
         {
             WideCharToMultiByte(CP_ACP,
                                 0,
                                 lpService->lpServiceName,
-                                wcslen(lpService->lpServiceName),
+                                (int)wcslen(lpService->lpServiceName),
                                 lpDisplayName,
                                 dwLength + 1,
                                 NULL,
@@ -4450,14 +4511,14 @@ DWORD RGetServiceDisplayNameA(
     }
     else
     {
-        dwLength = wcslen(lpService->lpDisplayName);
+        dwLength = (DWORD)wcslen(lpService->lpDisplayName);
         if (lpDisplayName != NULL &&
             *lpcchBuffer > dwLength)
         {
             WideCharToMultiByte(CP_ACP,
                                 0,
                                 lpService->lpDisplayName,
-                                wcslen(lpService->lpDisplayName),
+                                (int)wcslen(lpService->lpDisplayName),
                                 lpDisplayName,
                                 dwLength + 1,
                                 NULL,
@@ -4492,7 +4553,7 @@ DWORD RGetServiceKeyNameA(
     DPRINT("lpServiceName: %p\n", lpServiceName);
     DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
 
-    dwLength = strlen(lpDisplayName) + 1;
+    dwLength = (DWORD)(strlen(lpDisplayName) + 1);
     lpDisplayNameW = HeapAlloc(GetProcessHeap(),
                                HEAP_ZERO_MEMORY,
                                dwLength * sizeof(WCHAR));
@@ -4521,21 +4582,21 @@ DWORD RGetServiceKeyNameA(
             *lpcchBuffer = 1;
             if (lpServiceName != NULL)
             {
-                *lpServiceName = '\0';
+                *lpServiceName = 0;
             }
         }
 
         return ERROR_SERVICE_DOES_NOT_EXIST;
     }
 
-    dwLength = wcslen(lpService->lpServiceName);
+    dwLength = (DWORD)wcslen(lpService->lpServiceName);
     if (lpServiceName != NULL &&
         *lpcchBuffer > dwLength)
     {
         WideCharToMultiByte(CP_ACP,
                             0,
                             lpService->lpServiceName,
-                            wcslen(lpService->lpServiceName),
+                            (int)wcslen(lpService->lpServiceName),
                             lpServiceName,
                             dwLength + 1,
                             NULL,
@@ -4557,14 +4618,58 @@ DWORD RI_ScGetCurrentGroupStateW(
     LPWSTR lpLoadOrderGroup,
     LPDWORD lpState)
 {
-    UNIMPLEMENTED;
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
+    PMANAGER_HANDLE hManager;
+    PSERVICE_GROUP pServiceGroup;
+    DWORD dwError = ERROR_SUCCESS;
 
+    DPRINT("RI_ScGetCurrentGroupStateW() called\n");
 
-/* Function 35 */
-DWORD REnumServiceGroupW(
-    SC_RPC_HANDLE hSCManager,
+    if (ScmShutdown)
+        return ERROR_SHUTDOWN_IN_PROGRESS;
+
+    hManager = ScmGetServiceManagerFromHandle(hSCManager);
+    if (hManager == NULL)
+    {
+        DPRINT1("Invalid service manager handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+
+    /* Check for SC_MANAGER_ENUMERATE_SERVICE access right */
+    if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
+                                  SC_MANAGER_ENUMERATE_SERVICE))
+    {
+        DPRINT("Insufficient access rights! 0x%lx\n",
+                hManager->Handle.DesiredAccess);
+        return ERROR_ACCESS_DENIED;
+    }
+
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
+
+    /* Get the group list entry */
+    pServiceGroup = ScmGetServiceGroupByName(lpLoadOrderGroup);
+    if (pServiceGroup == NULL)
+    {
+        dwError = ERROR_SERVICE_DOES_NOT_EXIST;
+        goto done;
+    }
+
+    /* FIXME: Return the group state */
+    *lpState = 0;
+
+done:
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
+    DPRINT("RI_ScGetCurrentGroupStateW() done (Error %lu)\n", dwError);
+
+    return dwError;
+}
+
+
+/* Function 35 */
+DWORD REnumServiceGroupW(
+    SC_RPC_HANDLE hSCManager,
     DWORD dwServiceType,
     DWORD dwServiceState,
     LPBYTE lpBuffer,
@@ -4574,14 +4679,258 @@ DWORD REnumServiceGroupW(
     LPBOUNDED_DWORD_256K lpResumeIndex,
     LPCWSTR pszGroupName)
 {
-    UNIMPLEMENTED;
-    return ERROR_CALL_NOT_IMPLEMENTED;
+    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("REnumServiceGroupW() 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;
+    }
+
+    if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
+    {
+        return ERROR_INVALID_ADDRESS;
+    }
+
+    *pcbBytesNeeded = 0;
+    *lpServicesReturned = 0;
+
+    if ((dwServiceType == 0) ||
+        ((dwServiceType & ~SERVICE_TYPE_ALL) != 0))
+    {
+        DPRINT("Not a valid Service Type!\n");
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if ((dwServiceState == 0) ||
+        ((dwServiceState & ~SERVICE_STATE_ALL) != 0))
+    {
+        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 (lpResumeIndex)
+        dwLastResumeCount = *lpResumeIndex;
+
+    /* Lock the service database shared */
+    ScmLockDatabaseShared();
+
+    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;
+
+        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) +
+                 (DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                 (DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
+
+        if (dwRequiredSize + dwSize > cbBufSize)
+        {
+            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;
+
+        if (pszGroupName)
+        {
+            if (*pszGroupName == 0)
+            {
+                if (CurrentService->lpGroup != NULL)
+                    continue;
+            }
+            else
+            {
+                if ((CurrentService->lpGroup == NULL) ||
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
+                    continue;
+            }
+        }
+
+        dwRequiredSize += (sizeof(ENUM_SERVICE_STATUSW) +
+                           (DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                           (DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR)));
+
+        dwError = ERROR_MORE_DATA;
+    }
+
+    DPRINT("*pcbBytesNeeded: %lu\n", dwRequiredSize);
+
+    if (lpResumeIndex)
+        *lpResumeIndex = 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;
+
+        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) +
+                 (DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                 (DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
+
+        if (dwRequiredSize + dwSize > cbBufSize)
+            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 == ERROR_SUCCESS)
+    {
+        *pcbBytesNeeded = 0;
+        if (lpResumeIndex) *lpResumeIndex = 0;
+    }
+
+Done:
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
+    DPRINT("REnumServiceGroupW() done (Error %lu)\n", dwError);
+
+    return dwError;
 }
 
 
-//
-// WARNING: This function is untested
-//
 /* Function 36 */
 DWORD RChangeServiceConfig2A(
     SC_RPC_HANDLE hService,
@@ -4598,82 +4947,139 @@ DWORD RChangeServiceConfig2A(
 
     if (InfoW.dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
     {
-        LPSERVICE_DESCRIPTIONW lpServiceDescriptonW;
-        //LPSERVICE_DESCRIPTIONA lpServiceDescriptonA;
+        LPSERVICE_DESCRIPTIONW lpServiceDescriptionW;
+        LPSERVICE_DESCRIPTIONA lpServiceDescriptionA;
 
-        //lpServiceDescriptonA = Info.psd;
+        lpServiceDescriptionA = Info.psd;
 
-        ///if (lpServiceDescriptonA &&
-        ///lpServiceDescriptonA->lpDescription)
-        ///{
-            dwLength = (strlen(Info.lpDescription) + 1) * sizeof(WCHAR);
+        if (lpServiceDescriptionA &&
+            lpServiceDescriptionA->lpDescription)
+        {
+            dwLength = (DWORD)((strlen(lpServiceDescriptionA->lpDescription) + 1) * sizeof(WCHAR));
 
-            lpServiceDescriptonW = HeapAlloc(GetProcessHeap(),
-                                             0,
-                                             dwLength + sizeof(SERVICE_DESCRIPTIONW));
-            if (!lpServiceDescriptonW)
+            lpServiceDescriptionW = HeapAlloc(GetProcessHeap(),
+                                              HEAP_ZERO_MEMORY,
+                                              dwLength + sizeof(SERVICE_DESCRIPTIONW));
+            if (!lpServiceDescriptionW)
             {
                 return ERROR_NOT_ENOUGH_MEMORY;
             }
 
-            lpServiceDescriptonW->lpDescription = (LPWSTR)(lpServiceDescriptonW + 1);
+            lpServiceDescriptionW->lpDescription = (LPWSTR)(lpServiceDescriptionW + 1);
 
             MultiByteToWideChar(CP_ACP,
                                 0,
-                                Info.lpDescription,
+                                lpServiceDescriptionA->lpDescription,
                                 -1,
-                                lpServiceDescriptonW->lpDescription,
+                                lpServiceDescriptionW->lpDescription,
                                 dwLength);
 
-            ptr = lpServiceDescriptonW;
-            InfoW.psd = lpServiceDescriptonW;
-        ///}
+            ptr = lpServiceDescriptionW;
+            InfoW.psd = lpServiceDescriptionW;
+        }
     }
     else if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
     {
         LPSERVICE_FAILURE_ACTIONSW lpServiceFailureActionsW;
         LPSERVICE_FAILURE_ACTIONSA lpServiceFailureActionsA;
-        DWORD dwRebootLen = 0;
+        DWORD dwRebootLen  = 0;
         DWORD dwCommandLen = 0;
+        DWORD dwActionArrayLen = 0;
+        LPWSTR lpStr = NULL;
 
         lpServiceFailureActionsA = Info.psfa;
 
         if (lpServiceFailureActionsA)
         {
+            /*
+             * The following code is inspired by the
+             * SERVICE_CONFIG_FAILURE_ACTIONS case of
+             * the RQueryServiceConfig2W function.
+             */
+
+            /* Retrieve the needed length for the two data strings */
             if (lpServiceFailureActionsA->lpRebootMsg)
             {
-                dwRebootLen = (strlen(lpServiceFailureActionsA->lpRebootMsg) + 1) * sizeof(WCHAR);
+                dwRebootLen = (DWORD)((strlen(lpServiceFailureActionsA->lpRebootMsg) + 1) * sizeof(WCHAR));
             }
             if (lpServiceFailureActionsA->lpCommand)
             {
-                dwCommandLen = (strlen(lpServiceFailureActionsA->lpCommand) + 1) * sizeof(WCHAR);
+                dwCommandLen = (DWORD)((strlen(lpServiceFailureActionsA->lpCommand) + 1) * sizeof(WCHAR));
             }
-            dwLength = dwRebootLen + dwCommandLen + sizeof(SERVICE_FAILURE_ACTIONSW);
 
+            /*
+             * Retrieve the size of the lpsaActions array if needed.
+             * We will copy the lpsaActions array only if there is at
+             * least one action AND that the original array is valid.
+             */
+            if (lpServiceFailureActionsA->cActions > 0 && lpServiceFailureActionsA->lpsaActions)
+            {
+                dwActionArrayLen = lpServiceFailureActionsA->cActions * sizeof(SC_ACTION);
+            }
+
+            /* Compute the total length for the UNICODE structure, including data */
+            dwLength = sizeof(SERVICE_FAILURE_ACTIONSW) +
+                       dwActionArrayLen + dwRebootLen + dwCommandLen;
+
+            /* Allocate the structure */
             lpServiceFailureActionsW = HeapAlloc(GetProcessHeap(),
-                                                 0,
+                                                 HEAP_ZERO_MEMORY,
                                                  dwLength);
             if (!lpServiceFailureActionsW)
             {
                 return ERROR_NOT_ENOUGH_MEMORY;
             }
 
-            lpServiceFailureActionsW->cActions = lpServiceFailureActionsA->cActions;
+            /* Copy the members */
             lpServiceFailureActionsW->dwResetPeriod = lpServiceFailureActionsA->dwResetPeriod;
-            CopyMemory(lpServiceFailureActionsW->lpsaActions, lpServiceFailureActionsA->lpsaActions, sizeof(SC_ACTION));
+            lpServiceFailureActionsW->cActions      = lpServiceFailureActionsA->cActions;
 
-            if (lpServiceFailureActionsA->lpRebootMsg)
+            /* Copy the lpsaActions array if needed */
+            if (dwActionArrayLen > 0)
+            {
+                /* The storage zone is just after the end of the SERVICE_FAILURE_ACTIONSW structure */
+                lpServiceFailureActionsW->lpsaActions = (LPSC_ACTION)((ULONG_PTR)(lpServiceFailureActionsW + 1));
+
+                /* dwActionArrayLen == lpServiceFailureActionsW->cActions * sizeof(SC_ACTION) */
+                RtlCopyMemory(lpServiceFailureActionsW->lpsaActions,
+                              lpServiceFailureActionsA->lpsaActions,
+                              dwActionArrayLen);
+            }
+            else
+            {
+                /* No lpsaActions array */
+                lpServiceFailureActionsW->lpsaActions = NULL;
+            }
+            /* The data strings are stored just after the lpsaActions array */
+            lpStr = (LPWSTR)((ULONG_PTR)(lpServiceFailureActionsW + 1) + dwActionArrayLen);
+
+            /*
+             * Convert the data strings to UNICODE
+             */
+
+            lpServiceFailureActionsW->lpRebootMsg = NULL;
+            lpServiceFailureActionsW->lpCommand   = NULL;
+
+            if (dwRebootLen)
             {
+                /* lpRebootMsg points just after the lpsaActions array */
+                lpServiceFailureActionsW->lpRebootMsg = lpStr;
+
                 MultiByteToWideChar(CP_ACP,
                                     0,
                                     lpServiceFailureActionsA->lpRebootMsg,
                                     -1,
                                     lpServiceFailureActionsW->lpRebootMsg,
                                     dwRebootLen);
+
+                lpStr += dwRebootLen / sizeof(WCHAR);
             }
 
-            if (lpServiceFailureActionsA->lpCommand)
+            if (dwCommandLen)
             {
+                /* lpRebootMsg points just after the lpRebootMsg data string */
+                lpServiceFailureActionsW->lpCommand = lpStr;
+
                 MultiByteToWideChar(CP_ACP,
                                     0,
                                     lpServiceFailureActionsA->lpCommand,
@@ -4682,7 +5088,9 @@ DWORD RChangeServiceConfig2A(
                                     dwCommandLen);
             }
 
+            /* Set the pointers */
             ptr = lpServiceFailureActionsW;
+            InfoW.psfa = lpServiceFailureActionsW;
         }
     }
 
@@ -4694,6 +5102,226 @@ DWORD RChangeServiceConfig2A(
 }
 
 
+static DWORD
+ScmSetFailureActions(HKEY hServiceKey,
+                     LPSERVICE_FAILURE_ACTIONSW lpFailureActions)
+{
+    LPSERVICE_FAILURE_ACTIONSW lpReadBuffer = NULL;
+    LPSERVICE_FAILURE_ACTIONSW lpWriteBuffer = NULL;
+    DWORD dwRequiredSize = 0;
+    DWORD dwType = 0;
+    DWORD dwError;
+
+    /* There is nothing to be done if we have no failure actions */
+    if (lpFailureActions == NULL)
+        return ERROR_SUCCESS;
+
+    /*
+     * 1- Retrieve the original value of FailureActions.
+     */
+
+    /* Query value length */
+    dwError = RegQueryValueExW(hServiceKey,
+                               L"FailureActions",
+                               NULL,
+                               &dwType,
+                               NULL,
+                              &dwRequiredSize);
+    if (dwError != ERROR_SUCCESS &&
+        dwError != ERROR_MORE_DATA &&
+        dwError != ERROR_FILE_NOT_FOUND)
+        return dwError;
+
+    dwRequiredSize = (dwType == REG_BINARY) ? max(sizeof(SERVICE_FAILURE_ACTIONSW), dwRequiredSize)
+                                            : sizeof(SERVICE_FAILURE_ACTIONSW);
+
+    /* Initialize the read buffer */
+    lpReadBuffer = HeapAlloc(GetProcessHeap(),
+                             HEAP_ZERO_MEMORY,
+                             dwRequiredSize);
+    if (lpReadBuffer == NULL)
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    /* Now we can fill the read buffer */
+    if (dwError != ERROR_FILE_NOT_FOUND &&
+        dwType == REG_BINARY)
+    {
+        dwError = RegQueryValueExW(hServiceKey,
+                                   L"FailureActions",
+                                   NULL,
+                                   NULL,
+                                   (LPBYTE)lpReadBuffer,
+                                   &dwRequiredSize);
+        if (dwError != ERROR_SUCCESS &&
+            dwError != ERROR_FILE_NOT_FOUND)
+            goto done;
+
+        if (dwRequiredSize < sizeof(SERVICE_FAILURE_ACTIONSW))
+            dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW);
+    }
+    else
+    {
+        /*
+         * The value of the error doesn't really matter, the only
+         * important thing is that it must be != ERROR_SUCCESS.
+         */
+        dwError = ERROR_INVALID_DATA;
+    }
+
+    if (dwError == ERROR_SUCCESS)
+    {
+        lpReadBuffer->cActions = min(lpReadBuffer->cActions, (dwRequiredSize - sizeof(SERVICE_FAILURE_ACTIONSW)) / sizeof(SC_ACTION));
+        lpReadBuffer->lpsaActions = (lpReadBuffer->cActions > 0 ? (LPSC_ACTION)(lpReadBuffer + 1) : NULL);
+    }
+    else
+    {
+        lpReadBuffer->dwResetPeriod = 0;
+        lpReadBuffer->cActions = 0;
+        lpReadBuffer->lpsaActions = NULL;
+    }
+
+    lpReadBuffer->lpRebootMsg = NULL;
+    lpReadBuffer->lpCommand = NULL;
+
+    /*
+     * 2- Initialize the new value to set.
+     */
+
+    dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW);
+
+    if (lpFailureActions->lpsaActions == NULL)
+    {
+        /*
+         * lpFailureActions->cActions is ignored.
+         * Therefore we use the original values
+         * of cActions and lpsaActions.
+         */
+        dwRequiredSize += lpReadBuffer->cActions * sizeof(SC_ACTION);
+    }
+    else
+    {
+        /*
+         * The reset period and array of failure actions
+         * are deleted if lpFailureActions->cActions == 0 .
+         */
+        dwRequiredSize += lpFailureActions->cActions * sizeof(SC_ACTION);
+    }
+
+    lpWriteBuffer = HeapAlloc(GetProcessHeap(),
+                              HEAP_ZERO_MEMORY,
+                              dwRequiredSize);
+    if (lpWriteBuffer == NULL)
+    {
+        dwError = ERROR_NOT_ENOUGH_MEMORY;
+        goto done;
+    }
+
+    /* Clean the pointers as they have no meaning when the structure is stored in the registry */
+    lpWriteBuffer->lpRebootMsg = NULL;
+    lpWriteBuffer->lpCommand = NULL;
+    lpWriteBuffer->lpsaActions = NULL;
+
+    /* Set the members */
+    if (lpFailureActions->lpsaActions == NULL)
+    {
+        /*
+         * lpFailureActions->dwResetPeriod and lpFailureActions->cActions are ignored.
+         * Therefore we use the original values of dwResetPeriod, cActions and lpsaActions.
+         */
+        lpWriteBuffer->dwResetPeriod = lpReadBuffer->dwResetPeriod;
+        lpWriteBuffer->cActions = lpReadBuffer->cActions;
+
+        if (lpReadBuffer->lpsaActions != NULL)
+        {
+            memmove(lpWriteBuffer + 1,
+                    lpReadBuffer->lpsaActions,
+                    lpReadBuffer->cActions * sizeof(SC_ACTION));
+        }
+    }
+    else
+    {
+        if (lpFailureActions->cActions > 0)
+        {
+            lpWriteBuffer->dwResetPeriod = lpFailureActions->dwResetPeriod;
+            lpWriteBuffer->cActions = lpFailureActions->cActions;
+
+            memmove(lpWriteBuffer + 1,
+                    lpFailureActions->lpsaActions,
+                    lpFailureActions->cActions * sizeof(SC_ACTION));
+        }
+        else
+        {
+            /* The reset period and array of failure actions are deleted */
+            lpWriteBuffer->dwResetPeriod = 0;
+            lpWriteBuffer->cActions = 0;
+        }
+    }
+
+    /* Save the new failure actions into the registry */
+    dwError = RegSetValueExW(hServiceKey,
+                             L"FailureActions",
+                             0,
+                             REG_BINARY,
+                             (LPBYTE)lpWriteBuffer,
+                             dwRequiredSize);
+
+    /* We modify the strings only in case of success.*/
+    if (dwError == ERROR_SUCCESS)
+    {
+        /* Modify the Reboot Message value, if specified */
+        if (lpFailureActions->lpRebootMsg != NULL)
+        {
+            /* If the Reboot Message is "" then we delete it */
+            if (*lpFailureActions->lpRebootMsg == 0)
+            {
+                DPRINT("Delete Reboot Message value\n");
+                RegDeleteValueW(hServiceKey, L"RebootMessage");
+            }
+            else
+            {
+                DPRINT("Setting Reboot Message value %S\n", lpFailureActions->lpRebootMsg);
+                RegSetValueExW(hServiceKey,
+                               L"RebootMessage",
+                               0,
+                               REG_SZ,
+                               (LPBYTE)lpFailureActions->lpRebootMsg,
+                               (DWORD)((wcslen(lpFailureActions->lpRebootMsg) + 1) * sizeof(WCHAR)));
+            }
+        }
+
+        /* Modify the Failure Command value, if specified */
+        if (lpFailureActions->lpCommand != NULL)
+        {
+            /* If the FailureCommand string is an empty string, delete the value */
+            if (*lpFailureActions->lpCommand == 0)
+            {
+                DPRINT("Delete Failure Command value\n");
+                RegDeleteValueW(hServiceKey, L"FailureCommand");
+            }
+            else
+            {
+                DPRINT("Setting Failure Command value %S\n", lpFailureActions->lpCommand);
+                RegSetValueExW(hServiceKey,
+                               L"FailureCommand",
+                               0,
+                               REG_SZ,
+                               (LPBYTE)lpFailureActions->lpCommand,
+                               (DWORD)((wcslen(lpFailureActions->lpCommand) + 1) * sizeof(WCHAR)));
+            }
+        }
+    }
+
+done:
+    if (lpWriteBuffer != NULL)
+        HeapFree(GetProcessHeap(), 0, lpWriteBuffer);
+
+    if (lpReadBuffer != NULL)
+        HeapFree(GetProcessHeap(), 0, lpReadBuffer);
+
+    return dwError;
+}
+
+
 /* Function 37 */
 DWORD RChangeServiceConfig2W(
     SC_RPC_HANDLE hService,
@@ -4703,6 +5331,7 @@ DWORD RChangeServiceConfig2W(
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService = NULL;
     HKEY hServiceKey = NULL;
+    ACCESS_MASK RequiredAccess = SERVICE_CHANGE_CONFIG;
 
     DPRINT("RChangeServiceConfig2W() called\n");
     DPRINT("dwInfoLevel = %lu\n", Info.dwInfoLevel);
@@ -4713,17 +5342,27 @@ DWORD RChangeServiceConfig2W(
     hSvc = ScmGetServiceFromHandle(hService);
     if (hSvc == NULL)
     {
-        DPRINT1("Invalid service handle!\n");
+        DPRINT("Invalid service handle!\n");
         return ERROR_INVALID_HANDLE;
     }
 
+    if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
+        RequiredAccess |= SERVICE_START;
+
+    /* Check the access rights */
     if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
-                                  SERVICE_CHANGE_CONFIG))
+                                  RequiredAccess))
     {
         DPRINT("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
         return ERROR_ACCESS_DENIED;
     }
 
+    if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
+    {
+        /* FIXME: Check if the caller has the SE_SHUTDOWN_NAME privilege */
+
+    }
+
     lpService = hSvc->ServiceEntry;
     if (lpService == NULL)
     {
@@ -4731,6 +5370,13 @@ DWORD RChangeServiceConfig2W(
         return ERROR_INVALID_HANDLE;
     }
 
+    /* Failure actions can only be set for Win32 services, not for drivers */
+    if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
+    {
+        if (lpService->Status.dwServiceType & SERVICE_DRIVER)
+            return ERROR_CANNOT_DETECT_DRIVER_FAILURE;
+    }
+
     /* Lock the service database exclusively */
     ScmLockDatabaseExclusive();
 
@@ -4743,46 +5389,57 @@ DWORD RChangeServiceConfig2W(
 
     /* Open the service key */
     dwError = ScmOpenServiceKey(lpService->szServiceName,
-                                KEY_SET_VALUE,
+                                KEY_READ | KEY_SET_VALUE,
                                 &hServiceKey);
     if (dwError != ERROR_SUCCESS)
         goto done;
 
     if (Info.dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
     {
-        LPSERVICE_DESCRIPTIONW lpServiceDescription;
-
-        lpServiceDescription = (LPSERVICE_DESCRIPTIONW)Info.psd;
-        lpServiceDescription->lpDescription = (LPWSTR)((ULONG_PTR)lpServiceDescription + sizeof(LPSERVICE_DESCRIPTIONW));
+        LPSERVICE_DESCRIPTIONW lpServiceDescription = (LPSERVICE_DESCRIPTIONW)Info.psd;
 
+        /* Modify the service description, if specified */
         if (lpServiceDescription != NULL &&
             lpServiceDescription->lpDescription != NULL)
         {
-            DPRINT("Setting value %S\n", lpServiceDescription->lpDescription);
-            dwError = RegSetValueExW(hServiceKey,
-                                     L"Description",
-                                     0,
-                                     REG_SZ,
-                                     (LPBYTE)lpServiceDescription->lpDescription,
-                                     (wcslen(lpServiceDescription->lpDescription) + 1) * sizeof(WCHAR));
-            if (dwError != ERROR_SUCCESS)
-                goto done;
+            /* If the description is "" then we delete it */
+            if (*lpServiceDescription->lpDescription == 0)
+            {
+                DPRINT("Delete service description\n");
+                dwError = RegDeleteValueW(hServiceKey, L"Description");
+
+                if (dwError == ERROR_FILE_NOT_FOUND)
+                    dwError = ERROR_SUCCESS;
+            }
+            else
+            {
+                DPRINT("Setting service description value %S\n", lpServiceDescription->lpDescription);
+                dwError = RegSetValueExW(hServiceKey,
+                                         L"Description",
+                                         0,
+                                         REG_SZ,
+                                         (LPBYTE)lpServiceDescription->lpDescription,
+                                         (DWORD)((wcslen(lpServiceDescription->lpDescription) + 1) * sizeof(WCHAR)));
+            }
+        }
+        else
+        {
+            dwError = ERROR_SUCCESS;
         }
     }
     else if (Info.dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
     {
-        UNIMPLEMENTED;
-        dwError = ERROR_CALL_NOT_IMPLEMENTED;
-        goto done;
+        dwError = ScmSetFailureActions(hServiceKey,
+                                       (LPSERVICE_FAILURE_ACTIONSW)Info.psfa);
     }
 
 done:
-    /* Unlock the service database */
-    ScmUnlockDatabase();
-
     if (hServiceKey != NULL)
         RegCloseKey(hServiceKey);
 
+    /* Unlock the service database */
+    ScmUnlockDatabase();
+
     DPRINT("RChangeServiceConfig2W() done (Error %lu)\n", dwError);
 
     return dwError;
@@ -4801,7 +5458,11 @@ DWORD RQueryServiceConfig2A(
     PSERVICE_HANDLE hSvc;
     PSERVICE lpService = NULL;
     HKEY hServiceKey = NULL;
+    DWORD dwRequiredSize = 0;
+    DWORD dwType = 0;
     LPWSTR lpDescriptionW = 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);
@@ -4855,7 +5516,7 @@ DWORD RQueryServiceConfig2A(
 
         *pcbBytesNeeded = sizeof(SERVICE_DESCRIPTIONA);
         if (dwError == ERROR_SUCCESS)
-            *pcbBytesNeeded += ((wcslen(lpDescriptionW) + 1) * sizeof(WCHAR));
+            *pcbBytesNeeded += (DWORD)((wcslen(lpDescriptionW) + 1) * sizeof(WCHAR));
 
         if (cbBufSize < *pcbBytesNeeded)
         {
@@ -4872,7 +5533,7 @@ DWORD RQueryServiceConfig2A(
                                 lpDescriptionW,
                                 -1,
                                 lpStr,
-                                wcslen(lpDescriptionW),
+                                (int)wcslen(lpDescriptionW),
                                 NULL,
                                 NULL);
             lpServiceDescription->lpDescription = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceDescription);
@@ -4881,14 +5542,123 @@ DWORD RQueryServiceConfig2A(
         {
             lpServiceDescription->lpDescription = NULL;
             dwError = ERROR_SUCCESS;
-            goto done;
         }
     }
-    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 = NULL;
+
+        /* Query value length */
+        dwError = RegQueryValueExW(hServiceKey,
+                                   L"FailureActions",
+                                   NULL,
+                                   &dwType,
+                                   NULL,
+                                   &dwRequiredSize);
+        if (dwError != ERROR_SUCCESS &&
+            dwError != ERROR_MORE_DATA &&
+            dwError != ERROR_FILE_NOT_FOUND)
+            goto done;
+
+        dwRequiredSize = (dwType == REG_BINARY) ? max(sizeof(SERVICE_FAILURE_ACTIONSA), dwRequiredSize)
+                                                : sizeof(SERVICE_FAILURE_ACTIONSA);
+
+        /* Get the strings */
+        ScmReadString(hServiceKey,
+                      L"FailureCommand",
+                      &lpFailureCommandW);
+
+        ScmReadString(hServiceKey,
+                      L"RebootMessage",
+                      &lpRebootMessageW);
+
+        if (lpRebootMessageW)
+            dwRequiredSize += (DWORD)((wcslen(lpRebootMessageW) + 1) * sizeof(WCHAR));
+
+        if (lpFailureCommandW)
+            dwRequiredSize += (DWORD)((wcslen(lpFailureCommandW) + 1) * sizeof(WCHAR));
+
+        if (cbBufSize < dwRequiredSize)
+        {
+            *pcbBytesNeeded = dwRequiredSize;
+            dwError = ERROR_INSUFFICIENT_BUFFER;
+            goto done;
+        }
+
+        /* Now we can fill the buffer */
+        if (dwError != ERROR_FILE_NOT_FOUND && dwType == REG_BINARY)
+        {
+            dwError = RegQueryValueExW(hServiceKey,
+                                       L"FailureActions",
+                                       NULL,
+                                       NULL,
+                                       (LPBYTE)lpFailureActions,
+                                       &dwRequiredSize);
+            if (dwError != ERROR_SUCCESS && dwError != ERROR_FILE_NOT_FOUND)
+                goto done;
+
+            if (dwRequiredSize < sizeof(SERVICE_FAILURE_ACTIONSA))
+                dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSA);
+        }
+        else
+        {
+            /*
+             * The value of the error doesn't really matter, the only
+             * important thing is that it must be != ERROR_SUCCESS .
+             */
+            dwError = ERROR_INVALID_DATA;
+        }
+
+        if (dwError == ERROR_SUCCESS)
+        {
+            lpFailureActions->cActions = min(lpFailureActions->cActions, (dwRequiredSize - sizeof(SERVICE_FAILURE_ACTIONSA)) / sizeof(SC_ACTION));
+
+            /* Here lpFailureActions->lpsaActions contains an offset. The conversion is done by the caller. */
+            lpFailureActions->lpsaActions = (lpFailureActions->cActions > 0 ? (LPSC_ACTION)(ULONG_PTR)sizeof(SERVICE_FAILURE_ACTIONSA) : 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,
+                                (int)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,
+                                (int)wcslen(lpFailureCommandW),
+                                NULL,
+                                NULL);
+            lpFailureActions->lpCommand = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
+            /* lpStr += strlen(lpStr) + 1; */
+        }
+
+        dwError = ERROR_SUCCESS;
     }
 
 done:
@@ -4898,10 +5668,16 @@ done:
     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);
 
-    DPRINT("RQueryServiceConfig2W() done (Error %lu)\n", dwError);
+    DPRINT("RQueryServiceConfig2A() done (Error %lu)\n", dwError);
 
     return dwError;
 }
@@ -4919,10 +5695,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");
 
@@ -4975,7 +5752,7 @@ DWORD RQueryServiceConfig2W(
 
         *pcbBytesNeeded = sizeof(SERVICE_DESCRIPTIONW);
         if (dwError == ERROR_SUCCESS)
-            *pcbBytesNeeded += ((wcslen(lpDescription) + 1) * sizeof(WCHAR));
+            *pcbBytesNeeded += (DWORD)((wcslen(lpDescription) + 1) * sizeof(WCHAR));
 
         if (cbBufSize < *pcbBytesNeeded)
         {
@@ -4997,26 +5774,38 @@ DWORD RQueryServiceConfig2W(
     }
     else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
     {
-        LPWSTR lpStr;
         LPSERVICE_FAILURE_ACTIONSW lpFailureActions = (LPSERVICE_FAILURE_ACTIONSW)lpBuffer;
+        LPWSTR lpStr = NULL;
 
-        UNIMPLEMENTED;
-
-        dwError = ScmReadString(hServiceKey,
-                                L"FailureCommand",
-                                &lpFailureCommand);
+        /* Query value length */
+        dwError = RegQueryValueExW(hServiceKey,
+                                   L"FailureActions",
+                                   NULL,
+                                   &dwType,
+                                   NULL,
+                                   &dwRequiredSize);
+        if (dwError != ERROR_SUCCESS &&
+            dwError != ERROR_MORE_DATA &&
+            dwError != ERROR_FILE_NOT_FOUND)
+            goto done;
 
-        dwError = ScmReadString(hServiceKey,
-                                L"RebootMessage",
-                                &lpRebootMessage);
+        dwRequiredSize = (dwType == REG_BINARY) ? max(sizeof(SERVICE_FAILURE_ACTIONSW), dwRequiredSize)
+                                                : sizeof(SERVICE_FAILURE_ACTIONSW);
 
-        dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW);
+        /* Get the strings */
+        ScmReadString(hServiceKey,
+                      L"FailureCommand",
+                      &lpFailureCommand);
 
-        if (lpFailureCommand)
-            dwRequiredSize += (wcslen(lpFailureCommand) + 1) * sizeof(WCHAR);
+        ScmReadString(hServiceKey,
+                      L"RebootMessage",
+                      &lpRebootMessage);
 
         if (lpRebootMessage)
-            dwRequiredSize += (wcslen(lpRebootMessage) + 1) * sizeof(WCHAR);
+            dwRequiredSize += (DWORD)((wcslen(lpRebootMessage) + 1) * sizeof(WCHAR));
+
+        if (lpFailureCommand)
+            dwRequiredSize += (DWORD)((wcslen(lpFailureCommand) + 1) * sizeof(WCHAR));
 
         if (cbBufSize < dwRequiredSize)
         {
@@ -5025,28 +5814,65 @@ DWORD RQueryServiceConfig2W(
             goto done;
         }
 
-        lpFailureActions->cActions = 0;
-        lpFailureActions->dwResetPeriod = 0;
-        lpFailureActions->lpCommand = NULL;
+        /* Now we can fill the buffer */
+        if (dwError != ERROR_FILE_NOT_FOUND && dwType == REG_BINARY)
+        {
+            dwError = RegQueryValueExW(hServiceKey,
+                                       L"FailureActions",
+                                       NULL,
+                                       NULL,
+                                       (LPBYTE)lpFailureActions,
+                                       &dwRequiredSize);
+            if (dwError != ERROR_SUCCESS && dwError != ERROR_FILE_NOT_FOUND)
+                goto done;
+
+            if (dwRequiredSize < sizeof(SERVICE_FAILURE_ACTIONSW))
+                dwRequiredSize = sizeof(SERVICE_FAILURE_ACTIONSW);
+        }
+        else
+        {
+            /*
+             * The value of the error doesn't really matter, the only
+             * important thing is that it must be != ERROR_SUCCESS .
+             */
+            dwError = ERROR_INVALID_DATA;
+        }
+
+        if (dwError == ERROR_SUCCESS)
+        {
+            lpFailureActions->cActions = min(lpFailureActions->cActions, (dwRequiredSize - sizeof(SERVICE_FAILURE_ACTIONSW)) / sizeof(SC_ACTION));
+
+            /* Here lpFailureActions->lpsaActions contains an offset. The conversion is done by the caller. */
+            lpFailureActions->lpsaActions = (lpFailureActions->cActions > 0 ? (LPSC_ACTION)(ULONG_PTR)sizeof(SERVICE_FAILURE_ACTIONSW) : 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:
@@ -5127,7 +5953,12 @@ DWORD RQueryServiceStatusEx(
                   &lpService->Status,
                   sizeof(SERVICE_STATUS));
 
-    lpStatus->dwProcessId = (lpService->lpImage != NULL) ? lpService->lpImage->dwProcessId : 0; /* FIXME */
+    /* Copy the service process ID */
+    if ((lpService->Status.dwCurrentState == SERVICE_STOPPED) || (lpService->lpImage == NULL))
+        lpStatus->dwProcessId = 0;
+    else
+        lpStatus->dwProcessId = lpService->lpImage->dwProcessId;
+
     lpStatus->dwServiceFlags = 0;                      /* FIXME */
 
     /* Unlock the service database */
@@ -5151,6 +5982,7 @@ DWORD REnumServicesStatusExA(
     LPCSTR pszGroupName)
 {
     LPENUM_SERVICE_STATUS_PROCESSW lpStatusPtrW = NULL;
+    LPENUM_SERVICE_STATUS_PROCESSW lpStatusPtrIncrW;
     LPENUM_SERVICE_STATUS_PROCESSA lpStatusPtrA = NULL;
     LPWSTR lpStringPtrW;
     LPSTR lpStringPtrA;
@@ -5160,13 +5992,19 @@ DWORD REnumServicesStatusExA(
 
     DPRINT("REnumServicesStatusExA() called\n");
 
+    if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
+    {
+        return ERROR_INVALID_ADDRESS;
+    }
+
     if (pszGroupName)
     {
         pszGroupNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (strlen(pszGroupName) + 1) * sizeof(WCHAR));
         if (!pszGroupNameW)
         {
              DPRINT("Failed to allocate buffer!\n");
-             return ERROR_NOT_ENOUGH_MEMORY;
+             dwError = ERROR_NOT_ENOUGH_MEMORY;
+             goto Done;
         }
 
         MultiByteToWideChar(CP_ACP,
@@ -5174,7 +6012,7 @@ DWORD REnumServicesStatusExA(
                             pszGroupName,
                             -1,
                             pszGroupNameW,
-                            strlen(pszGroupName) + 1);
+                            (int)(strlen(pszGroupName) + 1));
     }
 
     if ((cbBufSize > 0) && (lpBuffer))
@@ -5183,7 +6021,8 @@ DWORD REnumServicesStatusExA(
         if (!lpStatusPtrW)
         {
             DPRINT("Failed to allocate buffer!\n");
-            return ERROR_NOT_ENOUGH_MEMORY;
+            dwError = ERROR_NOT_ENOUGH_MEMORY;
+            goto Done;
         }
     }
 
@@ -5202,6 +6041,7 @@ DWORD REnumServicesStatusExA(
     if (*lpServicesReturned == 0)
         goto Done;
 
+    lpStatusPtrIncrW = lpStatusPtrW;
     lpStatusPtrA = (LPENUM_SERVICE_STATUS_PROCESSA)lpBuffer;
     lpStringPtrA = (LPSTR)((ULONG_PTR)lpBuffer +
                   *lpServicesReturned * sizeof(ENUM_SERVICE_STATUS_PROCESSA));
@@ -5216,7 +6056,7 @@ DWORD REnumServicesStatusExA(
                             lpStringPtrW,
                             -1,
                             lpStringPtrA,
-                            wcslen(lpStringPtrW),
+                            (int)wcslen(lpStringPtrW),
                             0,
                             0);
 
@@ -5230,7 +6070,7 @@ DWORD REnumServicesStatusExA(
                             lpStringPtrW,
                             -1,
                             lpStringPtrA,
-                            wcslen(lpStringPtrW),
+                            (int)wcslen(lpStringPtrW),
                             0,
                             0);
 
@@ -5240,15 +6080,19 @@ DWORD REnumServicesStatusExA(
 
         /* Copy the status information */
         memcpy(&lpStatusPtrA->ServiceStatusProcess,
-               &lpStatusPtrW->ServiceStatusProcess,
+               &lpStatusPtrIncrW->ServiceStatusProcess,
                sizeof(SERVICE_STATUS));
 
-        lpStatusPtrA->ServiceStatusProcess.dwProcessId = lpStatusPtrW->ServiceStatusProcess.dwProcessId; /* FIXME */
+        /* Copy the service process ID */
+        lpStatusPtrA->ServiceStatusProcess.dwProcessId = lpStatusPtrIncrW->ServiceStatusProcess.dwProcessId;
+
         lpStatusPtrA->ServiceStatusProcess.dwServiceFlags = 0; /* FIXME */
+
+        lpStatusPtrIncrW++;
         lpStatusPtrA++;
     }
 
-Done:;
+Done:
     if (pszGroupNameW)
         HeapFree(GetProcessHeap(), 0, pszGroupNameW);
 
@@ -5302,19 +6146,23 @@ DWORD REnumServicesStatusExW(
         return ERROR_INVALID_HANDLE;
     }
 
+    if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
+    {
+        return ERROR_INVALID_ADDRESS;
+    }
+
     *pcbBytesNeeded = 0;
     *lpServicesReturned = 0;
 
     if ((dwServiceType == 0) ||
-        ((dwServiceType & ~(SERVICE_DRIVER | SERVICE_WIN32)) != 0))
+        ((dwServiceType & ~SERVICE_TYPE_ALL) != 0))
     {
         DPRINT("Not a valid Service Type!\n");
         return ERROR_INVALID_PARAMETER;
     }
 
-    if ((dwServiceState != SERVICE_ACTIVE) &&
-        (dwServiceState != SERVICE_INACTIVE) &&
-        (dwServiceState != SERVICE_STATE_ALL))
+    if ((dwServiceState == 0) ||
+        ((dwServiceState & ~SERVICE_STATE_ALL) != 0))
     {
         DPRINT("Not a valid Service State!\n");
         return ERROR_INVALID_PARAMETER;
@@ -5373,14 +6221,14 @@ DWORD REnumServicesStatusExW(
             else
             {
                 if ((CurrentService->lpGroup == NULL) ||
-                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
                     continue;
             }
         }
 
         dwSize = sizeof(ENUM_SERVICE_STATUS_PROCESSW) +
-                 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
-                 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
+                 (DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                 (DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
 
         if (dwRequiredSize + dwSize <= cbBufSize)
         {
@@ -5428,14 +6276,14 @@ DWORD REnumServicesStatusExW(
             else
             {
                 if ((CurrentService->lpGroup == NULL) ||
-                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
                     continue;
             }
         }
 
         dwRequiredSize += (sizeof(ENUM_SERVICE_STATUS_PROCESSW) +
-                           ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
-                           ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR)));
+                           (DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                           (DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR)));
 
         dwError = ERROR_MORE_DATA;
     }
@@ -5488,14 +6336,14 @@ DWORD REnumServicesStatusExW(
             else
             {
                 if ((CurrentService->lpGroup == NULL) ||
-                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
                     continue;
             }
         }
 
         dwSize = sizeof(ENUM_SERVICE_STATUS_PROCESSW) +
-                 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
-                 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
+                 (DWORD)((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                 (DWORD)((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
 
         if (dwRequiredSize + dwSize <= cbBufSize)
         {
@@ -5515,8 +6363,13 @@ DWORD REnumServicesStatusExW(
             memcpy(&lpStatusPtr->ServiceStatusProcess,
                    &CurrentService->Status,
                    sizeof(SERVICE_STATUS));
-            lpStatusPtr->ServiceStatusProcess.dwProcessId =
-                (CurrentService->lpImage != NULL) ? CurrentService->lpImage->dwProcessId : 0; /* FIXME */
+
+            /* Copy the service process ID */
+            if ((CurrentService->Status.dwCurrentState == SERVICE_STOPPED) || (CurrentService->lpImage == NULL))
+                lpStatusPtr->ServiceStatusProcess.dwProcessId = 0;
+            else
+                lpStatusPtr->ServiceStatusProcess.dwProcessId = CurrentService->lpImage->dwProcessId;
+
             lpStatusPtr->ServiceStatusProcess.dwServiceFlags = 0; /* FIXME */
 
             lpStatusPtr++;
@@ -5535,7 +6388,7 @@ DWORD REnumServicesStatusExW(
             *lpResumeIndex = 0;
     }
 
-Done:;
+Done:
     /* Unlock the service database */
     ScmUnlockDatabase();
 
@@ -5717,11 +6570,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);
 }