Compartir a través de


Migración de la funcionalidad de subprocesos

En este tema se describe cómo migrar el código de subproceso en una aplicación de Plataforma universal de Windows (UWP) a la SDK de Aplicaciones para Windows.

Resumen de las API y/o diferencias en las características

El modelo de subprocesos de UWP es una variación del modelo de apartamento de un solo subproceso (STA) denominado Application STA (ASTA), que bloquea la reentrada y ayuda a evitar varios errores y interbloqueos de reentrada. Un subproceso de ASTA también se conoce como subproceso de interfaz de usuario.

El SDK de Aplicaciones para Windows usa un modelo de subproceso STA estándar, que no proporciona las mismas medidas de seguridad de reentrada.

El tipo CoreDispatcher se migra a DispatcherQueue. Y el método CoreDispatcher.RunAsync se migra a DispatcherQueue.TryEnqueue.

C++/WinRT. Si usa winrt::resume_foreground con CoreDispatcher, migra eso para usar DispatcherQueue en su lugar.

Modelo de subprocesos de ASTA a STA

Para obtener más información sobre el modelo de subprocesos de ASTA, consulte la entrada de blog ¿Qué es tan especial sobre application STA?.

Dado que el modelo de subprocesos STA del SDK de Aplicaciones para Windows no tiene las mismas garantías en torno a la prevención de problemas de reentrada, si la aplicación para UWP asume el comportamiento no entrante del modelo de subprocesos de ASTA, es posible que el código no se comporte según lo esperado.

Una cosa que hay que tener en cuenta es reentrada en controles XAML (consulta el ejemplo en A SDK de Aplicaciones para Windows migración de la aplicación de ejemplo editor de fotos de UWP (C++/WinRT)). Y para algunos bloqueos, como las infracciones de acceso, la pila de llamadas directa de bloqueo suele ser la pila correcta que se va a usar. Pero si se trata de un bloqueo de excepción permitido, que tiene código de excepción: 0xc000027b, se requiere más trabajo para obtener la pila de llamadas correcta.

Excepciones permitidas

Los bloqueos de excepción permitidos ahorran un posible error y se usan más adelante si ninguna parte del código controla la excepción. XAML a veces decide que el error es irrecuperable inmediatamente, en cuyo caso la pila de bloqueo directo podría ser buena. Pero con más frecuencia la pila no se ha desenlazada antes de que se determine que es fatal. Para obtener más información sobre las excepciones permitidas, consulte el C000027B de excepción stowed del episodio Inside Show.

Para los bloqueos de excepción permitidos (para ver una bomba de mensajes anidada o para ver la excepción específica del control XAML que se está iniciando), puedes obtener más información sobre el bloqueo cargando un volcado de memoria en el Depurador de Windows (WinDbg) (consulta Descargar herramientas de depuración para Windows) y, a continuación, usar !pde.dse para volcar las excepciones permitidas.

La extensión del depurador PDE (para el !pde.dse comando) está disponible descargando el archivo PDE*.zip desde OneDrive. Coloque el archivo zip adecuado x64 o x86 .dll en el directorio de la winext instalación de WinDbg y, a continuación !pde.dse , funcionará en volcados de memoria de excepciones permitidos.

Con frecuencia, habrá varias excepciones permitidas, con algunas al final que se controlaron o ignoraron. Normalmente, la primera excepción permitida es la interesante. En algunos casos, la primera excepción permitida podría ser una repetición del segundo, por lo que si la segunda excepción permitida se muestra más profundamente en la misma pila que la primera, la segunda excepción podría ser la originación del error. El código de error que se muestra con cada excepción permitida también es útil, ya que proporciona el VALOR HRESULT asociado a esa excepción.

Cambie Windows.UI.Core.CoreDispatcher a Microsoft.UI.Dispatching.DispatcherQueue

Esta sección se aplica si usas la clase Windows.UI.Core.CoreDispatcher en tu aplicación para UWP. Esto incluye el uso de los métodos o propiedades que toman o devuelven un CoreDispatcher, como las propiedades DependencyObject.Dispatcher y CoreWindow.Dispatcher. Por ejemplo, llamará a DependencyObject.Dispatcher cuando recupere coreDispatcher que pertenece a 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())
{
    ...
}

En su lugar, en la aplicación de SDK de Aplicaciones para Windows, deberá usar la clase Microsoft.UI.Dispatching.DispatcherQueue. Y los métodos o propiedades correspondientes que toman o devuelven un DispatcherQueue, como las propiedades DependencyObject.DispatcherQueue y Microsoft.UI.Xaml.Window.DispatcherQueue. Por ejemplo, llamarás a DependencyObject.DispatcherQueue cuando recuperes dispatcherQueue que pertenece a un Microsoft.UI.Xaml.Controls.Page (la mayoría de los objetos XAML son 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())
{
    ...
}

Cambie CoreDispatcher.RunAsync a DispatcherQueue.TryEnqueue

Esta sección se aplica si usa el método Windows.UI.Core.CoreDispatcher.RunAsync para programar una tarea que se ejecute en el subproceso principal de la interfaz de usuario (o en el subproceso asociado a un windows.UI.Core.CoreDispatcher determinado).

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

En la aplicación SDK de Aplicaciones para Windows, use el método Microsoft.UI.Dispatching.DispatcherQueue.TryEnqueue) en su lugar. Agrega a Microsoft.UI.Dispatching.DispatcherQueue una tarea que se ejecutará en el subproceso asociado a 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);
            });
    }
}

Migración de winrt::resume_foreground (C++/WinRT)

Esta sección se aplica si usas la función winrt::resume_foreground en una corrutina en tu aplicación para UWP de C++/WinRT.

En UWP, el caso de uso de winrt::resume_foreground es cambiar la ejecución a un subproceso en primer plano (ese subproceso en primer plano suele ser el asociado a windows.UI.Core.CoreDispatcher). Este es un ejemplo de eso.

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

En la aplicación de SDK de Aplicaciones para Windows:

Por lo tanto, primero agregue una referencia al paquete NuGet Microsoft.Windows.ImplementationLibrary .

A continuación, agregue la siguiente inclusión en pch.h en el proyecto de destino.

#include <wil/cppwinrt_helpers.h>

Y luego siga el patrón que se muestra a continuación.

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

Consulte también