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ê.

TheSoftwareJedi
fonte
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()
Khalid Rahaman
fonte
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.

Greg Hewgill
fonte
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();
VladL
fonte
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.

Filip
fonte
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?

Brody
fonte
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.

tecnófilo
fonte
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) { }
}
user3235770
fonte
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ê.

dviljoen
fonte
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.

tangentMan
fonte
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.

TED
fonte
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.

plamb
fonte
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

Kristian
fonte
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

Erik Martino
fonte
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.

CreativeManix
fonte
-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


fonte
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();
        }
    }
Lee Smith
fonte