C# 在WinForms线程上使用CoInitializeEx

C# 在WinForms线程上使用CoInitializeEx,c#,.net,multithreading,com,apartments,C#,.net,Multithreading,Com,Apartments,我正在为单反相机开发一个SDK,它有以下说明: 创建应用程序时开发Windows应用程序的注意事项 在Windows下运行时,每个 线程,以便从主线程以外的线程访问摄像头 线创建用户线程并从该线程访问相机 线程,确保执行CoInitializeEx(NULL, Conit_APARTMENTTHREADED)在线程的开头,然后 在结尾处输入coninitialize()。示例代码如下所示。这是 从控制EdsVolumeRef或EdsDirectoryItemRef对象时相同 另一条线索,不仅仅是

我正在为单反相机开发一个SDK,它有以下说明:

创建应用程序时开发Windows应用程序的注意事项 在Windows下运行时,每个 线程,以便从主线程以外的线程访问摄像头 线创建用户线程并从该线程访问相机 线程,确保执行CoInitializeEx(NULL, Conit_APARTMENTTHREADED)在线程的开头,然后 在结尾处输入coninitialize()。示例代码如下所示。这是 从控制EdsVolumeRef或EdsDirectoryItemRef对象时相同 另一条线索,不仅仅是EdsCameraRef

我的应用程序是一个C#WinForms应用程序,通常使用托管线程类和控件。调用函数以避免跨线程问题

由于我没有使用SDK的C语言示例源代码,我的问题是,在标记有
[STAThread]
属性的应用程序中使用
CoInitializeEx
是否有用和/或必要

我没有遇到过这样的情况:我需要让我的应用程序为线程创建一个新的单元,这样一些见解将有助于更好地理解线程模型

更新:在阅读了有关公寓和COM的更多信息后,它开始变得有意义了。现在我想知道.NET托管线程类的默认值是什么,我们是否可以在不使用P/Invoke的情况下以托管方式为每个线程指定单元模型

每个线程都需要COM初始化

是的,严格要求。如此之多,以至于CLR会自动执行此操作,而无需您的帮助。每个.NET线程在开始运行之前都调用了CoInitializeEx()

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

这也是您的代码片段所做的错误,启动STA线程而不泵送消息循环是非法的。你很容易侥幸逃脱。有时您不会这样做,代码将以其他方式死锁或失败,例如不引发预期事件。既然供应商认可它做错了,这在这里可能并不重要。但如果你注意到死锁,你就会知道该去哪里找


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

如果我创建一个托管
线程
并调用
ThreadInstance.SetApartmentState(ApartmentState.STA)
,这会处理消息循环吗?或者你的意思是,我不能指望等待僵局并向供应商寻求支持吗?不,你必须自己处理。Application.Run()。这就是STA的意义所在,你只要承诺你会做好,COM就会信守诺言。供应商的代码片段说,如果您不调用它,它将意外工作,没有太多理由认为它不会。好吧,只要你不偏离示例代码太多。函数STA线程的代码示例。如果该线程只调用第三方API
EdsSendCommand
,而这似乎是一个正常的阻塞调用,那么在该调用期间就没有机会发送消息,假设API很快返回也可以。OTOH我见过一些奇怪的问题,比如当用户试图打开Word文档时,Windows shell挂起,这显然是由于另一个进程中的非泵送COM线程造成的。对于一个非常短命的线程,它可能是好的。(那么为什么要将其作为一个单独的线程呢?@DanielEarwicker:使用
EdsSendCommand
时,大多数调用实际上都是非常短暂的,而少数调用是长时间运行的线程,显示摄像机的实时视图。谢谢你提醒我你的撞车经历。如果遇到任何问题,我将返回到这个问题。是的,正如Hans在下面的回答中所说,在启动线程之前,您可以调用Thread.SetApartmentState。
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();
}