PCW_CALLBACK fonction de rappel (wdm.h)
Les fournisseurs peuvent éventuellement implémenter une fonction de PCW_CALLBACK
pour recevoir des notifications lorsque les consommateurs effectuent des demandes telles que l’énumération d’instances ou la collecte de données de compteurs. La bibliothèque de compteurs de performances (PERFLIB version 2.0) appelle la fonction PCW_CALLBACK
avant d’effectuer la demande du consommateur.
Syntaxe
PCW_CALLBACK PcwCallback;
NTSTATUS PcwCallback(
[in] PCW_CALLBACK_TYPE Type,
[in] PPCW_CALLBACK_INFORMATION Info,
[in, optional] PVOID Context
)
{...}
Paramètres
[in] Type
Valeur d’énumération PCW_CALLBACK_TYPE indiquant pourquoi le rappel a été appelé. Les valeurs possibles sont PcwCallbackAddCounter
, PcwCallbackRemoveCounter
, PcwCallbackEnumerateInstances
et PcwCallbackCollectData
.
[in] Info
Pointeur vers une union PCW_CALLBACK_INFORMATION qui fournit des détails sur la raison pour laquelle le rappel du fournisseur a été appelé. Les détails se trouveront dans le champ correspondant au paramètre Type
. Par exemple, si Type == PcwCallbackEnumerateInstances
les détails se trouveront dans Info->EnumerateInstances
.
[in, optional] Context
Contexte de rappel fourni par le fournisseur lors de l’appel de PcwRegister ou lors de l’appel de la fonction Register générée par CTRPP (qui appelle PcwRegister
).
Valeur de retour
La fonction de rappel PCW_CALLBACK
doit retourner STATUS_SUCCESS
si le rappel s’est terminé sans erreur ou si un code d’erreur NTSTATUS
dans le cas contraire. Notez que ce code de retour est à des fins d’information uniquement et que le traitement de la demande du consommateur continuera même si le rappel retourne une erreur.
Remarques
Les fournisseurs de compteurs peuvent fournir des informations au consommateur via deux systèmes différents :
Le fournisseur peut utiliser
PcwCreateInstance
etPcwCloseInstance
pour conserver une liste d’instances disponibles et les données de compteur correspondantes. Ce système est simple à implémenter mais limité en flexibilité. Lorsque vous utilisez ce système, le fournisseur n’a pas besoin de fournir une fonction de rappel. Pour plus d’informations sur ce système, reportez-vous à la documentation relative à PcwCreateInstance.Le fournisseur peut fournir une fonction
PCW_CALLBACK
qui sera appelée par la bibliothèque de compteurs de performances si nécessaire pour collecter des données.
L’implémentation de rappel doit être thread-safe. Plusieurs consommateurs différents peuvent demander simultanément des données du fournisseur sur différents threads.
Le rappel doit gérer les types de requêtes PcwCallbackEnumerateInstances
et PcwCallbackCollectData
. Le rappel n’a généralement pas besoin de gérer d’autres types de requêtes, mais dans des scénarios complexes, le rappel peut également gérer PcwCallbackAddCounter
et PcwCallbackRemoveCounter
pour optimiser la collecte de données (par exemple, pour désactiver le suivi des statistiques lorsqu’aucune requête n’est active).
Le rappel est chargé de générer des valeurs de Name
et de Id
pour les instances de compteurs.
Les valeurs d’instance
Id
DOIVENT être stables au fil du temps (la même instance logique doit utiliser la même valeurId
pour tous les appels du rappel), doit être unique (par exemple, n’utilisez pas simplement 0 pour toutes les instances) et doit être inférieure à 0xFFFFFFFE (n’utilisez pasPCW_ANY_INSTANCE_ID
). Dans la mesure du possible, l’instanceId
doit être significative (par exemple, un compteur de processus peut utiliser un PID commeId
) au lieu d’un arbitraire (par exemple, un numéro de séquence).Les valeurs d’instance
Name
DOIVENT être stables au fil du temps (la même instance logique doit utiliser la même valeurName
pour tous les appels du rappel) et DOIT être unique. Si le compteur prend en charge plusieurs instances, l’instanceName
ne doit pas être vide. La correspondance de chaîne est effectuée à l’aide d’une comparaison non sensible à la casse. Par conséquent,Name
valeurs ne doivent pas différer uniquement par cas.
Lors de la gestion des requêtes PcwCallbackCollectData
, une implémentation de rappel de base appelle simplement PcwAddInstance (ou la fonction AddXxx générée par CTRPP) une fois pour chaque instance de counterset. Pour plus d’informations, consultez fonction AddXxx générée par CTRPP.
Les optimisations suivantes peuvent être utilisées dans des implémentations plus avancées si nécessaire :
Si
Info->CollectData.CounterMask != (UINT64)-1
, le consommateur n’a pas besoin de tous les compteurs du compteur. Dans ce cas, le rappel peut optimiser la collecte de données en laissant les valeurs correspondantes comme 0 dans le bloc de données du compteur.Si
Info->CollectData.InstanceId != PCW_ANY_INSTANCE_ID
le consommateur souhaite uniquement des données sur les instances avec unInstanceId
égal àCollectData.InstanceId
. Le rappel peut optimiser la collecte de données en ignorant l’appel àPcwAddInstance
pour les instances avec desInstanceId
non correspondantes.Si
Info->CollectData.InstanceMask != "*"
le consommateur souhaite uniquement des données sur les instances avec unInstanceName
qui correspond au modèle générique deCollectData.InstanceMask
. Le rappel peut optimiser la collecte de données en ignorant l’appel àPcwAddInstance
pour les instances avec desInstanceName
non correspondantes. La mise en correspondance par caractères génériques est difficile à implémenter correctement. Cette optimisation est donc recommandée uniquement lorsque la collecte de données d’instance est très coûteuse.
Dans la plupart des cas, l’implémentation de rappel d’une demande de PcwCallbackEnumerateInstances
sera identique à l’implémentation d’un PcwCallbackCollectData
. Le rappel peut éventuellement optimiser la collecte de données en omettant les données de compteur réelles dans l’appel à PcwAddInstance
(c’est-à-dire en passant 0 et NULL pour les paramètres Count
et Data
).
Une implémentation de rappel peut être structurée comme suit :
NTSTATUS NTAPI
MyProviderCallback(
_In_ PCW_CALLBACK_TYPE Type,
_In_ PPCW_CALLBACK_INFORMATION Info,
_In_opt_ PVOID Context)
{
PCW_MASK_INFORMATION* MaskInfo;
PAGED_CODE();
switch (Type)
{
case PcwCallbackCollectData:
MaskInfo = &Info->CollectData;
break;
case PcwCallbackEnumerateInstances:
MaskInfo = &Info->EnumerateInstances;
break;
case PcwCallbackAddCounter:
// Optional (for optimizing data collection):
// InterlockedIncrement(&CollectionEnableCount);
return STATUS_SUCCESS; // Normally no action needed.
case PcwCallbackRemoveCounter:
// Optional (for optimizing data collection):
// InterlockedDecrement(&CollectionEnableCount);
return STATUS_SUCCESS; // Normally no action needed.
}
// Common code for CollectData and EnumerateInstances.
// Note that this code needs to be thread-safe, as multiple
// threads might invoke this callback at the same time.
for (Instance : InstanceList) { // Pseudocode, need thread-safe enumeration
NTSTATUS Status;
// Optional optimization:
// if (MaskInfo->InstanceId != PCW_ANY_INSTANCE_ID && Instance->Id != MaskInfo->InstanceId) {
// continue;
// }
// Note that in most cases, you'll use a CTRPP-generated Add wrapper instead of directly
// calling PcwAddInstance.
Status = PcwAddInstance(MaskInfo->Buffer,
&Instance->Name,
Instance->Id,
1, // Number of items in PcwData array
&Instance->PcwData);
if (!NT_SUCCESS(Status)) {
return Status;
}
}
return STATUS_SUCCESS;
}
La plupart des fournisseurs d’ensembles de compteurs utilisent l’outil CTRPP pour traiter leur manifeste d’ensemble de compteurs et générer des fonctions d’assistance, notamment l’habillage de fonctions PcwRegister
(CTRPP génère les descripteurs de compteurs) et PcwAddInstance
(CTRPP génère du code permettant d’encapsuler les structures de données du fournisseur dans le format requis par PcwAddInstance
).
Pour référence dans cet exemple, voici la fonction Register générée par CTRPP pour le manifeste KCS.man
de l’exemple KCS.
EXTERN_C FORCEINLINE NTSTATUS
KcsRegisterGeometricWave(
__in_opt PPCW_CALLBACK Callback,
__in_opt PVOID CallbackContext
)
{
PCW_REGISTRATION_INFORMATION RegInfo;
UNICODE_STRING Name = RTL_CONSTANT_STRING(L"Geometric Waves");
PCW_COUNTER_DESCRIPTOR Descriptors[] = {
{ 1, 0, FIELD_OFFSET(GEOMETRIC_WAVE_VALUES, Triangle), RTL_FIELD_SIZE(GEOMETRIC_WAVE_VALUES, Triangle)},
{ 2, 0, FIELD_OFFSET(GEOMETRIC_WAVE_VALUES, Square), RTL_FIELD_SIZE(GEOMETRIC_WAVE_VALUES, Square)},
};
PAGED_CODE();
RtlZeroMemory(&RegInfo, sizeof RegInfo);
RegInfo.Version = PCW_CURRENT_VERSION;
RegInfo.Counters = Descriptors;
RegInfo.CounterCount = RTL_NUMBER_OF(Descriptors);
RegInfo.Callback = Callback;
RegInfo.CallbackContext = CallbackContext;
RegInfo.Name = &Name;
return PcwRegister(&KcsGeometricWave, &RegInfo);
}
Le fournisseur de compteurs implémente la fonction PCW_CALLBACK
pour gérer les demandes du consommateur. L’exemple de code suivant montre une fonction PCW_CALLBACK
nommée KcsGeometricWaveCallback
qui énumère et collecte des données simulées. (Notez que KcsAddGeometricWave
est une fonction d’assistance générée par CTRPP qui appelle PcwAddInstance
.)
NTSTATUS
KcsAddGeometricInstance (
_In_ PPCW_BUFFER Buffer,
_In_ PCWSTR Name,
_In_ ULONG Id,
_In_ ULONG MinimalValue,
_In_ ULONG Amplitude
)
{
ULONG Index;
LARGE_INTEGER Timestamp;
UNICODE_STRING UnicodeName;
GEOMETRIC_WAVE_VALUES Values;
PAGED_CODE();
KeQuerySystemTime(&Timestamp);
Index = (Timestamp.QuadPart / 10000000) % 10;
Values.Triangle = MinimalValue + Amplitude * abs(5 - Index) / 5;
Values.Square = MinimalValue + Amplitude * (Index < 5);
RtlInitUnicodeString(&UnicodeName, Name);
return KcsAddGeometricWave(Buffer, &UnicodeName, Id, &Values);
}
NTSTATUS NTAPI
KcsGeometricWaveCallback (
__in PCW_CALLBACK_TYPE Type,
__in PPCW_CALLBACK_INFORMATION Info,
__in_opt PVOID Context
)
{
NTSTATUS Status;
UNICODE_STRING UnicodeName;
UNREFERENCED_PARAMETER(Context);
PAGED_CODE();
switch (Type) {
case PcwCallbackEnumerateInstances:
//
// Instances are being enumerated, so we add them without values.
//
RtlInitUnicodeString(&UnicodeName, L"Small Wave");
Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer,
&UnicodeName,
0,
NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
RtlInitUnicodeString(&UnicodeName, L"Medium Wave");
Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer,
&UnicodeName,
1,
NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
RtlInitUnicodeString(&UnicodeName, L"Large Wave");
Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer,
&UnicodeName,
2,
NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
break;
case PcwCallbackCollectData:
//
// Add values for 3 instances of Geometric Wave Counterset.
//
Status = KcsAddGeometricInstance(Info->CollectData.Buffer,
L"Small Wave",
0,
40,
20);
if (!NT_SUCCESS(Status)) {
return Status;
}
Status = KcsAddGeometricInstance(Info->CollectData.Buffer,
L"Medium Wave",
1,
30,
40);
if (!NT_SUCCESS(Status)) {
return Status;
}
Status = KcsAddGeometricInstance(Info->CollectData.Buffer,
L"Large Wave",
2,
20,
60);
if (!NT_SUCCESS(Status)) {
return Status;
}
break;
}
return STATUS_SUCCESS;
}
Dans la routine DriverEntry
de l’exemple KCS, la fonction KcsGeometricWaveCallback
est spécifiée comme Callback
lorsque KcsRegisterGeometricWave
inscrit le compteur.
//
// Register Countersets during DriverEntry. (TODO: Unregister at driver unload.)
//
Status = KcsRegisterGeometricWave(KcsGeometricWaveCallback, NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
Exigences
Exigence | Valeur |
---|---|
client minimum pris en charge | Disponible dans Windows 7 et versions ultérieures de Windows. |
plateforme cible | Bureau |
d’en-tête | wdm.h (include Wdm.h, Ntddk.h) |
IRQL | IRQL <=APC_LEVEL |
Voir aussi
bibliothèque de compteurs de performances (PERFLIB version 2.0)