2010-06-24 15 views
14

Aquí está mi ejemplo hipotético. Tengo una ventana WPF muy simple con un botón. El evento Button.Click tiene un controlador que va así.¿Por qué las excepciones no son propagadas por WPF Dispatcher.Invoke?

Action doit =() => 
{ 
    Action error =() => { throw new InvalidOperationException("test"); }; 

    try { 
     this.Dispatcher.Invoke(error, DispatcherPriority.Normal); 
    } catch (Exception ex) { 
     System.Diagnostics.Trace.WriteLine(ex); 
     throw; 
    } 
}; 
doit.BeginInvoke(null, null); 

Yo esperaría que la excepción sería capturado y escrito por el llamado Trace.WriteLine. En cambio, no se captura ninguna excepción y la aplicación se rompe.

¿Alguien sabe de una posible explicación para que esto suceda? ¿Y qué solución sugieres para detectar excepciones lanzadas por el delegado invocado por Dispatcher.Invoke?

Actualización 1: Puse un throw en el código de excepción. No quiero ignorar realmente la excepción. El objetivo de mi pregunta es manejarlo correctamente. El problema es que el código de manejo de excepciones nunca se ejecuta.

Recuerde que este es un ejemplo hipotético. Mi código real no se ve así. Además, supongamos que no puedo cambiar el código en el método que se invocará.

Actualización 2: Considere este ejemplo similar. En lugar de una ventana de WPF, tengo una ventana de Windows Forms. Tiene un botón con casi exactamente el mismo controlador. La única diferencia está en el código de invocación. Dice así.

this.Invoke(error); 

En Windows Forms, se ejecuta el código de excepción de manejo. ¿Por qué la diferencia?

Respuesta

5

Actualizado: Observar la excepción en el otro hilo, que desea utilizar un Task, Cola al hilo Dispatcher (usando TaskScheduler.FromCurrentSynchronizationContext), y esperar en él, como por ejemplo:

var ui = TaskScheduler.FromCurrentSynchronizationContext(); 
Action doit =() => 
{ 
    var error = Task.Factory.StartNew(
     () => { throw new InvalidOperationException("test"); }, 
     CancellationToken.None, 
     TaskCreationOptions.None, 
     ui); 

    try { 
     error.Wait(); 
    } catch (Exception ex) { 
     System.Diagnostics.Trace.WriteLine(ex); 
    } 
}; 
doit.BeginInvoke(null, null); 

actualizado (de nuevo): Dado que su objetivo es un componente reutilizable, recomiendo pasar a una interfaz o algo más basado Task basado en SynchronizationContext como el event-based asynchronous pattern, en lugar de basar el componente en Dispatcher o ISynchronizeInvoke.

Dispatcher -los componentes basados ​​solo funcionan en WPF/Silverlight; Los componentes basados ​​en ISynchronizeInvoke solo funcionan en Windows Forms. SynchronizationContext -los componentes basados ​​funcionarán con WPF o Windows Forms de forma transparente, y (con un poco más de trabajo) ASP.NET, aplicaciones de consola, servicios de Windows, etc.

El patrón asincrónico basado en eventos es la antigua forma recomendada de escritura SynchronizationContext -componentes basados ​​en; todavía existe para el código .NET 3.5-era. Sin embargo, si usa .NET 4, la biblioteca paralela de tareas es mucho más flexible, limpia y potente. El TaskScheduler.FromCurrentSynchronizationContext utiliza SynchronizationContext debajo, y es la nueva forma de escribir componentes reutilizables que necesitan este tipo de sincronización.

+0

La forma en que trato las excepciones detectadas es irrelevante. Podría poner algo de registro o lo que sea allí. El problema es que el código de manejo de excepciones nunca se ejecuta. – jpbochi

+0

Eso es porque la excepción se lanza en otro hilo: el hilo 'Dispatcher'. –

+1

@Sthepen: Lo sé. Esperaba obtener una TargetInvocationException o algo así. – jpbochi

Cuestiones relacionadas