/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    scwrap.c

Abstract:

    These are the Service Controller API RPC client wrapper routines.
    These are the entry points that are exported by the dll.
        ControlService
        EnumServicesStatusW
        EnumServicesStatusA
        EnumServicesStatusExW
        EnumServicesStatusExA
        EnumServiceGroupW
        OpenServiceW
        CloseServiceHandle
        OpenSCManagerW
        QueryServiceStatus
        QueryServiceStatusEx
        StartServiceW
        SetServiceStatus
        I_ScSetServiceBitsW
        I_ScSetServiceBitsA
        I_ScGetCurrentGroupStateW
        I_ScSendTSMessage
        SetServiceBits

        OpenSCManagerA
        OpenServiceA
        StartServiceA

        QueryServiceObjectSecurity
        SetServiceObjectSecurity
        ScConvertOffsetsW
        ScConvertOffsetsA
        ScConvertOffsetsExW
        ScConvertOffsetsExA
        ScConvertOffsets64

        ChangeServiceConfigA
        ChangeServiceConfigW
        ChangeServiceConfig2A
        ChangeServiceConfig2W
        CreateServiceA
        CreateServiceW
        DeleteService
        EnumDependentServicesA
        EnumDependentServicesW
        GetServiceDisplayNameA
        GetServiceDisplayNameW
        GetServiceKeyNameA
        GetServiceKeyNameW
        LockServiceDatabase
        QueryServiceConfigA
        QueryServiceConfigW
        QueryServiceConfig2A
        QueryServiceConfig2W
        QueryServiceLockStatusA
        QueryServiceLockStatusW
        UnlockServiceDatabase
        NotifyBootConfigStatus

Author:

    Dan Lafferty    (danl)  03-Feb-1992

Environment:

    User Mode - Win32

Revision History:

    07-May-1998 jschwart
        Added QueryServiceStatusEx and EnumServicesStatusEx
    11-Oct-1996 AnirudhS
        Added ChangeServiceConfig2 and QueryServiceConfig2.
    14-Feb-1996 AnirudhS
        Added EnumServiceGroupW.
    22-Sep-1995 AnirudhS
        ScWaitForStart: Fixed race condition - OpenEvent needs to be tried
        a second time after CreateEvent.
    15-Aug-1995 AnirudhS
        Added I_ScGetCurrentGroupStateW.
    05-Nov-1992 Danl
        Added display name changes (CreateService, ChangeServiceConfig) and
        new api (GetServiceDisplayName, GetServiceKeyName).
    13-Oct-1992 Danl
        Allow 0 length buffers to be passed into EnumServicesStatus and
        EnumDependentServices.
    04-Aug-1992 Danl
        Allow 0 length buffers to be passed into QueryServiceConfig and
        QueryServiceLockStatus.
    28-May-1992 JohnRo
        RAID 9829: winsvc.h and related file cleanup.
    14-Apr-1992 JohnRo
        Enable Lock and Unlock APIs.
    03-Feb-1992     Danl
        Created

--*/

//
// INCLUDES
//

extern "C"
{
#include <nt.h>         // DbgPrint prototype
#include <ntrtl.h>      // DbgPrint prototype
#include <nturtl.h>     // needed when we include windows.h
}
#include <rpc.h>        // DataTypes and runtime APIs

#include <windows.h>    // NO_ERROR
#include <svcctl.h>     // generated by the MIDL compiler
#include <lmcons.h>     // for lmserver.h
#include <srvann.h>     // MS-internal functions
#include <winsvcp.h>    // MS-internal functions
#include <rpcasync.h>   // I_RpcExceptionFilter

#include <string.h>     // needed by strarray.h
#include <scdebug.h>    // SCC_LOG
#include <sccrypt.h>    // ScEncryptPassword
#include <sclib.h>      // ScConvertToUnicode
#include <strarray.h>   // ScWStrArraySize
#include <lmerr.h>      // for lmserver.h
#include <lmserver.h>   // SV_TYPE_WORKSTATION ...
#include <scseclib.h>   // ScCreateStartEventSD
#include <scwow.h>      // 32/64-bit interop structures

//
// DEFINES
//
#define SC_START_TIMEOUT    180000      // 3 minute timeout

#define RESERVED_BITS  (SV_TYPE_WORKSTATION         |   \
                        SV_TYPE_SERVER              |   \
                        SV_TYPE_DOMAIN_CTRL         |   \
                        SV_TYPE_DOMAIN_BAKCTRL      |   \
                        SV_TYPE_TIME_SOURCE         |   \
                        SV_TYPE_AFP                 |   \
                        SV_TYPE_DOMAIN_MEMBER       |   \
                        SV_TYPE_PRINTQ_SERVER       |   \
                        SV_TYPE_DIALIN_SERVER       |   \
                        SV_TYPE_XENIX_SERVER        |   \
                        SV_TYPE_SERVER_UNIX         |   \
                        SV_TYPE_NT                  |   \
                        SV_TYPE_WFW                 |   \
                        SV_TYPE_POTENTIAL_BROWSER   |   \
                        SV_TYPE_BACKUP_BROWSER      |   \
                        SV_TYPE_MASTER_BROWSER      |   \
                        SV_TYPE_DOMAIN_MASTER       |   \
                        SV_TYPE_LOCAL_LIST_ONLY     |   \
                        SV_TYPE_DOMAIN_ENUM)



//
// LOCAL FUNCTIONS
//

VOID
ScConvertOffsetsW(
    LPENUM_SERVICE_STATUSW  lpServices,
    DWORD                   NumStructs
    );

VOID
ScConvertOffsetsA(
    LPENUM_SERVICE_STATUSA  lpServices,
    DWORD                   NumStructs
    );

VOID
ScConvertOffsetsExW(
    LPENUM_SERVICE_STATUS_PROCESSW  lpServices,
    DWORD                           NumStructs
    );

VOID
ScConvertOffsetsExA(
    LPENUM_SERVICE_STATUS_PROCESSA  lpServices,
    DWORD                           NumStructs
    );


#ifdef _WIN64

//
// API numbers for ScConvertOffsets64
//

typedef enum
{
    SC_API_ENUM_W = 0,
    SC_API_ENUM_A,
    SC_API_ENUM_GROUP,
    SC_API_ENUM_DEPEND_W,
    SC_API_ENUM_DEPEND_A,
    SC_API_ENUM_PROCESS_W,
    SC_API_ENUM_PROCESS_A,
    SC_API_QUERY_DESCRIPTION_W,
    SC_API_QUERY_DESCRIPTION_A,
    SC_API_QUERY_FAILURE_ACTIONS_W,
    SC_API_QUERY_FAILURE_ACTIONS_A,
    SC_API_MAX
}
SC_API_NUMBER;


BOOL
ScConvertOffsets64(
    SC_API_NUMBER  scApi,
    SC_HANDLE      hSCManager,
    DWORD          dwServiceType,
    DWORD          dwServiceState,
    LPBYTE         lpServices,
    DWORD          cbBufSize,
    LPDWORD        pcbBytesNeeded,
    LPDWORD        lpServicesReturned,
    LPDWORD        lpResumeIndex,
    LPVOID         pszGroupName,
    LPDWORD        lpdwError
    );

#endif // _WIN64


DWORD
ScMapRpcError(
    IN DWORD RpcError,
    IN DWORD BadContextError
    );

DWORD
ScWaitForStart(
    VOID
    );

//
// Globals
//

extern "C"
{

void
SccInit(
	DWORD dwReason
    )
{

    return;
}

}


BOOL
WINAPI
ControlService(
    IN  SC_HANDLE           hService,
    IN  DWORD               dwControl,
    OUT LPSERVICE_STATUS    lpServiceStatus
    )

