Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se describe la forma en que .NET Framework controla las llamadas desde código de C# y Visual Basic a objetos proporcionados por Windows Runtime o por componentes de Windows Runtime.
En .NET Framework, puede acceder a cualquier objeto desde varios subprocesos de forma predeterminada, sin control especial. Todo lo que necesita es una referencia al objeto . En Windows Runtime, estos objetos se denominan ágil. La mayoría de las clases de Windows Runtime son ágiles, pero algunas clases no son, e incluso las clases ágiles pueden requerir un control especial.
Siempre que sea posible, Common Language Runtime (CLR) trata objetos de otros orígenes, como Windows Runtime, como si fueran objetos de .NET Framework:
Si el objeto implementa la interfaz IAgileObject, o tiene el atributo MarshalingBehaviorAttribute con MarshalingType.Agile, el CLR lo trata como ágil.
Si el CLR puede gestionar una llamada desde el subproceso donde se realizó hacia el contexto de subproceso del objeto de destino, lo hace transparentemente.
Si el objeto tiene el atributo MarshalingBehaviorAttribute con MarshalingType.None, la clase no proporciona información de marshaling. CLR no puede serializar la llamada, por lo que produce un InvalidCastException excepción con un mensaje que indica que el objeto solo se puede usar en el contexto de subproceso donde se creó.
En las secciones siguientes se describen los efectos de este comportamiento en objetos de varios orígenes.
Objetos de un componente de Windows Runtime escrito en C# o Visual Basic
Todos los tipos del componente que se pueden activar son ágiles de forma predeterminada.
Nota:
La agilidad no implica la seguridad de los hilos. En Windows Runtime y .NET Framework, la mayoría de las clases no son seguras para subprocesos porque la seguridad de subprocesos tiene un costo de rendimiento y la mayoría de los objetos nunca son accedidos por múltiples subprocesos. Es más eficaz sincronizar el acceso a objetos individuales (o usar clases seguras para subprocesos) solo según sea necesario.
Al crear un componente de Windows Runtime, puede invalidar el valor predeterminado. Consulte la interfaz ICustomQueryInterface y la interfaz IAgileObject.
Objetos de Windows Runtime
La mayoría de las clases de Windows Runtime son ágiles y CLR las trata como ágiles. La documentación de estas clases enumera "MarshalingBehaviorAttribute(Agile)" entre los atributos de clase. Sin embargo, los miembros de algunas de estas clases ágiles, como los controles XAML, lanzan excepciones si no se les llama en el subproceso de la interfaz de usuario. Por ejemplo, el código siguiente intenta usar un subproceso en segundo plano para establecer una propiedad del botón en el 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
Puede acceder 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 la interfaz de usuario (como la página en la que está el botón). El código siguiente utiliza el objeto CoreDispatcher y su método RunAsync para despachar la llamada en el subproceso de la 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
Nota:
La propiedad Dispatcher
no produce una excepción cuando se llama desde otro hilo.
La duración de un objeto de Windows Runtime que se crea en el subproceso de la interfaz de usuario está limitado por la duración del subproceso. No intente acceder a objetos en un subproceso de interfaz de usuario después de cerrar la ventana.
Si creas tu propio control heredando un control XAML o redactando un conjunto de controles XAML, el control es ágil porque es un objeto de .NET Framework. Sin embargo, si llama a miembros de su clase base o clases constituyentes, o si llama a miembros heredados, esos miembros generarán excepciones cuando sean llamados desde cualquier hilo excepto el hilo de la interfaz de usuario.
Clases que no se pueden serializar
Las clases de Windows Runtime que no proporcionan información de marshaling tienen el atributo MarshalingBehaviorAttribute con MarshalingType.None. La documentación de esta clase enumera "MarshalingBehaviorAttribute(None)" entre sus atributos.
El código siguiente crea un objeto CameraCaptureUI en el subproceso de la interfaz de usuario y, a continuación, intenta establecer una propiedad del objeto a partir de un subproceso de grupo de hilos. CLR no puede manejar la llamada y produce una excepción System.InvalidCastException con un mensaje que indica que el objeto solo se puede usar en el contexto de subproceso donde fue creado.
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 de CameraCaptureUI también enumera "ThreadingAttribute(STA)" entre los atributos de la clase, ya que debe crearse en un contexto de un solo subproceso, como el subproceso de la interfaz de usuario.
Si desea acceder al objeto CameraCaptureUI desde otro subproceso, puede almacenar en caché el objeto CoreDispatcher para el subproceso de interfaz de usuario y usarlo más adelante para enviar la llamada en ese subproceso. O bien, puedes obtener el despachador de un objeto XAML, como una 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 Runtime escrito en C++
De forma predeterminada, las clases del componente que se pueden activar son ágiles. Sin embargo, C++ permite una cantidad significativa de control sobre los modelos de subprocesos y el comportamiento de serialización. Como se ha descrito anteriormente en este artículo, el CLR reconoce clases ágiles, intenta procesar las llamadas cuando las clases no son ágiles y produce una excepción de System.InvalidCastException cuando una clase no tiene información de marshaling.
En el caso de los objetos que se ejecutan en el subproceso de interfaz de usuario e inician excepciones cuando se llaman desde un subproceso que no sea el subproceso de interfaz de usuario, puede usar el objeto coreDispatcher para enviar la llamada.