Sägen Ihres Windows-Dienstes - eine Anleitung für "nicht echte Programmierer"


Sobald Sie darüber nachgedacht haben, wie Sie ein Skript oder eine Anwendung in einen Windows-Dienst verwandeln können. Höchstwahrscheinlich wird die Aufgabe nicht so trivial sein - zumindest benötigt die Anwendung eine spezielle Schnittstelle, um Befehle vom System zu empfangen. Und da es Anforderungen und Einschränkungen gibt, dh Skripte und Krücken, die das Herz lieben, zu überwinden.


Der Artikel wird für diejenigen nützlich sein, die wie ich "kein echter Programmierer" sind.


Warum brauche ich einen Dienst, wenn geplante Aufgaben vorliegen?


Im Gegensatz zu den zugewiesenen Aufgaben wird der Dienst ständig ausgeführt, startet am Start des PCs und kann von Windows-Tools gesteuert werden. Außerdem benötigt ein regelmäßig gestartetes Skript möglicherweise Daten von einem früheren Start, und es kann nützlich sein, Daten von externen Quellen abzurufen - beispielsweise im Fall von TCP oder Webserver.


Persönlich musste ich in den letzten fünf Jahren dreieinhalb Mal einen Dienst erstellen:


  • Es war erforderlich, einen Dienst unter fail2ban für Windows 2003 zu erstellen, der mit den FileZilla- und Apache-Protokollen funktionierte. Wenn der Verdacht auf Brute Force bestand, wurde die IP-Adresse mithilfe der regulären Windows-Tools - ipsec - blockiert.
  • Ein Analogon des Telnet-Servers für Heimversionen von Windows. Es dauerte, bis Befehle auf Remote-Workstations ausgeführt wurden, auf denen Windows 7 Home ausgeführt wurde. In der Tat der zweite Versuch, Dienste zu spielen.
  • Musik-Player für den Handelssaal unter Windows. Die Aufgabe auf TK konnte mit Hilfe von mpd und einem Paket von Skripten gelöst werden, aber ich entschied mich - wenn wir Skripte erstellen sollten, warum nicht den Player selbst „stapeln“. Basierend auf der Bibliothek BASS.dll .
  • Bei der Auswahl eines Webservers mit Unterstützung für das Herunterladen von Dateien unter Windows war eine Option HFS . Er kann nicht alleine arbeiten, also musste er ihn in den Dienst „schieben“. Infolgedessen gefiel mir die Lösung nicht und ich installierte nur das Apaxy-Design auf dem Apache-Webserver.

Zum Erstellen des Dienstes können Sie Programmiersprachen für Erwachsene wie C verwenden. Wenn Sie jedoch nicht mit Visual Studio Kontakt aufnehmen möchten, verwenden Sie vorgefertigte Tools. Es gibt kostenpflichtige Lösungen wie FireDaemon Pro oder AlwaysUp , aber wir konzentrieren uns traditionell auf kostenlose.


Der erste Weg. Von Microsoft


Dieser bereits ältere Mechanismus besteht aus zwei Komponenten: dem Dienstprogramm instsrv.exe zum Installieren des Dienstes und srvany.exe , dem Prozess zum Starten aller ausführbaren Dateien. Angenommen, wir haben mit dem Polaris- Modul einen Webserver in PowerShell erstellt. Das Skript wird sehr einfach sein:


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


Die Arbeit des sogenannten "Servers".


Versuchen wir nun, das Skript in einen Dienst umzuwandeln. Laden Sie dazu die Windows Resource Kit-Tools herunter, in denen sich unsere Dienstprogramme befinden. Beginnen wir mit der Installation eines leeren Dienstes mit dem folgenden Befehl:


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

Wobei WebServ der Name unseres neuen Dienstes ist. Bei Bedarf können Sie über das Snap-In services.msc den Benutzer angeben, unter dem der Dienst gestartet werden soll, und die Interaktion mit dem Desktop zulassen.


Schreiben wir nun den Pfad zu unserem Skript mit Registry Magic. Dienstparameter befinden sich im Registrierungsschlüssel HKLM \ SYSTEM \ CurrentControlSet \ Services \ WebServ . Darin müssen wir einen neuen Abschnitt Parameter hinzufügen und dort einen Zeichenfolgenparameter Application erstellen, der den Pfad zur ausführbaren Datei angibt. Im Fall eines PowerShell-Skripts sieht es folgendermaßen aus:


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


