Como um serviço do Windows pode se reiniciar programaticamente?


Respostas:


187

Defina o serviço para reiniciar após falha (clique duas vezes no serviço no painel de controle e dê uma olhada nessas guias - esqueço o nome dele). Então, sempre que desejar que o serviço reinicie, basta ligar Environment.Exit(1)(ou qualquer retorno diferente de zero) e o sistema operacional o reiniciará para você.


7
Se o local estiver no painel de serviços, clique com o botão direito do mouse no serviço em questão, selecione propriedades e escolha a guia recuperação.
James Michael Hare

20
Vejo como isso atinge o comportamento desejado, mas um código de retorno 1 deve informar ao sistema que houve um erro. Não é uma prática ruim se de fato não houver erro e você quiser apenas reiniciar o serviço?
mgttlinger

4
Isso pode ser feito programaticamente pelo instalador do serviço no evento após a instalação, não é necessário clicar ... E se o seu serviço estiver em um servidor remoto e você precisar instalá-lo várias vezes ao dia, por exemplo, durante o teste?
perfil

5
@mgttlinger: Eu acho que sim, é uma prática ruim quando seu serviço está saudável, por isso nunca precisará ser reiniciado. Mas algumas arquiteturas de serviços estão fora de nosso alcance e, se precisam ser reiniciadas, é um sintoma de que não está bem; portanto, não há problema em fechar e liberar algumas recursos mínimos (se possível) e 'cortar o pé', desde que o o serviço executado incorretamente pode ser pior (inútil).
Luciano

5
@JohnLeidegren, se você deseja um desligamento adequado, pode definir Service.ExitCode = 1e executar Service.Stop()junto com a caixa de seleção 'Ativar ações para paradas com erros' na tela de configuração de recuperação de serviço. Fazer dessa maneira permite um desligamento adequado e ainda aciona a reinicialização.
Chris Rice

22
Dim proc As New Process()
Dim psi As New ProcessStartInfo()

psi.CreateNoWindow = True
psi.FileName = "cmd.exe"
psi.Arguments = "/C net stop YOURSERVICENAMEHERE && net start YOURSERVICENAMEHERE"
psi.LoadUserProfile = False
psi.UseShellExecute = False
psi.WindowStyle = ProcessWindowStyle.Hidden
proc.StartInfo = psi
proc.Start()

3
Lembre-se de adicionar o nome do serviço "" ao redor, se este contiver espaços.
apc

4
Isso é apenas para serviços executados em uma conta privilegiada, o que é uma má ideia de qualquer maneira.
Dmitry Gusarov

17

Você não pode ter certeza de que a conta de usuário em que seu serviço está sendo executado tem permissões para parar e reiniciar o serviço.


Embora tenha marcado com +1 o mesmo problema, ao olhar para sc.exe, ele usa SERVICE_QUERY_CONFIG como dwDesiredAccess ao chamar OpenSCManager () e depois chama OpenService () com SERVICE_START | SERVICE_STOP para abrir um serviço específico e funciona OK (sem problemas com o acesso). Não tenho certeza sobre como isso pode ser feito no .NET.
N10p

A maioria dos serviços do Windows é executada como sistema, portanto não deve ser um problema.
Comute

@Switch, existe um enorme círculo de serviços que são executados no serviço de rede, que por padrão nem tem o direito de abrir seu próprio executável.
AgentFire 24/01/19

@AgentFire, correto, mas o padrão é Sistema Local, não SERVIÇO DE REDE. O sistema local possui inerentemente o nível mais alto de privilégios para o sistema operacional - excedendo os atribuídos aos membros do grupo local de administradores.
Alterna

O @Switch, mas usar os privilégios mais disponíveis, está violando o Princípio PoL .
AgentFire 30/01/19

12

Você pode criar um processo que é um prompt de comando do DOS que se reinicia:

 Process process = new Process();
 process.StartInfo.FileName = "cmd";
 process.StartInfo.Arguments = "/c net stop \"servicename\" & net start \"servicename\"";
 process.Start();

