Autor Beitrag
holgerbremen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 120



BeitragVerfasst: Mi 20.12.06 13:47 
Ich habe eine TService-Applikation. Soweit läuft auch alles, jedenfalls installieren und starten kann ich sie.

Nur Stoppen und Pause des Dienstes funktioniert nicht. Die Eigenschaften AllowPause/Stop sind auf true, daran kann es nicht liegen.

Hat jemand eine Ahnung, wo das Problem ist.

Gruß Holger
wdbee
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Mi 20.12.06 14:46 
Was treibt dein Dienst denn so? Wenn er eine Anforderung zum Stoppen oder zur Pause bekommt, muss er (auch in der Arbeitsschleife) darauf reagieren.

Wir haben auch Dienste, die wir intern verwenden, bei denen das nicht beachtet wird. Nach dem Start bzw. um bestimmte Zeiten legen die los und arbeiten einige Zeit. Während der Bearbeitung reagieren sie nicht auf Stop/Pause. Wenn die Arbeit abgeschlossen ist, dann klappt es.

Als Ergebnis bekommt man eine Fehlermeldung, wenn versucht wird, den Dienst zu beenden.
Da aber klar ist, wann er arbeitet, stört das niemand.
holgerbremen Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 120



BeitragVerfasst: Mi 20.12.06 15:45 
Das Programm erzeugt Timer-Gesteuert alle 60 Sekunden PDF-Dateien. Die Daten werden aus einem MS-SQLServer gelesen. In der Procedure ServiceExecute() mache ich eigentlich gar nichts, außer zu sleepen. Ich versuche den Dienst schon zu stoppen, wenn gerade keine Verarbeitung läuft, aber auch dann gehts nicht.

Kann es an der ServiceExecute()-Funktion liegen.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TTQMSaverFabo.ServiceExecute(Sender: TService);
begin

  while not Terminated do
  begin
    Sleep(200); 
  end;

end;

Gruß

PS: Wie füge ich hier eigentlich Code ein?

Moderiert von user profile iconTino: Delphi-Tags hinzugefügt
Tino
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Veteran
Beiträge: 9839
Erhaltene Danke: 45

Windows 8.1
Delphi XE4
BeitragVerfasst: Mi 20.12.06 15:56 
user profile iconholgerbremen hat folgendes geschrieben:
PS: Wie füge ich hier eigentlich Code ein?


www.delphi-forum.de/..._bbcodes_source.html
wdbee
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Do 21.12.06 10:15 
Was bei dir fehlt, ist die Bearbeitung von Nachrichten (ProcessRequests).
Das muss über den ServiceThread erfolgen, der dem Dienst zugeordnet ist.
Ein Timer ist nicht notwendig, aber möglich.
Für einfache Fälle:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
//==========================================================================*
// Working loop
//--------------------------------------------------------------------------*
procedure TXYZ_Service.ServiceExecute(Sender: TService);
begin
  // Hier wird Initialisiert (notfalls Timer starten)

  while not Terminated do                        // Solange Endeflag nicht gesetzt ist
  begin                                          // Anstehende Nachrichten bearbeiten
    ServiceThread.ProcessRequests(False);        // aber nicht auf Nachrichten warten

    // Hier arbeiten (falls nicht im Timer Event realisiert)
    ...

    Sleep(200);                                  // ggf. CPU-Last begrenzen
                                                 // bzw. Wiederholrate (statt Timer)
  end;

  // Hier wird aufgeräumt
end;
//==========================================================================*

Das sollte für viele Fälle schon ausreichen. Probleme gibt es, wenn die Arbeit (bzw. ein Durchlauf der while-Schleife) länger dauert, als die Wartezeit des Dienstecontrollers es akzeptiert.

Hier mal ein Gerüst, dass auch für kompliziertere Fälle geeignet ist. Die eigentliche Arbeit erfolgt im Datenmodul (Execute).
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
//==========================================================================*
// Moduldefinition
//--------------------------------------------------------------------------*
type
  TPCbCS_Service = class(TService)
    procedure ServiceExecute(Sender: TService);
  private
    procedure ProcessRequests(Sender: TObject);
    procedure Terminate;

  public
    function GetServiceController: TServiceController; override;
  end;
//==========================================================================*

//==========================================================================*
// Globale Variable
//--------------------------------------------------------------------------*
var
  PCbCS_Service: TPCbCS_Service;
//==========================================================================*

