2010-04-12 23 views
7

No hago mucha programación de Windows GUI, por lo que todo esto puede ser de conocimiento común para las personas más familiarizadas con WinForms que yo. Lamentablemente, no he podido encontrar ningún recurso para explicar el problema que encontré hoy durante la depuración.Control.EndInvoke restablece la pila de llamadas para la excepción

Si llamamos a EndInvoke en un delegado asíncrono. Obtendremos cualquier excepción lanzada durante la ejecución del método relanzado. La pila de llamadas reflejará la fuente original de la excepción.

Sin embargo, si hacemos algo similar en Windows.Forms.Control, la implementación de Control.EndInvoke restablece la pila de llamadas. Esto se puede observar con una simple prueba o mirando el código en Reflector. El extracto de código relevante de EndInvoke está aquí:

if (entry.exception != null) 
{ 
    throw entry.exception; 
} 

entiendo que comienzan/EndInvoke para el Control y delegados asincrónicos son diferentes, pero yo habría esperado un comportamiento similar en Control.EndInvoke.

¿Hay algún motivo por el que Control no haga lo que hace la delegada asíncrona para preservar la pila de llamadas original?

Respuesta

1

No sé la razón real, pero puedo adivinar que los delegados asíncronos son similares a RPC, mientras que los delegados de control pueden basarse en el envío de mensajes Win32. Diferentes tecnologías por lo que el impacto de esta característica puede no ser el mismo. El delegado Async se beneficiaría de todo el código remoto, para lo cual el desarrollador habría escrito el código para transferir la pila de llamadas de excepción entre diferentes procesos o computadoras, mientras que los delegados de control simularán RPC con PostMessage dentro del mismo proceso. Equipo diferente, código diferente.

1

Tenga en cuenta también que Control.EndInvoke es uno de los pocos gestionados EndInvokes en el marco (para que pueda ver el código en Reflector). Probablemente deberían tener un ayudante no administrado que arroje con la pila original en su lugar.

De hecho, creo que es la única lograron EndInvoke, pero hay otras que gestiona End* rutinas con un parámetro IAsyncResult. No los he comprobado a todos, pero parece que todos los que he revisado arrojan la excepción, o usan de manera efectiva la solución de desvío de Stephen Cleary para usar .NET 4 GetWaiter.GetResult, que tiene algunas trampas administradas y no administradas para tratar de obtener la pila restaurada por excepciones.

1

no estoy seguro de por qué el control no hace esto (probablemente un descuido), pero se puede trabajar alrededor de ella en .NET 4.0 mediante la programación de una tarea a la forma de interfaz de usuario:

private BackgroundWorker bgw; 
    private TaskFactory uiTaskFactory; 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     this.uiTaskFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()); 
     this.bgw = new BackgroundWorker(); 
     this.bgw.DoWork += bgw_DoWork; 
     this.bgw.RunWorkerAsync(); 
    } 

    void bgw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     var task = this.uiTaskFactory.StartNew(this.OuterTaskFunction); 
     try 
     { 
      task.Wait(); 
     } 
     catch (Exception ex) 
     { 
      // Note: Full stack trace preserved 
      MessageBox.Show(ex.InnerException.ToString()); 
     } 
    } 

    void OuterTaskFunction() 
    { 
     this.InnerTaskFunction(); 
    } 

    void InnerTaskFunction() 
    { 
     throw new InvalidOperationException("Blah."); 
    } 
-3

I no leí el 100% de su mensaje, así que no estoy seguro de si esto ayuda o simplemente estoy diciendo cosas obvias, , pero cuando se detecta una excepción y escribe

"throw iAmAnCaughtExceptionInstance;"

no se guardará la pila de llamadas, sólo debe escribir

"tirar";

y luego la pila de llamadas se guarda

+1

sé, pero como yo no soy el que implementa Windows.Forms.Control es de poca ayuda. –

Cuestiones relacionadas