FAQ DelphiConsultez toutes les FAQ

Nombre d'auteurs : 123, nombre de questions : 920, dernière mise à jour : 8 novembre 2019  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.

Commentez


SommaireServices WindowsGestion des services NT (11)
précédent sommaire suivant
 

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.

Mis à jour le 25 janvier 2014 Laurent Dardenne portu

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;
ArrayOfArguments est un tableau de PChar contenant les arguments optionnels pour démarrer le service ou NIL si vous n'utilisez pas d'argument.

Il est préférable de lire les notes liées à cette API sur le site MSDN.

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

Lien MSDN : fonction StartService

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)
Le dernier paramètre indique le type d'accès souhaité.
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;
TServiceStatus.dwCurrentState peut avoir une des valeurs suivantes :
  • 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.');

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

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;
Le second paramètre peut être une des valeurs suivantes :
  • 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.

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

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;
Le premier étant un pointeur sur un tableau, le second un tableau statique de taille suffisante pour récupérer l'ensemble des services.
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;

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

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;
Ici le handle récupéré est un handle sur un SCM, c'est-à-dire une base de données de services.

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);

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

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;
On liste tous les services installés en utilisant l'API EnumServicesStatus qui utilise le Handle SCM renseigné lors de l'appel à l'API OpenSCManager.
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;
Cette API fonctionne sous NT 4, Microsoft recommande d'utiliser pour les plates-formes plus récente l'API EnumServicesStatusEx.

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

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');

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

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;
Cette API renvoie une structure de type TQueryServiceConfig qui contient toute les informations de configuration d'un service installé.

Il existe une API similaire QueryServiceConfig2 renvoyant des informations supplémentaires.

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

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;
Assurez-vous que la nouvelle configuration d'un service est bien différente de sa configuration actuelle.

Note : il existe une API similaire, ChangeServiceConfig2, qui permet de modifier des informations supplémentaires.

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

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);
Voici la procédure qui traite l'appel des ces deux API :
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;

Mis à jour le 24 janvier 2014 Laurent Dardenne portu

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 ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les 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 © 2019 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.