Partager via


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 et PcwCloseInstance 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ême Id 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’utilisez PCW_ANY_INSTANCE_IDpas ). Dans la mesure du possible, le instance Id doit être significatif (par exemple, un contre-ensemble de processus peut utiliser un PID comme Id) 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ême Name valeur pour tous les appels du rappel) et doivent être uniques. Si le compteur prend en charge plusieurs instances, le instance Name 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 donc Name 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 dont InstanceId 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 correspond InstanceIdpas à .

  • Si Info->CollectData.InstanceMask != "*" le consommateur souhaite uniquement des données sur les instances avec un InstanceName qui correspond au modèle générique de CollectData.InstanceMask. Le rappel peut optimiser la collecte de données en ignorant l’appel à PcwAddInstance pour les instances dont la correspondance ne correspond InstanceNamepas à . 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

PcwRegister

PcwAddInstance

PcwCreateInstance

PCW_CALLBACK_TYPE

PCW_CALLBACK_INFORMATION

CTRPP

Bibliothèque de compteurs de performances (PERFLIB version 2.0)