FAQ DelphiConsultez toutes les FAQ
Nombre d'auteurs : 124, nombre de questions : 933, dernière mise à jour : 28 septembre 2024 Ajouter une question
Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums Delphi et Delphi et bases de données de www.developpez.com et de l'expérience personnelle des auteurs.
Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose soient correctes. Les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous souhaitez y apporter des corrections ou la compléter, contactez un responsable (lien au bas de cette page).
Nous espérons que cette FAQ saura répondre à vos attentes. Nous vous en souhaitons une bonne lecture.
L'équipe Delphi de Developpez.com.
- Comment utilise-t-on les services NT ?
- Comment démarrer un service ?
- Comment déterminer l'état courant d'un service ?
- Comment modifier l'état d'un service (Stop, Pause, etc) ?
- Comment retrouver la liste des services dépendant d'un service ?
- Comment se connecter au Service Control Manager ?
- Comment lister les services référencés dans le SCM ?
- Comment se déconnecter d'un Service Control Manager ?
- Comment récupérer la configuration d'un service ?
- Comment modifier la configuration d'un service ?
- Comment obtenir le nom court ou long d'un service ?
Toutes les solutions proposées dans la section Services NT nécessitent de renseigner l'unité WinSvc dans la clauses Uses.
Le manager de contrôle des services (Service Control Manager, SCM) est exécuté lors du boot du système.
C'est un serveur RPC (Remote Procedure Call), de sorte que la configuration de services et les programmes de gestion de services peuvent opérer sur les services de machines distantes.
Le SCM maintient dans la base de registre une 'base de données' des services installés.
La base de données est employée par le SCM et les programmes qui ajoutent, modifient, ou configurent des services.
À chaque entrée de service lue, à partir de la base de données des services installés, le SCM crée un enregistrement pour le service.
Un enregistrement de service inclut :
- le nom de service
- le type de démarrage (automatique ou à la demande)
- le statut du service :
- le type
- l'état courant
- le code de contrôle autorisé
- le code de sortie
- une estimation de temps pour l'opération, en millisecondes
- un pointeur vers la liste des dépendances.
L'entrée statut du service est une structure de type _SERVICE_STATUS.
Certaines opérations nécessitent un Handle SCM qui autorise ou non l'accès selon le profil de sécurité de l'appelant. Ils sont utilisés lors des opérations sur le SCM mais aussi pour manipuler un 'objet' service, c'est-à-dire un service installé.
Note :
L'unité JclSvcCtrl.pas disponible dans la JVCL vous propose de nombreuses procédures pour manipuler le SCM.
Vous trouverez un exemple complet dans le répertoire ...\jcl\examples\vcl\ntservice de la distribution.
Pour démarrer un service on utilise l'API StartService.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 | procedure Start_Service(Srv_Hwnd:Sc_Handle); Var ArrayOfArguments : PChar; begin ArrayOfArguments := nil; if StartService(Handle_Service, 0, ArrayOfArguments) =0 then MessageDlg(SysErrorMessage(GetLastError), mtError, [mbOK], 0); end; |
Il est préférable de lire les notes liées à cette API sur le site MSDN.
L'opération se fait en 2 temps, on doit d'abord obtenir un handle sur le service en appelant OpenService en lui passant en paramètre le handle obtenu lors de l'appel à OpenSCManager.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | var NomService : String; Handle_Service :SC_Handle; // handle du service Status_Service : TServiceStatus; EtatDuService : Word; begin NomService:='UPS'; // Nom interne du service, nom public 'Onduleur' Handle_Service := OpenService(Handle_SCM, PChar(NomService), SERVICE_ALL_ACCESS); If Handle_Service=0 then ShowMessage('Erreur lors de l''ouverture du service :'+NomService) |
Si la fonction échoue elle renvoie Zéro.
Ensuite on peux accéder au statut du service par l'appel à l'API QueryServiceStatus, elle utilise le handle du service interrogé.
On récupère les informations dans une structure de type TServiceStatus :
Code delphi : | Sélectionner tout |
1 2 3 4 5 | Else begin QueryServiceStatus(Handle_Service, Status_Service); EtatDuService:=Status_Service.dwCurrentState; end; |
- SERVICE_CONTINUE_PENDING :
Le service est en train d'être relancé après une opération continue - SERVICE_PAUSE_PENDING :
le service est en train d'être relancé après une opération pause - SERVICE_PAUSED :
Le service est en pause - SERVICE_RUNNING :
Le service est démarré - SERVICE_START_PENDING :
Le service est en cours de démarrage - SERVICE_STOP_PENDING :
Le service est en cours d'arrêt - SERVICE_STOPPED :
Le service est stoppé
Cette API fonctionne sous NT 4, Microsoft recommande d'utiliser pour les plates-formes plus récentes l'API QueryServiceStatusEx.
Chaque handle obtenu par un appel à OpenService devra être libéré par un appel à CloseServiceHandle.
Code delphi : | Sélectionner tout |
1 2 | if Not CloseServiceHandle(Handle_Service) then ShowMessage('Erreur : le handle spécifié est invalide.'); |
Pour modifier l'état d'un service on utilise l'API ControlService.
Par exemple pour arrêter un service :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | procedure Stop_Service(Srv_Hwnd:Sc_Handle); Var Status_Service : TServiceStatus; begin if ControlService(Srv_Hwnd, SERVICE_CONTROL_STOP,Status_Service)=0 then MessageDlg(SysErrorMessage(GetLastError), mtError, [mbOK], 0); end; |
- SERVICE_CONTROL_CONTINUE Informe un service à l'état pause qu'il devrait reprendre.
- SERVICE_CONTROL_INTERROGATE Informe un service qu'il devrait rapporter son statut d'état actuel au SCM.
- SERVICE_CONTROL_NETBINDADD Informe un service de réseau qu'il y a un nouveau composant à lier. Windows NT : Cette valeur n'est pas supportée.
- SERVICE_CONTROL_NETBINDDISABLE Informe un service réseau qu'une de ses liaisons (bindings) a été désactivée. Windows NT : Cette valeur n'est pas supportée.
- SERVICE_CONTROL_NETBINDENABLE Informe un service réseau qu'une liaison (binding) désactivée a été activée. Windows NT : Cette valeur n'est pas supportée.
- SERVICE_CONTROL_NETBINDREMOVE Informe un service réseau qu'un composant de liaison (binding) a été supprimé. Windows NT : Cette valeur n'est pas supportée.
- SERVICE_CONTROL_PARAMCHANGE Informe un service que ses paramètres de démarrage ont changé. Windows NT : Cette valeur n'est pas supportée.
- SERVICE_CONTROL_PAUSE Informe un service qu'il devrait faire une pause.
- SERVICE_CONTROL_STOP Informe un service qu'il devrait s'arrêter. Après l'envoi de la demande d'arrêt à un service, vous ne devriez pas envoyer d'autres commandes au service.
Consultez l'aide du SDK pour de plus amples informations, notamment celles liées aux droits d'accès nécessaires pour effectuer certaines de ces opérations qui n'ont pas été reportées ici.
La fonction EnumDependentServices permet de retrouver le nom et le statut de chaque service dépendant du service indiqué; c'est-à-dire que le service indiqué doit être actif (démarré) avant que les services dépendants puissent fonctionner.
Cette API utilise un handle de service.
Au préalable on déclare ces deux types de données :
Code delphi : | Sélectionner tout |
1 2 3 | Type PNTServices = ^TNTServices ; TNTServices = array[0..512] of TEnumServiceStatus; |
Code delphi : | Sélectionner tout |
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 | Procedure GetDependanceService(AHandle_Service : SC_Handle; List : TStringList); Var Mes_Services : PNTServices; OctetsNecessaires, Nombre_Services : DWord; i : Integer; begin Try New(Mes_Services); // Alloue un tableau de la taille du type TNTServices Nombre_Services := 0; BytesNeeded:=0; If EnumDependentServices(AHandle_Service, SERVICE_STATE_ALL, Mes_Services^[0], SizeOf(Mes_Services^), OctetsNecessaires, Nombre_Services)<>0 Then begin List.Clear; For i := 0 to Nombre_Services-1 do List.Add(Mes_Services^[i].lpServiceName); //Ou Mes_Services^[i].lpDisplayName end; Finally Dispose(Mes_Services); end; end; |
On utilise l'API OpenSCManager. Si le premier paramètre qui est le nom de la machine cible, est vide ou est à NIL alors la connexion se fait sur la machine locale.
Si le second paramètre est à NIL, la base de données SERVICES_ACTIVE_DATABASE est ouverte par défaut.
Le troisième paramètre indique le type d'accès demandé. Le système vérifie le profil de sécurité de l'appelant.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | Function ConnectToServicesManager(Computer_Name : String; Var AHandle_SCM : SC_Handle):Boolean; Begin AHandle_SCM:= OpenSCManager(PChar(Computer_Name), nil, SC_MANAGER_ALL_ACCESS); Result :=AHandle_SCM <> 0; end; |
Chaque handle obtenu par un appel à OpenSCManager devra être libéré par un appel à CloseServiceHandle.
On aura souvent au moins deux handles à libérer :
- un pour le SCM
- et un pour un objet service qu'on souhaite interroger.
Code delphi : | Sélectionner tout |
1 2 3 4 | If Handle_Service <> 0 then CloseServiceHandle(Handle_Service); If Handle_SCM <> 0 then CloseServiceHandle(Handle_SCM);CloseServiceHandle(Handle_Service); |
Une fois connecté au manager de service on doit créer un tableau d'une taille suffisante pour héberger tous les services recensés dans la base de données interrogée.
Code delphi : | Sélectionner tout |
1 2 3 | Type PNTServices = ^TNTServices ; TNTServices = array[0..512] of TEnumServiceStatus; |
Code delphi : | Sélectionner tout |
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 | Procedure Refresh_Liste_Services(AHandle_SCM : SC_Handle;List : TStringList); Var Mes_Services : PNTServices; ResumeHandle, OctetsNecessaires, Nombre_Service : DWord; i : Integer; begin Try New(Mes_Services); Nombre_Service := 0; OctetsNecessaires:=0; ResumeHandle := 0; if EnumServicesStatus(AHandle_SCM, SERVICE_WIN32, //Uniquement les services, pas les drivers SERVICE_STATE_ALL, // Peut importe l'état Mes_Services^[0], SizeOf(Mes_Services^), OctetsNecessaires, Nombre_Service, ResumeHandle) <>0 then begin List.Clear; For i := 0 to Nombre_Service-1 do List.Add(Mes_Services^[i].lpServiceName); end; //else GetlastErrorCode Finally Dispose(Mes_Services); end; end; |
On utilise l'API CloseServiceHandle en lui passant en paramètre le handle obtenu lors de l'appel à OpenSCManager.
Code delphi : | Sélectionner tout |
1 2 | if Not CloseServiceHandle(Handle_SCM) then ShowMessage('Erreur: le handle SCM spécifié est invalide'); |
On utilise l'API QueryServiceConfig. On doit en revanche effectuer deux appels consécutifs :
- le premier pour obtenir la taille des informations à récupérer,
- le second, une fois la taille connue, pour récupérer les informations proprement dites.
Code delphi : | Sélectionner tout |
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 | Procedure GetConfigurationService(AHandle_Service: Sc_Handle); var Config_Service : PQueryServiceConfig; OctetsNecessaires: DWORD; ErrorCode : Cardinal; begin try Config_Service:=Nil; //Premier appel, obtient la taille du buffer QueryServiceConfig(AHandle_Service, nil, 0, OctetsNecessaires); ErrorCode := GetLastError; if ErrorCode <> ERROR_INSUFFICIENT_BUFFER then begin MessageDlg(SysErrorMessage(ErrorCode), mtError, [mbOK], 0); Exit; end; GetMem(Config_Service, OctetsNecessaires); //Second appel, obtient les infos dans le buffer dimensionné à la taille nécessaire if WinSvc.QueryServiceConfig(AHandle_Service, Config_Service, OctetsNecessaires, OctetsNecessaires) then case Config_Service^.dwStartType of SERVICE_AUTO_START : rbtAuto.Checked:=True; SERVICE_DEMAND_START : rbtManuel.Checked:=True; SERVICE_DISABLED : rbtDesactive.Checked:=True; end else MessageDlg(SysErrorMessage(GetLastError), mtError, [mbOK], 0); finally If Assigned(Config_Service) then FreeMem(Config_Service); end; end; |
Il existe une API similaire QueryServiceConfig2 renvoyant des informations supplémentaires.
Pour modifier la configuration d'un service on utilise l'API ChangeServiceConfig. Elle n'utilise pas de structure particulière pour renseigner la nouvelle configuration du service.
Pour sérialiser les accès et avant de modifier la configuration d'un service, on doit verrouiller le SCM. L'appel de l'API LockServiceDatabase verrouille la base de données de manière exclusive. Pour libérer le verrou appellez l'API UnlockServiceDatabase.
Notez que l'opération de démarrage d'un service n'est plus autorisée pendant la phase de verrouillage.
Code delphi : | Sélectionner tout |
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 | procedure TMainForm.SetConfigurationService(AHandle_Service: Sc_Handle;StartType:DWORD); Var Verrou_SCM : SC_LOCK; procedure Informe; begin MessageDlg('La configuration du service ne peut être modifiée.'+#13+#10+SysErrorMessage(GetLastError), mtError, [mbOK], 0); end; begin // If StateType=Config_Service^.dwStartType then Exit // On ne change rien Verrou_SCM:=NIL; try Verrou_SCM:=LockServiceDatabase(Handle_SCM); if Not Assigned(Verrou_SCM) then begin Informe; Exit; end; if ChangeServiceConfig(AHandle_Service, SERVICE_NO_CHANGE, // On ne change pas le type du service StartType, // Nouvelle configuration de démarrage SERVICE_NO_CHANGE, // On ne change pas le mode de contrôle des erreurs NiL,NiL,NiL,NiL,NiL,NiL,NiL) // On ne modifie aucune des autres paramètres de configuration then ShowMessage('La configuration du service a été modifiée.') else Informe; Finally if Assigned(Verrou_SCM) then UnlockServiceDatabase(Verrou_SCM) end; end; |
Note : il existe une API similaire, ChangeServiceConfig2, qui permet de modifier des informations supplémentaires.
Lien MSDN : fonction LockServiceDatabase
Lien MSDN : fonction UnlockServiceDatabase
Certaines API de gestion du SCM attendent un nom court de service (lpServiceName) alors qu'un TComboBox attend par exemple un nom long, plus explicite pour l'utilisateur (lpDisplayName).
Dans ce cas on utilisera les API suivantes :
- GetServiceKeyName retrouve le 'nom court' d'un service.
- GetServiceDisplayName retrouve le 'nom long' d'un service .
Les traitements liés à l'appel de ces deux API étant identiques, on utilisera une énumération pour déterminer quel type de nom on souhaite récupérer.
Code delphi : | Sélectionner tout |
1 2 3 4 | Type // Key : nom de service court // Display : nom de service long DisplayService=(Key,Display); |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function TMainForm.ServiceGetName(AServiceName:string ; ATypeAffichage:DisplayService) : string; Const LongueurMaxNomSvr = 255; var NomService : Array[0..LongueurMaxNomSvr+1] of Char; TailleBuffer: DWord; Erreur: Boolean; begin FillChar(NomService,SizeOf(NomService),#0); TailleBuffer:=LongueurMaxNomSvr+1; Case ATypeAffichage of Key : Erreur:=GetServiceKeyName(Handle_SCM,PChar(AServiceName),NomService,TailleBuffer)=False; Display: Erreur:=GetServiceDisplayName(Handle_SCM,PChar(AServiceName),NomService,TailleBuffer)=False; end; if Erreur then MessageDlg(SysErrorMessage(GetLastError), mtError, [mbOK], 0); Result :=NomService; end; |
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.