Что вызывает InvalidComObjectException: COM-объект, который был отделен от его основного RCW, не может быть использован.?

Я рассмотрел различные вопросы, в которых упоминается это конкретное исключение (в этом вопросе перечислены многие из них, которые я посетил). Кроме того, у меня такой же общий вопрос, как в этом сообщении, но в другом контексте, поэтому ответ мне не поможет.

Контекст

У меня есть класс, производный от AxWindowsMediaPlayer который принадлежит классу с именем View, который находится внутри Panel, внутри Workspace. Недавно я спросил вопрос об этой ситуации, но этот вопрос был направлен на то, подходит ли мой способ решения этой проблемы. Предыстория этого вопроса актуальна здесь:

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

Когда View будет удален, для всех оставшихся View объектов будет вызван метод с именем Synchronize(). Для View, содержащего AxWindowsMediaPlayer, он вызывает videoPlayer.Error.clearErrorQueue() .

Проблема

Когда я вызываю Dispose() на верхнем уровне (Workspace.Dispose()), если другой View удаляется и затем вызывает вызов Synchronize() для оставшихся View объектов, View, содержащий класс AxWindowsMediaPlayer, генерирует исключение в строке videoPlayer.Error.clearErrorQueue(), заявляя:

InvalidComObjectException: COM-объект, который был отделен от основного RCW, использовать нельзя.

Я озадачен тем, как AxWindowsMediaPlayer отделяется от своего основного RCW (Runtime Callable Wrapper). Я прочитал эта статья, в которой рассказывается об этом исключении и об опасностях вызова _ 20_. Я не вызываю этот метод явно. Я установил точки останова в Dispose методах классов Panel, View и VideoPlayerControl (производных от AxWindowsMediaPlayer), но ни один из них не сработает до того, как произойдет исключение.

Мое решение - убедиться, что View с медиаплеером всегда удаляется первым. Это была мотивация моего предыдущего вопроса. Но я хотел бы понять, как это происходит, чтобы понять, нужно ли это исправить. Кто вызывает отделение AxWindowsMediaPlayer от его RCW до того, как Dispose будет вызван в родительском классе?

Я предполагаю, что сборщик мусора вызывает финализатор AxWindowsMediaPlayer, но я не понимаю, что его запускает. По какой-то причине вызов Dispose на более высоком уровне вызывает вызов Marshal.ReleaseComObject из-под пола. Может кто меня просветить?


person bentsai    schedule 14.03.2011    source источник


Ответы (1)


К сожалению, решение проблемы - это ваш обходной путь.

Control.Dispose сначала рекурсивно удаляет все элементы управления ActiveX, а затем рекурсивно вызывает Dispose дочерние элементы управления. В вашем примере, если вы вызываете Workspace.Dispose, ваш AxWindowsMediaPlayer будет удален первым, а затем Panel1, Panel2, View1, View2 (в зависимости от порядка, в котором вы построили свое дерево управления).

Мы можем обнаружить причину, используя 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, но это не так, как это реализовано.

person Lee Berger    schedule 06.04.2011