在与.NET中的COM交互之前,我是否需要调用CoInitialize?

时间:2012-01-17 21:20:11

标签: .net multithreading com

我知道每个线程在与COM系统交互之前调用CoInitialize的COM的要求

.NET公开了一些内部在线程上运行的项目,例如:

如果我要从一个线程与一个COM对象进行交互,我是否需要先调用CoInitialize

我问,因为可能有一些more magic that automagically calls it for me - 我不知道。


奖金阅读

  

Managed and Unmanaged Threading

     

对于互操作性,公共语言运行库创建和   在调用COM对象时初始化公寓。托管线程   可以创建并输入包含的单线程单元(STA)   只有一个线程,或包含一个的多线程单元(MTA)   或更多线程。当COM公寓和线程生成的公寓   兼容,COM允许调用线程直接进行调用   到COM对象。如果公寓不兼容,COM会创建一个   兼容的公寓和编组所有通过代理在新的呼叫   公寓。

     

运行时调用CoInitializeEx将COM公寓初始化为   无论是MTA还是STA公寓。

更新二:

看起来你不应该使用.NET提供的任何类型的线程中的COM:

  

The Managed Thread Pool

     

有几种情况适合创建和   管理自己的线程而不是使用线程池线程:

     
      
  • 您需要一个前台线程。

  •   
  • 您需要一个具有特定优先级的线程。

  •   
  • 您的任务导致线程长时间阻塞   时间。线程池有最大线程数,所以很大   被阻止的线程池线程数可能会阻止任务   开始。

  •   
  • 您需要将线程放入单线程单元中。所有   ThreadPool线程位于多线程单元中。

  •   
  • 您需要具有与该线程关联的稳定标识,或者   将线程专用于任务。

  •   

更新三

看起来你 可以 设置无人管线程的线程模型:

  

Managed and Unmanaged Threading in Microsoft Windows

     

可以标记托管线程以指示它将托管单线程或多线程单元。 GetApartmentState类的SetApartmentStateTrySetApartmentStateThread方法返回并指定线程的单元状态。如果尚未设置状态,则GetApartmentState会返回ApartmentState.Unknown

     

只有当线程处于ThreadState.Unstarted状态时,才能设置该属性;它只能为一个线程设置一次。

     

如果在线程启动之前未设置单元状态,则将线程初始化为多线程单元(MTA)。

很多相互矛盾的信息。

这就是为什么我们会使用Stackoverflow上的任何人所说的真实答案。

2 个答案:

答案 0 :(得分:10)

这里的信息实际上并没有冲突 - 如果你是COM的新手,那就不一定非常清楚了。

简短回答:

  • .Net线程总是已经为你进行了CoInitialized - 你不必(也不应该!)自己调用它。
  • ThreadPool线程(因此任何使用ThreadPool线程的东西,如异步委托等)总是初始化的MTA。创建STA线程的唯一选择是将[STAThread]属性添加到Main()以请求运行时将主线程初始化为STA,或者在您之前创建的新线程上使用thread.SetApartmentState(ApartmentState.STA)调用thread.Start() - 否则默认情况下它们是MTA。无论如何,一旦线程启动并运行,就无法修改线程单元模型。

更长的答案:有两种方法可以调用CoInitialize - 您可以使用它来将线程初始化为单线程单元线程(STA),或者作为多线程单元线程(MTA)。上面的文字说的是默认情况下,新线程和线程池线程自动被预先CoInitialized为MTA-flavor。但是使用新线程,如果在实际启动线程之前执行此操作,则可以使用ApartmentState来指定STA风格。无论如何,它始终以这种或那种方式进行初始化。

请注意,基于UI的程序上的Main()标有[STAThread]属性,以确保它是基于STA的;在控制台应用程序上,缺少[STAThread]意味着它被作为MTA CoInited。顺便说一下,这个属性的原因是调用Main()的线程是你不能使用ApartmentState指定STA vs MTA的一个线程 - 因为它已经运行并且在Main()执行时,所以太晚了用那个;所以将该属性视为运行时提示在调用Main()之前设置单元状态。

需要注意的关键是STA通常与UI一起使用,需要一个消息循环(.Net WinForms为您提供); STA代码永远不应该使用Sleep()或类似代码阻止,否则您的UI也会阻塞。另一方面,MTA是为工作人员使用而设计的 - 例如,后台任务,下载文件或在后台进行计算,通常不应该拥有UI。您可以使用其中任何一个中的COM,但这可能取决于COM对象正在执行的操作或从何处获取它。如果它是一个UI组件,可能你想从STA线程中使用它;另一方面,如果它是用于下载或进行计算的组件,则通常在MTA线程中使用它。

上面的更新1基本上是说.Net运行时总是为你调用CoInitialize - 但是让你选择STA vs MTA,MTA是默认值。

上面的更新2基本上是说因为ThreadPool线程是MTA(并且你不能改变它),所以你应该只使用它们来进行后台操作,而不是将它们用于UI任务。

更新3表示,对于新线程,您可以选择MTA与STA - 与更新1相同,只是更明确地了解API。

整个MTA与STA之间的关系可能非常复杂,建议将this article作为起点。不过,大局主要通过记住STA =单线程和UI来总结; MTA =多个线程,后台/工作人员任务。 (STA vs MTA也适用于对象,而不仅仅是线程,COM在幕后做了大量工作,让不同类型的线程使用不同类型的对象。当它运行良好时,你没有意识到它和可以幸福地忽略它;但是当你遇到限制或限制时,弄清楚发生了什么通常很棘手。)

答案 1 :(得分:1)

要回答你的第一个问题,如果我正确地记住了我的Don Box,每个线程都必须调用CoInitialize。没有例外。

关于自动部分,我不知道。