예: 원격 컴퓨터에서 WMI 데이터 가져오기

이 항목의 절차 및 코드 예제를 사용하여 COM 초기화를 수행하고, 원격 컴퓨터에서 WMI에 연결하고, 반동기적으로 데이터를 가져온 후 정리하는 완전한 WMI 클라이언트 응용 프로그램을 만들 수 있습니다. 로컬 컴퓨터에서 데이터를 가져오는 방법에 대한 자세한 내용은 예제: 로컬 컴퓨터에서 WMI 데이터 가져오기를 참조하세요. 비동기적으로 데이터를 가져오는 방법에 대한 자세한 내용은 예제: 로컬 컴퓨터에서 비동기적으로 WMI 데이터 가져오기를 참조하세요.

참고

원격 컴퓨터에 연결하려는 경우 원격으로 WMI에 연결의 정보를 참조하세요.

 

다음 절차는 WMI 응용 프로그램을 실행하는 방법을 보여줍니다. 1~5단계에서는 WMI를 설정하고 연결하며, 6~7단계에서는 데이터를 쿼리하고 수신합니다.

원격 컴퓨터에서 WMI 데이터 가져오기

  1. CoInitializeEx를 호출하여 COM 매개 변수를 초기화합니다.

    자세한 내용은 WMI 응용 프로그램에 대한 COM 초기화를 참조하세요.

  2. CoInitializeSecurity를 호출하여 COM 프로세스 보안을 초기화합니다.

    자세한 내용은 C++를 사용하여 기본 프로세스 보안 수준 설정을 참조하세요.

  3. CoCreateInstance를 호출하여 WMI에 대한 초기 로케이터를 가져옵니다.

    자세한 내용은 WMI 네임스페이스에 대한 연결 만들기를 참조하세요.

  4. IWbemLocator::ConnectServer를 호출하여 원격 컴퓨터에서 \\root\cimv2 네임스페이스의 IWbemServices에 대한 포인터를 가져옵니다. 원격 컴퓨터에 연결할 때 연결하려는 원격 컴퓨터의 컴퓨터 이름, 도메인, 사용자 이름 및 암호를 알아야 합니다. 이러한 특성은 모두 IWbemLocator::ConnectServer 메서드에 전달됩니다. 또한 원격 컴퓨터에 연결하려는 컴퓨터의 사용자 이름에 원격 컴퓨터에 대한 올바른 액세스 권한이 있는지 확인합니다. 자세한 내용은 Windows 방화벽을 통한 연결을 참조하세요. 로컬 컴퓨터에 연결하려면 예제: 로컬 컴퓨터에서 WMI 데이터 가져오기WMI 네임스페이스에 대한 연결 만들기를 참조하세요.

    사용자 이름 및 암호를 처리할 때 권한이 없는 사용자가 정보를 가로챌 가능성이 적어지도록 사용자에게 정보를 입력하라는 메시지를 표시하고 정보를 사용한 다음, 정보를 삭제하는 것이 좋습니다. 아래 예제 코드의 4단계에서는 CredUIPromptForCredentials를 사용하여 사용자 이름과 암호를 가져옵니다. 그런 다음 SecureZeroMemory를 사용하여 IWbemLocator::ConnectServer에서 사용한 정보를 제거합니다. 자세한 내용은 암호 처리 및 MSDN에서 사용자에게 자격 증명 요청을 참조하세요.

  5. 프록시 보안을 설정하기 위한 자격 증명을 제공하는 COAUTHIDENTITY 구조를 만듭니다.

  6. WMI 서비스가 CoSetProxyBlanket을 호출하여 클라이언트를 가장할 수 있도록 IWbemServices 프록시 보안을 설정합니다.

    자세한 내용은 WMI 연결에서 보안 수준 설정을 참조하세요.

  7. IWbemServices 포인터를 사용하여 WMI에 요청을 수행합니다. IWbemServices::ExecQuery를 호출하여 운영 체제의 이름과 사용 가능한 실제 메모리 양을 가져오는 쿼리가 실행됩니다.

    다음 WQL 쿼리는 메서드 인수 중 하나입니다.

    SELECT * FROM Win32_OperatingSystem

    이 쿼리의 결과는 IEnumWbemClassObject 포인터에 저장됩니다. 이를 통해 IEnumWbemClassObject 인터페이스를 사용하여 쿼리의 데이터 개체를 반동기적으로 검색할 수 있습니다. 자세한 내용은 WMI 열거를 참조하세요. 데이터를 비동기적으로 가져오는 방법은 예: 로컬 컴퓨터에서 비동기적으로 WMI 데이터 가져오기를 참조하세요.

    WMI의 요청을 수행하는 방법에 대한 자세한 내용은 클래스 및 인스턴스 정보 조작, WMI 쿼리메서드 호출을 참조하세요.

  8. IEnumWbemClassObject 열거자 프록시 보안을 설정합니다. 사용이 완료되면 메모리에서 자격 증명을 지워야 합니다.

    자세한 내용은 IWbemServices 및 기타 프록시에서 보안 설정을 참조하세요.

  9. WQL 쿼리에서 데이터를 가져와서 표시합니다. IEnumWbemClassObject 포인터는 쿼리가 반환한 데이터 개체에 연결되어 있고, 데이터 개체는 IEnumWbemClassObject::Next 메서드를 사용하여 검색할 수 있습니다. 이 메서드는 데이터 개체를 메서드에 전달된 IWbemClassObject 포인터에 연결합니다. IWbemClassObject::Get 메서드를 사용하여 데이터 개체에서 원하는 정보를 가져옵니다.

    다음 코드 예제는 데이터 개체에서 운영 체제의 이름을 제공하는 Name 속성을 가져오는 데 사용됩니다.

    VARIANT vtProp;
    
    // Get the value of the Name property
    hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
    wcout << " OS Name : " << vtProp.bstrVal << endl;
    

    Name 속성의 값이 VARIANTvtProp 변수에 저장되면 사용자에게 이름을 표시할 수 있습니다.

    다음 코드 예제에서는 VARIANT 변수를 다시 사용하여 사용 가능한 실제 메모리의 양 값을 저장하고 표시하는 방법을 보여줍니다.

    hr = pclsObj->Get(L"FreePhysicalMemory",
        0, &vtProp, 0, 0);
    wcout << " Free physical memory (in kilobytes): "
        << vtProp.uintVal << endl;
    

    자세한 내용은 WMI 열거를 참조하세요.

