Announcement

Collapse
No announcement yet.

Fehler bei Start NT-Dienst

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Fehler bei Start NT-Dienst

    Ich habe einen eigenen NT-Service geschrieben. Dieser benutzt sowohl COM/DCOM als auch Datenbankkomponenten (Eine TDatabase und ein TTable Komponente). Starte ich den Service über Systemsteuerungs-Applet Dienste, so tritt kein Problem auf. Laß ich den Service automatisch bei Systemhochlauf starten, so bekomme ich folgende Fehlermeldung:

    <b>Der Dienst antwortet nicht rechtzeitig auf die Start oder Steuerungsanforderung</b>

    Anschließend muß ich mich Anmelden und den Dienst per Hand starten.

    Stell ich den Startmodus des Dienstes auf Manuell, so startet der erste Client, welcher den COM-Server in meinem NT-Service anspricht auch den NT-Dienst. Der NT-Dienst läuft auch nachdem alle Clients wieder beendet sind.

    Außerdem werden die Log-Einträge, welche ich mittels <b>LogMessage(...)</b> absetze nicht ins Systemprotokoll aufgenommen.

    <b>Umgebung:</b><br>
    Delphi 4 mit Update Pack #3
    Windows NT Server mit SP #4

  • #2
    Hallo,

    die Fehlermeldung <i>"Der Dienst antwortet nicht rechtzeitig auf die Start oder Steuerungsanforderung"</i> deutet auch zwei potentielle Fehlerursachen hin:<br>
    1. Ein Timing-Problem, der eigene Service antwortet nicht schnell genug, oder<br>
    2. Ein Ressourcen-Problem, die ansprochene Datenbank (SQL-Server ?) ist noch nicht ready.<br>
    Wenn beim manuellen Start bzw. beim späteren automatischen Start durch den ersten Client der Service ohne Fehlermeldung hochfährt, ist die zweite Fehlerursache wahrscheinlicher.

    Wenn ein NT-Service gestartet wird, arbeitet NT die folgenden Schritte ab:<br>
    1. Der Service ruft StartServiceCtrlDispatcher auf <br>
    2. Der <b>SCM</b> (Service Control Manager) ruft ServiceMain im Service auf<br>
    3. Der Service ruft innerhalb von ServiceMain <b>RegisterServiceCtrlHandler</b> auf und meldet somit seinen Handler an. Danach wird die eigentliche Service-Funktion gestartet. Der Handler muß jedoch auf Aufrufe des SCM unmittelbar reagieren können.<br>
    4. Wenn der SCM einen Service stoppen will, ruft er den Handler im Service auf. Dieser reagiert mit dem Aufruf von PostStopMsg (a la WM_QUIT), um den laufenden Service zu stoppen.

    Zur Fehlereingrenzung würde ich folgendes machen: Falls ein SQL-Server (der auch als NT-Service läuft) verwendet wird, würde ich diesen vor dem eigenen Service über einen dritten Service starten. In diesem Beispiel werden auch Infos in die NT-Ereignisliste geschrieben:

    <pre>
    procedure TM_START.M_STARTStart(Sender: TService; var Started: Boolean);
    resourcestring
    cTry = 'M_START: Versuche die Dienste zu starten';
    var
    tel: TEventLogger;
    sServiceName : String;
    begin
    tel := TEventLogger.Create('MStart');
    try
    tel.LogMessage(cTry, EVENTLOG_INFORMATION_TYPE);
    finally
    tel.Free;
    end;
    Started := True;
    end;

    procedure TM_START.M_STARTExecute(Sender: TService);
    resourcestring
    cTry = 'M_START.Execute: Versuche die Dienste zu starten.';
    cMsg = 'M_START: Dienst »%s« konnte erfolgreich gestartet werden.';
    cErr = 'M_START: Dienst »%s« konnte nicht gestartet werden.';
    var
    tel: TEventLogger;
    sServiceName : String;
    begin
    tel := TEventLogger.Create('MStart');
    try
    tel.LogMessage(cTry, EVENTLOG_INFORMATION_TYPE);
    // 1. Service starten
    sServiceName := 'M_TEST';
    if StartMSTRService(sServiceName) then
    tel.LogMessage(Format(cMsg, [sServiceName]), EVENTLOG_INFORMATION_TYPE)
    else
    tel.LogMessage(Format(cErr, [sServiceName]), EVENTLOG_INFORMATION_TYPE);
    // Wartepause (ja nach Zeitbedarf des ersten Services definieren)
    Sleep(500);
    // 2. Service Starten
    sServiceName := 'M_TEST2';
    if StartMSTRService(sServiceName) then
    tel.LogMessage(Format(cMsg, [sServiceName]), EVENTLOG_INFORMATION_TYPE)
    else
    tel.LogMessage(Format(cErr, [sServiceName]), EVENTLOG_INFORMATION_TYPE);
    finally
    tel.Free;
    end;
    end;

    {---------------------------------------------------------------}
    { Private Methode startet einen NT-Service }
    {---------------------------------------------------------------}

    function TM_START.StartMSTRService(SrvName: String): Boolean;
    var
    mgr: THandle;
    svc: THandle;
    status: TServiceStatus;
    p: PChar;
    s_name: String;
    begin
    Result := False;
    mgr := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
    if mgr = 0 then
    raise Exception.Create('Service-Manager nicht erreicht!');
    svc := OpenService(mgr, PChar(SrvName), SERVICE_ALL_ACCESS);
    if svc = 0 then
    raise Exception.Create(Format('Service %s nicht gefunden!',[SrvName]));
    QueryServiceStatus(svc, status);
    // nur starten, wenn der Service nicht bereits läuft
    if status.dwCurrentState <> SERVICE_RUNNING then
    begin
    p := nil;
    StartService(svc, 0, p);
    end;
    CloseServiceHandle(svc);
    CloseServiceHandle(mgr);
    Result := True;
    end;
    </pre&gt

    Comment


    • #3
      Danke erstmal für die Antwort!<br>
      Aber ich komm nicht weiter!

      Dein Vorschlag über die beiden potentiellen Fehlerquellen können es aus folgenden Grund eigentlich nicht sein:

      zu 1. Beim Starten des Service wird nicht viel gemacht. Der Code ist folgender:

      procedure TfrmDBLockSrv.frmDBLockSrvStart(Sender: TService; var Started: Boolean);<br>
      begin<br>
      FEventLogger := TEventLogger.Create('frmDBLockSrv');<br>
      try<br>
      Started := TRUE;<br>
      Listener := TList.Create();<br>
      FEventLogger.LogMessage('DBLockserver successfull started', EVENTLOG_INFORMATION_TYPE);<br>
      except<br>
      on e: Exception do<br>
      begin<br>
      FEventLogger.LogMessage(e.Message, EVENTLOG_ERROR_TYPE);<br>
      Started := FALSE;<br>
      end;<br>
      end;<br>
      end;<br>

      Die Datenbankverbindung wird erst beim ersten Zugriff auf den COM-Server hergestellt.

      zu 2. Der Datenbankserver läuft auf einen anderen Rechner und ist damit auf jeden Fall verfügbar.

      Das Problem der <b>LogMessage(...)</b> besteht immer noch. Evtl liegt es an den eingestellten Properties meines NT-Service:

      AllowPause: TRUE<br>
      AllowStop: TRUE<br>
      Dependencies: keine<br>
      DisplayName: DBLockService<br>
      ErrorSeverity: esNormal<br>
      Interactive: FALSE<br>
      LoadGroup: <leer><br>
      Name: frmDBLockSrv (Überbleibsel aus nicht NT-Service-Zeit)<br>
      OldCreateOrder: False
      Password <leer><br>
      ServiceStartName <leer><br>
      ServiceType: stWin32<br>
      StartType: stManual (Beim Startproblem stand es auf stAuto)<br>
      Tag: 0<br>
      TagID: 0<br>
      WaitHint: 5000<br>

      Bei den Eventhandlern bediene ich nur folgende Ereignisse:<br>
      OnShotdown<br>
      OnStart<br>
      OnStop<br>

      Der Name meines COM-Servers ist DBLockSrv!

      Noch ne neue Frage:
      Wie kann ich den NT-Server in Bezug auf den Service "clean" machen. Nach einer Deinstallation mittels /UNINSTALL sind einige Einträge noch in der Registry vorhanden (HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\...), welche nicht löschbar sind

      Comment


      • #4
        Hallo Bernhard,

        sind die über <b>TEventLogger</b> abgeschickten Meldungen tatsächlich nicht unter <i>Ereignisanzeige: Protokoll Anwendung</i> zu lesen? Wenn ja, scheint in der Zwischenzeit einiges bei der Rechtevergabe auf dem NT-Rechner durcheinandergeraten sein.

        Ändert sich etwas, wenn die Checkbox <i>Interaktive Beziehung mit Desktop erlauben</i> aktiviert wird?

        Aber auch dann, wenn die Fehlermeldung beim Hochfahren des Systems erscheint, startet der erste Client beim Zugriff auf den COM-Server den Dienst (ohne das inzwischen ein Interaktiver Benutzer am Rechner angemeldet war)?

        Falls das Win32-SDK zur Verfügung steht, kann dort das <b>Service Control utility (SC.EXE)</b> zum Funktionstest des Services verwendet werden. Über <i>SC DELETE DBLockSrv</i> werden die Registry-Einträge entfernt (ich habe aber noch nicht nachgeprüft, ob noch Reste übrig bleiben)

        Comment


        • #5
          Danke Andreas,<br>
          das mit dem Eventlogging war mein Fehler.<br>
          Hab nicht gewußt, daß es eine Anwendungs-Protokoll gibt. Nachdem ich auf Protokoll Anwendung umgeschaltet habe, hab ich alle Events gesehen!

          Dort steht aber noch: <br>
          <b>Die Beschreibung der Ereignis-ID ( 0 ) in Quelle (<Mein App-Name>) konnte nicht gefunden werden. Sie enthält folgende Einfügezeichenkette(n): <Mein Text><br></b>
          Was bedeutet das bzw. wie kann man diesen "erweiterten" Text umgehen

          Comment


          • #6
            Hallo Berhard,

            diese <i>Fehlermeldung</i> ist normal. Normalerweise gibt es im Win32-SDK einen speziellen Compiler für Event-DLLs, in denen die Meldungstexte als spezielle Ressourcen abgelegt werden. Die Ereignisanzeige muß dann nur die Fehlernummer (ohne Text) speichern, so daß der Speicherplatzbedarf minimiert wird. Da der spezielle Compiler unter Delphi nicht zur Verfügung steht und auch der Aufwand für die DLL-Registrierung vermieden werden sollte, hat Borland entschieden, die <i>Minimalversion</i> für Eventlog-Einträge zu nutzen. Die Minimalversion besteht darin, keine Event-ID zu verwenden und statt dessen den Hinweistext im erweiterten Datenblock zu übergeben

            Comment


            • #7
              Hallo Andreas,

              nach langer Zeit komm ich mal wieder dazu, zu Versuchen das Problem des nicht automatisch startenden Dienstes zu lösen (siehe erster Eintrag in Diskussion).

              Ich habe jetzt verschiedene Tests gemacht und habe folgende Erfahrungen gesammelt (Durch Debug-Ausgaben in Text-Datei).

              - Der Dienst-Applikation kann nicht Initialisiert werden. Der Aufruf Application.Initialize führt zu einer EOleSysError-Fehlermeldung mit dem Text "Der RPC-Server ist nicht verfügbar"

              - Ein Start des Services 'RpcSs' mittels deiner Methode, StartMSTRService welche Du vorgeschlagen hast (etwas abgewandelt in meiner Applikation), führt auch nicht zum Erfolg (keine Einträge in Debug-Datei).

              Meine Projektdatei sieht im Moment so aus:

              <pre>
              var
              FFile: TextFile;

              begin
              DeleteFile('C:\DEBUG.TXT');
              AssignFile(FFile, 'C:\DEBUG.TXT');
              Rewrite(FFile);

              try
              WriteLn(FFile, 'Start Service');
              Flush(FFile);

              // Folgende 4 Zeilen auskommentieren, um Fehlermeldung "RPC-Server ist nicht verfügbar" angezeigt zu bekommen
              WriteLn(FFile, 'Try Start RPC-Service');
              Flush(FFile);
              StartServiceEx('RpcSs');
              WriteLn(FFile, 'Start RPC-Service');
              Flush(FFile);
              Application.Initialize;
              WriteLn(FFile, 'Initialize');
              Flush(FFile);
              Application.CreateForm(TfrmDBLockSrv, frmDBLockSrv);
              WriteLn(FFile, 'CreateForm');
              Flush(FFile);
              Application.Run;
              WriteLn(FFile, 'Run');
              Flush(FFile);
              except
              on e: Exception do
              begin
              WriteLn(FFile, e.ClassName);
              WriteLn(FFile, e.Message);
              Flush(FFile);
              end;
              end;
              CloseFile(FFile);
              </pre>

              Ich hoffe Du kannst mir noch einige Tipps geben.

              Die Entwicklungsumgebung ist folgende:
              Delphi 4 mit Update Pack #3 Windows NT Server mit SP #

              Comment


              • #8
                Eine kleine Änderung zur Angabe von oben:

                Startet man des Service 'RpcSs', so ist die Debug-Datei nicht leer, sondern der Eintrag 'Try Start RPC-Service' ist der letzte

                Comment


                • #9
                  Hallo Bernhard,

                  das hört sich nun nach einem COM-Problem an. Dafür gibt es zwei Hinweise: <br>
                  1. Die Beschreibung "<i>Der Aufruf Application.Initialize führt zu einer EOleSysError-Fehlermeldung mit dem Text "Der RPC-Server ist nicht verfügbar</i>" <br>
                  2. Der Ort: Application.Initialize

                  In TServiceApplication.Initialize wird Forms.Application.Initialize aufgerufen, und dort steht in Delphi 4 nur eine einzige Zeile:
                  <pre>
                  procedure TApplication.Initialize;
                  begin
                  if InitProc <> nil then TProcedure(InitProc);
                  end;
                  </pre>
                  Und je nach eingebunder VCL-Unit wird InitProc entsprechend vorbelegt. Bei der Unit ComObj zum Beispiel wird mit InitProc := @InitComObj; dafür gesorgt, das über <b>CoInitialize(nil)</b> ein STA (also das Hilfsfenster für die COM-Synchronisation) eingerichtet wird.

                  Ich würde daher zweierlei versuchen. Zuerst zum Test nachschauen, ob der Verzicht auf das STA (CoInitFlags := COINIT_MULTITHREADED) irgend etwas am Verhalten ändert. Und zum anderen würde ich über <b>TService.Dependencies</b> im Objektinspektor dafür sorgen, das "mein" Dienst als letzter gestartet wird, indem alle anderen als abhängige Dienste deklariert werden. Abhängigkeit bedeutet, dass ein Dienst nur dann ausgeführt werden kann, wenn auch der Dienst läuft, von dem er abhängig ist.

                  <pre>
                  program Project2;

                  uses
                  ComObj, ActiveX, SvcMgr,
                  Unit1 in 'Unit1.pas' {Service1: TService},
                  Unit2 in 'Unit2.pas' {Service2: TService};

                  {$R *.RES}

                  begin
                  IsMultiThread := True;
                  CoInitFlags := COINIT_MULTITHREADED;
                  Application.Initialize;
                  Application.CreateForm(TService1, Service1);
                  Application.CreateForm(TService2, Service2);
                  Application.Run;
                  end.
                  </pre>

                  Wenn das alles nichts hilft, kann das Verhalten in einem kurzen Beispielprojekt reproduziert werden

                  Comment


                  • #10
                    Wie immer, die Tipps sind erste Klasse!

                    Die beiden Zeilen haben zum gewünschten Ergebniss geführt!<br>
                    Hab keinen Eintrag mehr in den TService.Dependencies benötigt

                    Comment


                    • #11
                      Hallo Bernhard,

                      bei Delphi 5 wäre alles in Ordnung - aber bei Delphi 4 bleibt ein ungutes Geführ in der Magengegend zurück. Mit COINIT_MULTITHREADED gibt der Service zu erkennen, das er in der Lage ist, mit unsynchronisierten MTA's umzugehen. Leider ist die VCL erst ab Delphi 5 in diesem Punkt "wasserdicht", wobei Delphi 4 nicht generell ungeeignet ist (es kommt darauf an, was im Service gemacht wird).

                      Mache doch noch einen Versuch: <br>
                      a) Die beiden neuen Zeilen werden auskommentiert. <br>
                      b) Der Service wird mit <b>Interaktive Beziehung mit Desktop erlauben</b> konfiguriert.

                      Nur wenn das nicht weiterhilft, würde ich es bei COINIT_MULTITHREADED belassen (wenn im weiteren Einsatz keine Nebenwirkungen sichtbar werden)

                      Comment


                      • #12
                        Ich muß leider meine Aussage von vorhin korrigieren.

                        Die beiden Zeilen haben nicht den gewünschten Effekt gebracht.
                        Es tritt der gleiche Fehler auf.<br>
                        Scheinbar hat es für die ersten 3 Vesuche mit diesen beiden Einträgen geklappt, jedoch danach nicht mehr (Vielleich hab ich bei den 3 Versuchen auch noch einen Fehler gemacht).<br>
                        Auch ein Eintrag in TService.Dependencies bringt keine Besserung.

                        Ich hoffe Du hast noch ein paar Tipps für mich.

                        Das verwunderliche ist noch, daß ich einen ähnlich aufgebauten Service habe (NT-Dienst, Datenbank-Komponenten, DCOM-Schnittstelle).
                        Bei dieser Funktioniert der automatische Start. Jedoch eine (erstmalige) Aktivierung dieses Dienstes durch eine DCOM-Verbindung, wenn die Startart auf Manuell steht, funktioniert nicht! Der Dienst wird zwar gestartet, jedoch bekommt der erste Client, welche die Aktivierung versucht eine Fehlermeldung

                        Comment


                        • #13
                          Hallo Bernhard,

                          hier kommt meine nächste Empfehlung: Microsoft hat im SP4 einen Bug bei der DCOM-Rechtevergabe installiert, der erst mit dem SP6 beseitigt wird. Wenn die beiden Services zu unterschiedlichen Zeitpunkten (eventuell der eine unter SP3) installiert wurden, würde ich folgendes machen:

                          Programm DCOMCNFG - Registerseite <b>Standardsicherheit</b>: <br>
                          - Standard-Zugriffsberechtigung auf Jeder <br>
                          - Standard-Startberechtigung auf Jeder <br>
                          - Standard-Konfigurationsberechtigung auf Jeder

                          Wenn dann der Fehler verschwindet, würde ich das SP6 installieren und die DCOM-Rechte wieder einschränken.

                          P.S: Allerdings passen nicht alle Deine Beschreibungen in dieses Bild - wenn der Service nur bei 1 von 3 Startversuchen fehlschlägt, kann es eigentlich nicht ein DCOM-Rechteproblem sein.

                          P.S: Unter welchem Benutzerkonto läuft der DCOM-Server?
                          &#10

                          Comment


                          • #14
                            Der NT-Dienst läuft unter dem System-Konto!

                            Ich habe jetzt die Funktionalität mit einem neuen Delphi-Programm nachgestellt,
                            und bekomme jetzt andere Fehlermeldungen.

                            Die Exception die bei Application.Initialze ist:

                            Die Schnittstelle ist unbekannt

                            Die Projektdatei sieht folgendermaßen aus:

                            begin
                            try
                            DeleteFile('C:\DEBUG.TXT');
                            AssignFile(FFile, 'C:\DEBUG.TXT');
                            Rewrite(FFile);
                            WriteLn(FFile, 'Create File');
                            Flush(FFile);

                            IsMultiThread := True;
                            CoInitFlags := COINIT_MULTITHREADED;

                            Application.Initialize;
                            WriteLn(FFile, 'Initialize');
                            Flush(FFile);
                            Application.CreateForm(TSrvLockService, SrvLockService);
                            WriteLn(FFile, 'Create Form');
                            Flush(FFile);
                            Application.CreateForm(TdmLockService, dmLockService);
                            WriteLn(FFile, 'Create Form DataModule');
                            Flush(FFile);
                            Application.Run;
                            except
                            on e: Exception do
                            WriteLn(FFile, e.Message);
                            end;
                            CloseFile(FFile);
                            end.

                            Also ich glaube ich laß die Startart auf Manuell und starte den Dienst immer
                            per DCOM-Aufruf. Und werde mich erst wieder damit beschäftigen wenn
                            ich wieder etwas Zeit habe.

                            Oder ich Probier mal deinen Vorschlag einer "Hybrid"-Anwendung
                            unter der Diskussion "services / sockets" (Applikation sowohl als Dienst als auch
                            als TBNA laufen zu lassen). Weitere Fragen zu diesem Thema wirst Du dort von mir
                            hören (soll aber keine Drohung sein).

                            Trotzdem vielen Danke für die Hilfe

                            Comment

                            Working...
                            X