I'm currently working on an Inno Setup installer which checks for dependent Services of a given Service. According to the MSDN docs, I need the function EnumDependentServices()
.
However, when I pass parameters to the function, Inno Setup gives me status code 5, which stands for "Access Denied", although I execute my Setup as an Administrator.
I'm using the SC_MANAGER_ALL_ACCESS (0xF003F)
right for starting the Service Handler, and it still denies my function call.
Both the SCManager and the opened Service (I tested it with "DnsCache") return non-zero values, and work with my other functions.
Here is the [Code]
snippet from my .iss
file:
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
type
SERVICE_STATUS = record
dwServiceType: DWORD;
dwCurrentState: DWORD;
dwControlsAccepted: DWORD;
dwWin32ExitCode: DWORD;
dwServiceSpecificExitCode: DWORD;
dwCheckPoint: DWORD;
dwWaitHint: DWORD;
end;
HANDLE = THandle;
PDWord = DWORD;
const
SERVICE_QUERY_CONFIG = $1;
SC_MANAGER_CONNECT = $0001;
SERVICE_QUERY_STATUS = $0004;
SERVICE_CHANGE_CONFIG = $2;
SERVICE_CURRENT_STATUS = $0003;
SERVICE_START = $10;
SERVICE_STOP = $20;
SERVICE_ALL_ACCESS = $f01ff;
SC_MANAGER_ALL_ACCESS = $F003F;
SERVICE_WIN32_OWN_PROCESS = $10;
SERVICE_WIN32_SHARE_PROCESS = $20;
SERVICE_WIN32 = $30;
SERVICE_INTERACTIVE_PROCESS = $100;
SERVICE_BOOT_START = $0;
SERVICE_SYSTEM_START = $1;
SERVICE_AUTO_START = $2;
SERVICE_DEMAND_START = $3;
SERVICE_DISABLED = $4;
SERVICE_DELETE = $10000;
SERVICE_CONTROL_STOP = $1;
SERVICE_CONTROL_PAUSE = $2;
SERVICE_CONTROL_CONTINUE = $3;
SERVICE_CONTROL_INTERROGATE = $4;
SERVICE_STOPPED = $1;
SERVICE_START_PENDING = $2;
SERVICE_STOP_PENDING = $3;
SERVICE_RUNNING = $4;
SERVICE_CONTINUE_PENDING = $5;
SERVICE_PAUSE_PENDING = $6;
SERVICE_PAUSED = $7;
// #######################################################################################
// nt based service utilities
// #######################################################################################
function OpenSCManager(lpMachineName, lpDatabaseName: string; dwDesiredAccess :cardinal): HANDLE;
external 'OpenSCManager{#AW}@advapi32.dll stdcall';
function OpenService(hSCManager :HANDLE;lpServiceName: string; dwDesiredAccess :cardinal): HANDLE;
external 'OpenService{#AW}@advapi32.dll stdcall';
function CloseServiceHandle(hSCObject :HANDLE): boolean;
external 'CloseServiceHandle@advapi32.dll stdcall';
function CreateService(hSCManager :HANDLE;lpServiceName, lpDisplayName: string;dwDesiredAccess,dwServiceType,dwStartType,dwErrorControl: cardinal;lpBinaryPathName,lpLoadOrderGroup: String; lpdwTagId : cardinal;lpDependencies,lpServiceStartName,lpPassword :string): cardinal;
external 'CreateService{#AW}@advapi32.dll stdcall';
function DeleteService(hService :HANDLE): boolean;
external 'DeleteService@advapi32.dll stdcall';
function StartNTService(hService :HANDLE;dwNumServiceArgs : cardinal;lpServiceArgVectors : cardinal) : boolean;
external 'StartService{#AW}@advapi32.dll stdcall';
function ControlService(hService :HANDLE; dwControl :cardinal;var ServiceStatus :SERVICE_STATUS) : boolean;
external 'ControlService@advapi32.dll stdcall';
function QueryServiceStatus(hService :HANDLE;var ServiceStatus :SERVICE_STATUS) : boolean;
external 'QueryServiceStatus@advapi32.dll stdcall';
function QueryServiceStatusEx(hService :HANDLE;ServiceStatus :SERVICE_STATUS) : boolean;
external 'QueryServiceStatus@advapi32.dll stdcall';
function GetLastError() : cardinal;
external 'GetLastError@kernel32.dll stdcall';
function EnumDependentServices(
hService :HANDLE;
ServiceStatus : LongInt;
lpServices: DWORD;
cbBufSize: DWORD;
var pcbBytesNeeded: DWORD;
var lpServicesReturned : DWORD) : boolean;
external 'EnumDependentServices{#AW}@advapi32.dll stdcall';
function HasServiceDependencies(const ServiceName: string) : boolean;
var
hSCM: HANDLE;
hService: HANDLE;
pcbBytesNeeded : DWORD;
lpServicesReturned : DWORD;
begin
hSCM := OpenSCManager('', '', SC_MANAGER_ALL_ACCESS);
Log(Format('%d', [hSCM]));
if hSCM <> 0 then begin
hService := OpenService(hSCM, ServiceName, SERVICE_QUERY_STATUS);
Log(Format('%d', [hService]));
if hService <> 0 then begin
EnumDependentServices( hService, SERVICE_CURRENT_STATUS, 0, 0, pcbBytesNeeded, lpServicesReturned);
MsgBox(SysErrorMessage(DLLGetLastError()), mbError, mb_Ok);
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end;
Result := false;
end;
function InitializeSetup(): Boolean;
begin
Log('InitializeSetup called');
Result := HasServiceDependencies('Dnscache');
if Result = False then
MsgBox('InitializeSetup:' #13#13 'Ok, bye bye.', mbInformation, MB_OK);
end;
I tried modifying the Status and Query Codes to other ones, but that didn't help, either.
Per the EnumDependentServices()
documentation:
[in] hService
A handle to the service. This handle is returned by the
OpenService
orCreateService
function, and it must have theSERVICE_ENUMERATE_DEPENDENTS
access right. For more information, see Service Security and Access Right...
The following error codes may be set by the service control manager. Other error codes may be set by the registry functions that are called by the service control manager.
Return code Description ERROR_ACCESS_DENIED The handle does not have the SERVICE_ENUMERATE_DEPENDENTS access right.
When you are opening your hService
handle, you are not specifying the SERVICE_ENUMERATE_DEPENDENTS
right, only the SERVICE_QUERY_STATUS
right (which is used only by QueryServiceStatus/Ex()
and NotifyServiceStatusChange()
, not by EnumDependentServices()
).
Also, SC_MANAGER_ALL_ACCESS
is too many rights to ask for. Don't ask for more rights than you actually need. In this case, you only need SC_MANAGER_CONNECT
.
Also, SERVICE_CURRENT_STATUS
is not a valid symbol defined by the Win32 API. You are declaring it as $0003
, which is the same value as SERVICE_STATE_ALL
, which is the actual symbol that EnumDependentServices()
defines.
Try this instead:
const
...
SERVICE_STATE_ALL = $0003;
SERVICE_ENUMERATE_DEPENDENTS = $0008;
ERROR_MORE_DATA = 234;
...
procedure LogError(const FuncName: string);
var
ErrCode : DWORD;
ErrMsg : string;
begin
ErrCode := GetLastError();
ErrMsg := Format('%s failed: (%d) %s', [FuncName, ErrCode, SysErrorMessage(ErrCode)]);
Log(ErrMsg);
MsgBox(ErrMsg, mbError, mb_Ok);
end;
function HasServiceDependencies(const ServiceName: string) : boolean;
var
hSCM: HANDLE;
hService: HANDLE;
dwBytesNeeded : DWORD;
dwServicesReturned : DWORD;
begin
Result := False;
hSCM := OpenSCManager('', '', SC_MANAGER_CONNECT);
if hSCM = 0 then begin
LogError('OpenSCManager');
end
else begin
hService := OpenService(hSCM, ServiceName, SERVICE_ENUMERATE_DEPENDENTS);
if hService = 0 then begin
LogError('OpenService');
end
else begin
// specifying a nil buffer, so will return True if there are no dependencies,
// otherwise will return False and set LastError=ERROR_MORE_DATA if
// dependency data is available...
if EnumDependentServices(hService, SERVICE_STATE_ALL, 0, 0, dwBytesNeeded, dwServicesReturned) then begin
Log(Format('%s has no dependencies', [ServiceName]));
end
else if GetLastError() <> ERROR_MORE_DATA then begin
LogError('EnumDependentServices');
end
else begin
Log(Format('%s has dependencies', [ServiceName]));
end;
CloseServiceHandle(hService);
end;
CloseServiceHandle(hSCM);
end;
end;