Partager via


Migration des fonctionnalités de thread

Cette rubrique explique comment migrer le code de thread dans une application plateforme Windows universelle (UWP) vers le Kit de développement logiciel (SDK) d’application Windows.

Résumé des différences d’API et/ou de fonctionnalités

Le modèle de thread d’UWP est une variante du modèle STA (Single-Threaded Apartment) appelé Application STA (ASTA), qui bloque la réentrance et permet d’éviter divers bogues de réentrance et interblocages. Un thread ASTA est également appelé thread d’interface utilisateur.

Le Kit de développement logiciel (SDK) d’application Windows utilise un modèle de thread sta standard, qui ne fournit pas les mêmes protections de réentrance.

Le type CoreDispatcher migre vers DispatcherQueue. Et la méthode CoreDispatcher.RunAsync migre vers DispatcherQueue.TryEnqueue.

C++/WinRT. Si vous utilisez winrt ::resume_foreground avec CoreDispatcher, migrez-le pour utiliser DispatcherQueue à la place.

Modèle de thread ASTA / STA

Pour plus d’informations sur le modèle de thread ASTA, consultez le billet de blog Qu’est-ce qui est si spécial sur l’application STA ?.

Comme le modèle de thread sta du SDK d’application Windows n’a pas les mêmes garanties concernant la prévention des problèmes de réentrance, si votre application UWP suppose le comportement non entrant du modèle de thread ASTA, votre code peut ne pas se comporter comme prévu.

Une chose à observer est la réentrance dans les contrôles XAML (consultez l’exemple dans un kit sdk d’application Windows de la migration de l’exemple d’application UWP Photo Editor (C++/WinRT)). Et pour certains blocages, tels que les violations d’accès, la pile d’appels d’incident directe est généralement la pile appropriée à utiliser. Mais s’il s’agit d’un blocage d’exception non autorisé ( qui a le code d’exception : 0xc000027b), il est nécessaire d’obtenir la pile d’appels appropriée.

Exceptions autorisées

L’exception stowed bloque l’enregistrement d’une erreur possible et qui est utilisée ultérieurement si aucune partie du code ne gère l’exception. XAML décide parfois que l’erreur est irrécupérable immédiatement, auquel cas la pile d’incidents directes peut être bonne. Mais plus fréquemment la pile a été déwound avant qu’elle ait été déterminée à être fatale. Pour plus d’informations sur les exceptions non autorisées, consultez la C000027B d’exception stowed de l’épisode Inside Show.

Pour les incidents d’exception non autorisés (pour voir une pompe de messages imbriquée ou pour voir l’exception spécifique du contrôle XAML levée), vous pouvez obtenir plus d’informations sur le plantage en chargeant un vidage sur incident dans le débogueur Windows (WinDbg) (voir Télécharger les outils de débogage pour Windows), puis utiliser !pde.dse pour vider les exceptions autorisées.

L’extension du débogueur PDE (pour la !pde.dse commande) est disponible en téléchargeant le fichier PDE*.zip à partir de OneDrive. Placez le x64 ou x86 .dll approprié à partir de ce fichier zip dans le winext répertoire de votre installation WinDbg, puis !pde.dse travaillez sur les vidages d’incidents d’exception stowed.

Fréquemment, il y aura plusieurs exceptions éthentées, certaines à la fin qui ont été gérées/ignorées. Le plus souvent, la première exception est intéressante. Dans certains cas, la première exception est peut-être une nouvelle levée de la seconde. Par conséquent, si la deuxième exception est plus profonde dans la même pile que la première, la deuxième exception peut être l’origine de l’erreur. Le code d’erreur affiché avec chaque exception stowed est également utile, car il fournit le HRESULT associé à cette exception.

Remplacez Windows.UI.Core.CoreDispatcher par Microsoft.UI.Dispatching.DispatcherQueue

Cette section s’applique si vous utilisez la classe Windows.UI.Core.CoreDispatcher dans votre application UWP. Cela inclut l’utilisation de toutes les méthodes ou propriétés qui prennent ou retournent un CoreDispatcher, comme les propriétés DependencyObject.Dispatcher et CoreWindow.Dispatcher. Par exemple, vous appelez DependencyObject.Dispatcher lorsque vous récupérez coreDispatcher appartenant à un Windows.UI.Xaml.Controls.Page.

// MainPage.xaml.cs in a UWP app
if (this.Dispatcher.HasThreadAccess)
{
    ...
}
// MainPage.xaml.cpp in a UWP app
if (this->Dispatcher().HasThreadAccess())
{
    ...
}

