Serrar seu serviço Windows - um guia para "programadores não reais"


Depois de pensar em como transformar um script ou aplicativo em um serviço do Windows. Provavelmente, a tarefa não será tão trivial - pelo menos o aplicativo precisará de uma interface especial para receber comandos do sistema. E como existem requisitos e limitações, ou seja, scripts e muletas, adoráveis ​​para o coração, a serem superados.


O artigo será útil para quem, como eu, "não é um programador de verdade".


Por que preciso de um serviço se houver tarefas agendadas


Ao contrário das tarefas atribuídas, o serviço é executado constantemente, inicia no início do PC e pode ser controlado pelas ferramentas do Windows. Um script em execução regular também pode precisar de dados de uma execução anterior e pode ser útil obter dados de fontes externas - por exemplo, no caso de um servidor TCP ou Web.


Pessoalmente, nos últimos cinco anos, tive que criar um serviço três vezes e meia:


  • Foi necessário criar um serviço no fail2ban para o Windows 2003. que funcionasse com os logs do FileZilla e Apache e, se houvesse suspeita de força bruta, bloqueou o IP com as ferramentas padrão do Windows - ipsec.
  • Um análogo do servidor telnet para versões domésticas do Windows. Demorou para executar comandos em estações de trabalho remotas que estavam executando o Windows 7 Home. De fato, a segunda tentativa de prestar serviços.
  • Leitor de música para o pregão no Windows. A tarefa no TK poderia ser resolvida com a ajuda do mpd e de um pacote de scripts, mas eu decidi - se deveríamos criar scripts, por que não “empilhar” o jogador sozinho. Com base na biblioteca BASS.dll .
  • Ao escolher um servidor da Web com suporte para o download de arquivos no Windows, uma opção era o HFS . Ele não pode trabalhar por conta própria, então teve que "empurrá-lo" para o serviço. Como resultado, não gostei da solução e acabei de instalar o tema Apaxy no servidor da web Apache.

Para criar um serviço, você pode usar linguagens de programação para adultos como C. Mas se você não deseja se comunicar com o Visual Studio, use utilitários prontos. Existem soluções pagas, como FireDaemon Pro ou AlwaysUp , mas tradicionalmente focamos nas gratuitas.


O primeiro caminho. Da Microsoft


Esse mecanismo já antigo consiste em dois componentes: o utilitário instsrv.exe para instalar o serviço e srvany.exe , o processo para iniciar qualquer arquivo executável. Suponha que tenhamos criado um servidor Web no PowerShell usando o módulo Polaris . O script será extremamente simples:


New-PolarisGetRoute -Path '/helloworld' -Scriptblock { $Response.Send('Hello World!') } Start-Polaris -Port 8080 while($true) { Start-Sleep -Milliseconds 10 } 


O trabalho do chamado "servidor".


Agora vamos tentar transformar o script em um serviço. Para fazer isso, baixe as Ferramentas do Windows Resource Kit , onde estarão nossos utilitários. Vamos começar instalando um serviço vazio com o comando:


 instsrv WebServ C:\temp\rktools\srvany.exe 

Onde WebServ é o nome do nosso novo serviço. Se necessário, por meio do snap-in services.msc , você pode especificar o usuário sob o qual o serviço será iniciado e permitir a interação com a área de trabalho.


Agora vamos escrever o caminho para o nosso script usando a mágica do registro. Os parâmetros de serviço estão na chave do Registro HKLM \ SYSTEM \ CurrentControlSet \ Services \ WebServ . Nele, precisamos adicionar uma nova seção Parameters e criar lá um parâmetro de string Application , indicando nele o caminho para o arquivo executável. No caso de um script do PowerShell, ele terá a seguinte aparência:


 C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -NoProfile -File C:\temp\Polaris\server.ps1 


Serviço personalizado.


Você pode correr e se divertir.



Serviço de trabalho.


No entanto, este método tem desvantagens:


  • Os utilitários são antigos, desenvolvidos antes da invenção do PowerShell, UAC e outras coisas.
  • Srvany não controla a operação do aplicativo. Mesmo que caia em erro, o serviço continuará seu trabalho como se nada tivesse acontecido.
  • Terá que ajustar e aprofundar o registro. Você se lembra que cavar no registro não é seguro?

Portanto, nos voltamos para um método parcialmente desprovido desses problemas.


A segunda maneira, quase um adulto


Existe um utilitário chamado NSSM - Non-Sucking Service Manager , que pode ser traduzido como um gerenciador de serviços não ruim . Diferente do anterior, ele é suportado pelo desenvolvedor e o código fonte é publicado no site. Além do método usual, a instalação também está disponível através do gerenciador de pacotes Chocolately .


Você pode criar um serviço a partir da linha de comando usual, munida de documentação no site do desenvolvedor. Mas usaremos o PowerShell. Porque nós podemos, é claro.


 $nssm = (Get-Command ./nssm).Source $serviceName = 'WebServ' $powershell = (Get-Command powershell).Source $scriptPath = 'C:\temp\Polaris\server.ps1' $arguments = '-ExecutionPolicy Bypass -NoProfile -File "{0}"' -f $scriptPath & $nssm install $serviceName $powershell $arguments & $nssm status $serviceName Start-Service $serviceName Get-Service $serviceName 


Instalação através do PowerShell.


Para uma alteração, verificaremos o trabalho do serviço não com o navegador, mas também através do PowerShell usando o comando Invoke-RestMethod.



