Windows Management Instrumentation (WMI), seconde partie.

Laurent Dardenne

Cet article a pour objectif de mettre en pratique, sous Delphi, les aspects abordés dans l'article « Windows Management Instrumentation (WMI), première partie. ».

- Titre : Windows Management Instrumentation (WMI), seconde partie.

- Auteur : Laurent Dardenne

- Parution : 26 juin 2004

- Public visé :

- Licence : Copyright ® 2004 Laurent Dardenne. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Avant-propos

- I-A. Le code sources des exemples

- II. Opérations de base

- II-A. Gérer les erreurs WMI

- II-B. Comment contrôler les résultats ?

- III. Se connecter à WMI

- IV. Récupérer une collection d'instances d'objet

- V. Énumérer une collection WMI

- VI. Afficher le contenu d'un objet

- VI-A. Retrouver le path d'un objet

- VII. Récupérer une instance ou une définition de classe

- VIII. Récupérer une collection de propriétés

- IX. Récupérer une collection de qualificateurs

- X. Récupérer une collection de méthodes

- XI. Récupérer un élément d'une collection en utilisant son nom

- XII. Afficher une propriété

- XII-A. Gestion des différents types de donnée

- XIII. Modifier une propriété

- XIV. Mettre à jour un objet dans le référentiel WMI

- XV. Exécuter une requête WQL

- XVI. Modifier les privilèges de sécurité

- XVII. Exécuter une méthode

- XVIII. Exécuter une requête d'événement WQL synchrone

- XIX. Récupérer une notification d'événement synchrone

- XX. Exécuter une requête d'événement WQL asynchrone

- XXI. Récupérer une notification d'événement asynchrone

- XXII. Où trouver des informations sur les classes WMI disponibles ?

- XXIII. Windows Management Instrumentation (WMI), première partie.

I - Avant-propos

Testé avec Delphi 5 sous XP pro.
Version 1.0
Dernière mise à jour, le 26/06/2004

I-A - Le code sources des exemples

Vous trouverez ici une suite d'exemples en Delphi concernant la manipulation des objets de scripting WMI.J'ai fait en sorte d'ajouter le plus d'informations possible sur les méthodes et leurs paramètres.

Ce code vous permettra d'aborder simplement et progressivement WMI.

Merci d'utiliser le forum Delphi pour toute demande autre que les bugs et erreurs de documentation.

Sources des exemples.

II - Opérations de base

Il ne faut pas confondre les objets des API WMI (SwbemLocator, SwbemObject,SwbemServices…) et les classes d'objet ou les instances d'objet du référentiel WMI (Win32_Directory, Win32_Process…).

Les premiers de type TObject ou COM sont manipulables directement sous Delphi.

Exemple :

WmiService:= WMILocator.ConnectServer('.', 'ROOT\CIMV2', '', '', '','', wbemConnectFlagUseMaxWait, nil);

Les seconds ne sont pas manipulables directement sous Delphi.

L'exemple suivant n'est pas possible :

Var
NewProcess: Win32_Process;

Begin
NewProcess.Create('Notepad.exe','c:\temp',Nil, ProcessId);
End;

On doit utiliser un objet conteneur de type SwbemObject qui permettra de manipuler les propriétés et méthodes des classes d'objet et d'instances d'objet.

À noter que le VBScript permet une manipulation plus simple des propriétés et méthode des classes d'objet et d'instances d'objet.

II-A - Gérer les erreurs WMI

On utilise un objet TSWbemLastError qui nous informe sur les causes de l'erreur SI une erreur WMI est disponible.

Dans le cas contraire la méthode Connect permettant de se connecter à l'objet COM, déclenchera une exception.

begin
Try
WMILastError:=TSWbemLastError.Create(AOwner);
WMILastError.ConnectKind := ckNewInstance;
WMILastError.Connect;
Result:=True;
Except
on E:EOleSysError do
begin
// Si aucune erreur existe le code d'erreur est égal à E_FAIL = $80004005
Result:=False;
FreeAndNil(WMILastError);
end
end;
end;

II-B - Comment contrôler les résultats ?

