Seite 1 von 1

PBOSL: NTService -- Wie ?

Verfasst: 01.04.2010 18:20
von Tombi
Liebe Community,
ich habe schon die ganze Zeit rumprobiert, gegoogelt, die SuFu benutzt, aber ich bin einfach zu blöd dazu, mein Programm so einzurichten, das es als Dienst funktioniert.

Das Programm wird mit InstallShield installiert und als Dienst eingerichtet.
Der Dienst antwortet jedoch nicht, da das Programm keine Antwort gibt.

Wie baue ich dies nun in meine Anwendung ein?
Das komplette Programm soll als Dienst laufen.

Code: Alles auswählen

;- Allgemeine Werte aus- und festlegen
;

If OpenPreferences("settings.ini")
  PreferenceGroup("Server")
    Global Port = ReadPreferenceLong ("Port", 6832)
    
  PreferenceGroup("Text")
    ERROR_CREATE_SERVER$ = ReadPreferenceString("ERROR_CREATE_SERVER", "Can't create the server (port in use ?).")
    ERROR_NETWORK_INITIALIZE$ = ReadPreferenceString("ERROR_NETWORK_INITIALIZE", "Can't initialize the network !")
    
  ClosePreferences()
Else
  MessageRequester("Fehler", "Die Einstellungsdatei konnte nicht geöffnet werden.", 0)
  End
EndIf

;- Netzwerk einrichten
;
If InitNetwork() = 0
  MessageRequester("Error", ERROR_NETWORK_INITIALIZE$, 0)
  End
EndIf