Kundenspezifischer Service.


Sie können laufen und genießen.



Arbeitsservice.


Dieses Verfahren hat jedoch Nachteile:


  • Dienstprogramme sind alt und wurden vor der Erfindung von PowerShell, UAC und anderen Dingen entwickelt.
  • Srvany kontrolliert nicht den Betrieb der Anwendung. Selbst wenn ein Fehler auftritt, setzt der Dienst seine Arbeit fort, als wäre nichts passiert.
  • Muss angepasst werden und in die Registrierung eintauchen. Erinnern Sie sich, dass das Durchsuchen der Registrierung nicht sicher ist?

Daher wenden wir uns einer Methode zu, die teilweise ohne diese Probleme ist.


Der zweite Weg, fast ein Erwachsener


Es gibt ein Dienstprogramm namens NSSM - Non-Sucking Service Manager , das als nicht fehlerhafter Service Manager übersetzt werden kann . Im Gegensatz zum vorherigen wird es vom Entwickler unterstützt und der Quellcode wird auf der Site veröffentlicht. Zusätzlich zur üblichen Methode ist die Installation auch über den Chocolately- Paketmanager verfügbar.


Sie können einen Dienst über die übliche Befehlszeile erstellen, die mit der Dokumentation auf der Entwicklerseite ausgestattet ist. Wir werden jedoch PowerShell verwenden. Weil wir das natürlich können.


 $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 


Installation über PowerShell.


Zur Änderung überprüfen wir die Arbeit des Dienstes nicht mit dem Browser, sondern auch über PowerShell mit dem Befehl Invoke-RestMethod.



Und es funktioniert wirklich.


Im Gegensatz zu srvany können Sie mit dieser Methode die Anwendung beim Start neu starten, stdin und stdout umleiten und vieles mehr. Insbesondere wenn Sie keine Befehle in die Befehlszeile schreiben möchten, führen Sie einfach die GUI aus und geben Sie die erforderlichen Parameter über eine praktische Oberfläche ein.


Die GUI wird mit dem folgenden Befehl gestartet:


 nssm.exe install ServiceName 


Sie können sogar die Priorität und Verwendung von Prozessorkernen konfigurieren.


In der Tat gibt es viel mehr Möglichkeiten als srvany und eine Reihe anderer Analoga . Von den Minuspunkten fällt eine unzureichende Kontrolle über den gesamten Prozess auf.


Es fehlt an Zinn. Daher werde ich zur Hardcore-Methode aller getesteten übergehen.


Der dritte Weg. AutoIT


Da ich ein langjähriger Liebhaber dieser Skriptsprache bin, konnte ich nicht an der Bibliothek mit dem Namen _Services_UDF v4 vorbeikommen . Es ist mit umfangreichen Dokumentationen und Beispielen ausgestattet, daher werde ich unter dem Spoiler sofort den vollständigen Text des resultierenden Skripts angeben.


Skriptliste

Versuchen wir also, unseren Webdienst darin zu "verpacken":


 #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 

Ich werde den Moment, in dem die Anwendung startet, genauer analysieren. Es beginnt nach der Operation $ bServiceRunning = True und verwandelt sich in eine scheinbar endlose Schleife. Tatsächlich wird dieser Prozess unterbrochen, sobald der Dienst ein Abschlusssignal erhält - unabhängig davon, ob er sich abmeldet oder manuell stoppt.


Da das Programm für das Skript extern ist (Powershell.exe), müssen wir nach dem Beenden der Schleife die Arbeit mit ProcessClose beenden .


Dazu muss das Skript in die EXE-Datei kompiliert und anschließend der Dienst installiert werden, indem exe mit dem Schalter -i ausgeführt wird .



Es funktioniert!


Natürlich ist diese Methode nicht die bequemste, und alle zusätzlichen Funktionen müssen unabhängig voneinander implementiert werden, unabhängig davon, ob die Anwendung im Falle eines Fehlers oder einer Protokollrotation neu gestartet wird. Aber er gibt die volle Kontrolle darüber, was passiert. Und am Ende können Sie noch viel mehr tun - von der Benachrichtigung von Telegram über einen Dienstfehler bis zur IPC-Interaktion mit anderen Programmen. Und außerdem - in einer Skriptsprache, ohne Visual Studio zu installieren und zu lernen.


Sagen Sie uns, mussten Sie Skripte und Anwendungen in Dienste umwandeln?

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


All Articles