Je vous recommande dans les premiers temps d'utiliser en parallèle les outils WMI de Microsoft qui vous permettront de vérifier si votre code WMI renvoie les mêmes informations.

Cf. Windows Management Instrumentation (WMI), première partie.", chapitre Paramétrage WMI et outils divers.

III - Se connecter à WMI

Pour se connecter à WMI on utilise un composant TSWbemLocator.

La méthode ConnectServer établi une connexion au référentiel WMI dans un espace de nom particulier, dans l'exemple suivant on se connecte dans l'espace de nom ROOT\CIMV2.

La modification du curseur de la souris indique à l'utilisateur la phase de connexion au référentiel WMI. L'utilisation du flag wbemConnectFlagUseMaxWait évite une attente indéfinie en cas d'indisponibilité de la machine distante.

Si l'appel de la méthode ConnectServer réussi, elle renvoie un objet SwbemServices initialisé, sinon le traitement ne peut se poursuivre.

Var

WMILocator:          TSWbemLocator;
WmiService:          SWbemServices;

OldCursor:           TCursor;

begin
WMILocator:= TSWbemLocator.Create(self);
try

OldCursor := Screen.Cursor;
Screen.Cursor := crSQLWait;

WmiService:= WMILocator.ConnectServer('.', 'ROOT\CIMV2', '', '', '',
'', wbemConnectFlagUseMaxWait, nil);

Finally
WMILocator.Free;
Screen.Cursor:= OldCursor;
end;
end;

IV - Récupérer une collection d'instances d'objet

Une fois un objet SWbemServices initialisé, on utilise sa méthode .InstancesOf.

Elle renvoie, dans un objet SwbemObjectSet, une collection d'instances de la classe spécifiée.

Var
WmiService:          SWbemServices;
wmiObjectSet:        SWbemObjectSet;

Begin
…
wmiObjectSet := wmiService.InstancesOf('Win32_OperatingSystem',
wbemFlagReturnWhenComplete,
Nil);
…
end ;

V - Énumérer une collection WMI

Une fois une collection récupérée, on utilise une interface IEnumVariant qui implémente des fonctions d'énumération sur des données de type variant.

Elle nécessite le transtypage suivant sur un objet de type collection, ici WmiObjectSet :

ObjectEnumerator:= (WmiObjectSet._NewEnum) as IEnumVariant;

La méthode .Next permet une itération pour un ou plusieurs éléments. Elle renvoie S_OK tant qu'il reste des éléments à extraire.

L'élément récupéré de type OleVariant doit à son tour être transtypé pour permettre de le manipuler correctement :

WmiObject := IUnknown(ArrayVariant) as SWBemObject;

Ici l'objet ArrayVariant est transtypé vers la variable WmiObject de type SWBemObject.

On peut ensuite utiliser les propriétés et méthodes de la variable WmiObject .

Le transtypage de l'élément récupéré doit correspondre au type d'objet de la collection, par exemple pour une variable WmiProperty de type SWBemProperty :

WmiProperty:=IUnknown(ArrayVariant) as SWBemProperty;
Var
WmiObject:           SWbemObject;
wmiObjectSet:        SWbemObjectSet;

ObjectEnumerator:    IEnumVariant;

ArrayVariant:        OleVariant;
NumberItem:          LongWord;

Begin
….
// Affecte un énumérateur pour la collection d'objet SWbemObject
ObjectEnumerator:= (WmiObjectSet._NewEnum) as IEnumVariant;

// Retourne NumberItem éléments dans le tableau ArrayVariant,
// ici 1 élément est demandé
while (ObjectEnumerator.Next(1, ArrayVariant, NumberItem) = S_OK) do
begin
// Récupére de la collection l'objet SWbemObject courant
WmiObject := IUnknown(ArrayVariant) as SWBemObject;

// Retrouve la propriétés de l'instance au format MOF
Memo1.Lines.Add(AdjustLineBreaks(wmiObject.GetObjectText_(0)));
end;

VI - Afficher le contenu d'un objet

La méthode .GetObjectText_ d'un objet SwbemObject renvoie presque toutes les informations d'un objet (classe ou instance).

Le texte reçu est au format MOF (Managed Object Format) et doit être reformaté en raison de fin de ligne incomplète (CR /LF).

