我必须与第三方应用程序通信,唯一的方法是访问提供的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时,线程公寓类型显然是MTA。 COM组件将ThreadingModel设置为apartment。如果我理解这篇文章 http://msdn.microsoft.com/en-us/library/eaw10et3.aspx 正确地,互操作编组应该负责访问对象。
有没有人知道我可以尝试做些什么呢?
答案 0 :(得分:3)
您无法使用BackgroundWorker,其线程类型错误。哪个无法更改,它使用线程池线程,它始终是MTA。 COM自动创建一个STA线程,为COM服务器提供一个好客的家庭,这将导致任何调用被编组。哪个不适用于该组件,它没有正确注册其类型库。无论如何你想要避免的东西。
您必须创建自己的Thread,并在启动之前调用其SetApartmentState()方法将其切换到STA。在该线程上创建COM对象的实例也很重要,否则CLR仍会尝试编组调用。从技术上讲,你需要抽一个消息循环(Application.Run),但你可能不需要这样做。您将发现,如果呼叫死锁或预期事件未触发,则需要消息循环。
答案 1 :(得分:1)
发生的事情是COM Marshaller无法编组该物体。
第一个答案:标准编组需要一个类型库。可能是对象的类型库未正确注册,因此错误。你是x86还是x64?尝试使用REGTLB注册库。
第二个答案:如果这不起作用,简单的答案是使用STA公寓类型的线程。这可能意味着您不能使用BackgroundWorker,但可能必须使用在完成时销毁的特殊创建的线程。如果我们谈论的是三分钟的操作,那么额外的开销可以忽略不计。
请注意,必须在要使用它的线程上创建对象,并且公寓类型msut与对象的线程模型兼容,以避免编组。