在 .NET 中与 COM 交互之前是否需要调用 CoInitialize? [英] Do i need to call CoInitialize before interacting with COM in .NET?

查看:10
本文介绍了在 .NET 中与 COM 交互之前是否需要调用 CoInitialize?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

i know that a requirement of COM that every thread call CoInitialize before interacting with the COM system.

.NET 公开了一些在线程内部操作的项目,例如:

.NET exposes some items that internally operate on threads, e.g.:

  • ThreadPool threads
  • asychronous delegates (which use thread pool threads)
  • BackgroundWorker class (which use asychronous delegates (which use thread pool threads))
  • the garbage collector
  • and more! (i.e. e.g.)

如果我要通过线程与 COM 对象交互,我是否需要调用 CoInitialize先?

If i am going to be interacting with a COM object from a thread, do i need to call CoInitialize first?

我问是因为可能有一些更多的魔法自动为我调用它- 我不知道.

i ask because there may be some more magic that automagically calls it for me - i don't know.

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

Managed and Unmanaged Threading

For interoperability, the common language runtime creates and initializes an apartment when calling a COM object. A managed thread can create and enter a single-threaded apartment (STA) that contains only one thread, or a multi-threaded apartment (MTA) that contains one or more threads. When a COM apartment and a thread-generated apartment are compatible, COM allows the calling thread to make calls directly to the COM object. If the apartments are incompatible, COM creates a compatible apartment and marshals all calls through a proxy in the new apartment.

运行时调用 CoInitializeEx 将 COM 单元初始化为MTA 或 STA 公寓.

The runtime calls CoInitializeEx to initialize the COM apartment as either an MTA or an STA apartment.

更新二:

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

Looks like you should not use COM from any kind of thread that .NET can provide:

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

The Managed Thread Pool

There are several scenarios in which it is appropriate to create and manage your own threads instead of using thread pool threads:

  • 您需要一个前台线程.

  • You require a foreground thread.

您需要线程具有特定的优先级.

You require a thread to have a particular priority.

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

You have tasks that cause the thread to block for long periods of time. The thread pool has a maximum number of threads, so a large number of blocked thread pool threads might prevent tasks from starting.

您需要将线程放入单线程单元中.全部ThreadPool 线程位于多线程单元中.

你需要有一个稳定的身份与线程相关联,或者将线程专用于某项任务.

You need to have a stable identity associated with the thread, or to dedicate a thread to a task.

更新三:

看起来你可以设置未管理线程的线程模型:

Looks like you can set the threading model of unmanged threads:

可以标记托管线程以指示它将托管单线程或多线程单元.GetApartmentStateSetApartmentState线程<的 .threading.thread.trysetapartmentstate.aspx">TrySetApartmentState 方法/a> 类返回并分配线程的单元状态.如果尚未设置状态,则 GetApartmentState 返回 ApartmentState.Unknown.

Managed and Unmanaged Threading in Microsoft Windows

A managed thread can be marked to indicate that it will host a single-threaded or multithreaded apartment. The GetApartmentState, SetApartmentState, and TrySetApartmentState methods of the Thread class return and assign the apartment state of a thread. If the state has not been set, GetApartmentState returns ApartmentState.Unknown.

只有当线程在 ThreadState.Unstarted状态;一个线程只能设置一次.

The property can be set only when the thread is in the ThreadState.Unstarted state; it can be set only once for a thread.

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

If the apartment state is not set before the thread is started, the thread is initialized as a multithreaded apartment (MTA).

很多相互矛盾的信息.

这就是为什么我们将使用 Stackoverflow 上的人所说的作为真正答案的原因.

Which is why we'll use whatever the guy on Stackoverflow said as the true answer.

推荐答案

这里的信息实际上并不矛盾 - 如果您是 COM 新手,则不一定非常清楚.

The information here isn't actually conflicting - it's just not necessarily super clear if you're new to COM.

