简体   繁体   English

使用Inno Setup升级Windows服务

[英]Upgrading Windows service using Inno Setup

I've created a basic Windows service installation using Inno Setup. 我使用Inno Setup创建了一个基本的Windows服务安装。 both install and uninstall works properly. 安装和卸载都正常工作。

However, I'm having issues with the upgrade procedure. 但是,我在升级过程中遇到问题。

In order to upgrade the service executable it is necessary to stop the service, and only after the service is completely stopped can the updated executables be placed in the target folder. 为了升级服务可执行文件,必须停止服务,并且只有在服务完全停止后,才能将更新的可执行文件放在目标文件夹中。

How can I execute a service-stop command and wait for the service to be completely stopped before the file deployment step is initiated? 如何在启动文件部署步骤之前执行service-stop命令并等待服务完全停止?

The code below comes from the following page: 以下代码来自以下页面:
http://www.vincenzo.net/isxkb/index.php?title=Service_-_Functions_to_Start%2C_Stop%2C_Install%2C_Remove_a_Service http://www.vincenzo.net/isxkb/index.php?title=Service_-_Functions_to_Start%2C_Stop%2C_Install%2C_Remove_a_Service

However, I had to apply a minor fix to make it work. 但是,我必须应用一个小修补程序才能使其正常工作。

Note that I originally posted this answer in 2010. The code at the page above was updated in 2011, so it might be worth a visit. 请注意,我最初在2010年发布了此答案。上面页面中的代码已在2011年更新,因此可能值得一游。

I use this code in my installer, as an #include . 我在安装程序中使用此代码作为#include It is compiled in Inno Setup ANSI . 它是在Inno Setup ANSI中编译的。
It may work for the Unicode version of Inno Setup by replacing A@ with W@ in all external declarations (thank you JeroenWiertPluimers for pointing that out). 它可以通过更换为Inno Setup的Unicode版本工作A@W@在所有的external声明(谢谢你JeroenWiertPluimers指出了这一点)。

Also note that the StartService and StopService just send a start/stop signal, but do not wait for the service to be in the running of stopped state. 另请注意, StartServiceStopService只发送一个启动/停止信号,但不要等待服务处于停止状态的运行状态。 You can construct code that would wait for the service to be running, using the using IsServiceRunning in conjunction with the Pascal Script Sleep() function. 您可以使用IsServiceRunning和Pascal Script Sleep()函数来构造等待服务运行的代码。 Or just Sleep() a predefined number of seconds. 或者只是Sleep()预定义的秒数。

The code implements these functions: 代码实现了这些功能:

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;

The actual code: 实际代码:

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 additional 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;

As of Inno Setup 5.5.0, the CloseApplications and RestartApplications directives are now available. 从Inno Setup 5.5.0开始, CloseApplicationsRestartApplications指令现在可用。 These options will detect files in use and shut down the applications using them. 这些选项将检测正在使用的文件并使用它们关闭应用程序。

There are two parts to this: 这有两个部分:

  1. How can an installer created with Inno Setup start and stop services, how can it create and delete them, how can it change its startup mode? 如何使用Inno Setup创建的安装程序启动和停止服务,如何创建和删除它们,如何更改其启动模式?

    By using the helper functions provided in this collection of routines written for exactly this purpose. 通过使用为此目的而编写的此例程集合中提供的辅助函数。 It is written for the Ansi version of Inno Setup, so changes will be necessary to the API function imports and the PChar parameter types, but it should get you started. 它是为Inni Setup的Ansi版本编写的,因此对API函数导入和PChar参数类型进行更改是必要的,但它应该可以帮助您入门。

  2. How can an existing service be stopped before the new file version is copied? 如何在复制新文件版本之前停止现有服务?

    You have basically two ways of doing this. 你基本上有两种方法可以做到这一点。 You would use Pascal scripting to execute the functions linked to above to stop the service, you just need to decide whether to do it in one of the event functions, or whether to do it in a custom function that will be called via the Check parameter of the file entry for the service executable. 您可以使用Pascal脚本来执行链接到上面的函数来停止服务,您只需要决定是否在其中一个事件函数中执行它,或者是否在通过Check参数调用的自定义函数中执行它服务可执行文件的文件条目。 I would definitely do it in the former, so you can check whether the service was successfully stopped, and prohibit the installation from actually starting when this failed. 我肯定会在前者中执行此操作,因此您可以检查服务是否已成功停止,并禁止安装在此失败时实际启动。

    You should look into the CurStepChanged() and NextButtonClick() event functions, depending on whether you possibly need to prevent the next step. 您应该查看CurStepChanged()NextButtonClick()事件函数,具体取决于您是否可能需要阻止下一步。 The Inno Setup examples show the use of both functions. Inno Setup示例显示了这两种功能的使用。

I am using a batch file that stops, uninstalls, installs and starts a given service, i just call the batchfile using innosetup after all files have been copied. 我正在使用停止,卸载,安装和启动给定服务的批处理文件,我只需在复制完所有文件后使用innosetup调用批处理文件。

[Run]
Filename: "{app}\Scripts\installwindowsService.bat"; Parameters: "{app}"; Flags: runhidden

I put the following commands in my batch file 我将以下命令放在我的批处理文件中

net stop MyService

%SYSTEMROOT%\Microsoft.NET\Framework\v4.0.30319\installutil.exe /u MyService.exe 

%SYSTEMROOT%\Microsoft.NET\Framework\v4.0.30319\installutil.exe  MyService.exe /unattended

net start MyService

It works like a charm and is very simple. 它就像一个魅力,非常简单。 can be used for a first time install or for an update. 可用于首次安装或更新。 Hope it helps you. 希望它能帮到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM