[SERVICES]
[reactos.git] / reactos / base / system / services / rpcserver.c
index f36fa39..4dea07a 100644 (file)
@@ -405,6 +405,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 +483,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,8 +501,8 @@ 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");
+        return ERROR_NOT_ENOUGH_MEMORY;
     }
 
     ExpandedLen = NtPathName.Length / sizeof(WCHAR);
@@ -550,7 +555,6 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
             if (BufferSize > 0xFFFD)
             {
                 DPRINT("Too large buffer required\n");
-                *RelativeName = 0;
 
                 if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
                 HeapFree(GetProcessHeap(), 0, Expanded);
@@ -635,14 +639,11 @@ ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
     }
     else
     {
+        /* Failure */
         DPRINT("Error, Status = %08X\n", Status);
         HeapFree(GetProcessHeap(), 0, Expanded);
         return ERROR_INVALID_PARAMETER;
     }
-
-    /* Failure */
-    *RelativeName = NULL;
-    return ERROR_INVALID_PARAMETER;
 }
 
 
@@ -1114,7 +1115,7 @@ DWORD RControlService(
             DesiredAccess = SERVICE_PAUSE_CONTINUE;
             break;
 
-        case SERVICE_INTERROGATE:
+        case SERVICE_CONTROL_INTERROGATE:
             DesiredAccess = SERVICE_INTERROGATE;
             break;
 
@@ -1237,9 +1238,6 @@ DWORD RControlService(
                       sizeof(SERVICE_STATUS));
     }
 
-    if ((dwError == ERROR_SUCCESS) && (pcbBytesNeeded))
-        dwError = ERROR_DEPENDENT_SERVICES_RUNNING;
-
     return dwError;
 }
 
@@ -1619,6 +1617,9 @@ DWORD RSetServiceStatus(
     LPSERVICE_STATUS lpServiceStatus)
 {
     PSERVICE lpService;
+    DWORD dwPreviousState;
+    LPCWSTR lpErrorStrings[2];
+    WCHAR szErrorBuffer[32];
 
     DPRINT("RSetServiceStatus() called\n");
     DPRINT("hServiceStatus = %p\n", hServiceStatus);
@@ -1668,6 +1669,9 @@ DWORD RSetServiceStatus(
     /* Lock the service database exclusively */
     ScmLockDatabaseExclusive();
 
+    /* Save the current service state */
+    dwPreviousState = lpService->Status.dwCurrentState;
+
     RtlCopyMemory(&lpService->Status,
                   lpServiceStatus,
                   sizeof(SERVICE_STATUS));
@@ -1675,6 +1679,22 @@ DWORD RSetServiceStatus(
     /* Unlock the service database */
     ScmUnlockDatabase();
 
+    /* Log a failed service stop */
+    if ((lpServiceStatus->dwCurrentState == SERVICE_STOPPED) &&
+        (dwPreviousState != SERVICE_STOPPED))
+    {
+        if (lpServiceStatus->dwWin32ExitCode != ERROR_SUCCESS)
+        {
+            swprintf(szErrorBuffer, L"%lu", lpServiceStatus->dwWin32ExitCode);
+            lpErrorStrings[0] = lpService->lpDisplayName;
+            lpErrorStrings[1] = szErrorBuffer;
+
+            ScmLogError(EVENT_SERVICE_EXIT_FAILED,
+                        2,
+                        lpErrorStrings);
+        }
+    }
+
     DPRINT("Set %S to %lu\n", lpService->lpDisplayName, lpService->Status.dwCurrentState);
     DPRINT("RSetServiceStatus() done\n");
 
@@ -2481,207 +2501,16 @@ DWORD REnumServicesStatusW(
     LPBOUNDED_DWORD_256K lpServicesReturned,
     LPBOUNDED_DWORD_256K lpResumeHandle)
 {
-    PMANAGER_HANDLE hManager;
-    PSERVICE lpService;
-    DWORD dwError = ERROR_SUCCESS;
-    PLIST_ENTRY ServiceEntry;
-    PSERVICE CurrentService;
-    DWORD dwState;
-    DWORD dwRequiredSize;
-    DWORD dwServiceCount;
-    DWORD dwSize;
-    DWORD dwLastResumeCount = 0;
-    LPENUM_SERVICE_STATUSW lpStatusPtr;
-    LPWSTR lpStringPtr;
-
-    DPRINT("REnumServicesStatusW() called\n");
-
-    if (ScmShutdown)
-        return ERROR_SHUTDOWN_IN_PROGRESS;
-
-    hManager = ScmGetServiceManagerFromHandle(hSCManager);
-    if (hManager == NULL)
-    {
-        DPRINT1("Invalid service manager handle!\n");
-        return ERROR_INVALID_HANDLE;
-    }
-
-
-    *pcbBytesNeeded = 0;
-    *lpServicesReturned = 0;
-
-    if ((dwServiceType == 0) ||
-        ((dwServiceType & ~(SERVICE_DRIVER | SERVICE_WIN32)) != 0))
-    {
-        DPRINT("Not a valid Service Type!\n");
-        return ERROR_INVALID_PARAMETER;
-    }
-
-    if ((dwServiceState != SERVICE_ACTIVE) &&
-        (dwServiceState != SERVICE_INACTIVE) &&
-        (dwServiceState != SERVICE_STATE_ALL))
-    {
-        DPRINT("Not a valid Service State!\n");
-        return ERROR_INVALID_PARAMETER;
-    }
-
-    /* Check access rights */
-    if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
-                                  SC_MANAGER_ENUMERATE_SERVICE))
-    {
-        DPRINT("Insufficient access rights! 0x%lx\n",
-                hManager->Handle.DesiredAccess);
-        return ERROR_ACCESS_DENIED;
-    }
-
-    if (lpResumeHandle)
-        dwLastResumeCount = *lpResumeHandle;
-
-    /* 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;
-
-        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;
+    /* Enumerate all the services, not regarding of their group */
+    return REnumServiceGroupW(hSCManager,
+                              dwServiceType,
+                              dwServiceState,
+                              lpBuffer,
+                              dwBufSize,
+                              pcbBytesNeeded,
+                              lpServicesReturned,
+                              lpResumeHandle,
+                              NULL);
 }
 
 
@@ -2970,7 +2799,7 @@ DWORD RQueryServiceConfigW(
 
         lpConfig->lpDependencies = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
         if (lpDependencies != NULL)
-            lpStr += dwDependenciesLength * sizeof(WCHAR);
+            lpStr += dwDependenciesLength;
         else
             lpStr += (wcslen(lpStr) + 1);
 
@@ -4086,7 +3915,7 @@ DWORD RQueryServiceConfigA(
                         &lpDependencies,
                         &dwDependenciesLength);
 
-    dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGW);
+    dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGA);
 
     if (lpImagePath != NULL)
         dwRequiredSize += wcslen(lpImagePath) + 1;
