Неприятная проблема взаимодействия COM в VSIX - PullRequest
4 голосов
/ 15 июля 2011

В течение некоторого времени я наблюдал периодически возникающую проблему COM в моем пакете VSIX для Visual Studio 2010. При попытке подписаться на один из приемников событий в среде IDE в произвольном порядке выдается следующая ошибка:

«COM-объект, который был отделен от лежащего в его основе RCW, использовать нельзя»

Случай воспроизведения сводится к этому коду (который, очевидно, должен использоваться в VSIX):

using System;
using EnvDTE;
using EnvDTE80;

class Test
{
    private readonly Events _events;
    private readonly Events2 _events2;
    private readonly BuildEvents _buildEvents;
    private readonly ProjectItemsEvents _projectItemsEvents;

    public Test(IServiceProvider provider)
    {
        var dte = (DTE)provider.GetService(typeof(DTE));
        var dte2 = (DTE2)dte;

        // Store all references in fields as a GC precaution.
        _events = dte.Events;
        _events2 = (Events2)dte2.Events;
        _buildEvents = _events.BuildEvents;
        _projectItemsEvents = _events2.ProjectItemsEvents;

        // Proceed to subscribe to event sinks.
        _buildEvents.OnBuildBegin += BuildBeginHandler; // BOOM!
        _projectItemsEvents.ItemAdded += ItemAddedHandler;
    }

    private void ItemAddedHandler(ProjectItem projectItem) { }

    private void BuildBeginHandler(vsBuildScope scope, vsBuildAction action) { }
}

Я узнал о возможной причине из многочисленных описаний подобных проблем, которые можно найти в сети.По сути, это побочный эффект от того, как Runtime Callable Wrappers и GC взаимодействуют во время COM-взаимодействия.Вот ссылка на похожую проблему с объяснением.

Я согласен с этим объяснением, особенно потому, что оно предлагает простой обходной путь - сохранение ссылки на приемник события в поле, чтобы предотвратить его преждевременное сборку GC.Действительно, многие люди, похоже, решили свою проблему таким образом.

Меня беспокоит то, что это не работает в моем случае .Я действительно озадачен, почему.Как вы можете видеть, я уже храню все ссылки на объекты в полях в качестве меры предосторожности.Все же ошибка все еще происходит.Я пытался быть еще более явным, используя GC.KeepAlive() вызовы в конце ctor, но безрезультатно.Осталось ли что-нибудь еще сделать?

Без решения мой VSIX случайно не загружается, оставляя пользователю одну возможность: перезапустить Visual Studio и надеяться, что в следующий раз этого не произойдет.

Любая помощь будет по достоинству оценена!

Ответы [ 2 ]

1 голос
/ 18 июля 2011

Ну, я сдался и просто сделал единственное, что пришло мне в голову. Я подумал, что, поскольку это, очевидно, состояние гонки, на которое я не могу повлиять предсказуемым образом, я мог бы с таким же успехом вновь принять участие в гонке, если проиграл.

Таким образом, я переместил строки подписки в цикл while, который try .. catch -es их и повторяет попытку через немного Thread.Sleep(). Цикл завершается либо при успешном завершении обеих подписок, либо когда я непрерывно проигрывал гонку более 2 секунд.

Кикер, я ни разу не проиграл гонку с тех пор, как внедрил изменения. Настоящий гейзенбаг, если я когда-нибудь его видел.

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

0 голосов
/ 15 июля 2011

Я подозреваю, что ваша проблема в том, что вы пытаетесь подключить обработчики событий слишком рано.Обычно вам нужно делать такие вещи в методе Initialize вашего пакета / окна инструментов / чего угодно - вообще говоря, если вам нужно использовать сервис, который вам нужно сделать, если после вызова метода Initialize,определенно не делайте этого в конструкторе вашего Package.

(Это просто догадка - ваш класс Test не реализует никаких интерфейсов VSX, и поэтому я не могу видеть из вашего примера, когдаконструктор вызывается)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...