AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Delphi Wie erstelle ich einen Dienst für WinNT / Win2000 / Win XP
Tutorial durchsuchen
Ansicht
Themen-Optionen

Wie erstelle ich einen Dienst für WinNT / Win2000 / Win XP

Ein Tutorial von MaBuSE · begonnen am 28. Dez 2005 · letzter Beitrag vom 21. Okt 2013
Antwort Antwort
Seite 2 von 4     12 34      
Benutzerbild von MaBuSE
MaBuSE
Registriert seit: 23. Sep 2002
Ich habe in dem Beitrag ISDN-Anrufmonitor eine kleine Anleitung zu Diensten geschrieben, das gehört aber eigentlich hier hin

Was ist ein Dienst?
In WinNT/Win2000/WinXP (und neuer) gibt es die Möglichkeit Programme schon beim Starten des Betriebssystems, noch bevor sich ein Benutzer anmeldet, auszuführen. Diese Programme werden als Dienste (engl: Service) bezeichnet. Diese Dienste werden über die Diensteverwaltung gesteuert.
Mit Systemsteuerung -> Verwaltung -> Dienste kommt man zur Diensteverwaltung. hier kann man sehen welche Dienste auf dem Rechner verfügbar sind. Es ist auch der Status und die Beschreibung zu sehen.
(Von der Konsole (cmd.exe) kann man mit "net start" eine Liste der gestarteten Dienste sehen.)

Wie erstellt man einen Dienst?
Unter Delphi 7 geht das ganz einfach.
(Ich habe gerade mal Delphi 5 gestartet, dort geht es auch.)
  • Menü Datei -> Neu... aufrufen.
  • In der Objektgalerie -> Neu -> Service Anwendung aufrufen.
  • Das Grundgerüst zu einem Dienst ist nun da. (Projekt1.dpr und Unit1.pas/dfm)
  • Es wird eine Unit1.pas mit dem TService namens Service1 angezeigt. (nicht visuelles Form)
  • In den Eigenschaften gibt es
    • DisplayName - Das ist der Name des Dienstes, der in der Diensteverwaltung angezeigt wird.
    • Interactive - sollte True sein und bedeutet, das der Dienst auch mit dem Benutzer interagieren darf
      (z.B. Dialogfenster, Icon in der Taskleiste (TNA), ...)
    • Dependencies - Abhängigkeiten von anderen Diensten: z.B. "Telefonie" (TAPI)
  • über die Ereignisse wird der Code ausgeführt:
    • onExecute - Das ist das Hauptereignis:
      Es wird aufgerufen wenn der Dienst ausgeführt wird. In diesem Ereignis sollte eine Schleife stehen die die Tätigkeiten des Dienstes ausführt. Benutzt Du eine Komponente (z.B. TTimer) um die Anrufe zu pollen dann z.B.
      Delphi-Quellcode:
      ...
        while not Terminated do
        begin
          ServiceThread.ProcessRequests(False);
        end;
      ...
      Wenn Du das selbst in einer Schleife macht z.B.
      Delphi-Quellcode:
      ...
        while not Terminated do
        begin
          ServiceThread.ProcessRequests(False);

          if isTelephoneCall then
          begin
            // show Infos about Call
          end;
        end;
      ...
Die Hilfe von Delphi enthält weitere Infos

Zu jedem Dienst wird ein TServiceTread ausgeführt. (Es können auch mehrere Dienste in einer Exe enthalten sein.) Sollten mehrere Dienste in einer Applikation enthalten sein muss unbedingt threadsicher programmiert werden. Der TServiceThread ist so gestaltet, dass die Funktionalität des Dienstes in der OnExecute implementiert wird. Der TServiceThread hat seine eigene Execute Methode die OnStart und OnExecute aufruft.
Delphi-Quellcode:
{*******************************************************}
{       Borland Delphi Visual Component Library         }
{       Services                                        }
{       Copyright (c) 1997,99 Inprise Corporation       }
{*******************************************************}
unit SvcMgr;
...
procedure TServiceThread.Execute;
...
    FService.Status := csStartPending;
    Started := True;
    if Assigned(FService.OnStart) then FService.OnStart(FService, Started);
    // wird Started in OnStart aud False gesetzt wird OnExecute nie aufgerufen
    if not Started then Exit;
    try
      FService.Status := csRunning;
      if Assigned(FService.OnExecute)
        then FService.OnExecute(FService) // onExecute aufrufen
        else ProcessRequests(True);
      ProcessRequests(False);
...
Da die Ausführung der OnStart / OnExecute zeit braucht kann der Thread derweil nicht auf simultane Anfragen von anderen Clients reagieren. Deshalb kann man auch einen eigenen Thread erzeugen.
Das würde dann so aussehen:
Delphi-Quellcode:
...
  TeigenerThread = class(TThread)
    public
      procedure Execute; override;
  end;
