[Rückfrage] Dienst unter Windows starten

Für allgemeine Fragen zur Programmierung mit PureBasic.
MenschMarkus
Beiträge: 220
Registriert: 30.04.2009 21:21
Computerausstattung: i5-2300 (2.8 Ghz) Win10 -64bit / PB 5.73 LTS

[Rückfrage] Dienst unter Windows starten

Beitrag von MenschMarkus »

Hallo zusammen.

Suche nach einer Möglichkeit eines meiner Programme als Windows Dienst zu starten. Auf meiner Suche im Forum bin ich auf diesen Beitrag von mk-soft gestoßen. Diesen code wollte ich dann mal testen.
Ich kenne mich mit Diensten und der Steuerung nicht wirklich aus und benötige etwas Starthilfe.
Ich brauche einfach mal ein Beispiel wie ich ein Programm als Dienst einbinden und kontrollieren kann. Mein folgender Versuch hat den Dienst zwar installiert bzw deinstalliert, zum laufen habe ich das Ding aber nicht gebracht, ganz zu Schweigen von der Kontrolle via Taskmanager.
Hier mein Versuch

Code: Alles auswählen

;==========================================
; Service Test (HowTo)
;==========================================
;
;
Structure service
  service_status.i ;0=starten, 1=gestoppet, 2=beenden, -1 = exit
  programid.l
EndStructure
Declare programfunction()
Global service.service
XIncludeFile "A3ServiceBaseProgram.pb"
svMain("install")
svInit()

Procedure programfunction()
  Protected i.i
  Repeat
    Debug Str(i)
    Delay(1000)
    Select service\service_status
      Case 0      ;Service wurde gestartet
        i+1
      Case 1
        Repeat    ;Service wurde pausiert
          If service\service_status = 0 : Break : EndIf
          Delay(100)
        ForEver
      Case 2      ;Service wurde beendet
        service\service_status = -1
        Break
    EndSelect
  ForEver
EndProcedure

Repeat        
  Delay(100)
Until service\service_status = -1
End
; IDE Options = PureBasic 5.70 LTS (Windows - x86)
; CursorPosition = 12
; Folding = --
; EnableAsm
; EnableThread
; EnableXP
; EnableAdmin
; EnableOnError
; CompileSourceDirectory
; EnablePurifier
; EnableCompileCount = 23
; EnableBuildCount = 0
; EnableExeConstant
Der Programmteil thMain() wurde wir folgt geändert

Code: Alles auswählen

Procedure thMain(id)
 
  ; Global code for init
  WriteLog("Thread - Init")
       
  Repeat
    Select thCommand
      Case #SERVICE_START
        thCommand = 0
        ; Code for start
        programfunction()                      <-- startet die Procedure
        WriteLog("Thread - Start")
       
      Case #SERVICE_CONTROL_CONTINUE
        thCommand = 0
        ; Code for contine
        service\service_status = 0
        WriteLog("Thread - Continue")
       
      Case #SERVICE_CONTROL_PAUSE
        thCommand = 0
        ; Code for pause before exit
        Service\service_status = 1           <-- setzt den Prüfwert auf pausieren
        WriteLog("Thread - Pause")
       
        Break
       
      Case #SERVICE_CONTROL_STOP
        thCommand = 0
        ; Code for stop before exit
        service\service_status = 1            <-- setzt den Prüfwert auf pausieren
        WriteLog("Thread - Stop")
       
        Break
       
      Case #SERVICE_CONTROL_SHUTDOWN
        thCommand = 0
        ; Code for shutdown before exit
        service\service_status = 2            <-- setzt den Prüfwert auf beenden
        WriteLog("Thread - Shutdown")
       
        Break
       
      Default
        ; Any cycle code
        Delay(100)
       
    EndSelect
   
  ForEver
 
  ; Global code for exit
  WriteLog("Thread - Exit")
       
EndProcedure
Wie oben ersichtlich wird das Programm mit Admin Rechten gestartet. Starte ich das Programm erscheint der Dienst zwar im Task Manager, dort steht aber als Status "beendet", obwohl mir der Debugger schön immer einen Wert ausgibt.
Ich vermute mal, dass ich svInit() nicht direkt aufrufen darf sondern

