Compartir a través de


Usar objetos de Windows en tiempo de ejecución en un entorno multiproceso

En este artículo se describe la manera en que .NET Framework trata las llamadas de código de C# y Visual Basic a los objetos proporcionados por Windows en tiempo de ejecución o por componentes de Windows en tiempo de ejecución.

En .NET Framework puedes tener acceso a cualquier objeto de varios subprocesos de manera predeterminada sin un tratamiento especial. Lo único que necesitas es una referencia al objeto. En Windows en tiempo de ejecución, estos objetos se denominan ágiles. La mayoría de las clases de Windows en tiempo de ejecución son ágiles, pero algunas clases no lo son, e incluso las clases ágiles pueden requerir un tratamiento especial.

Siempre que es posible, Common Language Runtime (CLR) trata los objetos de otros orígenes, como Windows en tiempo de ejecución, como si fueran objetos de .NET Framework:

  • Si el objeto implementa la interfaz IAgileObject o tiene el atributo MarshalingBehaviorAttribute con MarshalingType.Agile, CLR lo trata como ágil.

  • Si CLR puede calcular las referencias de una llamada del subproceso donde se realizó al contexto de subprocesos del objeto de destino, lo hace de forma transparente.

  • Si el objeto tiene el atributo MarshalingBehaviorAttribute con MarshalingType.None, la clase no proporciona información de cálculo de referencias. CLR no puede calcular las referencias de la llamada, por lo que produce una excepción InvalidCastException con un mensaje que indica que el objeto se puede utilizar solo en el contexto de subprocesos donde se creó.

En las secciones siguientes se describen los efectos de este comportamiento en objetos de diferentes orígenes.

Objetos de un componente de Windows en tiempo de ejecución escritos en C# o Visual Basic

Todos los tipos del componente que pueden activarse son ágiles de forma predeterminada.

NotaNota

La agilidad no implica la seguridad de los subprocesos. En Windows en tiempo de ejecución y .NET Framework, la mayoría de las clases no son seguras para subprocesos porque la seguridad de los subprocesos tiene un costo de rendimiento, y la mayoría de los objetos nunca son objeto de acceso por parte de varios subprocesos. Es más eficaz sincronizar el acceso a los objetos individuales (o usar clases seguras para subprocesos) solo en caso necesario.

Cuando creas un componente de Windows en tiempo de ejecución, puedes invalidar el predeterminado. Consulta la interfaz ICustomQueryInterface y la interfaz IAgileObject.

Objetos de Windows en tiempo de ejecución

La mayoría de las clases de Windows en tiempo de ejecución son ágiles y CLR las trata como ágiles. La documentación para estas listas de clases muestra “MarshalingBehaviorAttribute (Agile)” entre los atributos de clase. Sin embargo, los miembros de algunas de estas clases ágiles, como controles XAML, producen excepciones si no se invocan en el subproceso de interfaz de usuario. Por ejemplo, el código siguiente intenta utilizar un subproceso en segundo plano para establecer una propiedad del botón en que se hizo clic. La propiedad Content del botón produce una excepción.

        private async void Button_Click_2(object sender, RoutedEventArgs e)
        {
            Button b = (Button) sender;
            await Task.Run(() => { 
                b.Content += "."; 
            });
        }
    Private Async Sub Button_Click_2(sender As Object, e As RoutedEventArgs)
        Dim b As Button = CType(sender, Button)
        Await Task.Run(Sub()
                           b.Content &= "."
                       End Sub)
    End Sub

Puedes tener acceso al botón de forma segura mediante su propiedad Dispatcher, o la propiedad Dispatcher de cualquier objeto que exista en el contexto del subproceso de interfaz de usuario (como la página donde está el botón). El código siguiente utiliza el método RunAsync del objeto CoreDispatcher para enviar la llamada en el subproceso de interfaz de usuario.

        private async void Button_Click_2(object sender, RoutedEventArgs e)
        {
            Button b = (Button) sender;
            await b.Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal, 
                () => { 
                    b.Content += "."; 
            });
        }
    Private Async Sub Button_Click_2(sender As Object, e As RoutedEventArgs)
        Dim b As Button = CType(sender, Button)
        Await b.Dispatcher.RunAsync(
            Windows.UI.Core.CoreDispatcherPriority.Normal, 
            Sub()
                b.Content &= "."
            End Sub)
    End Sub
