Home » Tutorials » Systemnahe Programmierung » NT-Services

NT-Services

Ein eigener Service

Wie schreibe ich nun einen Service?

Das ist nicht so schwer wie man glauben mag. Man binde die WinSVC Unit in sein Projekt ein (das im besten Falle ein Konsolenprogramm sein sollte, da nur interaktive Services mit dem Benutzer interagieren können).
Wir gehen also erstmal von einem Konsolenprogramm aus.

program svc;
uses
  windows,
  winsvc;

const
  servicename='testservice';
  displayname='assarbad''s testservice';

procedure service_main;forward;

{$include consolehlp.pas}
{$include service.pas}

procedure service_main;
begin
  repeat
    if not paused then begin
    end;
  until stopped;
end;

begin
  prog_main;
end.

Die Prozedur PROG_MAIN ist in SERVICE.PAS definiert und CONSOLEHLP.PAS enthält einige Hilfroutinen für Konsolenprogramme die zum Teil der CRT Unit von Turbo Pascal nachempfunden sind.
Da eine jede eingebundene Unit extra Overhead mit sich bringt (das entsteht durch die Compilertechnologie, die verschiedene Informationen bei Units zwischenspeichert, die dann nicht mehr entfernt werden), nutzen wir INCLUDE Dateien.
PROG_MAIN sieht wie folgt aus:

procedure PROG_MAIN;
begin
  case paramcount OF
    0:startasservice;
  else begin
      GetModuleFileName(hInstance, @modname[0], MAX_PATH);
      Getlasterror;
      param:=paramstr(1);
      case param[1]='/' OF
        true: begin
            currtextattr:=textattribute;
            settextattribute(FOREGROUND_GREEN or FOREGROUND_INTENSITY);
            writeln(cmd_header);
            settextattribute(currtextattr);
            case param[2] of
              'I', 'i':begin
                  currtextattr:=textattribute;
                  settextattribute(FOREGROUND_BLUE or
                    FOREGROUND_INTENSITY);
                  StartupMode:=SERVICE_DEMAND_START;
                  if length(param)>2 then
                    case param[3] of
                      'A', 'a':startupMode:=SERVICE_AUTO_START;
                    end
                    else StartupMode:=SERVICE_DEMAND_START;
                  case startupMode of
                    SERVICE_AUTO_START:writeln(cmd_install+
                    'n autostart service');
                    SERVICE_DEMAND_START:writeln(cmd_install+
                    ' manual start service');
                  end;
                  settextattribute(currtextattr);
                  hSCM:=OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
                  case hSCM of
                    0:FatalError;
                  else begin
                      hService:=CreateService(hSCM,
                        PChar(ServiceName),
                        PChar(DisplayName),
                        SERVICE_START OR SERVICE_QUERY_STATUS OR _DELETE,
                        SERVICE_WIN32_OWN_PROCESS,
                        StartupMode,
                        SERVICE_ERROR_NORMAL,
                        @modname[0],
                        NIL, NIL, NIL, NIL, NIL);
                      case hService of
                        0:begin
                            CloseServiceHandle(hSCM);
                            FatalError;
                          end;
                      else begin
                          CloseServiceHandle(hSCM);
                          CloseServiceHandle(hService);
                          writeln(frmt(cmd_installed, [pchar(servicename),
                          NIL]));
                        end;
                      end;
                    end;
                  end;
                end;
              'U', 'u':begin
                  currtextattr:=textattribute;
                  settextattribute(FOREGROUND_BLUE or
                    FOREGROUND_INTENSITY);
                  writeln('Attempting to uninstall "'+servicename+'"');
                  settextattribute(currtextattr);
                  hSCM:=OpenSCManager(NIL, NIL, SC_MANAGER_ALL_ACCESS);
                  case hSCM of
                    0:FatalError;
                  else begin
                      hService:=OpenService(hSCM, PChar(Servicename),
                      SERVICE_ALL_ACCESS);
                      case hService of
                        0:begin
                            CloseServiceHandle(hSCM);
                            FatalError;
                          end;
                      else begin
                          startupMode:=
                            integer(DeleteService(hService));
                          CloseServiceHandle(hService);
                          CloseServiceHandle(hSCM);
                          case startupMode of
                            0:FatalError;
                          else
                            writeln(frmt(cmd_installed,
                            [pchar(servicename), PChar('un')]));
                          end;//case
                        end;
                      end;//case
                    end;
                  end;//case
                end;
            else showsyntax;
              begin

              end;
            end;//case
          end;
        false:startasservice;
      end;
    end;
  end;
end;

Es überprüft die übergebenen Parameter und installiert/deinstalliert gegebenenfalls den Service oder eben startet diesen als Service:

procedure startasservice;
begin
  dispatchtable[0].lpservicename:=pchar(servicename);
  dispatchtable[0].lpserviceproc:=@serviceproc;
  dispatchtable[1].lpservicename:=NIL;
  dispatchtable[1].lpserviceproc:=NIL;
  startservicectrldispatcher(dispatchtable[0]);
end;

startservicectrldispatcher() übergibt den Hauptthread des Prozesses an den Service Control Manager (SCM).
Auffällig im Hauptprogramm ist noch die Prozedur SERVICE_MAIN. Sie enthält die eigentliche Hauptschleife des Services, wobei die Variablen STOPPED und PAUSED innerhalb des Programms vordefiniert sind.
Alles innerhalb dieser Prozedur wird also ausgeführt, während der Service als Service läuft.
Download des Beispielservices (Service-Skelett) (ca. 14 KB)