Upgrade a Windows Service without Uninstalling

2019-01-13 08:23发布

问题:

Currently I have to uninstall the old version of my service before I install the new version. I am pretty sure this has something to do with it not being smart enough to update or remove the old service entries before adding the new ones.

Is there a way to have the installer skip registering the service if it already exists? (I can assume the installation folder and service name do not change between versions.)

Also, is there a way to automatically stop the service when uninstalling?


Edit:

I am using MSI packages and the Visual Studio setup project.

回答1:

I've done this with WiX, which generates .MSI files using the ServiceInstall & SeviceControl commands:

<Component Id='c_WSService' Guid='*'>
    <File Id='f_WSService' Name='WSService.exe' Vital='yes' Source='..\wssvr\release\wsservice.exe' />
    <ServiceInstall Id='WSService.exe' Name='WSService' DisplayName='[product name]' Type='ownProcess'
                    Interactive='no' Start='auto' Vital='yes' ErrorControl='normal'
                    Description='Provides local and remote access to [product name] search facilities.' />
    <ServiceControl Id='WSService.exe' Name='WSService' Start='install' Stop='both' Remove='uninstall' Wait='yes' />
</Component>

This stops the service, installs the new version and re-starts the service.



回答2:

Use sc tool from a command line to stop and start the service:

sc stop {name of your service}
sc start {name of your service}

When the service is stopped, update the corresponding files and then start the service again. You should be able to do that from your installer as well. If you use Wix for your installer, then take a look at ServiceControl element.



回答3:

I do not use Visual Studio setup projects, so I might be wrong, but it seems it has no support for the ServiceInstall and ServiceControl tables which are standard features of Windows Installer. Those two tables are specially for installing and updating services....

Wix does support it (see this example), Maybe you can create a merge module, and use that in your project.

Otherwise this might help: Installing Services with Visual Studio (Phil Wilson)



回答4:

Can't you just stop the service, and overwrite the service executable, and then restart the service?



回答5:

You can make separate DLL which service would load and call every time it does its work. Make sure that service unloads DLL after use.

Use should load it into separate Application Domain.

SO http://msdn.microsoft.com/en-us/library/c5b8a8f9.aspx



回答6:

My hacky solution is to modify the ProjectInstaller.vb file so it issues a command to stop and delete the service and then pauses a bit. Probably not as neat from an install perpective as modifying the msi file, but much more readable/logical to whoever inherits my code.

Note that the RunCommandCom bit is blatently cribbed from How to run DOS/CMD/Command Prompt commands from VB.NET?

Using this method combined with code from How to automatically start your service after install? you can have the service install experience you want - A service that installs and starts automatically, and will overwrite a currently running service if there is one.

'This works.  It leaves the MSI in a state that tells you to reboot the PC, but you really don't need to.

Private Sub ProjectInstaller_BeforeInstall(sender As Object, e As System.Configuration.Install.InstallEventArgs) Handles Me.BeforeInstall

    Dim sEchoMessage As String = String.Empty
    sEchoMessage &= " & ECHO ******************       Please be patient      *******************************"
    sEchoMessage &= " & ECHO Pausing to stop and delete the previous version of the following service:"
    sEchoMessage &= " & ECHO " & ServiceInstaller1.ServiceName
    sEchoMessage &= " & ECHO -------------------------------------------------------------------------------"
    sEchoMessage &= " & ECHO After install is complete, you may see a message that says you need to reboot."
    sEchoMessage &= " & ECHO You may IGNORE this message - The service will be installed and running."
    sEchoMessage &= " & ECHO There is NO Reboot required."
    sEchoMessage &= " & ECHO *******************************************************************************"

    RunCommandCom("sc stop " & ServiceInstaller1.ServiceName & " & sc delete " & ServiceInstaller1.ServiceName & sEchoMessage, 15000)

End Sub

Private Sub RunCommandCom(command As String, mSecSleepAfterExecution As Integer)

    Using p As Process = New Process()
        Dim pi As ProcessStartInfo = New ProcessStartInfo()
        pi.Arguments = " /K " + command
        pi.FileName = "cmd.exe"
        p.StartInfo = pi
        p.Start()
        System.Threading.Thread.Sleep(mSecSleepAfterExecution)
        p.CloseMainWindow()
    End Using

End Sub