@Andnotor: le SetEvent arrête instantanément le WaitFor, il y a pas d’attente et un passage dans wrTimeout…
@Andnotor: le SetEvent arrête instantanément le WaitFor, il y a pas d’attente et un passage dans wrTimeout…
Puisque tu n'en es toujours pas convaincu, mets 30 secondes et dis-nous si l'arrêt est vraiment instantané![]()
j'ai tout essayé rien n'y fait pour l'instant, le code exécuté est celui de XeGregory
La piste que je regarde est le fait que le type de service installé n'est pas celui de l'exécution:
journal d'évènement:
Le type de service est : mode utilisateur et le compte de service est LocalSystem.Un service a été installé sur le système.
Nom du service : DSIServiceSF
Nom du fichier de service : C:\DSILogs\TestServiceSF.exe
Type de service : service en mode utilisateur
Type de démarrage du service : Démarrage automatique
Compte de service : LocalSystem
Je regarde où on peut configurer le type de service qui devrait être LocalSystem aussi
Oui, je vient de le comprendre, du fait du mode de fonctionnement des services il faut bannir dans le waitfor, une valeur supérieur ou égale à 30 secondes qui est le temps qu’attends le gestionnaire de service avant de tuer ce dernier considérant qu’il est planté car le onstop (deadlock de service) n’a pas répondu dans les 30 secondes…
J’aurais pas perdu mon temps dans cette discutions, toujours un plaisir d’échanger avec toi Andnotor![]()
Ce qui est bizarre, je ne sais pas si vous avez cela,
TestServiceSF -install installe le service avec la dialogbox "service installé avec succès", mais il n'apparait pas dans le gestionnaire de service 'Services.msc'
par contre sc create .... installe bien le service dans le gestionnaire, mais à contrario, je n'ai pas la dialogbox qui s'affiche.
Du coup je fais les 2, mais cela ne marche pas plus, le service se met en démarrage mais n'aboutit pas, par contre le process tourne dans le gestionnaire des tâches. Il me semble que c'est la communication entre le service et le SCM qui ne marche pas.
Je l'ai essayé sur 3 machines différentes, celle de dev, une VM et une physique avec compte admin local, j'ai le même comportement partout.
Je ne sais donc pas faire, j'abandonne....
Merci à tous pour votre tentative d'aide, c'est franchement sympa.
@der§en
Il faut bannir TEvent complètement !
Le problème est que tu (xeGregory aussi) réfléchis comme s'il s'agissait d'une app de bureau classique : TService comme une "fiche" dans la tâche principale et une tâche de travail par TServiceThread.
Ce n'est pas le cas ! La tâche principale n'est utilisée que pour la mise en route, puis est définitivement mise en attente jusqu'à la terminaison de TServiceThread. Les événements suivants sont générés dans le contexte de ce dernier (pourquoi historiquement avoir procédé ainsi et non traité ces messages dans TApplicationService, mystère !). En d'autres termes tu es comme dans une app mono-thread.
OnExecute n'est pas LA procédure du TServiceThread, juste un événement appelé si défini. Refaire dans cet événement ce qui se fait d'origine dans TServiceThread.Execute n'a pas de sens (une boucle). Comme je le disais dans ma première intervention, on définira OnExecute pour une tâche sporadique : démarrage manuel, exécution, arrêt automatique.
Il y sera, rafraichi la page par F5. Tu peux aussi voir les services dans le gestionnaires de tâches. Et oublie sc create !
Le message "service installé avec succès" est une spécialité Delphi et est plus emmerdant qu'autre chose. Ils auraient au moins dû ajouter le support d'un -silent. Afficher ce message lorsque son enregistrement est fait par un installateur (ex. InnoSetup) n'a aucun intérêt.
Après l'installation par une console en Admin avec Service.exe /install et en utilisant powershell (en Admin) pour lister et lancer le service ça a fonctionné ! ....
je n'ai pas encore compris ce qu'il se passe, je vais continuer de chercher.
Merci AndNotor
Sinon " /SILENT " ça existe !
Au moins depuis Seattle (la version sur laquelle je suis depuis 6 ans)You can suppress the confirmation message by running the service application using the /SILENT option.
Sinon, j'ai l'impression que tout est compliqué dans les exemples que j'ai vu !
Je n'ai JAMAIS mis en place de gestionnaire ServiceExecute pour OnExecute, JAMAIS malgré plus de 10 services créés en 20 ans (certains ont tourné plus de 10 ans, sans aucune maintenance puisque je changeais de taf tous les 2 ans)
AndNotOr, doit-on vraiment systématiquement fournir un gestionnaire pour OnExecute ?
Je ne l'ai jamais fait puisque la documentation indique qu'il faut définir un OnExecute "Si vous ne déclenchez pas un nouveau thread pour gérer individuellement les requêtes de service dans le gestionnaire d'événement OnStart", hors c'est toujours dans OnStart que je démarre 1 à n threads.
Dans les 10 services que je maintiens, je n'en suis pas l'auteur ça se limite à ceci :
C'est ServiceStart (OnStart) qui démarre des Timer et\ou Thread, comme dans les services que j'ai écrit en Delphi ou C++Builder.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 procedure TServerService.ServiceExecute(Sender: TService); begin while not Terminated do ServiceThread.ProcessRequests( True ) ; end;
Je recommande la création d'un fichier log dédié aussi via ServiceAfterInstall
Penser aussi éventuellement à définir un compte utilisateur pour avoir des Policy et ACL personnalisé (règle de firewall, proxy par exemple)
/
Code bat : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 MyService.exe /INSTALL /SILENT sc.exe config [ServiceName] obj= xxx password= xxx
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 unit AutomateXXX_ServiceController; interface uses Winapi.Windows, Winapi.Messages, Winapi.WinSvc, System.SysUtils, System.Classes, Vcl.SvcMgr, System.Win.Registry, AutomateXXX_XXX_BatchClasses; type TAutomateXXXService = class(TService) procedure ServiceStart(Sender: TService; var Started: Boolean); procedure ServiceStop(Sender: TService; var Stopped: Boolean); procedure ServiceShutdown(Sender: TService); procedure ServiceDestroy(Sender: TObject); procedure ServiceCreate(Sender: TObject); procedure ServiceAfterInstall(Sender: TService); private { Déclarations privées } FBatch: TAutomateXXXXXXBatch; public function GetServiceController: TServiceController; override; { Déclarations publiques } end; var AutomateXXXService: TAutomateXXXService; implementation {$R *.DFM} (* AutomateXXX /INSTALL sc.exe config ServiceName obj= xxx password= xxx *) //------------------------------------------------------------------------------ procedure ServiceController(CtrlCode: DWord); stdcall; begin // Le traitement des demandes est délégué au thread interne (propriété ServiceThread) AutomateXXXService.Controller(CtrlCode); end; //------------------------------------------------------------------------------ function TAutomateXXXService.GetServiceController: TServiceController; begin // Lien avec l'API RegisterServiceCtrlHandler qui recçoit les notifications tel que Start, Stop ... Result := ServiceController; end; //------------------------------------------------------------------------------ procedure TAutomateXXXService.ServiceAfterInstall(Sender: TService); const ALXXX_DESCRIPTION = 'Service gérant de façon autonome les interactions' + ' entre les modules du XXX (la Gestion des Vagues, le Module XXX, le Module dAdministration de lAutomate XXX, ...)' + ' et le Sorter Control System (SCS) de XXX. Larrêt de ce service entraîne la perte de la connectivité' + ' avec les trieurs automatisés intégrés à la chaîne XXX de XXX XXX.'; REG_KEY_EVENT_LOG = 'SYSTEM\CurrentControlSet\services\eventlog'; REG_KEY_XXX_EVENT_LOG = 'XXX XXX Event Log'; var Svc: SC_HANDLE; SvcMgr: SC_HANDLE; Info: SERVICE_DESCRIPTION; Reg: TRegistry; begin // Ajout de la description dans services.msc SvcMgr := OpenSCManager(nil, nil, STANDARD_RIGHTS_REQUIRED); try Svc := OpenService(SvcMgr, PChar(Self.Name), SERVICE_CHANGE_CONFIG); try Info.lpDescription := PChar(ALXXX_DESCRIPTION); Winapi.WinSvc.ChangeServiceConfig2(Svc, SERVICE_CONFIG_DESCRIPTION, @Info); finally CloseServiceHandle(Svc); end; finally CloseServiceHandle(SvcMgr); end; // Ajout du journal personnalisé dans mmc.exe dans sa variante "Afficher les journaux d'évènements" Reg := TRegistry.Create(); try Reg.RootKey := HKEY_LOCAL_MACHINE; if Reg.OpenKey(REG_KEY_EVENT_LOG, False) then begin if Reg.OpenKey(REG_KEY_XXX_EVENT_LOG, True) then begin // Ajout de l'application courante comme élément concerné par ce journal personnalisé spécifique à XXX XXX // Je n'ai pas réussi à changer le fichier evt // Il sera donc dans "%SystemRoot%\System32\Winevt\Logs\" ce qui donne "C:\Windows\System32\winevt\Logs\XXX XXX Event Log.evtx" if not Reg.KeyExists(Self.Name) then Reg.CreateKey(Self.Name); end; end; finally Reg.Free(); end; end; //------------------------------------------------------------------------------ procedure TAutomateXXXService.ServiceCreate(Sender: TObject); begin // Pause et Reprise NON gérée ! AllowPause := False; end; //------------------------------------------------------------------------------ procedure TAutomateXXXService.ServiceDestroy(Sender: TObject); begin FreeAndNil(FBatch); end; //------------------------------------------------------------------------------ procedure TAutomateXXXService.ServiceShutdown(Sender: TService); begin // Le service s'arrete ! // Gérer une alerte ? end; //------------------------------------------------------------------------------ procedure TAutomateXXXService.ServiceStart(Sender: TService; var Started: Boolean); begin try if not Assigned(FBatch) then FBatch := TAutomateXXXXXXBatch.Create(Self); Started := FBatch.Start(); if Started then LogMessage(Self.Name + ' démarré', EVENTLOG_INFORMATION_TYPE); except on E: Exception do begin LogMessage(Self.Name + ' exception durant le démarrage : ' + E.Message, EVENTLOG_ERROR_TYPE); Started := False; end; end; end; //------------------------------------------------------------------------------ procedure TAutomateXXXService.ServiceStop(Sender: TService; var Stopped: Boolean); begin try if Assigned(FBatch) then FBatch.Stop(); LogMessage(Self.Name + ' arrêté', EVENTLOG_INFORMATION_TYPE); Stopped := True; except on E: Exception do begin LogMessage(Self.Name + ' exception durant l''arrêt : ' + E.Message, EVENTLOG_ERROR_TYPE); Stopped := False; end; end; end; end.
TAutomateXXXXXXBatch.Start() lance plusieurs threads ... en général, je procède toujours ainsi.
Et cette même classe peut être utilisée depuis une application GUI, bien plus facile à déboguer (la seule diff étant le LogMessage selon si Service ou App)
Pour ce Service par exemple, il y a un thread pour le Server TCP/IP, un Server FTP intégré, un thread d'envoie de mail asynchrone, plusieurs threads sur une base de données, un thread qui monitore les autres threads ... tous à base de TEvent + WaitFor
Tous les threads contiennent une surcharge d'un TerminatedSet, je recommande de toujours surcharger cette méthode pour arrêter proprement un thread qui contient un TEvent WaitFor, je n'ai pas l'impression que le TEvent soit correctement utilisé dans le code présent dans ce sujet (pire il est passé en paramètre, faut encapsuler tout ça via TerminatedSet, voir ce sujet AndNotOr y avait participé aussi )
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Pour ma culture personnel, peux-tu me dire où mon tevent est mal utilisé dans mon exemple ?
Je te remercie par avance du temps que tu pourras y consacrer![]()
FStopEvent de TWorkerThread - OK
En dehors que Stop() pourrait etre géré par un Terminate() + TerminatedSet, ça passe.
Et j'ai un gros doute sur le paramètre True pour ManualReset
Je vois les WaitFor mais pas de ResetEvent() en mode True pour ManualReset, il y a un truc qui cloche puisque le TEvent est utilisé en interne par un seul thread.
99% de mes TEvent "interne" sont crées ainsi :
Les rares en ManualReset à True sont en réalité des "One Shot"
Code : Sélectionner tout - Visualiser dans une fenêtre à part FSignal := TEvent.Create(nil, False, False, '', False); // Reset Auto !
Après si j'ai des Event "externe" entre processus, je passe par CreateMutex+CreateEvent nommé via lpName.
FServiceEvent de ServiceExecute - NON !
Mais c'est parce que pour moi, créer un gestionnaire de OnExecute est une mauvaise approche par rapport à la création d'un thread dans le gestionnaire de OnStart
Comme tu as déjà TWorkerThread lancé depuis le OnStart, le "OnExecute FServiceEvent.WaitFor" est inutile voire potentiellement nuisible (je ne vais pas répéter l'histoire du mono-thread de AndNotOr, sa démonstration est parfaite.)
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Merci de ton retour
J’oublie toujours le TerminatedSet…
Chez moi, le OnExecute, c’est pour tracer qu’il n’y a pas de pb.
Ah ! tant mieux
Du tout ! C'est ce que j'essayais d'expliquer.
L'implémentation que tu montres, outre que OnExecute ne sera exécuté que deux fois (au démarrage et à l'arrêt), elle ne fait que copier le fonctionnement de TServiceThread.Execute :
J'ai également peu implémenté cet événement : un fois parce que je devais piloter un service tiers et que pour se faire depuis une application sans élévation j'aurais dû redéfinir ses ACLs ; avec le risque qu'ils soient resettés après une mise à jour et que mon app ne fonctionne plus. Et une autre fois pour de petites tâches de maintenance à la demande.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 ... if Assigned(FService.OnExecute) then FService.OnExecute(FService) else ProcessRequests(True); ...
J'aurais évidemment pu les laisser tourner et les piloter par event mais était-ce bien nécessaire alors qu'ils ne faisaient rien 99.99% du temps
Par analogie, OnExecute sans boucle serait similaire à un TThread.FreeOnTerminate (en l'occurrence un StopOnTerminate).
Ce contrôle est illusoire si tu passes par un worker thread. A moins de remonter les erreurs, TServiceThread n'en saura rien![]()
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Petite question technique, en mode debug comment, ou on peux détecter ou localiser un deadlock de service ?
Ca ne devrait pas arrivé, à moins de faire quelque chose de bloquant dans les événements du ServiceThread. Mais si ça devait être le cas l'OS va tenter un redémarrage dudit service, un redémarrage de l'OS ou le lancement d'une autre app en fonction des réglages du service (onglet "Récupération"). Lancer une app permettrait une autre forme de notification qu'une inscription dans l'observateur.
Par contre si le worker thread est en deadlock ; c'est cuit !
Mais ça ne m'est jamais arrivé, dès lors difficile d'en dire plus![]()
Partager