Gewusst wie: Behandeln von Ereignissen mit WRL

In diesem Dokument wird gezeigt, wie Sie die Windows-Runtime C++-Vorlagenbibliothek (WRL) verwenden, um die Ereignisse eines Windows-Runtime-Objekts zu abonnieren und zu behandeln.

Ein einfacheres Beispiel, das eine Instanz dieser Komponente erstellt und einen Eigenschaftswert abruft, finden Sie unter How to: Activate and Use a Windows-Runtime Component.

Abonnieren und Behandeln von Ereignissen

Mit den folgenden Schritten starten Sie ein ABI::Windows::System::Threading::IDeviceWatcher-Objekt und überwachen den Fortschritt mit Ereignishandlern. Die IDeviceWatcher-Schnittstelle ermöglicht es Ihnen, Geräte asynchron oder im Hintergrund aufzulisten und benachrichtigt zu werden, wenn Geräte hinzugefügt, entfernt oder geändert werden. Die Rückruffunktion ist ein wichtiger Bestandteil dieses Beispiels, da sie Ereignishandler angeben kann, die die Ergebnisse des Hintergrundvorgangs verarbeiten. Im Folgenden finden Sie das vollständige Beispiel.

Warnung

Obwohl Sie in der Regel die Windows-Runtime C++-Vorlagenbibliothek in einer Universelle Windows-Plattform-App verwenden, wird in diesem Beispiel eine Konsolen-App zur Veranschaulichung verwendet. Funktionen wie zwprintf_s. B. sind in einer Universelle Windows-Plattform-App nicht verfügbar. Weitere Informationen zu den Typen und Funktionen, die Sie in einer Universelle Windows-Plattform-App verwenden können, finden Sie unter CRT-Funktionen, die in Universelle Windows-Plattform Apps und Win32 und COM für UWP-Apps nicht unterstützt werden.

  1. #includeSchließen Sie alle erforderlichen Windows-Runtime, Windows-Runtime C++-Vorlagenbibliothek oder C++-Standardbibliotheksheader ein.

    #include <Windows.Devices.Enumeration.h>
    #include <wrl/event.h>
    #include <stdio.h>
    
    using namespace ABI::Windows::Devices::Enumeration;
    using namespace ABI::Windows::Foundation;
    using namespace Microsoft::WRL;
    using namespace Microsoft::WRL::Wrappers;
    

    Windows.Devices.Enumeration.h deklariert die Typen, die zum Aufzählen von Geräten erforderlich sind.

    Es wird empfohlen, den Code mithilfe der using namespace-Direktive in der CPP-Datei verständlicher zu gestalten.

  2. Deklarieren Sie die lokalen Variablen für die App. In diesem Beispiel wird die Anzahl der aufgelisteten Geräte und Registrierungstoken abgelegt; dadurch kann später das Abonnement von Ereignissen gekündigt werden.

    // Counts the number of enumerated devices.
    unsigned int deviceCount = 0;
    
    // Event registration tokens that enable us to later unsubscribe from events.
    EventRegistrationToken addedToken;
    EventRegistrationToken stoppedToken;
    EventRegistrationToken enumCompletedToken;
    
  3. Initialisieren Sie die Windows-Runtime.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  4. Erstellen Sie ein Event-Objekt, das den Abschluss des Enumerationsprozesses mit der Standard-App synchronisiert.

    // Create an event that is set after device enumeration completes. We later use this event to wait for the timer to complete. 
    // This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete.
    Event enumerationCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
    HRESULT hr = enumerationCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    

    Hinweis

    Dieses Ereignis dient lediglich zur Veranschaulichung im Rahmen der Konsolen-App. In diesem Beispiel sorgt das Ereignis dafür, dass ein asynchroner Vorgang abgeschlossen wird, bevor die App beendet wird. In den meisten Apps warten Sie in der Regel nicht, bis asynchrone Vorgänge abgeschlossen sind.

  5. Erstellen Sie eine Aktivierungsfactory für die IDeviceWatcher-Schnittstelle.

    // Get the activation factory for the IDeviceWatcher interface.
    ComPtr<IDeviceInformationStatics> watcherFactory;
    hr = ABI::Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), &watcherFactory);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    

    Die Windows-Runtime verwendet vollqualifizierte Namen, um Typen zu identifizieren. Der RuntimeClass_Windows_Devices_Enumeration_DeviceInformation Parameter ist eine Zeichenfolge, die vom Windows-Runtime bereitgestellt wird und den erforderlichen Laufzeitklassennamen enthält.

  6. Erstellen Sie das Objekt IDeviceWatcher.

    // Create a IDeviceWatcher object from the factory.
    ComPtr<IDeviceWatcher> watcher;
    hr = watcherFactory->CreateWatcher(&watcher);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
  7. Abonnieren Sie mit der Callback-Funktion die Ereignisse Added, EnumerationCompleted und Stopped.

    // Subscribe to the Added event.
    hr = watcher->add_Added(Callback<AddedHandler>([&deviceCount](IDeviceWatcher* watcher, IDeviceInformation*) -> HRESULT
    {
        // Print a message and increment the device count.
        // When we reach 10 devices, stop enumerating devices.
        wprintf_s(L"Added device...\n");
        deviceCount++;
        if (deviceCount == 10)
        {
            return watcher->Stop();
        }
        return S_OK;
    
    }).Get(), &addedToken);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
    hr = watcher->add_Stopped(Callback<StoppedHandler>([=, &enumerationCompleted](IDeviceWatcher* watcher, IInspectable*) -> HRESULT
    {
        wprintf_s(L"Device enumeration stopped.\nRemoving event handlers...");
    
        // Unsubscribe from the events. This is shown for demonstration.
        // The need to remove event handlers depends on the requirements of 
        // your app. For instance, if you only need to handle an event for 
        // a short period of time, you might remove the event handler when you
        // no longer need it. If you handle an event for the duration of the app,
        // you might not need to explicitly remove it.
        HRESULT hr1 = watcher->remove_Added(addedToken);
        HRESULT hr2 = watcher->remove_Stopped(stoppedToken);
        HRESULT hr3 = watcher->remove_EnumerationCompleted(enumCompletedToken);
    
        // Set the completion event and return.
        SetEvent(enumerationCompleted.Get());
    
        return FAILED(hr1) ? hr1 : FAILED(hr2) ? hr2 : hr3;
    
    }).Get(), &stoppedToken);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
    // Subscribe to the EnumerationCompleted event.
    hr = watcher->add_EnumerationCompleted(Callback<EnumerationCompletedHandler>([](IDeviceWatcher* watcher, IInspectable*) -> HRESULT
    {
        wprintf_s(L"Enumeration completed.\n");
    
        return watcher->Stop();
    
    }).Get(), &enumCompletedToken);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    

    Der Ereignishandler Added erhöht die Anzahl der aufgelisteten Geräte. Er beendet den Enumerationsprozess, nachdem zehn Geräte gefunden wurden.

    Der Ereignishandler Stopped entfernt die Ereignishandler und legt das Abschlussereignis fest.

    Der Ereignishandler EnumerationCompleted beendet den Enumerationsprozess. Dieses Ereignis wird behandelt, falls es einmal weniger als zehn Geräte gibt.

    Tipp

    Dieses Beispiel definiert die Rückrufe mithilfe eines Lambdaausdrucks. Sie können auch Funktionsobjekte (Functors), Funktionszeiger oder std::function-Objekte verwenden. Weitere Informationen zu Lambdaausdrücken finden Sie unter Lambda Expressions (Lambdaausdrücke).

  8. Starten Sie den Enumerationsprozess.

    wprintf_s(L"Starting device enumeration...\n");
    hr = watcher->Start();
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
  9. Warten Sie, bis der Enumerationsprozess abgeschlossen ist, und drucken Sie dann eine Meldung aus. Alle ComPtr- und RAII-Objekte verlassen den Bereich und werden automatisch freigegeben.

    // Wait for the operation to complete.
    WaitForSingleObjectEx(enumerationCompleted.Get(), INFINITE, FALSE);
    
    wprintf_s(L"Enumerated %u devices.\n", deviceCount);
    
    // All smart pointers and RAII objects go out of scope here.
    