Memo1.Lines.Add(AdjustLineBreaks(wmiObject.GetObjectText_(0)));

VI-A - Retrouver le path d'un objet

L'objet SWbemObjectPath permet de retrouver des informations sur un objet.

// Retrouve l'objet SWbemObjectPath contenant toutes les propriétés
WmiObjectPath:= WmiObject.Path_;
// retrouve le nom de la classe via le chemin d'accés relatif.
ClasseList.Add(WmiObjectPath.RelPath);

VII - Récupérer une instance ou une définition de classe

Une fois un objet SWbemServices initialisé, on utilise sa méthode .Get.

Elle renvoie, dans un objet SwbemObject, une instance ou une définition de classe si le paramètre strObjectPath contient respectivement un chemin d'objet d'instance ou un chemin d'objet de classe.

Chemin d'objet d'instance

WmiObject:= Service.Get(' Win32_Directory.Name="c:\windows\system32\wbem", 0, nil);

La propriété Name étant ici la clé de l'instance.

Chemin d'objet de classe

WmiObject:=WmiService.Get('Win32_ Directory ',0,Nil);

VIII - Récupérer une collection de propriétés

La propriété Properties_ de l'objet SWbemObject renvoie une collection de type SwbemPropertySet contenant toutes les propriétés de la classe ou de l'instance d'objet.

Var
ListePropriétés : SwbemPropertySet ;
WmiObjet :        SWbemObject ;

Begin
ListePropriétés := WmiObjet.Properties_;

IX - Récupérer une collection de qualificateurs

La propriété Qualifiers_ de l'objet SWbemObject renvoie une collection de type SwbemQualifiersSet contenant toutes les propriétés de la classe ou de l'instance d'objet.

Var
ListeQualificateurs : SwbemQualifiersSet ;
WmiObjet :              SWbemObject ;

Begin
ListeQualificateurs := WmiObjet.Qualifiers_;

X - Récupérer une collection de méthodes

La propriété Methods_ de l'objet SWbemObject renvoie une collection de type SwbemMethodsSet contenant toutes les propriétés de la classe ou de l'instance d'objet.

Var
ListeMethodes : SwbemMethodsSet ;
WmiObjet :        SWbemObject ;

Begin
ListeMethods := WmiObjet.Methods_;

XI - Récupérer un élément d'une collection en utilisant son nom

La méthode .Item d'un objet collection permet d'extraire un élément par son nom.

WmiProperty:= Instance.Properties_.Item('CommandLine',0);

XII - Afficher une propriété

Un objet SWBemProperty représente une propriété de classe. Cet objet possède la propriété Name qui contient le nom de la propriété manipulée.

Avant de récupérer sa valeur par la méthode .Get_Value, on doit lire la propriété CIMType qui détermine le type de donnée de la propriété manipulée puis la convertir vers une chaîne de caractères.

Ici la propriété de nom Description est de type string (wbemCimtypeString), on peut donc directement l'insérer dans un TMemo.

...
If WmiProperty.name='Description' then
if WmiProperty.CIMType=wbemCimtypeString then
Memo1.Lines.Add(WmiProperty.Get_Value);
else Convertion
...

XII-A - Gestion des différents types de donnée

Voici une fonction de conversion prenant en charge tous les types de donnée possible pour la propriété CIMType.

Function WMIPropertyToStr(WmiService:SWbemServices; WmiProperty : SWbemProperty):String;
var sValue: String;
Count:  Integer;
Value: Variant;
WmiObjet : SWbemObject;

begin
try
Value:=WmiProperty.Get_Value;
sValue := '';
if VarIsNull(Value) then
sValue := 'Is Null'
else
try
case WmiProperty.CIMType of
wbemCimtypeSint8,
wbemCimtypeUint8,
wbemCimtypeSint16,
wbemCimtypeUint16,
wbemCimtypeSint32,
wbemCimtypeUint32,
wbemCimtypeSint64,
wbemCimtypeUint64:
if VarIsArray(Value) then begin
if VarArrayHighBound(Value, 1) > 0 then
for Count := 1 to VarArrayHighBound(Value, 1) do
sValue := sValue + ' ' + IntToStr(Value[Count]);
end
else
sValue := IntToStr(Value);

wbemCimtypeReal32,
wbemCimtypeReal64:
sValue := FloatToStr(Value);

wbemCimtypeBoolean:
if Value then sValue := 'True' else sValue := 'False';

// Single 16-bit Unicode character in Universal Character Set-2 (UCS-2) format
wbemCimtypeChar16,
// Unicode character string
wbemCimtypeString:
if VarIsArray(Value) then begin
if VarArrayHighBound(Value, 1) > 0 then
try
for Count := 1 to VarArrayHighBound(Value, 1) do
sValue := sValue + ' ' + Value[Count];
except
on E: EConvertError do
ShowMessage(E.Message);
end;
end
else
sValue := Value;

wbemCimtypeDatetime:
sValue := Value;

// Référence d'objet, le chemin de l'objet peut être
//    un nom complet serveur\espace de nom
//    ou relatif dans le même espace de nom
wbemCimtypeReference: begin
// Récupére l'objet référençé
WmiObjet:=WmiService.Get(Value,wbemFlagReturnWhenComplete,Nil);
sValue:=AdjustLineBreaks(WmiObjet.GetObjectText_(0))
end;
// Objet imbriqué
// Renvoie le path de la propriété de type Objet
wbemCimtypeObject: begin
// Récupére l'objet imbriqué
WmiObjet:=IUnknown(WmiProperty.Get_Value) as SWbemObject ;
sValue:=WmiObjet.Path_.Relpath;
end;
end; // case
except
on E: EVariantError do
ShowMessage('WMIPropertyToStr : Erreur de convertion de variant');
end;
Finally
Result:=sValue;
end;
end;

XIII - Modifier une propriété

La méthode .Set_Value permet de modifier le contenue d'une propriété.

Dans l'exemple suivant on modifie la propriété CommandLine de la méthode .Create de la classe Win32_Process.

Var
PropertyCommandLine: OleVariant;
Instance_inParameters:   SWbemObject;

Begin
WmiProperty:= Instance_inParameters.Properties_.Item('CommandLine',0);
PropertyCommandLine:= 'Notepad.exe';
WmiProperty.Set_Value(PropertyCommandLine);

Dans l'unité Delphi la déclaration de la propriété Value de l'objet SWBemProperty n'existe pas, utilisez la méthode .Set_Value.

XIV - Mettre à jour un objet dans le référentiel WMI

Une fois un objet modifié, exécutez sa méthode .Put_ afin de le modifier dans le référentiel WMI.

Var
WmiObject:           SWbemObject;

PropertyMaxFileSize: SWbemProperty;

MaxFileSize :  OleVariant;

Begin
// Retrouve la propriété à modifier
PropertyMaxFileSize:= WmiObject.Properties_.Item('MaxFileSize',0);

// Nouvelle valeur de la propriété
MaxFileSize:= 512000; // 512 Ko (unité byte)

//Renseigne la valeur de la propriété
PropertyMaxFileSize.Set_Value(MaxFileSize);

// Mise à jour de l'instance courante
WmiObject.Put_(wbemFlagReturnWhenComplete,Nil) ;

XV - Exécuter une requête WQL

La méthode .ExecQuery de l'objet SwbemServices permet d'interroger le référentiel en exécutant une requête WQL synchrone.

Cette méthode renvoie une collection de type SwbemObjectSet contenant des objets correspondants à la requête.

Le premier paramètre peut être une variable chaîne de caractère.

var
WmiService:          SWbemServices;

WmiObject:           SWbemObject;
wmiObjectSet:        SWbemObjectSet;

ObjectEnumerator:    IEnumVariant;
ArrayVariant:        OleVariant;  // Tableau de variant
NumberItem:          LongWord;

begin
…
WmiObjectSet := wmiService.ExecQuery('SELECT Name FROM Win32_Service Where
State=''Stopped''',
'WQL',
wbemFlagReturnImmediately, nil);

XVI - Modifier les privilèges de sécurité

La propriété Security_ d'un objet SWbemServices contient la collection Privileges contenant des objets de type SWbemPrivilege permet de modifier les priviléges de sécurité.

Dans l'exemple suivant on ajoute le privilège 'Shutdown' avant d'exécuter la méthode Win32Shutdown de la classe Win32_OperatingSystem.

WmiServices.Security_.Privileges.Add(wbemPrivilegeShutdown, True);

XVII - Exécuter une méthode

La méthode ExecMethod_ d'un objet SWbemObject permet d'exécuter une méthode de classe ou d'instance. Cette méthode renvoie un objet qui contient la valeur de retour et les paramètres modifiés.

Mais son exécution nécessite quelques traitements préliminaires.

Retrouver la définition, de la classe ou de l'instance concernée, par le méthode .Get.

Modifier si besoin les privilèges de sécurité nécessaires à l'appel de méthode.

Récupérer, à partir de la collection Methods_ , les propriétés de la méthode que vous souhaitez exécuter. Pour ce faire vous pouvez utiliser la méthode .Item.

Créer une nouvelle instance de la méthode de la classe concernée en utilisant la méthode .SpawnInstance_ . Cette création alloue l'espace mémoire pour manipuler les paramètres de cette méthode. La collection InParameters.properties_ contient le nombre, le nom et le type des paramètres de cette nouvelle instance de méthode.

Renseigner les paramètres de la méthode avec les valeurs appropriées.

Appeler la méthode .ExecMethod_.

Et enfin si vous souhaitez contrôler les valeurs de retour de la méthode consulter les propriétés de l'objet renvoyé par l‘exécution de la méthode .ExecMethod_.

var
WMILocator:            TSWbemLocator;

Process,
NewProcess,
Instance_inParameters: SWbemObject;

WmiProperty:           SWbemProperty;

ProcessMethod:         ISWbemMethod;

PropertyCommandLine,
PropertyReturnValue,
ProcessID        OleVariant;

begin
WMILocator:= TSWbemLocator.Create(self);

try
// Création d'une connexion à un espace de nom local
// ici le nom de l'espace de nom cible est : CIMV2
// L'appel renvoie un pointeur sur un objet SWbemServices
WmiService:= WMILocator.ConnectServer('.', 'ROOT\CIMV2', '', '', '',
'', wbemConnectFlagUseMaxWait, nil);

// Retrouve la définition de la classe Win32_Process
// Sur le site de MS, la classe Win32_Process se trouve dans :
//   Win32 Classes.Operating system Classes.Process
Process:=WmiService.Get('Win32_Process',0,Nil);

// Si vous devez modifier la sécurité nécessaire à l'appel de méthode,
// par exemple 'Shutdown' utilisez :
//     wmiServices.Security_.Privileges.Add(wbemPrivilegeShutdown, True);

// Récupère, à partir de la collection de méthode,
// la définition de la méthode 'Create' de la classe Win32_Process
// qui correspond à ceci :
{    Win32_Process.Create(IN CommandLine string ,
IN CurrentDirectory   string ,
IN ProcessStartupInformation Win32_ProcessStartup ,
OUT ProcessId uint32)
Valeur de retour :
[Out] ProcessId uint32 ;
[out] ReturnValue uint32 ;
}

ProcessMethod:=Process.Methods_.Item('Create', 0);

// Crée une nouvelle instance de la méthode '.Create' de la classe Win32_Process
// alloue l'espace mémoire pour manipuler ces paramètres.
// ProcessMethod.InParameters.Properties_ contient la liste des paramètres de la méthode
Instance_inParameters:= ProcessMethod.InParameters.SpawnInstance_(0);

// Retrouve la propriété 'CommandLine' de la méthode '.Create'
WmiProperty:= Instance_inParameters.Properties_.Item('CommandLine',0);

// Nom du programe à exécuter
PropertyCommandLine:= 'Notepad.exe';

//Renseigne la valeur de la propriété 'CommandLine' de la méthode '.Create'
WmiProperty.Set_Value(PropertyCommandLine);

//Crée un process Via la Méthode '.Create'
NewProcess:= Process.ExecMethod_('Create', Instance_inParameters, wbemFlagReturnWhenComplete, nil);

{ Un objet OutParameters est créé par la méthode exécutée }

// Récupère la valeur de retour de la méthode via la propriété 'ReturnValue'
PropertyReturnValue:= NewProcess.Properties_.Item('ReturnValue', 0);

if PropertyReturnValue.Value = 0 then
begin
// Réutilise la propriété pour récupérer l'ID de process
PropertyReturnValue:= NewProcess.Properties_.Item('ProcessId', 0);
ProcessID:=PropertyReturnValue.Value;
MessageBox(0, PChar('Process crée.'+#10#13+'Pid : '+inttostr(ProcessID)),
PChar(Form1.Caption), MB_OK);
end
else
MessageBox(0, PChar('Erreur '+inttostr(PropertyReturnValue.Value)+' lors de la création du process.'),
PChar(Form1.Caption), MB_OK);
Finally
WMILocator.Free;
end;

XVIII - Exécuter une requête d'événement WQL synchrone

La méthode .ExecNotificationQuery de l'objet SwbemServices permet d'interroger le référentiel périodiquement (polling) en exécutant une requête WQL d'événement synchrone.

Le polling de l'événement se fait dans l'intermédiaire de WMI et non par un provider.

Cette méthode renvoie un objet de type SwbemEventSource.

La notification d'événement se faisant via l'appel de sa méthode .NextEvent.

L‘intervalle de polling recommandé est de 300 secondes pour éviter de monopoliser les ressources de la machine.

Dans cet exemple on souhaite récupérer les événements de suppression d'instance de process en utilisant la classe d'événement __instancedeletionevent sur la propriété TargetInstance de classe Win32_Process.

Seuls les événements concernant les instances de process en cours ayant changées pendant l'intervalle de polling seront détectés.

Si après l'exécution de la requête et pendant l'intervalle de polling de 300 secondes, vous exécutiez une application puis la supprimiez tout de suite après, un événement __instancedeletionevent est déclenché dans le référentiel WMI.

Mais cet événement ne sera pas 'vue' par la requête, car le contexte (nombre de process) ne présentera aucun changement entre le moment où la requête est exécutée par la méthode .ExecNotificationQuery et le moment où l'intervalle de polling se déclenche afin de contrôler si un événement attendu c'est produit.

Il n'a donc pas, avec ce type de requête, d'historique des événements déclenchés pendant la durée du polling.

Le premier paramètre peut être une variable chaîne de caractères.

Var
WmiMonitoredProcesses:      SWbemEventSource;

WmiLatestProcess,
WmiObjet:                    SWbemObject;

Query :             String;
Begin

// Recherche les événements de type '__instancedeletionevent' (Intrinsèque).
// WMI effectue le polling toute les 1 secondes.
// Seule les instances de la classe Win32_Process sont concernées.
Query:='Select * from __instancedeletionevent within 1 where TargetInstance isa "Win32_Process"';
WmiMonitoredProcesses:=WMIService.ExecNotificationQuery(Query,
'WQL',
wbemFlagForwardOnly +
wbemFlagReturnImmediately,
Nil);

XIX - Récupérer une notification d'événement synchrone

La notification d'événement se fait par l'appel de la méthode .NextEvent d'un objet de type SwbemEventSource.

Cette méthode est bloquante car elle attend un événement avant de se terminer.

Si un événement est disponible elle le retrouve et renvoie un objet SWbemObject contenant une instance de la classe d'événement demandé par la requête associée.

Var
WmiMonitoredProcesses:      SWbemEventSource;

WmiLatestProcess,
WmiObjet:                    SWbemObject;

Query :             String;

Begin
Query:='Select * from __instancedeletionevent within 1 where TargetInstance isa "Win32_Process"';
WmiMonitoredProcesses:=WMIService.ExecNotificationQuery(Query,
'WQL',
wbemFlagForwardOnly +
wbemFlagReturnImmediately,
Nil);

// Renvoie une classe de type '__InstanceDeletionEvent'
// -1 = Attente indéfinie. Le traitement attend un événement...
WmiLatestProcess:= WmiMonitoredProcesses.NextEvent(-1);

// Retrouve l'objet contenue dans la propriété TargetInstance
// à partir de l'objet crée par la méthode WmiMonitoredProcesses.NextEvent
// L'objet retrouvé contient une copie de l'instance du process supprimé.
WmiObjet:=IUnknown(WmiLatestProcess.Properties_.Item('TargetInstance',0).Get_Value) as SWbemObject;

XX - Exécuter une requête d'événement WQL asynchrone

Ajouter un composant TSWbemSink sur votre fiche. Renseignez ces gestionnaires d'événements :

OnCompleted est déclenché quand une opération asynchrone est terminée, l'appel de la méthode TSWbemSink.Cancel déclenche cet événement.

OnObjectPut est déclenché après une opération PUT asynchrone, ie. aprés mise à jour d'un instance ou d'une classe.

OnProgress est déclenché pour fournir le statut d'une opération asynchrone. Attention, cet événement n'est pas pris en charge par tous les providers.

OnObjectReady est déclenché quand un objet, fourni par un appel asynchrone est disponible. C'est cet événement qui sera le plus utilisé.

La méthode .ExecNotificationQueryAsync de l'objet SwbemServices permet d'interroger le référentiel périodiquement (polling) en exécutant une requête WQL d'événement asynchrone.

Elle similaire à la méthode .ExecNotificationQuery en dehors du fait que l'on doit spécifier un Object collecteur qui reçoit de WMI les notifications d'événements asynchrone et les renvoie vers le client.

À noter que le collecteur peut gérer les événements de plusieurs requêtes asynchrones.

Le code suivant exécute la requête de polling et déclenche la collecte des événements effectué par le composant TSWbemSink.

Vous devez renseigner le paramètre iFlags de l'appel asynchrone avec la valeur wbemFlagSendStatus si vous voulez recevoir des mises à jour de statut, autrement cet événement n'est pas déclenché. Sous réserve que le provider gère les événements de mises à jour de statut.

var
Query:               String;

begin
Query:='Select * from __instancedeletionevent within 1 where TargetInstance isa "Win32_Process"';

WMIService.ExecNotificationQueryAsync(SWbemSink1.DefaultInterface,
Query,
'WQL',
wbemFlagSendStatus,
Nil, Nil);
end;

XXI - Récupérer une notification d'événement asynchrone

La notification d'événement se fait via le gestionnaire d'événements TSWbemSink.OnObjectReady d'un objet de type TSWbemSink.

Dés qu'un objet est disponible WMI déclenche cet événement et renvoie un objet contenant une instance de la classe demandé par la requête associée.

Dans l'exemple suivant le code est lié à la requête suivante :

Select * from __instancedeletionevent within 1 where TargetInstance isa "Win32_Process"

L'objet reçu, objWbemObject, est une instance de la classe '__InstanceDeletionEvent'.

On doit donc transtyper sa propriété TargetInstance pour récupérer la copie de l'instance du process supprimé.

procedure TForm1.SWbemSink1ObjectReady(Sender: TObject; var objWbemObject,
objWbemAsyncContext: OleVariant);
// Déclenché lorsqu'un objet fourni par l'appel asynchrone est disponible.
// ObjWbemObject contient l'objet retourné

var WmiObjet: ISWbemObject;

begin
// L'objet retourné contient une copie de l'instance du process supprimé.
WmiObjet:=IUnknown(objWbemObject.Properties_.Item('TargetInstance',0).Value) as ISWbemObject;

Memo1.Lines.Add(#13#10+'Le process '+WMIVariantToStr(WmiObjet.Properties_.Item('Name',0).Get_Value)+
' s''est terminé.'+#13#10);
Memo1.Lines.Add(WmiObjet.Path_.path);
Memo1.Lines.Add(AdjustLineBreaks(WmiObjet.GetObjectText_(0)));
end;

XXII - Où trouver des informations sur les classes WMI disponibles ?

Liste et définitions des classes WMI.

Par exemple vous trouverez des informations sur Windows dans une des catégories de base nommée Win32 Classes qui contient les sous-catégories suivantes :

- Computer system hardware

- Operating system Classes

- Installed applications

- WMI service management

- Performance counters

XXIII - Windows Management Instrumentation (WMI), première partie.

Article précédent : Les bases de WMI.