英文:
Checking dependent services of a given service in Inno Setup
问题
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'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.
答案1
得分: 2
> [in] hService
>
> 服务句柄。此句柄由OpenService
或CreateService
函数返回,必须具有SERVICE_ENUMERATE_DEPENDENTS
访问权限。有关更多信息,请参阅服务安全性和访问权限
>
> ...
>
> 服务控制管理器可以设置以下错误代码。服务控制管理器调用的注册表函数可能设置其他错误代码。
>
> | 返回代码 | 描述 |
> | ----------- | ----------- |
> | ERROR_ACCESS_DENIED | 句柄没有SERVICE_ENUMERATE_DEPENDENTS
访问权限。 |
当您打开hService
句柄时,您未指定SERVICE_ENUMERATE_DEPENDENTS
权限,只指定了SERVICE_QUERY_STATUS
权限(仅由QueryServiceStatus/Ex()
和NotifyServiceStatusChange()
使用,而不是EnumDependentServices()
)。
此外,SC_MANAGER_ALL_ACCESS
权限太多,不要请求比实际需要的权限更多。在这种情况下,您只需要SC_MANAGER_CONNECT
。
此外,SERVICE_CURRENT_STATUS
不是Win32 API定义的有效符号。您将其声明为$0003
,这与SERVICE_STATE_ALL
的值相同,而EnumDependentServices()
实际定义了SERVICE_STATE_ALL
。
请尝试使用以下代码:
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
// 指定空缓冲区,如果没有依赖项,将返回True,否则将返回False并设置LastError=ERROR_MORE_DATA
if EnumDependentServices(hService, SERVICE_STATE_ALL, nil, 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;
英文:
Per the EnumDependentServices()
documentation:
> [in] hService
>
> A handle to the service. This handle is returned by the OpenService
or CreateService
function, and it must have the SERVICE_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;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论