C++에서 성능 데이터 액세스

WMI 고성능 API는 성능 카운터 클래스에서 데이터를 가져오는 일련의 인터페이스입니다. 이 인터페이스가 샘플링 속도를 높이려면 리프레셔 개체를 사용해야 합니다. 스크립팅에서 리프레셔 개체를 사용하는 방법에 대한 자세한 내용은 스크립트에서 성능 데이터 액세스WMI 작업: 성능 모니터링을 참조하세요.

이 항목에서 다루는 섹션은 다음과 같습니다.

성능 데이터 새로 고침

리프레셔 개체는 프로세스 경계를 넘지 않는 상태로 데이터를 검색하여 데이터 공급자 및 클라이언트의 성능을 향상합니다. 클라이언트와 서버가 동일한 컴퓨터에 있는 경우 리프레셔는 프로세스 내에서 고성능 공급자를 클라이언트에 로드하고 공급자 개체에서 클라이언트 개체로 직접 데이터를 복사합니다. 클라이언트와 서버가 서로 다른 컴퓨터에 있는 경우 리프레셔는 원격 컴퓨터에 개체를 캐싱하고 최소한의 데이터 세트를 클라이언트로 전송하여 성능을 향상합니다.

리프레셔는 다음 작업도 수행합니다.

  • 네트워크 오류가 발생하거나 원격 컴퓨터가 다시 시작되면 클라이언트를 원격 WMI 서비스에 자동으로 다시 연결합니다.

    두 컴퓨터 간의 원격 연결이 실패한 경우 리프레셔는 기본적으로 애플리케이션을 관련 고성능 공급자에 다시 연결하려고 시도합니다. 재연결을 방지하려면 Refresh 메서드 호출에서 WBEM_FLAG_REFRESH_NO_AUTO_RECONNECT 플래그를 전달합니다. 스크립팅 클라이언트는 SWbemRefresher.AutoReconnect 속성을 FALSE로 설정해야 합니다.

  • 동일한 공급자나 다른 공급자가 제공하는 여러 개체와 열거자를 로드합니다.

    여러 개체, 열거자 또는 둘 다를 리프레셔에 추가할 수 있도록 지원합니다.

  • 개체를 열거합니다.

    고성능 공급자는 다른 공급자와 마찬가지로 개체를 열거할 수 있습니다.

고성능 클라이언트의 작성을 완료한 후에는 응답 시간을 개선해야 할 수 있습니다. IWbemObjectAccess 인터페이스는 속도에 최적화되어 있으므로 본질적으로 스레드세이프가 아닙니다. 따라서 새로 고침 작업 중에는 새로 고칠 수 있는 개체 또는 열거에 액세스하지 않아야 합니다. IWbemObjectAccess 메서드 호출 중에 스레드에 있는 모든 개체를 보호하려면 IWbemObjectAccess::LockUnlock 메서드를 사용합니다. 성능을 개선하려면 개별 스레드를 잠글 필요가 없도록 스레드를 동기화합니다. 스레드를 줄이고 새로 고침 작업을 위해 개체 그룹을 동기화하면 전반적으로 가장 높은 성능을 얻을 수 있습니다.

WMI 리프레셔에 열거자 추가

인스턴스의 개수와 각 인스턴스의 데이터는 IWbemRefresher::Refresh가 호출될 때마다 완전한 열거가 생성되도록 리프레셔에 열거자를 추가하여 새로 고쳐집니다.

다음 C++ 코드 예제가 올바르게 컴파일되려면 다음 참조 및 #include 문이 필요합니다.

#define _WIN32_DCOM

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

다음 절차에서는 리프레셔에 열거자를 추가하는 방법을 보여 줍니다.

리프레셔에 열거자를 추가하려면 다음을 수행합니다.

  1. 새로 고칠 수 있는 개체의 경로와 IWbemServices 인터페이스를 사용하여 IWbemConfigureRefresher::AddEnum 메서드를 호출합니다.

    리프레셔는 IWbemHiPerfEnum 인터페이스에 대한 포인터를 반환합니다. IWbemHiPerfEnum 인터페이스를 사용하여 열거에 있는 개체에 액세스할 수 있습니다.

    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. 다음 작업을 수행하는 루프를 만듭니다.

다음 C++ 코드 예제에서는 클라이언트가 첫 번째 개체의 속성 핸들을 가져온 다음 새로 고침 작업의 나머지 부분에서 이 핸들을 재사용하는 고성능 클래스를 열거합니다. 각 Refresh 메서드 호출은 인스턴스의 개수와 인스턴스 데이터를 업데이트합니다.

#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;
}

성능 카운터 클래스

스크립트에서 성능 데이터 액세스

스크립트에서 WMI 데이터 새로 고침

WMI 작업: 성능 모니터링

성능 데이터 모니터링

형식이 지정된 성능 카운터 클래스의 속성 한정자

WMI 성능 카운터 형식

Wmiadap.exe

QueryPerformanceCounter