Beispiel: Abrufen von WMI-Daten von einem Remotecomputer

Sie können mithilfe des Verfahrens und der Codebeispiele in diesem Themenbereich eine vollständige WMI-Clientanwendung erstellen, die eine COM-Initialisierung durchführt, eine Verbindung mit WMI auf dem Remotecomputer herstellt, Daten halbsynchron abruft und dann bereinigt. Weitere Informationen zum Abrufen von Daten vom lokalen Computer finden Sie unter Beispiel: Abrufen von WMI-Daten vom lokalen Computer. Weitere Informationen zum asynchronen Abrufen der Daten finden Sie unter Beispiel: Asynchrones Abrufen von WMI-Daten vom lokalen Computer.

Hinweis

Wenn Sie versuchen, eine Verbindung mit einem Remotecomputer herzustellen, lesen Sie die Informationen unter Herstellen einer Remoteverbindung mit WMI.

 

Das folgende Verfahren zeigt die Ausführung der WMI-Anwendung. Die Schritte 1 bis 5 enthalten alle Schritte, die zum Einrichten und Herstellen einer Verbindung mit WMI erforderlich sind, und in den Schritten 6 und 7 werden Daten abgefragt und empfangen.

So rufen Sie WMI-Daten von einem Remotecomputer ab

  1. Initialisieren Sie COM-Parameter durch einen Aufruf von CoInitializeEx.

    Weitere Informationen finden Sie unter Initialisieren von COM für eine WMI-Anwendung.

  2. Initialisieren Sie die COM-Prozesssicherheit, indem Sie CoInitializeSecurity aufrufen.

    Weitere Informationen finden Sie unter Festlegen der Sicherheitsstufe für Standardprozesse mit C++.

  3. Rufen Sie den anfänglichen Locator in WMI ab, indem Sie CoCreateInstance aufrufen.

    Weitere Informationen finden Sie unter Erstellen einer Verbindung mit einem WMI-Namespace.

  4. Rufen Sie einen Zeiger auf IWbemServices für den Namespace \\root\cimv2 auf dem Remotecomputer ab, indem Sie IWbemLocator::ConnectServer aufrufen. Wenn Sie eine Verbindung mit einem Remotecomputer herstellen, müssen Sie den Computernamen, die Domäne, den Benutzernamen und das Kennwort des Remotecomputers kennen, mit dem Sie eine Verbindung herstellen. Diese Attribute werden alle an die Methode IWbemLocator::ConnectServer übergeben. Stellen Sie außerdem sicher, dass der Benutzername auf dem Computer, der versucht, eine Verbindung mit dem Remotecomputer herzustellen, über die richtigen Zugriffsberechtigungen auf dem Remotecomputer verfügt. Weitere Informationen finden Sie unter Herstellen einer Verbindung über die Windows-Firewall. Informationen zum Herstellen einer Verbindung mit dem lokalen Computer finden Sie unter Beispiel: Abrufen von WMI-Daten vom lokalen Computer und Erstellen einer Verbindung mit einem WMI-Namespace.

    Bei der Verarbeitung von Benutzernamen und Kennwörtern wird empfohlen, dass der Benutzer zur Eingabe der Informationen aufgefordert wird, die Informationen zu verwenden und die Informationen dann zu löschen, damit die Wahrscheinlichkeit, dass die Informationen von einem nicht autorisierten Benutzer abgefangen werden, geringer ist. Schritt 4 im folgenden Beispielcode verwendet CredUIPromptForCredentials, um den Benutzernamen und das Kennwort abzurufen, und verwendet dann SecureZeroMemory, um die Informationen zu entfernen, nachdem sie in IWbemLocator::ConnectServer verwendet wurden. Weitere Informationen finden Sie unter Behandeln von Kennwörtern und Fragen des Benutzers nach Anmeldeinformationen auf MSDN.

  5. Erstellen Sie eine Struktur COAUTHIDENTITY, um Anmeldeinformationen zum Festlegen der Proxysicherheit bereitzustellen.

  6. Legen Sie die Proxysicherheit für IWbemServices fest, damit der WMI-Dienst die Identität des Clients annehmen kann, indem CoSetProxyBlanket aufgerufen wird.

    Weitere Informationen finden Sie unter Festlegen der Sicherheitsebenen für eine WMI-Verbindung.

  7. Verwenden Sie den Zeiger IWbemServices, um Anforderungen an WMI auszuführen. Eine Abfrage wird ausgeführt, um den Namen des Betriebssystems und die Menge des freien physischen Arbeitsspeichers abzurufen, indem IWbemServices::ExecQuery aufgerufen wird.

    Die folgende WQL-Abfrage ist eines der Methodenargumente.

    SELECT * FROM Win32_OperatingSystem

    Das Ergebnis dieser Abfrage wird in einem Zeiger IEnumWbemClassObject gespeichert. Dadurch können die Datenobjekte aus der Abfrage halbsynchron mit der Schnittstelle IEnumWbemClassObject abgerufen werden. Weitere Informationen finden Sie unter WMI-Enumeration. Informationen zum asynchronen Abrufen der Daten finden Sie unter Beispiel: Asynchrones Abrufen von WMI-Daten vom lokalen Computer.

    Weitere Informationen zum Erstellen von Anforderungen an WMI finden Sie unter Bearbeiten von Klassen- und Instanzinformationen, Abfragen von WMI und Aufrufen einer Methode.

  8. Legen Sie die Enumeratorproxysicherheit IEnumWbemClassObject fest. Stellen Sie sicher, dass Sie die Anmeldeinformationen aus dem Arbeitsspeicher löschen, nachdem Sie sie verwendet haben.

    Weitere Informationen finden Sie unter Festlegen von Sicherheitseinstellungen für IWbemServices und andere Proxys.

  9. Rufen Sie die Daten aus der WQL-Abfrage ab, und zeigen Sie sie an. Der Zeiger IEnumWbemClassObject ist mit den von der Abfrage zurückgegebenen Datenobjekten verknüpft, und die Datenobjekte können mit der Methode IEnumWbemClassObject::Next abgerufen werden. Diese Methode verknüpft die Datenobjekte mit einem Zeiger IWbemClassObject, der an die Methode übergeben wird. Verwenden Sie die Methode IWbemClassObject::Get, um die gewünschten Informationen aus den Datenobjekten abzurufen.

    Im folgenden Codebeispiel wird die Eigenschaft Name aus dem Datenobjekt abgerufen, das den Namen des Betriebssystems bereitstellt.

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

    Sowie der Wert der Eigenschaft Name in der Variablen VARIANTvtProp gespeichert wurde, kann er dem Benutzer angezeigt werden.

    Das folgende Codebeispiel zeigt, wie die Variable VARIANT erneut verwendet werden kann, um den Wert der Menge des freien physischen Speichers zu speichern und anzuzeigen.

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

    Weitere Informationen finden Sie unter WMI-Enumeration.

Das folgende Codebeispiel zeigt, wie WMI-Daten halbsynchron von einem Remotecomputer abgerufen werden.

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