Entwickler-Ecke
Windows API - Programm wird durch Service gestartet, aber nicht beendet
jasocul - Di 13.01.09 10:51
Titel: Programm wird durch Service gestartet, aber nicht beendet
Folgende Situation:
Ich habe einen Service, der eine Stapeldatei aufruft.
Diese ruft wiederum eine Anwendung auf, die ein paar Dinge macht. Durch
Application.Terminate wird diese Anwendung automatisch beendet.
Zumindest sollte es so sein. :(
Starte ich die Stapeldatei manuell, funktioniert alles, wei gewünscht. Wird aus dem Service heraus gestartet, Wird die Anwendung nicht beendet. Es hat den Eindruck, dass das
Appplication.Terminate nicht ausgeführt wird.
Da ich während des Durchlaufs in der Anwendung Protokolle er zeuge, weiß ich genau, dass das Programm bis zum Terminate durchläuft.
Im FormCreate mache ich folgendes:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure TSDIAppForm.FormCreate(Sender: TObject); begin if ParamCount > 0 then begin Automatiklauf := True; SetzeFelder(ParamStr(1)); btnKomplett.Click; end else begin Automatiklauf := False; end; end; |
Wenn der Anwendung also ein Parameter übergeben wird, läuft es automatisch:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure TSDIAppForm.btnKomplettClick(Sender: TObject); begin btnKomplett.Tag := 1; btnCommand.Click; btnSourceEinlesen.Click; btnTargetEinlesen.Click; btnDatensicherung.Click; btnKomplett.Tag := 0; if Automatiklauf then Application.Terminate; end; |
Das Programm ist bis auf die Fertig-Meldung ohne Dialoge.
Hat jemand eine Idee, was falsch sein könnte?
Ergänzung:
Das ist der Source, der am Schluss in btnDatensicherung.Click ausgeführt wird:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| if not Automatiklauf then begin frmProtokoll.ListBox1.Clear; frmProtokoll.ListBox1.Items.AddStrings(Protokoll); frmProtokoll.show; end else begin tmp := DateTimeToStr(now); tmp := StringReplace(tmp, '.', '-', [rfReplaceAll]); tmp := StringReplace(tmp, ':', '-', [rfReplaceAll]); Protokoll.SaveToFile(ExtractFilePath(ParamStr(0)) + tmp + '.pkl'); end; |
Das Protokoll wird definitiv erzeugt und ist wirklich der letzte Schritt vor dem Terminate.
Boldar - Di 13.01.09 15:06
Lass dir mal Automatiklauf ausgeben: Ist das wirklich auch wenn es vom Service gestartet wird, true?
Villeicht stimmt etwas mit den von der Batch übergebenen Parametern nicht, sodass es hier:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| if ParamCount > 0 then begin Automatiklauf := True; SetzeFelder(ParamStr(1)); btnKomplett.Click; end |
sofort in den else-Zweig springt??
jasocul - Di 13.01.09 15:11
Wäre es so, würde kein Protokoll erstellt werden.
Eigentlich würde dann gar nichts passieren, weil das Programm von sich aus nichts macht.
wdbee - Di 13.01.09 16:02
Hallo jasocul,
was mir auffällt:
Die ganze Verarbeitung inkl. Terminate erfolgt in .CreateForm.
Mach als schnellen Test mal folgendes:
Verschiebe die Verarbeitung in eine Funktion, die du verzögert starten kannst, indem du in CreateForm einen Timer mit kurzem Intervall startest (nicht vergessen, den Timer dass sofort wieder zu stoppen).
Dann bist du sicher, dass die gesamte Initialisierung auch wirklich abgeschlossen ist, wenn die Verarbeitung anläuft und Application.Terminate aufgerufen wird. Wenn ich das richtig verstanden habe, ist das ja genau der Unterschied zum manuellen Aufruf.
Gruß
wdbee
jasocul - Di 13.01.09 16:10
Verdammt, du könntest Recht haben.
Danke für deinen Hinweis.
Narses - Di 13.01.09 16:14
Moin Peter!
Aber (sollte es damit zu tun haben) lass das mit dem Timer und schick dir eine Nachricht, in deren Handler du die Aktionen ausführst. :idea: ;)
cu
Narses
jasocul - Di 13.01.09 16:20
Habe ich auch vor.
Zum Testen komme ich aber erst morgen. Das Ergebnis wird natürlich geliefert.
jasocul - Mi 14.01.09 08:34
Das war es nicht.
Jetzt weiß ich auch wieder, warum ich ein Terminate verwendet habe. Ein Close funktioniert nicht immer im FormCreate.
Alles sehr merkwürdig.
Bin für jede weitere Idee offen.
Narses - Mi 14.01.09 11:44
Moin!
jasocul hat folgendes geschrieben : |
Das war es nicht.
Jetzt weiß ich auch wieder, warum ich ein Terminate verwendet habe. Ein Close funktioniert nicht immer im FormCreate. |
Poste dir doch mal ein WM_CLOSE, testweise. :nixweiss:
cu
Narses
jasocul - Mi 14.01.09 12:29
Ich lasse jetzt erstmal für jeden Schritt einen Protokoll-Eintrag erstellen.
Die Anwendung macht sogar noch NACH dem Application.Terminate im Testlauf (ohne Service) einen entsprechenden Eintrag ins Protokoll. Jetzt will ich das nochmal mit einem Application.ProcessMessages nach dem Terminate versuchen.
Sieht schon ziemlich verzweifelt aus. :?
Narses - Mi 14.01.09 14:17
Moin!
Ich erinnere mich düster, dass ich dabei auch schonmal Probleme hatte... :gruebel:
Setz mal hinter das APM ein Exit; Nutzt das was?
Bzw. hier eigentlich (also in der ersten Methode, die aus dem MessageLoop heraus aufgerufen wird:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| procedure TSDIAppForm.FormCreate(Sender: TObject); begin if ParamCount > 0 then begin Automatiklauf := True; SetzeFelder(ParamStr(1)); btnKomplett.Click; Exit; end else begin Automatiklauf := False; end; end; |
cu
Narses
wdbee - Mi 14.01.09 14:23
Hallo jasocul,
ich weiß nicht genau, wie du dein Programm als Service startest, aber wenn du dein Formular direkt aus einem TService heraus verwendest klappt das so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
| procedure TServiceCloseDemo.ServiceCreate(Sender: TObject); begin AddToLogfile('TServiceCloseDemo.ServiceCreate'); end;
procedure TServiceCloseDemo.ServiceDestroy(Sender: TObject); begin AddToLogfile('TServiceCloseDemo.ServiceDestroy'); end;
procedure TServiceCloseDemo.ServiceExecute(Sender: TService); begin AddToLogfile('>TServiceCloseDemo.ServiceExecute ...');
if FormMain <> nil then FormMain.OnTerminateService := DoTerminateService; while not (Terminated or oTerminate) do begin AddToLogfile('> ... TServiceCloseDemo.ServiceExecute ...'); Sleep(1000); ServiceThread.ProcessRequests(False); end; AddToLogfile('<... TServiceCloseDemo.ServiceExecute'); end;
procedure TServiceCloseDemo.DoTerminateService(Sender: TObject); begin AddToLogfile('>TServiceCloseDemo.DoTerminateService ...'); oTerminate := True; AddToLogfile('<... TServiceCloseDemo.DoTerminateService'); end; |
Im Formular:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
| procedure TFormMain.FormCreate(Sender: TObject); begin AddToLocalLogfile('>FormCreate ...');
AddToLocalLogfile('>Timer started ...'); TimerClose.Enabled := True; AddToLocalLogfile('<... Timer started'); AddToLocalLogfile('<... FormCreate'); end;
procedure TFormMain.TimerCloseTimer(Sender: TObject); begin TimerClose.Enabled := False; AddToLocalLogfile('>Timer ...'); Close; AddToLocalLogfile('<... Timer'); end;
procedure TFormMain.FormClose(Sender: TObject; var Action: TCloseAction); begin AddToLocalLogfile('>FormClose ...');
if Assigned(OnTerminateService) then OnTerminateService(self);
AddToLocalLogfile('<... FormClose'); end; |
Dabei kannst du Close per Timer (Bequem) oder per Message (umständlicher, da du dennoch für eine geeignete Verzögerung sorgen musst) aufrufen.
Als Ergebnis bekomme ich:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| 2009.01.14 13:03:09:716 >TServiceCloseDemo.ServiceCreate< 2009.01.14 13:03:09:716 >>FormCreate ...< 2009.01.14 13:03:09:716 >>Timer started ...< 2009.01.14 13:03:09:716 ><... Timer started< 2009.01.14 13:03:09:732 ><... FormCreate< 2009.01.14 13:03:09:732 >TServiceCloseDemo.ServiceStart< 2009.01.14 13:03:09:732 >>TServiceCloseDemo.ServiceExecute ...< 2009.01.14 13:03:09:732 >> ... TServiceCloseDemo.ServiceExecute ...< 2009.01.14 13:03:10:732 >> ... TServiceCloseDemo.ServiceExecute ...< 2009.01.14 13:03:11:732 >> ... TServiceCloseDemo.ServiceExecute ...< 2009.01.14 13:03:12:716 >>Timer ...< 2009.01.14 13:03:12:716 >>FormClose ...< 2009.01.14 13:03:12:716 >>TServiceCloseDemo.DoTerminateService ...< 2009.01.14 13:03:12:716 ><... TServiceCloseDemo.DoTerminateService< 2009.01.14 13:03:12:716 ><... FormClose< 2009.01.14 13:03:12:716 ><... Timer< 2009.01.14 13:03:12:732 ><... TServiceCloseDemo.ServiceExecute< 2009.01.14 13:03:12:732 >TServiceCloseDemo.ServiceDestroy< |
Ich mache das immer etwas anders:
1. Service.exe verwendet ServiceModul und das DataModulGlobal
2. Programm.exe verwendet TFormMain und das DataModulGlobal
Der servicespezifische (nicht interaktive) Teil ist im ServiceModul, der programmspezifische (interaktive) Teil im FormMain.
Alles was beide machen/verwenden sollen ist im DataModul. Damit bin ich auch sehr komplizierten Fällen immer gut gefahren.
Gruß
wdbee
jasocul - Mi 14.01.09 15:41
Danke für eure Tipps.
Da habe ich ja erstmal was zu tun. Ich melde mich, sobald ich mit den Tests durch bin.
jasocul - Do 15.01.09 16:37
Ich werde aaaaalt
:bawling:
Der Dienst lief mit dem System-Konto. Dieses hat aber auf anderen Servern (in diesem Fall ein NAS) keine passenden Rechte. Dadurch hat es wohl eine Exception gegeben und das Terminate wurde gar nicht erst aufgerufen. :roll:
Deswegen funktioniert es auch, wenn ich manuell starte. Dann bin ich Domänen-Admin.
:autsch: :autsch: :autsch:
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!