Como fazer um serviço .NET do Windows iniciar logo após a instalação?

88

Além do service.StartType = ServiceStartMode.Automatic meu serviço não inicia após a instalação

Solução

Inseri este código no meu ProjectInstaller

protected override void OnAfterInstall(System.Collections.IDictionary savedState)
{
    base.OnAfterInstall(savedState);
    using (var serviceController = new ServiceController(this.serviceInstaller1.ServiceName, Environment.MachineName))
        serviceController.Start();
}

Agradecimentos a ScottTx e Francis B.

Jader Dias
fonte
Ele não inicia logo após a instalação ou não inicia quando você reinicia?
Chris Van Opstal

Respostas:

21

Você pode fazer tudo isso de dentro do executável do serviço em resposta a eventos disparados do processo InstallUtil. Substitua o evento OnAfterInstall para usar uma classe ServiceController para iniciar o serviço.

http://msdn.microsoft.com/en-us/library/system.serviceprocess.serviceinstaller.aspx

ScottTx
fonte
3
Esta é uma boa solução, mas ainda requer o uso do utilitário InstallUtil. Se você já está entregando o InstallUtil como parte da instalação, isso faz mais sentido. Mas, se você quiser ignorar o empacotamento do InstallUtil, use a solução de linha de comando.
Matt Davis
181

Publiquei um procedimento passo a passo para criar um serviço do Windows em C # aqui . Parece que você chegou pelo menos a este ponto e agora está se perguntando como iniciar o serviço depois de instalado. Definir a propriedade StartType como Automático fará com que o serviço seja iniciado automaticamente após a reinicialização do sistema, mas não (como você descobriu) iniciará automaticamente o serviço após a instalação.

Não me lembro onde o encontrei originalmente (talvez Marc Gravell?), Mas encontrei uma solução online que permite que você instale e inicie seu serviço executando o próprio serviço. Aqui está o passo a passo:

  1. Estruture a Main()função do seu serviço assim:

    static void Main(string[] args)
    {
        if (args.Length == 0) {
            // Run your service normally.
            ServiceBase[] ServicesToRun = new ServiceBase[] {new YourService()};
            ServiceBase.Run(ServicesToRun);
        } else if (args.Length == 1) {
            switch (args[0]) {
                case "-install":
                    InstallService();
                    StartService();
                    break;
                case "-uninstall":
                    StopService();
                    UninstallService();
                    break;
                default:
                    throw new NotImplementedException();
            }
        }
    }
  2. Aqui está o código de suporte:

    using System.Collections;
    using System.Configuration.Install;
    using System.ServiceProcess;
    
    private static bool IsInstalled()
    {
        using (ServiceController controller = 
            new ServiceController("YourServiceName")) {
            try {
                ServiceControllerStatus status = controller.Status;
            } catch {
                return false;
            }
            return true;
        }
    }
    
    private static bool IsRunning()
    {
        using (ServiceController controller = 
            new ServiceController("YourServiceName")) {
            if (!IsInstalled()) return false;
            return (controller.Status == ServiceControllerStatus.Running);
        }
    }
    
    private static AssemblyInstaller GetInstaller()
    {
        AssemblyInstaller installer = new AssemblyInstaller(
            typeof(YourServiceType).Assembly, null);
        installer.UseNewContext = true;
        return installer;
    }
  3. Continuando com o código de suporte ...

    private static void InstallService()
    {
        if (IsInstalled()) return;
    
        try {
            using (AssemblyInstaller installer = GetInstaller()) {
                IDictionary state = new Hashtable();
                try {
                    installer.Install(state);
                    installer.Commit(state);
                } catch {
                    try {
                        installer.Rollback(state);
                    } catch { }
                    throw;
                }
            }
        } catch {
            throw;
        }
    }
    
    private static void UninstallService()
    {
        if ( !IsInstalled() ) return;
        try {
            using ( AssemblyInstaller installer = GetInstaller() ) {
                IDictionary state = new Hashtable();
                try {
                    installer.Uninstall( state );
                } catch {
                    throw;
                }
            }
        } catch {
            throw;
        }
    }
    
    private static void StartService()
    {
        if ( !IsInstalled() ) return;
    
        using (ServiceController controller = 
            new ServiceController("YourServiceName")) {
            try {
                if ( controller.Status != ServiceControllerStatus.Running ) {
                    controller.Start();
                    controller.WaitForStatus( ServiceControllerStatus.Running, 
                        TimeSpan.FromSeconds( 10 ) );
                }
            } catch {
                throw;
            }
        }
    }
    
    private static void StopService()
    {
        if ( !IsInstalled() ) return;
        using ( ServiceController controller = 
            new ServiceController("YourServiceName")) {
            try {
                if ( controller.Status != ServiceControllerStatus.Stopped ) {
                    controller.Stop();
                    controller.WaitForStatus( ServiceControllerStatus.Stopped, 
                         TimeSpan.FromSeconds( 10 ) );
                }
            } catch {
                throw;
            }
        }
    }
  4. Neste ponto, depois de instalar o serviço na máquina de destino, apenas execute o serviço na linha de comando (como qualquer aplicativo comum) com o -installargumento da linha de comando para instalar e iniciar o serviço.

Acho que abordei tudo, mas se você achar que isso não funciona, entre em contato para que eu possa atualizar a resposta.

