在 WinForms 线程上使用 CoInitializeEx

Using CoInitializeEx on WinForms threads

我正在为 DSLR 相机开发 SDK,其中包含以下说明:

Notes on Developing Windows Applications When creating applications that run under Windows, a COM initialization is required for each thread in order to access a camera from a thread other than the main thread. To create a user thread and access the camera from that thread, be sure to execute CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ) at the start of the thread and CoUnInitialize() at the end. Sample code is shown below. This is the same when controlling EdsVolumeRef or EdsDirectoryItemRef objects from another thread, not just with EdsCameraRef.

void TakePicture(EdsCameraRef camera)
{
    // Executed by another thread
    HANDLE hThread = (HANDLE)_beginthread(threadProc, 0, camera);
    // Block until finished
    ::WaitForSingleObject( hThread, INFINITE );
}

void threadProc(void* lParam)
{
    EdsCameraRef camera = (EdsCameraRef)lParam;
    CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
    EdsSendCommand(camera, kEdsCameraCommand_TakePicture, 0);
    CoUninitialize();
    _endthread();
}

我的应用程序是 C# WinForms 应用程序,通常,我使用托管线程 class 和 Control.Invoke 函数来避免跨线程问题。

由于我没有用于使用 SDK 的 C# 示例源代码,我的问题是,在标有 [=12= 的应用程序中使用 CoInitializeEx 是否有用 and/or ] 属性?

我还没有遇到过需要让我的应用程序为线程创建一个新单元的场景,因此一些见解将有助于更好地理解线程模型。

更新: 在阅读了更多有关公寓和 COM 的内容后,它开始变得有些道理了。现在我想知道 .NET 托管线程 class 默认是什么,我们可以在没有 P/Invoke 的情况下以托管方式为每个线程指定一个单元模型吗?

a COM initialization is required for each thread

是的,坚如磐石的要求。如此之多,以至于 CLR 会自动执行此操作,而无需您提供帮助。每个 .NET 线程在启动前都会调用 CoInitializeEx() 运行.

CLR 需要知道传递给 CoInitializeEx() 的参数,在 STA 和 MTA 之间进行选择。对于 Winforms 程序的启动线程,它由 Program.cs 中 Main() 方法的 [STAThread] 属性决定。它必须 是 STA,这是对显示 UI 的线程的硬性要求。对于您自己启动的任何线程,它由您调用 Thread.SetApartmentState() 决定,默认为 MTA。对于任何线程池线程,如 BackgroundWorker 或 Task 或 QUWI 使用的线程,它始终是 MTA 且无法更改。如果正确使用,此类线程的自动结果永远无法正确支持 STA。

这也是您的代码片段做错的地方,启动 STA 线程而不抽取消息循环是非法的。你往往会意外地逃脱它。有时你不这样做,代码会死锁或以其他方式失败,比如没有引发预期的事件。由于供应商批准它做错了,所以在这里可能无关紧要。但是,如果您曾经注意到死锁,那么您就会知道去哪里查看。

长话短说,你不能自己调用​​ CoInitializeEx(),它已经完成了。