PCW_CALLBACK fonction de rappel (wdm.h)
Les fournisseurs peuvent éventuellement implémenter une PCW_CALLBACK
fonction pour recevoir des notifications lorsque les consommateurs effectuent des demandes telles que l’énumération d’instances ou la collecte de données de compteur. La bibliothèque de compteurs de performances (PERFLIB version 2.0) appelle la PCW_CALLBACK
fonction avant de terminer 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 la raison pour laquelle le rappel a été appelé. Les valeurs possibles sont PcwCallbackAddCounter
, PcwCallbackRemoveCounter
, PcwCallbackEnumerateInstances
et PcwCallbackCollectData
.
[in] Info
Pointeur vers une union de 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 Type
paramètre. 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 retournée
La PCW_CALLBACK
fonction de rappel doit retourner STATUS_SUCCESS
si le rappel s’est terminé sans erreurs, ou si un code d’erreur NTSTATUS
n’est pas indiqué. Notez que ce code de retour est à des fins d’information uniquement et que le traitement de la demande du consommateur se poursuivra même si le rappel retourne une erreur.
Remarques
Les fournisseurs de contre-ensembles peuvent fournir des informations au consommateur par le biais de deux systèmes différents :
Le fournisseur peut utiliser
PcwCreateInstance
etPcwCloseInstance
pour gérer une liste d’instances disponibles et les données de compteur correspondantes. Ce système est simple à implémenter, mais sa flexibilité est limitée. Lors de l’utilisation de 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 de PcwCreateInstance.Le fournisseur peut fournir une
PCW_CALLBACK
fonction qui sera appelée par la bibliothèque de compteurs de performances en fonction des besoins pour collecter des données.
L’implémentation du rappel doit être thread-safe. Plusieurs consommateurs différents peuvent demander simultanément des données au fournisseur sur différents threads.
Le rappel doit gérer les types de PcwCallbackEnumerateInstances
requête 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
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 responsable de la génération et Id
des Name
valeurs pour les instances de compteurset.
Les valeurs d’instance
Id
DOIVENT être stables au fil du temps (la même instance logique doit utiliser la mêmeId
valeur pour tous les appels du rappel), doivent être uniques (par exemple, n’utilisez pas simplement 0 pour toutes les instances) et doivent être inférieures à 0xFFFFFFFE (n’utilisezPCW_ANY_INSTANCE_ID
pas ). Dans la mesure du possible, le instanceId
doit être significatif (par exemple, un contre-ensemble de processus peut utiliser un PID commeId
) au lieu d’arbitraire (par exemple, un numéro de séquence).Les valeurs d’instance
Name
DOIVENT être stables dans le temps (la même instance logique doit utiliser la mêmeName
valeur pour tous les appels du rappel) et doivent être uniques. Si le compteur prend en charge plusieurs instances, le instanceName
ne doit pas être vide. La correspondance de chaînes est effectuée à l’aide d’une comparaison qui ne respecte pas la casse. Les valeurs ne doivent doncName
pas différer uniquement selon la casse.
Lors de la gestion des PcwCallbackCollectData
demandes, 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 compteur. Pour plus d’informations, consultez La 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 dans le compteur. Dans ce cas, le rappel peut optimiser la collecte de données en laissant les valeurs correspondantes 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 dontInstanceId
la valeur est égale àCollectData.InstanceId
. Le rappel peut optimiser la collecte de données en ignorant l’appel àPcwAddInstance
pour les instances dont la correspondance ne correspondInstanceId
pas à .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 dont la correspondance ne correspondInstanceName
pas à . La mise en correspondance de caractères génériques étant difficile à implémenter correctement, cette optimisation n’est recommandée que lorsque instance collecte de données est très coûteuse.
Dans la plupart des cas, l’implémentation de rappel d’une PcwCallbackEnumerateInstances
requête 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
(par exemple, en passant 0 et NULL pour les Count
paramètres 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 de contre-ensembles utilisent l’outil CTRPP pour traiter leur manifeste de contre-ensemble et générer des fonctions d’assistance, y compris l’encapsulage PcwRegister
des fonctions (CTRPP génère les descripteurs de compteur) et PcwAddInstance
(CTRPP génère du code pour l’encapsulage des structures de données du fournisseur au format requis par PcwAddInstance
).
Pour référence dans cet exemple, voici la fonction Register générée par CTRPP pour le manifeste de l’exemple KCS.man
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 contre-ensemble implémente la PCW_CALLBACK
fonction pour gérer les demandes des consommateurs. L’exemple de code suivant montre une PCW_CALLBACK
fonction nommée KcsGeometricWaveCallback
qui énumère et collecte des données simulées. (Notez qu’il KcsAddGeometricWave
s’agit d’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 DriverEntry
routine de l’exemple KCS, la KcsGeometricWaveCallback
fonction est spécifiée comme Callback
quand KcsRegisterGeometricWave
inscrit le compteur.
//
// Register Countersets during DriverEntry. (TODO: Unregister at driver unload.)
//
Status = KcsRegisterGeometricWave(KcsGeometricWaveCallback, NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
Configuration requise
Condition requise | Valeur |
---|---|
Client minimal pris en charge | Disponible dans Windows 7 et versions ultérieures de Windows. |
Plateforme cible | Desktop (Expérience utilisateur) |
En-tête | wdm.h (inclure Wdm.h, Ntddk.h) |
IRQL | IRQL <=APC_LEVEL |
Voir aussi
Bibliothèque de compteurs de performances (PERFLIB version 2.0)