E isso realmente funciona.


Ao contrário do srvany , esse método permite reiniciar o aplicativo na inicialização, redirecionar stdin e stdout e muito mais. Em particular, se você não deseja gravar comandos na linha de comando, basta executar a GUI e inserir os parâmetros necessários por meio de uma interface conveniente.


A GUI é iniciada com o comando:


 nssm.exe install ServiceName 


Você pode até configurar a prioridade e o uso dos núcleos do processador.


De fato, há muito mais oportunidades do que o srvany e vários outros análogos . Das desvantagens, o controle insuficiente sobre todo o processo é impressionante.


Há falta de estanho. Portanto, passarei ao método mais incondicional de todos os testados.


O terceiro caminho. AutoIT


Como sou amante de longa data dessa linguagem de script, não consegui superar a biblioteca chamada _Services_UDF v4 . Ele é equipado com documentação e exemplos sofisticados; portanto, no spoiler, darei imediatamente o texto completo do script resultante.


Listagem de scripts

Então, vamos tentar "agrupar" nosso serviço da Web nele:


 #NoTrayIcon #RequireAdmin #Region #AutoIt3Wrapper_Version=Beta #AutoIt3Wrapper_UseUpx=n #AutoIt3Wrapper_Compile_Both=y #AutoIt3Wrapper_UseX64=y #EndRegion Dim $MainLog = @ScriptDir & "\test_service.log" #include <services.au3> #include <WindowsConstants.au3> $sServiceName="WebServ" If $cmdline[0] > 0 Then Switch $cmdline[1] Case "install", "-i", "/i" InstallService() Case "remove", "-u", "/u", "uninstall" RemoveService() Case Else ConsoleWrite(" - - - Help - - - " & @CRLF) ConsoleWrite("params : " & @CRLF) ConsoleWrite(" -i : install service" & @CRLF) ConsoleWrite(" -u : remove service" & @CRLF) ConsoleWrite(" - - - - - - - - " & @CRLF) Exit EndSwitch Else _Service_init($sServiceName) Exit EndIf Func _main($iArg, $sArgs) If Not _Service_ReportStatus($SERVICE_RUNNING, $NO_ERROR, 0) Then _Service_ReportStatus($SERVICE_STOPPED, _WinAPI_GetLastError(), 0) Exit EndIf $bServiceRunning = True $PID=Run("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -NoProfile -File C:\temp\Polaris\server.ps1") While $bServiceRunning _sleep(1000) WEnd ProcessClose($PID) _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, 1000) DllCallbackFree($tServiceMain) DllCallbackFree($tServiceCtrl) _Service_ReportStatus($SERVICE_STOPPED, $NO_ERROR, 0) DllClose($hAdvapi32_DLL) DllClose($hKernel32_DLL) EndFunc Func _Sleep($delay) Local $result = DllCall($hKernel32_DLL, "none", "Sleep", "dword", $delay) EndFunc Func InstallService() #RequireAdmin Local $bDebug = True If $cmdline[0] > 1 Then $sServiceName = $cmdline[2] EndIf If $bDebug Then ConsoleWrite("InstallService("&$sServiceName &"): Installing service, please wait") _Service_Create($sServiceName, $sServiceName, $SERVICE_WIN32_OWN_PROCESS, $SERVICE_AUTO_START, $SERVICE_ERROR_SEVERE, '"' & @ScriptFullPath & '"');,"",False,"","NT AUTHORITY\NetworkService") If @error Then Msgbox("","","InstallService(): Problem installing service, Error number is " & @error & @CRLF & " message : " & _WinAPI_GetLastErrorMessage()) Else If $bDebug Then ConsoleWrite("InstallService(): Installation of service successful") EndIf Exit EndFunc Func RemoveService() _Service_Stop($sServiceName) _Service_Delete($sServiceName) If Not @error Then EndIf Exit EndFunc Func _exit() _Service_ReportStatus($SERVICE_STOPPED, $NO_ERROR, 0); EndFunc Func StopTimer() _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, $iServiceCounter) $iServiceCounter += -100 EndFunc Func _Stopping() _Service_ReportStatus($SERVICE_STOP_PENDING, $NO_ERROR, 3000) EndFunc 

Analisarei com mais detalhes o momento em que o aplicativo é iniciado. Inicia após a operação $ bServiceRunning = True e se transforma em um loop aparentemente infinito. De fato, esse processo será interrompido assim que o serviço receber um sinal de conclusão - seja efetuando logout ou parando manualmente.


Como o programa para o script é externo (powershell.exe), depois de sair do loop, precisamos concluir seu trabalho usando ProcessClose .


Para fazer isso, o script deve ser compilado em .exe e, em seguida, instale o serviço executando exe com a opção -i.



Isso funciona!


Obviamente, esse método não é o mais conveniente e todos os recursos adicionais terão que ser implementados independentemente, seja ele reiniciando o aplicativo no caso de uma falha ou rotação de log. Mas ele dá controle total sobre o que está acontecendo. E, no final, você pode fazer muito mais, desde notificar o Telegram sobre uma falha no serviço até a interação do IPC com outros programas. Além disso - em uma linguagem de script, sem instalar e aprender o Visual Studio.


Diga-nos, você teve que transformar scripts e aplicativos em serviços?

Source: https://habr.com/ru/post/pt421019/


All Articles