Accès aux données de performances en C++

L’API hautes performances WMI est une série d’interfaces qui obtiennent des données à partir de classes de compteur de performances. Ces interfaces nécessitent l’utilisation d’un objet actualisateur pour augmenter le taux d’échantillonnage. Pour plus d’informations sur l’utilisation de l’objet actualisateur dans les scripts, consultez Accès aux données de performances dans le script et Tâches WMI : Analyse des performances.

Les sections suivantes sont abordées dans cette rubrique :

Actualisation des données de performances

Un objet actualisateur augmente les performances du fournisseur de données et du client en récupérant des données sans dépasser les limites du processus. Si le client et le serveur se trouvent sur le même ordinateur, un actualisateur charge le fournisseur hautes performances in-process sur le client et copie les données directement des objets fournisseurs dans des objets client. Si le client et le serveur se trouvent sur des ordinateurs différents, l’actualisateur augmente les performances en mettant en cache les objets sur l’ordinateur distant et en transmettant des jeux de données minimaux au client.

Un actualisateur effectue également les opérations suivantes :

  • Il reconnecte automatiquement un client à un service WMI distant quand une erreur réseau se produit ou que l’ordinateur distant est redémarré.

    Par défaut, un actualisateur tente de reconnecter votre application au fournisseur hautes performances approprié en cas d’échec d’une connexion à distance entre les deux ordinateurs. Pour empêcher la reconnexion, transmettez l’indicateur WBEM_FLAG_REFRESH_NO_AUTO_RECONNECT dans l’appel de la méthode Refresh. Les clients de script doivent définir la propriété SWbemRefresher.AutoReconnect sur FALSE.

  • Il charge plusieurs objets et énumérateurs fournis par le même fournisseur ou par des fournisseurs différents.

    Cela vous permet d’ajouter plusieurs objets, énumérateurs ou les deux à un actualisateur.

  • Il énumère les objets.

    Comme d’autres fournisseurs, un fournisseur hautes performances peut énumérer des objets.

Une fois que vous aurez terminé d’écrire votre client hautes performances, vous souhaiterez peut-être améliorer votre temps de réponse. Comme l’interface IWbemObjectAccess est optimisée pour la vitesse, l’interface n’est pas intrinsèquement sécurisée au niveau des threads. Par conséquent, pendant une opération d’actualisation, n’accédez pas à l’objet ou à l’énumération actualisable. Pour protéger les objets sur les threads pendant les appels de méthode IWbemObjectAccess, utilisez les méthodes IWbemObjectAccess::Lock et Unlock. Pour améliorer les performances, synchronisez vos threads pour ne pas avoir besoin de verrouiller des threads individuels. La réduction des threads et la synchronisation des groupes d’objets pour les opérations d’actualisation offrent les meilleures performances globales.

Ajout d’énumérateurs à l’actualisateur WMI

Le nombre d’instances et de données dans chaque instance sont actualisés en ajoutant un énumérateur à l’actualisateur afin que chaque appel à IWbemRefresher::Refresh entraîne une énumération complète.

L’exemple de code C++ suivant nécessite les références et les instructions #include suivantes pour être compilé correctement.

#define _WIN32_DCOM

#include <iostream>
using namespace std;
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")

La procédure suivante montre comment ajouter un énumérateur à un actualisateur.

Pour ajouter un énumérateur à un actualisateur

  1. Appelez la méthode IWbemConfigureRefresher::AddEnum à l’aide du chemin d’accès à l’objet actualisable et de l’interface IWbemServices.

    L’actualisateur retourne un pointeur vers une interface IWbemHiPerfEnum. Vous pouvez utiliser l’interface IWbemHiPerfEnum pour accéder aux objets de l’énumération.

    IWbemHiPerfEnum* pEnum = NULL;
    long lID;
    IWbemConfigureRefresher* pConfig;
    IWbemServices* pNameSpace;
    
    // Add an enumerator to the refresher.
    if (FAILED (hr = pConfig->AddEnum(
        pNameSpace, 
        L"Win32_PerfRawData_PerfProc_Process", 
        0, 
        NULL,
        &pEnum, 
        &lID)))
    {
        goto CLEANUP;
    }
    pConfig->Release();
    pConfig = NULL;
    
  2. Créez une boucle qui effectue les actions suivantes :