Im Folgenden sehen Sie das vollständige Beispiel:

// wrl-consume-events.cpp
// compile with: runtimeobject.lib
#include <Windows.Devices.Enumeration.h>
#include <wrl/event.h>
#include <stdio.h>

using namespace ABI::Windows::Devices::Enumeration;
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;

// Prints an error string for the provided source code line and HRESULT
// value and returns the HRESULT value as an int.
int PrintError(unsigned int line, HRESULT hr)
{
    wprintf_s(L"ERROR: Line:%d HRESULT: 0x%X\n", line, hr);
    return hr;
}

int wmain()
{
    // Type define the event handler types to make the code more readable.
    typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_Windows__CDevices__CEnumeration__CDeviceInformation AddedHandler;
    typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_IInspectable EnumerationCompletedHandler;
    typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_IInspectable StoppedHandler;

    // Counts the number of enumerated devices.
    unsigned int deviceCount = 0;

    // Event registration tokens that enable us to later unsubscribe from events.
    EventRegistrationToken addedToken;
    EventRegistrationToken stoppedToken;
    EventRegistrationToken enumCompletedToken;

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }

    // Create an event that is set after device enumeration completes. We later use this event to wait for the timer to complete. 
    // This event is for demonstration only in a console app. In most apps, you typically don't wait for async operations to complete.
    Event enumerationCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
    HRESULT hr = enumerationCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    // Get the activation factory for the IDeviceWatcher interface.
    ComPtr<IDeviceInformationStatics> watcherFactory;
    hr = ABI::Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), &watcherFactory);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    // Create a IDeviceWatcher object from the factory.
    ComPtr<IDeviceWatcher> watcher;
    hr = watcherFactory->CreateWatcher(&watcher);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    // Subscribe to the Added event.
    hr = watcher->add_Added(Callback<AddedHandler>([&deviceCount](IDeviceWatcher* watcher, IDeviceInformation*) -> HRESULT
    {
        // Print a message and increment the device count.
        // When we reach 10 devices, stop enumerating devices.
        wprintf_s(L"Added device...\n");
        deviceCount++;
        if (deviceCount == 10)
        {
            return watcher->Stop();
        }
        return S_OK;

    }).Get(), &addedToken);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    hr = watcher->add_Stopped(Callback<StoppedHandler>([=, &enumerationCompleted](IDeviceWatcher* watcher, IInspectable*) -> HRESULT
    {
        wprintf_s(L"Device enumeration stopped.\nRemoving event handlers...");

        // Unsubscribe from the events. This is shown for demonstration.
        // The need to remove event handlers depends on the requirements of 
        // your app. For instance, if you only need to handle an event for 
        // a short period of time, you might remove the event handler when you
        // no longer need it. If you handle an event for the duration of the app,
        // you might not need to explicitly remove it.
        HRESULT hr1 = watcher->remove_Added(addedToken);
        HRESULT hr2 = watcher->remove_Stopped(stoppedToken);
        HRESULT hr3 = watcher->remove_EnumerationCompleted(enumCompletedToken);

        // Set the completion event and return.
        SetEvent(enumerationCompleted.Get());

        return FAILED(hr1) ? hr1 : FAILED(hr2) ? hr2 : hr3;

    }).Get(), &stoppedToken);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    // Subscribe to the EnumerationCompleted event.
    hr = watcher->add_EnumerationCompleted(Callback<EnumerationCompletedHandler>([](IDeviceWatcher* watcher, IInspectable*) -> HRESULT
    {
        wprintf_s(L"Enumeration completed.\n");

        return watcher->Stop();

    }).Get(), &enumCompletedToken);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    wprintf_s(L"Starting device enumeration...\n");
    hr = watcher->Start();
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    // Wait for the operation to complete.
    WaitForSingleObjectEx(enumerationCompleted.Get(), INFINITE, FALSE);

    wprintf_s(L"Enumerated %u devices.\n", deviceCount);

    // All smart pointers and RAII objects go out of scope here.
}
/*
Sample output:
Starting device enumeration...
Added device...
Added device...
Added device...
Added device...
Added device...
Added device...
Added device...
Added device...
Added device...
Added device...
Device enumeration stopped.
Removing event handlers...
Enumerated 10 devices.
*/

Kompilieren des Codes

Um den Code zu kompilieren, kopieren Sie ihn, und fügen Sie ihn dann in ein Visual Studio-Projekt ein, oder fügen Sie ihn in eine Datei ein, die benannt wrl-consume-events.cpp ist, und führen Sie dann den folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster aus .

cl.exe wrl-consume-events.cpp runtimeobject.lib

Siehe auch

C++-Vorlagenbibliothek für Windows-Runtime (WRL)