EventType clr20r3

Der Titel dieses Beitrages sollte eigentlich besser lauten:

EventType clr20r3 – oder starte niemals einen Windows Dienst ohne eine globale Fehlerbehandlung.

Ich hatte einen neuen Windows Dienst geschrieben, der wie schon so viele Dienste vorher die Aufgabe zur Systemüberwachung auf eingehende Interfacedateien per FTP durchführen sollte.

Der Dienst bezieht aus einer SQL Datenbank die Informationen für welche Mandanten dieser Dienst zu verrichten ist und überwacht dann die ebenfalls in der SQL angegebenen FTP Verzeichnisse auf eingehende neue Daten. Sind neue Daten vorhanden lädt der Dienst diese Dateien herunter, verarbeitet diese und löscht dann wenn alles erfolgreich war die Daten auf dem FTP Server.

So ungefähr in dieser Art habe ich schon unzählige Dienste erstellt. Auch dieser Dienst, der in der Entwicklungsumgebung einwandfrei funktioniert, macht nach der Installation auf dem W2K3 Server des Kunden Probleme.

Ich starte den Dienst, es dauert ein paar Sekunden und es kommt die Meldung, dass der Dienst nicht gestartet werden konnte.

image

Also schaue ich im Ereignisprotokoll nach und stoße auf eben diese Meldung :

image Ich habe eine ähnliche Fehlermeldung schon öfter gehabt, und die hat eigentlich immer auf falschen Einstellungen in der ´Konfigurationsdatei hingedeutet.

Aber warm muss ich eigentlich im trüben Wasser fischen, wieso gibt mir das System keine ordentliche Fehlermeldung geben. Da kan doch nur Bill Gates (Ich meine Microsoft) dran schuld sein, haben wohl wieder mal vergessen ordentliche Fehlermeldungen einzubauen.
Andere werden doch das gleiche Problem haben, was machen die denn gegen eine solche Abstrakte Fehlermeldung, was fängt man damit an.

Also einfach mal nach EventType clr20r3 gegoogelt (oh man was für ein Wort smile_speedy). Die Suchergebnisse sind weniger als zufriedenstellend. Das einzige was sicher zu sein scheint, der Fehler tritt wohl immer mit Windows Diensten auf, nicht auf allen Maschinen (was sich später als Logisch herausstellt) und er tritt auf Systemen mit dem NET Framework 2 auf.

Das alles ist und war mir einfach zu wenig und da ich bereits einige male über einen Fehler dieser Art gestolpert war, habe ich mir dieses mal die Zeit genommen, dass ganze mal etwas mehr Global zu betrachten und nicht nur den speziellen Fehler in diesem Fall im trüben Wasser zu suchen (und hoffentlich auch zu finden).

Ich möchte auch jetzt nicht den ganzen Weg beschreiben wie ich hier vorgegangen bin und was ich alles getestet habe, da man ja einen Service der im Start nicht funktioniert nicht einfach so Debuggen kann.

Die Essenz meiner Untersuchungen haben folgendes Ergeben:

Diese Blöde Fehlermeldung wird vom NET Framework ausgegeben wenn keine ordentlich Exception innerhalb des beim Start ausgeführten Codes enthalten ist. Dabei kann der Fehler eine Fehlende Datei, die falsche oder fehlende Konfiguration, ein Problem mit einem verwendet Port oder wie es in meinem Fall war, einfach das nicht Vorhandensein einer Tabelle in der DB sein.

Ja ich hatte einfach vergessen, nach der Installation des Dienstes auch das SQL Skript zur Erstellung des Tabellen und Gespeicherten Prozeduren auszuführen.

Und was macht man, damit man nicht mehr diese nichtssagende Fehlermeldung bekommt, sondern etwas mit dem man dann auch etwas anfangen kann:

Lösung:

Man muss in der ersten, was auch immer, die durch die OnStart() Methode aufgerufen wird unbedingt darauf achten dass man dort eine try – catch Anweisung einbaut, die dann eine ordentlich Fehlermeldung erzeugt und am besten auch in das Ereignisprotokoll schreibt.

Windows Programme automatisch starten

Während der Umsetzung eines Projektes bin ich damit konfrontiert worden, dass es notwendig war ein kleines Windows Programm, dass vom Hersteller der Hardware (Barcode Scanner Terminals) zur Verfügung gestellt wurde, auf einem Rechner permanent laufen zu lassen.

Da es sich um ein Windows Programm und nicht um einen Windows Service handelte smile_sad musste ich das Programm in der Autostart eines Benutzers unterbringen.

Das hat aber dazu geführt, dass:

  • Immer ein Benutzer angemeldet sein musste (was in diesem Fall nicht so schlimm war)
  • Das es möglich war das Programm einfach zu beenden (das ist nicht so klasse, da das Programm ständig laufen musste im die Scanner Terminals abzufragen)´