Code: Alles auswählen

svServiceMain(dwArgc.i, lpszArgv.i)
verwenden muss. Nur welche Argumente übergebe ich hier und wo bekomme ich die her?
(Die werden im Übrigen in der procedure nicht aufgerufen !??)
Oder liege ich hier ganz falsch?
Den Dienst über den Taskmanager irgendwie zu beeinflussen schlägt fehl. Hier kommt die Meldung, dass sich das Programm nicht zurückmeldet.

wie funktioniert das denn?
Zuletzt geändert von MenschMarkus am 01.08.2019 20:54, insgesamt 2-mal geändert.
Wissen schadet nur dem, der es nicht hat !
Benutzeravatar
RSBasic
Admin
Beiträge: 8022
Registriert: 05.10.2006 18:55
Wohnort: Gernsbach
Kontaktdaten:

Re: Dienst unter Windows starten

Beitrag von RSBasic »

Ich habe mich mit dem Code von mk-soft nicht beschäftigt. Ich kenne aber die WinAPI-Funktion zum Starten eines Dienstes und zwar: StartService_()
Beispiel:

Code: Alles auswählen

EnableExplicit

Procedure Service_Start(Name$)
  Protected hSCManager, hServ, lResult
  hSCManager = OpenSCManager_(#Null, #Null, #GENERIC_READ	| #SC_MANAGER_CONNECT)
  If hSCManager
    hServ = OpenService_(hSCManager, Name$, #GENERIC_EXECUTE)
    If hServ
      lResult = StartService_(hServ, 0, #Null)
      CloseServiceHandle_(hServ)
    EndIf
    CloseServiceHandle_(hSCManager)
  EndIf
  If hServ And lResult
    ProcedureReturn #True
  EndIf
  ProcedureReturn #False
EndProcedure

Service_Start("Themes");Administratorenrechte erforderlich
Du kannst den Code mit deinem Code bzw. mit dem Code von mk-soft vergleichen. Vielleicht findest du da schon den Fehler oder Unterschied.
Aus privaten Gründen habe ich leider nicht mehr so viel Zeit wie früher. Bitte habt Verständnis dafür.
Bild
Bild
MenschMarkus
Beiträge: 220
Registriert: 30.04.2009 21:21
Computerausstattung: i5-2300 (2.8 Ghz) Win10 -64bit / PB 5.73 LTS

Re: Dienst unter Windows starten

Beitrag von MenschMarkus »

@RSBasic
Danke für die blitzschnelle Rückantwort.
Ich kann Deinem Code folgen. Er kann einen Dienst (hier: Name$) starten. Was mir aber gänzlich fehlt ist die Funktionsweise was passiert wenn ich den Dienst anhalten oder beenden möchte. Auch finde ich die Einbindung des funktionalen codes als solches in Deinem Snippet nicht.
Wie gesagt, ich habe keine Ahnung von Diensten und deren Steuerung.

Könntest Du vielleicht mal mein Beispiel nehmen und diesen als Dienst installieren, starten, stoppen?
Dann kann ich sehen wie das funktioniert.

Vielen Dank schon mal im Voraus.
Wissen schadet nur dem, der es nicht hat !
Benutzeravatar
RSBasic
Admin
Beiträge: 8022
Registriert: 05.10.2006 18:55
Wohnort: Gernsbach
Kontaktdaten:

Re: Dienst unter Windows starten

Beitrag von RSBasic »

Die meisten Codes findest du in meiner WinAPI Library in der Kategorie "Dienste":
  • Dienst auf Verfügbarkeit überprüfen
  • Dienst beenden
  • Dienst entfernen
  • Dienst erstellen
  • Dienst pausieren
  • Dienst starten
https://www.rsbasic.de/winapi-library/
MenschMarkus hat geschrieben:Könntest Du vielleicht mal mein Beispiel nehmen und diesen als Dienst installieren, starten, stoppen?
Wenn ich irgendwann mehr Zeit habe, kann ich deinen Code genauer anschauen, aber gerade nicht.
Jemand aus dem Forum kann dir bestimmt schneller helfen und deinen Code direkt testen und anpassen.
Aus privaten Gründen habe ich leider nicht mehr so viel Zeit wie früher. Bitte habt Verständnis dafür.
Bild
Bild
MenschMarkus
Beiträge: 220
Registriert: 30.04.2009 21:21
Computerausstattung: i5-2300 (2.8 Ghz) Win10 -64bit / PB 5.73 LTS

Re: Dienst unter Windows starten

Beitrag von MenschMarkus »

Dein Sammlung ist mir wohl bekannt und ist wirklich sehr wertvoll.
Könnte man im Code je noch eine Zeile
;Hier Code für starten/stoppen/beenden
an passender Stelle einfügen?

Dann teste ich auch mal gerne Deine Variante aus.
Wissen schadet nur dem, der es nicht hat !
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Dienst unter Windows starten

Beitrag von mk-soft »

Wenn du mein Beispiel verwendest, darfst du von dem Code NICHTS entfernen.
Alles was dort drin ist, ist auch erforderlich um den Dienst am laufen zu bekommen.

Anzupassen ist in svMain() der gewünschte SERVICE_NAME und SERVICE_DESCRIPTION

Die eigene Programmierung was der Dienst machen soll erfolgt im thMain()
Dabei darf der Select - Case - Default - EndSelect nicht geändert werden.

Aktionen werden dort nur hinzugefügt was dein Programm durchführen soll.
Im Bereich 'Default' von thMain() sollte IMMER ein kleiner Delay(x) drin sein, Sonst kocht deine CPU

Nach dem Kompilieren und den Aufruf mit dem Parameter /i für Install Service
kannst du Deinen Dienst unter Systemsteuerung > Verwaltung > Dienste auch finden
und starten, sowie Konfigurieren

P.S.
Compiler-Option ThreadSafe nicht vergessen.
Compiler-Option Administrator-Modus einschalten. Hilft bei Install und Uninstall vom Dienst.

Habe mein Code noch mal getestet. Läuft immer noch.
Logs stehen im Verzeichnis %ProgramData%\MyService :wink:

P.P.S
Die Repeat Forever Schleife im thMain() darf nicht blockiert werden, da sonst der Dienst auf Starten, stoppen, etc nicht mehr reagiert.
Man kann aber zum Beispiel beim Case #SERVICE_START auch wieder eigene Threads starten und
bei #SERVICE_CONTROL_STOP , #SERVICE_CONTROL_SHUTDOWN wieder die eigene Threads beenden.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
MenschMarkus
Beiträge: 220
Registriert: 30.04.2009 21:21
Computerausstattung: i5-2300 (2.8 Ghz) Win10 -64bit / PB 5.73 LTS

Re: Dienst unter Windows starten

Beitrag von MenschMarkus »

mk-soft hat geschrieben:Wenn du mein Beispiel verwendest, darfst du von dem Code NICHTS entfernen.
Alles was dort drin ist, ist auch erforderlich um den Dienst am laufen zu bekommen.
ist folgendes legitim:

1. Der Aufruf "svMain()" am Ende Deines Codes auskommentiert und in mein Code eingesetzt
Mein Code sieht jetzt wie folgt aus:
mk-soft hat geschrieben:Anzupassen ist in svMain() der gewünschte SERVICE_NAME und SERVICE_DESCRIPTION
hab ich gemacht
mk-soft hat geschrieben:Im Bereich 'Default' von thMain() sollte IMMER ein kleiner Delay(x) drin sein, Sonst kocht deine CPU
ist vorhanden, hab ich vom Original übernommen
mk-soft hat geschrieben:Compiler-Option ThreadSafe nicht vergessen.
Compiler-Option Administrator-Modus einschalten. Hilft bei Install und Uninstall vom Dienst.
ist eingeschaltet
mk-soft hat geschrieben:Die Repeat Forever Schleife im thMain() darf nicht blockiert werden, da sonst der Dienst auf Starten, stoppen, etc nicht mehr reagiert.
Man kann aber zum Beispiel beim Case #SERVICE_START auch wieder eigene Threads starten
OK das mit den Threads hatte ich noch nicht. Jetzt kann ich zumindest den Dienst starten, erhalte aber folgende Fehlermeldung:
Von einem auf diesem Computer ausgeführten Programm wir versucht, eine Meldung anzuzeigen
Programme und Geräte, für die ein Eingreifen angefordert wird
Programmpfad: C:\PureBasicProjekt\DienstTest\PureBasic_Compilation0.exe
Dieses Problem tritt auf, wenn ein Programm nicht vollständig kompatibel mit Windows ist. Wenden Sie sich an den Programm- oder Gerätehersteller, um weitere Informationen zu erhalten.
und jetzt?
Wissen schadet nur dem, der es nicht hat !
Benutzeravatar
HeX0R
Beiträge: 2954
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win10 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2
Kontaktdaten:

Re: Dienst unter Windows starten

Beitrag von HeX0R »

Willst Du mit dem Dienst irgendwelche Meldungen ausgeben?
Das geht nicht mehr, glaube seit Windows 7.

Ausserdem solltest Du eine exe Datei kompilieren, und nicht per IDE-Aufruf starten.

Ach ja, das hier nutze ich i.d.R. für Dienste:
https://www.purebasic.fr/english/viewto ... 40&t=56178
MenschMarkus
Beiträge: 220
Registriert: 30.04.2009 21:21
Computerausstattung: i5-2300 (2.8 Ghz) Win10 -64bit / PB 5.73 LTS

Re: Dienst unter Windows starten

Beitrag von MenschMarkus »

HeX0R hat geschrieben:Willst Du mit dem Dienst irgendwelche Meldungen ausgeben?
Das geht nicht mehr, glaube seit Windows 7.
Nein, natürlich nicht. Dienste führen nur etwas aus ohne jegliche Ausgabe.
OK, in meinem Beispiel gebe ich etwas aus, das funktioniert nicht, wie ich auch bereits gemerkt habe. Habe meinen Code dahingehend geändert eine Datei zu öffnen um was hinein zu schreiben. Das sollte auf alle Fälle funktionieren.
Den Link schaue ich mir mal an. Danke schon mal dafür.
Wissen schadet nur dem, der es nicht hat !
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Dienst unter Windows starten

Beitrag von mk-soft »

svMain muss so aufgebaut sein wie er ist, sonst funktioniert der Dienst nicht.

Es gibt regeln wie ein Dienst zu programmieren ist und dazu gehört auch der svMain()
Was anderes darf im Hauptprogramm nicht sein.

Es ist ein wenig aufwendiger einen Dienst zu schreiben...
1. Bei start wird ein Dispatcher eingetragen.
2. Dieser starte dann den ServiceProc (svServiceName)
3.1 Dieser ServiceProc trägt dann den Service Control Handler ein (svHandler)
3.2 Und initialisiert das Programm (svInit startet den eigene Thread)
4.1 Der Service Control Handler (svHandler) steuert dann den Dienst (svPause, svContinue, svStop, svShutdown)
4.2 Über svPause, svStop, etc übergebe mit ich der Variable Thread\Command die anfordern an den thMain weiter.
4.3 Reagiert der Thread thMain nicht auf die Anforderung, wird dieser zwangsweise beendet.

Der thMain() ist somit deine Schnittstelle für deine Aufgaben.

Hier mal die Beta-Version als Module.

Code: Alles auswählen

;- TOP

; Comment : Modul Service Base
; Author1 : ?
; Author2 : mk-soft
; Version : v2.01 Beta
; Update  : 28.07.2019

; Install Service 'Servicename.exe install or /i'
; Uninstall Service 'Servicename.exe uninstall or /u'


EnableExplicit

;- Begin Module Service

DeclareModule Service
  
  Structure udtThreadControl
    ID.i
    Command.i
  EndStructure
  
  Declare svMain()
  Declare svSetServiceName(Name.s)
  Declare svSetServiceDescription(Description.s)
  Declare svSetThread(*MainThread, *ThreadData)
  Declare svWriteLog(Text.s)
  
EndDeclareModule

Module Service
  
  EnableExplicit
  
  ;- Konstanten
  #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_ACCESS2 = #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_INTERACTIVE_PROCESS = $100
  
  #SERVICE_AUTO_START = $2
  #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
  
  #SERVICE_USERDATA_128 = 128
  #SERVICE_USERDATA_129 = 129
  #SERVICE_USERDATA_130 = 130
  #SERVICE_USERDATA_131 = 131
  
  ;- Structuren
  
  ;- IncludeFile´s
  
  ;- Global Variables
  Global ServiceStatus.SERVICE_STATUS
  Global hServiceStatus.i
  Global AppPath.s
  Global AppPathName.s
  Global AppPathLog.s
  Global Finish.i
  Global *UserData
  Global SERVICE_NAME.s = "MyService"
  Global SERVICE_DESCRIPTION.s = "MyServiceDescription"
  Global SERVICE_STARTNAME.s
  Global SERVICE_PASSWORD.s
  
  Global MutexLog
  
  Global *thMainThread
  Global *ThreadControl.udtThreadControl
  
  ;- Declare Function´s
  Declare svHandler(fdwControl.i)
  Declare svServiceMain(dwArgc.i, lpszArgv.i)
  
  Declare svInit()
  Declare svPause()
  Declare svContinue()
  Declare svInterrogate()
  Declare svStop()
  Declare svShutdown()
  Declare svUserdata128()
  Declare svUserdata129()
  Declare svUserdata130()
  Declare svUserdata131()
  Declare MyWriteLog(Text.s)
  
  
  ; *************************************************************************************************
  
  Procedure.s FormatMessage(Errorcode)
    
    Protected *Buffer, len, result.s
    
    len = FormatMessage_(#FORMAT_MESSAGE_ALLOCATE_BUFFER|#FORMAT_MESSAGE_FROM_SYSTEM,0,Errorcode,0,@*Buffer,0,0)
    If len
      result = PeekS(*Buffer, len)
      LocalFree_(*Buffer)
      ProcedureReturn result
    Else
      ProcedureReturn "Errorcode: " + Hex(Errorcode)
    EndIf
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure.s GetSpecialFolder(iCSIDL)
    Protected sPath.s = Space(#MAX_PATH)
    If SHGetSpecialFolderPath_(#Null, @sPath, iCSIDL, 0) = #True
      ProcedureReturn sPath
    Else
      ProcedureReturn ""
    EndIf
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure.s CreateLogFolder(name.s)
    
    Protected path.s
    
    path = GetSpecialFolder(#CSIDL_COMMON_APPDATA)
    If Right(path, 1) <> "\"
      path + "\"
    EndIf
    path + name + "\"
    
    CreateDirectory(path)
    ;MakeSureDirectoryPathExists_(path)
    
    ProcedureReturn path
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure MyWriteLog(Text.s)
    
    If OpenFile(0, AppPathLog)
      FileSeek(0, Lof(0))
      WriteStringN(0, FormatDate("%YYYY-%MM-%DD %HH:%II:%SS : ",Date()) + Text)
      CloseFile(0)
    EndIf
  EndProcedure
  
  Procedure svWriteLog(text.s)
    LockMutex(MutexLog) : MyWriteLog(text) : UnlockMutex(MutexLog)
  EndProcedure
  
  Macro WriteLog(text)
    LockMutex(MutexLog) : MyWriteLog(text) : UnlockMutex(MutexLog)
  EndMacro
  
  ; *************************************************************************************************
  
  Procedure svSetServiceName(ServiceName.s)
    SERVICE_NAME = ServiceName
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svSetServiceDescription(Description.s)
    SERVICE_DESCRIPTION = Description
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svSetThread(*MainThread, *ThreadData)
    *thMainThread = *MainThread
    *ThreadControl = *ThreadData
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svMain()
    
    Protected hSCManager.i
    Protected hService.i
    Protected ServiceTableEntry.SERVICE_TABLE_ENTRY
    Protected lpServiceStatus.SERVICE_STATUS
    Protected lpInfo
    Protected r1.i
    Protected cmd.s
    Protected result
    
    ;Change SERVICE_NAME and app name as needed
    ;-- Service name
    ; SERVICE_NAME = "MyService"
    
    ;-- Service description
    ; SERVICE_DESCRIPTION = "MyService Test"
    
    ;-- Service startname 
    ;--- LocalSystem account (Default)
    SERVICE_STARTNAME = "" ; NULL
    SERVICE_PASSWORD = ""  ; Null
    
    ;--- LocalService account
    ;   SERVICE_STARTNAME = "NT AUTHORITY\LocalService"
    ;   SERVICE_PASSWORD = ""
    
    ;--- NetworkService account
    ;   SERVICE_STARTNAME = "NT AUTHORITY\NetworkService"
    ;   SERVICE_PASSWORD = ""
    
    ;--- DomainName\UserName or .\Username account
    ;   SERVICE_STARTNAME = ".\username"
    ;   SERVICE_PASSWORD = "password"
    
    AppPathName.s = Space(1023)
    GetModuleFileName_(0, AppPathName, 1023)
    
    cmd = Trim(LCase(ProgramParameter()))
    
    Select cmd
        
      Case "install", "/i" ;Install service on machine
        
        Repeat
          result = 0
          hSCManager = OpenSCManager_(0, 0, #SC_MANAGER_CREATE_SERVICE)
          If hSCManager = #Null
            result = GetLastError_()
            Break
          EndIf
          If SERVICE_STARTNAME
            hService = CreateService_(hSCManager, SERVICE_NAME, SERVICE_NAME, #SERVICE_ALL_ACCESS2, #SERVICE_WIN32_OWN_PROCESS, #SERVICE_AUTO_START, #SERVICE_ERROR_NORMAL, AppPathName, 0, 0, 0, SERVICE_STARTNAME, SERVICE_PASSWORD)
          Else ; Local service
            hService = CreateService_(hSCManager, SERVICE_NAME, SERVICE_NAME, #SERVICE_ALL_ACCESS2, #SERVICE_INTERACTIVE_PROCESS | #SERVICE_WIN32_OWN_PROCESS, #SERVICE_AUTO_START, #SERVICE_ERROR_NORMAL, AppPathName, 0, 0, 0, 0, 0)
          EndIf
          
          If hService = #Null
            result = GetLastError_()
            Break
          EndIf
          lpInfo = @SERVICE_DESCRIPTION
          ChangeServiceConfig2_(hService, #SERVICE_CONFIG_DESCRIPTION, @lpInfo)
        Until #True
        
        If hService
          CloseServiceHandle_(hService)
        EndIf
        If hSCManager
          CloseServiceHandle_(hSCManager)
        EndIf
        
        If result
          MessageRequester("Fehler", "Service nicht installiert! " + FormatMessage(result), #MB_ICONSTOP) 
        EndIf
        
        finish = 1
        
      Case "uninstall", "/u" ;Remove service from machine
        
        Repeat
          result = 0
          hSCManager = OpenSCManager_(0, 0, #SC_MANAGER_CREATE_SERVICE)
          If hSCManager = #Null
            result = GetLastError_()
            Break
          EndIf
          hService = OpenService_(hSCManager, SERVICE_NAME, #SERVICE_ALL_ACCESS)
          If hService = #Null
            result = GetLastError_()
            Break
          EndIf
          If QueryServiceStatus_(hService, lpServiceStatus) = #Null
            result = GetLastError_()
            Break
          EndIf
          If lpServiceStatus\dwCurrentState <> #SERVICE_STOPPED
            result = #ERROR_SERVICE_ALREADY_RUNNING
            Break
          EndIf
          If DeleteService_(hService) = #Null
            result = GetLastError_()
            Break
          EndIf
          
        Until #True
        
        If hService
          CloseServiceHandle_(hService)
        EndIf
        If hSCManager
          CloseServiceHandle_(hSCManager)
        EndIf
        
        If result
          MessageRequester("Fehler", "Service nicht deinstalliert! " + FormatMessage(result), #MB_ICONSTOP) 
        EndIf
        
        finish = 1
        
      Default
        ;Start the service
        ServiceTableEntry\lpServiceName = @SERVICE_NAME
        ServiceTableEntry\lpServiceProc = @svServiceMain()
        r1 = StartServiceCtrlDispatcher_(@ServiceTableEntry)
        
        If r1 = 0
          Finish = 1
        EndIf
    EndSelect
    
    Repeat
      Delay(100)
    Until Finish = 1
    
    End
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svHandler(fdwControl.i)
    
    Protected r1.i
    
    Select fdwControl
      Case #SERVICE_CONTROL_PAUSE
        
        ;** Do whatever it takes To pause here.
        If svPause()
          ServiceStatus\dwCurrentState = #SERVICE_PAUSED
        EndIf
        
      Case #SERVICE_CONTROL_CONTINUE
        
        ;** Do whatever it takes To continue here.
        If svContinue()
          ServiceStatus\dwCurrentState = #SERVICE_RUNNING
        EndIf
        
      Case #SERVICE_CONTROL_STOP
        ServiceStatus\dwWin32ExitCode = 0
        ServiceStatus\dwCurrentState = #SERVICE_STOP_PENDING
        ServiceStatus\dwCheckPoint = 0
        ServiceStatus\dwWaitHint = 0 ;Might want a time estimate
        r1 = SetServiceStatus_(hServiceStatus, ServiceStatus)
        
        ;** Do whatever it takes to stop here.
        If svStop()
          Finish = 1
          ServiceStatus\dwCurrentState = #SERVICE_STOPPED
        EndIf
        
      Case #SERVICE_CONTROL_INTERROGATE
        
        ;Fall through To send current status.
        svInterrogate()
        
        
      Case #SERVICE_CONTROL_SHUTDOWN
        ServiceStatus\dwWin32ExitCode = 0
        ServiceStatus\dwCurrentState = #SERVICE_STOP_PENDING
        ServiceStatus\dwCheckPoint = 0
        ServiceStatus\dwWaitHint = 0 ;Might want a time estimate
        r1 = SetServiceStatus_(hServiceStatus, ServiceStatus)
        
        ;** Do whatever it takes to stop here.
        If svShutdown()
          Finish = 1
          ServiceStatus\dwCurrentState = #SERVICE_STOPPED
        EndIf
        
      Case #SERVICE_USERDATA_128
        svUserdata128()
        
      Case #SERVICE_USERDATA_129
        svUserdata129()
        
      Case #SERVICE_USERDATA_130
        svUserdata130()
        
      Case #SERVICE_USERDATA_131
        svUserdata131()
        
    EndSelect
    
    ;Send current status.
    r1 = SetServiceStatus_(hServiceStatus, ServiceStatus)
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svServiceMain(dwArgc.i, lpszArgv.i)
    
    Protected r1.i
    
    ;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, @svHandler())
    ServiceStatus\dwCurrentState = #SERVICE_START_PENDING
    r1 = SetServiceStatus_(hServiceStatus, ServiceStatus)
    
    ;** Do Initialization Here
    If svInit()
      ServiceStatus\dwCurrentState = #SERVICE_RUNNING
      r1 = SetServiceStatus_(hServiceStatus, ServiceStatus)
    Else
      ServiceStatus\dwCurrentState = #SERVICE_STOP_PENDING
      r1 = SetServiceStatus_(hServiceStatus, ServiceStatus)
      ServiceStatus\dwCurrentState = #SERVICE_STOPPED
      r1 = SetServiceStatus_(hServiceStatus, ServiceStatus)
      Finish = 1
    EndIf
    
    ;** 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
  
  ; *************************************************************************************************
  
  Procedure svInit()
    
    Protected path.s
    
    ; Create folder for logs
    AppPathLog = CreateLogFolder(SERVICE_NAME)
    AppPathLog + "Service.log"
    
    ; Create mutex for logs
    MutexLog = CreateMutex()
    
    WriteLog("Service Start")
    
    If *thMainThread
      *ThreadControl\Command = #SERVICE_START
      *ThreadControl\ID = CreateThread(*thMainThread, *ThreadControl)
      If *ThreadControl\ID
        ProcedureReturn 1
      Else
        WriteLog("Service Start - Error: Start Thread")
        *ThreadControl\Command = 0
        ProcedureReturn 0
      EndIf
    Else
      ProcedureReturn 0
    EndIf
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svPause()
    
    WriteLog("Service Pause")
    
    *ThreadControl\Command = #SERVICE_CONTROL_PAUSE
    If IsThread(*ThreadControl\ID)
      If WaitThread(*ThreadControl\ID, 10000) = 0
        WriteLog("Service Pause - Error: Killed Thread")
        KillThread(*ThreadControl\ID)
      EndIf
    EndIf
    *ThreadControl\Command = 0
    *ThreadControl\ID = 0
    ProcedureReturn 1
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svContinue()
    
    WriteLog("Service Continue")
    
    *ThreadControl\Command = #SERVICE_CONTROL_CONTINUE
    *ThreadControl\ID = CreateThread(*thMainThread, *ThreadControl)
    
    If *ThreadControl\ID
      ProcedureReturn 1
    Else
      WriteLog("Service Continue - Error: Start Thread")
      *ThreadControl\Command = 0
      ProcedureReturn 0
    EndIf
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svStop()
    
    Protected ctime
    
    WriteLog("Service Stop")
    
    *ThreadControl\Command = #SERVICE_CONTROL_STOP
    If IsThread(*ThreadControl\ID)
      If WaitThread(*ThreadControl\ID, 10000) = 0
        WriteLog("Service Stop - Error: Killed Thread")
        KillThread(*ThreadControl\ID)
      EndIf
    EndIf
    *ThreadControl\Command = 0
    *ThreadControl\ID = 0
    ProcedureReturn 1
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svInterrogate()
    
    WriteLog("Service Interrogate")
    
    ProcedureReturn 1
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svShutdown()
    
    Protected ctime
    
    WriteLog("Service Shutdown")
    
    *ThreadControl\Command = #SERVICE_CONTROL_SHUTDOWN
    If IsThread(*ThreadControl\ID)
      If WaitThread(*ThreadControl\ID, 10000) = 0
        WriteLog("Service Shutdown - Error: Killed Thread")
        KillThread(*ThreadControl\ID)
      EndIf
    EndIf
    *ThreadControl\Command = 0
    *ThreadControl\ID = 0
    ProcedureReturn 1
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svUserdata128()
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svUserdata129()
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svUserdata130()
    
  EndProcedure
  
  ; *************************************************************************************************
  
  Procedure svUserdata131()
    
  EndProcedure
  
  ; *************************************************************************************************
  
  
EndModule

;- End Module Service

; *************************************************************************************************

CompilerIf #PB_Compiler_IsMainFile
  
  ;-Example
  
  UseModule Service
  
  ;- Thread main
  
  Structure udtThreadData Extends udtThreadControl
    Counter.i
  EndStructure
  
  Global thData.udtThreadData
  
  Procedure thMain(*Thread.udtThreadData)
    
    ; Global code for init
    svWriteLog("Thread - Init")
    
    Repeat
      Select *Thread\Command
        Case #SERVICE_START
          *Thread\Command = 0
          ; Code for start
          svWriteLog("Thread - Start")
          ; End
          
        Case #SERVICE_CONTROL_CONTINUE
          *Thread\Command = 0
          ; Code for contine
          svWriteLog("Thread - Continue")
          ; End
        Case #SERVICE_CONTROL_PAUSE
          *Thread\Command = 0
          ; Code for pause before exit
          svWriteLog("Thread - Pause")
          ; end
          Break ; Always exit
          
        Case #SERVICE_CONTROL_STOP
          *Thread\Command = 0
          ; Code for stop before exit
          svWriteLog("Thread - Stop")
          ; end
          Break ; Always exit
          
        Case #SERVICE_CONTROL_SHUTDOWN
          *Thread\Command = 0
          ; Code for shutdown before exit
          svWriteLog("Thread - Shutdown")
          ; end
          Break ; Always exit
          
        Default
          ; Any cycle code
          *Thread\Counter + 1
          Delay(100) ; Always Delay 
          ; End
      EndSelect
      
    ForEver
    
    ; Global code for exit
    svWriteLog("Thread - Exit")
    
  EndProcedure
  
  svSetServiceName("MyService")
  svSetServiceDescription("Module Test Service")
  svSetThread(@thMain(), thData)
  svMain()
  
CompilerEndIf
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Antworten