...
var
  eigenerThread: TeigenerThread;
...
procedure TeigenerThread.Execute;
begin
  while not Terminated do
  begin
    Beep;
    // Das braucht die Zeit, in der sonst der Dienst tot wäre.
    Sleep(500);
  end;
end;
...
procedure TService1.Service1Start(Sender: TService; var Started: Boolean);
begin
  eigenerThread := TeigenerThread.Create(False);
  Started := True;
end;
...
procedure TService1.Service1Continue(Sender: TService; var Continued: Boolean);
begin
  eigenerThread.Resume;
  Continued := True;
end;
...
procedure TService1.Service1Pause(Sender: TService; var Paused: Boolean);
begin
  eigenerThread.Suspend;
  Paused := True;
end;
...
procedure TService1.Service1Stop(Sender: TService; var Stopped: Boolean);
begin
  eigenerThread.Terminate;
  Stopped := True;
end;
...
Das der TeigeneThread thadtsicher programmiert werden sollte versteht sich ja von selbst.

Mein Tipp:
Schreib dir mal einen Dienst, der in jedem Ereignis nur ein ShowMessage stehen hat.
Damit kannst Du dann schnell rausbekommen wann welche Ereignisse aufgerufen werden.
z.B.
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs;

type
  TService1 = class(TService)
    procedure ServiceAfterInstall(Sender: TService);
    procedure ServiceAfterUninstall(Sender: TService);
    procedure ServiceBeforeInstall(Sender: TService);
    procedure ServiceBeforeUninstall(Sender: TService);
    procedure ServiceContinue(Sender: TService; var Continued: Boolean);
    procedure ServiceCreate(Sender: TObject);
    procedure ServiceDestroy(Sender: TObject);
    procedure ServiceExecute(Sender: TService);
    procedure ServicePause(Sender: TService; var Paused: Boolean);
    procedure ServiceShutdown(Sender: TService);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
  private
    { Private-Deklarationen }
  public
    function GetServiceController: TServiceController; override;
    { Public-Deklarationen }
  end;

var
  Service1: TService1;

implementation

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TService1.ServiceAfterInstall(Sender: TService);
begin
  showMessage('AfterInstall');
end;

procedure TService1.ServiceAfterUninstall(Sender: TService);
begin
  showMessage('AfterUninstall');
end;

procedure TService1.ServiceBeforeInstall(Sender: TService);
begin
  showMessage('BeforeInstall');
end;

procedure TService1.ServiceBeforeUninstall(Sender: TService);
begin
  showMessage('BeforeUninstall');
end;

procedure TService1.ServiceContinue(Sender: TService;
  var Continued: Boolean);
begin
  showMessage('OnContinue');
  Continued := True;
end;

procedure TService1.ServiceCreate(Sender: TObject);
begin
  showMessage('OnCreate');
end;

procedure TService1.ServiceDestroy(Sender: TObject);
begin
  showMessage('OnDestroy');
end;

procedure TService1.ServiceExecute(Sender: TService);
begin
  showMessage('OnExecute - begin');
  while not Terminated do
  begin
    ServiceThread.ProcessRequests(False);
  end;
  showMessage('OnExecute - end');
end;

procedure TService1.ServicePause(Sender: TService; var Paused: Boolean);
begin
  showMessage('OnPause');
  Paused := True;
end;

procedure TService1.ServiceShutdown(Sender: TService);
begin
  showMessage('OnShutdown');
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  showMessage('OnStart');
  Started := True;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  showMessage('OnStop');
  Stopped := True;
end;

end.
Die erzeugte Exe Datei mußt Du Mit dem Parameter INSTALL aufrufen um den Dienst zu registrieren.
Mit Projekt1.exe UNINSTALL kannst Du den Dienst wieder aus der Diesteverwaltung entfernen.

Ich hoffe das hilft Dir erst mal weiter.
(°¿°) MaBuSE - proud to be a DP member
(°¿°) MaBuSE - proud to be a "Rüsselmops" ;-)
 
mimi

 
FreePascal / Lazarus
 
#11
  Alt 6. Apr 2006, 18:38
ah alles klar, ich möchte auch kakein dienst erstlelen, war nur mal so ne frage *G*
Michael Springwald
  Mit Zitat antworten Zitat
Andru

 
Delphi 6 Enterprise
 
#12
  Alt 15. Jun 2006, 12:09
Hi,
erstmal vielen Dank für dieses TUT. Hat mir auf jeden Fall super weitergeholfen.
Mein Problem jedoch ist, wenn ich den Service installiert habe und starte,
startet er meine Anwendung (ShellExecute in der ServiceStart-Prozedur), aber der Status
in der Dienste verwaltung ändert sich nicht.
Es steht zwar, dass der Dienst gestartet ist, aber ich habe nicht die Möglichkeit ihn wieder
zu beenden.
Was mache ich denn noch falsch? Wird der Status nicht über 'started := true' übergeben?

