Inno Setup 查询 Windows 服务状态

Inno Setup query Windows service status

我正在尝试找到一种查询 Windows 服务状态的方法,即它是 运行、已停止还是已禁用。我知道我可以使用 sc.exe query ServiceName,但这将涉及使用批处理文件来查找输出中的状态,通过管道传输到文件,然后将其读入,这似乎有点令人费解。我发现了以下 Windows API function on MSDN,我相信它可能会做我想做的,但我不确定如何或是否可以将其合并到 Inno Setup 中。或者,是否有另一种方法可用于使用 Inno Setup 本机返回 Windows 服务的状态?

Alternatively, is there another method that could be used for returning the status of a Windows service natively with Inno Setup?

InnoSetup 不提供检查安装/运行 服务状态的本机方法。

I have found the following Windows API function on MSDN, which I believe will probably do what I want, but am not sure how or if this could be incorporated into 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        = ;
    SERVICE_CHANGE_CONFIG       = ;
    SERVICE_QUERY_STATUS        = ;
    SERVICE_START               = ;
    SERVICE_STOP                = ;
    SERVICE_ALL_ACCESS          = $f01ff;
    SC_MANAGER_ALL_ACCESS       = $f003f;
    SERVICE_WIN32_OWN_PROCESS   = ;
    SERVICE_WIN32_SHARE_PROCESS = ;
    SERVICE_WIN32               = ;
    SERVICE_INTERACTIVE_PROCESS = 0;
    SERVICE_BOOT_START          = [=10=];
    SERVICE_SYSTEM_START        = ;
    SERVICE_AUTO_START          = ;
    SERVICE_DEMAND_START        = ;
    SERVICE_DISABLED            = ;
    SERVICE_DELETE              = 000;
    SERVICE_CONTROL_STOP        = ;
    SERVICE_CONTROL_PAUSE       = ;
    SERVICE_CONTROL_CONTINUE    = ;
    SERVICE_CONTROL_INTERROGATE = ;
    SERVICE_STOPPED             = ;
    SERVICE_START_PENDING       = ;
    SERVICE_STOP_PENDING        = ;
    SERVICE_RUNNING             = ;
    SERVICE_CONTINUE_PENDING    = ;
    SERVICE_PAUSE_PENDING       = ;
    SERVICE_PAUSED              = ;

// #######################################################################################
// 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;

到目前为止,WinAPI 是您可以选择从 Inno Setup 控制服务的最佳方式。出于您的目的,使用 QueryServiceStatus function. It has been superseded by the Ex version just to return things that you don't need for your task; it's not deprecated. The following code uses the lowest necessary access rights 就足够了,因此即使没有管理员提升也可以 运行:

[Code]
#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

const
  SC_MANAGER_CONNECT = [=10=]01;

  SERVICE_QUERY_STATUS = [=10=]04;

  SERVICE_STOPPED = [=10=]000001;
  SERVICE_START_PENDING = [=10=]000002;
  SERVICE_STOP_PENDING = [=10=]000003;
  SERVICE_RUNNING = [=10=]000004;
  SERVICE_CONTINUE_PENDING = [=10=]000005;
  SERVICE_PAUSE_PENDING = [=10=]000006;
  SERVICE_PAUSED = [=10=]000007;

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;