Service Windows

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
tatanas
Messages : 39
Inscription : mar. 05/nov./2019 18:40

Service Windows

Message par tatanas »

Bonjour tout le monde,

J'ai trouvé un code rudement bien foutu pour créer son propre service : https://f-lefevre.developpez.com/tutori ... e-windows/

Il fonctionne plutôt bien sauf pour une chose : stopper le service.
Si l'on passe par le gestionnaire de service de Windows, on a droit à un message d'erreur lors de la tentative d'arrêt. Si l'on réessaye, on a droit à un second message d'erreur (différent du premier) mais le service s'arrête (notre .exe disparait bien des processus). Idem si l'on passe par le paramètre -k de l'exécutable. Il faut lancer la commande 2 fois.

Dans les 2 cas, je suis surpris de ne trouver à aucun moment la ligne "Service_MainLoop() > Stop" dans le fichier de log, qui correspond à la sortie de notre boucle infinie principale.
Je teste ce code sous Windows 7 64bits avec Purebasic 5.72 64bits. Pourriez-vous me confirmer ou infirmer le problème que je rencontre ?

Merci d'avance.

Voici le code :

Code : Tout sélectionner


#MyService_LogFile = "logservice.txt"
#MyService_Name = "testservice"
#MyService_AppName = "test_service.exe"
#MyService_DisplayName = "test_service"
#MyService_Description = "test_service"

Global ServiceStatus.SERVICE_STATUS


Enumeration SC_ACTION_TYPE 0
  #SC_ACTION_NONE
  #SC_ACTION_RESTART
  #SC_ACTION_REBOOT
  #SC_ACTION_RUN_COMMAND
EndEnumeration

#SERVICE_CONTROL_SESSIONCHANGE = $0000000E

Structure SC_ACTION
	Type.l
	Delay.i
EndStructure

Structure SERVICE_FAILURE_ACTIONS
	dwResetPeriod.i
	*lpRebootMsg
	*lpCommand
	cActions.i
	*lpsaActions.SC_ACTION
EndStructure

Structure SERVICE_FAILURE_ACTIONS_FLAG
	fFailureActionsOnNonCrashFailures.l
EndStructure


