Comment : terminer une opération asynchrone à l'aide de WRL

Ce document montre comment utiliser la bibliothèque de modèles C++ Windows Runtime pour démarrer des opérations asynchrones et effectuer des opérations lorsque les opérations se terminent.

Ce document présente deux exemples. Le premier exemple démarre un minuteur asynchrone et attend que le minuteur expire. Dans cet exemple, vous spécifiez l’action asynchrone lorsque vous créez l’objet minuteur. Le deuxième exemple exécute un thread de travail en arrière-plan. Cet exemple montre comment utiliser une méthode Windows Runtime qui retourne une IAsyncInfo interface. La fonction de rappel est une partie importante des deux exemples, car elle leur permet de spécifier un gestionnaire d’événements pour traiter les résultats des opérations asynchrones.

Pour obtenir un exemple plus simple qui crée une instance d’un composant et récupère une valeur de propriété, consultez Guide pratique pour activer et utiliser un composant Windows Runtime.

Conseil

Ces exemples utilisent des expressions lambda pour définir les rappels. Vous pouvez également utiliser des objets de fonction (fonctors), des pointeurs de fonction ou des objets std ::function . Pour plus d’informations sur les expressions lambda C++, consultez Expressions lambda.

Exemple : Utilisation d’un minuteur

Les étapes suivantes démarrent un minuteur asynchrone et attendent que le minuteur expire. L’exemple complet suit.

Avertissement

Bien que vous utilisiez généralement la bibliothèque de modèles C++ Windows Runtime dans une application plateforme Windows universelle (UWP), cet exemple utilise une application console pour l’illustration. Les fonctions telles que wprintf_s celles-ci ne sont pas disponibles à partir d’une application UWP. Pour plus d’informations sur les types et fonctions que vous pouvez utiliser dans une application UWP, consultez les fonctions CRT non prises en charge dans les applications plateforme Windows universelle et Win32 et COM pour les applications UWP.

  1. Incluez (#include) tous les en-têtes de bibliothèque de modèles Windows Runtime, C++ C++ standard requis.

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

    Windows.System.Threading.h déclare les types requis pour utiliser un minuteur asynchrone.

    Nous vous recommandons d'utiliser la directive using namespace dans votre fichier .cpp pour rendre le code plus lisible.

  2. Initialisez Windows Runtime.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Créez une fabrique d’activation pour l’interface ABI::Windows::System::Threading::IThreadPoolTimer .

    // Get the activation factory for the IThreadPoolTimer interface.
    ComPtr<IThreadPoolTimerStatics> timerFactory;
    HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    

    Windows Runtime utilise des noms complets pour identifier les types. Le RuntimeClass_Windows_System_Threading_ThreadPoolTimer paramètre est une chaîne fournie par Windows Runtime et contient le nom de classe runtime requis.

  4. Créez un objet Event qui synchronise le rappel du minuteur sur l’application principale.

    // Create an event that is set after the timer callback 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 timerCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
    hr = timerCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    

    Remarque

    Cet événement est destiné à la démonstration uniquement dans le cadre d’une application console. Cet exemple utilise l’événement pour s’assurer qu’une opération asynchrone se termine avant la fermeture de l’application. Dans la plupart des applications, vous n’attendez généralement pas que les opérations asynchrones se terminent.

  5. Créez un IThreadPoolTimer objet qui expire après deux secondes. Utilisez la Callback fonction pour créer le gestionnaire d’événements (un ABI::Windows::System::Threading::ITimerElapsedHandler objet).

    // Create a timer that prints a message after 2 seconds.
    
    TimeSpan delay;
    delay.Duration = 20000000; // 2 seconds.
    
    auto callback = Callback<ITimerElapsedHandler>([&timerCompleted](IThreadPoolTimer* timer) -> HRESULT
    {
        wprintf_s(L"Timer fired.\n");
    
        TimeSpan delay;
        HRESULT hr = timer->get_Delay(&delay);
        if (SUCCEEDED(hr))
        {
            wprintf_s(L"Timer duration: %2.2f seconds.\n", delay.Duration / 10000000.0);
        }
    
        // Set the completion event and return.
        SetEvent(timerCompleted.Get());
        return hr;
    });
    hr = callback ? S_OK : E_OUTOFMEMORY;
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
    ComPtr<IThreadPoolTimer> timer;
    hr = timerFactory->CreateTimer(callback.Get(), delay, &timer);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
  6. Imprimez un message dans la console et attendez que le rappel du minuteur se termine. Tous les objets ComPtr et RAII quittent la portée et sont libérés automatiquement.

    // Print a message and wait for the timer callback to complete.
    wprintf_s(L"Timer started.\nWaiting for timer...\n");
    
    // Wait for the timer to complete.
    WaitForSingleObjectEx(timerCompleted.Get(), INFINITE, FALSE);
    // All smart pointers and RAII objects go out of scope here.
    

Voici l’exemple complet :

// wrl-consume-async.cpp
// compile with: runtimeobject.lib
#include <Windows.Foundation.h>
#include <Windows.System.Threading.h>
#include <wrl/event.h>
#include <stdio.h>
#include <Objbase.h>

using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;
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()
{
    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }

    // Get the activation factory for the IThreadPoolTimer interface.
    ComPtr<IThreadPoolTimerStatics> timerFactory;
    HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    // Create an event that is set after the timer callback 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 timerCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
    hr = timerCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    // Create a timer that prints a message after 2 seconds.

    TimeSpan delay;
    delay.Duration = 20000000; // 2 seconds.

    auto callback = Callback<ITimerElapsedHandler>([&timerCompleted](IThreadPoolTimer* timer) -> HRESULT
    {
        wprintf_s(L"Timer fired.\n");

        TimeSpan delay;
        HRESULT hr = timer->get_Delay(&delay);
        if (SUCCEEDED(hr))
        {
            wprintf_s(L"Timer duration: %2.2f seconds.\n", delay.Duration / 10000000.0);
        }

        // Set the completion event and return.
        SetEvent(timerCompleted.Get());
        return hr;
    });
    hr = callback ? S_OK : E_OUTOFMEMORY;
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    ComPtr<IThreadPoolTimer> timer;
    hr = timerFactory->CreateTimer(callback.Get(), delay, &timer);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    // Print a message and wait for the timer callback to complete.
    wprintf_s(L"Timer started.\nWaiting for timer...\n");

    // Wait for the timer to complete.
    WaitForSingleObjectEx(timerCompleted.Get(), INFINITE, FALSE);
    // All smart pointers and RAII objects go out of scope here.
}
/*
Output:
Timer started.
Waiting for timer...
Timer fired.
Timer duration: 2.00 seconds.
*/