Die Möglichkeiten die ich hatte waren nun folgende:

  • Einen eigenen Windows Dienst zu schreiben der die Scanner Terminals abfragt (was in Ermangelung fehlender Bibliotheken (API) für die Scanner Terminals sehr aufwendig geworden wäre.
  • Eine Möglichkeit zu schaffen, dass der Aufruf des Programms automatisch geschieht und die Ausführung des Programms überwacht wird und sollte das Programm beendet worden sein, das Programm wieder startet.

Nun bin ich keine 20 mehr und habe gelernt, dass man nicht immer alles neu erfinden muss, und habe bevor ich an die Planung und Entwicklung  eines entsprechenden Programmes (Windows Dienst) gegangen bin, erst einmal ausführlich im Internet nach einer fertigen Lösung gesucht.

Und bin auch fündig geworden. Und nicht als teures Tool irgend eines Herstellers, von dem ich nicht weiß ob es in Übermorgen noch gibt, sondern als Projekt auf www.codeproject.com (Also mit dem Quellcode)

Das Projekt hat den Namen ist in C++ geschrieben und einfach genial.

Man kann diesen Service beliebig oft auf einem Windows Rechner installieren und pro Service jeweils bis zu 127 Windows Programme starten und überwachen.

Und wer es ganz komfortabel möchte, der kann die ebenfalls verfügbare für das Programm verwenden.

Und um es nicht zu vergessen, bedanken können wir uns für dieses kleine Tool bei Xiangyang Liu

Windows Dienste – Alternative Installations Möglichkeiten

Beim durchstöbern des Web bin ich auf den Beitrag „Install a Windows Service in a smart way instead of using Windows Installer MSI package“ gestoßen. Dieser Beitrag beschreibt einen alternativen Weg wie man Windows Dienste ohne einen eigens dafür erstellte Setup Routine installieren kann.


Der Artikel ist in Englisch und wurde auf http://www.codeproject.com/ veröffentlicht.

Windows Service – OnStart mal etwas anders !

Nachdem ich in einem Projekt mit dem Problem kämpfen musste, das einer der für dieses Projekt entwickelten Windows Dienste durch eine aufwendige Initialisierung bis zu einer Minute in der OnStart Methode verweilt hat bis er endlich den Dienst als gestartet anzeigt habe ich mir überlegt wie man diese Problem anders lösen könnte.

Eigentlich war das auch kein wirklich großes Problem, aber da ich auch erst einmal gezielt darüber Nachdenken musste um auf die Lösung zu kommen, denke ich dass vielleicht auch andere erst einmal auf das Problem aufmerksam werden müssen um dann einen anderen Ansatz zum Start der Windows Dienste zu verwenden.

Im Nachfolgenden Beschreibe ich eine einfach Lösung wie man einen Windows Dienst mit Hilfe eines Timers dazu bringt, sofort zu starten, unabhängig davon, ob aufwendige Initialisierungsprozesse für den Dienst durchgeführt werden müssen oder nicht.

Schauen wir und doch einfach mal wie der normale Startvorgang eines Dienstes aussieht:

Hier ein Beispiel wie es normalerweise aussehen kann, wobei osc irgendeine Klasse ist deren Methoden nach deren Initialisierung vom Service verwendet werden:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Diagnostics;

using System.ServiceProcess;

using System.Text;

using System.Timers; 
 


namespace Namespace.Service

{

      publicpartialclassMeinService : ServiceBase

      {

            MeineKlasse osc;

            public MeinService()

            {

                  InitializeComponent();

                  Osc = new Osc();

            } 


            protectedoverridevoid OnStart(string[] args)

            {

                  Osc.Init();

                  Osc.StartAll();

            } 


            protectedoverridevoid OnStop()

            {

                  osc.StopAll();

            } 


            protectedoverridevoid OnContinue()

            {

                  osc.StartAll();

            } 


            protectedoverridevoid OnPause()

            {

                  osc.StopAll();

            } 


            protectedoverridevoid OnShutdown()

            {

                  osc.StopAll();

                  osc.Dispose();

                  osc = null; 


            }

      } 

Wenn nun davon ausgehen dass Osc.Init() und Osc.StartAll() Methoden sind die lange dauern können, dann befindet sich dieser Dienst in der Gesamt Zeit in welcher diese beiden Methoden ausgeführt werden im Dienst- Status StartPending. Dies kann in bestimmten Situationen aber mehr als unerwünscht sein. Und für diesen Fall habe ich folgende Änderungen vorgenommen, die dazu führen, dass der Service sofort nach dem Start wirklich in den Status Running übergeht.

Nachfolgend nun die Lösung für das Problem:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Diagnostics;

using System.ServiceProcess;

using System.Text;

using System.Timers ; 
 


namespace Namespace.Service

{

      publicpartialclassMeinService : ServiceBase

      {

            Timer startTimer = newTimer();

            MeineKlasse osc;

            public MeinService()

            {

                  InitializeComponent();

                  startTimer.Interval = 1000;        // 1 seconds 

                  startTimer.Elapsed += newElapsedEventHandler(startTimer_Elapsed);

                  startTimer.Enabled = false;

            } 


            void startTimer_Elapsed(object sender, ElapsedEventArgs e)

            {

                  startTimer.Enabled = false;

                  osc = MeineKlasse.loadActiveSystems();

                  osc.StartAll();

            } 


            protectedoverridevoid OnStart(string[] args)

            {

                  startTimer.Enabled = true;

            } 


            protectedoverridevoid OnStop()

            {

                  osc.StopAll();

            } 


            protectedoverridevoid OnContinue()

            {

                  osc.StartAll();

            } 


            protectedoverridevoid OnPause()

            {

                  osc.StopAll();

            } 


            protectedoverridevoid OnShutdown()

            {

                  osc.StopAll();

                  osc.Dispose();

                  osc = null; 


            }

      }

} 

Ich habe die Änderungen in Fettschrift dargestellt damit man diese gleich auf Anhieb erkennen kann.
Und die Erklärung ist eigentlich auch selbstredend.
Ich verwende also einen Timer, der vom OnStart Event eine Sekunde nach dem der Service gestartet wurde die eigentlichen Funktionen des Service aktiviert.

Manchmal ist einfach, einfach Einfach !