Au lieu de cela, dans votre application sdk d’application Windows, vous devez utiliser la classe Microsoft.UI.Dispatching.DispatcherQueue. Et les méthodes ou propriétés correspondantes qui prennent ou retournent un DispatcherQueue, telles que les propriétés DependencyObject.DispatcherQueue et Microsoft.UI.Xaml.Window.DispatcherQueue. Par exemple, vous appelez DependencyObject.DispatcherQueue lorsque vous récupérez le DispatcherQueue appartenant à un Microsoft.UI.Xaml.Controls.Page (la plupart des objets XAML sont DependencyObject).

// MainPage.xaml.cs in a Windows App SDK app
if (this.DispatcherQueue.HasThreadAccess)
{
    ...
}
// MainPage.xaml.cpp in a Windows App SDK app
#include <winrt/Microsoft.UI.Dispatching.h>
...
if (this->DispatcherQueue().HasThreadAccess())
{
    ...
}

Remplacez CoreDispatcher.RunAsync par DispatcherQueue.TryEnqueue

Cette section s’applique si vous utilisez la méthode Windows.UI.Core.CoreDispatcher.RunAsync pour planifier une tâche à exécuter sur le thread d’interface utilisateur principal (ou sur le thread associé à un Windows.UI.Core.CoreDispatcher particulier).

// MainPage.xaml.cs in a UWP app
public void NotifyUser(string strMessage)
{
    if (this.Dispatcher.HasThreadAccess)
    {
        StatusBlock.Text = strMessage;
    }
    else
    {
        var task = this.Dispatcher.RunAsync(
            Windows.UI.Core.CoreDispatcherPriority.Normal,
            () => StatusBlock.Text = strMessage);
    }
}
// MainPage.cpp in a UWP app
void MainPage::NotifyUser(std::wstring strMessage)
{
    if (this->Dispatcher().HasThreadAccess())
    {
        StatusBlock().Text(strMessage);
    }
    else
    {
        auto task = this->Dispatcher().RunAsync(
            Windows::UI::Core::CoreDispatcherPriority::Normal,
            [strMessage, this]()
            {
                StatusBlock().Text(strMessage);
            });
    }
}

Dans votre application sdk d’application Windows, utilisez plutôt la méthode Microsoft.UI.Dispatching.DispatcherQueue.TryEnqueue). Il ajoute à Microsoft.UI.Dispatching.DispatcherQueue une tâche qui sera exécutée sur le thread associé à DispatcherQueue.

// MainPage.xaml.cs in a Windows App SDK app
public void NotifyUser(string strMessage)
{
    if (this.DispatcherQueue.HasThreadAccess)
    {
        StatusBlock.Text = strMessage;
    }
    else
    {
        bool isQueued = this.DispatcherQueue.TryEnqueue(
        Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal,
        () => StatusBlock.Text = strMessage);
    }
}
// MainPage.xaml.cpp in a Windows App SDK app
#include <winrt/Microsoft.UI.Dispatching.h>
...
void MainPage::NotifyUser(std::wstring strMessage)
{
    if (this->DispatcherQueue().HasThreadAccess())
    {
        StatusBlock().Text(strMessage);
    }
    else
    {
        bool isQueued = this->DispatcherQueue().TryEnqueue(
            Microsoft::UI::Dispatching::DispatcherQueuePriority::Normal,
            [strMessage, this]()
            {
                StatusBlock().Text(strMessage);
            });
    }
}

Migrer winrt::resume_foreground (C++/WinRT)

Cette section s’applique si vous utilisez la fonction winrt ::resume_foreground dans une coroutine dans votre application UWP C++/WinRT.

Dans UWP, le cas d’usage pour winrt ::resume_foreground consiste à basculer l’exécution vers un thread de premier plan (ce thread de premier plan est souvent celui associé à windows.UI.Core.CoreDispatcher). Voici un exemple de cela.

// MainPage.cpp in a UWP app
winrt::fire_and_forget MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    ...
    co_await winrt::resume_foreground(this->Dispatcher());
    ...
}

Dans votre application sdk d’application Windows :

Ajoutez d’abord une référence au package NuGet Microsoft.Windows.ImplementationLibrary .

Ajoutez ensuite l’élément suivant dans pch.h le projet cible.

#include <wil/cppwinrt_helpers.h>

Suivez ensuite le modèle ci-dessous.

// MainPage.xaml.cpp in a Windows App SDK app
...
winrt::fire_and_forget MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    ...
    co_await wil::resume_foreground(this->DispatcherQueue());
    ...
}

Voir aussi