@@ -4129,7 +3958,7 @@ DWORD RQueryServiceConfigA(
         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)
         {
@@ -4574,31 +4403,274 @@ 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");
 
-//
-// WARNING: This function is untested
-//
-/* Function 36 */
-DWORD RChangeServiceConfig2A(
-    SC_RPC_HANDLE hService,
-    SC_RPC_CONFIG_INFOA Info)
-{
-    SC_RPC_CONFIG_INFOW InfoW;
-    DWORD dwRet, dwLength;
-    PVOID ptr = NULL;
+    if (ScmShutdown)
+        return ERROR_SHUTDOWN_IN_PROGRESS;
 
-    DPRINT("RChangeServiceConfig2A() called\n");
-    DPRINT("dwInfoLevel = %lu\n", Info.dwInfoLevel);
+    hManager = ScmGetServiceManagerFromHandle(hSCManager);
+    if (hManager == NULL)
+    {
+        DPRINT1("Invalid service manager handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
 
-    InfoW.dwInfoLevel = Info.dwInfoLevel;
+    *pcbBytesNeeded = 0;
+    *lpServicesReturned = 0;
 
-    if (InfoW.dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
+    if ((dwServiceType == 0) ||
+        ((dwServiceType & ~(SERVICE_DRIVER | SERVICE_WIN32)) != 0))
     {
-        LPSERVICE_DESCRIPTIONW lpServiceDescriptonW;
+        DPRINT("Not a valid Service Type!\n");
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if ((dwServiceState != SERVICE_ACTIVE) &&
+        (dwServiceState != SERVICE_INACTIVE) &&
+        (dwServiceState != SERVICE_STATE_ALL))
+    {
+        DPRINT("Not a valid Service State!\n");
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /* Check access rights */
+    if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
+                                  SC_MANAGER_ENUMERATE_SERVICE))
+    {
+        DPRINT("Insufficient access rights! 0x%lx\n",
+                hManager->Handle.DesiredAccess);
+        return ERROR_ACCESS_DENIED;
+    }
+
+    if (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) +
+                 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                 ((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) +
+                           ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                           ((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) +
+                 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
+                 ((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,
+    SC_RPC_CONFIG_INFOA Info)
+{
+    SC_RPC_CONFIG_INFOW InfoW;
+    DWORD dwRet, dwLength;
+    PVOID ptr = NULL;
+
+    DPRINT("RChangeServiceConfig2A() called\n");
+    DPRINT("dwInfoLevel = %lu\n", Info.dwInfoLevel);
+
+    InfoW.dwInfoLevel = Info.dwInfoLevel;
+
+    if (InfoW.dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
+    {
+        LPSERVICE_DESCRIPTIONW lpServiceDescriptonW;
         //LPSERVICE_DESCRIPTIONA lpServiceDescriptonA;
 
         //lpServiceDescriptonA = Info.psd;
@@ -4753,7 +4825,6 @@ DWORD RChangeServiceConfig2W(
         LPSERVICE_DESCRIPTIONW lpServiceDescription;
 
         lpServiceDescription = (LPSERVICE_DESCRIPTIONW)Info.psd;
-        lpServiceDescription->lpDescription = (LPWSTR)((ULONG_PTR)lpServiceDescription + sizeof(LPSERVICE_DESCRIPTIONW));
 
         if (lpServiceDescription != NULL &&
             lpServiceDescription->lpDescription != NULL)
@@ -4801,7 +4872,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);
@@ -4881,14 +4956,119 @@ 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 += (wcslen(lpRebootMessageW) + 1) * sizeof(WCHAR);
+
+        if (lpFailureCommandW)
+            dwRequiredSize += (wcslen(lpFailureCommandW) + 1) * sizeof(WCHAR);
+
+        if (cbBufSize < dwRequiredSize)
+        {
+            *pcbBytesNeeded = dwRequiredSize;
+            dwError = ERROR_INSUFFICIENT_BUFFER;
+            goto done;
+        }
+
+        /* Now we can fill the buffer */
+        if (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
+        {
+            dwError = ERROR_UNSUPPORTED_TYPE;
+        }
+
+        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,
+                                wcslen(lpRebootMessageW),
+                                NULL,
+                                NULL);
+            lpFailureActions->lpRebootMsg = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
+            lpStr += strlen(lpStr) + 1;
+        }
+
+        if (lpFailureCommandW)
+        {
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpFailureCommandW,
+                                -1,
+                                lpStr,
+                                wcslen(lpFailureCommandW),
+                                NULL,
+                                NULL);
+            lpFailureActions->lpCommand = (LPSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpFailureActions);
+            /* lpStr += strlen(lpStr) + 1; */
+        }
+
+        dwError = ERROR_SUCCESS;
     }
 
 done:
@@ -4898,10 +5078,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 +5105,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");
 
@@ -4997,27 +5184,39 @@ 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);
 
+        if (lpFailureCommand)
+            dwRequiredSize += (wcslen(lpFailureCommand) + 1) * sizeof(WCHAR);
+
         if (cbBufSize < dwRequiredSize)
         {
             *pcbBytesNeeded = dwRequiredSize;
@@ -5025,28 +5224,61 @@ DWORD RQueryServiceConfig2W(
             goto done;
         }
 
-        lpFailureActions->cActions = 0;
-        lpFailureActions->dwResetPeriod = 0;
-        lpFailureActions->lpCommand = NULL;
+        /* Now we can fill the buffer */
+        if (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
+        {
+            dwError = ERROR_UNSUPPORTED_TYPE;
+        }
+
+        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:
@@ -5373,7 +5605,7 @@ DWORD REnumServicesStatusExW(
             else
             {
                 if ((CurrentService->lpGroup == NULL) ||
-                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
                     continue;
             }
         }
@@ -5428,7 +5660,7 @@ DWORD REnumServicesStatusExW(
             else
             {
                 if ((CurrentService->lpGroup == NULL) ||
-                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
                     continue;
             }
         }
@@ -5488,7 +5720,7 @@ DWORD REnumServicesStatusExW(
             else
             {
                 if ((CurrentService->lpGroup == NULL) ||
-                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
+                    _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName) != 0)
                     continue;
             }
         }
@@ -5717,11 +5949,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);
 }