/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS system libraries
 * FILE:            lib/advapi32/service/scm.c
 * PURPOSE:         Service control manager functions
 * PROGRAMMER:      Emanuele Aliberti
 *                  Eric Kohl
 *                  Pierre Schweitzer
 */

/* INCLUDES ******************************************************************/

#include <advapi32.h>
WINE_DEFAULT_DEBUG_CHANNEL(advapi);

NTSTATUS
WINAPI
SystemFunction004(
    const struct ustring *in,
    const struct ustring *key,
    struct ustring *out);

NTSTATUS
WINAPI
SystemFunction028(
    IN PVOID ContextHandle,
    OUT LPBYTE SessionKey);

/* FUNCTIONS *****************************************************************/

handle_t __RPC_USER
SVCCTL_HANDLEA_bind(SVCCTL_HANDLEA szMachineName)
{
    handle_t hBinding = NULL;
    RPC_CSTR pszStringBinding;
    RPC_STATUS status;

    TRACE("SVCCTL_HANDLEA_bind(%s)\n",
          debugstr_a(szMachineName));

    status = RpcStringBindingComposeA(NULL,
                                      (RPC_CSTR)"ncacn_np",
                                      (RPC_CSTR)szMachineName,
                                      (RPC_CSTR)"\\pipe\\ntsvcs",
                                      NULL,
                                      &pszStringBinding);
    if (status != RPC_S_OK)
    {
        ERR("RpcStringBindingCompose returned 0x%x\n", status);
        return NULL;
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBindingA(pszStringBinding,
                                          &hBinding);
    if (status != RPC_S_OK)
    {
        ERR("RpcBindingFromStringBinding returned 0x%x\n", status);
    }

    status = RpcStringFreeA(&pszStringBinding);
    if (status != RPC_S_OK)
    {
        ERR("RpcStringFree returned 0x%x\n", status);
    }

    return hBinding;
}


void __RPC_USER
SVCCTL_HANDLEA_unbind(SVCCTL_HANDLEA szMachineName,
                      handle_t hBinding)
{
    RPC_STATUS status;

    TRACE("SVCCTL_HANDLEA_unbind(%s %p)\n",
          debugstr_a(szMachineName), hBinding);

    status = RpcBindingFree(&hBinding);
    if (status != RPC_S_OK)
    {
        ERR("RpcBindingFree returned 0x%x\n", status);
    }
}


handle_t __RPC_USER
SVCCTL_HANDLEW_bind(SVCCTL_HANDLEW szMachineName)
{
    handle_t hBinding = NULL;
    RPC_WSTR pszStringBinding;
    RPC_STATUS status;

    TRACE("SVCCTL_HANDLEW_bind(%s)\n",
          debugstr_w(szMachineName));

    status = RpcStringBindingComposeW(NULL,
                                      L"ncacn_np",
                                      szMachineName,
                                      L"\\pipe\\ntsvcs",
                                      NULL,
                                      &pszStringBinding);
    if (status != RPC_S_OK)
    {
        ERR("RpcStringBindingCompose returned 0x%x\n", status);
        return NULL;
    }

    /* Set the binding handle that will be used to bind to the server. */
    status = RpcBindingFromStringBindingW(pszStringBinding,
                                          &hBinding);
    if (status != RPC_S_OK)
    {
        ERR("RpcBindingFromStringBinding returned 0x%x\n", status);
    }

    status = RpcStringFreeW(&pszStringBinding);
    if (status != RPC_S_OK)
    {
        ERR("RpcStringFree returned 0x%x\n", status);
    }

    return hBinding;
}


void __RPC_USER
SVCCTL_HANDLEW_unbind(SVCCTL_HANDLEW szMachineName,
                      handle_t hBinding)
{
    RPC_STATUS status;

    TRACE("SVCCTL_HANDLEW_unbind(%s %p)\n",
          debugstr_w(szMachineName), hBinding);

    status = RpcBindingFree(&hBinding);
    if (status != RPC_S_OK)
    {
        ERR("RpcBindingFree returned 0x%x\n", status);
    }
}


DWORD
ScmRpcStatusToWinError(RPC_STATUS Status)
{
    TRACE("ScmRpcStatusToWinError(%lx)\n",
          Status);

    switch (Status)
    {
        case STATUS_ACCESS_VIOLATION:
        case RPC_S_INVALID_BINDING:
        case RPC_X_SS_IN_NULL_CONTEXT:
            return ERROR_INVALID_HANDLE;

        case RPC_X_ENUM_VALUE_OUT_OF_RANGE:
        case RPC_X_BYTE_COUNT_TOO_SMALL:
            return ERROR_INVALID_PARAMETER;

        case RPC_X_NULL_REF_POINTER:
            return ERROR_INVALID_ADDRESS;

        default:
            return (DWORD)Status;
    }
}


static
DWORD
ScmEncryptPassword(
    _In_ PVOID ContextHandle,
    _In_ PCWSTR pClearTextPassword,
    _Out_ PBYTE *pEncryptedPassword,
    _Out_ PDWORD pEncryptedPasswordSize)
{
    struct ustring inData, keyData, outData;
    BYTE SessionKey[16];
    PBYTE pBuffer;
    NTSTATUS Status;

    /* Get the session key */
    Status = SystemFunction028(ContextHandle,
                               SessionKey);
    if (!NT_SUCCESS(Status))
    {
        ERR("SystemFunction028 failed (Status 0x%08lx)\n", Status);
        return RtlNtStatusToDosError(Status);
    }

    inData.Length = (wcslen(pClearTextPassword) + 1) * sizeof(WCHAR);
    inData.MaximumLength = inData.Length;
    inData.Buffer = (unsigned char *)pClearTextPassword;

    keyData.Length = sizeof(SessionKey);
    keyData.MaximumLength = keyData.Length;
    keyData.Buffer = SessionKey;

    outData.Length = 0;
    outData.MaximumLength = 0;
    outData.Buffer = NULL;

    /* Get the required buffer size */
    Status = SystemFunction004(&inData,
                               &keyData,
                               &outData);
    if (Status != STATUS_BUFFER_TOO_SMALL)
    {
        ERR("SystemFunction004 failed (Status 0x%08lx)\n", Status);
        return RtlNtStatusToDosError(Status);
    }

    /* Allocate a buffer for the encrypted password */
    pBuffer = HeapAlloc(GetProcessHeap(), 0, outData.Length);
    if (pBuffer == NULL)
        return ERROR_OUTOFMEMORY;

    outData.MaximumLength = outData.Length;
    outData.Buffer = pBuffer;

    /* Encrypt the password */
    Status = SystemFunction004(&inData,
                               &keyData,
                               &outData);
    if (!NT_SUCCESS(Status))
    {
        ERR("SystemFunction004 failed (Status 0x%08lx)\n", Status);
        HeapFree(GetProcessHeap(), 0, pBuffer);
        return RtlNtStatusToDosError(Status);
    }

    *pEncryptedPassword = outData.Buffer;
    *pEncryptedPasswordSize = outData.Length;

    return ERROR_SUCCESS;
}


/**********************************************************************
 *  ChangeServiceConfig2A
 *
 * @implemented
 */
BOOL WINAPI
ChangeServiceConfig2A(SC_HANDLE hService,
                      DWORD dwInfoLevel,
                      LPVOID lpInfo)
{
    SC_RPC_CONFIG_INFOA Info;
    DWORD dwError;

    TRACE("ChangeServiceConfig2A(%p %lu %p)\n",
          hService, dwInfoLevel, lpInfo);

    if (lpInfo == NULL) return TRUE;

    /* Fill relevant field of the Info structure */
    Info.dwInfoLevel = dwInfoLevel;
    switch (dwInfoLevel)
    {
        case SERVICE_CONFIG_DESCRIPTION:
            Info.psd = lpInfo;
            break;

        case SERVICE_CONFIG_FAILURE_ACTIONS:
            Info.psfa = lpInfo;
            break;

        default:
            WARN("Unknown info level 0x%lx\n", dwInfoLevel);
            SetLastError(ERROR_INVALID_PARAMETER);
            return FALSE;
    }

    RpcTryExcept
    {
        dwError = RChangeServiceConfig2A((SC_RPC_HANDLE)hService,
                                         Info);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RChangeServiceConfig2A() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  ChangeServiceConfig2W
 *
 * @implemented
 */
BOOL WINAPI
ChangeServiceConfig2W(SC_HANDLE hService,
                      DWORD dwInfoLevel,
                      LPVOID lpInfo)
{
    SC_RPC_CONFIG_INFOW Info;
    DWORD dwError;

    TRACE("ChangeServiceConfig2W(%p %lu %p)\n",
          hService, dwInfoLevel, lpInfo);

    if (lpInfo == NULL) return TRUE;

    /* Fill relevant field of the Info structure */
    Info.dwInfoLevel = dwInfoLevel;
    switch (dwInfoLevel)
    {
        case SERVICE_CONFIG_DESCRIPTION:
            Info.psd = lpInfo;
            break;

        case SERVICE_CONFIG_FAILURE_ACTIONS:
            Info.psfa = lpInfo;
            break;

        default:
            WARN("Unknown info level 0x%lx\n", dwInfoLevel);
            SetLastError(ERROR_INVALID_PARAMETER);
            return FALSE;
    }

    RpcTryExcept
    {
        dwError = RChangeServiceConfig2W((SC_RPC_HANDLE)hService,
                                         Info);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RChangeServiceConfig2W() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  ChangeServiceConfigA
 *
 * @implemented
 */
BOOL WINAPI
ChangeServiceConfigA(SC_HANDLE hService,
                     DWORD dwServiceType,
                     DWORD dwStartType,
                     DWORD dwErrorControl,
                     LPCSTR lpBinaryPathName,
                     LPCSTR lpLoadOrderGroup,
                     LPDWORD lpdwTagId,
                     LPCSTR lpDependencies,
                     LPCSTR lpServiceStartName,
                     LPCSTR lpPassword,
                     LPCSTR lpDisplayName)
{
    DWORD dwError;
    DWORD dwDependenciesLength = 0;
    SIZE_T cchLength;
    LPCSTR lpStr;
    DWORD dwPasswordSize = 0;
    LPWSTR lpPasswordW = NULL;
    LPBYTE lpEncryptedPassword = NULL;

    TRACE("ChangeServiceConfigA(%p %lu %lu %lu %s %s %p %s %s %s %s)\n",
          hService, dwServiceType, dwStartType, dwErrorControl, debugstr_a(lpBinaryPathName),
          debugstr_a(lpLoadOrderGroup), lpdwTagId, debugstr_a(lpDependencies),
          debugstr_a(lpServiceStartName), debugstr_a(lpPassword), debugstr_a(lpDisplayName));

    /* Calculate the Dependencies length*/
    if (lpDependencies != NULL)
    {
        lpStr = lpDependencies;
        while (*lpStr)
        {
            cchLength = strlen(lpStr) + 1;
            dwDependenciesLength += (DWORD)cchLength;
            lpStr = lpStr + cchLength;
        }
        dwDependenciesLength++;
    }

    if (lpPassword != NULL)
    {
        /* Convert the password to unicode */
        lpPasswordW = HeapAlloc(GetProcessHeap(),
                                HEAP_ZERO_MEMORY,
                                (strlen(lpPassword) + 1) * sizeof(WCHAR));
        if (lpPasswordW == NULL)
        {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }

        MultiByteToWideChar(CP_ACP,
                            0,
                            lpPassword,
                            -1,
                            lpPasswordW,
                            (int)(strlen(lpPassword) + 1));

        /* Encrypt the unicode password */
        dwError = ScmEncryptPassword(hService,
                                     lpPasswordW,
                                     &lpEncryptedPassword,
                                     &dwPasswordSize);
        if (dwError != ERROR_SUCCESS)
            goto done;
    }

    RpcTryExcept
    {
        dwError = RChangeServiceConfigA((SC_RPC_HANDLE)hService,
                                        dwServiceType,
                                        dwStartType,
                                        dwErrorControl,
                                        (LPSTR)lpBinaryPathName,
                                        (LPSTR)lpLoadOrderGroup,
                                        lpdwTagId,
                                        (LPBYTE)lpDependencies,
                                        dwDependenciesLength,
                                        (LPSTR)lpServiceStartName,
                                        lpEncryptedPassword,
                                        dwPasswordSize,
                                        (LPSTR)lpDisplayName);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

done:
    if (lpPasswordW != NULL)
    {
        /* Wipe and release the password buffers */
        SecureZeroMemory(lpPasswordW, (wcslen(lpPasswordW) + 1) * sizeof(WCHAR));
        HeapFree(GetProcessHeap(), 0, lpPasswordW);

        if (lpEncryptedPassword != NULL)
        {
            SecureZeroMemory(lpEncryptedPassword, dwPasswordSize);
            HeapFree(GetProcessHeap(), 0, lpEncryptedPassword);
        }
    }

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RChangeServiceConfigA() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  ChangeServiceConfigW
 *
 * @implemented
 */
BOOL WINAPI
ChangeServiceConfigW(SC_HANDLE hService,
                     DWORD dwServiceType,
                     DWORD dwStartType,
                     DWORD dwErrorControl,
                     LPCWSTR lpBinaryPathName,
                     LPCWSTR lpLoadOrderGroup,
                     LPDWORD lpdwTagId,
                     LPCWSTR lpDependencies,
                     LPCWSTR lpServiceStartName,
                     LPCWSTR lpPassword,
                     LPCWSTR lpDisplayName)
{
    DWORD dwError;
    DWORD dwDependenciesLength = 0;
    SIZE_T cchLength;
    LPCWSTR lpStr;
    DWORD dwPasswordSize = 0;
    LPBYTE lpEncryptedPassword = NULL;

    TRACE("ChangeServiceConfigW(%p %lu %lu %lu %s %s %p %s %s %s %s)\n",
          hService, dwServiceType, dwStartType, dwErrorControl, debugstr_w(lpBinaryPathName),
          debugstr_w(lpLoadOrderGroup), lpdwTagId, debugstr_w(lpDependencies),
          debugstr_w(lpServiceStartName), debugstr_w(lpPassword), debugstr_w(lpDisplayName));

    /* Calculate the Dependencies length*/
    if (lpDependencies != NULL)
    {
        lpStr = lpDependencies;
        while (*lpStr)
        {
            cchLength = wcslen(lpStr) + 1;
            dwDependenciesLength += (DWORD)cchLength;
            lpStr = lpStr + cchLength;
        }
        dwDependenciesLength++;
        dwDependenciesLength *= sizeof(WCHAR);
    }

    if (lpPassword != NULL)
    {
        dwError = ScmEncryptPassword(hService,
                                     lpPassword,
                                     &lpEncryptedPassword,
                                     &dwPasswordSize);
        if (dwError != ERROR_SUCCESS)
        {
            ERR("ScmEncryptPassword failed (Error %lu)\n", dwError);
            goto done;
        }
    }

    RpcTryExcept
    {
        dwError = RChangeServiceConfigW((SC_RPC_HANDLE)hService,
                                        dwServiceType,
                                        dwStartType,
                                        dwErrorControl,
                                        (LPWSTR)lpBinaryPathName,
                                        (LPWSTR)lpLoadOrderGroup,
                                        lpdwTagId,
                                        (LPBYTE)lpDependencies,
                                        dwDependenciesLength,
                                        (LPWSTR)lpServiceStartName,
                                        lpEncryptedPassword,
                                        dwPasswordSize,
                                        (LPWSTR)lpDisplayName);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

done:
    if (lpEncryptedPassword != NULL)
    {
        /* Wipe and release the password buffer */
        SecureZeroMemory(lpEncryptedPassword, dwPasswordSize);
        HeapFree(GetProcessHeap(), 0, lpEncryptedPassword);
    }

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RChangeServiceConfigW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  CloseServiceHandle
 *
 * @implemented
 */
BOOL WINAPI
CloseServiceHandle(SC_HANDLE hSCObject)
{
    DWORD dwError;

    TRACE("CloseServiceHandle(%p)\n",
          hSCObject);

    if (!hSCObject)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    RpcTryExcept
    {
        dwError = RCloseServiceHandle((LPSC_RPC_HANDLE)&hSCObject);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError)
    {
        TRACE("RCloseServiceHandle() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    TRACE("CloseServiceHandle() done\n");

    return TRUE;
}


/**********************************************************************
 *  ControlService
 *
 * @implemented
 */
BOOL WINAPI
ControlService(SC_HANDLE hService,
               DWORD dwControl,
               LPSERVICE_STATUS lpServiceStatus)
{
    DWORD dwError;

    TRACE("ControlService(%p %lu %p)\n",
          hService, dwControl, lpServiceStatus);

    RpcTryExcept
    {
        dwError = RControlService((SC_RPC_HANDLE)hService,
                                  dwControl,
                                  lpServiceStatus);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RControlService() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    TRACE("ControlService() done\n");

    return TRUE;
}


/**********************************************************************
 *  ControlServiceEx
 *
 * @unimplemented
 */
BOOL WINAPI
ControlServiceEx(IN SC_HANDLE hService,
                 IN DWORD dwControl,
                 IN DWORD dwInfoLevel,
                 IN OUT PVOID pControlParams)
{
    FIXME("ControlServiceEx(%p %lu %lu %p)\n",
          hService, dwControl, dwInfoLevel, pControlParams);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}


/**********************************************************************
 *  CreateServiceA
 *
 * @implemented
 */
SC_HANDLE WINAPI
CreateServiceA(SC_HANDLE hSCManager,
               LPCSTR lpServiceName,
               LPCSTR lpDisplayName,
               DWORD dwDesiredAccess,
               DWORD dwServiceType,
               DWORD dwStartType,
               DWORD dwErrorControl,
               LPCSTR lpBinaryPathName,
               LPCSTR lpLoadOrderGroup,
               LPDWORD lpdwTagId,
               LPCSTR lpDependencies,
               LPCSTR lpServiceStartName,
               LPCSTR lpPassword)
{
    SC_HANDLE hService = NULL;
    DWORD dwDependenciesLength = 0;
    DWORD dwError;
    SIZE_T cchLength;
    LPCSTR lpStr;
    DWORD dwPasswordSize = 0;
    LPWSTR lpPasswordW = NULL;
    LPBYTE lpEncryptedPassword = NULL;

    TRACE("CreateServiceA(%p %s %s %lx %lu %lu %lu %s %s %p %s %s %s)\n",
          hSCManager, debugstr_a(lpServiceName), debugstr_a(lpDisplayName),
          dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
          debugstr_a(lpBinaryPathName), debugstr_a(lpLoadOrderGroup), lpdwTagId,
          debugstr_a(lpDependencies), debugstr_a(lpServiceStartName), debugstr_a(lpPassword));

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return NULL;
    }

    /* Calculate the Dependencies length */
    if (lpDependencies != NULL)
    {
        lpStr = lpDependencies;
        while (*lpStr)
        {
            cchLength = strlen(lpStr) + 1;
            dwDependenciesLength += (DWORD)cchLength;
            lpStr = lpStr + cchLength;
        }
        dwDependenciesLength++;
    }

    if (lpPassword != NULL)
    {
        /* Convert the password to unicode */
        lpPasswordW = HeapAlloc(GetProcessHeap(),
                                HEAP_ZERO_MEMORY,
                                (strlen(lpPassword) + 1) * sizeof(WCHAR));
        if (lpPasswordW == NULL)
        {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }

        MultiByteToWideChar(CP_ACP,
                            0,
                            lpPassword,
                            -1,
                            lpPasswordW,
                            (int)(strlen(lpPassword) + 1));

        /* Encrypt the password */
        dwError = ScmEncryptPassword(hSCManager,
                                     lpPasswordW,
                                     &lpEncryptedPassword,
                                     &dwPasswordSize);
        if (dwError != ERROR_SUCCESS)
            goto done;
    }

    RpcTryExcept
    {
        dwError = RCreateServiceA((SC_RPC_HANDLE)hSCManager,
                                  (LPSTR)lpServiceName,
                                  (LPSTR)lpDisplayName,
                                  dwDesiredAccess,
                                  dwServiceType,
                                  dwStartType,
                                  dwErrorControl,
                                  (LPSTR)lpBinaryPathName,
                                  (LPSTR)lpLoadOrderGroup,
                                  lpdwTagId,
                                  (LPBYTE)lpDependencies,
                                  dwDependenciesLength,
                                  (LPSTR)lpServiceStartName,
                                  lpEncryptedPassword,
                                  dwPasswordSize,
                                  (SC_RPC_HANDLE *)&hService);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

done:
    if (lpPasswordW != NULL)
    {
        /* Wipe and release the password buffers */
        SecureZeroMemory(lpPasswordW, (wcslen(lpPasswordW) + 1) * sizeof(WCHAR));
        HeapFree(GetProcessHeap(), 0, lpPasswordW);

        if (lpEncryptedPassword != NULL)
        {
            SecureZeroMemory(lpEncryptedPassword, dwPasswordSize);
            HeapFree(GetProcessHeap(), 0, lpEncryptedPassword);
        }
    }

    SetLastError(dwError);
    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RCreateServiceA() failed (Error %lu)\n", dwError);
        return NULL;
    }

    return hService;
}


/**********************************************************************
 *  CreateServiceW
 *
 * @implemented
 */
SC_HANDLE WINAPI
CreateServiceW(SC_HANDLE hSCManager,
               LPCWSTR lpServiceName,
               LPCWSTR lpDisplayName,
               DWORD dwDesiredAccess,
               DWORD dwServiceType,
               DWORD dwStartType,
               DWORD dwErrorControl,
               LPCWSTR lpBinaryPathName,
               LPCWSTR lpLoadOrderGroup,
               LPDWORD lpdwTagId,
               LPCWSTR lpDependencies,
               LPCWSTR lpServiceStartName,
               LPCWSTR lpPassword)
{
    SC_HANDLE hService = NULL;
    DWORD dwDependenciesLength = 0;
    DWORD dwError;
    SIZE_T cchLength;
    LPCWSTR lpStr;
    DWORD dwPasswordSize = 0;
    LPBYTE lpEncryptedPassword = NULL;

    TRACE("CreateServiceW(%p %s %s %lx %lu %lu %lu %s %s %p %s %s %s)\n",
          hSCManager, debugstr_w(lpServiceName), debugstr_w(lpDisplayName),
          dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
          debugstr_w(lpBinaryPathName), debugstr_w(lpLoadOrderGroup), lpdwTagId,
          debugstr_w(lpDependencies), debugstr_w(lpServiceStartName), debugstr_w(lpPassword));

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return NULL;
    }

    /* Calculate the Dependencies length */
    if (lpDependencies != NULL)
    {
        lpStr = lpDependencies;
        while (*lpStr)
        {
            cchLength = wcslen(lpStr) + 1;
            dwDependenciesLength += (DWORD)cchLength;
            lpStr = lpStr + cchLength;
        }
        dwDependenciesLength++;
        dwDependenciesLength *= sizeof(WCHAR);
    }

    if (lpPassword != NULL)
    {
        /* Encrypt the password */
        dwError = ScmEncryptPassword(hSCManager,
                                     lpPassword,
                                     &lpEncryptedPassword,
                                     &dwPasswordSize);
        if (dwError != ERROR_SUCCESS)
            goto done;
    }

    RpcTryExcept
    {
        dwError = RCreateServiceW((SC_RPC_HANDLE)hSCManager,
                                  lpServiceName,
                                  lpDisplayName,
                                  dwDesiredAccess,
                                  dwServiceType,
                                  dwStartType,
                                  dwErrorControl,
                                  lpBinaryPathName,
                                  lpLoadOrderGroup,
                                  lpdwTagId,
                                  (LPBYTE)lpDependencies,
                                  dwDependenciesLength,
                                  lpServiceStartName,
                                  lpEncryptedPassword,
                                  dwPasswordSize,
                                  (SC_RPC_HANDLE *)&hService);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

done:
    if (lpEncryptedPassword != NULL)
    {
        /* Wipe and release the password buffers */
        SecureZeroMemory(lpEncryptedPassword, dwPasswordSize);
        HeapFree(GetProcessHeap(), 0, lpEncryptedPassword);
    }

    SetLastError(dwError);
    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RCreateServiceW() failed (Error %lu)\n", dwError);
        return NULL;
    }

    return hService;
}


/**********************************************************************
 *  DeleteService
 *
 * @implemented
 */
BOOL WINAPI
DeleteService(SC_HANDLE hService)
{
    DWORD dwError;

    TRACE("DeleteService(%p)\n",
          hService);

    RpcTryExcept
    {
        dwError = RDeleteService((SC_RPC_HANDLE)hService);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RDeleteService() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  EnumDependentServicesA
 *
 * @implemented
 */
BOOL WINAPI
EnumDependentServicesA(SC_HANDLE hService,
                       DWORD dwServiceState,
                       LPENUM_SERVICE_STATUSA lpServices,
                       DWORD cbBufSize,
                       LPDWORD pcbBytesNeeded,
                       LPDWORD lpServicesReturned)
{
    ENUM_SERVICE_STATUSA ServiceStatus;
    LPENUM_SERVICE_STATUSA lpStatusPtr;
    DWORD dwBufferSize;
    DWORD dwError;
    DWORD dwCount;

    TRACE("EnumDependentServicesA(%p %lu %p %lu %p %p)\n",
          hService, dwServiceState, lpServices, cbBufSize,
          pcbBytesNeeded, lpServicesReturned);

    if (lpServices == NULL || cbBufSize < sizeof(ENUM_SERVICE_STATUSA))
    {
        lpStatusPtr = &ServiceStatus;
        dwBufferSize = sizeof(ENUM_SERVICE_STATUSA);
    }
    else
    {
        lpStatusPtr = lpServices;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = REnumDependentServicesA((SC_RPC_HANDLE)hService,
                                          dwServiceState,
                                          (LPBYTE)lpStatusPtr,
                                          dwBufferSize,
                                          pcbBytesNeeded,
                                          lpServicesReturned);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError == ERROR_SUCCESS || dwError == ERROR_MORE_DATA)
    {
        for (dwCount = 0; dwCount < *lpServicesReturned; dwCount++)
        {
            if (lpStatusPtr->lpServiceName)
                lpStatusPtr->lpServiceName =
                    (LPSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpServiceName);

            if (lpStatusPtr->lpDisplayName)
                lpStatusPtr->lpDisplayName =
                    (LPSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpDisplayName);

            lpStatusPtr++;
        }
    }

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("REnumDependentServicesA() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    TRACE("EnumDependentServicesA() done\n");

    return TRUE;
}


/**********************************************************************
 *  EnumDependentServicesW
 *
 * @implemented
 */
BOOL WINAPI
EnumDependentServicesW(SC_HANDLE hService,
                       DWORD dwServiceState,
                       LPENUM_SERVICE_STATUSW lpServices,
                       DWORD cbBufSize,
                       LPDWORD pcbBytesNeeded,
                       LPDWORD lpServicesReturned)
{
    ENUM_SERVICE_STATUSW ServiceStatus;
    LPENUM_SERVICE_STATUSW lpStatusPtr;
    DWORD dwBufferSize;
    DWORD dwError;
    DWORD dwCount;

    TRACE("EnumDependentServicesW(%p %lu %p %lu %p %p)\n",
          hService, dwServiceState, lpServices, cbBufSize,
          pcbBytesNeeded, lpServicesReturned);

    if (lpServices == NULL || cbBufSize < sizeof(ENUM_SERVICE_STATUSW))
    {
        lpStatusPtr = &ServiceStatus;
        dwBufferSize = sizeof(ENUM_SERVICE_STATUSW);
    }
    else
    {
        lpStatusPtr = lpServices;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = REnumDependentServicesW((SC_RPC_HANDLE)hService,
                                          dwServiceState,
                                          (LPBYTE)lpStatusPtr,
                                          dwBufferSize,
                                          pcbBytesNeeded,
                                          lpServicesReturned);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError == ERROR_SUCCESS || dwError == ERROR_MORE_DATA)
    {
        for (dwCount = 0; dwCount < *lpServicesReturned; dwCount++)
        {
            if (lpStatusPtr->lpServiceName)
                lpStatusPtr->lpServiceName =
                    (LPWSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpServiceName);

            if (lpStatusPtr->lpDisplayName)
                lpStatusPtr->lpDisplayName =
                    (LPWSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpDisplayName);

            lpStatusPtr++;
        }
    }

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("REnumDependentServicesW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    TRACE("EnumDependentServicesW() done\n");

    return TRUE;
}


/**********************************************************************
 *  EnumServiceGroupW
 *
 * @implemented
 */
BOOL WINAPI
EnumServiceGroupW(SC_HANDLE hSCManager,
                  DWORD dwServiceType,
                  DWORD dwServiceState,
                  LPENUM_SERVICE_STATUSW lpServices,
                  DWORD cbBufSize,
                  LPDWORD pcbBytesNeeded,
                  LPDWORD lpServicesReturned,
                  LPDWORD lpResumeHandle,
                  LPCWSTR lpGroup)
{
    ENUM_SERVICE_STATUSW ServiceStatus;
    LPENUM_SERVICE_STATUSW lpStatusPtr;
    DWORD dwBufferSize;
    DWORD dwError;
    DWORD dwCount;

    TRACE("EnumServiceGroupW(%p %lu %lu %p %lu %p %p %p %s)\n",
          hSCManager, dwServiceType, dwServiceState, lpServices,
          cbBufSize, pcbBytesNeeded, lpServicesReturned,
          lpResumeHandle, debugstr_w(lpGroup));

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
    {
        SetLastError(ERROR_INVALID_ADDRESS);
        return FALSE;
    }

    if (lpServices == NULL || cbBufSize < sizeof(ENUM_SERVICE_STATUSW))
    {
        lpStatusPtr = &ServiceStatus;
        dwBufferSize = sizeof(ENUM_SERVICE_STATUSW);
    }
    else
    {
        lpStatusPtr = lpServices;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        if (lpGroup == NULL)
        {
            dwError = REnumServicesStatusW((SC_RPC_HANDLE)hSCManager,
                                           dwServiceType,
                                           dwServiceState,
                                           (LPBYTE)lpStatusPtr,
                                           dwBufferSize,
                                           pcbBytesNeeded,
                                           lpServicesReturned,
                                           lpResumeHandle);
        }
        else
        {
            dwError = REnumServiceGroupW((SC_RPC_HANDLE)hSCManager,
                                         dwServiceType,
                                         dwServiceState,
                                         (LPBYTE)lpStatusPtr,
                                         dwBufferSize,
                                         pcbBytesNeeded,
                                         lpServicesReturned,
                                         lpResumeHandle,
                                         lpGroup);
        }
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError == ERROR_SUCCESS || dwError == ERROR_MORE_DATA)
    {
        for (dwCount = 0; dwCount < *lpServicesReturned; dwCount++)
        {
            if (lpStatusPtr->lpServiceName)
                lpStatusPtr->lpServiceName =
                    (LPWSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpServiceName);

            if (lpStatusPtr->lpDisplayName)
                lpStatusPtr->lpDisplayName =
                    (LPWSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpDisplayName);

            lpStatusPtr++;
        }
    }

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("REnumServiceGroupW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    TRACE("EnumServiceGroupW() done\n");

    return TRUE;
}


/**********************************************************************
 *  EnumServicesStatusA
 *
 * @implemented
 */
BOOL WINAPI
EnumServicesStatusA(SC_HANDLE hSCManager,
                    DWORD dwServiceType,
                    DWORD dwServiceState,
                    LPENUM_SERVICE_STATUSA lpServices,
                    DWORD cbBufSize,
                    LPDWORD pcbBytesNeeded,
                    LPDWORD lpServicesReturned,
                    LPDWORD lpResumeHandle)
{
    ENUM_SERVICE_STATUSA ServiceStatus;
    LPENUM_SERVICE_STATUSA lpStatusPtr;
    DWORD dwBufferSize;
    DWORD dwError;
    DWORD dwCount;

    TRACE("EnumServicesStatusA(%p %lu %lu %p %lu %p %p %p)\n",
          hSCManager, dwServiceType, dwServiceState, lpServices,
          cbBufSize, pcbBytesNeeded, lpServicesReturned, lpResumeHandle);

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
    {
        SetLastError(ERROR_INVALID_ADDRESS);
        return FALSE;
    }

    if (lpServices == NULL || cbBufSize < sizeof(ENUM_SERVICE_STATUSA))
    {
        lpStatusPtr = &ServiceStatus;
        dwBufferSize = sizeof(ENUM_SERVICE_STATUSA);
    }
    else
    {
        lpStatusPtr = lpServices;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = REnumServicesStatusA((SC_RPC_HANDLE)hSCManager,
                                       dwServiceType,
                                       dwServiceState,
                                       (LPBYTE)lpStatusPtr,
                                       dwBufferSize,
                                       pcbBytesNeeded,
                                       lpServicesReturned,
                                       lpResumeHandle);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError == ERROR_SUCCESS || dwError == ERROR_MORE_DATA)
    {
        for (dwCount = 0; dwCount < *lpServicesReturned; dwCount++)
        {
            if (lpStatusPtr->lpServiceName)
                lpStatusPtr->lpServiceName =
                    (LPSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpServiceName);

            if (lpStatusPtr->lpDisplayName)
                lpStatusPtr->lpDisplayName =
                    (LPSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpDisplayName);

            lpStatusPtr++;
        }
    }

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("REnumServicesStatusA() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    TRACE("EnumServicesStatusA() done\n");

    return TRUE;
}


/**********************************************************************
 *  EnumServicesStatusW
 *
 * @implemented
 */
BOOL WINAPI
EnumServicesStatusW(SC_HANDLE hSCManager,
                    DWORD dwServiceType,
                    DWORD dwServiceState,
                    LPENUM_SERVICE_STATUSW lpServices,
                    DWORD cbBufSize,
                    LPDWORD pcbBytesNeeded,
                    LPDWORD lpServicesReturned,
                    LPDWORD lpResumeHandle)
{
    ENUM_SERVICE_STATUSW ServiceStatus;
    LPENUM_SERVICE_STATUSW lpStatusPtr;
    DWORD dwBufferSize;
    DWORD dwError;
    DWORD dwCount;

    TRACE("EnumServicesStatusW(%p %lu %lu %p %lu %p %p %p)\n",
          hSCManager, dwServiceType, dwServiceState, lpServices,
          cbBufSize, pcbBytesNeeded, lpServicesReturned, lpResumeHandle);

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
    {
        SetLastError(ERROR_INVALID_ADDRESS);
        return FALSE;
    }

    if (lpServices == NULL || cbBufSize < sizeof(ENUM_SERVICE_STATUSW))
    {
        lpStatusPtr = &ServiceStatus;
        dwBufferSize = sizeof(ENUM_SERVICE_STATUSW);
    }
    else
    {
        lpStatusPtr = lpServices;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = REnumServicesStatusW((SC_RPC_HANDLE)hSCManager,
                                       dwServiceType,
                                       dwServiceState,
                                       (LPBYTE)lpStatusPtr,
                                       dwBufferSize,
                                       pcbBytesNeeded,
                                       lpServicesReturned,
                                       lpResumeHandle);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError == ERROR_SUCCESS || dwError == ERROR_MORE_DATA)
    {
        for (dwCount = 0; dwCount < *lpServicesReturned; dwCount++)
        {
            if (lpStatusPtr->lpServiceName)
                lpStatusPtr->lpServiceName =
                    (LPWSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpServiceName);

            if (lpStatusPtr->lpDisplayName)
                lpStatusPtr->lpDisplayName =
                    (LPWSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpDisplayName);

            lpStatusPtr++;
        }
    }

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("REnumServicesStatusW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    TRACE("EnumServicesStatusW() done\n");

    return TRUE;
}


/**********************************************************************
 *  EnumServicesStatusExA
 *
 * @implemented
 */
BOOL WINAPI
EnumServicesStatusExA(SC_HANDLE hSCManager,
                      SC_ENUM_TYPE InfoLevel,
                      DWORD dwServiceType,
                      DWORD dwServiceState,
                      LPBYTE lpServices,
                      DWORD cbBufSize,
                      LPDWORD pcbBytesNeeded,
                      LPDWORD lpServicesReturned,
                      LPDWORD lpResumeHandle,
                      LPCSTR pszGroupName)
{
    ENUM_SERVICE_STATUS_PROCESSA ServiceStatus;
    LPENUM_SERVICE_STATUS_PROCESSA lpStatusPtr;
    DWORD dwBufferSize;
    DWORD dwError;
    DWORD dwCount;

    TRACE("EnumServicesStatusExA(%p %lu %lu %p %lu %p %p %p %s)\n",
          hSCManager, dwServiceType, dwServiceState, lpServices,
          cbBufSize, pcbBytesNeeded, lpServicesReturned, lpResumeHandle,
          debugstr_a(pszGroupName));

    if (InfoLevel != SC_ENUM_PROCESS_INFO)
    {
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
    {
        SetLastError(ERROR_INVALID_ADDRESS);
        return FALSE;
    }

    if (lpServices == NULL || cbBufSize < sizeof(ENUM_SERVICE_STATUS_PROCESSA))
    {
        lpStatusPtr = &ServiceStatus;
        dwBufferSize = sizeof(ENUM_SERVICE_STATUS_PROCESSA);
    }
    else
    {
        lpStatusPtr = (LPENUM_SERVICE_STATUS_PROCESSA)lpServices;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = REnumServicesStatusExA((SC_RPC_HANDLE)hSCManager,
                                         InfoLevel,
                                         dwServiceType,
                                         dwServiceState,
                                         (LPBYTE)lpStatusPtr,
                                         dwBufferSize,
                                         pcbBytesNeeded,
                                         lpServicesReturned,
                                         lpResumeHandle,
                                         (LPSTR)pszGroupName);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError == ERROR_SUCCESS || dwError == ERROR_MORE_DATA)
    {
        if (InfoLevel == SC_ENUM_PROCESS_INFO)
        {
            for (dwCount = 0; dwCount < *lpServicesReturned; dwCount++)
            {
                if (lpStatusPtr->lpServiceName)
                    lpStatusPtr->lpServiceName =
                        (LPSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpServiceName);

                if (lpStatusPtr->lpDisplayName)
                    lpStatusPtr->lpDisplayName =
                        (LPSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpDisplayName);

                lpStatusPtr++;
            }
        }
    }

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("REnumServicesStatusExA() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    TRACE("EnumServicesStatusExA() done\n");

    return TRUE;
}


/**********************************************************************
 *  EnumServicesStatusExW
 *
 * @implemented
 */
BOOL WINAPI
EnumServicesStatusExW(SC_HANDLE hSCManager,
                      SC_ENUM_TYPE InfoLevel,
                      DWORD dwServiceType,
                      DWORD dwServiceState,
                      LPBYTE lpServices,
                      DWORD cbBufSize,
                      LPDWORD pcbBytesNeeded,
                      LPDWORD lpServicesReturned,
                      LPDWORD lpResumeHandle,
                      LPCWSTR pszGroupName)
{
    ENUM_SERVICE_STATUS_PROCESSW ServiceStatus;
    LPENUM_SERVICE_STATUS_PROCESSW lpStatusPtr;
    DWORD dwBufferSize;
    DWORD dwError;
    DWORD dwCount;

    TRACE("EnumServicesStatusExW(%p %lu %lu %p %lu %p %p %p %s)\n",
          hSCManager, dwServiceType, dwServiceState, lpServices,
          cbBufSize, pcbBytesNeeded, lpServicesReturned, lpResumeHandle,
          debugstr_w(pszGroupName));

    if (InfoLevel != SC_ENUM_PROCESS_INFO)
    {
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    if (pcbBytesNeeded == NULL || lpServicesReturned == NULL)
    {
        SetLastError(ERROR_INVALID_ADDRESS);
        return FALSE;
    }

    if (lpServices == NULL || cbBufSize < sizeof(ENUM_SERVICE_STATUS_PROCESSW))
    {
        lpStatusPtr = &ServiceStatus;
        dwBufferSize = sizeof(ENUM_SERVICE_STATUS_PROCESSW);
    }
    else
    {
        lpStatusPtr = (LPENUM_SERVICE_STATUS_PROCESSW)lpServices;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = REnumServicesStatusExW((SC_RPC_HANDLE)hSCManager,
                                         InfoLevel,
                                         dwServiceType,
                                         dwServiceState,
                                         (LPBYTE)lpStatusPtr,
                                         dwBufferSize,
                                         pcbBytesNeeded,
                                         lpServicesReturned,
                                         lpResumeHandle,
                                         (LPWSTR)pszGroupName);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError == ERROR_SUCCESS || dwError == ERROR_MORE_DATA)
    {
        if (InfoLevel == SC_ENUM_PROCESS_INFO)
        {
            for (dwCount = 0; dwCount < *lpServicesReturned; dwCount++)
            {
                if (lpStatusPtr->lpServiceName)
                    lpStatusPtr->lpServiceName =
                        (LPWSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpServiceName);

                if (lpStatusPtr->lpDisplayName)
                    lpStatusPtr->lpDisplayName =
                        (LPWSTR)((ULONG_PTR)lpServices + (ULONG_PTR)lpStatusPtr->lpDisplayName);

                lpStatusPtr++;
            }
        }
    }

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("REnumServicesStatusExW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    TRACE("EnumServicesStatusExW() done\n");

    return TRUE;
}


/**********************************************************************
 *  GetServiceDisplayNameA
 *
 * @implemented
 */
BOOL WINAPI
GetServiceDisplayNameA(SC_HANDLE hSCManager,
                       LPCSTR lpServiceName,
                       LPSTR lpDisplayName,
                       LPDWORD lpcchBuffer)
{
    DWORD dwError;
    LPSTR lpNameBuffer;
    CHAR szEmptyName[] = "";

    TRACE("GetServiceDisplayNameA(%p %s %p %p)\n",
          hSCManager, debugstr_a(lpServiceName), lpDisplayName, lpcchBuffer);

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    if (!lpDisplayName || *lpcchBuffer < sizeof(CHAR))
    {
        lpNameBuffer = szEmptyName;
        *lpcchBuffer = sizeof(CHAR);
    }
    else
    {
        lpNameBuffer = lpDisplayName;
    }

    RpcTryExcept
    {
        dwError = RGetServiceDisplayNameA((SC_RPC_HANDLE)hSCManager,
                                          lpServiceName,
                                          lpNameBuffer,
                                          lpcchBuffer);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RGetServiceDisplayNameA() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  GetServiceDisplayNameW
 *
 * @implemented
 */
BOOL WINAPI
GetServiceDisplayNameW(SC_HANDLE hSCManager,
                       LPCWSTR lpServiceName,
                       LPWSTR lpDisplayName,
                       LPDWORD lpcchBuffer)
{
    DWORD dwError;
    LPWSTR lpNameBuffer;
    WCHAR szEmptyName[] = L"";

    TRACE("GetServiceDisplayNameW(%p %s %p %p)\n",
          hSCManager, debugstr_w(lpServiceName), lpDisplayName, lpcchBuffer);

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    /*
     * NOTE: A size of 1 character would be enough, but tests show that
     * Windows returns 2 characters instead, certainly due to a WCHAR/bytes
     * mismatch in their code.
     */
    if (!lpDisplayName || *lpcchBuffer < sizeof(WCHAR))
    {
        lpNameBuffer = szEmptyName;
        *lpcchBuffer = sizeof(WCHAR);
    }
    else
    {
        lpNameBuffer = lpDisplayName;
    }

    RpcTryExcept
    {
        dwError = RGetServiceDisplayNameW((SC_RPC_HANDLE)hSCManager,
                                          lpServiceName,
                                          lpNameBuffer,
                                          lpcchBuffer);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RGetServiceDisplayNameW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  GetServiceKeyNameA
 *
 * @implemented
 */
BOOL WINAPI
GetServiceKeyNameA(SC_HANDLE hSCManager,
                   LPCSTR lpDisplayName,
                   LPSTR lpServiceName,
                   LPDWORD lpcchBuffer)
{
    DWORD dwError;
    LPSTR lpNameBuffer;
    CHAR szEmptyName[] = "";

    TRACE("GetServiceKeyNameA(%p %s %p %p)\n",
          hSCManager, debugstr_a(lpDisplayName), lpServiceName, lpcchBuffer);

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    if (!lpServiceName || *lpcchBuffer < sizeof(CHAR))
    {
        lpNameBuffer = szEmptyName;
        *lpcchBuffer = sizeof(CHAR);
    }
    else
    {
        lpNameBuffer = lpServiceName;
    }

    RpcTryExcept
    {
        dwError = RGetServiceKeyNameA((SC_RPC_HANDLE)hSCManager,
                                      lpDisplayName,
                                      lpNameBuffer,
                                      lpcchBuffer);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        /* HACK: because of a problem with rpcrt4, rpcserver is hacked to return 6 for ERROR_SERVICE_DOES_NOT_EXIST */
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RGetServiceKeyNameA() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  GetServiceKeyNameW
 *
 * @implemented
 */
BOOL WINAPI
GetServiceKeyNameW(SC_HANDLE hSCManager,
                   LPCWSTR lpDisplayName,
                   LPWSTR lpServiceName,
                   LPDWORD lpcchBuffer)
{
    DWORD dwError;
    LPWSTR lpNameBuffer;
    WCHAR szEmptyName[] = L"";

    TRACE("GetServiceKeyNameW(%p %s %p %p)\n",
          hSCManager, debugstr_w(lpDisplayName), lpServiceName, lpcchBuffer);

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    /*
     * NOTE: A size of 1 character would be enough, but tests show that
     * Windows returns 2 characters instead, certainly due to a WCHAR/bytes
     * mismatch in their code.
     */
    if (!lpServiceName || *lpcchBuffer < sizeof(WCHAR))
    {
        lpNameBuffer = szEmptyName;
        *lpcchBuffer = sizeof(WCHAR);
    }
    else
    {
        lpNameBuffer = lpServiceName;
    }

    RpcTryExcept
    {
        dwError = RGetServiceKeyNameW((SC_RPC_HANDLE)hSCManager,
                                      lpDisplayName,
                                      lpNameBuffer,
                                      lpcchBuffer);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RGetServiceKeyNameW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  I_ScGetCurrentGroupStateW
 *
 * @implemented
 */
DWORD WINAPI
I_ScGetCurrentGroupStateW(SC_HANDLE hSCManager,
                          LPWSTR pszGroupName,
                          LPDWORD pdwGroupState)
{
    DWORD dwError;

    TRACE("I_ScGetCurrentGroupStateW(%p %s %p)\n",
          hSCManager, debugstr_w(pszGroupName), pdwGroupState);

    RpcTryExcept
    {
        dwError = RI_ScGetCurrentGroupStateW((SC_RPC_HANDLE)hSCManager,
                                             pszGroupName,
                                             pdwGroupState);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RI_ScGetCurrentGroupStateW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
    }

    return dwError;
}


/**********************************************************************
 *  I_ScValidatePnpService
 *
 * Undocumented
 *
 * @implemented
 */
DWORD
WINAPI
I_ScValidatePnpService(
    _In_ LPCWSTR pszMachineName,
    _In_ LPCWSTR pszServiceName,
    _Out_ SERVICE_STATUS_HANDLE *phServiceStatus)
{
    SC_RPC_HANDLE hSCManager = NULL;
    SERVICE_STATUS_HANDLE hServiceStatus = NULL;
    DWORD dwError;

    TRACE("I_ScValidatePnpService(%S %S %p)\n",
         pszMachineName, pszServiceName, phServiceStatus);

    hSCManager = OpenSCManagerW(pszMachineName,
                                SERVICES_ACTIVE_DATABASEW,
                                SC_MANAGER_CONNECT);
    if (hSCManager == NULL)
    {
        dwError = GetLastError();
        goto done;
    }

    RpcTryExcept
    {
        dwError = RI_ScValidatePnPService(hSCManager,
                                          (LPWSTR)pszServiceName,
                                          (RPC_SERVICE_STATUS_HANDLE *)&hServiceStatus);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept

    *phServiceStatus = hServiceStatus;

done:
    if (hSCManager != NULL)
        CloseServiceHandle(hSCManager);

    return dwError;
}


/**********************************************************************
 *  LockServiceDatabase
 *
 * @implemented
 */
SC_LOCK WINAPI
LockServiceDatabase(SC_HANDLE hSCManager)
{
    SC_LOCK hLock;
    DWORD dwError;

    TRACE("LockServiceDatabase(%p)\n",
          hSCManager);

    RpcTryExcept
    {
        dwError = RLockServiceDatabase((SC_RPC_HANDLE)hSCManager,
                                       (SC_RPC_LOCK *)&hLock);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RLockServiceDatabase() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return NULL;
    }

    TRACE("hLock = %p\n", hLock);

    return hLock;
}


static VOID
WaitForSCManager(VOID)
{
    HANDLE hEvent;

    TRACE("WaitForSCManager()\n");

    /* Try to open the existing event */
    hEvent = OpenEventW(SYNCHRONIZE, FALSE, SCM_START_EVENT);
    if (hEvent == NULL)
    {
        if (GetLastError() != ERROR_FILE_NOT_FOUND)
            return;

        /* Try to create a new event */
        hEvent = CreateEventW(NULL, TRUE, FALSE, SCM_START_EVENT);
        if (hEvent == NULL)
            return;
    }

    /* Wait for 3 minutes */
    WaitForSingleObject(hEvent, 180000);
    CloseHandle(hEvent);

    TRACE("ScmWaitForSCManager() done\n");
}


/**********************************************************************
 *  OpenSCManagerA
 *
 * @implemented
 */
SC_HANDLE WINAPI
OpenSCManagerA(LPCSTR lpMachineName,
               LPCSTR lpDatabaseName,
               DWORD dwDesiredAccess)
{
    SC_HANDLE hScm = NULL;
    DWORD dwError;

    TRACE("OpenSCManagerA(%s %s %lx)\n",
          debugstr_a(lpMachineName), debugstr_a(lpDatabaseName), dwDesiredAccess);

    WaitForSCManager();

    RpcTryExcept
    {
        dwError = ROpenSCManagerA((LPSTR)lpMachineName,
                                  (LPSTR)lpDatabaseName,
                                  dwDesiredAccess,
                                  (SC_RPC_HANDLE *)&hScm);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    SetLastError(dwError);
    if (dwError != ERROR_SUCCESS)
    {
        TRACE("ROpenSCManagerA() failed (Error %lu)\n", dwError);
        return NULL;
    }

    TRACE("hScm = %p\n", hScm);

    return hScm;
}


/**********************************************************************
 *  OpenSCManagerW
 *
 * @implemented
 */
SC_HANDLE WINAPI
OpenSCManagerW(LPCWSTR lpMachineName,
               LPCWSTR lpDatabaseName,
               DWORD dwDesiredAccess)
{
    SC_HANDLE hScm = NULL;
    DWORD dwError;

    TRACE("OpenSCManagerW(%s %s %lx)\n",
          debugstr_w(lpMachineName), debugstr_w(lpDatabaseName), dwDesiredAccess);

    WaitForSCManager();

    RpcTryExcept
    {
        dwError = ROpenSCManagerW((LPWSTR)lpMachineName,
                                  (LPWSTR)lpDatabaseName,
                                  dwDesiredAccess,
                                  (SC_RPC_HANDLE *)&hScm);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    SetLastError(dwError);
    if (dwError != ERROR_SUCCESS)
    {
        TRACE("ROpenSCManagerW() failed (Error %lu)\n", dwError);
        return NULL;
    }

    TRACE("hScm = %p\n", hScm);

    return hScm;
}


/**********************************************************************
 *  OpenServiceA
 *
 * @implemented
 */
SC_HANDLE WINAPI
OpenServiceA(SC_HANDLE hSCManager,
             LPCSTR lpServiceName,
             DWORD dwDesiredAccess)
{
    SC_HANDLE hService = NULL;
    DWORD dwError;

    TRACE("OpenServiceA(%p %s %lx)\n",
           hSCManager, debugstr_a(lpServiceName), dwDesiredAccess);

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return NULL;
    }

    RpcTryExcept
    {
        dwError = ROpenServiceA((SC_RPC_HANDLE)hSCManager,
                                (LPSTR)lpServiceName,
                                dwDesiredAccess,
                                (SC_RPC_HANDLE *)&hService);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    SetLastError(dwError);
    if (dwError != ERROR_SUCCESS)
    {
        TRACE("ROpenServiceA() failed (Error %lu)\n", dwError);
        return NULL;
    }

    TRACE("hService = %p\n", hService);

    return hService;
}


/**********************************************************************
 *  OpenServiceW
 *
 * @implemented
 */
SC_HANDLE WINAPI
OpenServiceW(SC_HANDLE hSCManager,
             LPCWSTR lpServiceName,
             DWORD dwDesiredAccess)
{
    SC_HANDLE hService = NULL;
    DWORD dwError;

    TRACE("OpenServiceW(%p %s %lx)\n",
           hSCManager, debugstr_w(lpServiceName), dwDesiredAccess);

    if (!hSCManager)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return NULL;
    }

    RpcTryExcept
    {
        dwError = ROpenServiceW((SC_RPC_HANDLE)hSCManager,
                                (LPWSTR)lpServiceName,
                                dwDesiredAccess,
                                (SC_RPC_HANDLE *)&hService);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    SetLastError(dwError);
    if (dwError != ERROR_SUCCESS)
    {
        TRACE("ROpenServiceW() failed (Error %lu)\n", dwError);
        return NULL;
    }

    TRACE("hService = %p\n", hService);

    return hService;
}


/**********************************************************************
 *  QueryServiceConfigA
 *
 * @implemented
 */
BOOL WINAPI
QueryServiceConfigA(SC_HANDLE hService,
                    LPQUERY_SERVICE_CONFIGA lpServiceConfig,
                    DWORD cbBufSize,
                    LPDWORD pcbBytesNeeded)
{
    QUERY_SERVICE_CONFIGA ServiceConfig;
    LPQUERY_SERVICE_CONFIGA lpConfigPtr;
    DWORD dwBufferSize;
    DWORD dwError;

    TRACE("QueryServiceConfigA(%p %p %lu %p)\n",
           hService, lpServiceConfig, cbBufSize, pcbBytesNeeded);

    if (lpServiceConfig == NULL ||
        cbBufSize < sizeof(QUERY_SERVICE_CONFIGA))
    {
        lpConfigPtr = &ServiceConfig;
        dwBufferSize = sizeof(QUERY_SERVICE_CONFIGA);
    }
    else
    {
        lpConfigPtr = lpServiceConfig;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = RQueryServiceConfigA((SC_RPC_HANDLE)hService,
                                       (LPBYTE)lpConfigPtr,
                                       dwBufferSize,
                                       pcbBytesNeeded);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RQueryServiceConfigA() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    /* Adjust the pointers */
    if (lpConfigPtr->lpBinaryPathName)
        lpConfigPtr->lpBinaryPathName =
            (LPSTR)((ULONG_PTR)lpConfigPtr +
                    (ULONG_PTR)lpConfigPtr->lpBinaryPathName);

    if (lpConfigPtr->lpLoadOrderGroup)
        lpConfigPtr->lpLoadOrderGroup =
            (LPSTR)((ULONG_PTR)lpConfigPtr +
                    (ULONG_PTR)lpConfigPtr->lpLoadOrderGroup);

    if (lpConfigPtr->lpDependencies)
        lpConfigPtr->lpDependencies =
            (LPSTR)((ULONG_PTR)lpConfigPtr +
                    (ULONG_PTR)lpConfigPtr->lpDependencies);

    if (lpConfigPtr->lpServiceStartName)
        lpConfigPtr->lpServiceStartName =
            (LPSTR)((ULONG_PTR)lpConfigPtr +
                    (ULONG_PTR)lpConfigPtr->lpServiceStartName);

    if (lpConfigPtr->lpDisplayName)
        lpConfigPtr->lpDisplayName =
           (LPSTR)((ULONG_PTR)lpConfigPtr +
                   (ULONG_PTR)lpConfigPtr->lpDisplayName);

    TRACE("QueryServiceConfigA() done\n");

    return TRUE;
}


/**********************************************************************
 *  QueryServiceConfigW
 *
 * @implemented
 */
BOOL WINAPI
QueryServiceConfigW(SC_HANDLE hService,
                    LPQUERY_SERVICE_CONFIGW lpServiceConfig,
                    DWORD cbBufSize,
                    LPDWORD pcbBytesNeeded)
{
    QUERY_SERVICE_CONFIGW ServiceConfig;
    LPQUERY_SERVICE_CONFIGW lpConfigPtr;
    DWORD dwBufferSize;
    DWORD dwError;

    TRACE("QueryServiceConfigW(%p %p %lu %p)\n",
           hService, lpServiceConfig, cbBufSize, pcbBytesNeeded);

    if (lpServiceConfig == NULL ||
        cbBufSize < sizeof(QUERY_SERVICE_CONFIGW))
    {
        lpConfigPtr = &ServiceConfig;
        dwBufferSize = sizeof(QUERY_SERVICE_CONFIGW);
    }
    else
    {
        lpConfigPtr = lpServiceConfig;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = RQueryServiceConfigW((SC_RPC_HANDLE)hService,
                                       (LPBYTE)lpConfigPtr,
                                       dwBufferSize,
                                       pcbBytesNeeded);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RQueryServiceConfigW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    /* Adjust the pointers */
    if (lpConfigPtr->lpBinaryPathName)
        lpConfigPtr->lpBinaryPathName =
            (LPWSTR)((ULONG_PTR)lpConfigPtr +
                     (ULONG_PTR)lpConfigPtr->lpBinaryPathName);

    if (lpConfigPtr->lpLoadOrderGroup)
        lpConfigPtr->lpLoadOrderGroup =
            (LPWSTR)((ULONG_PTR)lpConfigPtr +
                     (ULONG_PTR)lpConfigPtr->lpLoadOrderGroup);

    if (lpConfigPtr->lpDependencies)
        lpConfigPtr->lpDependencies =
            (LPWSTR)((ULONG_PTR)lpConfigPtr +
                     (ULONG_PTR)lpConfigPtr->lpDependencies);

    if (lpConfigPtr->lpServiceStartName)
        lpConfigPtr->lpServiceStartName =
            (LPWSTR)((ULONG_PTR)lpConfigPtr +
                     (ULONG_PTR)lpConfigPtr->lpServiceStartName);

    if (lpConfigPtr->lpDisplayName)
        lpConfigPtr->lpDisplayName =
           (LPWSTR)((ULONG_PTR)lpConfigPtr +
                    (ULONG_PTR)lpConfigPtr->lpDisplayName);

    TRACE("QueryServiceConfigW() done\n");

    return TRUE;
}


/**********************************************************************
 *  QueryServiceConfig2A
 *
 * @implemented
 */
BOOL WINAPI
QueryServiceConfig2A(SC_HANDLE hService,
                     DWORD dwInfoLevel,
                     LPBYTE lpBuffer,
                     DWORD cbBufSize,
                     LPDWORD pcbBytesNeeded)
{
    SERVICE_DESCRIPTIONA ServiceDescription;
    SERVICE_FAILURE_ACTIONSA ServiceFailureActions;
    LPBYTE lpTempBuffer;
    BOOL bUseTempBuffer = FALSE;
    DWORD dwBufferSize;
    DWORD dwError;

    TRACE("QueryServiceConfig2A(%p %lu %p %lu %p)\n",
          hService, dwInfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded);

    lpTempBuffer = lpBuffer;
    dwBufferSize = cbBufSize;

    switch (dwInfoLevel)
    {
        case SERVICE_CONFIG_DESCRIPTION:
            if ((lpBuffer == NULL) || (cbBufSize < sizeof(SERVICE_DESCRIPTIONA)))
            {
                lpTempBuffer = (LPBYTE)&ServiceDescription;
                dwBufferSize = sizeof(SERVICE_DESCRIPTIONA);
                bUseTempBuffer = TRUE;
            }
            break;

        case SERVICE_CONFIG_FAILURE_ACTIONS:
            if ((lpBuffer == NULL) || (cbBufSize < sizeof(SERVICE_FAILURE_ACTIONSA)))
            {
                lpTempBuffer = (LPBYTE)&ServiceFailureActions;
                dwBufferSize = sizeof(SERVICE_FAILURE_ACTIONSA);
                bUseTempBuffer = TRUE;
            }
            break;

        default:
            WARN("Unknown info level 0x%lx\n", dwInfoLevel);
            SetLastError(ERROR_INVALID_LEVEL);
            return FALSE;
    }

    RpcTryExcept
    {
        dwError = RQueryServiceConfig2A((SC_RPC_HANDLE)hService,
                                        dwInfoLevel,
                                        lpTempBuffer,
                                        dwBufferSize,
                                        pcbBytesNeeded);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RQueryServiceConfig2A() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    if (bUseTempBuffer != FALSE)
    {
        TRACE("RQueryServiceConfig2A() returns ERROR_INSUFFICIENT_BUFFER\n");
        *pcbBytesNeeded = dwBufferSize;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    switch (dwInfoLevel)
    {
        case SERVICE_CONFIG_DESCRIPTION:
            {
                LPSERVICE_DESCRIPTIONA lpPtr = (LPSERVICE_DESCRIPTIONA)lpTempBuffer;

                if (lpPtr->lpDescription != NULL)
                    lpPtr->lpDescription =
                        (LPSTR)((ULONG_PTR)lpPtr + (ULONG_PTR)lpPtr->lpDescription);
            }
            break;

        case SERVICE_CONFIG_FAILURE_ACTIONS:
            {
                LPSERVICE_FAILURE_ACTIONSA lpPtr = (LPSERVICE_FAILURE_ACTIONSA)lpTempBuffer;

                if (lpPtr->lpRebootMsg != NULL)
                    lpPtr->lpRebootMsg =
                        (LPSTR)((ULONG_PTR)lpPtr + (ULONG_PTR)lpPtr->lpRebootMsg);

                if (lpPtr->lpCommand != NULL)
                    lpPtr->lpCommand =
                        (LPSTR)((ULONG_PTR)lpPtr + (ULONG_PTR)lpPtr->lpCommand);

                if (lpPtr->lpsaActions != NULL)
                    lpPtr->lpsaActions =
                        (LPSC_ACTION)((ULONG_PTR)lpPtr + (ULONG_PTR)lpPtr->lpsaActions);
            }
            break;
    }

    TRACE("QueryServiceConfig2A() done\n");

    return TRUE;
}


/**********************************************************************
 *  QueryServiceConfig2W
 *
 * @implemented
 */
BOOL WINAPI
QueryServiceConfig2W(SC_HANDLE hService,
                     DWORD dwInfoLevel,
                     LPBYTE lpBuffer,
                     DWORD cbBufSize,
                     LPDWORD pcbBytesNeeded)
{
    SERVICE_DESCRIPTIONW ServiceDescription;
    SERVICE_FAILURE_ACTIONSW ServiceFailureActions;
    LPBYTE lpTempBuffer;
    BOOL bUseTempBuffer = FALSE;
    DWORD dwBufferSize;
    DWORD dwError;

    TRACE("QueryServiceConfig2W(%p %lu %p %lu %p)\n",
          hService, dwInfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded);

    lpTempBuffer = lpBuffer;
    dwBufferSize = cbBufSize;

    switch (dwInfoLevel)
    {
        case SERVICE_CONFIG_DESCRIPTION:
            if ((lpBuffer == NULL) || (cbBufSize < sizeof(SERVICE_DESCRIPTIONW)))
            {
                lpTempBuffer = (LPBYTE)&ServiceDescription;
                dwBufferSize = sizeof(SERVICE_DESCRIPTIONW);
                bUseTempBuffer = TRUE;
            }
            break;

        case SERVICE_CONFIG_FAILURE_ACTIONS:
            if ((lpBuffer == NULL) || (cbBufSize < sizeof(SERVICE_FAILURE_ACTIONSW)))
            {
                lpTempBuffer = (LPBYTE)&ServiceFailureActions;
                dwBufferSize = sizeof(SERVICE_FAILURE_ACTIONSW);
                bUseTempBuffer = TRUE;
            }
            break;

        default:
            WARN("Unknown info level 0x%lx\n", dwInfoLevel);
            SetLastError(ERROR_INVALID_LEVEL);
            return FALSE;
    }

    RpcTryExcept
    {
        dwError = RQueryServiceConfig2W((SC_RPC_HANDLE)hService,
                                        dwInfoLevel,
                                        lpTempBuffer,
                                        dwBufferSize,
                                        pcbBytesNeeded);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RQueryServiceConfig2W() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    if (bUseTempBuffer != FALSE)
    {
        TRACE("RQueryServiceConfig2W() returns ERROR_INSUFFICIENT_BUFFER\n");
        *pcbBytesNeeded = dwBufferSize;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    switch (dwInfoLevel)
    {
        case SERVICE_CONFIG_DESCRIPTION:
            {
                LPSERVICE_DESCRIPTIONW lpPtr = (LPSERVICE_DESCRIPTIONW)lpTempBuffer;

                if (lpPtr->lpDescription != NULL)
                    lpPtr->lpDescription =
                        (LPWSTR)((ULONG_PTR)lpPtr + (ULONG_PTR)lpPtr->lpDescription);
            }
            break;

        case SERVICE_CONFIG_FAILURE_ACTIONS:
            {
                LPSERVICE_FAILURE_ACTIONSW lpPtr = (LPSERVICE_FAILURE_ACTIONSW)lpTempBuffer;

                if (lpPtr->lpRebootMsg != NULL)
                    lpPtr->lpRebootMsg =
                        (LPWSTR)((ULONG_PTR)lpPtr + (ULONG_PTR)lpPtr->lpRebootMsg);

                if (lpPtr->lpCommand != NULL)
                    lpPtr->lpCommand =
                        (LPWSTR)((ULONG_PTR)lpPtr + (ULONG_PTR)lpPtr->lpCommand);

                if (lpPtr->lpsaActions != NULL)
                    lpPtr->lpsaActions =
                        (LPSC_ACTION)((ULONG_PTR)lpPtr + (ULONG_PTR)lpPtr->lpsaActions);
            }
            break;
    }

    TRACE("QueryServiceConfig2W() done\n");

    return TRUE;
}


/**********************************************************************
 *  QueryServiceLockStatusA
 *
 * @implemented
 */
BOOL WINAPI
QueryServiceLockStatusA(SC_HANDLE hSCManager,
                        LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus,
                        DWORD cbBufSize,
                        LPDWORD pcbBytesNeeded)
{
    QUERY_SERVICE_LOCK_STATUSA LockStatus;
    LPQUERY_SERVICE_LOCK_STATUSA lpStatusPtr;
    DWORD dwBufferSize;
    DWORD dwError;

    TRACE("QueryServiceLockStatusA(%p %p %lu %p)\n",
          hSCManager, lpLockStatus, cbBufSize, pcbBytesNeeded);

    if (lpLockStatus == NULL || cbBufSize < sizeof(QUERY_SERVICE_LOCK_STATUSA))
    {
        lpStatusPtr = &LockStatus;
        dwBufferSize = sizeof(QUERY_SERVICE_LOCK_STATUSA);
    }
    else
    {
        lpStatusPtr = lpLockStatus;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = RQueryServiceLockStatusA((SC_RPC_HANDLE)hSCManager,
                                           (LPBYTE)lpStatusPtr,
                                           dwBufferSize,
                                           pcbBytesNeeded);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RQueryServiceLockStatusA() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    if (lpStatusPtr->lpLockOwner != NULL)
    {
        lpStatusPtr->lpLockOwner =
            (LPSTR)((ULONG_PTR)lpStatusPtr + (ULONG_PTR)lpStatusPtr->lpLockOwner);
    }

    TRACE("QueryServiceLockStatusA() done\n");

    return TRUE;
}


/**********************************************************************
 *  QueryServiceLockStatusW
 *
 * @implemented
 */
BOOL WINAPI
QueryServiceLockStatusW(SC_HANDLE hSCManager,
                        LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus,
                        DWORD cbBufSize,
                        LPDWORD pcbBytesNeeded)
{
    QUERY_SERVICE_LOCK_STATUSW LockStatus;
    LPQUERY_SERVICE_LOCK_STATUSW lpStatusPtr;
    DWORD dwBufferSize;
    DWORD dwError;

    TRACE("QueryServiceLockStatusW(%p %p %lu %p)\n",
          hSCManager, lpLockStatus, cbBufSize, pcbBytesNeeded);

    if (lpLockStatus == NULL || cbBufSize < sizeof(QUERY_SERVICE_LOCK_STATUSW))
    {
        lpStatusPtr = &LockStatus;
        dwBufferSize = sizeof(QUERY_SERVICE_LOCK_STATUSW);
    }
    else
    {
        lpStatusPtr = lpLockStatus;
        dwBufferSize = cbBufSize;
    }

    RpcTryExcept
    {
        dwError = RQueryServiceLockStatusW((SC_RPC_HANDLE)hSCManager,
                                           (LPBYTE)lpStatusPtr,
                                           dwBufferSize,
                                           pcbBytesNeeded);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RQueryServiceLockStatusW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    if (lpStatusPtr->lpLockOwner != NULL)
    {
        lpStatusPtr->lpLockOwner =
            (LPWSTR)((ULONG_PTR)lpStatusPtr + (ULONG_PTR)lpStatusPtr->lpLockOwner);
    }

    TRACE("QueryServiceLockStatusW() done\n");

    return TRUE;
}


/**********************************************************************
 *  QueryServiceObjectSecurity
 *
 * @implemented
 */
BOOL WINAPI
QueryServiceObjectSecurity(SC_HANDLE hService,
                           SECURITY_INFORMATION dwSecurityInformation,
                           PSECURITY_DESCRIPTOR lpSecurityDescriptor,
                           DWORD cbBufSize,
                           LPDWORD pcbBytesNeeded)
{
    DWORD dwError;

    TRACE("QueryServiceObjectSecurity(%p %lu %p)\n",
           hService, dwSecurityInformation, lpSecurityDescriptor);

    RpcTryExcept
    {
        dwError = RQueryServiceObjectSecurity((SC_RPC_HANDLE)hService,
                                              dwSecurityInformation,
                                              (LPBYTE)lpSecurityDescriptor,
                                              cbBufSize,
                                              pcbBytesNeeded);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("QueryServiceObjectSecurity() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  SetServiceObjectSecurity
 *
 * @implemented
 */
BOOL WINAPI
SetServiceObjectSecurity(SC_HANDLE hService,
                         SECURITY_INFORMATION dwSecurityInformation,
                         PSECURITY_DESCRIPTOR lpSecurityDescriptor)
{
    PSECURITY_DESCRIPTOR SelfRelativeSD = NULL;
    ULONG Length;
    NTSTATUS Status;
    DWORD dwError;

    TRACE("SetServiceObjectSecurity(%p %lu %p)\n",
          hService, dwSecurityInformation, lpSecurityDescriptor);

    Length = 0;
    Status = RtlMakeSelfRelativeSD(lpSecurityDescriptor,
                                   SelfRelativeSD,
                                   &Length);
    if (Status != STATUS_BUFFER_TOO_SMALL)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    SelfRelativeSD = HeapAlloc(GetProcessHeap(), 0, Length);
    if (SelfRelativeSD == NULL)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    Status = RtlMakeSelfRelativeSD(lpSecurityDescriptor,
                                   SelfRelativeSD,
                                   &Length);
    if (!NT_SUCCESS(Status))
    {
        HeapFree(GetProcessHeap(), 0, SelfRelativeSD);
        SetLastError(RtlNtStatusToDosError(Status));
        return FALSE;
    }

    RpcTryExcept
    {
        dwError = RSetServiceObjectSecurity((SC_RPC_HANDLE)hService,
                                            dwSecurityInformation,
                                            (LPBYTE)SelfRelativeSD,
                                            Length);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    HeapFree(GetProcessHeap(), 0, SelfRelativeSD);

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RServiceObjectSecurity() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  QueryServiceStatus
 *
 * @implemented
 */
BOOL WINAPI
QueryServiceStatus(SC_HANDLE hService,
                   LPSERVICE_STATUS lpServiceStatus)
{
    DWORD dwError;

    TRACE("QueryServiceStatus(%p %p)\n",
          hService, lpServiceStatus);

    if (!hService)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    RpcTryExcept
    {
        dwError = RQueryServiceStatus((SC_RPC_HANDLE)hService,
                                      lpServiceStatus);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RQueryServiceStatus() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  QueryServiceStatusEx
 *
 * @implemented
 */
BOOL WINAPI
QueryServiceStatusEx(SC_HANDLE hService,
                     SC_STATUS_TYPE InfoLevel,
                     LPBYTE lpBuffer,
                     DWORD cbBufSize,
                     LPDWORD pcbBytesNeeded)
{
    DWORD dwError;

    TRACE("QueryServiceStatusEx(%p %lu %p %lu %p)\n",
          hService, InfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded);

    if (InfoLevel != SC_STATUS_PROCESS_INFO)
    {
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }

    if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
    {
        *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    RpcTryExcept
    {
        dwError = RQueryServiceStatusEx((SC_RPC_HANDLE)hService,
                                        InfoLevel,
                                        lpBuffer,
                                        cbBufSize,
                                        pcbBytesNeeded);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RQueryServiceStatusEx() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  StartServiceA
 *
 * @implemented
 */
BOOL WINAPI
StartServiceA(SC_HANDLE hService,
              DWORD dwNumServiceArgs,
              LPCSTR *lpServiceArgVectors)
{
    DWORD dwError;

    TRACE("StartServiceA(%p %lu %p)\n",
          hService, dwNumServiceArgs, lpServiceArgVectors);

    RpcTryExcept
    {
        dwError = RStartServiceA((SC_RPC_HANDLE)hService,
                                 dwNumServiceArgs,
                                 (LPSTRING_PTRSA)lpServiceArgVectors);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        ERR("RStartServiceA() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  StartServiceW
 *
 * @implemented
 */
BOOL WINAPI
StartServiceW(SC_HANDLE hService,
              DWORD dwNumServiceArgs,
              LPCWSTR *lpServiceArgVectors)
{
    DWORD dwError;

    TRACE("StartServiceW(%p %lu %p)\n",
          hService, dwNumServiceArgs, lpServiceArgVectors);

    RpcTryExcept
    {
        dwError = RStartServiceW((SC_RPC_HANDLE)hService,
                                 dwNumServiceArgs,
                                 (LPSTRING_PTRSW)lpServiceArgVectors);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        ERR("RStartServiceW() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  UnlockServiceDatabase
 *
 * @implemented
 */
BOOL WINAPI
UnlockServiceDatabase(SC_LOCK ScLock)
{
    DWORD dwError;

    TRACE("UnlockServiceDatabase(%x)\n",
          ScLock);

    RpcTryExcept
    {
        dwError = RUnlockServiceDatabase((LPSC_RPC_LOCK)&ScLock);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError == ERROR_INVALID_HANDLE)
        dwError = ERROR_INVALID_SERVICE_LOCK;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("RUnlockServiceDatabase() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}


/**********************************************************************
 *  NotifyBootConfigStatus
 *
 * @implemented
 */
BOOL WINAPI
NotifyBootConfigStatus(BOOL BootAcceptable)
{
    DWORD dwError;

    TRACE("NotifyBootConfigStatus(%u)\n",
          BootAcceptable);

    RpcTryExcept
    {
        dwError = RNotifyBootConfigStatus(NULL,
                                          BootAcceptable);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    if (dwError != ERROR_SUCCESS)
    {
        TRACE("NotifyBootConfigStatus() failed (Error %lu)\n", dwError);
        SetLastError(dwError);
        return FALSE;
    }

    return TRUE;
}

DWORD
I_ScQueryServiceTagInfo(PVOID Unused,
                        TAG_INFO_LEVEL dwInfoLevel,
                        PTAG_INFO_NAME_FROM_TAG InOutParams)
{
    SC_HANDLE hScm;
    DWORD dwError;
    PTAG_INFO_NAME_FROM_TAG_IN_PARAMS InParams;
    PTAG_INFO_NAME_FROM_TAG_OUT_PARAMS OutParams;
    LPWSTR lpszName;

    /* We only support one class */
    if (dwInfoLevel != TagInfoLevelNameFromTag)
    {
        return ERROR_RETRY;
    }

    /* Validate input structure */
    if (InOutParams->InParams.dwPid == 0 || InOutParams->InParams.dwTag == 0)
    {
        return ERROR_INVALID_PARAMETER;
    }

    /* Validate output structure */
    if (InOutParams->OutParams.pszName != NULL)
    {
        return ERROR_INVALID_PARAMETER;
    }

    /* Open service manager */
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
    if (hScm == NULL)
    {
        return GetLastError();
    }

    /* Setup call parameters */
    InParams = &InOutParams->InParams;
    OutParams = NULL;

    /* Call SCM to query tag information */
    RpcTryExcept
    {
        dwError = RI_ScQueryServiceTagInfo(hScm, TagInfoLevelNameFromTag, &InParams, &OutParams);
    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        dwError = ScmRpcStatusToWinError(RpcExceptionCode());
    }
    RpcEndExcept;

    /* Quit if not a success */
    if (dwError != ERROR_SUCCESS)
    {
        goto Cleanup;
    }

    /* OutParams must be set now and we must have a name */
    if (OutParams == NULL ||
        OutParams->pszName == NULL)
    {
        dwError = ERROR_INVALID_DATA;
        goto Cleanup;
    }

    /* Copy back what SCM returned */
    lpszName = LocalAlloc(LPTR,
                          sizeof(WCHAR) * wcslen(OutParams->pszName) + sizeof(UNICODE_NULL));
    if (lpszName == NULL)
    {
        dwError = GetLastError();
        goto Cleanup;
    }

    wcscpy(lpszName, OutParams->pszName);
    InOutParams->OutParams.pszName = lpszName;
    InOutParams->OutParams.TagType = OutParams->TagType;

Cleanup:
    CloseServiceHandle(hScm);

    /* Free memory allocated by SCM */
    if (OutParams != NULL)
    {
        if (OutParams->pszName != NULL)
        {
            midl_user_free(OutParams->pszName);
        }

        midl_user_free(OutParams);
    }

    return dwError;
}

/**********************************************************************
 *  I_QueryTagInformation
 *
 * @implemented
 */
DWORD WINAPI
I_QueryTagInformation(PVOID Unused,
                      TAG_INFO_LEVEL dwInfoLevel,
                      PTAG_INFO_NAME_FROM_TAG InOutParams)
{
    /*
     * We only support one information class and it
     * needs parameters
     */
    if (dwInfoLevel != TagInfoLevelNameFromTag ||
        InOutParams == NULL)
    {
        return ERROR_INVALID_PARAMETER;
    }

    /* Validate input structure */
    if (InOutParams->InParams.dwPid == 0 || InOutParams->InParams.dwTag == 0)
    {
        return ERROR_INVALID_PARAMETER;
    }

    /* Validate output structure */
    if (InOutParams->OutParams.pszName != NULL)
    {
        return ERROR_INVALID_PARAMETER;
    }

    /* Call internal function for the RPC call */
    return I_ScQueryServiceTagInfo(Unused, TagInfoLevelNameFromTag, InOutParams);
}

/* EOF */