Procedure WriteToLog(entry.s)
	Protected hFile.l
	hFile = OpenFile(#PB_Any, #MyService_LogFile)
	If hFile = #Null
		ProcedureReturn #False
	EndIf
	FileSeek(hFile, Lof(hFile))
	WriteStringN(hFile, entry)
	CloseFile(hFile)
	ProcedureReturn #True
EndProcedure


Procedure Service_CtrlHandler(CtrlRequest.l)
	WriteToLog("Service_CtrlHandler() > Start")
	Select CtrlRequest
		Case #SERVICE_CONTROL_CONTINUE
			WriteToLog("Monitoring resumed.")
			With ServiceStatus
				\dwCurrentState = #SERVICE_RUNNING
			EndWith

		Case #SERVICE_CONTROL_INTERROGATE
			WriteToLog("Monitoring reported its current status information to the service control manager.")

		Case #SERVICE_CONTROL_PAUSE
			WriteToLog("Monitoring paused.")
			With ServiceStatus
				\dwCurrentState = #SERVICE_PAUSED
			EndWith

		Case #SERVICE_CONTROL_STOP
			;{
			WriteToLog("Monitoring stopped.")
			
			With ServiceStatus
				\dwCurrentState = #SERVICE_STOP_PENDING
				\dwWin32ExitCode = 0
				\dwServiceSpecificExitCode = 0
				\dwCheckPoint = 0
				\dwWaitHint = 0
			EndWith
			SetServiceStatus_(hStatus, @ServiceStatus)
			ServiceStatus\dwCurrentState = #SERVICE_STOPPED
			SetServiceStatus_(hStatus, @ServiceStatus)
      
      
		Case #SERVICE_CONTROL_SHUTDOWN
			WriteToLog("Monitoring shutdowned.")
			With ServiceStatus
				\dwWin32ExitCode = 0
				\dwCurrentState  = #SERVICE_STOPPED
			EndWith
			
		Case #SERVICE_CONTROL_SESSIONCHANGE
			WriteToLog("Session changed.")
			
		Default
			WriteToLog("CtrlRequest Unknown = "+Str(CtrlRequest))

	EndSelect
	; Report current status
	SetServiceStatus_(hStatus, @ServiceStatus)
	WriteToLog("Service_CtrlHandler() > End")
EndProcedure


;@desc : Actualiser l'état du service
Procedure Service_UpdateStatus()
	Protected hSCManager.l, hServ.l
	hSCManager  = OpenSCManager_(#Null, #Null, #SC_MANAGER_ALL_ACCESS)
	hServ       = OpenService_(hSCManager, #MyService_Name, #SERVICE_ALL_ACCESS)
	QueryServiceStatus_(hServ, @ServiceStatus)
	CloseServiceHandle_(hServ)
	CloseServiceHandle_(hSCManager)
EndProcedure


Procedure Service_MainLoop()
	Protected hError.l
	Protected memory.MEMORYSTATUS
	
	WriteToLog("Service_MainLoop() > Start")
	With ServiceStatus
		\dwServiceType             = #SERVICE_WIN32_OWN_PROCESS | #SERVICE_INTERACTIVE_PROCESS
		\dwCurrentState            = #SERVICE_START_PENDING
		\dwControlsAccepted        = #SERVICE_ACCEPT_STOP | #SERVICE_ACCEPT_SHUTDOWN | #SERVICE_ACCEPT_PAUSE_CONTINUE | #SERVICE_ACCEPT_SESSIONCHANGE ; SERVICE_CONTROL_SESSIONCHANGE notification
		\dwWin32ExitCode           = 0
		\dwServiceSpecificExitCode = 0
		\dwCheckPoint              = 0
		\dwWaitHint                = 0
	EndWith
	hStatus = RegisterServiceCtrlHandler_(#MyService_Name, @Service_CtrlHandler()) 
	If hStatus = 0
		WriteToLog("Registering Control Handler failed")
		ProcedureReturn
	EndIf
	SetServiceStatus_(hStatus, @ServiceStatus)
	
	; Faire l'initialisation ici
	
	ServiceStatus\dwCurrentState = #SERVICE_RUNNING
	SetServiceStatus_(hStatus, @ServiceStatus)
	
	; La boucle principale
	While ServiceStatus\dwCurrentState = #SERVICE_RUNNING
		
; 		Protected buffer.s{16}
; 		GlobalMemoryStatus_(@memory)
; 		buffer = Str(memory\dwAvailPhys)
; 		result = WriteToLog("Mem>"+buffer)
; 		If result = #False
; 			ServiceStatus\dwCurrentState = #SERVICE_STOPPED
; 			ServiceStatus\dwWin32ExitCode= -1 
; 			SetServiceStatus_(hStatus, @ServiceStatus)
; 			ProcedureReturn #False
; 		EndIf
		Sleep_(1000)
		Service_UpdateStatus()
		
	Wend
	
	With ServiceStatus
		\dwCurrentState = #SERVICE_STOP_PENDING
		\dwWin32ExitCode = 0
		\dwServiceSpecificExitCode = 0
		\dwCheckPoint = 0
		\dwWaitHint = 0
	EndWith
	SetServiceStatus_(hStatus, @ServiceStatus)
	ServiceStatus\dwCurrentState = #SERVICE_STOPPED
	SetServiceStatus_(hStatus, @ServiceStatus)
    
	WriteToLog("Service_MainLoop() > End")
EndProcedure

; Lance le Service_Mainloop où l'on fera notre traitement
Procedure Service_Default(Name.s)
	Protected Dim ServiceTable.SERVICE_TABLE_ENTRY(1)
	WriteToLog("Service_Default() > Start")
	With ServiceTable(0)
		\lpServiceName = @Name
		\lpServiceProc = @Service_MainLoop()
	EndWith
	With ServiceTable(1)
		\lpServiceName = #Null
		\lpServiceProc = #Null
	EndWith	
	If StartServiceCtrlDispatcher_(@ServiceTable()) = 0
		Protected lnErr.l = GetLastError_()
		Select lnErr
			Case #ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
				WriteToLog("Service_Default() > FAILED_SERVICE_CONTROLLER_CONNECT")
			Case #ERROR_INVALID_DATA
				WriteToLog("Service_Default() > INVALID_DATA")
			Case #ERROR_SERVICE_ALREADY_RUNNING
				WriteToLog("Service_Default() > SERVICE ALREADY_RUNNING")
			Default
				WriteToLog("Service_Default() > " + StrU(lnErr, #PB_Word))
		EndSelect
	EndIf	
	WriteToLog("Service_Default() > End")
EndProcedure

;@desc : Installe le service #MyService_Name dans le ServiceControlManager
Procedure Service_Install()
	Protected sDir.s
	Protected hSCManager.l, hService.l
	Protected SD.SERVICE_DESCRIPTION
	WriteToLog("Service_Install() > Start")
	sDir        = GetCurrentDirectory() + #MyService_AppName
	hSCManager  = OpenSCManager_(#Null, #Null, #SC_MANAGER_ALL_ACCESS)
	; nous créons le service
	hService    = CreateService_(hSCManager, #MyService_Name, #MyService_DisplayName, #SERVICE_ALL_ACCESS, 
	                             #SERVICE_WIN32_OWN_PROCESS, #SERVICE_AUTO_START, #SERVICE_ERROR_NORMAL, sDir, #Null, #Null, #Null, #Null, #Null)
	; nous définissons sa description pour le SCM
	Protected lpDescription$ = #MyService_Description
	SD\lpDescription = @lpDescription$

	If ChangeServiceConfig2_(hService, #SERVICE_CONFIG_DESCRIPTION, @SD)
		Dim sAction.SC_ACTION(3)
		sAction(0)\Type = #SC_ACTION_RESTART
		sAction(0)\Delay = 60000
		sAction(1)\Type = #SC_ACTION_RESTART
		sAction(1)\Delay = 60000
		sAction(2)\Type = #SC_ACTION_NONE
		sAction(2)\Delay = #Null
		
		Protected sFailure.SERVICE_FAILURE_ACTIONS
		With sFailure
			\dwResetPeriod = 86400
			\lpCommand = #Null
			\lpRebootMsg = #Null
			\cActions = 3
			\lpsaActions = sAction()
		EndWith
		
		If ChangeServiceConfig2_(hService, #SERVICE_CONFIG_FAILURE_ACTIONS, @sFailure)
			Protected sFlag.SERVICE_FAILURE_ACTIONS_FLAG
			sFlag\fFailureActionsOnNonCrashFailures = #True
			ChangeServiceConfig2_(hService, #SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, @sFlag)
		EndIf
	EndIf	
		
	CloseServiceHandle_(hService)
	
	WriteToLog("Monitoring installé.")
	WriteToLog("Service_Install() > End")
EndProcedure

;@desc : Désinstalle le service #MyService_Name du ServiceControlManager
Procedure Service_Delete()
	Protected hSCManager.l, hServ.l
	WriteToLog("Service_Delete() > Start")
	hSCManager  = OpenSCManager_(#Null, #Null, #SC_MANAGER_ALL_ACCESS)
	hServ       = OpenService_(hSCManager, #MyService_Name, #SERVICE_ALL_ACCESS)
	DeleteService_(hServ)
	CloseServiceHandle_(hServ)
	CloseServiceHandle_(hSCManager)
	
	WriteToLog("Monitoring désinstallé.")
	WriteToLog("Service_Delete() > End")
EndProcedure

;@desc : Changement d'état : START
Procedure Service_Start()
	Protected hSCManager.l, hServ.l
	WriteToLog("Service_Start() > Start")
	hSCManager  = OpenSCManager_(#Null, #Null, #SC_MANAGER_ALL_ACCESS)
	hServ       = OpenService_(hSCManager, #MyService_Name, #SERVICE_ALL_ACCESS)
	StartService_(hServ, 0, #Null)
	WriteToLog("Monitoring démarré.")
	CloseServiceHandle_(hServ)
	CloseServiceHandle_(hSCManager)
	WriteToLog("Service_Start() > End")
EndProcedure

;@desc : Changement d'état : STOP
Procedure Service_Stop()
	Protected hSCManager.l, hServ.l
	WriteToLog("Service_Stop() > Start")
	hSCManager  = OpenSCManager_(#Null, #Null, #SC_MANAGER_ALL_ACCESS)
	hServ       = OpenService_(hSCManager, #MyService_Name, #SERVICE_ALL_ACCESS)
	ControlService_(hServ, #SERVICE_CONTROL_STOP, @ServiceStatus)
	WriteToLog("Monitoring arrété.")
	CloseServiceHandle_(hServ)
	CloseServiceHandle_(hSCManager)
	WriteToLog("Service_Stop() > End")
EndProcedure

;@desc : Changement d'état : PAUSE > Start
Procedure Service_Pause()
	Protected hSCManager.l, hServ.l
	WriteToLog("Service_Pause() > Start")
	hSCManager  = OpenSCManager_(#Null, #Null, #SC_MANAGER_ALL_ACCESS)
	hServ       = OpenService_(hSCManager, #MyService_Name, #SERVICE_ALL_ACCESS)
	ControlService_(hServ, #SERVICE_CONTROL_PAUSE, @ServiceStatus)
	WriteToLog("Monitoring suspendu.")
	CloseServiceHandle_(hServ)
	CloseServiceHandle_(hSCManager)
	WriteToLog("Service_Pause() > End")
EndProcedure

;@desc : Changement d'état : PAUSE > Stop
Procedure Service_Continue()
	Protected hSCManager.l, hServ.l
	WriteToLog("Service_Continue() > Start")
	hSCManager  = OpenSCManager_(#Null, #Null, #SC_MANAGER_ALL_ACCESS)
	hServ       = OpenService_(hSCManager, #MyService_Name, #SERVICE_ALL_ACCESS)
	ControlService_(hServ, #SERVICE_CONTROL_CONTINUE, @ServiceStatus)
	WriteToLog("Monitoring repris.")
	CloseServiceHandle_(hServ)
	CloseServiceHandle_(hSCManager)
	WriteToLog("Service_Continue() > End")
EndProcedure


Procedure Main()
	Select ProgramParameter(0)
		Case "-i", "install"
			Service_Install()
			Service_Start()
		Case "-d", "delete"
			Service_Stop()
			Service_Delete()
		Case "-s", "start"
			Service_Start()
		Case "-k", "kill", "stop"
			Service_Stop()
		Default
			Service_Default(#MyService_Name)
	EndSelect
EndProcedure


Main()