1

我有一个使用System.Timers.TimermyTimer 的 Visual Studio 加载项。
每 N 秒 myTimer 触发并执行以下代码:

foreach(Window window in DTE2.Windows)
{
    TextDocument td = window.Document.Object("TextDocument") as TextDocument;
    // do stuff with td...  
}

因为这是从另一个线程调用的,所以我有时会遇到以下错误之一:

  • IEnumVARIANT 的 QI 在非托管服务器上失败。
    在 foreach 线上的 EnvDTE.Windows.GetEnumerator()
    (DTE2.Windows 中的窗口窗口)

  • 应用程序调用了为不同线程编组的接口。(HRESULT 异常:0x8001010E (RPC_E_WRONG_THREAD))
    在 EnvDTE.Window.get_Document()
    行 TextDocument td = window.Document.Object("TextDocument") as TextDocument;

由于涉及 COM 对象,在另一个线程中访问此枚举器的正确方法是什么?
某种 COM 线程编组?
还有什么?

4

1 回答 1

2

您在尝试保护不是线程安全的对象模型时与 COM 发生冲突。像 Visual Studio 自动化界面这样复杂的对象模型从来没有。COM 尝试通过自动将后台线程上的调用编组到 STA 线程来做到这一点。这是通过代理完成的,该代理是原始 COM 接口的副本,该接口具有所有相同的方法,但将对它们进行的任何调用编组到在 STA 线程上运行方法的接口。

此代理具有线程亲和性,它只能在创建它的线程上使用。DTE2 是你的问题。如果任何可扩展性代码之前运行并创建了 DTE2 接口实例,则 DTE2 将是“真正的”接口指针,而不是代理。如果你然后在工作线程上使用它,比如 Timer 为其 Elapsed 事件创建的那个,那么你就会得到炸弹。它也可以反过来工作,如果 DTE2 是由您的代码首先创建的,那么您将破坏任何以正常方式运行的可扩展性代码。

也许 DTE2.DTE 会解决你的问题,不确定。最终它没有解决任何问题,代码总是会在 Visual Studio STA 线程上运行。只是不要使用 System.Timer.Timer,使用像 System.Windows.Forms.Timer 这样的同步计时器

于 2010-12-22T14:56:33.243 回答