/*++

Routine Description:

    This is the DLL entrypoint for Control Service

Arguments:


Return Value:



--*/
{
    DWORD      status;


    RpcTryExcept {

        status = RControlService (
                    (SC_RPC_HANDLE)hService,
                    dwControl,
                    lpServiceStatus);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


BOOL
WINAPI
EnumServicesStatusW(
    IN      SC_HANDLE               hSCManager,
    IN      DWORD                   dwServiceType,
    IN      DWORD                   dwServiceState,
    OUT     LPENUM_SERVICE_STATUSW  lpServices,
    IN      DWORD                   cbBufSize,
    OUT     LPDWORD                 pcbBytesNeeded,
    OUT     LPDWORD                 lpServicesReturned,
    IN OUT  LPDWORD                 lpResumeIndex
    )
/*++

Routine Description:

    This is the DLL entrypoint for EnumServicesStatusW

Arguments:


Return Value:


Note:


--*/
{
    return EnumServiceGroupW(
                hSCManager,
                dwServiceType,
                dwServiceState,
                lpServices,
                cbBufSize,
                pcbBytesNeeded,
                lpServicesReturned,
                lpResumeIndex,
                NULL);
}


BOOL
WINAPI
EnumServicesStatusExW(
    IN      SC_HANDLE                  hSCManager,
    IN      SC_ENUM_TYPE               InfoLevel,
    IN      DWORD                      dwServiceType,
    IN      DWORD                      dwServiceState,
    OUT     LPBYTE                     lpServices,
    IN      DWORD                      cbBufSize,
    OUT     LPDWORD                    pcbBytesNeeded,
    OUT     LPDWORD                    lpServicesReturned,
    IN OUT  LPDWORD                    lpResumeIndex,
    IN      LPCWSTR                    pszGroupName
    )
/*++

Routine Description:

    This is the DLL entrypoint for EnumServicesStatusExW

Arguments:


Return Value:


Note:


--*/
{
    DWORD                           status;
    LPENUM_SERVICE_STATUS_PROCESSW  pEnumBuf;
    ENUM_SERVICE_STATUS_PROCESSW    enumBuf;
    DWORD                           tempBufSize;

#ifdef _WIN64

    DWORD                           dwOldResumeIndex = 0;

    if (lpResumeIndex != NULL)
    {
        dwOldResumeIndex = *lpResumeIndex;
    }

#endif // _WIN64

    //
    // Make sure we were passed a valid InfoLevel
    //
    if (InfoLevel != SC_ENUM_PROCESS_INFO)
    {
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }

    tempBufSize = cbBufSize;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(ENUM_SERVICE_STATUS_PROCESSW) || (lpServices == NULL)) {
        pEnumBuf = &enumBuf;
        tempBufSize = sizeof(ENUM_SERVICE_STATUS_PROCESSW);
    }
    else {
        pEnumBuf = (LPENUM_SERVICE_STATUS_PROCESSW)lpServices;
    }


    RpcTryExcept {

        status = REnumServicesStatusExW (
                        hSCManager,
                        InfoLevel,
                        dwServiceType,
                        dwServiceState,
                        (LPBYTE)pEnumBuf,
                        tempBufSize,
                        pcbBytesNeeded,
                        lpServicesReturned,
                        lpResumeIndex,
                        pszGroupName);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    //
    // If data is returned, convert Offsets in the Enum buffer to pointers.
    //
    if ((status == NO_ERROR) || (status == ERROR_MORE_DATA))
    {
        if ((*lpServicesReturned) > 0)
        {

#ifdef _WIN64

            DWORD dwError;

            if (!ScConvertOffsets64(SC_API_ENUM_PROCESS_W,
                                    hSCManager,
                                    dwServiceType,
                                    dwServiceState,
                                    (LPBYTE) lpServices,
                                    cbBufSize,
                                    pcbBytesNeeded,
                                    lpServicesReturned,
                                    &dwOldResumeIndex,
                                    (LPVOID) pszGroupName,
                                    &dwError))
            {
                status = dwError;

                if (lpResumeIndex != NULL)
                {
                    *lpResumeIndex = dwOldResumeIndex;
                }
            }

#else  // ndef _WIN64

            ScConvertOffsetsExW((LPENUM_SERVICE_STATUS_PROCESSW) lpServices,
                                *lpServicesReturned);

#endif // _WIN64

        }

#ifdef _WIN64

        //
        // The byte count returned is the size needed to hold all of
        // the 32-bit structures rather than the 64-bit ones.  Assume
        // a buffer full of fixed-length structures (i.e., no variable-
        // length data) and scale from 32- to 64-bit sizes to get the
        // minimum guaranteed size for all the 64-bit structures.
        //

        *pcbBytesNeeded = *pcbBytesNeeded
                              * sizeof(ENUM_SERVICE_STATUS_PROCESSW)
                              / sizeof(ENUM_SERVICE_STATUS_PROCESS_WOW64);

#endif // _WIN64

    }

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return FALSE;
    }

    return TRUE;
}


BOOL
WINAPI
EnumServiceGroupW(
    IN      SC_HANDLE               hSCManager,
    IN      DWORD                   dwServiceType,
    IN      DWORD                   dwServiceState,
    OUT     LPENUM_SERVICE_STATUSW  lpServices,
    IN      DWORD                   cbBufSize,
    OUT     LPDWORD                 pcbBytesNeeded,
    OUT     LPDWORD                 lpServicesReturned,
    IN OUT  LPDWORD                 lpResumeIndex,
    IN      LPCWSTR                 pszGroupName
    )
/*++

Routine Description:

    This is the DLL entrypoint for EnumServiceGroupW

Arguments:


Return Value:


Note:

--*/
{
    DWORD                   status;
    LPENUM_SERVICE_STATUSW  pEnumBuf;
    ENUM_SERVICE_STATUSW    enumBuf;
    DWORD                   tempBufSize;

#ifdef _WIN64

    DWORD                   dwOldResumeIndex = 0;

    if (lpResumeIndex != NULL)
    {
        dwOldResumeIndex = *lpResumeIndex;
    }

#endif // _WIN64


    tempBufSize = cbBufSize;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(ENUM_SERVICE_STATUSW) || (lpServices == NULL)) {
        pEnumBuf = &enumBuf;
        tempBufSize = sizeof(ENUM_SERVICE_STATUSW);
    }
    else {
        pEnumBuf = lpServices;
    }


    RpcTryExcept {

        if (pszGroupName == NULL) {
            //
            // Call the downlevel API, so that the call will work on targeted
            // machines running Windows NT 3.51 or earlier
            //
            status = REnumServicesStatusW (
                        (SC_RPC_HANDLE)hSCManager,
                        dwServiceType,
                        dwServiceState,
                        (LPBYTE)pEnumBuf,
                        tempBufSize,
                        pcbBytesNeeded,
                        lpServicesReturned,
                        lpResumeIndex);
        }
        else {
            status = REnumServiceGroupW (
                        (SC_RPC_HANDLE)hSCManager,
                        dwServiceType,
                        dwServiceState,
                        (LPBYTE)pEnumBuf,
                        tempBufSize,
                        pcbBytesNeeded,
                        lpServicesReturned,
                        lpResumeIndex,
                        pszGroupName);
        }
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    //
    // If data is returned, convert Offsets in the Enum buffer to pointers.
    //
    if ((status == NO_ERROR) || (status == ERROR_MORE_DATA))
    {
        if ((*lpServicesReturned) > 0)
        {

#ifdef _WIN64

            DWORD dwError;

            if (!ScConvertOffsets64(pszGroupName ? SC_API_ENUM_GROUP :
                                                   SC_API_ENUM_W,
                                    hSCManager,
                                    dwServiceType,
                                    dwServiceState,
                                    (LPBYTE) lpServices,
                                    cbBufSize,
                                    pcbBytesNeeded,
                                    lpServicesReturned,
                                    &dwOldResumeIndex,
                                    (LPVOID) pszGroupName,
                                    &dwError))
            {
                status = dwError;

                if (lpResumeIndex != NULL)
                {
                    *lpResumeIndex = dwOldResumeIndex;
                }
            }

#else  // ndef _WIN64

            ScConvertOffsetsW(lpServices, *lpServicesReturned);

#endif // _WIN64

        }

#ifdef _WIN64

        //
        // The byte count returned is the size needed to hold all of
        // the 32-bit structures rather than the 64-bit ones.  Assume
        // a buffer full of fixed-length structures (i.e., no variable-
        // length data) and scale from 32- to 64-bit sizes to get the
        // minimum guaranteed size for all the 64-bit structures.
        //

        *pcbBytesNeeded = *pcbBytesNeeded
                              * sizeof(ENUM_SERVICE_STATUSW)
                              / sizeof(ENUM_SERVICE_STATUS_WOW64);

#endif // _WIN64

    }

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


SC_HANDLE
WINAPI
OpenServiceW(
    IN  SC_HANDLE   hSCManager,
    IN  LPCWSTR     lpServiceName,
    IN  DWORD       dwDesiredAccess
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/

{
    DWORD           status;
    SC_RPC_HANDLE   hService=NULL;


    RpcTryExcept {

        status = ROpenServiceW (
                    (SC_RPC_HANDLE)hSCManager,
                    (LPWSTR) lpServiceName,
                    dwDesiredAccess,
                    &hService);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(NULL);
    }

    return (SC_HANDLE)hService;
}

BOOL
WINAPI
CloseServiceHandle(
    IN  SC_HANDLE   hSCObject
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/

{
    DWORD      status;


    RpcTryExcept {

        status = RCloseServiceHandle((LPSC_RPC_HANDLE)&hSCObject);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


SC_HANDLE
WINAPI
OpenSCManagerW(
    IN  LPCWSTR lpMachineName,
    IN  LPCWSTR lpDatabaseName OPTIONAL,
    IN  DWORD   dwDesiredAccess
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD           status;
    SC_RPC_HANDLE   ScHandle=NULL;


    //
    // Check to see if the local Service Controller is started yet.
    // If not, then wait for it to start (or timeout).
    //

    status = ScWaitForStart();

    if (status != NO_ERROR) {
        SetLastError(status);
        return(NULL);
    }

    RpcTryExcept {

        status = ROpenSCManagerW (
                    (LPWSTR) lpMachineName,
                    (LPWSTR) lpDatabaseName,
                    dwDesiredAccess,
                    &ScHandle);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(NULL);
    }

    return (SC_HANDLE)ScHandle;
}

BOOL
WINAPI
QueryServiceStatus(
    IN  SC_HANDLE           hService,
    OUT LPSERVICE_STATUS    lpServiceStatus
    )
/*++

Routine Description:

    This is the DLL entrypoint for QueryServiceStatus.

Arguments:


Return Value:


--*/

{
    DWORD          status;

    RpcTryExcept {

        status = RQueryServiceStatus (
            (SC_RPC_HANDLE)hService,
            lpServiceStatus);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


BOOL
WINAPI
QueryServiceStatusEx(
    IN      SC_HANDLE       hService,
    IN      SC_STATUS_TYPE  InfoLevel,
    OUT     LPBYTE          lpBuffer,
    IN      DWORD           cbBufSize,
    OUT     LPDWORD         pcbBytesNeeded
    )
/*++

Routine Description:

    This is the DLL entrypoint for QueryServiceStatusEx.

Arguments:


Return Value:


Note:

--*/
{
    DWORD                        status;

    switch (InfoLevel) {

        case SC_STATUS_PROCESS_INFO:

            if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS)) {

                //
                // The buffer is too small -- since the structure is a fixed
                // size, we can handle this error on the client side
                //
                *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);

                SetLastError(ERROR_INSUFFICIENT_BUFFER);
                return FALSE;
            }

            break;

        default:
            SetLastError(ERROR_INVALID_LEVEL);
            return FALSE;
    }

    RpcTryExcept {

        status = RQueryServiceStatusEx (
            (SC_RPC_HANDLE)hService,
            InfoLevel,
            lpBuffer,
            cbBufSize,
            pcbBytesNeeded);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR) {
        SetLastError(status);
        return FALSE;
    }

    return TRUE;
}


BOOL
WINAPI
StartServiceW(
    IN SC_HANDLE            hService,
    IN DWORD                dwNumServiceArgs,
    IN LPCWSTR              *lpServiceArgVectors
    )
/*++

Routine Description:

    This is the DLL entrypoint for StartServiceW

Arguments:

    servername - Points to a string containing the name of the computer
        that is to execute the API function.

    service- Points to a string containing the name of the service
        that is to be started.

    argc - Indicates the number or argument vectors in argv.

    argv - A pointer to an array of pointers to strings.  These
        are command line arguments that are to be passed to the service.

    bufptr - This is the address where a pointer to the service's
        information buffer (SERVICE_INFO_2) is to be placed.

Return Value:



--*/

{
    DWORD          status;



    RpcTryExcept {

        status = RStartServiceW (
                    (SC_RPC_HANDLE)hService,
                    dwNumServiceArgs,
                    (LPSTRING_PTRSW)lpServiceArgVectors);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


BOOL
WINAPI
SetServiceStatus(
    IN SERVICE_STATUS_HANDLE    hServiceStatus,
    IN LPSERVICE_STATUS         lpServiceStatus
    )
/*++

Routine Description:

    This is the DLL entrypoint for SetServiceStatus.  It is called from
    a service when that service changes its state or receives a control.
    The status is maintained by the service controller.

Arguments:

    hServiceStatus - This is a handle that was obtained from calling
        the RegisterControlHandler function.

    lpServiceStatus - This is a pointer to a service status structure.

Return Value:


--*/
{
    DWORD      status;

    //
    // Do the RPC call with an exception handler since RPC will raise an
    // exception if anything fails. It is up to us to figure out what
    // to do once the exception is raised.
    //
    RpcTryExcept {
        status = RSetServiceStatus (
                    (SC_HANDLE)hServiceStatus,
                    lpServiceStatus);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED) {

        //
        // Service is stopping -- close the handle we opened
        // in StartServiceCtrlDispatcher when the service started
        //
        CloseServiceHandle((SC_HANDLE)hServiceStatus);
    }

    return(TRUE);
}

BOOL
I_ScSetServiceBitsA(
    IN SERVICE_STATUS_HANDLE    hServiceStatus,
    IN DWORD                    dwServiceBits,
    IN BOOL                     bSetBitsOn,
    IN BOOL                     bUpdateImmediately,
    IN LPSTR                    pszTransportName
    )

/*++

Routine Description:

    This is an internal routine that sets the Server Announcement bits
    in the service controller.

Arguments:

    hServiceStatus -

    dwServiceBits -

Return Value:


Note:


--*/
{
    DWORD   status;
    DWORD   setBitsOnFlag=0;
    DWORD   updateImmediatelyFlag=0;

    if(bSetBitsOn) {
        setBitsOnFlag = 1;
    }

    if(bUpdateImmediately) {
        updateImmediatelyFlag = 1;
    }
    //
    // Do the RPC call with an exception handler since RPC will raise an
    // exception if anything fails. It is up to us to figure out what
    // to do once the exception is raised.
    //
    RpcTryExcept {
        status = RI_ScSetServiceBitsA (
                    (SC_HANDLE)hServiceStatus,
                    dwServiceBits,
                    setBitsOnFlag,
                    updateImmediatelyFlag,
                    pszTransportName);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}

BOOL
I_ScSetServiceBitsW(
    IN SERVICE_STATUS_HANDLE    hServiceStatus,
    IN DWORD                    dwServiceBits,
    IN BOOL                     bSetBitsOn,
    IN BOOL                     bUpdateImmediately,
    IN LPWSTR                   pszTransportName
    )

/*++

Routine Description:

    This is an internal routine that sets the Server Announcement bits
    in the service controller.

Arguments:

    hServiceStatus -

    dwServiceBits -

Return Value:


Note:


--*/
{
    DWORD   status;
    DWORD   setBitsOnFlag=0;
    DWORD   updateImmediatelyFlag=0;

    if(bSetBitsOn) {
        setBitsOnFlag = 1;
    }

    if(bUpdateImmediately) {
        updateImmediatelyFlag = 1;
    }

    //
    // Do the RPC call with an exception handler since RPC will raise an
    // exception if anything fails. It is up to us to figure out what
    // to do once the exception is raised.
    //
    RpcTryExcept {
        status = RI_ScSetServiceBitsW (
                    (SC_HANDLE)hServiceStatus,
                    dwServiceBits,
                    setBitsOnFlag,
                    updateImmediatelyFlag,
                    pszTransportName);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


DWORD
I_ScGetCurrentGroupStateW(
    IN  SC_HANDLE               hSCManager,
    IN  LPWSTR                  pszGroupName,
    OUT LPDWORD                 pdwCurrentState
    )
/*++

Routine Description:

    This is obsolete but some (MS) apps still statically link to it.

--*/
{
    return ERROR_NOT_SUPPORTED;
}


extern "C" {

DWORD
I_ScSendTSMessage(
    DWORD        OpCode,
    DWORD        dwEvent,
    DWORD        cbData,
    LPBYTE       lpData
    )

/*++

Routine Description:

    Private entrypoint for Terminal Server to tell the SCM to send
    console switch notification to services that are interested.

--*/

{
    DWORD     status;
    SC_HANDLE hSCManager;

    hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

    if (hSCManager == NULL)
    {
        return GetLastError();
    }

    //
    // Do the RPC call with an exception handler since RPC will raise an
    // exception if anything fails. It is up to us to figure out what
    // to do once the exception is raised.
    //
    RpcTryExcept
    {
        status = RI_ScSendTSMessage(hSCManager,
                                    OpCode,
                                    dwEvent,
                                    cbData,
                                    lpData);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
    {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    CloseServiceHandle(hSCManager);

    return status;
}

}  // extern "C"


BOOL
SetServiceBits(
    IN SERVICE_STATUS_HANDLE    hServiceStatus,
    IN DWORD                    dwServiceBits,
    IN BOOL                     bSetBitsOn,
    IN BOOL                     bUpdateImmediately
    )

/*++

Routine Description:

    This is an internal routine that sets the Server Announcement bits
    in the service controller.

Arguments:

    hServiceStatus -

    dwServiceBits -

Return Value:


Note:


--*/
{
    if (dwServiceBits & RESERVED_BITS) {
        SetLastError(ERROR_INVALID_DATA);
        return(FALSE);
    }
    return(I_ScSetServiceBitsW(
            hServiceStatus,
            dwServiceBits,
            bSetBitsOn,
            bUpdateImmediately,
            (LPWSTR)NULL));
}


SC_HANDLE
WINAPI
OpenSCManagerA(
    IN  LPCSTR  lpMachineName,
    IN  LPCSTR  lpDatabaseName OPTIONAL,
    IN  DWORD   dwDesiredAccess
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD           status;
    SC_RPC_HANDLE   ScHandle=NULL;


    //
    // Check to see if the local Service Controller is started yet.
    // If not, then wait for it to start (or timeout).
    //

    status = ScWaitForStart();

    if (status != NO_ERROR) {
        SetLastError(status);
        return(NULL);
    };


    RpcTryExcept {

        status = ROpenSCManagerA (
                    (LPSTR) lpMachineName,
                    (LPSTR) lpDatabaseName,
                    dwDesiredAccess,
                    &ScHandle);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(NULL);
    }

    return (SC_HANDLE)ScHandle;
}



SC_HANDLE
WINAPI
OpenServiceA(
    IN  SC_HANDLE   hSCManager,
    IN  LPCSTR      lpServiceName,
    IN  DWORD       dwDesiredAccess
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{

    DWORD           status;
    SC_RPC_HANDLE   hService=NULL;


    RpcTryExcept {

        status = ROpenServiceA (
                    (SC_RPC_HANDLE)hSCManager,
                    (LPSTR) lpServiceName,
                    dwDesiredAccess,
                    &hService);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(NULL);
    }

    return (SC_HANDLE)hService;
}



BOOL
WINAPI
StartServiceA(
    IN SC_HANDLE            hService,
    IN DWORD                dwNumServiceArgs,
    IN LPCSTR               *lpServiceArgVectors
    )
/*++

Routine Description:



Arguments:

    servername - Points to a string containing the name of the computer
        that is to execute the API function.

    service- Points to a string containing the name of the service
        that is to be started.

    argc - Indicates the number or argument vectors in argv.

    argv - A pointer to an array of pointers to strings.  These
        are command line arguments that are to be passed to the service.

    bufptr - This is the address where a pointer to the service's
        information buffer (SERVICE_INFO_2) is to be placed.

Return Value:



--*/
{
    DWORD          status;

    RpcTryExcept {

        status = RStartServiceA (
                    (SC_RPC_HANDLE)hService,
                    dwNumServiceArgs,
                    (LPSTRING_PTRSA)lpServiceArgVectors);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}



BOOL
WINAPI
EnumServicesStatusA(
    IN      SC_HANDLE               hSCManager,
    IN      DWORD                   dwServiceType,
    IN      DWORD                   dwServiceState,
    OUT     LPENUM_SERVICE_STATUSA  lpServices,
    IN      DWORD                   cbBufSize,
    OUT     LPDWORD                 pcbBytesNeeded,
    OUT     LPDWORD                 lpServicesReturned,
    IN OUT  LPDWORD                 lpResumeIndex
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{

    DWORD                   status;
    LPENUM_SERVICE_STATUSA  pEnumBuf;
    ENUM_SERVICE_STATUSA    enumBuf;
    DWORD                   tempBufSize;

#ifdef _WIN64

    DWORD                   dwOldResumeIndex = 0;

    if (lpResumeIndex != NULL)
    {
        dwOldResumeIndex = *lpResumeIndex;
    }

#endif // _WIN64


    tempBufSize = cbBufSize;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(ENUM_SERVICE_STATUSA) || (lpServices == NULL)) {
        pEnumBuf = &enumBuf;
        tempBufSize = sizeof(ENUM_SERVICE_STATUSA);
    }
    else {
        pEnumBuf = lpServices;
    }

    RpcTryExcept {

        status = REnumServicesStatusA (
                    (SC_RPC_HANDLE)hSCManager,
                    dwServiceType,
                    dwServiceState,
                    (LPBYTE)pEnumBuf,
                    tempBufSize,
                    pcbBytesNeeded,
                    lpServicesReturned,
                    lpResumeIndex);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    //
    // If data is returned, convert Offsets in the Enum buffer to pointers.
    //
    if ((status == NO_ERROR) || (status == ERROR_MORE_DATA))
    {
        if ((*lpServicesReturned) > 0)
        {

#ifdef _WIN64

            DWORD dwError;

            if (!ScConvertOffsets64(SC_API_ENUM_A,
                                    hSCManager,
                                    dwServiceType,
                                    dwServiceState,
                                    (LPBYTE) lpServices,
                                    cbBufSize,
                                    pcbBytesNeeded,
                                    lpServicesReturned,
                                    &dwOldResumeIndex,
                                    NULL,
                                    &dwError))
            {
                status = dwError;

                if (lpResumeIndex != NULL)
                {
                    *lpResumeIndex = dwOldResumeIndex;
                }
            }

#else  // ndef _WIN64

            ScConvertOffsetsA(lpServices, *lpServicesReturned);

#endif // _WIN64

        }

#ifdef _WIN64

        //
        // The byte count returned is the size needed to hold all of
        // the 32-bit structures rather than the 64-bit ones.  Assume
        // a buffer full of fixed-length structures (i.e., no variable-
        // length data) and scale from 32- to 64-bit sizes to get the
        // minimum guaranteed size for all the 64-bit structures.
        //

        *pcbBytesNeeded = *pcbBytesNeeded
                              * sizeof(ENUM_SERVICE_STATUSA)
                              / sizeof(ENUM_SERVICE_STATUS_WOW64);

#endif // _WIN64

    }

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


BOOL
WINAPI
EnumServicesStatusExA(
    IN      SC_HANDLE                  hSCManager,
    IN      SC_ENUM_TYPE               InfoLevel,
    IN      DWORD                      dwServiceType,
    IN      DWORD                      dwServiceState,
    OUT     LPBYTE                     lpServices,
    IN      DWORD                      cbBufSize,
    OUT     LPDWORD                    pcbBytesNeeded,
    OUT     LPDWORD                    lpServicesReturned,
    IN OUT  LPDWORD                    lpResumeIndex,
    IN      LPCTSTR                    pszGroupName
    )
/*++

Routine Description:



Arguments:



Return Value:



--*/
{

    DWORD                           status;
    LPENUM_SERVICE_STATUS_PROCESSA  pEnumBuf;
    ENUM_SERVICE_STATUS_PROCESSA    enumBuf;
    DWORD                           tempBufSize;

#ifdef _WIN64

    DWORD                           dwOldResumeIndex = 0;

    if (lpResumeIndex != NULL)
    {
        dwOldResumeIndex = *lpResumeIndex;
    }

#endif // _WIN64


    //
    // Make sure we were passed a valid InfoLevel
    //
    if (InfoLevel != SC_ENUM_PROCESS_INFO)
    {
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }

    tempBufSize = cbBufSize;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(ENUM_SERVICE_STATUS_PROCESSA) || (lpServices == NULL)) {
        pEnumBuf = &enumBuf;
        tempBufSize = sizeof(ENUM_SERVICE_STATUS_PROCESSA);
    }
    else {
        pEnumBuf = (LPENUM_SERVICE_STATUS_PROCESSA) lpServices;
    }

    RpcTryExcept {

        status = REnumServicesStatusExA (
                    (SC_RPC_HANDLE)hSCManager,
                    InfoLevel,
                    dwServiceType,
                    dwServiceState,
                    (LPBYTE)pEnumBuf,
                    tempBufSize,
                    pcbBytesNeeded,
                    lpServicesReturned,
                    lpResumeIndex,
                    pszGroupName);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    //
    // If data is returned, convert Offsets in the Enum buffer to pointers.
    //
    if ((status == NO_ERROR) || (status == ERROR_MORE_DATA))
    {
        if ((*lpServicesReturned) > 0)
        {

#ifdef _WIN64

            DWORD dwError;

            if (!ScConvertOffsets64(SC_API_ENUM_PROCESS_A,
                                    hSCManager,
                                    dwServiceType,
                                    dwServiceState,
                                    (LPBYTE) lpServices,
                                    cbBufSize,
                                    pcbBytesNeeded,
                                    lpServicesReturned,
                                    &dwOldResumeIndex,
                                    (LPVOID) pszGroupName,
                                    &dwError))
            {
                status = dwError;

                if (lpResumeIndex != NULL)
                {
                    *lpResumeIndex = dwOldResumeIndex;
                }
            }

#else  // ndef _WIN64

            ScConvertOffsetsExA((LPENUM_SERVICE_STATUS_PROCESSA) lpServices,
                                *lpServicesReturned);

#endif // _WIN64

        }

#ifdef _WIN64

        //
        // The byte count returned is the size needed to hold all of
        // the 32-bit structures rather than the 64-bit ones.  Assume
        // a buffer full of fixed-length structures (i.e., no variable-
        // length data) and scale from 32- to 64-bit sizes to get the
        // minimum guaranteed size for all the 64-bit structures.
        //

        *pcbBytesNeeded = *pcbBytesNeeded
                              * sizeof(ENUM_SERVICE_STATUS_PROCESSA)
                              / sizeof(ENUM_SERVICE_STATUS_PROCESS_WOW64);

#endif // _WIN64

    }

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return FALSE;
    }

    return TRUE;
}


BOOL
WINAPI
QueryServiceObjectSecurity(
    IN  SC_HANDLE hService,
    IN  SECURITY_INFORMATION dwSecurityInformation,
    OUT PSECURITY_DESCRIPTOR lpSecurityDescriptor,
    IN  DWORD cbBufSize,
    OUT LPDWORD pcbBytesNeeded
    )
/*++

Routine Description:

    This is the DLL entrypoint for the QueryServiceObjectSecurity API.

    This function returns to the caller requested security information
    currently assigned to an object.

    Based on the caller's access rights this procedure
    will return a security descriptor containing any or all of the
    object's owner ID, group ID, discretionary ACL or system ACL.  To
    read the owner ID, group ID, or the discretionary ACL the caller
    must be granted READ_CONTROL access to the object.  To read the
    system ACL the caller must be granted ACCESS_SYSTEM_SECURITY
    access.

Arguments:

    hService - Supplies a handle to an existing service object.

    dwSecurityInformation - Supplies a value describing which pieces of
        security information are being queried.

    lpSecurityInformation - Supplies the output buffer from the user
        which security descriptor information will be written to on
        return.

    cbBufSize - Supplies the size of lpSecurityInformation buffer.

    pcbBytesNeeded - Returns the number of bytes needed of the
        lpSecurityInformation buffer to get all the requested
        information.

Return Value:

    NO_ERROR - The operation was successful.

    ERROR_INVALID_HANDLE - The specified handle was invalid.

    ERROR_ACCESS_DENIED - The specified handle was not opened for
        either READ_CONTROL or ACCESS_SYSTEM_SECURITY
        access.

    ERROR_INVALID_PARAMETER - The dwSecurityInformation parameter is
        invalid.

    ERROR_INSUFFICIENT_BUFFER - The specified output buffer is smaller
        than the required size returned in pcbBytesNeeded.  None of
        the security descriptor is returned.

--*/
{
    DWORD status;

    RpcTryExcept {

        status = RQueryServiceObjectSecurity(
                     (SC_RPC_HANDLE) hService,
                     (DWORD) dwSecurityInformation,
                     (LPBYTE) lpSecurityDescriptor,
                     cbBufSize,
                     pcbBytesNeeded
                     );
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


BOOL
WINAPI
SetServiceObjectSecurity(
    IN SC_HANDLE hService,
    IN SECURITY_INFORMATION dwSecurityInformation,
    IN PSECURITY_DESCRIPTOR lpSecurityDescriptor
    )
/*++

Routine Description:

    This is the DLL entrypoint for the SetServiceObjectSecurity API.

    This function takes a well-formed Security Descriptor provided by the
    caller and assigns specified portions of it to an existing service
    object.  Based on the flags set in the SecurityInformation
    parameter and the caller's access rights, this procedure will
    replace any or all of the security information associated with an
    object.

    This is the only function available to users and applications for
    changing security information, including the owner ID, group ID, and
    the discretionary and system ACLs of an object.  The caller must
    have WRITE_OWNER access to the object to change the owner or primary
    group of the object.  The caller must have WRITE_DAC access to the
    object to change the discretionary ACL.  The caller must have
    ACCESS_SYSTEM_SECURITY access to an object to assign a system ACL
    to the object.

Parameters:

    hService - Supplies a handle to an existing service object.

    dwSecurityInformation - Supplies a value describing which pieces of
        security information are being set.

    lpSecurityInformation - Supplies a pointer to a well-formed security
        descriptor.


Return Values:

    NO_ERROR - The operation was successful.

    ERROR_INVALID_HANDLE - The specified handle was invalid.

    ERROR_ACCESS_DENIED - The specified handle was not opened for
        either WRITE_OWNER, WRITE_DAC, or ACCESS_SYSTEM_SECURITY
        access.

    ERROR_INVALID_PARAMETER - The lpSecurityDescriptor or dwSecurityInformation
        parameter is invalid.

    ERROR_NOT_ENOUGH_MEMORY - Not enough memory to complete the API call.

--*/
{
    DWORD status;
    NTSTATUS ntstatus;

    DWORD UserSdSize = 0;
    PSECURITY_DESCRIPTOR SelfRelativeSd;


    //
    // Find out the length of the user supplied security descriptor
    //
    ntstatus = RtlMakeSelfRelativeSD(
                   lpSecurityDescriptor,
                   NULL,
                   &UserSdSize
                   );

    if (ntstatus != STATUS_BUFFER_TOO_SMALL) {

        //
        // lpSecurityDescriptor is invalid
        //
        SetLastError(ERROR_INVALID_PARAMETER);
        return(FALSE);
    }

    SelfRelativeSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_ZEROINIT, (UINT) UserSdSize);

    if (SelfRelativeSd == NULL) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return(FALSE);
    }

    //
    // Make a self-relative security descriptor for the RPC call
    //
    ntstatus = RtlMakeSelfRelativeSD(
                   lpSecurityDescriptor,
                   SelfRelativeSd,
                   &UserSdSize
                   );

    if (! NT_SUCCESS(ntstatus)) {
        LocalFree(SelfRelativeSd);
        SetLastError(RtlNtStatusToDosError(ntstatus));
        return(FALSE);
    }


    //
    // Call the server
    //
    RpcTryExcept {

        status = RSetServiceObjectSecurity(
                     (SC_RPC_HANDLE) hService,
                     (DWORD) dwSecurityInformation,
                     (LPBYTE) SelfRelativeSd,
                     UserSdSize
                     );
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    (void) LocalFree(SelfRelativeSd);

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


VOID
ScConvertOffsetsW(
    LPENUM_SERVICE_STATUSW  lpServices,
    DWORD                   NumStructs
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LPBYTE  pBuffer;
    DWORD   i;

    pBuffer = (LPBYTE)lpServices;

    for (i=0; i<NumStructs; i++ ) {
        lpServices[i].lpServiceName = (LPWSTR)(pBuffer +
                                      (DWORD)(ULONG_PTR)(lpServices[i].lpServiceName));
        lpServices[i].lpDisplayName = (LPWSTR)(pBuffer +
                                      (DWORD)(ULONG_PTR)(lpServices[i].lpDisplayName));
    }
}

VOID
ScConvertOffsetsA(
    LPENUM_SERVICE_STATUSA  lpServices,
    DWORD                   NumStructs
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LPBYTE  pBuffer;
    DWORD   i;

    pBuffer = (LPBYTE)lpServices;

    for (i=0; i<NumStructs; i++ ) {
        lpServices[i].lpServiceName = (LPSTR)(pBuffer +
                                      (DWORD)(ULONG_PTR)(lpServices[i].lpServiceName));
        lpServices[i].lpDisplayName = (LPSTR)(pBuffer +
                                      (DWORD)(ULONG_PTR)(lpServices[i].lpDisplayName));
    }
}

VOID
ScConvertOffsetsExW(
    LPENUM_SERVICE_STATUS_PROCESSW  lpServices,
    DWORD                      NumStructs
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LPBYTE  pBuffer;
    DWORD   i;

    pBuffer = (LPBYTE)lpServices;

    for (i=0; i<NumStructs; i++ ) {
        lpServices[i].lpServiceName = (LPWSTR)(pBuffer +
                                      (DWORD)(ULONG_PTR)(lpServices[i].lpServiceName));
        lpServices[i].lpDisplayName = (LPWSTR)(pBuffer +
                                      (DWORD)(ULONG_PTR)(lpServices[i].lpDisplayName));
    }
}

VOID
ScConvertOffsetsExA(
    LPENUM_SERVICE_STATUS_PROCESSA  lpServices,
    DWORD                      NumStructs
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    LPBYTE  pBuffer;
    DWORD   i;

    pBuffer = (LPBYTE)lpServices;

    for (i=0; i<NumStructs; i++ ) {
        lpServices[i].lpServiceName = (LPSTR)(pBuffer +
                                      (DWORD)(ULONG_PTR)(lpServices[i].lpServiceName));
        lpServices[i].lpDisplayName = (LPSTR)(pBuffer +
                                      (DWORD)(ULONG_PTR)(lpServices[i].lpDisplayName));
    }
}


#ifdef _WIN64

BOOL
ScConvertOffsets64(
    SC_API_NUMBER  scApi,
    SC_HANDLE      hSCManager,
    DWORD          dwServiceType,
    DWORD          dwServiceState,
    LPBYTE         lpServices,
    DWORD          cbBufSize,
    LPDWORD        pcbBytesNeeded,
    LPDWORD        lpServicesReturned,
    LPDWORD        lpResumeIndex,
    LPVOID         pszGroupName,
    LPDWORD        lpdwError
    )
/*++

Routine Description:

    Perform API-specific offset-to-pointer conversions for the
    client side of the SCM APIs on 64-bit clients.

Arguments:



Return Value:

    TRUE if the conversion succeeded, FALSE otherwise (with lpdwError
    holding the true error).

--*/
{
    switch (scApi)
    {
        case SC_API_ENUM_W:
        case SC_API_ENUM_A:
        case SC_API_ENUM_GROUP:
        case SC_API_ENUM_PROCESS_W:
        case SC_API_ENUM_PROCESS_A:
        case SC_API_ENUM_DEPEND_W:
        case SC_API_ENUM_DEPEND_A:
        {
            //
            // Convert buffer returned by EnumServicesStatusW
            //

            LPBYTE  lpIter = lpServices;
            LPBYTE  lpConverted;
            DWORD   dwCount64;
            DWORD   dwTotalVarData = 0;
            DWORD   dwSize64 = 0;
            DWORD   dwStatusSize;
            DWORD   dwStrucSize64;
            DWORD   dwStrucSizeWOW;

            if (scApi == SC_API_ENUM_PROCESS_W || scApi == SC_API_ENUM_PROCESS_A)
            {
                dwStrucSize64  = sizeof(ENUM_SERVICE_STATUS_PROCESSW);
                dwStrucSizeWOW = sizeof(ENUM_SERVICE_STATUS_PROCESS_WOW64);
                dwStatusSize   = sizeof(SERVICE_STATUS_PROCESS);
            }
            else
            {
                dwStrucSize64  = sizeof(ENUM_SERVICE_STATUSW);
                dwStrucSizeWOW = sizeof(ENUM_SERVICE_STATUS_WOW64);
                dwStatusSize   = sizeof(SERVICE_STATUS);
            }

            for (dwCount64 = 0;
                 dwCount64 < *lpServicesReturned;
                 dwCount64++, lpIter += dwStrucSizeWOW)
            {
                DWORD  dwCurrentVarData;
                DWORD  dwOffset;

                //
                // Compute size of this record's variable-length data
                //

                dwOffset = ((LPENUM_SERVICE_STATUS_WOW64) lpIter)->dwDisplayNameOffset;

                dwCurrentVarData = cbBufSize - dwOffset - dwTotalVarData;


                //
                // Is there room to expand the current record to 64-bit pointers?
                //

                if (dwSize64 + dwStrucSize64 + dwCurrentVarData > cbBufSize)
                {
                    //
                    // Nope.
                    //

                    break;
                }

                //
                // Update the total number of variable-length data bytes
                // in the post-conversion buffer
                //

                dwTotalVarData += dwCurrentVarData;

                dwSize64 += dwStrucSize64 + dwCurrentVarData;
            }


            //
            // Set up the pointer to the last soon-to-be-converted structure.
            // Cast to INT to sign-extend the offset to 64 bits (required
            // when (dwCount64 == 0) or else the sum gets bizarre).
            //


            lpIter      = lpServices + (INT) (dwStrucSizeWOW * (dwCount64 - 1));
            lpConverted = lpServices + (INT) (dwStrucSize64 * (dwCount64 - 1));


            for ( ;
                  lpIter >= lpServices;
                  lpIter -= dwStrucSizeWOW, lpConverted -= dwStrucSize64)
            {
                LPENUM_SERVICE_STATUSW      lpEnum = (LPENUM_SERVICE_STATUSW) lpConverted;
                LPENUM_SERVICE_STATUS_WOW64 lpEnumWOW = (LPENUM_SERVICE_STATUS_WOW64) lpIter;

                //
                // Copy fields individually in reverse order in case there's overlap
                //

                RtlMoveMemory(&lpEnum->ServiceStatus,
                              &lpEnumWOW->ServiceStatus,
                              dwStatusSize);

                //
                // Do the offset-to-pointer conversion.  Can do straight addition
                // since we didn't move the variable length data.
                //

                lpEnum->lpDisplayName = (LPWSTR) (lpServices + lpEnumWOW->dwDisplayNameOffset);
                lpEnum->lpServiceName = (LPWSTR) (lpServices + lpEnumWOW->dwServiceNameOffset);
            }

            ASSERT(lpIter < lpServices && lpConverted < lpServices);

            if (*lpServicesReturned != dwCount64)
            {
                //
                // Not enough room to fit all the records returned.  Update
                // all the OUT parameters.  Add on the size of the overwritten
                // records' variable-length data first.
                //

                *pcbBytesNeeded += (cbBufSize
                                     - dwTotalVarData
                                     - *lpServicesReturned * dwStrucSizeWOW);

                //
                // And now do the fixed-length data.  Use the 32-bit structures
                // and the caller will multiply it up to 64-bit lengths.
                //

                *pcbBytesNeeded += (*lpServicesReturned - dwCount64)
                                       * dwStrucSizeWOW;

                //
                // Update the count of services returned
                //

                *lpServicesReturned = dwCount64;

                if (scApi == SC_API_ENUM_DEPEND_W || scApi == SC_API_ENUM_DEPEND_A)
                {
                    *lpdwError = ERROR_MORE_DATA;
                    return FALSE;
                }


                //
                // And now things get ugly.  We need to update the resume index
                // since we removed some services from the buffer.  However,
                // there's no easy way to get the resume index of a particular
                // service from the server side.  So we have to build up an
                // enum buffer that's just small enough to hold only the records
                // we're going to return and enum again just to get the RI.
                //

                LPBYTE  lpTemp;
                DWORD   status;
                DWORD   dwTempBytes;
                DWORD   dwTempCount;
                DWORD   dwTempSize = dwCount64 * dwStrucSizeWOW + dwTotalVarData;

                lpTemp = (LPBYTE) LocalAlloc(LMEM_FIXED, dwTempSize);

                if (lpTemp == NULL)
                {
                    *lpdwError = ERROR_NOT_ENOUGH_MEMORY;
                    return FALSE;
                }

                RpcTryExcept
                {
                    switch (scApi)
                    {
                        case SC_API_ENUM_W:

                            status = REnumServicesStatusW (
                                        (SC_RPC_HANDLE) hSCManager,
                                        dwServiceType,
                                        dwServiceState,
                                        (LPBYTE) lpTemp,
                                        dwTempSize,
                                        &dwTempBytes,
                                        &dwTempCount,
                                        lpResumeIndex);

                             break;

                        case SC_API_ENUM_A:

                            status = REnumServicesStatusA (
                                        (SC_RPC_HANDLE) hSCManager,
                                        dwServiceType,
                                        dwServiceState,
                                        (LPBYTE) lpTemp,
                                        dwTempSize,
                                        &dwTempBytes,
                                        &dwTempCount,
                                        lpResumeIndex);

                             break;

                        case SC_API_ENUM_GROUP:

                            status = REnumServiceGroupW (
                                        (SC_RPC_HANDLE) hSCManager,
                                        dwServiceType,
                                        dwServiceState,
                                        (LPBYTE) lpTemp,
                                        dwTempSize,
                                        &dwTempBytes,
                                        &dwTempCount,
                                        lpResumeIndex,
                                        (LPCWSTR) pszGroupName);

                            break;

                        case SC_API_ENUM_PROCESS_W:

                            status = REnumServicesStatusExW (
                                        hSCManager,
                                        SC_ENUM_PROCESS_INFO,
                                        dwServiceType,
                                        dwServiceState,
                                        (LPBYTE) lpTemp,
                                        dwTempSize,
                                        &dwTempBytes,
                                        &dwTempCount,
                                        lpResumeIndex,
                                        (LPCWSTR) pszGroupName);

                             break;

                        case SC_API_ENUM_PROCESS_A:

                            status = REnumServicesStatusExA (
                                        hSCManager,
                                        SC_ENUM_PROCESS_INFO,
                                        dwServiceType,
                                        dwServiceState,
                                        (LPBYTE) lpTemp,
                                        dwTempSize,
                                        &dwTempBytes,
                                        &dwTempCount,
                                        lpResumeIndex,
                                        (LPCSTR) pszGroupName);

                             break;

                        default:

                            ASSERT(FALSE && "Unsupported API in ScConvertOffsets64 enum path");
                            status = ERROR_NOT_SUPPORTED;
                    }
                }
                RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
                {
                    status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
                }
                RpcEndExcept

                LocalFree(lpTemp);

                //
                // We had to overwrite at least one structure so we need
                // to return some form of error, even if it's ERROR_MORE_DATA.
                //

                *lpdwError = status;
                return FALSE;
            }

            return TRUE;
            break;
        }

        case SC_API_QUERY_DESCRIPTION_W:
        case SC_API_QUERY_DESCRIPTION_A:
        {
            DWORD  dwBytesNeeded = *pcbBytesNeeded;
            DWORD  dwSizeDiff    = sizeof(SERVICE_DESCRIPTIONW) - sizeof(SERVICE_DESCRIPTION_WOW64);

            LPSERVICE_DESCRIPTION_WOW64  psdWOW = (LPSERVICE_DESCRIPTION_WOW64) lpServices;
            LPSERVICE_DESCRIPTIONW       psd    = (LPSERVICE_DESCRIPTIONW) lpServices;

            //
            // *pcbBytesNeeded is filled in on success or "buffer too small"
            // failure, so update it with the 64-bit size.
            //

            *pcbBytesNeeded += dwSizeDiff;

            if (*pcbBytesNeeded > cbBufSize)
            {
                return FALSE;
            }

            //
            // We have room -- move the variable-length data
            //

            RtlMoveMemory(psd + 1,
                          psdWOW + 1,
                          dwBytesNeeded - sizeof(SERVICE_DESCRIPTION_WOW64));

            //
            // Resize the offset from 4 to 8 bytes.
            //

            if (psdWOW->dwDescriptionOffset == 0)
            {
                psd->lpDescription = NULL;
            }
            else
            {
                psd->lpDescription = (LPWSTR) UlongToPtr(psdWOW->dwDescriptionOffset + dwSizeDiff);
            }

            return TRUE;
        }

        case SC_API_QUERY_FAILURE_ACTIONS_W:                          
        case SC_API_QUERY_FAILURE_ACTIONS_A:
        {
            DWORD dwBytesNeeded = *pcbBytesNeeded;
            DWORD dwSizeDiff    = sizeof(SERVICE_FAILURE_ACTIONSW) - sizeof(SERVICE_FAILURE_ACTIONS_WOW64);

            LPSERVICE_FAILURE_ACTIONS_WOW64 psfaWOW = (LPSERVICE_FAILURE_ACTIONS_WOW64) lpServices;
            LPSERVICE_FAILURE_ACTIONSW      psfa    = (LPSERVICE_FAILURE_ACTIONSW) lpServices;

            //
            // *pcbBytesNeeded is filled in on success or "buffer too small"
            // failure, so update it with the 64-bit size.
            //

            *pcbBytesNeeded += dwSizeDiff;

            if (*pcbBytesNeeded > cbBufSize)
            {
                return FALSE;
            }

            //
            // We have room -- move the variable-length data
            //

            RtlMoveMemory(psfa + 1,
                          psfaWOW + 1,
                          dwBytesNeeded - sizeof(SERVICE_FAILURE_ACTIONS_WOW64));

            //
            // Resize the offsets from 4 to 8 bytes.  Do this in reverse field order
            // to avoid trampling over any still-to-be-converted offsets.
            //

            if (psfaWOW->dwsaActionsOffset == 0)
            {
                psfa->lpsaActions = NULL;
            }
            else
            {
                psfa->lpsaActions = (SC_ACTION *) UlongToPtr(psfaWOW->dwsaActionsOffset + dwSizeDiff);
            }

            psfa->cActions = psfaWOW->cActions;

            if (psfaWOW->dwCommandOffset == 0)
            {
                psfa->lpCommand = NULL;
            }
            else
            {
                psfa->lpCommand = (LPWSTR) UlongToPtr(psfaWOW->dwCommandOffset + dwSizeDiff);
            }

            if (psfaWOW->dwRebootMsgOffset == 0)
            {
                psfa->lpRebootMsg = NULL;
            }
            else
            {
                psfa->lpRebootMsg = (LPWSTR) UlongToPtr(psfaWOW->dwRebootMsgOffset + dwSizeDiff);
            }

            psfa->dwResetPeriod = psfaWOW->dwResetPeriod;

            return TRUE;
        }

        default:
            ASSERT(FALSE && "Unsupported API passed to ScConvertOffsets64");
            *lpdwError = ERROR_NOT_SUPPORTED;
            return FALSE;
    }
}

#endif  // _WIN64    


BOOL
WINAPI
ChangeServiceConfigA(
    IN  SC_HANDLE    hService,
    IN  DWORD        dwServiceType,
    IN  DWORD        dwStartType,
    IN  DWORD        dwErrorControl,
    IN  LPCSTR       lpBinaryPathName,
    IN  LPCSTR       lpLoadOrderGroup,
    OUT LPDWORD      lpdwTagId,
    IN  LPCSTR       lpDependencies,
    IN  LPCSTR       lpServiceStartName,
    IN  LPCSTR       lpPassword,
    IN  LPCSTR       lpDisplayName
    )

/*++

Routine Description:

    This is the DLL entry point for the ChangeServiceConfig function.
    ChangeServiceConfig changes the service configuration kept in the
    Service Control Manager database.  This configuration information
    was first set in the database via the CreateService API, and can
    be queried (exept for the password parameter) using the
    QueryServiceConfig API.

Arguments:

    hService - Handle obtained from a previous OpenService call.

    dwServiceType - Value to indicate the type of service this is.

    dwStartType - Value to specify when to start the service.

    dwErrorControl - Value to specify the severity of the error if this
        service fails to start during boot so that the appropriate action
        can be taken.

    lpBinaryPathName - Fully-qualified path name to the service binary file.

    lpLoadOrderGroup - Name of the load ordering group which this service
        is a member of.  Groups of services are started based on the group
        order list specified in the registry at
        HKEY_LOCAL_SYSTEM\Control\Service_Group_Order.

    lpdwTagId - On output this pointer receives a unique tag identification
        number within the group.  If this parameter is specified (non-NULL)
        but lpLoadOrderGroup is not specified, ERROR_INVALID_PARAMETER
        will be returned.

    lpDependencies - NULL-separated names of services which must be
        running before this service can run.  An empty string means that
        this service has no dependencies.

    lpServiceStartName - If service type is SERVICE_WIN32, this name is
        the account name in the form of "DomainName\Username" which the
        service process will be logged on as when it runs.  If service
        type is SERVICE_DRIVER, this name must be the NT driver object
        name (e.g. \FileSystem\LanManRedirector or \Driver\Xns) which
        the I/O system uses to load the device driver.

    lpPassword - Password to the account name specified by
        lpServiceStartName if service type is SERVICE_WIN32.  This
        password will be changed periodically by the Service Control
        Manager so that it will not expire. If service type is
        SERVICE_DRIVER, this parameter is ignored.

    lpDisplayName - This is the internationalized name that is used for
        display purposes only.


Return Value:


Note:


--*/
{
    DWORD          status;

    LPWSTR lpPasswordW;
    LPBYTE EncryptedPassword = NULL;
    DWORD PasswordSize = 0;

    LPSTR Ptr;
    LPWSTR DependBuffer = NULL;
    DWORD DependSize = 0;


    RpcTryExcept {

        //
        // Create a unicode version of lpPassword, and then encrypt it.
        //
        if (ARGUMENT_PRESENT(lpPassword)) {

            if (! ScConvertToUnicode(&lpPasswordW, lpPassword)) {
                SCC_LOG0(ERROR,"ChangeServiceConfigA: convert password to Unicode failed\n");
                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
                return(FALSE);
            }

            status = ScEncryptPassword(
                         (SC_RPC_HANDLE)hService,
                         lpPasswordW,
                         &EncryptedPassword,
                         &PasswordSize
                         );

            (void) LocalFree(lpPasswordW);

            if (status != NO_ERROR) {
                SCC_LOG0(ERROR,"ChangeServiceConfigA: ScEncryptPassword failed\n");
                SetLastError(status);
                return(FALSE);
            }
        }

        if (ARGUMENT_PRESENT(lpDependencies)) {

            DependSize = ScAStrArraySize((LPSTR) lpDependencies) / sizeof(CHAR) * sizeof(WCHAR);

            if ((DependBuffer = (LPWSTR)LocalAlloc(
                                            LMEM_ZEROINIT,
                                            (UINT) DependSize)) == NULL) {
                SCC_LOG1(ERROR,
                         "ChangeServiceConfigA: LocalAlloc of DependBuffer failed "
                         FORMAT_DWORD "\n", GetLastError());
                status = ERROR_NOT_ENOUGH_MEMORY;
            }

            if (DependSize > sizeof(WCHAR)) {

                //
                // There is at least one dependency entry.
                //

                Ptr = (LPSTR) lpDependencies;

                //
                // Convert each dependency into Unicode, and append it to the
                // DependBuffer.
                //
                while (*Ptr != 0) {

                    LPWSTR ConvertedDependency = NULL;


                    if (! ScConvertToUnicode(&ConvertedDependency, Ptr)) {
                        SCC_LOG0(ERROR,
                                 "ChangeServiceConfigA: convert dependency to Unicode failed\n");
                        status = ERROR_NOT_ENOUGH_MEMORY;
                        goto CleanExit;
                    }

                    ScAddWStrToWStrArray(DependBuffer, ConvertedDependency);

                    (void) LocalFree(ConvertedDependency);

                    Ptr = ScNextAStrArrayEntry(Ptr);
                }
            }
        }

        status = RChangeServiceConfigA(
                    (SC_RPC_HANDLE)hService,
                    dwServiceType,
                    dwStartType,
                    dwErrorControl,
                    (LPSTR) lpBinaryPathName,
                    (LPSTR) lpLoadOrderGroup,
                    lpdwTagId,
                    (LPBYTE) DependBuffer,
                    DependSize,
                    (LPSTR) lpServiceStartName,
                    EncryptedPassword,
                    PasswordSize,
                    (LPSTR)lpDisplayName);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

CleanExit:

    if (EncryptedPassword != NULL) {
        (void) LocalFree(EncryptedPassword);
    }

    if (DependBuffer != NULL) {
        (void) LocalFree(DependBuffer);
    }

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}

BOOL
WINAPI
ChangeServiceConfigW(
    IN  SC_HANDLE    hService,
    IN  DWORD        dwServiceType,
    IN  DWORD        dwStartType,
    IN  DWORD        dwErrorControl,
    IN  LPCWSTR      lpBinaryPathName,
    IN  LPCWSTR      lpLoadOrderGroup,
    OUT LPDWORD      lpdwTagId,
    IN  LPCWSTR      lpDependencies,
    IN  LPCWSTR      lpServiceStartName,
    IN  LPCWSTR      lpPassword,
    IN  LPCWSTR      lpDisplayName
    )

/*++

Routine Description:

    see ChangeServiceConfigA

Arguments:


Return Value:


Note:


--*/
{
    DWORD          status;

    LPBYTE EncryptedPassword = NULL;
    DWORD PasswordSize = 0;

    DWORD DependSize = 0;


    RpcTryExcept {

        //
        // Create a unicode version of lpPassword, and then encrypt it.
        //
        if (ARGUMENT_PRESENT(lpPassword)) {

            status = ScEncryptPassword(
                         (SC_RPC_HANDLE)hService,
                         (LPWSTR) lpPassword,
                         &EncryptedPassword,
                         &PasswordSize
                         );

            if (status != NO_ERROR) {
                SCC_LOG0(ERROR,"ChangeServiceConfigW: ScEncryptPassword failed\n");
                SetLastError(status);
                return(FALSE);
            }
        }

        if (ARGUMENT_PRESENT(lpDependencies)) {
            DependSize = ScWStrArraySize((LPWSTR) lpDependencies);
        }

        status = RChangeServiceConfigW(
                    (SC_RPC_HANDLE)hService,
                    dwServiceType,
                    dwStartType,
                    dwErrorControl,
                    (LPWSTR) lpBinaryPathName,
                    (LPWSTR) lpLoadOrderGroup,
                    lpdwTagId,
                    (LPBYTE) lpDependencies,
                    DependSize,
                    (LPWSTR) lpServiceStartName,
                    EncryptedPassword,
                    PasswordSize,
                    (LPWSTR)lpDisplayName);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (EncryptedPassword != NULL) {
        (void) LocalFree(EncryptedPassword);
    }
    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}

BOOL
WINAPI
ChangeServiceConfig2A(
    IN  SC_HANDLE    hService,
    IN  DWORD        dwInfoLevel,
    IN  LPVOID       lpInfo
    )

/*++

Routine Description:


Arguments:


Return Value:


Note:

--*/
{
    DWORD status;

    // Transform the parameters into a union that RPC likes
    SC_RPC_CONFIG_INFOA RpcInfo = { dwInfoLevel,
                                    (LPSERVICE_DESCRIPTIONA) lpInfo };

    RpcTryExcept
    {
        status = RChangeServiceConfig2A(
                        (SC_RPC_HANDLE) hService,
                        RpcInfo
                        );
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
    {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status == RPC_S_INVALID_TAG)
    {
        status = ERROR_INVALID_LEVEL;
    }

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return FALSE;
    }

    return TRUE;
}

BOOL
WINAPI
ChangeServiceConfig2W(
    IN  SC_HANDLE    hService,
    IN  DWORD        dwInfoLevel,
    IN  LPVOID       lpInfo
    )

/*++

Routine Description:


Arguments:


Return Value:


Note:

--*/
{
    DWORD status;

    // Transform the parameters into a union that RPC likes
    SC_RPC_CONFIG_INFOW RpcInfo = { dwInfoLevel,
                                    (LPSERVICE_DESCRIPTIONW) lpInfo };

    RpcTryExcept
    {
        status = RChangeServiceConfig2W(
                        (SC_RPC_HANDLE) hService,
                        RpcInfo
                        );
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
    {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status == RPC_S_INVALID_TAG)
    {
        status = ERROR_INVALID_LEVEL;
    }

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return FALSE;
    }

    return TRUE;
}

SC_HANDLE
WINAPI
CreateServiceA(
    IN  SC_HANDLE    hSCManager,
    IN  LPCSTR       lpServiceName,
    IN  LPCSTR       lpDisplayName,
    IN  DWORD        dwDesiredAccess,
    IN  DWORD        dwServiceType,
    IN  DWORD        dwStartType,
    IN  DWORD        dwErrorControl,
    IN  LPCSTR       lpBinaryPathName,
    IN  LPCSTR       lpLoadOrderGroup,
    OUT LPDWORD      lpdwTagId,
    IN  LPCSTR       lpDependencies,
    IN  LPCSTR       lpServiceStartName,
    IN  LPCSTR       lpPassword
    )

/*++

Routine Description:

    This function is the DLL entry point for the ansi version
    of CreateService.  On the server side, this function will create
    a service object and add it to the Service Control Manager database.

Arguments:

    hSCManager - Handle obtained from a previous OpenSCManager call.

    lpServiceName - Name of the service to install.

    lpDisplayName - This is the internationalized name that is used for
        display purposes only.

    dwDesiredAccess - Access types desired to access the service.

    dwServiceType - Value to indicate the type of service this is.

    dwStartType - Value to specify when to start the service.

    dwErrorControl - Value to specify the severity of the error if this
        service fails to start during boot so that the appropriate action
        can be taken.

    lpBinaryPathName - Fully-qualified path name to the service binary file.

    lpLoadOrderGroup - Name of the load ordering group which this service
        is a member of.  Groups of services are started based on the group
        order list specified in the registry at
        HKEY_LOCAL_SYSTEM\Control\Service_Group_Order.

    lpdwTagId - On output this pointer receives a unique tag identification
        number within the group.  If this parameter is specified (non-NULL)
        but lpLoadOrderGroup is not specified, ERROR_INVALID_PARAMETER
        will be returned.

    lpDependencies - Space-separated names of services which must be
        running before this service can run.  An empty string means that
        this service has no dependencies.

    lpServiceStartName - If service type is SERVICE_WIN32, this name is
        the account name in the form of "DomainName\Username" which the
        service process will be logged on as when it runs.  If service
        type is SERVICE_DRIVER, this name must be the NT driver object
        name (e.g. \FileSystem\LanManRedirector or \Driver\Xns) which
        the I/O system uses to load the device driver.

    lpPassword - Password to the account name specified by
        lpServiceStartName if service type is SERVICE_WIN32.  This
        password will be changed periodically by the Service Control
        Manager so that it will not expire. If service type is
        SERVICE_DRIVER, this parameter is ignored.

Return Value:


Note:


--*/
{
    DWORD           status;
    SC_RPC_HANDLE   hService=NULL;

    LPWSTR lpPasswordW;
    LPBYTE EncryptedPassword = NULL;
    DWORD PasswordSize = 0;

    LPSTR Ptr;
    LPWSTR DependBuffer = NULL;
    DWORD DependSize = 0;


    RpcTryExcept {

        //
        // Create a unicode version of lpPassword, and then encrypt it.
        //
        if (ARGUMENT_PRESENT(lpPassword)) {

            if (! ScConvertToUnicode(&lpPasswordW, lpPassword)) {
                SCC_LOG0(ERROR,"CreateServiceA: convert password to Unicode failed\n");
                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
                return(NULL);
            }

            status = ScEncryptPassword(
                         (SC_RPC_HANDLE)hSCManager,
                         lpPasswordW,
                         &EncryptedPassword,
                         &PasswordSize
                         );

            (void) LocalFree(lpPasswordW);

            if (status != NO_ERROR) {
                SCC_LOG0(ERROR,"CreateServiceA: ScEncryptPassword failed\n");
                SetLastError(status);
                return(NULL);
            }
        }

        if (ARGUMENT_PRESENT(lpDependencies)) {

            DependSize = ScAStrArraySize((LPSTR) lpDependencies) / sizeof(CHAR) * sizeof(WCHAR);

            if ((DependBuffer = (LPWSTR)LocalAlloc(
                                            LMEM_ZEROINIT,
                                            (UINT) DependSize)) == NULL) {
                SCC_LOG1(ERROR,
                         "CreateServiceA: LocalAlloc of DependBuffer failed "
                         FORMAT_DWORD "\n", GetLastError());
                status = ERROR_NOT_ENOUGH_MEMORY;
                goto CleanExit;
            }

            if (DependSize > sizeof(WCHAR)) {

                //
                // There is at least one dependency entry.
                //

                Ptr = (LPSTR) lpDependencies;

                //
                // Convert each dependency into Unicode, and append it to the
                // DependBuffer.
                //
                while (*Ptr != 0) {

                    LPWSTR ConvertedDependency = NULL;


                    if (! ScConvertToUnicode(&ConvertedDependency, Ptr)) {
                        SCC_LOG0(ERROR,
                                 "CreateServiceA: convert dependency to Unicode failed\n");
                        status = ERROR_NOT_ENOUGH_MEMORY;
                        goto CleanExit;
                    }

                    ScAddWStrToWStrArray(DependBuffer, ConvertedDependency);

                    (void) LocalFree(ConvertedDependency);

                    Ptr = ScNextAStrArrayEntry(Ptr);
                }
            }
        }

        status = RCreateServiceA (
                    (SC_RPC_HANDLE)hSCManager,
                    (LPSTR) lpServiceName,
                    (LPSTR) lpDisplayName,
                    dwDesiredAccess,
                    dwServiceType,
                    dwStartType,
                    dwErrorControl,
                    (LPSTR) lpBinaryPathName,
                    (LPSTR) lpLoadOrderGroup,
                    lpdwTagId,
                    (LPBYTE) DependBuffer,
                    DependSize,
                    (LPSTR) lpServiceStartName,
                    EncryptedPassword,
                    PasswordSize,
                    &hService);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

CleanExit:
    if (DependBuffer != NULL) {
        (void) LocalFree(DependBuffer);
    }

    if (EncryptedPassword != NULL) {
        (void) LocalFree(EncryptedPassword);
    }

    if (status != NO_ERROR){
        SetLastError(status);
        return(NULL);
    }

    return (SC_HANDLE)hService;
}

SC_HANDLE
WINAPI
CreateServiceW(
    IN  SC_HANDLE    hSCManager,
    IN  LPCWSTR      lpServiceName,
    IN  LPCWSTR      lpDisplayName,
    IN  DWORD        dwDesiredAccess,
    IN  DWORD        dwServiceType,
    IN  DWORD        dwStartType,
    IN  DWORD        dwErrorControl,
    IN  LPCWSTR      lpBinaryPathName,
    IN  LPCWSTR      lpLoadOrderGroup,
    OUT LPDWORD      lpdwTagId,
    IN  LPCWSTR      lpDependencies,
    IN  LPCWSTR      lpServiceStartName,
    IN  LPCWSTR      lpPassword
    )

/*++

Routine Description:

    see CreateServiceA

Arguments:


Return Value:


Note:


--*/
{
    DWORD           status;
    SC_RPC_HANDLE   hService = NULL;

    LPBYTE EncryptedPassword = NULL;
    DWORD PasswordSize = 0;

    DWORD DependSize = 0;


    RpcTryExcept {

        if (ARGUMENT_PRESENT(lpPassword)) {

            status = ScEncryptPassword(
                         (SC_RPC_HANDLE)hSCManager,
                         (LPWSTR) lpPassword,
                         &EncryptedPassword,
                         &PasswordSize
                         );

            if (status != NO_ERROR) {
                SCC_LOG0(ERROR,"CreateServiceW: ScEncryptPassword failed\n");
                SetLastError(status);
                return NULL;
            }
        }

        if (ARGUMENT_PRESENT(lpDependencies)) {
            DependSize = ScWStrArraySize((LPWSTR) lpDependencies);
        }

        status = RCreateServiceW (
                    (SC_RPC_HANDLE)hSCManager,
                    (LPWSTR) lpServiceName,
                    (LPWSTR) lpDisplayName,
                    dwDesiredAccess,
                    dwServiceType,
                    dwStartType,
                    dwErrorControl,
                    (LPWSTR) lpBinaryPathName,
                    (LPWSTR) lpLoadOrderGroup,
                    lpdwTagId,
                    (LPBYTE) lpDependencies,
                    DependSize,
                    (LPWSTR) lpServiceStartName,
                    EncryptedPassword,
                    PasswordSize,
                    &hService);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (EncryptedPassword != NULL) {
        LocalFree(EncryptedPassword);
    }

    if (status != NO_ERROR){
        SetLastError(status);
        return NULL;
    }

    return (SC_HANDLE)hService;
}

BOOL
WINAPI
DeleteService(
    IN  SC_HANDLE   hService
    )

/*++

Routine Description:

    This is the DLL entry point for the DeleteService function.
    DeleteService removes the service from the Service Control
    Manager's database.

Arguments:

    hService - Handle obtained from a previous CreateService or
        OpenService call.

Return Value:


Note:


--*/
{
    DWORD          status;


    SCC_LOG1(TRACE,
             "---------DeleteService called (%ws)\n",
             GetCommandLineW());

    RpcTryExcept {

        status = RDeleteService ((SC_RPC_HANDLE)hService);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){

        SCC_LOG2(TRACE,
                 "---------DeleteService FAILED, %ld (%ws)\n",
                 status,
                 GetCommandLineW());

        SetLastError(status);
        return(FALSE);
    }

    SCC_LOG1(TRACE,
             "---------DeleteService SUCCESS (%ws)\n",
             GetCommandLineW());


    return TRUE;
}

BOOL
WINAPI
EnumDependentServicesA(
    IN      SC_HANDLE               hService,
    IN      DWORD                   dwServiceState,
    OUT     LPENUM_SERVICE_STATUSA  lpServices,
    IN      DWORD                   cbBufSize,
    OUT     LPDWORD                 pcbBytesNeeded,
    OUT     LPDWORD                 lpServicesReturned
    )


/*++

Routine Description:

    This function lists the services which depend on the specified
    service to be running before they can run.  The returned
    services entries are ordered in the reverse order of start
    dependencies with group order taken into account.  Services can
    be stopped in the proper order based on the order of entries
    written to the output buffer.

Arguments:

    hService - Handle obtained from a previous OpenService call.

    dwServiceState - Value to select the services to enumerate based on
        the running state.

    lpServices - A pointer to a buffer to receive an array of service
        entries; each entry is the ENUM_SERVICE_STATUS information
        structure.  The services returned in the buffer is ordered by
        the reverse dependency order.

    cbBufSize - Size of the buffer in bytes pointed to by lpServices.

    pcbBytesNeeded - A pointer to a variable to receive the number of
        bytes needed to fit the remaining service entries.

    lpServicesReturned - A pointer to a variable to receive the number
        of service entries returned.


Return Value:

    TRUE - if all Services are successfully written into the supplied
        output buffer.

    FALSE - If an error has occured - Use GetLastError to determine the
        cause of the failure.


--*/
{
    DWORD                   status;
    LPENUM_SERVICE_STATUSA  pEnumBuf;
    ENUM_SERVICE_STATUSA    enumBuf;
    DWORD                   tempBufSize;

    tempBufSize = cbBufSize;
    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(ENUM_SERVICE_STATUSA) || (lpServices == NULL)) {
        pEnumBuf = &enumBuf;
        tempBufSize = sizeof(ENUM_SERVICE_STATUSA);
    }
    else {
        pEnumBuf = lpServices;
    }


    RpcTryExcept {

        status = REnumDependentServicesA(
                    (SC_RPC_HANDLE)hService,
                    dwServiceState,
                    (LPBYTE)pEnumBuf,
                    tempBufSize,
                    pcbBytesNeeded,
                    lpServicesReturned);


    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    //
    // If data is returned, convert Offsets in the Enum buffer to pointers.
    //
    if ((status == NO_ERROR) || (status == ERROR_MORE_DATA))
    {
        if ((*lpServicesReturned) > 0)
        {

#ifdef _WIN64

            DWORD dwError;

            if (!ScConvertOffsets64(SC_API_ENUM_DEPEND_A,
                                    NULL,                  // no hSCManager
                                    0,                     // no service type
                                    0,                     // no service state
                                    (LPBYTE) lpServices,
                                    cbBufSize,
                                    pcbBytesNeeded,
                                    lpServicesReturned,
                                    NULL,                  // no old resume index
                                    NULL,                  // no group name
                                    &dwError))
            {
                status = dwError;
            }
        }

        //
        // Scale required size up to the minimum size guaranteed to
        // succeed with 64-bit structures.
        //

        *pcbBytesNeeded = *pcbBytesNeeded
                              * sizeof(ENUM_SERVICE_STATUSA)
                              / sizeof(ENUM_SERVICE_STATUS_WOW64);


#else  // ndef _WIN64

            ScConvertOffsetsA(lpServices, *lpServicesReturned);
        }

#endif // _WIN64

    }

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


BOOL
WINAPI
EnumDependentServicesW(
    IN      SC_HANDLE               hService,
    IN      DWORD                   dwServiceState,
    OUT     LPENUM_SERVICE_STATUSW  lpServices,
    IN      DWORD                   cbBufSize,
    OUT     LPDWORD                 pcbBytesNeeded,
    OUT     LPDWORD                 lpServicesReturned
    )


/*++

Routine Description:

    This function lists the services which depend on the specified
    service to be running before they can run.  The returned
    services entries are ordered in the reverse order of start
    dependencies with group order taken into account.  Services can
    be stopped in the proper order based on the order of entries
    written to the output buffer.

Arguments:

    hService - Handle obtained from a previous OpenService call.

    dwServiceState - Value to select the services to enumerate based on
        the running state.

    lpServices - A pointer to a buffer to receive an array of service
        entries; each entry is the ENUM_SERVICE_STATUS information
        structure.  The services returned in the buffer is ordered by
        the reverse dependency order.

    cbBufSize - Size of the buffer in bytes pointed to by lpServices.

    pcbBytesNeeded - A pointer to a variable to receive the number of
        bytes needed to fit the remaining service entries.

    lpServicesReturned - A pointer to a variable to receive the number
        of service entries returned.


Return Value:

    TRUE - if all Services are successfully written into the supplied
        output buffer.

    FALSE - If an error has occured - Use GetLastError to determine the
        cause of the failure.


--*/
{
    DWORD                   status;
    LPENUM_SERVICE_STATUSW  pEnumBuf;
    ENUM_SERVICE_STATUSW    enumBuf;
    DWORD                   tempBufSize;

    tempBufSize = cbBufSize;
    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(ENUM_SERVICE_STATUSW) || (lpServices == NULL)) {
        pEnumBuf = &enumBuf;
        tempBufSize = sizeof(ENUM_SERVICE_STATUSW);
    }
    else {
        pEnumBuf = lpServices;
    }

    RpcTryExcept {

        status = REnumDependentServicesW(
                    (SC_RPC_HANDLE)hService,
                    dwServiceState,
                    (LPBYTE)pEnumBuf,
                    tempBufSize,
                    pcbBytesNeeded,
                    lpServicesReturned);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    //
    // If data is returned, convert Offsets in the Enum buffer to pointers.
    //

    if ((status == NO_ERROR) || (status == ERROR_MORE_DATA))
    {
        if ((*lpServicesReturned) > 0)
        {

#ifdef _WIN64

            DWORD dwError;

            if (!ScConvertOffsets64(SC_API_ENUM_DEPEND_W,
                                    NULL,                  // no hSCManager
                                    0,                     // no service type
                                    0,                     // no service state
                                    (LPBYTE) lpServices,
                                    cbBufSize,
                                    pcbBytesNeeded,
                                    lpServicesReturned,
                                    NULL,                  // no old resume index
                                    NULL,                  // no group name
                                    &dwError))
            {
                status = dwError;
            }
        }

        //
        // Scale required size up to the minimum size guaranteed to
        // succeed with 64-bit structures.
        //

        *pcbBytesNeeded = *pcbBytesNeeded
                              * sizeof(ENUM_SERVICE_STATUSA)
                              / sizeof(ENUM_SERVICE_STATUS_WOW64);

#else  // ndef _WIN64

            ScConvertOffsetsW(lpServices, *lpServicesReturned);
        }

#endif // _WIN64

    }

    if (status != NO_ERROR)
    {
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


BOOL
WINAPI
GetServiceDisplayNameA(
    SC_HANDLE       hSCManager,
    LPCSTR          lpServiceName,
    LPSTR           lpDisplayName,
    LPDWORD         lpcchBuffer
    )
/*++

Routine Description:

    This function returns the display name for a service that is identified
    by its key name (ServiceName).

Arguments:

    hSCManager - This is the handle to the Service Controller Manager that
        is expected to return the display name.

    lpServiceName -  This is the ServiceName (which is actually a key
        name) that identifies the service.

    lpDisplayName - This is a pointer to a buffer that is to receive the
        DisplayName string.

    lpcchBuffer - This is a pointer to the size (in characters) of the
        buffer that is to receive the DisplayName string.  If the buffer
        is not large enough to receive the entire string, then the required
        buffer size is returned in this location.  (NOTE:  Ansi Characters,
        including DBCS, are assumed to be 8 bits).

Return Value:



--*/
{
    DWORD   status;
    LPSTR   bufPtr;
    CHAR    tempString[] = "";

    //
    // Create a dummy buffer that is at least the size of a CHAR.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    RpcTryExcept {

        if ((*lpcchBuffer < sizeof(CHAR)) || (lpDisplayName == NULL)){
            bufPtr = tempString;
            *lpcchBuffer = sizeof(CHAR);
        }
        else {
            bufPtr = (LPSTR)lpDisplayName;
        }

        status = RGetServiceDisplayNameA(
                    (SC_RPC_HANDLE)hSCManager,
                    (LPSTR)lpServiceName,
                    bufPtr,
                    lpcchBuffer);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR) {
        SetLastError(status);
        return(FALSE);
    }
    return(TRUE);
}

BOOL
WINAPI
GetServiceDisplayNameW(
    SC_HANDLE       hSCManager,
    LPCWSTR         lpServiceName,
    LPWSTR          lpDisplayName,
    LPDWORD         lpcchBuffer
    )


/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD   status;
    LPWSTR  bufPtr;
    WCHAR   tempString[]=L"";

    //
    // Create a dummy buffer that is at least the size of a WCHAR.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    RpcTryExcept {

        if ((*lpcchBuffer < sizeof(WCHAR)) || (lpDisplayName == NULL)) {
            bufPtr = tempString;
            *lpcchBuffer = sizeof(WCHAR);
        }
        else {
            bufPtr = (LPWSTR)lpDisplayName;
        }

        status = RGetServiceDisplayNameW(
                    (SC_RPC_HANDLE)hSCManager,
                    (LPWSTR)lpServiceName,
                    bufPtr,
                    lpcchBuffer);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR) {
        SetLastError(status);
        return(FALSE);
    }
    return(TRUE);
}

BOOL
WINAPI
GetServiceKeyNameA(
    SC_HANDLE   hSCManager,
    LPCSTR      lpDisplayName,
    LPSTR       lpServiceName,
    LPDWORD     lpcchBuffer
    )

/*++

Routine Description:



Arguments:

    hSCManager - This is the handle to the Service Controller Manager that
        is expected to return the service name (key name).

    lpServiceName -  This is the Service Display Name that identifies
        the service.

    lpServiceName - This is a pointer to a buffer that is to receive the
        Service Key Name string.

    lpcchBuffer - This is a pointer to the size of the buffer that is
        to receive the Service Key Name string.  If the buffer is not large
        enough to receive the entire string, then the required buffer size
        is returned in this location.


Return Value:



--*/
{
    DWORD   status;
    LPSTR   bufPtr;
    CHAR    tempString[]="";

    //
    // Create a dummy buffer that is at least the size of a CHAR.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    RpcTryExcept {

        if ((*lpcchBuffer < sizeof(CHAR)) || (lpServiceName == NULL)) {
            bufPtr = tempString;
            *lpcchBuffer = sizeof(CHAR);
        }
        else {
            bufPtr = (LPSTR)lpServiceName;
        }

        status = RGetServiceKeyNameA(
                    (SC_RPC_HANDLE)hSCManager,
                    (LPSTR)lpDisplayName,
                    bufPtr,
                    lpcchBuffer);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR) {
        SetLastError(status);
        return(FALSE);
    }
    return(TRUE);
}

BOOL
WINAPI
GetServiceKeyNameW(
    SC_HANDLE   hSCManager,
    LPCWSTR     lpDisplayName,
    LPWSTR      lpServiceName,
    LPDWORD     lpcchBuffer
    )

/*++

Routine Description:



Arguments:



Return Value:



--*/
{
    DWORD   status = NO_ERROR;
    LPWSTR  bufPtr;
    WCHAR   tempString[]=L"";

    //
    // Create a dummy buffer that is at least the size of a WCHAR.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    RpcTryExcept {

        if ((*lpcchBuffer < sizeof(WCHAR)) || (lpServiceName == NULL)) {
            bufPtr = tempString;
            *lpcchBuffer = sizeof(WCHAR);
        }
        else {
            bufPtr = (LPWSTR)lpServiceName;
        }

        status = RGetServiceKeyNameW(
                    (SC_RPC_HANDLE)hSCManager,
                    (LPWSTR)lpDisplayName,
                    bufPtr,
                    lpcchBuffer);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR) {
        SetLastError(status);
        return(FALSE);
    }
    return(TRUE);
}


SC_LOCK
WINAPI
LockServiceDatabase(
    IN  SC_HANDLE   hSCManager
    )

/*++

Routine Description:

    This is the DLL entry point for the LockServiceDatabase function.
    This function acquires a lock on the database that was opened from
    a previous OpenSCManager call.  There can only be one lock
    outstanding on a database for a given time.

Arguments:

    hSCManager - Handle obtained from a previous OpenSCManager call
        which specifies the database to lock.

Return Value:



--*/
{
    DWORD           status;
    SC_RPC_LOCK     lock = NULL;


    RpcTryExcept {

        status = RLockServiceDatabase(
                    (SC_RPC_HANDLE)hSCManager,
                    &lock);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(NULL);
    }

    return((SC_LOCK)lock);
}


BOOL
WINAPI
QueryServiceConfigA(
    IN  SC_HANDLE               hService,
    OUT LPQUERY_SERVICE_CONFIGA lpServiceConfig,
    IN  DWORD                   cbBufSize,
    OUT LPDWORD                 pcbBytesNeeded
    )

/*++

Routine Description:

    This is the DLL entry point for the QueryServiceConfig function.
    QueryServiceConfig obtains the service configuration information
    stored in the Service Control Manager database.  This configuration
    information was first set in the database via the CreateService API,
    and may have been updated via the ChangeServiceConfig API.

Arguments:

    hService - Handle obtained from a previous CreateService or
        OpenService call.

    lpServiceConfig - A pointer to a buffer to receive a
        QUERY_SERVICE_CONFIG information structure.

    cbBufSize - Size of the buffer in bytes pointed to by lpServiceConfig.

    pcbBytesNeeded - A pointer to a variable to receive the number of
        bytes needed to fit the entire QUERY_SERVICE_CONFIG information
        structure.

Return Value:


Note:


--*/
{
    DWORD                   status;
    LPSTR                   pDepend;
    LPQUERY_SERVICE_CONFIGA pConfigBuf;
    QUERY_SERVICE_CONFIGA   configBuf;
    DWORD                   tempBufSize;

    tempBufSize = cbBufSize;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(QUERY_SERVICE_CONFIGA))
    {
        pConfigBuf = &configBuf;
        tempBufSize = sizeof(QUERY_SERVICE_CONFIGA);
    }
    else
    {
        pConfigBuf = lpServiceConfig;
    }

    RpcTryExcept
    {
        status = RQueryServiceConfigA(
                    (SC_RPC_HANDLE)hService,
                    pConfigBuf,
                    tempBufSize,
                    pcbBytesNeeded);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
    {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR)
    {

#ifdef _WIN64

        //
        // pcbBytesNeeded isn't filled in if the byte count is too
        // small (returned when the buffer size is large enough to
        // hold the 32-bit structure but too small to hold the
        // 64-bit structure.  Get the necessary (32-bit) size.
        //

        if (status == RPC_X_BYTE_COUNT_TOO_SMALL)
        {
            RpcTryExcept
            {
                status = RQueryServiceConfigA(
                            (SC_RPC_HANDLE)hService,
                            pConfigBuf,
                            sizeof(QUERY_SERVICE_CONFIGA),
                            pcbBytesNeeded);
            }
            RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
            {
                status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
            }
            RpcEndExcept
        }

        //
        // Since the ACF file specifies byte_count for this API, we're
        // responsible for managing the count of bytes needed by the
        // caller.  For 64-bit clients calling 32-bit servers, the
        // returned buffer size is too small because of differing
        // pointer sizes.  Add on the minimum number of bytes that
        // will guarantee enough space in the buffer for the next
        // call.
        //

        if (status == ERROR_INSUFFICIENT_BUFFER)
        {
            //
            // 5 embedded pointers in the structure and max
            // space required for alignment.
            //
            *pcbBytesNeeded += 5 * (sizeof(PVOID) - sizeof(ULONG) + sizeof(PVOID));
        }

#endif  // _WIN64

        SetLastError(status);
        return(FALSE);
    }
    else
    {
        //
        // Replace the '/' separator characters by NULLs.  We used
        // separator characters in the double NULL terminated set of
        // strings so that RPC could treat it as a single string.
        //
        if ((pDepend = lpServiceConfig->lpDependencies) != NULL) {
            while (*pDepend != '\0') {
                if (*pDepend == '/') {
                    *pDepend = '\0';
                }
                pDepend++;
            }
        }
    }

    return(TRUE);
}


BOOL
WINAPI
QueryServiceConfigW(
    IN  SC_HANDLE               hService,
    OUT LPQUERY_SERVICE_CONFIGW lpServiceConfig,
    IN  DWORD                   cbBufSize,
    OUT LPDWORD                 pcbBytesNeeded
    )

/*++

Routine Description:

    see QueryServiceConfigA

Arguments:


Return Value:


Note:


--*/
{
    DWORD                   status;
    LPWSTR                  pDepend;
    LPQUERY_SERVICE_CONFIGW pConfigBuf;
    QUERY_SERVICE_CONFIGW   configBuf;
    DWORD                   tempBufSize;

    tempBufSize = cbBufSize;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(QUERY_SERVICE_CONFIGW))
    {
        pConfigBuf = &configBuf;
        tempBufSize = sizeof(QUERY_SERVICE_CONFIGW);
    }
    else
    {
        pConfigBuf = lpServiceConfig;
    }

    RpcTryExcept
    {
        status = RQueryServiceConfigW(
                    (SC_RPC_HANDLE)hService,
                    pConfigBuf,
                    tempBufSize,
                    pcbBytesNeeded);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
    {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR)
    {

#ifdef _WIN64

        //
        // pcbBytesNeeded isn't filled in if the byte count is too
        // small (returned when the buffer size is large enough to
        // hold the 32-bit structure but too small to hold the
        // 64-bit structure.  Get the necessary (32-bit) size.
        //

        if (status == RPC_X_BYTE_COUNT_TOO_SMALL)
        {
            RpcTryExcept
            {
                status = RQueryServiceConfigW(
                            (SC_RPC_HANDLE)hService,
                            pConfigBuf,
                            sizeof(QUERY_SERVICE_CONFIGW),
                            pcbBytesNeeded);
            }
            RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
            {
                status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
            }
            RpcEndExcept
        }

        //
        // Since the ACF file specifies byte_count for this API, we're
        // responsible for managing the count of bytes needed by the
        // caller.  For 64-bit clients calling 32-bit servers, the
        // returned buffer size is too small because of differing
        // pointer sizes.  Add on the minimum number of bytes that
        // will guarantee enough space in the buffer for the next
        // call.
        //

        if (status == ERROR_INSUFFICIENT_BUFFER)
        {
            //
            // 5 embedded pointers in the structure and max
            // space required for alignment.
            //
            *pcbBytesNeeded += 5 * (sizeof(PVOID) - sizeof(ULONG) + sizeof(PVOID));
        }

#endif  // _WIN64

        SetLastError(status);
        return(FALSE);
    }
    else
    {
        //
        // Replace the '/' separator characters by NULLs.  We used
        // separator characters in the double NULL terminated set of
        // strings so that RPC could treat it as a single string.
        //
        if ((pDepend = lpServiceConfig->lpDependencies) != NULL) {
            while (*pDepend != L'\0') {
                if (*pDepend == L'/') {
                    *pDepend = L'\0';
                }
                pDepend++;
            }
        }
    }

    return(TRUE);
}


BOOL
WINAPI
QueryServiceConfig2A(
    IN  SC_HANDLE   hService,
    IN  DWORD       dwInfoLevel,
    OUT LPBYTE      lpBuffer,
    IN  DWORD       cbBufSize,
    OUT LPDWORD     pcbBytesNeeded
    )

/*++

Routine Description:


Arguments:


Return Value:


Note:


--*/
{
    DWORD                        status;
    LPBYTE                       lpTempBuffer;
    SERVICE_DESCRIPTIONA         sdDescription;
    SERVICE_FAILURE_ACTIONSA     sfaActions;
    DWORD                        tempBufSize;
    BOOL                         fDummyBuffer = FALSE;

    tempBufSize  = cbBufSize;
    lpTempBuffer = lpBuffer;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, put the correct size in the BytesNeeded parameter, and
    // fail the call.
    //
    switch(dwInfoLevel) {

    case SERVICE_CONFIG_DESCRIPTION:

        if (cbBufSize < sizeof(SERVICE_DESCRIPTION_WOW64)) {
            lpTempBuffer = (LPBYTE) &sdDescription;
            tempBufSize  = sizeof(SERVICE_DESCRIPTION_WOW64);
            fDummyBuffer = TRUE;
        }

        break;

    case SERVICE_CONFIG_FAILURE_ACTIONS:

        if (cbBufSize < sizeof(SERVICE_FAILURE_ACTIONS_WOW64)) {
            lpTempBuffer = (LPBYTE) &sfaActions;
            tempBufSize  = sizeof(SERVICE_FAILURE_ACTIONS_WOW64);
            fDummyBuffer = TRUE;
        }

        break;

    default:
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }

    RpcTryExcept
    {
        status = RQueryServiceConfig2A(
                    (SC_RPC_HANDLE) hService,
                    dwInfoLevel,
                    lpTempBuffer,
                    tempBufSize,
                    pcbBytesNeeded);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
    {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept


#ifdef _WIN64

    //
    // 64-bit needs more space than the 32-bit-oriented structure
    // coming from the server.  Make sure we have it.  Do this
    // even if fDummyBuffer is TRUE since ScConvertOffsets64 will
    // update *pcbBytesNeeded appropriately.
    //

    if (status == NO_ERROR || status == ERROR_INSUFFICIENT_BUFFER)
    {
        if (!ScConvertOffsets64(dwInfoLevel == SERVICE_CONFIG_DESCRIPTION ?
                                    SC_API_QUERY_DESCRIPTION_A :
                                    SC_API_QUERY_FAILURE_ACTIONS_A,
                                NULL,
                                0,
                                0,
                                lpTempBuffer,
                                tempBufSize,
                                pcbBytesNeeded,
                                NULL,
                                NULL,
                                NULL,
                                NULL))
        {
            status = ERROR_INSUFFICIENT_BUFFER;
        }
    }

#endif // _WIN64


    if (status != NO_ERROR)
    {
        SetLastError(status);
        return FALSE;
    }

    //
    // Catch the case where the RPC call succeeded even though we used
    // a dummy buffer (e.g., SERVICE_FAILURE_ACTIONS on a service with
    // no command line or reboot message).  Note that the server side
    // of this API fills in pcbBytesNeeded even on success
    //
    if (fDummyBuffer)
    {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    //
    // Convert offsets to pointers in the returned structure
    //
    switch (dwInfoLevel)
    {
    case SERVICE_CONFIG_DESCRIPTION:
        {
            LPSERVICE_DESCRIPTIONA psd =
                    (LPSERVICE_DESCRIPTIONA) lpBuffer;
            if (psd->lpDescription != NULL)
            {
                psd->lpDescription = (LPSTR)
                        (lpBuffer + (DWORD)(ULONG_PTR)psd->lpDescription);
            }
        }
        break;

    case SERVICE_CONFIG_FAILURE_ACTIONS:
        {
            LPSERVICE_FAILURE_ACTIONSA psfa =
                    (LPSERVICE_FAILURE_ACTIONSA) lpBuffer;
            if (psfa->lpRebootMsg != NULL)
            {
                psfa->lpRebootMsg = (LPSTR)
                        (lpBuffer + (DWORD)(ULONG_PTR)psfa->lpRebootMsg);
            }
            if (psfa->lpCommand != NULL)
            {
                psfa->lpCommand = (LPSTR)
                        (lpBuffer + (DWORD)(ULONG_PTR)psfa->lpCommand);
            }
            if (psfa->lpsaActions != NULL)
            {
                psfa->lpsaActions = (SC_ACTION *)
                        (lpBuffer + (DWORD)(ULONG_PTR)psfa->lpsaActions);
            }
        }
        break;
    }

    return TRUE;
}


BOOL
WINAPI
QueryServiceConfig2W(
    IN  SC_HANDLE   hService,
    IN  DWORD       dwInfoLevel,
    OUT LPBYTE      lpBuffer,
    IN  DWORD       cbBufSize,
    OUT LPDWORD     pcbBytesNeeded
    )

/*++

Routine Description:


Arguments:


Return Value:


Note:


--*/
{
    DWORD                        status;
    LPBYTE                       lpTempBuffer;
    SERVICE_DESCRIPTIONW         sdDescription;
    SERVICE_FAILURE_ACTIONSW     sfaActions;
    DWORD                        tempBufSize;
    BOOL                         fDummyBuffer = FALSE;

    tempBufSize  = cbBufSize;
    lpTempBuffer = lpBuffer;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, put the correct size in the BytesNeeded parameter, and
    // fail the call.
    //
    switch(dwInfoLevel) {

    case SERVICE_CONFIG_DESCRIPTION:

        if (cbBufSize < sizeof(SERVICE_DESCRIPTIONW)) {
            lpTempBuffer = (LPBYTE) &sdDescription;
            tempBufSize  = sizeof(SERVICE_DESCRIPTIONW);
            fDummyBuffer = TRUE;
        }

        break;

    case SERVICE_CONFIG_FAILURE_ACTIONS:

        if (cbBufSize < sizeof(SERVICE_FAILURE_ACTIONSW)) {
            lpTempBuffer = (LPBYTE) &sfaActions;
            tempBufSize  = sizeof(SERVICE_FAILURE_ACTIONSW);
            fDummyBuffer = TRUE;
        }

        break;

    default:
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }

    RpcTryExcept
    {
        status = RQueryServiceConfig2W(
                    (SC_RPC_HANDLE) hService,
                    dwInfoLevel,
                    lpTempBuffer,
                    tempBufSize,
                    pcbBytesNeeded);
    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
    {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept


#ifdef _WIN64

    //
    // 64-bit needs more space than the 32-bit-oriented structure
    // coming from the server.  Make sure we have it.  Do this
    // even if fDummyBuffer is TRUE since ScConvertOffsets64 will
    // update *pcbBytesNeeded appropriately.
    //

    if (status == NO_ERROR || status == ERROR_INSUFFICIENT_BUFFER)
    {
        if (!ScConvertOffsets64(dwInfoLevel == SERVICE_CONFIG_DESCRIPTION ?
                                    SC_API_QUERY_DESCRIPTION_W :
                                    SC_API_QUERY_FAILURE_ACTIONS_W,
                                NULL,
                                0,
                                0,
                                lpTempBuffer,
                                tempBufSize,
                                pcbBytesNeeded,
                                NULL,
                                NULL,
                                NULL,
                                NULL))
        {
            status = ERROR_INSUFFICIENT_BUFFER;
        }
    }

#endif // _WIN64


    if (status != NO_ERROR)
    {
        SetLastError(status);
        return FALSE;
    }

    //
    // Catch the case where the RPC call succeeded even though we used
    // a dummy buffer (e.g., SERVICE_FAILURE_ACTIONS on a service with
    // no command line or reboot message).  Note that the server side
    // of this API fills in pcbBytesNeeded even on success
    //
    if (fDummyBuffer)
    {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    //
    // Convert offsets to pointers in the returned structure
    //
    switch (dwInfoLevel)
    {
    case SERVICE_CONFIG_DESCRIPTION:
        {
            LPSERVICE_DESCRIPTIONW psd =
                    (LPSERVICE_DESCRIPTIONW) lpBuffer;
            if (psd->lpDescription != NULL)
            {
                psd->lpDescription = (LPWSTR)
                        (lpBuffer + (DWORD)(ULONG_PTR) psd->lpDescription);
            }
        }
        break;

    case SERVICE_CONFIG_FAILURE_ACTIONS:
        {
            LPSERVICE_FAILURE_ACTIONSW psfa =
                    (LPSERVICE_FAILURE_ACTIONSW) lpBuffer;
            if (psfa->lpRebootMsg != NULL)
            {
                psfa->lpRebootMsg = (LPWSTR)
                        (lpBuffer + (DWORD)(ULONG_PTR) psfa->lpRebootMsg);
            }
            if (psfa->lpCommand != NULL)
            {
                psfa->lpCommand = (LPWSTR)
                        (lpBuffer + (DWORD)(ULONG_PTR) psfa->lpCommand);
            }
            if (psfa->lpsaActions != NULL)
            {
                psfa->lpsaActions = (SC_ACTION *)
                        (lpBuffer + (DWORD)(ULONG_PTR) psfa->lpsaActions);
            }
        }
        break;
    }

    return TRUE;
}


BOOL
WINAPI
QueryServiceLockStatusA(
    SC_HANDLE                       hSCManager,
    LPQUERY_SERVICE_LOCK_STATUSA    lpLockStatus,
    DWORD                           cbBufSize,
    LPDWORD                         pcbBytesNeeded
    )

/*++

Routine Description:

    This is the DLL entry point for the QueryServiceLockStatus function.
    This function returns lock status information on a Service Control
    Manager database.


Arguments:

    hSCManager - Handled obtained from a previous call to OpenSCManager
        call.

    lpLockStatus - A pointer to a buffer to receive a
        QUERY_SERVICE_LOCK_STATUS information structure.

Return Value:


Note:


--*/
{
    DWORD                           status;
    LPQUERY_SERVICE_LOCK_STATUSA    pStatusBuf;
    QUERY_SERVICE_LOCK_STATUSA      statusBuf;
    DWORD                           tempBufSize;

    tempBufSize = cbBufSize;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(QUERY_SERVICE_LOCK_STATUSA))
    {
        pStatusBuf = &statusBuf;
        tempBufSize = sizeof(QUERY_SERVICE_LOCK_STATUSA);
    }
    else
    {
        pStatusBuf = lpLockStatus;
    }

    RpcTryExcept
    {
        status = RQueryServiceLockStatusA(
                    (SC_RPC_HANDLE)hSCManager,
                    pStatusBuf,
                    tempBufSize,
                    pcbBytesNeeded);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
    {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR)
    {

#ifdef _WIN64

        //
        // pcbBytesNeeded isn't filled in if the byte count is too
        // small (returned when the buffer size is large enough to
        // hold the 32-bit structure but too small to hold the
        // 64-bit structure.  Get the necessary (32-bit) size.
        //

        if (status == RPC_X_BYTE_COUNT_TOO_SMALL)
        {
            RpcTryExcept
            {
                status = RQueryServiceLockStatusA(
                            (SC_RPC_HANDLE)hSCManager,
                            pStatusBuf,
                            sizeof(QUERY_SERVICE_LOCK_STATUSA),
                            pcbBytesNeeded);
            }
            RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
            {
                status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
            }
            RpcEndExcept
        }


        //
        // Since the ACF file specifies byte_count for this API, we're
        // responsible for managing the count of bytes needed by the
        // caller.  For 64-bit clients calling 32-bit servers, the
        // returned buffer size is too small because of differing
        // pointer sizes.  Add on the minimum number of bytes that
        // will guarantee enough space in the buffer for the next
        // call.
        //

        if (status == ERROR_INSUFFICIENT_BUFFER)
        {
            //
            // 1 embedded pointer in the structure and max
            // space required for alignment.
            //
            *pcbBytesNeeded += (sizeof(PVOID) - sizeof(ULONG) + sizeof(PVOID));
        }
        else if (status == RPC_X_BYTE_COUNT_TOO_SMALL)
        {
            //
            // We get here if we called a 32-bit server where the lock
            // was unowned and we used a buffer size smaller than
            // sizeof(QUERY_SERVICE_LOCK_STATUSA).
            //

            *pcbBytesNeeded = sizeof(QUERY_SERVICE_LOCK_STATUSA) + sizeof(WCHAR);
            status = ERROR_INSUFFICIENT_BUFFER;
        }

#endif  // _WIN64

        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


BOOL
WINAPI
QueryServiceLockStatusW(
    SC_HANDLE                       hSCManager,
    LPQUERY_SERVICE_LOCK_STATUSW    lpLockStatus,
    DWORD                           cbBufSize,
    LPDWORD                         pcbBytesNeeded
    )

/*++

Routine Description:

    see QueryServiceLockStatusA

Arguments:


Return Value:


Note:


--*/
{
    DWORD                           status;
    LPQUERY_SERVICE_LOCK_STATUSW    pStatusBuf;
    QUERY_SERVICE_LOCK_STATUSW      statusBuf;
    DWORD                           tempBufSize;

    tempBufSize = cbBufSize;

    //
    // Create a dummy buffer that is at least the size of the structure.
    // This way RPC should at least send the request to the server side.
    // The server should recognize that the buffer is to small for any
    // strings, and put the correct size in the BytesNeeded parameter,
    // and fail the call.
    //
    if (cbBufSize < sizeof(QUERY_SERVICE_LOCK_STATUSW))
    {
        pStatusBuf = &statusBuf;
        tempBufSize = sizeof(QUERY_SERVICE_LOCK_STATUSW);
    }
    else
    {
        pStatusBuf = lpLockStatus;
    }

    RpcTryExcept
    {
        status = RQueryServiceLockStatusW(
                    (SC_RPC_HANDLE)hSCManager,
                    pStatusBuf,
                    tempBufSize,
                    pcbBytesNeeded);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
    {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR)
    {

#ifdef _WIN64

        //
        // pcbBytesNeeded isn't filled in if the byte count is too
        // small (returned when the buffer size is large enough to
        // hold the 32-bit structure but too small to hold the
        // 64-bit structure.  Get the necessary (32-bit) size.
        //

        if (status == RPC_X_BYTE_COUNT_TOO_SMALL)
        {
            RpcTryExcept
            {
                status = RQueryServiceLockStatusW(
                            (SC_RPC_HANDLE)hSCManager,
                            pStatusBuf,
                            sizeof(QUERY_SERVICE_LOCK_STATUSW),
                            pcbBytesNeeded);
            }
            RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
            {
                status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
            }
            RpcEndExcept
        }

        //
        // Since the ACF file specifies byte_count for this API, we're
        // responsible for managing the count of bytes needed by the
        // caller.  For 64-bit clients calling 32-bit servers, the
        // returned buffer size is too small because of differing
        // pointer sizes.  Add on the minimum number of bytes that
        // will guarantee enough space in the buffer for the next
        // call.
        //

        if (status == ERROR_INSUFFICIENT_BUFFER)
        {
            //
            // 1 embedded pointer in the structure and max
            // space required for alignment.
            //
            *pcbBytesNeeded += (sizeof(PVOID) - sizeof(ULONG) + sizeof(PVOID));
        }
        else if (status == RPC_X_BYTE_COUNT_TOO_SMALL)
        {
            //
            // We get here if we called a 32-bit server where the lock
            // was unowned and we used a buffer size smaller than
            // sizeof(QUERY_SERVICE_LOCK_STATUSW).
            //

            *pcbBytesNeeded = sizeof(QUERY_SERVICE_LOCK_STATUSW) + sizeof(WCHAR);
            status = ERROR_INSUFFICIENT_BUFFER;
        }

#endif  // _WIN64

        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}


BOOL
WINAPI
UnlockServiceDatabase(
    IN  SC_LOCK     ScLock
    )

/*++

Routine Description:

    This is the DLL entry point for the UnlockServiceDatabase function.
    This function releases a lock on a Service Control Manager database.


Arguments:

    ScLock - Lock obtained from a previous LockServiceDatabase call.

Return Value:



--*/
{
    DWORD          status;


    UNREFERENCED_PARAMETER(ScLock);

    RpcTryExcept {

        status = RUnlockServiceDatabase((LPSC_RPC_LOCK)&ScLock);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_SERVICE_LOCK);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}

BOOL
WINAPI
NotifyBootConfigStatus(
    IN BOOL     BootAcceptable
    )

/*++

Routine Description:

    If we are not currently booted with Last Known Good, this function
    will revert to Last Known Good if the boot is not acceptable.  Or it
    will save the boot configuration that we last booted from as the
    Last Known Good.  This is the configuration that we will fall back
    to if a future boot fails.

Arguments:

    BootAcceptable - This indicates whether or not the boot was acceptable.

Return Value:

    TRUE - This is only returned if the boot is acceptable, and we
        successfully replaced Last Known Good with the current boot
        configuration.

    FALSE - This is returned if an error occured when attempting to replace
        Last Known Good or if the system is currently booted from Last
        Known Good.


--*/
{
    DWORD          status;


    RpcTryExcept {

        status = RNotifyBootConfigStatus(
                    NULL,                   // A Local Call Only.
                    (DWORD)BootAcceptable);

    }
    RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
        status = ScMapRpcError(RpcExceptionCode(), ERROR_INVALID_HANDLE);
    }
    RpcEndExcept

    if (status != NO_ERROR){
        SetLastError(status);
        return(FALSE);
    }

    return(TRUE);
}

DWORD
ScWaitForStart(
    VOID
    )

/*++

Routine Description:

    This routine waits until the SC_INTERNAL_START_EVENT is set or until
    a timeout occurs.  Then it returns.

Arguments:

    none

Return Value:

    none

--*/
{
    DWORD                   status;
    HANDLE                  ScStartEvent = NULL;

    //
    // Try opening the event first because it will work most of the
    // time.
    //
    ScStartEvent = OpenEventW(
                    SYNCHRONIZE,
                    FALSE,
                    SC_INTERNAL_START_EVENT );

    if (ScStartEvent == NULL) {

        status = GetLastError();

        if (status == ERROR_FILE_NOT_FOUND) {
            //
            // Only if we can't find the event do we attempt to create
            // it here.
            //

            SCC_LOG0(ERROR,
                     "ScWaitForStart: Event does not exist -- attempting to create it\n");

            //
            // Create the event that the OpenSCManager will use to wait on the
            // service controller with.
            //
            SECURITY_ATTRIBUTES     SecurityAttributes;
            PSECURITY_DESCRIPTOR    SecurityDescriptor=NULL;

            status = ScCreateStartEventSD(&SecurityDescriptor);

            if (status != NO_ERROR) {
                SCC_LOG0(ERROR,"ScGetStartEvent: Couldn't allocate for SecurityDesc\n");
                return status;
            }

            SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
            SecurityAttributes.bInheritHandle = FALSE;
            SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor;

            ScStartEvent = CreateEventW(
                        &SecurityAttributes,
                        TRUE,                   // Must be manually reset
                        FALSE,                  // The event is initially not signalled
                        SC_INTERNAL_START_EVENT );

            status = GetLastError();    // must do this before LocalFree

            LocalFree(SecurityDescriptor);

            if (ScStartEvent == NULL) {
                //
                // Failed to create StartEvent.
                //
                // If we failed to create it because someone else created
                // it between our calls to OpenEvent and CreateEvent, try
                // to open it once more.
                //
                if (status == ERROR_ALREADY_EXISTS) {

                    ScStartEvent = OpenEventW(
                                    SYNCHRONIZE,
                                    FALSE,
                                    SC_INTERNAL_START_EVENT );

                    if (ScStartEvent == NULL) {

                        status = GetLastError();

                        SCC_LOG1(ERROR,"ScWaitForStart: OpenEvent (StartEvent) failed "
                                FORMAT_DWORD " on second attempt\n", status);
                        return status;
                    }
                }
                else {
                    SCC_LOG1(ERROR,"ScWaitForStart: CreateEvent (StartEvent) Failed "
                            FORMAT_DWORD "\n", status);
                    return status;
                }
            }
        }
        else {
            //
            // Could not open the event for some unknown reason.  Give up.
            //
            SCC_LOG1(ERROR,"ScWaitForStart: OpenEvent (StartEvent) Failed "
                    FORMAT_DWORD "\n", status);
            return status;
        }
    }

    SCC_LOG0(TRACE,"Beginning wait for ScStartEvent\n");

    status = WaitForSingleObject(ScStartEvent, SC_START_TIMEOUT);

    CloseHandle(ScStartEvent);

    if (status == WAIT_TIMEOUT) {
        SCC_LOG0(ERROR,"ScWaitForStart: TIMEOUT waiting for StartEvent\n");
        return ERROR_TIMEOUT;
    }

    return NO_ERROR;
}


DWORD
ScMapRpcError(
    IN DWORD RpcError,
    IN DWORD BadContextError
    )
/*++

Routine Description:

    This routine maps the RPC error into a more meaningful error
    for the caller.

Arguments:

    RpcError - Supplies the exception error raised by RPC

    BadContextError - Supplies the error code to return whenever an error
        which indicates invalid context is received.  In some cases, this
        value is ERROR_INVALID_HANDLE; in others, it is ERROR_INVALID_SERVICE_LOCK.

Return Value:

    Returns the mapped error.

--*/
{
    switch (RpcError)
    {
        case RPC_S_INVALID_BINDING:
        case RPC_X_SS_IN_NULL_CONTEXT:
        case RPC_X_SS_CONTEXT_DAMAGED:
        case RPC_X_SS_HANDLES_MISMATCH:
        case ERROR_INVALID_HANDLE:
            return BadContextError;

        case RPC_X_NULL_REF_POINTER:
        case EXCEPTION_ACCESS_VIOLATION:
            return ERROR_INVALID_ADDRESS;

        case RPC_S_INVALID_TAG:
            return ERROR_INVALID_LEVEL;

        case RPC_S_PROCNUM_OUT_OF_RANGE:
            return ERROR_CALL_NOT_IMPLEMENTED;

        default:
            return RpcError;
    }
}