다음 코드 예제에서는 원격 컴퓨터에서 WMI 데이터를 반동기적으로 가져오는 방법을 보여줍니다.

#define _WIN32_DCOM
#define UNICODE
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "credui.lib")
#pragma comment(lib, "comsuppw.lib")
#include <wincred.h>
#include <strsafe.h>

int __cdecl main(int argc, char **argv)
{
    HRESULT hres;

    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------

    hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 
    if (FAILED(hres))
    {
        cout << "Failed to initialize COM library. Error code = 0x" 
            << hex << hres << endl;
        return 1;                  // Program has failed.
    }

    // Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------

    hres =  CoInitializeSecurity(
        NULL, 
        -1,                          // COM authentication
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
        RPC_C_IMP_LEVEL_IDENTIFY,    // Default Impersonation  
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities 
        NULL                         // Reserved
        );

                      
    if (FAILED(hres))
    {
        cout << "Failed to initialize security. Error code = 0x" 
            << hex << hres << endl;
        CoUninitialize();
        return 1;                    // Program has failed.
    }
    
    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------

    IWbemLocator *pLoc = NULL;

    hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &pLoc);
 
    if (FAILED(hres))
    {
        cout << "Failed to create IWbemLocator object."
            << " Err code = 0x"
            << hex << hres << endl;
        CoUninitialize();
        return 1;                 // Program has failed.
    }

    // Step 4: -----------------------------------------------------
    // Connect to WMI through the IWbemLocator::ConnectServer method

    IWbemServices *pSvc = NULL;

    // Get the user name and password for the remote computer
    CREDUI_INFO cui;
    bool useToken = false;
    bool useNTLM = true;
    wchar_t pszName[CREDUI_MAX_USERNAME_LENGTH+1] = {0};
    wchar_t pszPwd[CREDUI_MAX_PASSWORD_LENGTH+1] = {0};
    wchar_t pszDomain[CREDUI_MAX_USERNAME_LENGTH+1];
    wchar_t pszUserName[CREDUI_MAX_USERNAME_LENGTH+1];
    wchar_t pszAuthority[CREDUI_MAX_USERNAME_LENGTH+1];
    BOOL fSave;
    DWORD dwErr;

    memset(&cui,0,sizeof(CREDUI_INFO));
    cui.cbSize = sizeof(CREDUI_INFO);
    cui.hwndParent = NULL;
    // Ensure that MessageText and CaptionText identify
    // what credentials to use and which application requires them.
    cui.pszMessageText = TEXT("Press cancel to use process token");
    cui.pszCaptionText = TEXT("Enter Account Information");
    cui.hbmBanner = NULL;
    fSave = FALSE;

    dwErr = CredUIPromptForCredentials( 
        &cui,                             // CREDUI_INFO structure
        TEXT(""),                         // Target for credentials
        NULL,                             // Reserved
        0,                                // Reason
        pszName,                          // User name
        CREDUI_MAX_USERNAME_LENGTH+1,     // Max number for user name
        pszPwd,                           // Password
        CREDUI_MAX_PASSWORD_LENGTH+1,     // Max number for password
        &fSave,                           // State of save check box
        CREDUI_FLAGS_GENERIC_CREDENTIALS |// flags
        CREDUI_FLAGS_ALWAYS_SHOW_UI |
        CREDUI_FLAGS_DO_NOT_PERSIST);  

    if(dwErr == ERROR_CANCELLED)
    {
        useToken = true;
    }
    else if (dwErr)
    {
        cout << "Did not get credentials " << dwErr << endl;
        pLoc->Release();     
        CoUninitialize();
        return 1;      
    }

    // change the computerName strings below to the full computer name
    // of the remote computer
    if(!useNTLM)
    {
        StringCchPrintf(pszAuthority, CREDUI_MAX_USERNAME_LENGTH+1, L"kERBEROS:%s", L"COMPUTERNAME");
    }

    // Connect to the remote root\cimv2 namespace
    // and obtain pointer pSvc to make IWbemServices calls.
    //---------------------------------------------------------
   
    hres = pLoc->ConnectServer(
        _bstr_t(L"\\\\COMPUTERNAME\\root\\cimv2"),
        _bstr_t(useToken?NULL:pszName),    // User name
        _bstr_t(useToken?NULL:pszPwd),     // User password
        NULL,                              // Locale             
        NULL,                              // Security flags
        _bstr_t(useNTLM?NULL:pszAuthority),// Authority        
        NULL,                              // Context object 
        &pSvc                              // IWbemServices proxy
        );
    
    if (FAILED(hres))
    {
        cout << "Could not connect. Error code = 0x" 
             << hex << hres << endl;
        pLoc->Release();     
        CoUninitialize();
        return 1;                // Program has failed.
    }

    cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;


    // step 5: --------------------------------------------------
    // Create COAUTHIDENTITY that can be used for setting security on proxy

    COAUTHIDENTITY *userAcct =  NULL ;
    COAUTHIDENTITY authIdent;

    if( !useToken )
    {
        memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
        authIdent.PasswordLength = wcslen (pszPwd);
        authIdent.Password = (USHORT*)pszPwd;

        LPWSTR slash = wcschr (pszName, L'\\');
        if( slash == NULL )
        {
            cout << "Could not create Auth identity. No domain specified\n" ;
            pSvc->Release();
            pLoc->Release();     
            CoUninitialize();
            return 1;               // Program has failed.
        }

        StringCchCopy(pszUserName, CREDUI_MAX_USERNAME_LENGTH+1, slash+1);
        authIdent.User = (USHORT*)pszUserName;
        authIdent.UserLength = wcslen(pszUserName);

        StringCchCopyN(pszDomain, CREDUI_MAX_USERNAME_LENGTH+1, pszName, slash - pszName);
        authIdent.Domain = (USHORT*)pszDomain;
        authIdent.DomainLength = slash - pszName;
        authIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

        userAcct = &authIdent;

    }

    // Step 6: --------------------------------------------------
    // Set security levels on a WMI connection ------------------

    hres = CoSetProxyBlanket(
       pSvc,                           // Indicates the proxy to set
       RPC_C_AUTHN_DEFAULT,            // RPC_C_AUTHN_xxx
       RPC_C_AUTHZ_DEFAULT,            // RPC_C_AUTHZ_xxx
       COLE_DEFAULT_PRINCIPAL,         // Server principal name 
       RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // RPC_C_AUTHN_LEVEL_xxx 
       RPC_C_IMP_LEVEL_IMPERSONATE,    // RPC_C_IMP_LEVEL_xxx
       userAcct,                       // client identity
       EOAC_NONE                       // proxy capabilities 
    );

    if (FAILED(hres))
    {
        cout << "Could not set proxy blanket. Error code = 0x" 
            << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();     
        CoUninitialize();
        return 1;               // Program has failed.
    }

    // Step 7: --------------------------------------------------
    // Use the IWbemServices pointer to make requests of WMI ----

    // For example, get the name of the operating system
    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery(
        bstr_t("WQL"), 
        bstr_t("Select * from Win32_OperatingSystem"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 
        NULL,
        &pEnumerator);
    
    if (FAILED(hres))
    {
        cout << "Query for operating system name failed."
            << " Error code = 0x" 
            << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return 1;               // Program has failed.
    }

    // Step 8: -------------------------------------------------
    // Secure the enumerator proxy
    hres = CoSetProxyBlanket(
        pEnumerator,                    // Indicates the proxy to set
        RPC_C_AUTHN_DEFAULT,            // RPC_C_AUTHN_xxx
        RPC_C_AUTHZ_DEFAULT,            // RPC_C_AUTHZ_xxx
        COLE_DEFAULT_PRINCIPAL,         // Server principal name 
        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // RPC_C_AUTHN_LEVEL_xxx 
        RPC_C_IMP_LEVEL_IMPERSONATE,    // RPC_C_IMP_LEVEL_xxx
        userAcct,                       // client identity
        EOAC_NONE                       // proxy capabilities 
        );

    if (FAILED(hres))
    {
        cout << "Could not set proxy blanket on enumerator. Error code = 0x" 
             << hex << hres << endl;
        pEnumerator->Release();
        pSvc->Release();
        pLoc->Release();     
        CoUninitialize();
        return 1;               // Program has failed.
    }

    // When you have finished using the credentials,
    // erase them from memory.
    SecureZeroMemory(pszName, sizeof(pszName));
    SecureZeroMemory(pszPwd, sizeof(pszPwd));
    SecureZeroMemory(pszUserName, sizeof(pszUserName));
    SecureZeroMemory(pszDomain, sizeof(pszDomain));


    // Step 9: -------------------------------------------------
    // Get the data from the query in step 7 -------------------
 
    IWbemClassObject *pclsObj = NULL;
    ULONG uReturn = 0;
   
    while (pEnumerator)
    {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, 
            &pclsObj, &uReturn);

        if(0 == uReturn)
        {
            break;
        }

        VARIANT vtProp;

        // Get the value of the Name property
        hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
        wcout << " OS Name : " << vtProp.bstrVal << endl;

        // Get the value of the FreePhysicalMemory property
        hr = pclsObj->Get(L"FreePhysicalMemory",
            0, &vtProp, 0, 0);
        wcout << " Free physical memory (in kilobytes): "
            << vtProp.uintVal << endl;
        VariantClear(&vtProp);

        pclsObj->Release();
        pclsObj = NULL;
    }

    // Cleanup
    // ========
    
    pSvc->Release();
    pLoc->Release();
    pEnumerator->Release();
    if( pclsObj )
    {
        pclsObj->Release();
    }
    
    CoUninitialize();

    return 0;   // Program successfully completed.
    
}