Matt Davis
fonte
12
Observe que esta solução não requer o uso de InstallUtil.exe, portanto, você não precisa entregá-lo como parte de seu programa de instalação.
Matt Davis
2
Qual é o ponto com as cláusulas vazias "catch {throw;}"? Além disso, provavelmente não é uma boa ideia ocultar falhas por "Rollback ()", já que essa situação basicamente deixa o sistema em um estado indefinido, eu acho (você tentou instalar um serviço, falhou em algum lugar no meio e não conseguiu desfazê-lo ) Você deve pelo menos "mostrar" ao usuário que há algo suspeito - ou a função Rollback () grava algumas mensagens no console?
Christian.K
5
A reversão grava dados no console. Quanto aos blocos catch vazios, é uma coisa de depuração. Posso colocar um ponto de interrupção na instrução throw para examinar quaisquer exceções que possam ocorrer.
Matt Davis
4
Estou recebendo o erro Erro: O tipo ou nome do namespace 'YourServiceType' não foi encontrado (está faltando uma diretiva using ou uma referência de assembly?
Yogesh
5
YourServiceTypeé o que ProjectInstallervocê adicionou ao serviço que contém ServiceInstallereServiceProcessInstaller
bansi
6

Estúdio visual

Se você estiver criando um projeto de instalação com o VS, pode criar uma ação personalizada que chamou um método .NET para iniciar o serviço. Porém, não é realmente recomendável usar a ação personalizada gerenciada em um MSI. Veja esta página .

ServiceController controller  = new ServiceController();
controller.MachineName = "";//The machine where the service is installed;
controller.ServiceName = "";//The name of your service installed in Windows Services;
controller.Start();

InstallShield ou Wise

Se você estiver usando o InstallShield ou Wise, esses aplicativos fornecem a opção de iniciar o serviço. Por exemplo, com o Wise, você deve adicionar uma ação de controle de serviço. Nesta ação, você especifica se deseja iniciar ou parar o serviço.

Wix

Usando o Wix, você precisa adicionar o seguinte código xml no componente do seu serviço. Para mais informações sobre isso, você pode verificar esta página .

<ServiceInstall 
    Id="ServiceInstaller"  
    Type="ownProcess"  
    Vital="yes"  
    Name=""  
    DisplayName=""  
    Description=""  
    Start="auto"  
    Account="LocalSystem"   
    ErrorControl="ignore"   
    Interactive="no">  
        <ServiceDependency Id="????"/> ///Add any dependancy to your service  
</ServiceInstall>
Francis B.
fonte
5

Você precisa adicionar uma ação personalizada ao final da sequência 'ExecuteImmediate' no MSI, usando o nome do componente do EXE ou um lote (início do sc) como origem. Não acho que isso possa ser feito com o Visual Studio, talvez seja necessário usar uma ferramenta de criação MSI real para isso.

Otávio Décio
fonte
4

Para iniciá-lo logo após a instalação, eu gero um arquivo em lote com installutil seguido por sc start

Não é o ideal, mas funciona ....

Matt
fonte
4

Use a classe .NET ServiceController para iniciá-lo ou emita o comando de linha de comando para iniciá-lo --- "net start servicename". Qualquer uma das maneiras funciona.

ScottTx
fonte
4

Para adicionar à resposta do ScottTx, aqui está o código real para iniciar o serviço se você estiver fazendo maneira Microsoft (ou seja, usando um projeto de instalação, etc ...)

(desculpe o código VB.net, mas é nisso que estou preso)

Private Sub ServiceInstaller1_AfterInstall(ByVal sender As System.Object, ByVal e As System.Configuration.Install.InstallEventArgs) Handles ServiceInstaller1.AfterInstall
    Dim sc As New ServiceController()
    sc.ServiceName = ServiceInstaller1.ServiceName

    If sc.Status = ServiceControllerStatus.Stopped Then
        Try
            ' Start the service, and wait until its status is "Running".
            sc.Start()
            sc.WaitForStatus(ServiceControllerStatus.Running)

            ' TODO: log status of service here: sc.Status
        Catch ex As Exception
            ' TODO: log an error here: "Could not start service: ex.Message"
            Throw
        End Try
    End If
End Sub

Para criar o manipulador de eventos acima, vá para o designer ProjectInstaller onde estão os 2 controladores. Clique no controle ServiceInstaller1. Vá para a janela de propriedades em eventos e lá você encontrará o evento AfterInstall.

Nota: Não coloque o código acima no evento AfterInstall para ServiceProcessInstaller1. Não vai funcionar, vindo da experiência. :)

goku_da_master
fonte
O código VB.net não é ruim! Para aqueles de nós que trabalham em vários idiomas, é bom não ter que converter o código de C!
Steve Reed Sr
Obrigado, isso me ajudou a descobrir como iniciar um serviço automaticamente.
Charles Owen
0

A solução mais fácil é encontrada aqui install-windows-service-without-installutil-exe por @ Hoàng Long

@echo OFF
echo Stopping old service version...
net stop "[YOUR SERVICE NAME]"
echo Uninstalling old service version...
sc delete "[YOUR SERVICE NAME]"

echo Installing service...
rem DO NOT remove the space after "binpath="!
sc create "[YOUR SERVICE NAME]" binpath= "[PATH_TO_YOUR_SERVICE_EXE]" start= auto
echo Starting server complete
pause
Robert Green MBA
fonte