mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5250 lines
131 KiB
5250 lines
131 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
}
|