如何从另一个线程正确访问COM对象DTE2.Windows枚举器?

时间:2010-12-22 12:09:27

标签: c# visual-studio com vs-extensibility

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

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

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

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

  •   
  • 该应用程序称为接口   那是为不同的人而编组的   线。 (HRESULT的例外情况:   0x8001010E(RPC_E_WRONG_THREAD))
      在EnvDTE.Window.get_Document()
      在线TextDocument td =   window.Document.Object( “TextDocument”)   作为TextDocument;

  •   

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

1 个答案:

答案 0 :(得分: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这样的同步计时器