Hoffe, Ihr könnt mir weiterhelfen!


Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
  shellAPI, tlhelp32;

type
  TTestService = class(TService)
    procedure ServiceAfterInstall(Sender: TService);
    procedure ServiceAfterUninstall(Sender: TService);
    procedure ServiceBeforeInstall(Sender: TService);
    procedure ServiceBeforeUninstall(Sender: TService);
    procedure ServiceContinue(Sender: TService; var Continued: Boolean);
    procedure ServiceCreate(Sender: TObject);
    procedure ServiceDestroy(Sender: TObject);
    procedure ServiceExecute(Sender: TService);
    procedure ServicePause(Sender: TService; var Paused: Boolean);
    procedure ServiceShutdown(Sender: TService);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);

  private
    { Private-Deklarationen }
  public
    function GetServiceController: TServiceController; override;
    { Public-Deklarationen }
  end;

var
  TestService: TTestService;
  ProgHandle, RemoteHandle: THandle;

implementation

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  TestService.Controller(CtrlCode);
end;

function TTestService.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TTestService.ServiceAfterInstall(Sender: TService);
begin
  showMessage('AfterInstall');
end;

procedure TTestService.ServiceAfterUninstall(Sender: TService);
begin
  showMessage('AfterUninstall');
end;

procedure TTestService.ServiceBeforeInstall(Sender: TService);
begin
  showMessage('BeforeInstall');
end;

procedure TTestService.ServiceBeforeUninstall(Sender: TService);
begin
  showMessage('BeforeUninstall');
end;

procedure TTestService.ServiceContinue(Sender: TService;
  var Continued: Boolean);
begin
  showMessage('OnContinue');
  Continued := True;
end;

procedure TTestService.ServiceCreate(Sender: TObject);
begin
  showMessage('OnCreate');
   ServiceExecute(self);
end;

procedure TTestService.ServiceDestroy(Sender: TObject);
begin
  showMessage('OnDestroy');
end;

procedure TTestService.ServiceExecute(Sender: TService);
begin
  showMessage('OnExecute - begin');
{  while not Terminated do
  begin
    ServiceThread.ProcessRequests(False);
  end;
}



  showMessage('OnExecute - end');


end;

procedure TTestService.ServicePause(Sender: TService; var Paused: Boolean);
begin
  showMessage('OnPause');
  Paused := True;
end;

procedure TTestService.ServiceShutdown(Sender: TService);
begin
  showMessage('OnShutdown');
end;

procedure TTestService.ServiceStart(Sender: TService; var Started: Boolean);
begin
  ShellExecute(ProgHandle, 'open', PChar('notepad.exe'),
  Nil, Nil, SW_NORMAL);
 // showMessage('OnStart');
  Started := True;
end;

procedure TTestService.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  RemoteHandle := FindWindow(NIL,'unbenannt - Editor');
  if RemoteHandle<>0 then PostMessage(RemoteHandle, wm_close, 1, 1);
 // showMessage('OnStop');
  Stopped := True;
end;




end.
  Mit Zitat antworten Zitat
franktron

 
Delphi 10.2 Tokyo Enterprise
 
#13
  Alt 24. Aug 2006, 11:26
Zitat von MaBuSE:
Die erzeugte Exe Datei mußt Du Mit dem Parameter INSTALL aufrufen um den Dienst zu registrieren.
Mit Projekt1.exe UNINSTALL kannst Du den Dienst wieder aus der Diesteverwaltung entfernen.

Ich hoffe das hilft Dir erst mal weiter.
Da hat sich ein kleiner Fehler eingeschlichen das mus Projekt1.exe /INSTALL und /UNINSTALL heisen
Frank
  Mit Zitat antworten Zitat
Benutzerbild von stz
stz

 
Turbo Delphi für Win32
 
#14
  Alt 10. Aug 2007, 18:03
Erstmal vielen Dank für das Tutorial

Zitat von MaBuSE:
Benutzt Du eine Komponente (z.B. TTimer) um die Anrufe zu pollen dann z.B.
Delphi-Quellcode:
...
  while not Terminated do
  begin
    ServiceThread.ProcessRequests(False);
  end;
...
Wenn ich das tatsächlich so mache (inklusive Timer) habe ich einen wunderbaren Dienst zum lahmlegen des Systems . Durch die Endlosschleife braucht der Thread fast 100% der CPU-Zeit. Deutlich besser ist in diesem FallServiceThread.ProcessRequests(True); Nun wartet ProcessRequest auf die nächste Message, bevor es weitergeht. Da man ja eh mit dem Timer auf genau eine solche wartet, empfiehlt sich das.