Isso, sem fazer mais nada, herdará o contexto de segurança do encadeamento de chamada ... e contanto que seja um contexto com potência suficiente para reiniciar, você estará bem.
Clay

12
const string strCmdText = "/C net stop \"SERVICENAME\"&net start \"SERVICENAME\"";
Process.Start("CMD.exe", strCmdText);

onde SERVICENAMEé o nome do seu serviço (aspas duplas incluídas para contabilizar espaços no nome do serviço, caso contrário, pode ser omitido).

Limpo, nenhuma configuração de reinicialização automática é necessária.


9
aprendeu algo novo sobre o & vs &&: [command1] & [command2] sempre executará ambos os comandos seqüencialmente, enquanto [command1] && [command2] executa o comando2 somente se o comando1 for executado com êxito ( autoitscript.com/forum/topic/… )
Jeffrey Knight

5

Depende do motivo pelo qual você deseja que ele seja reiniciado.

Se você está apenas procurando uma maneira de fazer com que o serviço se limpe periodicamente, poderá ter um timer em execução no serviço que periodicamente causa uma rotina de limpeza.

Se você estiver procurando uma maneira de reiniciar em caso de falha - o próprio host de serviço poderá fornecer essa capacidade quando estiver configurado.

Então, por que você precisa reiniciar o servidor? O que você está tentando alcançar?


1
Isso não funcionaria se você estivesse preocupado com a pilha de objetos grandes e a fragmentação da memória. Supostamente, os processos .NET 4 / 4.5 e 64 bits ajudaram muito nisso.
David Kassa

3

Eu não acho que você possa em um serviço independente (quando você chamar Reiniciar, ele interromperá o serviço, o que interromperá o comando Reiniciar e nunca mais será iniciado). Se você puder adicionar um segundo .exe (um aplicativo de console que use a classe ServiceManager), poderá iniciar o .exe autônomo, reiniciar o serviço e sair.

Pensando bem, você provavelmente poderia fazer com que o serviço registrasse uma Tarefa Agendada (usando o comando 'at' da linha de comando, por exemplo) para iniciar o serviço e depois interromper a operação; isso provavelmente funcionaria.


3

O problema com a distribuição de um arquivo em lotes ou EXE é que um serviço pode ou não ter as permissões necessárias para executar o aplicativo externo.

A maneira mais limpa de fazer isso que encontrei é usar o método OnStop (), que é o ponto de entrada para o Service Control Manager. Todo o seu código de limpeza será executado e você não terá soquetes pendentes ou outros processos, supondo que seu código de parada esteja fazendo seu trabalho.

Para fazer isso, você precisa definir um sinalizador antes de terminar, que informa ao método OnStop para sair com um código de erro; o SCM sabe que o serviço precisa ser reiniciado. Sem esse sinalizador, você não poderá parar o serviço manualmente no SCM. Isso também pressupõe que você configurou o serviço para reiniciar com um erro.

Aqui está o meu código de parada:

...

bool ABORT;

protected override void OnStop()
{
    Logger.log("Stopping service");
    WorkThreadRun = false;
    WorkThread.Join();
    Logger.stop();
    // if there was a problem, set an exit error code
    // so the service manager will restart this
    if(ABORT)Environment.Exit(1);
}

Se o serviço tiver um problema e precisar reiniciar, inicio um encadeamento que interrompe o serviço do SCM. Isso permite que o serviço seja limpo depois de si mesmo:

...

if(NeedToRestart)
{
    ABORT = true;
    new Thread(RestartThread).Start();
}

void RestartThread()
{
    ServiceController sc = new ServiceController(ServiceName);
    try
    {
        sc.Stop();
    }
    catch (Exception) { }
}

2

Eu usaria o Windows Scheduler para agendar uma reinicialização do seu serviço. O problema é que você não pode se reiniciar, mas pode se parar. (Você essencialmente cortou o galho em que está sentado ... se você entender minha analogia) Você precisa de um processo separado para fazer isso por você. O Agendador do Windows é apropriado. Agende uma tarefa única para reiniciar o serviço (mesmo de dentro do próprio serviço) para executar imediatamente.

