Что вызывает InvalidComObjectException: «COM-объект, который был отделен от лежащего в его основе RCW, не может быть использован.»? - PullRequest
5 голосов
/ 14 марта 2011

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

Контекст

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

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

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

Проблема

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

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

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

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

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

1 Ответ

3 голосов
/ 07 апреля 2011

Ваш обходной путь, к сожалению, является решением.

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

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

...