1

我必须与第三方应用程序通信,唯一的方法是访问提供的 COM 组件。因为交互大约需要 3 分钟,所以必须在后台进行。所以我试图做的是使用选项“embedd interop-types”= true 添加对组件的引用,并创建一个通过接口读取非常基本数据的测试。记录在案的方法是遵循以下代码:

System sys = new System();
if(Convert.ToBoolean(sys.Initialize()) && Convert.ToBoolean(sys.Login("John Smith", out userInstance)))
Project proj = new Project();
if (Convert.ToBoolean(proj.Open(sys, m_projName, m_scenarioName)))
    someValue = proj.Name;

在使用 BackgroundWorker 之前,这非常有效。然后我在第一行代码中收到以下错误:

无法将“System.__ComObject”类型的 COM 对象转换为接口类型“ICAPILib.System”。此操作失败,因为 IID 为“{1F5EB3E2-35F6-11D2-A191-0060083A260B}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:加载类型库/DLL 时出错。(来自 HRESULT 的异常:0x80029C4A (TYPE_E_CANTLOADLIBRARY))。

我已经尝试重新注册组件但没有任何成功。

当使用 BackgroundWorker 时,Thread Apartment Type 显然是 MTA。COM 组件的 ThreadingModel 设置为公寓。如果我正确理解了这篇文章 http://msdn.microsoft.com/en-us/library/eaw10et3.aspx ,则互操作编组应该负责访问对象。

有人知道我可以尝试做什么吗?

4

2 回答 2

3

您不能使用 BackgroundWorker,它的线程类型错误。无法更改,它使用线程池线程,并且始终是 MTA。COM 自动创建一个 STA 线程,为 COM 服务器提供一个好客的家,这将导致任何调用被编组。哪个不适用于该组件,它没有正确注册其类型库。无论如何你都想避免的事情。

您必须改为创建自己的 Thread 并调用其 SetApartmentState() 方法将其切换到 STA,然后再启动它。在该线程上创建 COM 对象的实例也很重要,否则 CLR 仍将尝试编组调用。从技术上讲,您需要泵送一个消息循环(Application.Run),但您可能不需要这样做就可以逃脱。您会发现,如果调用死锁或预期事件未触发,则需要消息循环。

于 2012-05-16T09:04:34.420 回答
1

发生的事情是 COM Marshaller 无法编组该对象。

第一个答案:标准编组需要一个类型库。可能是对象的类型库未正确注册,因此出现错误。你是x86还是x64?尝试使用 REGTLB 注册库。

第二个答案:如果这不起作用,简单的答案是使用 STA 公寓类型的线程。这可能意味着您不能使用 BackgroundWorker,但可能必须使用专门创建的线程,您在完成时将其销毁。如果我们谈论的是三分钟的操作,那么额外的开销可以忽略不计。

请注意,对象必须在要使用它的线程上创建,并且单元类型必须与对象的线程模型兼容,以避免编组。

于 2012-05-16T08:24:40.977 回答