我有一个使用 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 为您完成所有这些)。每个库都会(在文档中)指定其入口点的线程/单元要求(例如,“此 DLL 的 FooExport
函数必须从 STA 线程调用。”)。库拥有的线程的单元状态将由库控制。从库中调用 CoInitialize/Ex
的唯一真正好处是尝试检测您的线程当前所处的单元状态,以便以编程方式检查库的单元要求,但有一些场景(中性线程单元),这会成为问题。
针对您的场景:
- 如果您的 DLL 需要 STA,请在您的客户端应用程序中手动创建后台线程,并在启动线程之前将单元状态设置为 STA(参见
SetApartmentState
)。还可以考虑删除库中的CoInitialize
调用。 - 如果您的 DLL 可以使用 MTA,请从您的 DLL 中删除
CoInitialize
调用,或使用CoInitializeEx(NULL, COINIT_MULTITHREADED)
。
关于c# - 从 BackgroundWorker C# 调用的 CoInitialize,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48282022/