Caso contrário, você precisará criar um processo de "pastoreamento" que faça isso por você.


2

A primeira resposta à pergunta é a solução mais simples: "Environment.Exit (1)" Estou usando isso no Windows Server 2008 R2 e funciona perfeitamente. O serviço para automaticamente, o O / S aguarda 1 minuto e o reinicia.


Quanto tempo espera depende de quanto tempo você o configura para esperar. O padrão é de 1 minuto, mas se alguém não quiser que sua resposta dê uma impressão errada.
Espen

1

Eu não acho que pode. Quando um serviço é "parado", ele é totalmente descarregado.

Bem, sempre há uma maneira de supor. Por exemplo, você pode criar um processo desanexado para interromper o serviço, reiniciá-lo e sair.


0

A melhor abordagem pode ser utilizar o Serviço NT como um invólucro para seu aplicativo. Quando o serviço NT é iniciado, seu aplicativo pode iniciar no modo "ocioso" aguardando o início do comando (ou ser configurado para iniciar automaticamente).

Pense em um carro, quando é iniciado, ele começa em um estado ocioso, aguardando o comando avançar ou reverter. Isso também permite outros benefícios, como melhor administração remota, como você pode escolher como expor seu aplicativo.


0

Apenas de passagem: e pensei em adicionar algumas informações extras ...

você também pode lançar uma exceção, isso fechará automaticamente o serviço Windows e as opções de reinicialização automática serão ativadas. o único problema é que, se você tiver um ambiente de desenvolvimento no seu PC, o JIT tentará ativar, e você receberá uma mensagem dizendo debug Y / N. diga não e depois ele será fechado e reinicie corretamente. (em um PC sem JIT, tudo funciona). a razão pela qual estou trollando, esse JIT é novo no Win 7 (costumava funcionar bem com o XP etc.) e estou tentando encontrar uma maneira de desabilitar o JIT .... eu posso tentar o método Environment.Exit mencionado aqui, veja como isso funciona também.

Kristian: Bristol, Reino Unido


3
Todas essas soluções de exceção, exit (1) evitam a limpeza adequada de um aplicativo. Un-graciosamente que sai é uma solução pobre
devshorts

0

Crie um arquivo restart.bat como este

@echo on
set once="C:\Program Files\MyService\once.bat"
set taskname=Restart_MyService
set service=MyService
echo rem %time% >%once%
echo net stop %service% >>%once%
echo net start %service% >>%once%
echo del %once% >>%once%

schtasks /create /ru "System" /tn %taskname% /tr '%once%' /sc onstart /F /V1 /Z
schtasks /run /tn %taskname%

Em seguida, exclua a tarefa% taskname% quando seu% service% iniciar


0

Crie um domínio de aplicativo separado para hospedar o código do aplicativo. Quando é necessário reiniciar, poderíamos descarregar e recarregar o domínio do aplicativo em vez do processo (serviço do Windows). É assim que o pool de aplicativos do IIS funciona, eles não executam o aplicativo asp.net diretamente, eles usam o domínio de aplicativo separado.


-2

A maneira mais fácil é ter um arquivo em lotes com:

net stop net start

e adicione o arquivo ao planejador com o intervalo de tempo desejado


Não está funcionando porque o lote está parado entre os dois comandos, porque é um processo filho do próprio serviço.
Orabit

-3
private static void  RestartService(string serviceName)
    {
        using (var controller = new ServiceController(serviceName))
        {
            controller.Stop();
            int counter = 0;
            while (controller.Status != ServiceControllerStatus.Stopped)
            {
                Thread.Sleep(100);
                controller.Refresh();
                counter++;
                if (counter > 1000)
                {
                    throw new System.TimeoutException(string.Format("Could not stop service: {0}", Constants.Series6Service.WindowsServiceName));
                }
            }

            controller.Start();
        }
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.