导致InvalidComObjectException的原因是:“无法使用已与其基础RCW分离的COM对象。”?

时间:2011-03-14 18:45:12

标签: winforms interop activex dispose finalizer

我已经查看了提到这个特定异常的各种问题(this question lists many of them,我已访问过)。另外,我有相同的general question as this post,但在不同的背景下,the answer对我没有帮助。

上下文

我有一个派生自AxWindowsMediaPlayer的类,该类由名为View的类所有,该类位于PanelWorkspace内。我最近询问a question这种情况,但问题是针对问题的解决方法是否正常。该问题的背景与此相关:

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

View被释放时,将在所有剩余的Synchronize()个对象上调用名为View的方法。对于包含View的{​​{1}},它会调用videoPlayer.Error.clearErrorQueue()

问题

当我在顶层(AxWindowsMediaPlayer)拨打Dispose()时,如果另一个Workspace.Dispose()被处理掉,然后在剩余的View上调用Synchronize()对象,包含View类的View会在AxWindowsMediaPlayer行引发异常,说明:

  

InvalidComObjectException:无法使用已与其基础RCW分离的COM对象。

我对videoPlayer.Error.clearErrorQueue()如何与其基础RCW(Runtime Callable Wrapper)分离感到困惑。我看过this article that talks about this exception以及调用Marshal.ReleaseComObject()的危险。我没有明确地称这种方法。我在AxWindowsMediaPlayerDispose以及Panel(来自View)类的VideoPlayerControl方法中设置了断点,但在异常之前没有一个被击中发生的情况。

我的解决方法是确保首先处理媒体播放器的AxWindowsMediaPlayer。这是我上一个问题背后的动机。但我想了解这是如何发生的,所以我可以看出这是否是我需要解决的问题。 在父类调用View之前,是谁导致AxWindowsMediaPlayer与其RCW分离?

我的猜测是Dispose终结器被GC调用,但我不明白触发它的是什么。出于某种原因,在较高级别调用AxWindowsMediaPlayer会导致Dispose在地板下被调用。有人可以开导我吗?

1 个答案:

答案 0 :(得分:3)

不幸的是,您的解决方案是解决方案。

Control.Dispose首先以递归方式处理所有ActiveX控件,然后在子控件上递归调用Dispose。在您的示例中,如果您致电Workspace.Dispose,您的AxWindowsMediaPlayer将首先处理,然后Panel1Panel2View1View2(取决于在您构建控件树的顺序上。)

我们可以使用Reflector来发现原因。如果我们检查Control.Dispose,它基本上实现如下(注意,省略了不相关的代码并略微改变以提高可读性):

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

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

        base.Dispose(disposing);
    }
}

DisposeAxControls实现为:

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

AxHost类会覆盖DisposeAxControls以销毁托管的ActiveX控件。

我不确定为什么存在DisposeAxControls函数。似乎AxHost会像其他人一样简单地覆盖Dispose来销毁ActiveX控件,但事实并非如此。