Exemple

L’exemple de code C++ suivant énumère une classe hautes performances, où le client récupère un handle de propriété à partir du premier objet et réutilise le handle pour le reste de l’opération d’actualisation. Chaque appel à la méthode Refresh met à jour le nombre d’instances et les données d’instance.

#define _WIN32_DCOM

#include <iostream>
using namespace std;
#include <Wbemidl.h>

#pragma comment(lib, "wbemuuid.lib")

int __cdecl wmain(int argc, wchar_t* argv[])
{
    // To add error checking,
    // check returned HRESULT below where collected.
    HRESULT                 hr = S_OK;
    IWbemRefresher          *pRefresher = NULL;
    IWbemConfigureRefresher *pConfig = NULL;
    IWbemHiPerfEnum         *pEnum = NULL;
    IWbemServices           *pNameSpace = NULL;
    IWbemLocator            *pWbemLocator = NULL;
    IWbemObjectAccess       **apEnumAccess = NULL;
    BSTR                    bstrNameSpace = NULL;
    long                    lID = 0;
    long                    lVirtualBytesHandle = 0;
    long                    lIDProcessHandle = 0;
    DWORD                   dwVirtualBytes = 0;
    DWORD                   dwProcessId = 0;
    DWORD                   dwNumObjects = 0;
    DWORD                   dwNumReturned = 0;
    DWORD                   dwIDProcess = 0;
    DWORD                   i=0;
    int                     x=0;

    if (FAILED (hr = CoInitializeEx(NULL,COINIT_MULTITHREADED)))
    {
        goto CLEANUP;
    }

    if (FAILED (hr = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_NONE,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL, EOAC_NONE, 0)))
    {
        goto CLEANUP;
    }

    if (FAILED (hr = CoCreateInstance(
        CLSID_WbemLocator, 
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator,
        (void**) &pWbemLocator)))
    {
        goto CLEANUP;
    }

    // Connect to the desired namespace.
    bstrNameSpace = SysAllocString(L"\\\\.\\root\\cimv2");
    if (NULL == bstrNameSpace)
    {
        hr = E_OUTOFMEMORY;
        goto CLEANUP;
    }
    if (FAILED (hr = pWbemLocator->ConnectServer(
        bstrNameSpace,
        NULL, // User name
        NULL, // Password
        NULL, // Locale
        0L,   // Security flags
        NULL, // Authority
        NULL, // Wbem context
        &pNameSpace)))
    {
        goto CLEANUP;
    }
    pWbemLocator->Release();
    pWbemLocator=NULL;
    SysFreeString(bstrNameSpace);
    bstrNameSpace = NULL;

    if (FAILED (hr = CoCreateInstance(
        CLSID_WbemRefresher,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IWbemRefresher, 
        (void**) &pRefresher)))
    {
        goto CLEANUP;
    }

    if (FAILED (hr = pRefresher->QueryInterface(
        IID_IWbemConfigureRefresher,
        (void **)&pConfig)))
    {
        goto CLEANUP;
    }

    // Add an enumerator to the refresher.
    if (FAILED (hr = pConfig->AddEnum(
        pNameSpace, 
        L"Win32_PerfRawData_PerfProc_Process", 
        0, 
        NULL, 
        &pEnum, 
        &lID)))
    {
        goto CLEANUP;
    }
    pConfig->Release();
    pConfig = NULL;

    // Get a property handle for the VirtualBytes property.

    // Refresh the object ten times and retrieve the value.
    for(x = 0; x < 10; x++)
    {
        dwNumReturned = 0;
        dwIDProcess = 0;
        dwNumObjects = 0;

        if (FAILED (hr =pRefresher->Refresh(0L)))
        {
            goto CLEANUP;
        }

        hr = pEnum->GetObjects(0L, 
            dwNumObjects, 
            apEnumAccess, 
            &dwNumReturned);
        // If the buffer was not big enough,
        // allocate a bigger buffer and retry.
        if (hr == WBEM_E_BUFFER_TOO_SMALL 
            && dwNumReturned > dwNumObjects)
        {
            apEnumAccess = new IWbemObjectAccess*[dwNumReturned];
            if (NULL == apEnumAccess)
            {
                hr = E_OUTOFMEMORY;
                goto CLEANUP;
            }
            SecureZeroMemory(apEnumAccess,
                dwNumReturned*sizeof(IWbemObjectAccess*));
            dwNumObjects = dwNumReturned;

            if (FAILED (hr = pEnum->GetObjects(0L, 
                dwNumObjects, 
                apEnumAccess, 
                &dwNumReturned)))
            {
                goto CLEANUP;
            }
        }
        else
        {
            if (hr == WBEM_S_NO_ERROR)
            {
                hr = WBEM_E_NOT_FOUND;
                goto CLEANUP;
            }
        }

        // First time through, get the handles.
        if (0 == x)
        {
            CIMTYPE VirtualBytesType;
            CIMTYPE ProcessHandleType;
            if (FAILED (hr = apEnumAccess[0]->GetPropertyHandle(
                L"VirtualBytes",
                &VirtualBytesType,
                &lVirtualBytesHandle)))
            {
                goto CLEANUP;
            }
            if (FAILED (hr = apEnumAccess[0]->GetPropertyHandle(
                L"IDProcess",
                &ProcessHandleType,
                &lIDProcessHandle)))
            {
                goto CLEANUP;
            }
        }
           
        for (i = 0; i < dwNumReturned; i++)
        {
            if (FAILED (hr = apEnumAccess[i]->ReadDWORD(
                lVirtualBytesHandle,
                &dwVirtualBytes)))
            {
                goto CLEANUP;
            }
            if (FAILED (hr = apEnumAccess[i]->ReadDWORD(
                lIDProcessHandle,
                &dwIDProcess)))
            {
                goto CLEANUP;
            }

            wprintf(L"Process ID %lu is using %lu bytes\n",
                dwIDProcess, dwVirtualBytes);

            // Done with the object
            apEnumAccess[i]->Release();
            apEnumAccess[i] = NULL;
        }

        if (NULL != apEnumAccess)
        {
            delete [] apEnumAccess;
            apEnumAccess = NULL;
        }

       // Sleep for a second.
       Sleep(1000);
    }
    // exit loop here
    CLEANUP:

    if (NULL != bstrNameSpace)
    {
        SysFreeString(bstrNameSpace);
    }

    if (NULL != apEnumAccess)
    {
        for (i = 0; i < dwNumReturned; i++)
        {
            if (apEnumAccess[i] != NULL)
            {
                apEnumAccess[i]->Release();
                apEnumAccess[i] = NULL;
            }
        }
        delete [] apEnumAccess;
    }
    if (NULL != pWbemLocator)
    {
        pWbemLocator->Release();
    }
    if (NULL != pNameSpace)
    {
        pNameSpace->Release();
    }
    if (NULL != pEnum)
    {
        pEnum->Release();
    }
    if (NULL != pConfig)
    {
        pConfig->Release();
    }
    if (NULL != pRefresher)
    {
        pRefresher->Release();
    }

    CoUninitialize();

    if (FAILED (hr))
    {
        wprintf (L"Error status=%08x\n",hr);
    }

    return 1;
}

Classes de compteur de performances

Accès aux données de performances dans le script

Actualisation des données WMI dans les scripts

Tâches WMI : Analyse des performances

Supervision des données de performance

Qualificateurs de propriétés pour les classes de compteur de performances formatées

Types de compteur de performances WMI

Wmiadap.exe

QueryPerformanceCounter