NotaNota

La propiedad Dispatcher no produce una excepción cuando se invoca desde otro subproceso.

La vigencia de un objeto de Windows en tiempo de ejecución que se crea en el subproceso de interfaz de usuario está limitada por la vigencia del subproceso. No intentes tener acceso a los objetos de un subproceso de interfaz de usuario después de que se cierre la ventana.

Si creas tu propio control heredando un control XAML o creando un conjunto de controles XAML, tu control será ágil porque es un objeto de .NET Framework. Sin embargo, si llama a los miembros de su clase base o de las clases constituyentes, o si tú llamas a miembros heredados, esos miembros producirán excepciones cuando se invoquen desde cualquier subproceso excepto el subproceso de interfaz de usuario.

JJ157115.collapse_all(es-es,VS.120).gifClases cuyas referencias no se pueden calcular

Las clases de Windows en tiempo de ejecución que no proporcionan información de cálculo de referencias tienen el atributo MarshalingBehaviorAttribute con MarshalingType.None. La documentación para ese tipo de clase muestra “MarshalingBehaviorAttribute (None)” entre sus atributos.

El código siguiente crea un objeto CameraCaptureUI en el subproceso de interfaz de usuario y, después, intenta establecer una propiedad del objeto desde un subproceso de grupo de subprocesos. CLR no puede calcular las referencias de la llamada, y produce una excepción InvalidCastException con un mensaje que indica que el objeto se puede utilizar solo en el contexto de subprocesos donde se creó.

        Windows.Media.Capture.CameraCaptureUI ccui;

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            ccui = new Windows.Media.Capture.CameraCaptureUI();

            await Task.Run(() => { 
                ccui.PhotoSettings.AllowCropping = true; 
            });
        }
    Private ccui As Windows.Media.Capture.CameraCaptureUI

    Private Async Sub Button_Click_1(sender As Object, e As RoutedEventArgs)
        ccui = New Windows.Media.Capture.CameraCaptureUI()

        Await Task.Run(Sub()
                           ccui.PhotoSettings.AllowCropping = True
                       End Sub)
    End Sub

La documentación para CameraCaptureUI también muestra “ThreadingAttribute (STA)” entre los atributos de la clase, porque debe crearse en el contexto de un único subproceso como el subproceso de interfaz de usuario.

Si deseas tener acceso al objeto CameraCaptureUI desde otro subproceso, puedes almacenar en caché el objeto CoreDispatcher para el subproceso de interfaz de usuario y utilizarlo posteriormente para enviar la llamada en ese subproceso. O bien, puedes obtener el distribuidor de un objeto XAML como la página, como se muestra en el código siguiente.

        Windows.Media.Capture.CameraCaptureUI ccui;

        private async void Button_Click_3(object sender, RoutedEventArgs e)
        {
            ccui = new Windows.Media.Capture.CameraCaptureUI();

            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                () => {
                    ccui.PhotoSettings.AllowCropping = true;
                });
        }
    Dim ccui As Windows.Media.Capture.CameraCaptureUI

    Private Async Sub Button_Click_3(sender As Object, e As RoutedEventArgs)

        ccui = New Windows.Media.Capture.CameraCaptureUI()

        Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                    Sub()
                                        ccui.PhotoSettings.AllowCropping = True
                                    End Sub)
    End Sub

Objetos de un componente de Windows en tiempo de ejecución escritos en C++

De forma predeterminada, las clases del componente que pueden activarse son ágiles. Sin embargo, C++ permite una cantidad significativa de control sobre los modelos de subprocesos y el comportamiento del cálculo de referencias. Como se ha descrito anteriormente en este artículo, CLR reconoce las clases ágiles, intenta calcular las referencias de las llamadas cuando las clases no son ágiles y produce una excepción InvalidCastException cuando una clase no tiene información de cálculo de referencias.

Para los objetos que se ejecutan en el subproceso de interfaz de usuario y producen excepciones cuando se invocan desde un subproceso distinto del subproceso de interfaz de usuario, puedes utilizar el objeto CoreDispatcher del subproceso de interfaz de usuario para enviar la llamada.

Vea también

Conceptos

Referencia del lenguaje Visual Basic y C#