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


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

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

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

我问,因为可能有一些 更多魔法自动称呼它为我 - 我不知道。


奖金阅读

托管和非托管线程

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

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

更新二:

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

托管线程池

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

  • 您需要前台线程。

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

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

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


11573
2018-01-17 21:20


起源



答案:


这里的信息实际上并没有冲突 - 如果你是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之间的关系会非常复杂 本文 作为一个起点。不过,大局主要通过记住STA =单线程和UI来总结; MTA =多个线程,后台/工作人员任务。 (STA vs MTA也适用于对象,而不仅仅是线程,COM在幕后做了大量的工作,让不同类型的线程使用不同类型的对象。当它运行良好时,你没有意识到它和可以幸福地忽略它;但是当你遇到限制或限制时,弄清楚发生了什么通常是很棘手的。)


9
2018-01-18 02:30



我想缺少的是示例代码如何设置i)a Thread,ii)a ThreadPool 线程,iii)异步代理,iv)a BackgroundWorker 到不同的COM线程模型。一世 认为 短版是你 不能 改变a的线程模型 ThreadPool 线程,非同步代表或 BackgroundWorker。使用STA线程模型在线程上运行某些东西的唯一方法是构造一个 System.Threading.Thread 对象,并打电话 thread.SetApartmentState(ApartmentState.STA)。如果这一切都正确,你可以加入你的接受答案。 - Ian Boyd
@IanBoyd - 我将“简答”部分分为两部分,总结了你提到的问题。 - BrendanMcK


答案:


这里的信息实际上并没有冲突 - 如果你是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之间的关系会非常复杂 本文 作为一个起点。不过,大局主要通过记住STA =单线程和UI来总结; MTA =多个线程,后台/工作人员任务。 (STA vs MTA也适用于对象,而不仅仅是线程,COM在幕后做了大量的工作,让不同类型的线程使用不同类型的对象。当它运行良好时,你没有意识到它和可以幸福地忽略它;但是当你遇到限制或限制时,弄清楚发生了什么通常是很棘手的。)


9
2018-01-18 02:30



我想缺少的是示例代码如何设置i)a Thread,ii)a ThreadPool 线程,iii)异步代理,iv)a BackgroundWorker 到不同的COM线程模型。一世 认为 短版是你 不能 改变a的线程模型 ThreadPool 线程,非同步代表或 BackgroundWorker。使用STA线程模型在线程上运行某些东西的唯一方法是构造一个 System.Threading.Thread 对象,并打电话 thread.SetApartmentState(ApartmentState.STA)。如果这一切都正确,你可以加入你的接受答案。 - Ian Boyd
@IanBoyd - 我将“简答”部分分为两部分,总结了你提到的问题。 - BrendanMcK


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

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


1
2018-01-17 21:23



我拥有那本书。第一章应该是必读的。 - Ian Boyd