Ansonsten muss man zu mindestens irgendetwas tun, um die Endlosschleife zu entschärfen (sleep?!)

Noch eine weitere Frage: Kann ich auch irgendwie komfortabel eine Beschreibung definieren, die in Systemsteuerung -> Verwaltung -> Dienste angezeigt wird?

Gruß
Malte

PS: Ich hoffe ich darf den Thread nach so langer Zeit noch aufwärmen...
Malte Schmitz
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry

 
Delphi 2006 Professional
 
#15
  Alt 10. Aug 2007, 18:09
mit der Funktion ChangeServiceConfig2A kannst du die Beschreibung ändern
Delphi-Quellcode:
function ChangeServiceConfig2A(hService: HWND; dwInfoLevel: DWord; lpInfo: Pointer): BOOL; stdcall; external 'advapi32.dll';
[...]
var
  lDescription : TSERVICE_DESCRIPTION;
  lManager,
  lServiceHandle : Cardinal;
begin
  lManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  if (lManager <> 0) then
  begin
    lServiceHandle := OpenService(lManager, 'NameDeinesService'{bei Ableitung von TService wäre das: PChar(Self.Name)}, SERVICE_ALL_ACCESS);
    if (lServiceHandle <> 0) then
    begin
      lDescription.lpDescription := 'Deine Beschreibung';
      ChangeServiceConfig2A(lServiceHandle, SERVICE_CONFIG_DESCRIPTION, @lDescription);
      CloseServiceHandle(lServiceHandle);
    end;
    CloseServiceHandle(lManager);
  end;
Jens
  Mit Zitat antworten Zitat
Benutzerbild von stz
stz

 
Turbo Delphi für Win32
 
#16
  Alt 11. Aug 2007, 11:41
@SirThornberry: Das sieht doch ganz gut aus, aber...
Ich habe jetzt die Unit Winsvc eingebunden und mir den Typ TSERVICE_DESCRIPTION mithilfe des PSDKs zusammengebastelt:
Delphi-Quellcode:
type
  PServiceDescription = ^TServiceDescription;
  {$EXTERNALSYM _SERVICE_DESCRIPTION}
  _SERVICE_DESCRIPTION = record
    lpDescription: LPTSTR;
  end;
  {$EXTERNALSYM SERVICE_DESCRIPTION}
  SERVICE_DESCRIPTION = _SERVICE_DESCRIPTION;
  TServiceDescription = _SERVICE_DESCRIPTION;
Aber die Konstante SERVICE_CONFIG_DESCRIPTION kann ich nirgendwo finden

Gruß
Malte
Malte Schmitz
  Mit Zitat antworten Zitat
Benutzerbild von xZise
xZise

 
Delphi 2009 Professional
 
#17
  Alt 25. Nov 2007, 17:03
Es wird hier häufig geschrieben, dass der Dienst möglichst nicht interaktiv sein sollte.
Nun wie kann ich dennoch etwas für den Benutzer ausgeben?
Mir schwebt ein Konzept vor, dass im Autostart sozusagen das Frontend (eine stinknormale exe) gestartet wird.
Aber ich weiß jetzt nicht wie man das machen könnte.

MfG
xZise
Fabian
  Mit Zitat antworten Zitat
mkinzler

 
Delphi 11 Alexandria
 
#18
  Alt 25. Nov 2007, 17:11
Das ist auch der zugehende Weg. Du musst entweder per Netzprotokoll oder durch Pipes, IPC o.ä. mit dem Dienst kommunizieren.
Markus Kinzler
  Mit Zitat antworten Zitat
Apollonius

 
Turbo Delphi für Win32
 
#19
  Alt 25. Nov 2007, 17:18
Wenn es nur um einfache Ausgabe geht, kann man auch die Messagebox mit MB_SERVICE_NOTIFICATION verwenden.
  Mit Zitat antworten Zitat
Benutzerbild von xZise
xZise

 
Delphi 2009 Professional
 
#20
  Alt 26. Nov 2007, 15:01
Zitat von Apollonius:
Wenn es nur um einfache Ausgabe geht, kann man auch die Messagebox mit MB_SERVICE_NOTIFICATION verwenden.
Achso Das reicht dann auch eigentlich

Nun eine ganz andere Frage zu der Schleife.
Und zwar erstelle ich eine UDP-Verbindung mithilfe von UdpSockUtil. Nun weiß ich nicht, wie ich die Schleife gestalten muss, weil das Pollen übernimmt ja die Komponente.
Aber der Dienst wird ja ohne "Aufgaben" beendet.
Kann man irgendwo sagen: "KillOnIdle := false"? ^^

MfG
xZise
Fabian
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 4     12 34      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:36 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz