【问题标题】:Using CoInitializeEx on WinForms threads在 WinForms 线程上使用 CoInitializeEx
【发布时间】:2015-06-05 18:55:34
【问题描述】:

我正在为具有以下说明的 DSLR 相机开发 SDK:

Windows 应用程序开发注意事项 创建应用程序时 在 Windows 下运行的,每个都需要一个 COM 初始化 线程,以便从主线程以外的线程访问相机 线。创建用户线程并从中访问相机 线程,请务必执行 CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ) 在线程的开头和 CoUnInitialize() 最后。示例代码如下所示。这是 控制 EdsVolumeRef 或 EdsDirectoryItemRef 对象时相同 另一个线程,不仅仅是 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 应用程序,通常我使用托管线程类和 Control.Invoke 函数来避免跨线程问题。

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

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

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

【问题讨论】:

  • 是的,正如 Hans 在下面的回答中所说,您在启动线程之前调用 Thread.SetApartmentState

标签: c# .net multithreading com apartments


【解决方案1】:

每个线程都需要进行 COM 初始化

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

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

这也是你的代码 sn-p 做错了什么,启动 STA 线程而不泵送消息循环是非法的。你往往会意外地逃脱它。有时你不这样做,代码会以其他方式死锁或失败,比如不引发预期的事件。由于供应商认可它做错了,所以这里可能无关紧要。但是,如果您注意到死锁,那么您就会知道该往哪里看。

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

【讨论】:

  • 如果我创建一个托管的Thread 并调用ThreadInstance.SetApartmentState(ApartmentState.STA),它会负责处理消息循环吗?或者您的意思是我无能为力,等待死锁并请求供应商提供支持?
  • 不行,你必须自己处理。应用程序.运行()。这就是 STA 的全部意义所在,您只需承诺您会做对,COM 会相信您的话。供应商的代码 sn-p 表示,如果您不调用它,它会意外工作,没有太多理由假设它不会。好吧,只要您不偏离示例代码太多。功能性 STA 线程 is here 的代码示例。
  • 如果线程所做的唯一事情是调用第 3 方 API EdsSendCommand,并且这似乎是一个正常的阻塞调用,那么在该调用期间将没有机会发送消息,假设 API 快速返回可能没问题。 OTOH 我看到了一些奇怪的问题,例如当用户尝试打开 Word 文档时 Windows shell 挂起,这显然是由于不同进程中的非泵送 COM 线程造成的。对于一个非常短暂的线程,它可能没问题。 (但是为什么要让它成为一个单独的线程呢?)
  • @DanielEarwicker:使用EdsSendCommand 的大多数调用确实会非常短暂,而少数将是长时间运行的线程,显示摄像机的实时视图。感谢您提醒您有关崩溃的经历。如果我遇到任何问题,将回到这个问题。
猜你喜欢
  • 1970-01-01
  • 2017-07-14
  • 2017-01-13
  • 1970-01-01
  • 2012-12-04
  • 1970-01-01
  • 1970-01-01
  • 2011-07-10
  • 1970-01-01
相关资源
最近更新 更多