【问题标题】:A nasty COM interop problem in VSIXVSIX 中一个令人讨厌的 COM 互操作问题
【发布时间】:2011-07-15 09:01:38
【问题描述】:

一段时间以来,我在 Visual Studio 2010 的 VSIX 包中发现了间歇性 COM 问题。尝试订阅 IDE 的基于 COM 的事件接收器之一随机引发以下错误:

“无法使用已与其底层 RCW 分离的 COM 对象”

复制案例归结为这段代码(显然必须在 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) { }
}

我从网上对类似问题的大量描述中了解到可能的原因。它基本上是在 COM 互操作期间 Runtime Callable Wrappers 和 GC 交互方式的副作用。这是一个类似问题的link,并附有解释。

我对这个解释很好,特别是因为它提出了一个简单的解决方法 - 将事件接收器引用存储在一个字段中,以防止它被过早地 GC'ed。确实,很多人似乎都通过这种方式解决了他们的问题。

困扰我的是它在我的情况下不起作用。我真的很困惑为什么。如您所见,我已经将 all 对象引用存储在字段中以防万一。然而错误仍然发生。我尝试在 ctor 末尾使用 GC.KeepAlive() 调用更加明确,但无济于事。还有什么事情要做吗?

如果没有解决方案,我的 VSIX 随机加载失败,用户只有一个选择:重新启动 Visual Studio 并希望下次不会发生这种情况。

任何帮助将不胜感激!

【问题讨论】:

    标签: c# visual-studio com-interop vsix


    【解决方案1】:

    好吧,我放弃了,只是做了我唯一想到的事情。我想,既然这显然是一个我无法以可预测的方式影响的比赛条件,那么如果我输了,我还不如重新参加比赛。

    所以我将订阅行移动到 while 循环中,try..catch-es 并在一段时间 Thread.Sleep() 后重试。当两个订阅都成功或我连续输掉比赛超过 2 秒时,循环退出。

    关键是,自从我实施了更改以来,我还没有输过一次比赛。一个真正的黑森虫,如果我见过的话。

    无论如何,我会坚持下去,直到找到合适的解决方案或错误再次出现。

    【讨论】:

    • 感谢您提供解决方案。不完全是我想听到的,但如果我找不到其他任何东西,那将不得不这样做。
    【解决方案2】:

    我怀疑您的问题实际上是您试图过早地连接您的事件处理程序。你通常需要在你的包/工具窗口/任何东西的Initialize方法中做这些事情——一般来说,如果你需要使用一个服务,你需要在调用Initialize方法之后做,绝对不要在包的构造函数中执行此操作。

    (这只是一种预感 - 您的 Test 类没有实现任何 VSX 接口,因此在调用构造函数时我无法从您的示例中看到)

    【讨论】:

    • 在调用 base.Initialize() 之后,我正在从包的 Initialize 内部调用 Test 的构造函数。包类本身派生自 Microsoft.VisualStudio.Project.ProjectPackage。所以一切似乎都井然有序,至少在这方面是这样。
    猜你喜欢
    • 1970-01-01
    • 2015-01-07
    • 2011-04-13
    • 2012-09-02
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多