简答:

  • .Net threads are always already CoInitialized for you - you don't have to (and should not!) call it yourself.
  • ThreadPool threads (and therefore anything that uses ThreadPool threads, like asynchronous delegates and so on) are always initialized MTA. The only option for creating an STA thread is either adding the [STAThread] attribute to Main() to request that the runtime initializes the main thread as STA, or using thread.SetApartmentState(ApartmentState.STA) on a new thread you create before calling thread.Start() - otherwise they are MTA by default. In any case, the thread apartment model cannot be modified once the thread is started and running.

更长的答案:有两种方法可以调用 CoInitialize - 您可以使用它将线程初始化为单线程单元线程 (STA) 或多线程单元线程 (MTA).上面的文字是说,默认情况下,新线程和线程池线程会自动预 CoInitialized 为 MTA-flavor.但是对于一个新线程,您可以使用 ApartmentState 来指定 STA 风格,如果您在实际启动线程之前这样做的话.无论如何,它总是以一种或另一种方式 CoInitialized.

Longer answer: there are two ways to call CoInitialize - you can use it to initialize your thread as a Single-Threaded Apartment thread (STA), or as a Multi-Threaded Apartment thread (MTA). What the text above is saying is that by default, new threads and threadpool threads are automatically pre-CoInitialized as MTA-flavor. But with a new thread, you can use ApartmentState to specifiy STA-flavor, if you do so before actually starting the thread. It's always CoInitialized one way or the other by the time it's started anyhow.

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

Note that Main() on UI-based programs is marked with the [STAThread] attribute to ensure that it's STA-based; while on a console app, lack of [STAThread] means it's CoInited as MTA. The reason for this attribute, by the way, is that the thread that calls Main() is the one thread you can't specify STA vs MTA using ApartmentState - because it's already running and by the time Main() executes, so too late to use that; so think of the attribute as a hint to the runtime to set the apartment state before Main() is called.

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

The key thing to be aware of is that STA is usually used with UI and requires a message loop (which .Net WinForms provides for you); STA code should never block with Sleep() or similar, else your UI will also block. MTA, on the other hand, is designed for worker usage - background tasks, downloading files or doing computations in the background, for example, and generally should not own UI. You can use COM from either of these, but it may depend on what the COM object is doing or where you got it from. If it's a UI component, likely you'd want to use it from a STA thread; on the other hand, if it's a component for downloading or doing computations, you'd typically use it from an MTA thread.

上面的更新 1 基本上是说 .Net 运行时总是为您调用 CoInitialize - 但允许您选择 STA 与 MTA,MTA 是默认值.

Update 1 above is basically saying that the .Net runtime always calls CoInitialize for you - but lets you chose STA vs MTA, with MTA being the default.

上面的更新 2 基本上是说,由于 ThreadPool 线程是 MTA(并且您无法更改),因此您应该只将它们用于执行后台操作,而不是将它们用于 UI 任务.

Update 2 above is basically saying that since ThreadPool threads are MTA (and you don't get to change that), you should only use them for doing background operations, and not use them for UI tasks.

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

Update 3 is saying that for new threads, you can chose MTA vs STA - same as update 1, just being more explicit about the APIs.

整个 MTA 与 STA 的事情可能会变得相当复杂,建议阅读 这篇文章 作为起点.不过,大局主要是通过记住 STA = 单线程和 UI 来概括的;MTA = 多线程、后台/工作任务.(STA vs MTA 也适用于对象,而不仅仅是线程,COM 在幕后做了一大堆工作,让不同类型的线程使用不同类型的对象.当它运行良好时,你没有意识到它和可以很高兴地忽略它;但是当您遇到限制或限制时,通常很难弄清楚到底发生了什么.)

The whole MTA vs STA thing can get quite complex, suggest reading this article as a starting point. The big picture, though, is mostly summarized by remembering that STA = single thread and UI; MTA = multiple threads, background/worker tasks. (STA vs MTA also applies to objects, not just threads, and COM does a whole bunch of work behind the scenes to let the different types of threads use the different types of objects. When it works well, you don't realize it and can blissfully ignore it; but when you hit a restriction or limitation, it can often be tricky to figure out just what's going on.)

这篇关于在 .NET 中与 COM 交互之前是否需要调用 CoInitialize?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