Inno Setup查询Windows服务状态

时间:2015-09-08 17:27:31

标签: inno-setup

我试图找到一种查询Windows服务状态的方法,即它是运行,停止还是禁用。我知道我可以使用sc.exe query ServiceName,但这将涉及使用批处理文件来查找输出中的状态,管道到文件然后读取它,这看起来有点复杂。我发现了以下Windows API function on MSDN,我相信它可能会做我想要的,但我不确定如何将这种方式纳入Inno Setup。或者,是否有另一种方法可用于通过Inno Setup本机返回Windows服务的状态?

2 个答案:

答案 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;