Porady: obsługa zdarzeń z użyciem biblioteki WRL

W tym dokumencie pokazano, jak za pomocą biblioteki szablonów języka C++ (WRL) środowisko wykonawcze systemu Windows subskrybować i obsługiwać zdarzenia obiektu środowisko wykonawcze systemu Windows.

Aby uzyskać bardziej podstawowy przykład, który tworzy wystąpienie tego składnika i pobiera wartość właściwości, zobacz Instrukcje: aktywowanie i użycie składnika środowisko wykonawcze systemu Windows.

Subskrybowanie zdarzeń i obsługa ich

Poniższe kroki umożliwiają uruchomienie ABI::Windows::System::Threading::IDeviceWatcher obiektu i użycie procedur obsługi zdarzeń do monitorowania postępu. Interfejs IDeviceWatcher umożliwia wyliczanie urządzeń asynchronicznie lub w tle oraz odbieranie powiadomień po dodaniu, usunięciu lub zmianie urządzeń. Funkcja wywołania zwrotnego jest ważną częścią tego przykładu, ponieważ umożliwia określenie procedur obsługi zdarzeń, które przetwarzają wyniki operacji w tle. Poniższy kompletny przykład.

Ostrzeżenie

Mimo że zazwyczaj używasz biblioteki szablonów środowisko wykonawcze systemu Windows C++ w aplikacji platforma uniwersalna systemu Windows, w tym przykładzie użyto aplikacji konsolowej na potrzeby ilustracji. Funkcje, takie jak wprintf_s nie są dostępne w aplikacji platforma uniwersalna systemu Windows. Aby uzyskać więcej informacji na temat typów i funkcji, których można używać w aplikacji platforma uniwersalna systemu Windows, zobacz Funkcje CRT nieobsługiwane w aplikacjach platforma uniwersalna systemu Windows i Win32 i COM dla aplikacji platformy UWP.

  1. Uwzględnij (#include) wszystkie wymagane środowisko wykonawcze systemu Windows, bibliotekę szablonów języka C++ środowisko wykonawcze systemu Windows lub nagłówki standardowej biblioteki języka C++.

    #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 deklaruje typy wymagane do wyliczania urządzeń.

    Zalecamy użycie using namespace dyrektywy w pliku cpp, aby kod był bardziej czytelny.

  2. Zadeklaruj zmienne lokalne dla aplikacji. W tym przykładzie jest przechowywana liczba wyliczonych urządzeń i tokenów rejestracji, które umożliwiają jej późniejsze anulowanie subskrypcji zdarzeń.

    // 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. Zainicjuj środowisko wykonawcze systemu Windows.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  4. Utwórz obiekt zdarzenia, który synchronizuje ukończenie procesu wyliczania z główną aplikacją.

    // 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);
    }
    

    Uwaga

    To zdarzenie jest przeznaczone tylko dla pokazu w ramach aplikacji konsolowej. W tym przykładzie użyto zdarzenia , aby upewnić się, że operacja asynchronizuja się przed zakończeniem działania aplikacji. W większości aplikacji zwykle nie czekasz na ukończenie operacji asynchronicznych.

  5. Utwórz fabrykę aktywacji dla interfejsu IDeviceWatcher .

    // 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);
    }
    

    Środowisko wykonawcze systemu Windows używa w pełni kwalifikowanych nazw do identyfikowania typów. Parametr RuntimeClass_Windows_Devices_Enumeration_DeviceInformation jest ciągiem dostarczonym przez środowisko wykonawcze systemu Windows i zawiera wymaganą nazwę klasy środowiska uruchomieniowego.

  6. IDeviceWatcher Utwórz obiekt.

    // Create a IDeviceWatcher object from the factory.
    ComPtr<IDeviceWatcher> watcher;
    hr = watcherFactory->CreateWatcher(&watcher);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
  7. Callback Użyj funkcji , aby zasubskrybować Addedzdarzenia , EnumerationCompletedi 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);
    }
    

    Procedura Added obsługi zdarzeń zwiększa liczbę wyliczonych urządzeń. Zatrzymuje proces wyliczania po znalezieniu dziesięciu urządzeń.

    Program Stopped obsługi zdarzeń usuwa programy obsługi zdarzeń i ustawia zdarzenie ukończenia.

    Procedura EnumerationCompleted obsługi zdarzeń zatrzymuje proces wyliczania. Obsługujemy to zdarzenie w przypadku, gdy istnieje mniej niż dziesięć urządzeń.

    Napiwek

    W tym przykładzie użyto wyrażenia lambda do zdefiniowania wywołań zwrotnych. Można również używać obiektów funkcji (functors), wskaźników funkcji lub obiektów std::function . Aby uzyskać więcej informacji na temat wyrażeń lambda, zobacz Wyrażenia lambda.

  8. Uruchom proces wyliczania.

    wprintf_s(L"Starting device enumeration...\n");
    hr = watcher->Start();
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
  9. Poczekaj na ukończenie procesu wyliczania, a następnie wyświetl komunikat. Wszystkie ComPtr obiekty RAII opuszczają zakres i są zwalniane automatycznie.

    // 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.
    

Oto kompletny przykład:

// 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.
*/

Kompilowanie kodu

Aby skompilować kod, skopiuj go, a następnie wklej go w projekcie programu Visual Studio lub wklej go w pliku o nazwie wrl-consume-events.cpp , a następnie uruchom następujące polecenie w oknie wiersza polecenia programu Visual Studio.

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

Zobacz też

Biblioteka szablonów języka C++ środowiska uruchomieniowego systemu Windows (WRL)