Start, Logon, Logoff und Shutdown

  • Hallo liebe Community,

    ich möchte für eine Vielzahl an Produktionsrechnern die o.g. Events loggen.

    Etwas ausführlicher: ich betreue Rund 500 Rechner und möchte aussagekräftig sein über die genannten Events.

    Wann startet ein Rechner, wer loggt sich wann ein (Benutzername + Timestamp), wann loggt sich der Benutzer aus und wann wird der Rechner runtergefahren bzw. neu gestartet.

    Ich habe schon sehr viel probiert und auch schon einige Ergebnisse erhalten.

    Aber leider nicht so zuverlässig wie ich es mir vorstelle.

    Dazu habe ich ein kleines Programm geschrieben, was auf die 4 Events hört und per Script angesprochen wird.

    Dann setzt das Programm einen eigenen Timestamp und schreibt die Daten temporär in eine log.txt

    Weiterhin wird geprüft ob der Rechner in der Lage ist Daten zu versenden (also ob er online ist) - ist dies der Fall, wird das Logfile zeilenweise ausgelesen und sendet die Daten per HTTP request an einen Host.

    Also, die Hürde mit dem Erfassen und Versenden der Daten ist schon mal geschafft.

    Es geht mir primär darum, zuverlässig die 4 Events zu erfassen.

    Derzeit läuft es so ab, dass ich 4 Scripte in folgende Ordner gelegt habe:

    1. C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup

    2. C:\Windows\System32\GroupPolicy\Machine\Scripts\Shutdown

    3. C:\Windows\System32\GroupPolicy\User\Scripts\Logon

    4. C:\Windows\System32\GroupPolicy\User\Scripts\Logoff

    Wenn ich es richtig verstanden habe, schaut das Betriebssystem bei eben diesen 4 Events ob etwas vorhanden ist und führt diese dann aus.

    Natürlich habe ich auch über gpedit.msc die Daten hinterlegt - in meinem Fall per Registryeintrag.

    ____________________

    Mein Problem ist aber, dass manche Rechner weder Start noch Shutdown erfassen und manche wiederum alle 4 Events sauber loggen. In einigen Fällen wird nur Logon erfasst. Bei manchen kommt auch einfach gar nichts an.

    Fehler im Programm würde ich fast gänzlich ausschließen, da ich die absolut sichere Variante gewählt habe:

    Erst alles lokal erfassen (log.txt) und später dann versenden (sobald online).

    Es gibt auch keine Rechteprobleme, da das Programm und die Skripte im Adminmodus ausgeführt werden.

    Habt ihr vielleicht eine Idee oder Ansätze, wie ich diese 4 Events sauberer und auch zuverlässiger loggen kann?

    Das Programm ist knapp 1MB groß und kann von mir aus ab Start permanent laufen und auch gern auf irgendwelche "Events" horchen und dann reagieren...aber dafür brauche ich eure Hilfe.

    Ich freue mich auf eure Beiträge.

    • Offizieller Beitrag

    Würde ich so machen:

    Bei LogOff/ShutDown/Restart:

    http://www.autoitscript.com/autoit3/docs/f…xitRegister.htm

    Bei Startup:

    In der Registry unter HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run führst du bei jedem Start ein Skript aus:

    - Auslesen Startzeit

    AutoIt
    Func _GetLastBootUp($_sComputer='.')
        Local $oWMIService = ObjGet("winmgmts:\\" & $_sComputer & "\root\cimv2")
        If Not IsObj($oWMIService) Then Return ''
        Local $oColOperatingSystems = $oWMIService.ExecQuery("Select LastBootUpTime from Win32_OperatingSystem")
        Local $sBootup
        For $oOS in $oColOperatingSystems
            $sBootup = $oOS.LastBootUpTime   ; --> 20180210112637.125599+060
        Next
        Return StringRegExpReplace($sBootup, '(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).+', '\1-\2-\3 \4:\5:\6')
    EndFunc

    - Auslesen CurrentUser @UserName

    -- beides Eintragen in deine Logdatei

    Die LogOn Info bekommst du doch am Besten serverseitig über AD-Funktionen ausgelesen.

  • Hey BugFix, vielen Dank für deine schnelle Antwort und auch schon ersten Lösungsansatz.

    Allerdings verstehe ich die Funktion "OnAutoItExitRegister" nicht so ganz.

    Wie habe ich den in welchem Kontext zu verwenden?

    Vielleicht nochmal kurz die Ist-Situation.

    Im Moment, läuft das Programm nicht dauerhaft im Hintergrund.

    Es wird durch die 4 Events per CommandLine aktiviert, schreibt in die Log.txt und schließt sich dann wieder.

    Mit der Funktion die du vorschlägst, müsste doch das Programm im Autostart verankert sein richtig?

    Finde die Idee schon sehr gut, würde aber gern vorher noch weiter sammeln bzw. darüber schreiben.

  • Allerdings verstehe ich die Funktion "OnAutoItExitRegister" nicht so ganz.


    Wie habe ich den in welchem Kontext zu verwenden?

    Wenn der User ausgeloggt wird oder das System heruntergefahren wird sendet Windows eine Nachricht an alle Prozesse, dass sie sich doch beenden sollen.

    Der Prozess hat die Möglichkeit noch offene Handles oder Dateien abzuspeichern, damit nichts verloren geht (z.B. offenes Word-Dokument, Windows fährt herunter aber hält an).

    Du registrierst für OnAutoItExit eine Funktion die ausgeführt wird wenn die Anwendung beendet wird (eben durch den LogOff oder Shutdown) und dort schreibst du in deine Datei, dass der User sich um @HOUR, @MIN, @SEC ausgeloggt hat etc. Dazu brauchst du keine externe Events sondern kannst alles sauber und kurz im Script halten.

  • Hey alpines,

    vielen Dank für die tolle Erklärung der Funktion, genau sowas brauche ich ;)

    Bedeutet das aber im gleichen Fall auch, dass das Programm beim Start bzw. Logon mit startet und quasi im Hintergrund aktiv bleibt?

  • Hier mal mein Quellcode dazu.

  • BugFix - bekomme dein Script irgendwie nicht zum Laufen - muss ich noch irgendwas mitgeben?

    • Offizieller Beitrag

    bekomme dein Script irgendwie nicht zum Laufen - muss ich noch irgendwas mitgeben?

    Bei Abfrage des lokalen PC ist der Name vorbelegt (.). Es reicht also _GetLastBootUp(). Nur für andere PC im Netzwerk ist der PC-Name mit zu übergeben.

    Auf meinem PC (Win7 Pro x64) funktioniert es, wie gewollt.

    Netzzugriff kann ich mangels anderer Netzwerk-PC nicht testen. Jedoch muss in der Windows Firewall WMI eingehend zugelassen werden.

    - abfragen: netsh advfirewall firewall show rule name="Windows-Verwaltungsinstrumentation (WMI eingehend)"

    - erlauben: netsh advfirewall firewall set rule group="Windows-Verwaltungsinstrumentation (WMI)" new enable=yes

  • Bei Abfrage des lokalen PC ist der Name vorbelegt (.). Es reicht also _GetLastBootUp(). Nur für andere PC im Netzwerk ist der PC-Name mit zu übergeben.

    Auf meinem PC (Win7 Pro x64) funktioniert es, wie gewollt.

    Netzzugriff kann ich mangels anderer Netzwerk-PC nicht testen. Jedoch muss in der Windows Firewall WMI eingehend zugelassen werden.

    - abfragen: netsh advfirewall firewall show rule name="Windows-Verwaltungsinstrumentation (WMI eingehend)"

    - erlauben: netsh advfirewall firewall set rule group="Windows-Verwaltungsinstrumentation (WMI)" new enable=yes

    Also ich habe die Funktion jetzt mal 1:1 eingesetzt und wollte sie für meinen Rechner testen.

    Habe mir dazu nur noch eine MsgBox erstellt...leider bekomme ich kein Ergebnis und keine Anzeige.

    Wo liegt mein Fehler?

  • Kann es sein, dass der WMI-Dienst bei dir nicht gestartet ist?

    Frag das mal ab mit: sc query winmgmt

    Wenn nicht aktiviert, starte mit: sc start winmgmt

    Der Dienst ist aktiv.


    Mach mal die beiden -1 in der MsgBox(64, "BootUp", _GetLastBootUp(), -1, -1) raus , so daß nur noch MsgBox(64, "BootUp", _GetLastBootUp()) da steht.

    :Face:es lag echt an den -1,-1 Werten :Face:

    So ein dummer Fehler meinerseits!

    Danke für die Hinweise und eure Geduld :)

    Ich bastel jetzt mal alles zusammen und melde mich dann nochmal mit neuen Erkenntnissen oder wahlweise auch noch Fragen;)

  • Sauber Leute!

    Jetzt bekomme ich erstmal eine ganze Liste an Ergebnissen.

    Nun will ich das ganze noch an den Server senden.

    Melde mich gern wieder, sobald ich das fertig habe.

    Hier der aktuelle Code (ohne Codeporn:P:(

    • Offizieller Beitrag

    Func _GetLastBootUp($_sComputer=$sComputer) ;"." steht für lokalen Rechner

    Das ist unklug, als Standardwert eine Variable zu verwenden. Wenn ein Parameter vorbelegt wird, verwendet man einen fixen Wert, der in der wahrscheinlich häufigsten Nutzung der Funktion zur Anwendung kommt. Dann spart man sich die Übergabe dieses Parameters.

    Also entweder mit Vorbelegung lokaler PC

    Func _GetLastBootUp($_sComputer='.')

    oder ohne Vorbelegung

    Func _GetLastBootUp($_sComputer)

  • HotKeySet: Bei globalen Hotkeys würde ich immer überprüfen, ob diese gesetzt werden konnten. Zudem muss der Funktionsname in ""!

    IsAdmin: Nur dann Ausgabe machen und Script beenden, wenn es nicht der Fall ist.

    getRow: FileClose muss vor dem Return ausgeführt werden!

    getRow/hasRows - sind überflüssig!

    structure: Das erste FileOpen $hFilehandle wird an keiner Stelle geschlossen!

    Es heißt nicht $sCount, $sLine... sondern $iCount, $iLine, denn dass $s steht für String, das $i für Int[eger].

    Du darfst die Modes für FileOpen nicht mit & verknüpfen, sondern mit + oder besser noch mit BitOr. "Haus" & "katze" = "Hauskatze", 1 + 2 = 3, BitOr(1, 2) = 3

    Falsch: Local $hFileAusgabe = FileOpen($sDateiAusgabe, $FO_UTF8 & $FO_APPEND)

    Richtig: Local $hFileAusgabe = FileOpen($sDateiAusgabe, $FO_UTF8 + $FO_APPEND)

    Besser: Local $hFileAusgabe = FileOpen($sDateiAusgabe, BitOr($FO_UTF8, $FO_APPEND))

    If $iPing Then ... ist falsch, denn die "roundtrip-time" kann sehr wohl auch Null sein, was in der Hilfe leider immer noch nicht richtig beschrieben ist!

    Richtig ist also...

    If @error Then

    $sPath wird an keiner Stelle im Script verwendet.

    In Zeile 59/64 verwendest du "Magic Numbers", in Zeile 7 aber Konstanten für MsgBox... du solltest dich für eine Form entscheiden.

  • Nein! Seit 3.3.10.2 (?) kann man Funktionen als Referenz übergeben und es ist auch möglich sie direkt zu übergeben.

    Stimmt... danke... habe es soeben getestet... das ist in der Hilfe zu HotKeySet aber nicht mal ansatzweise beschrieben.

    Du kannst sogar Funktionen aus Variablen callen!

    Ja, das wusste ich bereits... bis dato hatte ich aber angenommen, dass es zumindest bei HotKeySet nicht der Fall ist.