我试图找到一种查询Windows服务状态的方法,即它是运行,停止还是禁用。我知道我可以使用sc.exe query ServiceName
,但这将涉及使用批处理文件来查找输出中的状态,管道到文件然后读取它,这看起来有点复杂。我发现了以下Windows API function on MSDN,我相信它可能会做我想要的,但我不确定如何将这种方式纳入Inno Setup。或者,是否有另一种方法可用于通过Inno Setup本机返回Windows服务的状态?
答案 0 :(得分:8)
到目前为止,WinAPI是您可以选择从Inno Setup控制服务的最佳方式。出于您的目的,足以使用QueryServiceStatus
功能。它已被Ex
版本取代,只是为了返回您不需要执行任务的内容;它没有被弃用。以下代码使用最低必需access rights,因此即使没有管理员提升,它也可以运行:
[Code]
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
const
SC_MANAGER_CONNECT = $0001;
SERVICE_QUERY_STATUS = $0004;
SERVICE_STOPPED = $00000001;
SERVICE_START_PENDING = $00000002;
SERVICE_STOP_PENDING = $00000003;
SERVICE_RUNNING = $00000004;
SERVICE_CONTINUE_PENDING = $00000005;
SERVICE_PAUSE_PENDING = $00000006;
SERVICE_PAUSED = $00000007;
type
TSCHandle = THandle;
TServiceStatus = record
dwServiceType: DWORD;
dwCurrentState: DWORD;
dwControlsAccepted: DWORD;
dwWin32ExitCode: DWORD;
dwServiceSpecificExitCode: DWORD;
dwCheckPoint: DWORD;
dwWaitHint: DWORD;
end;
function OpenService(hSCManager: TSCHandle; lpServiceName: string;
dwDesiredAccess: DWORD): TSCHandle;
external 'OpenService{#AW}@advapi32.dll stdcall';
function OpenSCManager(lpMachineName: string; lpDatabaseName: string;
dwDesiredAccess: DWORD): TSCHandle;
external 'OpenSCManager{#AW}@advapi32.dll stdcall';
function QueryServiceStatus(hService: TSCHandle;
out lpServiceStatus: TServiceStatus): BOOL;
external 'QueryServiceStatus@advapi32.dll stdcall';
function CloseServiceHandle(hSCObject: TSCHandle): BOOL;
external 'CloseServiceHandle@advapi32.dll stdcall';
function GetServiceState(const SvcName: string): DWORD;
var
Status: TServiceStatus;
Manager: TSCHandle;
Service: TSCHandle;
begin
// open service manager with the lowest required access rights for this task
Manager := OpenSCManager('', '', SC_MANAGER_CONNECT);
if Manager <> 0 then
try
// open service with the only required access right needed for this task
Service := OpenService(Manager, SvcName, SERVICE_QUERY_STATUS);
if Service <> 0 then
try
// and query service status
if QueryServiceStatus(Service, Status) then
Result := Status.dwCurrentState
else
RaiseException('QueryServiceStatus failed. ' + SysErrorMessage(DLLGetLastError));
finally
CloseServiceHandle(Service);
end
else
RaiseException('OpenService failed. ' + SysErrorMessage(DLLGetLastError));
finally
CloseServiceHandle(Manager);
end
else
RaiseException('OpenSCManager failed. ' + SysErrorMessage(DLLGetLastError));
end;
示例用法:
try
case GetServiceState('netman') of
SERVICE_STOPPED: MsgBox('The service is not running.', mbInformation, MB_OK);
SERVICE_START_PENDING: MsgBox('The service is starting.', mbInformation, MB_OK);
SERVICE_STOP_PENDING: MsgBox('The service is stopping.', mbInformation, MB_OK);
SERVICE_RUNNING: MsgBox('The service is running.', mbInformation, MB_OK);
SERVICE_CONTINUE_PENDING: MsgBox('The service continue is pending.', mbInformation, MB_OK);
SERVICE_PAUSE_PENDING: MsgBox('The service pause is pending.', mbInformation, MB_OK);
SERVICE_PAUSED: MsgBox('The service is paused.', mbInformation, MB_OK);
else
RaiseException('GetServiceState returned unknown state.');
end;
except
MsgBox(GetExceptionMessage, mbError, MB_OK);
end;
或者,如果没有报告失败的内容,您可以编写如下函数:
function TryGetServiceState(const SvcName: string; out State: DWORD): Boolean;
var
Status: TServiceStatus;
Manager: TSCHandle;
Service: TSCHandle;
begin
Result := False;
Manager := OpenSCManager('', '', SC_MANAGER_CONNECT);
if Manager <> 0 then
begin
Service := OpenService(Manager, SvcName, SERVICE_QUERY_STATUS);
if Service <> 0 then
begin
if QueryServiceStatus(Service, Status) then
begin
Result := True;
State := Status.dwCurrentState;
end;
CloseServiceHandle(Service);
end;
CloseServiceHandle(Manager);
end;
end;
可能的用法:
var
State: DWORD;
begin
if TryGetServiceState('netman', State) then
begin
case State of
SERVICE_STOPPED: MsgBox('The service is not running.', mbInformation, MB_OK);
SERVICE_START_PENDING: MsgBox('The service is starting.', mbInformation, MB_OK);
SERVICE_STOP_PENDING: MsgBox('The service is stopping.', mbInformation, MB_OK);
SERVICE_RUNNING: MsgBox('The service is running.', mbInformation, MB_OK);
SERVICE_CONTINUE_PENDING: MsgBox('The service continue is pending.', mbInformation, MB_OK);
SERVICE_PAUSE_PENDING: MsgBox('The service pause is pending.', mbInformation, MB_OK);
SERVICE_PAUSED: MsgBox('The service is paused.', mbInformation, MB_OK);
else
MsgBox('GetServiceState returned unknown state.', mbError, MB_OK);
end;
end
else
MsgBox('Something failed during service state checking.', mbError, MB_OK);
end;
答案 1 :(得分:3)
或者,是否有另一种方法可用于通过Inno Setup本机返回Windows服务的状态?
InnoSetup不提供检查服务的安装/运行状态的本机方法。
我在MSDN上找到了以下Windows API函数,我相信它可能会做我想要的,但我不确定如何将这个功能合并到Inno Setup中。
是的,有一些旧代码在vincenzo.net处飞来飞去,它使用了QueryServiceStatus()
。随意根据您的需求调整代码。我还没有测试过。
[Code]
// function IsServiceInstalled(ServiceName: string) : boolean;
// function IsServiceRunning(ServiceName: string) : boolean;
// function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean;
// function RemoveService(ServiceName: string) : boolean;
// function StartService(ServiceName: string) : boolean;
// function StopService(ServiceName: string) : boolean;
// function SetupService(service, port, comment: string) : boolean;
type
SERVICE_STATUS = record
dwServiceType : cardinal;
dwCurrentState : cardinal;
dwControlsAccepted : cardinal;
dwWin32ExitCode : cardinal;
dwServiceSpecificExitCode : cardinal;
dwCheckPoint : cardinal;
dwWaitHint : cardinal;
end;
HANDLE = cardinal;
const
SERVICE_QUERY_CONFIG = $1;
SERVICE_CHANGE_CONFIG = $2;
SERVICE_QUERY_STATUS = $4;
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 'OpenSCManagerA@advapi32.dll stdcall';
function OpenService(hSCManager :HANDLE;lpServiceName: string; dwDesiredAccess :cardinal): HANDLE;
external 'OpenServiceA@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 'CreateServiceA@advapi32.dll stdcall';
function DeleteService(hService :HANDLE): boolean;
external 'DeleteService@advapi32.dll stdcall';
function StartNTService(hService :HANDLE;dwNumServiceArgs : cardinal;lpServiceArgVectors : cardinal) : boolean;
external 'StartServiceA@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 OpenServiceManager() : HANDLE;
begin
if UsingWinNT() = true then begin
Result := OpenSCManager('','',SC_MANAGER_ALL_ACCESS);
if Result = 0 then
MsgBox('the servicemanager is not available', mbError, MB_OK)
end
else begin
MsgBox('only nt based systems support services', mbError, MB_OK)
Result := 0;
end
end;
function IsServiceInstalled(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_CONFIG);
if hService <> 0 then begin
Result := true;
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end
end;
function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := CreateService(hSCM,ServiceName,DisplayName,SERVICE_ALL_ACCESS,ServiceType,StartType,0,FileName,'',0,'','','');
if hService <> 0 then begin
Result := true;
// Win2K & WinXP supports aditional description text for services
if Description<> '' then
RegWriteStringValue(HKLM,'System\CurrentControlSet\Services\' + ServiceName,'Description',Description);
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end
end;
function RemoveService(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_DELETE);
if hService <> 0 then begin
Result := DeleteService(hService);
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end
end;
function StartService(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_START);
if hService <> 0 then begin
Result := StartNTService(hService,0,0);
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end;
end;
function StopService(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
Status : SERVICE_STATUS;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_STOP);
if hService <> 0 then begin
Result := ControlService(hService,SERVICE_CONTROL_STOP,Status);
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end;
end;
function IsServiceRunning(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
Status : SERVICE_STATUS;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_STATUS);
if hService <> 0 then begin
if QueryServiceStatus(hService,Status) then begin
Result :=(Status.dwCurrentState = SERVICE_RUNNING)
end;
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end
end;
我的建议是将代码放入外部的iss文件(service.iss),然后#include它。这将使安装程序本身的代码部分更加清晰。
用法:
function InitializeSetup(): boolean;
begin
if IsServiceInstalled('myservice') = false then begin
if InstallService('c:\winnt\system32\myservice.exe','myservice','my service','my service is doing usefull things',SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START) = true then begin
StartService('myservice');
StopService('myservice');
// after stopping a service you should wait some seconds before removing
RemoveService('myservice');
// otherwise removing can fail
end
end
else if IsServiceRunning('myservice') then
MsgBox('myservice is running',mbInformation, MB_OK);
Result := false
end;