Compilation du code

Pour compiler le code, copiez-le, collez-le dans un projet Visual Studio ou collez-le dans un fichier nommé wrl-consume-async.cpp , puis exécutez la commande suivante dans une fenêtre d’invite de commandes Visual Studio.

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

Exemple : utilisation d’un thread d’arrière-plan

Les étapes suivantes démarrent un thread de travail et définissent l’action effectuée par ce thread. L’exemple complet suit.

Conseil

Cet exemple montre comment utiliser l’interface ABI::Windows::Foundation::IAsyncAction . Vous pouvez appliquer ce modèle à n’importe quelle interface qui implémente : , , IAsyncActionWithProgressIAsyncOperation, et IAsyncOperationWithProgress. IAsyncActionIAsyncInfo

  1. Incluez (#include) tous les en-têtes de bibliothèque de modèles Windows Runtime, C++ C++ standard requis.

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

    Windows.System.Threading.h déclare les types requis pour utiliser un thread de travail.

    Nous vous recommandons d’utiliser la using namespace directive dans votre fichier .cpp pour rendre le code plus lisible.

  2. Initialisez Windows Runtime.

    // Initialize the Windows Runtime.
    RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }
    
  3. Créez une fabrique d’activation pour l’interface ABI::Windows::System::Threading::IThreadPoolStatics .

    // Get the activation factory for the IThreadPoolStatics interface.
    ComPtr<IThreadPoolStatics> threadPool;
    HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &threadPool);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    
  4. Créez un objet Event qui synchronise l’achèvement du thread de travail avec l’application principale.

    // Create an event that is set after the timer callback 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 threadCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
    hr = threadCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    

    Remarque

    Cet événement est destiné à la démonstration uniquement dans le cadre d’une application console. Cet exemple utilise l’événement pour s’assurer qu’une opération asynchrone se termine avant la fermeture de l’application. Dans la plupart des applications, vous n’attendez généralement pas que les opérations asynchrones se terminent.

  5. Appelez la IThreadPoolStatics::RunAsync méthode pour créer un thread de travail. Utilisez la Callback fonction pour définir l’action.

    wprintf_s(L"Starting thread...\n");
    
    // Create a thread that computes prime numbers.
    ComPtr<IAsyncAction> asyncAction;
    hr = threadPool->RunAsync(Callback<IWorkItemHandler>([&threadCompleted](IAsyncAction* asyncAction) -> HRESULT
    {
        // Print a message.
        const unsigned int start = 0;
        const unsigned int end = 100000;
        unsigned int primeCount = 0;
        for (int n = start; n < end; n++)
        {
            if (IsPrime(n))
            {
                primeCount++;
            }
        }
    
        wprintf_s(L"There are %u prime numbers from %u to %u.\n", primeCount, start, end);
    
        // Set the completion event and return.
        SetEvent(threadCompleted.Get());
        return S_OK;
    
    }).Get(), &asyncAction);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }
    

    La IsPrime fonction est définie dans l’exemple complet qui suit.

  6. Imprimez un message dans la console et attendez que le thread se termine. Tous les objets ComPtr et RAII quittent la portée et sont libérés automatiquement.

    // Print a message and wait for the thread to complete.
    wprintf_s(L"Waiting for thread...\n");
    
    // Wait for the thread to complete.
    WaitForSingleObjectEx(threadCompleted.Get(), INFINITE, FALSE);
    
    wprintf_s(L"Finished.\n");
    
    // All smart pointers and RAII objects go out of scope here.
    