Wir wollen als erste erfahren, dass der Dienst beendet werden soll. Deshalb schleifen wir uns hier ein:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
//==========================================================================*
// Check for terminate command
//--------------------------------------------------------------------------*
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  if CtrlCode = 1 then                // Hier erfahren wir aus erster Hand
    PCBCS_Service.Terminate;          // das der Dienst beendet werden soll!
  PCBCS_Service.Controller(CtrlCode); // Normalen Ablauf durchführen 
end;
//==========================================================================*

Das was bei dir gefehlt hatte, wird in der eigentlichen Arbeitsschleife des Datenmoduls
zyklisch aufgerufen, so dass der Dienst schnell genug auf Anforderungen des Controllers reagieren kann.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
//==========================================================================*
// Process requests (Als Callback für das Datenmodul)
//--------------------------------------------------------------------------*
procedure TPCbCS_Service.ProcessRequests(Sender: TObject);
begin
  ServiceThread.ProcessRequests(False);          // Process Messages
end;
//==========================================================================*

An dieser Stelle wird die Arbeit einfach delegiert.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
//==========================================================================*
// Working loop
//--------------------------------------------------------------------------*
procedure TPCbCS_Service.ServiceExecute(Sender: TService);
begin
  DMGlobal.OnProcessRequests := ProcessRequests; // set event handler
  DMGlobal.Execute;                              // work
  // Rückkehr erst nach Terminate!
end;
//==========================================================================*

Die eigentliche Arbeit ist im Datenmodul bequemer unterzubringen, wenn dort Komponenten abgelegt werden müssen.
ausblenden 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:
//==========================================================================*
// Ausgelagerte Arbeitsschleife
//--------------------------------------------------------------------------*
function TDMGlobal.Execute: Boolean;
begin
  // Initialisieren

  // Arbeitsschleife
  while not oTerminated do
  begin
    // in kleine Schritten Arbeiten
    ...
    if Assigned(OnProcessRequests) then          
      OnProcessRequests(self);
    if oTerminated then
      break;

    // in kleine Schritten Arbeiten
    ...
    // ggf. mehrfach einbauen, damit die Reaktionszeit kurz bleibt
    if Assigned(OnProcessRequests) then                
      OnProcessRequests(self);
    if oTerminated then
      break;
  end;

  // Aufräumen
  Result := True;
end;
//==========================================================================*

Anforderung des Controllers umsetzen lassen.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
//==========================================================================*
// Stop service
//--------------------------------------------------------------------------*
procedure TPCbCS_Service.Terminate;
begin
  DMGlobal.Terminate;
end;
//==========================================================================*

Dabei ist wichtig, dass nicht alle diese Funktionen im gleichen Kontext ausgeführt werden! Der Aufruf von TDMGlobal.Terminate läuft im Kontext des Controllers, weshalb die Anweisung Sleep(xyz) nicht die Arbeitsschleife blockiert.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
//==========================================================================*
// Beenden
//--------------------------------------------------------------------------*
procedure TDMGlobal.Terminate;
begin
  oTerminated := True; // Flag setzen
  Sleep(xyz);          // Der Arbeitsschleife Zeit für Reaktion geben
end;
//==========================================================================*
holgerbremen Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 120



BeitragVerfasst: Do 21.12.06 14:14 
@wdbee

Danke, das war es doch schon. Die Funktion ProcessRequests() fehlte mir noch. Jetzt läßt sich der Service starten/stoppen und pausen.


ausblenden 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:
//==========================================================================*  
// Working loop  
//--------------------------------------------------------------------------*  
procedure TXYZ_Service.ServiceExecute(Sender: TService);  
begin  
  // Hier wird Initialisiert (notfalls Timer starten)  
 
  while not Terminated do                        // Solange Endeflag nicht gesetzt ist  
  begin                                          // Anstehende Nachrichten bearbeiten  
    ServiceThread.ProcessRequests(False);        // aber nicht auf Nachrichten warten  
 
    // Hier arbeiten (falls nicht im Timer Event realisiert)  
    ...  

 
    Sleep(200);                                  // ggf. CPU-Last begrenzen  
                                                 // bzw. Wiederholrate (statt Timer)  
  end;  

 
  // Hier wird aufgeräumt  
end;  
//==========================================================================*


Besten Dank noch für das ganze Programmgerüst. Muss ich mir mal in Ruhe anschauen.

Gruß
Holger