;- Prozedure zum Herunterfahren des Rechners vorbereiten
;
Procedure EnableShutDown()
  Protected Privileges.TOKEN_PRIVILEGES
  Protected hToken.i
  OpenProcessToken_(GetCurrentProcess_(), #TOKEN_ADJUST_PRIVILEGES  | #TOKEN_QUERY, @hToken)
  Privileges\PrivilegeCount           = 1
  Privileges\Privileges[0]\Attributes = #SE_PRIVILEGE_ENABLED
  LookupPrivilegeValue_(0, "SeShutdownPrivilege", @Privileges\Privileges[0]\Luid)
  AdjustTokenPrivileges_(hToken, 0, @Privileges, 0, 0, 0)
  CloseHandle_(hToken)
EndProcedure

;- Speicher (Buffer) einrichten
;
*Buffer = AllocateMemory(1000)


;-----------------------------------------
;- START
;-----------------------------------------

If CreateNetworkServer(0, Port)
  
  Repeat
      
    SEvent = NetworkServerEvent()
  
    If SEvent
    
      ClientID = EventClient()
  
      Select SEvent
  
        Case 2
          ReceiveNetworkData(ClientID, *Buffer, 1000)
          
          If PeekS(*Buffer) = "SHUTDOWN"
            SendNetworkString(ClientID, "SHUTDOWN_OK")
            EnableShutDown()
            ExitWindowsEx_(#EWX_SHUTDOWN | #EWX_FORCE, 0)
          EndIf
          
      EndSelect
    EndIf
    
    Delay(1000)
    
  Until Quit = 1

  CloseNetworkServer(0)
  
Else
  MessageRequester("Fehler", ERROR_CREATE_SERVER$, 0)
EndIf

  
End   

Re: PBOSL: NTService -- Wie ?

Verfasst: 01.04.2010 18:41
von bobobo

Re: PBOSL: NTService -- Wie ?

Verfasst: 01.04.2010 19:01
von Tombi
Irgendwie komme ich mit dem ganzen nicht klar.
Kannst du mir vielleicht da einen Ansatz geben?
Zum Beispiel bei deinem genannten zweiten Beispiel, muss ich da nicht noch was verändern, oder einfach vor meinem Programm mit einfügen?

MFG Tombi

Re: PBOSL: NTService -- Wie ?

Verfasst: 12.04.2010 17:16
von bobobo
Ansatz ? ich hab die Links aus der Suche und dachte eigentlich, dass da was brauchbares dabei wäre.
Getestet habe ich da allerdings nix.

Im Allgemeinen macht ein Service ja Sachen, die keinen Nutzereingriff benötigen und ohne direkten
Screenoutput, da so ein Dienst ja gerne unter dem SystemKonto laufen soll und das hat eben keinen
Screen. Kommunizieren kann man mit sowas über die Netzwerkfunktionalität (pb kann das ja ganz gut)
oder auch z.B. mit externen Dateien in die der Dienst reinschreibt und aus der der Dienst liest.

Das, was da auszuführen ist, kann im Dienstprogramm an relevanter Stelle selber drinstehen oder als
externes Programm aufgerufen werden. Runprogram() unterstützt die Kontrolle über das aufgerufene
Programm, so dass hier eine Kontrollmöglichkeit über das aufgerufene Programm gegeben wäre.

Die relevante Stelle wäre im code aus dem zweiten Link die Stelle an der

Code: Alles auswählen

 Repeat
; Service Executing Code
   WritetoLog("Looping on Service Executing Code")
   Delay(10000)
   Until ServiceStatus\dwCurrentState <> #SERVICE_RUNNING
steht und zwar statt ; Service Executing Code

Dieser Code läuft allerdings irgendwie nicht richtig (warum auch immer).

Es gibt da noch die PB-OSL (PureBasicOpenSourceLibs) unter pbosl.purearea.net,
Dabei ist auch eine lib NTService und da der Source dabei ist, kannst Du dort
auch mal reinschauen.

Re: PBOSL: NTService -- Wie ?

Verfasst: 12.04.2010 21:15
von AndyMars
Ich hab mit dem Beispiel vom letzten Link von bobobo etwas rumgespielt - scheint mir durchaus zu funktionieren...

Frag mich jetzt nur nicht, wie man korrekt den eigenen Code integriert! Ich würde es so machen, dass ich meinen Code in ServiceProcedure() einfügen und entsprechend auf den Status reagieren würde.
Ausserdem sollte man daran denken, keine GUI-Ausgaben zu machen, d.h. keine MessageRequester etc. - Dienste machen das nicht, besonders nicht unter Windows Vista/7 - so wie ich das mitbekommen hab... ^^

In ServiceFramework.pbi müsste man evtl. in ServiceMain() den Sleep_(100) entfernen, je nach dem was man genau machen will (allerdings nicht für ExampleService.pb!). Fragt sich noch, ob man ServiceInit() verändern dürfte, denn diese Prozedur wird offenbar auch beim Stoppen des Dienstes ausgeführt. Dort würde zum Beispiel dein CloseNetworkServer(0) hin gehören... imho...
Achja - die Deklarationen der Konstanten sind evtl. überflüssig - hab mich aber nicht getraut, die zu entfernen. Die einzige die PB moniert hat, habe ich auskommentiert...

Hier noch die unter PB 4.40 lauffähigen Codes (WinXP 32):

ServiceFramework.pbi

Code: Alles auswählen

;Originalcode from  akee
;http://www.purebasic.fr/english/viewtopic.php?p=84001#p84001
;Converted for PureBasic 4.40

Global AppHome.s
Global AppExeName.s
Global AppExeFileName.s
Global AppIniFileName.s

AppIniFileName = ProgramFilename()
AppHome.s = GetPathPart(AppIniFileName)
AppExeFileName.s = GetFilePart(AppIniFileName)
AppExeName.s = Left(AppExeFileName, Len(AppExeFileName) - Len(GetExtensionPart(AppIniFileName)) - 1)
AppIniFileName.s = AppExeName + ".ini"

#READ_CONTROL = $20000
#STANDARD_RIGHTS_ALL = $1F0000
#STANDARD_RIGHTS_EXECUTE = (#READ_CONTROL)
#STANDARD_RIGHTS_READ = (#READ_CONTROL)
#STANDARD_RIGHTS_REQUIRED = $F0000
#STANDARD_RIGHTS_WRITE = (#READ_CONTROL)

#SERVICE_WIN32_OWN_PROCESS = $10
#SERVICE_WIN32_SHARE_PROCESS = $20
#SERVICE_WIN32 = #SERVICE_WIN32_OWN_PROCESS + #SERVICE_WIN32_SHARE_PROCESS

#SC_MANAGER_CONNECT = $1
#SC_MANAGER_CREATE_SERVICE = $2
#SC_MANAGER_ENUMERATE_SERVICE = $4
#SC_MANAGER_LOCK = $8
#SC_MANAGER_QUERY_LOCK_STATUS = $10
#SC_MANAGER_MODIFY_BOOT_CONFIG = $20
#SC_MANAGER_ALL_ACCESS_2 = (#STANDARD_RIGHTS_REQUIRED | #SC_MANAGER_CONNECT | #SC_MANAGER_CREATE_SERVICE | #SC_MANAGER_ENUMERATE_SERVICE | #SC_MANAGER_LOCK | #SC_MANAGER_QUERY_LOCK_STATUS | #SC_MANAGER_MODIFY_BOOT_CONFIG)

#SERVICE_ACCEPT_STOP = $1
#SERVICE_ACCEPT_PAUSE_CONTINUE = $2
#SERVICE_ACCEPT_SHUTDOWN = $4
#SERVICE_ACTIVE = $1
#SERVICE_ALL_ACCESS_2 = (#STANDARD_RIGHTS_REQUIRED | #SERVICE_QUERY_CONFIG | #SERVICE_CHANGE_CONFIG | #SERVICE_QUERY_STATUS | #SERVICE_ENUMERATE_DEPENDENTS | #SERVICE_START | #SERVICE_STOP | #SERVICE_PAUSE_CONTINUE | #SERVICE_INTERROGATE | #SERVICE_USER_DEFINED_CONTROL)
#SERVICE_CHANGE_CONFIG = $2
#SERVICE_CONTINUE_PENDING = $5
#SERVICE_CONTROL_CONTINUE = $3
#SERVICE_CONTROL_INTERROGATE = $4
#SERVICE_CONTROL_PAUSE = $2
#SERVICE_CONTROL_SHUTDOWN = $5
#SERVICE_CONTROL_STOP = $1
#SERVICE_DEMAND_START = $3
#SERVICE_ENUMERATE_DEPENDENTS = $8
#SERVICE_ERROR_NORMAL = $1
#SERVICE_INACTIVE = $2
#SERVICE_INTERROGATE = $80
;#SERVICE_NO_CHANGE = $FFFF
#SERVICE_PAUSE_CONTINUE = $40
#SERVICE_PAUSE_PENDING = $6
#SERVICE_PAUSED = $7
#SERVICE_QUERY_CONFIG = $1
#SERVICE_QUERY_STATUS = $4
#SERVICE_RUNNING = $4
#SERVICE_START = $10
#SERVICE_START_PENDING = $2
#SERVICE_STATE_ALL = (#SERVICE_ACTIVE | #SERVICE_INACTIVE)
#SERVICE_STOP = $20
#SERVICE_STOP_PENDING = $3
#SERVICE_STOPPED = $1
#SERVICE_USER_DEFINED_CONTROL = $100
#SERVICE_CONFIG_DESCRIPTION = $1
#SERVICE_CONFIG_FAILURE_ACTIONS = $2


Global X_SERVICE_STATUS.SERVICE_STATUS
Global X_SERVICE_STATUS_HANDLE.i
Global X_SERVICE_RUNNING.i


Declare ServiceProcedure(ServiceState.i)
;
;
;
Procedure _WriteLog(LogMessage.s)
  T_LOGFILE.s = AppHome + AppExeName + ".log"
  T_HANDLE.i
  If FileSize(T_LOGFILE) = -1
    T_HANDLE = CreateFile(#PB_Any, T_LOGFILE)
  Else
    T_HANDLE = OpenFile(#PB_Any, T_LOGFILE)
    FileSeek(T_HANDLE,Lof(T_HANDLE))
  EndIf
  WriteStringN(T_HANDLE,FormatDate("%yyyy/%mm/%dd,%hh:%ii:%ss,", Date()) + LogMessage)
  CloseFile(T_HANDLE)
EndProcedure

;
;
;
Procedure ServiceHandler(OperationCode.i)
  Select OperationCode
    Case #SERVICE_CONTROL_PAUSE
      X_SERVICE_STATUS\dwCurrentState = #SERVICE_PAUSED
      SetServiceStatus_(X_SERVICE_STATUS_HANDLE, X_SERVICE_STATUS)
    Case #SERVICE_CONTROL_CONTINUE
      X_SERVICE_STATUS\dwCurrentState = #SERVICE_RUNNING
      SetServiceStatus_(X_SERVICE_STATUS_HANDLE, X_SERVICE_STATUS)
    Case #SERVICE_CONTROL_STOP
      X_SERVICE_STATUS\dwWin32ExitCode = 0
      X_SERVICE_STATUS\dwCheckPoint = 0
      X_SERVICE_STATUS\dwWaitHint = 0
      X_SERVICE_STATUS\dwCurrentState = #SERVICE_STOPPED
      SetServiceStatus_(X_SERVICE_STATUS_HANDLE, X_SERVICE_STATUS)
      X_SERVICE_RUNNING = #False
    Case #SERVICE_CONTROL_INTERROGATE
      X_SERVICE_RUNNING = #False
  EndSelect
EndProcedure

;
;
;
Procedure ServiceMain()
  X_SERVICE_STATUS\dwServiceType = #SERVICE_WIN32
  X_SERVICE_STATUS\dwCurrentState = #SERVICE_START_PENDING
  X_SERVICE_STATUS\dwControlsAccepted = #SERVICE_ACCEPT_STOP | #SERVICE_ACCEPT_PAUSE_CONTINUE
  X_SERVICE_STATUS\dwWin32ExitCode = 0
  X_SERVICE_STATUS\dwServiceSpecificExitCode = 0
  X_SERVICE_STATUS\dwCheckPoint = 0
  X_SERVICE_STATUS\dwWaitHint = 0

  X_SERVICE_STATUS_HANDLE = RegisterServiceCtrlHandler_(AppExeName, @ServiceHandler())

  X_SERVICE_STATUS\dwCurrentState = #SERVICE_RUNNING
  SetServiceStatus_(X_SERVICE_STATUS_HANDLE, X_SERVICE_STATUS)

  X_SERVICE_RUNNING = #True
  While X_SERVICE_RUNNING
    Sleep_(100)
    ServiceProcedure(X_SERVICE_STATUS\dwCurrentState)
  Wend
EndProcedure

;
;
;
Procedure ServiceInit(ServiceName.s)
  T_TABLE_ENTRY.SERVICE_TABLE_ENTRY
  T_SCMANAGER.i
  T_SERVICE.i = 0
  T_RETURNS.i = 1
  T_PARAMETER.s = Trim(LCase(ProgramParameter()))

  X_SERVICE_RUNNING = #False

  If T_PARAMETER = ""
    T_TABLE_ENTRY\lpServiceName = @AppExeName
    T_TABLE_ENTRY\lpServiceProc = @ServiceMain()
    If StartServiceCtrlDispatcher_(@T_TABLE_ENTRY)
      _WriteLog("Service " + AppExeName + " dispatched.")
      T_RETURNS = 0
    EndIf
  Else
    T_SCMANAGER = OpenSCManager_(0, 0, #SC_MANAGER_ALL_ACCESS_2)
    If T_SCMANAGER
      Select T_PARAMETER
        Case "-i"
          _WriteLog("Installing service " + AppExeName + ".")
          ServiceName = Trim(ServiceName)
          If ServiceName = ""
            ServiceName = AppExeName
          EndIf
          T_SERVICE = CreateService_(T_SCMANAGER, AppExeName, ServiceName, #SERVICE_ALL_ACCESS, #SERVICE_WIN32_OWN_PROCESS, #SERVICE_DEMAND_START, #SERVICE_ERROR_NORMAL, AppHome + AppExeFileName, 0, 0, 0, 0, 0)
          If T_SERVICE
            _WriteLog("Service installed succesfully.")
            T_RETURNS = 0
          Else
            _WriteLog("Error installing service.")
          EndIf
          CloseServiceHandle_(T_SERVICE)
        Case "-u"
          _WriteLog("Uninstalling service " + AppExeName + ".")
          T_SERVICE = OpenService_(T_SCMANAGER, AppExeName, #SERVICE_ALL_ACCESS_2)
          If T_SERVICE
            DeleteService_(T_SERVICE)
            _WriteLog("Service uninstalled succesfully.")
            T_RETURNS = 0
          Else
            _WriteLog("Error uninstalling service.")
          EndIf
          CloseServiceHandle_(T_SERVICE)
        Default
          _WriteLog("Unknown parameter " + T_PARAMETER + ". Use -i to install or -u to uninstall service.")
      EndSelect
    Else
      _WriteLog("Error opening service control manager.")
    EndIf
    CloseServiceHandle_(T_SCMANAGER)
  EndIf
  _WriteLog("Program returned " + Str(T_RETURNS) + ".")
  End T_RETURNS
EndProcedure
ExampleService.pb

Code: Alles auswählen

XIncludeFile "ServiceFramework.pbi"

;
;
;
Procedure ServiceProcedure(State.i)
  Select State
    Case #SERVICE_RUNNING
      _WriteLog("RUNNING:" + Str(State))
    Case #SERVICE_PAUSED
      _WriteLog("PAUSED:" + Str(State))
  EndSelect
EndProcedure


ServiceInit("ExampleService")
Executable erstellen und mit dem Parameter "-i" starten, um es als Dienst zu installieren - mit "-u" um es zu deinstallieren. Nicht laufen lassen - sonst wird die Logdatei irgendwann die Festplatte füllen... :).

Re: PBOSL: NTService -- Wie ?

Verfasst: 12.04.2010 21:34
von ts-soft
Den ersten Teil des ersten Codes sollte man auf ProgramFileName() umstellen.
Die API ist ja seit langer Zeit überflüssig. Ausserdem die Variablentypen auf
Interger umstellen, weil Handle passen nur unter 32-Bit in ein Long und Integer
ist der dafür vorgesehene Typ.

Gruß
Thomas

Re: PBOSL: NTService -- Wie ?

Verfasst: 13.04.2010 00:26
von AndyMars
Hab's grad mal oben entsprechend geändert.

Interessant, Integer in PB kannte ich noch gar nicht... :oops:

Wieder was dazu gelernt...

Edit: Hab alle Long auf Integer geändert... was wohl nicht nötig gewesen wäre... macht es was?

Re: PBOSL: NTService -- Wie ?

Verfasst: 13.04.2010 09:56
von ts-soft
AndyMars hat geschrieben: Edit: Hab alle Long auf Integer geändert... was wohl nicht nötig gewesen wäre... macht es was?
Normallerweise ist das besser so, es sei denn, es werden explicit Longs gebraucht, was eher
sehr selten ist.

Re: PBOSL: NTService -- Wie ?

Verfasst: 22.07.2010 13:25
von Tombi
Dann hol ich wohl doch nochmals meinen alten Thread raus /:->

Momentan probiere ich es mit folgendem Code:
http://www.purebasic.fr/english/viewtop ... 33#p310833

Bekomme soweit eigentlich alles hin.
Nur immer wenn ich den Dienst starten möchte, beschwert sich Windoof,
das der Dienst nicht antwortet.

Wie bekomme ich mein Programm nun dazu, zu antworten? ^^

MFG,
Tombi

Re: PBOSL: NTService -- Wie ?

Verfasst: 23.07.2010 10:50
von grapy
Also bei mir läuft dad: (getestet:PB4.50 Win7/x64)

Code: Alles auswählen

Global AppPath.s = ProgramFilename()
extpart.s = "."+GetExtensionPart(AppPath)
Global PathPart.s = GetPathPart(AppPath)
Global ExeName.s = RemoveString(GetFilePart(AppPath), extpart)

#SERVICE_WIN32_OWN_PROCESS = $10
#SERVICE_WIN32_SHARE_PROCESS = $20
#SERVICE_WIN32 = #SERVICE_WIN32_OWN_PROCESS + #SERVICE_WIN32_SHARE_PROCESS
#SERVICE_ACCEPT_STOP = $1
#SERVICE_ACCEPT_PAUSE_CONTINUE = $2
#SERVICE_ACCEPT_SHUTDOWN = $4
#SC_MANAGER_CONNECT = $1
#SC_MANAGER_CREATE_SERVICE = $2
#SC_MANAGER_ENUMERATE_SERVICE = $4
#SC_MANAGER_LOCK = $8
#SC_MANAGER_QUERY_LOCK_STATUS = $10
#SC_MANAGER_MODIFY_BOOT_CONFIG = $20

#STANDARD_RIGHTS_REQUIRED = $F0000
#SERVICE_QUERY_CONFIG = $1
#SERVICE_CHANGE_CONFIG = $2
#SERVICE_QUERY_STATUS = $4
#SERVICE_ENUMERATE_DEPENDENTS = $8
#SERVICE_START = $10
#SERVICE_STOP = $20
#SERVICE_PAUSE_CONTINUE = $40
#SERVICE_INTERROGATE = $80
#SERVICE_USER_DEFINED_CONTROL = $100
#SERVICE__ALL__ACCESS = #STANDARD_RIGHTS_REQUIRED | #SERVICE_QUERY_CONFIG | #SERVICE_CHANGE_CONFIG | #SERVICE_QUERY_STATUS | #SERVICE_ENUMERATE_DEPENDENTS | #SERVICE_START | #SERVICE_STOP | #SERVICE_PAUSE_CONTINUE | #SERVICE_INTERROGATE |#SERVICE_USER_DEFINED_CONTROL

#SERVICE_DEMAND_START = $3
#SERVICE_ERROR_NORMAL = $1

;- SERVICE_CONTROL
#SERVICE_CONTROL_STOP = $1
#SERVICE_CONTROL_PAUSE = $2
#SERVICE_CONTROL_CONTINUE = $3
#SERVICE_CONTROL_INTERROGATE = $4
#SERVICE_CONTROL_SHUTDOWN = $5


;-SERVICE_STATE
#SERVICE_STOPPED = $1
#SERVICE_START_PENDING = $2
#SERVICE_STOP_PENDING = $3
#SERVICE_RUNNING = $4
#SERVICE_CONTINUE_PENDING = $5
#SERVICE_PAUSE_PENDING = $6
#SERVICE_PAUSED = $7

Global ServiceStatus.SERVICE_STATUS, hServiceStatus.l, SERVICE_NAME.s, Finish.l
Declare Handler(fdwControl.l)
Declare ServiceMain(dwArgc.l, lpszArgv.l)

;##############################################################################
Procedure WriteLog(Value.s)
  sfile.s = PathPart+ExeName+".log"
  If OpenFile(0, sfile)
    FileSeek(0, Lof(0))  
    WriteString(0, Value+Chr(13)+Chr(10))
    CloseFile(0)
  EndIf
EndProcedure
;##############################################################################

Procedure Main() 
  hSCManager.l
  hService.l
  ServiceTableEntry.SERVICE_TABLE_ENTRY
  b.l
  cmd.s
  ;Change SERVICE_NAME and app name as needed
  ;AppPath.s = "C:\AAA_NTService.exe"
  SERVICE_NAME = ExeName
  
  cmd = Trim(LCase(ProgramParameter()))
  Select cmd
  Case "install" ;Install service on machine
    hSCManager = OpenSCManager_(0, 0, #SC_MANAGER_CREATE_SERVICE)
    hService = CreateService_(hSCManager, SERVICE_NAME, SERVICE_NAME, #SERVICE__ALL__ACCESS, #SERVICE_WIN32_OWN_PROCESS, #SERVICE_DEMAND_START, #SERVICE_ERROR_NORMAL, AppPath, 0, 0, 0, 0, 0)
    CloseServiceHandle_(hService)
    CloseServiceHandle_(hSCManager)
    Finish = 1
  Case "uninstall" ;Remove service from machine
    hSCManager = OpenSCManager_(0, 0, #SC_MANAGER_CREATE_SERVICE)
    hService = OpenService_(hSCManager, SERVICE_NAME, #SERVICE__ALL__ACCESS)
    DeleteService_(hService)
    CloseServiceHandle_(hService)
    CloseServiceHandle_(hSCManager)
    Finish = 1
    Default
    *sname.s = SERVICE_NAME
    ;Start the service
    ServiceTableEntry\lpServiceName = @SERVICE_NAME
    ServiceTableEntry\lpServiceProc = @ServiceMain()
    b = StartServiceCtrlDispatcher_(@ServiceTableEntry)
    ;WriteLog("Starting Service bResult=" + Str(b))
    If b = 0
      Finish = 1
    EndIf
  EndSelect 
  Repeat
;##############################################################################    
    Delay(1)
;##############################################################################    
  Until Finish =1 
  End 
EndProcedure

Procedure Handler(fdwControl.l)
  b.l
  Select fdwControl
  Case #SERVICE_CONTROL_PAUSE
    ;** Do whatever it takes To pause here.
    ServiceStatus\dwCurrentState = #SERVICE_PAUSED
  Case #SERVICE_CONTROL_CONTINUE
    ;** Do whatever it takes To continue here.
    ServiceStatus\dwCurrentState = #SERVICE_RUNNING
  Case #SERVICE_CONTROL_STOP
    ServiceStatus\dwWin32ExitCode = 0
    ServiceStatus\dwCurrentState = #SERVICE_STOP_PENDING
    ServiceStatus\dwCheckPoint = 0
    ServiceStatus\dwWaitHint = 0 ;Might want a time estimate
    b = SetServiceStatus_(hServiceStatus, ServiceStatus)
    ;** Do whatever it takes to stop here.
    Finish = 1
    ServiceStatus\dwCurrentState = #SERVICE_STOPPED
  Case #SERVICE_CONTROL_INTERROGATE
    ;Fall through To send current status.
    Finish = 1
    ;Else
  EndSelect
  ;Send current status.
  b = SetServiceStatus_(hServiceStatus, ServiceStatus)
EndProcedure

Procedure ServiceMain(dwArgc.l, lpszArgv.l)
  b.l
  ;WriteLog("ServiceMain")
  ;Set initial state
  ServiceStatus\dwServiceType = #SERVICE_WIN32_OWN_PROCESS
  ServiceStatus\dwCurrentState = #SERVICE_START_PENDING
  ServiceStatus\dwControlsAccepted = #SERVICE_ACCEPT_STOP | #SERVICE_ACCEPT_PAUSE_CONTINUE | #SERVICE_ACCEPT_SHUTDOWN
  ServiceStatus\dwWin32ExitCode = 0
  ServiceStatus\dwServiceSpecificExitCode = 0
  ServiceStatus\dwCheckPoint = 0
  ServiceStatus\dwWaitHint = 0
  
  hServiceStatus = RegisterServiceCtrlHandler_(SERVICE_NAME, @Handler())
  ServiceStatus\dwCurrentState = #SERVICE_START_PENDING
  b = SetServiceStatus_(hServiceStatus, ServiceStatus)
  
  ;** Do Initialization Here
  
  ServiceStatus\dwCurrentState = #SERVICE_RUNNING
  b = SetServiceStatus_(hServiceStatus, ServiceStatus)
  
;##############################################################################
  Repeat
    Delay(1000)
    x+1
    WriteLog("Repeat"+Str(x))
  ForEver 
;##############################################################################
  
  ;** Perform tasks -- If none exit
  
  ;** If an error occurs the following should be used for shutting
  ;** down:
  ; SetServerStatus SERVICE_STOP_PENDING
  ; Clean up
  ; SetServerStatus SERVICE_STOPPED
EndProcedure

Main()
Hab den Code schon ewig und anscheinend gehts immer noch.