Voici l’exemple complet :

// wrl-consume-asyncOp.cpp
// compile with: runtimeobject.lib 
#include <Windows.Foundation.h>
#include <Windows.System.Threading.h>
#include <wrl/event.h>
#include <stdio.h>
#include <Objbase.h>

using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;
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;
}

// Determines whether the input value is prime.
bool IsPrime(int n)
{
    if (n < 2)
    {
        return false;
    }
    for (int i = 2; i < n; ++i)
    {
        if ((n % i) == 0)
        {
            return false;
        }
    }
    return true;
}

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

    // Get the activation factory for the IThreadPoolStatics interface.
    ComPtr<IThreadPoolStatics> threadPool;
    HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &threadPool);
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }

    // Create an event that is set after the timer callback 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 threadCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
    hr = threadCompleted.IsValid() ? S_OK : HRESULT_FROM_WIN32(GetLastError());
    if (FAILED(hr))
    {
        return PrintError(__LINE__, hr);
    }


    wprintf_s(L"Starting thread...\n");

    // Create a thread that computes prime numbers.
    ComPtr<IAsyncAction> asyncAction;
    hr = threadPool->RunAsync(Callback<IWorkItemHandler>([&threadCompleted](IAsyncAction* asyncAction) -> HRESULT
    {
        // Print a message.
        const unsigned int start = 0;
        const unsigned int end = 100000;
        unsigned int primeCount = 0;
        for (int n = start; n < end; n++)
        {
            if (IsPrime(n))
            {
                primeCount++;
            }
        }

        wprintf_s(L"There are %u prime numbers from %u to %u.\n", primeCount, start, end);

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

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

    // Print a message and wait for the thread to complete.
    wprintf_s(L"Waiting for thread...\n");

    // Wait for the thread to complete.
    WaitForSingleObjectEx(threadCompleted.Get(), INFINITE, FALSE);

    wprintf_s(L"Finished.\n");

    // All smart pointers and RAII objects go out of scope here.
}
/*
Output:
Starting thread...
Waiting for thread...
There are 9592 prime numbers from 0 to 100000.
Finished.
*/

Compilation du code

Pour compiler le code, copiez-le, collez-le dans un projet Visual Studio ou collez-le dans un fichier nommé wrl-consume-asyncOp.cpp , puis exécutez la commande suivante dans une fenêtre d’invite de commandes Visual Studio.

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

Voir aussi

Bibliothèque de modèles C++ Windows Runtime (WRL)