4

一段时间以来,我在 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) { }
}

我从网上可以找到的许多类似问题的描述中了解到可能的原因。它基本上是运行时可调用包装器和 GC 在 COM 互操作期间交互方式的副作用。这是一个类似问题的链接,并附有解释。

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

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

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

任何帮助将不胜感激!

4

2 回答 2

1

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

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

更重要的是,自从我实施更改以来,我还没有输过一次比赛。一个真正的海森堡,如果我见过的话。

无论如何,我将坚持这一点,直到我找到合适的解决方案或错误再次出现。

于 2011-07-18T13:26:12.957 回答
0

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

(这只是一种预感——你的Test类没有实现任何 VSX 接口,所以当构造函数被调用时,我无法从你的示例中看到)

于 2011-07-15T09:43:59.270 回答