방법: WRL을 사용하여 이벤트 처리

이 문서에서는 Windows 런타임 C++ WRL(템플릿 라이브러리)을 사용하여 Windows 런타임 개체의 이벤트를 구독하고 처리하는 방법을 보여 줍니다.

해당 구성 요소의 인스턴스를 만들고 속성 값을 검색하는 보다 기본적인 예제는 방법: Windows 런타임 구성 요소 활성화 및 사용을 참조하세요.

이벤트 구독 및 처리

다음 단계에서는 개체를 ABI::Windows::System::Threading::IDeviceWatcher 시작하고 이벤트 처리기를 사용하여 진행률을 모니터링합니다. 인터페이스 IDeviceWatcher 를 사용하면 디바이스를 비동기적으로 또는 백그라운드에서 열거하고 디바이스가 추가, 제거 또는 변경될 때 알림을 받을 수 있습니다. 콜백 함수는 백그라운드 작업의 결과를 처리하는 이벤트 처리기를 지정할 수 있으므로 이 예제에서 중요한 부분입니다. 전체 예제는 다음과 같습니다.

Warning

일반적으로 유니버설 Windows 플랫폼 앱에서 Windows 런타임 C++ 템플릿 라이브러리를 사용하지만 이 예제에서는 그림에 콘솔 앱을 사용합니다. 이러한 wprintf_s 함수는 유니버설 Windows 플랫폼 앱에서 사용할 수 없습니다. 유니버설 Windows 플랫폼 앱에서 사용할 수 있는 형식 및 함수에 대한 자세한 내용은 유니버설 Windows 플랫폼 앱에서 지원되지 않는 CRT 함수와 UWP 앱용 Win32 및 COM을 참조하세요.

  1. #include필요한 Windows 런타임, Windows 런타임 C++ 템플릿 라이브러리 또는 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 는 디바이스를 열거하는 데 필요한 형식을 선언합니다.

    .cpp 파일의 using namespace 지시문을 활용하여 코드를 보다 읽기 쉽게 만드는 것이 좋습니다.

  2. 앱의 지역 변수를 선언합니다. 이 예제에서는 나중에 이벤트에서 구독을 취소할 수 있도록 하는 열거형 디바이스 및 등록 토큰의 수를 보유합니다.

    // 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. Windows 런타임 초기화합니다.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  4. 열거형 프로세스 완료를 기본 앱과 동기화하는 Event 개체를 만듭니다.

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

    참고 항목

    이 이벤트는 콘솔 앱의 일부로만 데모용입니다. 이 예제에서는 이벤트를 사용하여 앱이 종료되기 전에 비동기 작업이 완료되도록 합니다. 대부분의 앱에서는 일반적으로 비동기 작업이 완료되기를 기다리지 않습니다.

  5. 인터페이스에 대한 활성화 팩터리를 만듭니다 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);
    }
    

    Windows 런타임 정규화된 이름을 사용하여 형식을 식별합니다. RuntimeClass_Windows_Devices_Enumeration_DeviceInformation 매개 변수는 Windows 런타임 제공되고 필요한 런타임 클래스 이름을 포함하는 문자열입니다.

  6. 개체를 만듭니다 IDeviceWatcher .

    // Create a IDeviceWatcher object from the factory.
    ComPtr<IDeviceWatcher> watcher;
    hr = watcherFactory->CreateWatcher(&watcher);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
  7. 함수를 Callback 사용하여 , EnumerationCompletedStopped 이벤트를 구독Added합니다.

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

    Added 이벤트 처리기는 열거된 디바이스의 수를 증분합니다. 10개의 디바이스를 찾은 후 열거형 프로세스를 중지합니다.

    Stopped 이벤트 처리기는 이벤트 처리기를 제거하고 완료 이벤트를 설정합니다.

    EnumerationCompleted 이벤트 처리기는 열거형 프로세스를 중지합니다. 디바이스가 10개 미만인 경우 이 이벤트를 처리합니다.

    이 예제에서는 람다 식을 사용하여 콜백을 정의합니다. 함수 개체(functors), 함수 포인터 또는 std::function 개체를 사용할 수도 있습니다. 람다 식에 대한 자세한 내용은 람다 식을 참조하세요.

  8. 열거형 프로세스를 시작합니다.

    wprintf_s(L"Starting device enumeration...\n");
    hr = watcher->Start();
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
  9. 열거 프로세스가 완료되기를 기다린 다음 메시지를 인쇄합니다. 모든 ComPtr 및 RAII 개체는 범위를 벗어나면 자동으로 릴리스됩니다.

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

전체 예제는 다음과 같습니다.

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

코드 컴파일

코드를 컴파일하려면 코드를 복사한 다음 Visual Studio 프로젝트에 붙여넣거나 이름이 지정된 wrl-consume-events.cpp 파일에 붙여넣은 다음 Visual Studio 명령 프롬프트 창에서 다음 명령을 실행합니다.

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

참고 항목

Windows 런타임 C++ 템플릿 라이브러리(WRL)