如何:使用 WRL 處理事件
本文件將示範如何使用 Windows 執行階段 C++ 範本庫 (WRL)訂閱,並處理 Windows 執行階段 物件的事件。
如需建立元件的執行個體並擷取屬性值的更基本的範例,請參閱 如何:利用 WRL 啟動與使用 Windows 執行階段元件。
訂閱及處理事件。
下列步驟會啟動 ABI::Windows::System::Threading::IDeviceWatcher 物件,並使用事件處理常式監控進度。 在裝置重設時,加入、移除或變更時, IDeviceWatcher 介面讓您能夠列舉裝置非同步,或在背景,並收到告知。 回呼 函式是此範例中很重要的一部分,因為它可以指定處理背景作業結果的事件處理常式。 完整的程式碼範例如下所示。
警告
雖然您通常會在 Windows 市集 應用程式中使用 WRL ,這個範例使用主控台應用程式來描述。例如 wprintf_s 函式無法從 Windows 市集 應用程式使用。如需您可以在 Windows 市集 應用程式中使用的型別和函式的詳細資訊,請參閱 CRT 函式不支援使用 /ZW 和 Win32 和 COM Windows 市集應用程式的。
包含 (#include) 所有必要的 Windows 執行階段、 WRL或 Standard 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 指示詞讓程式碼更容易讀取。
宣告應用程式的區域變數。 這個範例會持有列舉裝置和登入語彙基元數目的計數,讓它可以從事件之後取消訂閱。
// 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;
初始化 Windows 執行階段。
// 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); }
注意事項 這個事件僅供示範使用,且僅為主控台應用程式的一部分。這個範例會使用事件以確保在應用程式結束之前非同步作業已完成。在多數應用程式,您通常不會等候非同步作業完成。
建立介面 IDeviceWatcher 的啟用 Factory。
// 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 執行階段 提供和包含所需的執行階段類別名稱的字串。
建立 IDeviceWatcher 物件。
// Create a IDeviceWatcher object from the factory. ComPtr<IDeviceWatcher> watcher; hr = watcherFactory->CreateWatcher(&watcher); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
使用 Callback 函式訂閱 Added、 EnumerationCompleted和 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); }
Added 事件處理常式會遞增列舉裝置的數量。 在找到十個裝置之後,即會停止列舉處理序。
Stopped 事件處理常式中移除事件處理常式並設定完成事件。
EnumerationCompleted 事件處理常式會停止列舉處理序。 為了處理小於十個的裝置的情況,我們會處理這個事件。
提示
這個範例使用 Lambda 運算式定義回呼。您也可以使用函式物件 (functors),函式指標或 std::function 物件。如需 Lambda 運算式的詳細資訊,請參閱 C++ 中的 Lambda 運算式。
開始列舉處理序。
wprintf_s(L"Starting device enumeration...\n"); hr = watcher->Start(); if (FAILED(hr)) { return PrintError(__LINE__, hr); }
等候列舉型別 (Enumeration) 處理序完成之後列印訊息。 所有 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-asyncOp.cpp 的檔案然後在 Visual Studio 命令提示字元視窗中執行下列命令。
cl.exe wrl-consume-events.cpp runtimeobject.lib