从 BackgroundWorker C# 调用的 CoInitialize

CoInitialize Called from BackgroundWorker C#

我有一个使用 C++ DLL 的 C# Windows 表单应用程序。在 DLL 中,我初始化 COM:

auto hResult = CoInitialize(NULL); // Initialize COM
if (hResult != S_OK && hResult != S_FALSE) { 
    WSACleanup(); 
    return 1; 
}

当我 运行 在 BackgroundWorker 进程之外的 DLL 时,一切正常。但是,如果我这样做,我的应用程序会在 DLL 完成时冻结。所以,我正在尝试使用 BackgroundWorker;但是每当我 运行 DoWork 函数中的 DLL 时,我都无法初始化 COM。

请有人解释一下,并就如何 运行 我在 BackgroundWorker 中的 DLL 提出任何建议?

谢谢。

BackgroundWorker 使用线程池线程。 .NET 线程池线程自动初始化为 MTA (CoInitializeEx(NULL, COINIT_MULTITHREADED))。您的 DLL 正在尝试将线程初始化为 STA (CoInitialize()),并且该调用应该返回 RPC_E_CHANGED_MODE。这是一个失败。

通常,我不会在库中的调用线程 上初始化 COM 。我认为这是一种反模式。单个客户端应用程序可以使用多个库,并且每个库都可能(尝试)初始化 COM。更好的设计是让每个线程的所有者在该线程上初始化 COM。您的客户端应用程序将为主线程和它拥有的任何后台线程初始化 COM(.NET 为您完成所有这些)。每个库都会(在文档中)指定其入口点的 threading/apartment 要求(例如 "This DLL's FooExport function must be called from an STA thread.")。库拥有的线程的单元状态将由库控制。从库中调用 CoInitialize/Ex 的唯一真正好处是尝试检测您的线程当前所处的单元状态,以便以编程方式检查库的单元要求,但有一些场景(中性线程单元)这会成为问题。

针对您的场景:

  • 如果您的 DLL 需要 STA,请在您的客户端应用程序中手动创建后台线程,并在启动线程之前将单元状态设置为 STA(请参阅 SetApartmentState)。还可以考虑删除库中的 CoInitialize 调用。
  • 如果您的 DLL 可以使用 MTA,请从您的 DLL 中删除 CoInitialize 调用,或者使用 CoInitializeEx(NULL, COINIT_MULTITHREADED).