2011-03-14 21 views
5

He examinado las diversas preguntas que mencionan esta excepción específica (this question lists many of them, que he visitado). Además, tengo el mismo general question as this post, pero en un contexto diferente, por lo que the answer no es útil para mí.¿Qué causa la excepción InvalidComObjectException: "No se puede usar el objeto COM que se ha separado de su RCW subyacente"?

El contexto

tengo una clase derivada de AxWindowsMediaPlayer que es propiedad de una clase llamada View, que está dentro de un Panel, dentro de un Workspace. Hace poco pregunté a question acerca de esta situación, pero esa pregunta se dirigió a si mi solución para este problema está bien. El fondo de esta cuestión es relevante aquí:

 
    .-----------------------. 
    |Workspace    | 
    |.--------. .--------. | 
    ||Panel1 | |Panel2 | | 
    ||.-----. | |.-----. | | 
    |||View1| | ||View2| | | 
    ||'-----' | |'-----' | | 
    |'--------' '--------' | 
    '-----------------------' 

Cuando un View consigue dispuestos, un método llamado Synchronize() se llamará en todos los View objetos restantes. Para el View que contiene el AxWindowsMediaPlayer, llama al videoPlayer.Error.clearErrorQueue().

El problema

Cuando llamo Dispose() en el nivel superior (Workspace.Dispose()), si otro View consigue dispuestos y luego causas Synchronize() para ser llamado en los View objetos restantes, el View que contiene la clase AxWindowsMediaPlayer lanza una excepción en la línea de videoPlayer.Error.clearErrorQueue(), declarando:

InvalidComObjectException: objeto COM que se ha separado de su RCW subyacente no puede b e usado.

estoy desconcertado por la forma en la AxWindowsMediaPlayer está siendo separado de su RCW subyacente (Runtime Callable Wrapper). He leído this article that talks about this exception y los peligros de llamar al Marshal.ReleaseComObject(). No estoy llamando a este método explícitamente. He puesto puntos de interrupción en los métodos Dispose de Panel y View y VideoPlayerControl (deriva de AxWindowsMediaPlayer) clases, pero ninguno de estos se activan antes de que ocurra la excepción.

Mi solución consiste en asegurarme de que el View con el reproductor multimedia siempre se elimine primero. Esta fue la motivación detrás de mi pregunta anterior. Pero me gustaría entender cómo está sucediendo esto, así puedo ver si esto es algo que necesito solucionar. ¿Quién está causando que el AxWindowsMediaPlayer se separe de su RCW antes de llamar al Dispose en la clase principal?

Supongo que el finalizador AxWindowsMediaPlayer está siendo llamado por el GC, pero no entiendo qué lo está desencadenando. Por alguna razón, al llamar al Dispose en el nivel superior está causando que Marshal.ReleaseComObject se llame bajo el piso. ¿Alguien me puede iluminar?

Respuesta

3

Su solución es, desafortunadamente, la solución.

Control.Dispose recursivamente dispone todos los controles ActiveX primero, y luego llama recursivamente Dispose en los controles secundarios.En su ejemplo, si llama al Workspace.Dispose, su AxWindowsMediaPlayer se eliminará primero, luego Panel1, Panel2, View1, View2 (según el orden en que haya creado su árbol de control).

Podemos descubrir la causa utilizando Reflector. Si examinamos Control.Dispose, que se lleva a cabo esencialmente como sigue (nota, el código irrelevante omitido y ligeramente modificado para mejorar la legibilidad):

protected override void Dispose(bool disposing) 
{ 
    if (disposing) 
    { 
     DisposeAxControls(); 

     foreach (Control control in Controls) 
     { 
      control.Parent = null; 
      control.Dispose(); 
     } 

     base.Dispose(disposing); 
    } 
} 

Y DisposeAxControls se implementa como:

internal virtual void DisposeAxControls() 
{ 
    foreach (Control control in Controls) 
    { 
     control.DisposeAxControls(); 
    } 
} 

La clase AxHost anula DisposeAxControls para destruir el control ActiveX alojado

No estoy muy seguro de por qué existe la función DisposeAxControls. Parece que AxHost simplemente habría reemplazado Dispose, como todos los demás, para destruir el control ActiveX, pero esa no es la forma en que se implementan las cosas.

Cuestiones relacionadas