Dienstag, 31. Juli 2012

VBScript: Dienst unter Windows starten/stoppen/neustarten

Das (per VB Skript veranlasste) Beenden des Media Centers beim Standby und das Neustarten nach dem Aufwecken haben sich bislang mit relativ großer Stabilität ond hohem "WAF" bezahlt gemacht.

Nach dem Update auf das aktuelle DVBLink Release 4.5 hatte ich jedoch häufiger mit ominösen Fehlermeldungen beim Live-TV Betrieb zu kämpfen, die sich meist erst duch Neustart des "dvblink_server" Dienstes beheben ließen.

Ich habe daher folgende Routine in VBScript geschrieben, um dies automatisert nach dem Aufwecken des Systems und vor dem Start des Media Centers ablaufen zu lassen:

Sub ServiceControl(sname, purpose, count)
    'sname is the service name
    'purpose is whether or not you want to start, stop or restart a service
    'count is the instance counter of the sub routine. It is increased with
    ' every recursive call from within in the sub routine for the same value
    ' of sname and should be 0 when called from the main program.
    ' When purpose is "stop" and count has reached the level of cntMaxRetries,
    ' the service is terminated/killed.
   
    Dim strComputer : strComputer = "."
    Dim objWMIService
    Dim strQuery
    Dim objMainService, objSubService, objProcess
    Dim colProcesses,colServices
    Dim delay : delay = 1
    Dim errReturn
   
    Const errAccessDenied  =  2
    Const cntMaxRetries    = 10
   
    Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
   
    Set objMainService = objWMIService.Get("Win32_Service.Name='" & sname & "'")
    strQuery = "Associators of " _
      & "{Win32_Service.Name='" & objMainService.name & "'} Where " _
      & "AssocClass=Win32_DependentService " & _
      "Role=Antecedent"
    Set colServices = objWMIService.ExecQuery(strQuery)
   
    Select Case purpose
       
        Case "stop", "restart"
       
        For Each objSubService In colServices
            ServiceControl objSubService.name, "stop", count
        Next
       
        If objMainService.state = "Running" Then
            errReturn = objMainService.StopService()    
            If errReturn = errAccessDenied Then
                'You do not have sufficient privileges to start/stop services. Abort. 
                WScript.Quit
            End If
        End If
       
        If Not objMainService.state = "Stopped" And count < cntMaxRetries Then
            WScript.Sleep delay*1000
            ServiceControl sname, "stop", count+1
        Else
            If objMainService.started Then
                Set colProcesses = objWMIService.ExecQuery("Select * from Win32_Process")
                For Each objProcess In colProcesses
                    If objProcess.ProcessID = objMainService.ProcessID Then
                        objProcess.terminate()
                    End If
                Next
                Set colProcesses = Nothing
                WScript.Sleep delay*1000
            End If
           
            If objMainService.started Then
                wshShell.Run "taskkill /f /pid " & objMainService.ProcessiD & " /t", 0, True
            End If
        End If
       
        If purpose = "restart" Then
            ServiceControl sname, "start", count
        End If
       
        Case "start"    
       
        If objMainService.state = "Stopped" Then
            errReturn = objMainService.StartService()
            If errReturn = errAccessDenied Then
                'You do not have sufficient privileges to start/stop services. Abort.
                WScript.Quit
            End If
        End If
       
        If Not objMainService.state = "Running" Then
            WScript.Sleep delay*1000
            ServiceControl sname," start", count +1
        End If
       
        For Each objSubService In colServices
            ServiceControl objSubService.name, "start", count
        Next
       
    End Select
   
    Set colServices = Nothing
   
End Sub 'ServiceControl       
Mittels der Routine "ServiceControl" kann ein Dienst, dessen Name bekannt ist, gestoppt, gestartet oder neugestartet werden.  Die Namen der Dienste lassen sich z.B. mit dem Befehl "sc query | find 'NAME'" per cmd ermitteln (Wichtig ist der Inhalt von SERVICE_NAME, nicht DISPLAY_NAME).

Die Routine arbeitet rekursiv und zählt dabei die Anzahl der Durchläufe. Wird eine bestimmte Anzahl an Rekursionsdurchläufen erreicht, wird versucht, den Dienst auf "brachialere" Art, d.h. mittels "Prozess.terminate()"oder "taskkill /F" zu beenden, Die Dauer der Verzögerungen zwischen den Durchläufen ist über den Wert "delay" in Sekunden einstellbar.

Die Routine läßt sich leicht in das vorhandene Skript einbinden, ist aber evtl. auch für andere Zwecke zu gebrauchen. Leider ist bei Windows 7 die Benutzerkontensteuerung (UAC) im Weg und verhindert, dass ein VB Skript ohne administrative Rechte einen Dienst starten oder stoppen kann. Ich habe auch noch keinen Weg gefunden, ein Skript automatisiert mit admin-Rechten zu starten, weshalb ich empfehle, die Benutzerkontensteuerung über "msconfig" (unter "Tools") komplett zu deaktivieren. 

Viel Spaß beim Skripten...