无法从其他STA线程调用从STAThread创建的COM对象

我是COM的新手,试图了解STA和MTA之间的区别。 我试图创建一个示例,该示例将显示COM可以管理在STA中创建的对象的调用,该对象不是线程安全的。

这里的MyCalcServer类是使用ATL Simple Object创建的。 使用的设置与本文中的相同:

  • 线程模型:公寓
  • 聚合:没有
  • 界面:自定义

MyCalcServer COM对象用于另一个C#项目,它是:

 class Program { [STAThread] static void Main(string[] args) { MyCOMLib.MyCalcServer instance = new MyCOMLib.MyCalcServer(); string output1; instance.ChangeValue("Gant", out output1); Console.WriteLine(output1); Thread t1 = new Thread(() => { while (true) { string output; instance.ChangeValue("Gant", out output); Console.WriteLine(output); } }); t1.SetApartmentState(ApartmentState.STA); t1.Start(); // : // also has t2 and t3 here with similar code // : t1.Join(); t2.Join(); t3.Join(); } } 

但是,这总是导致在t1的代码中InvalidCastException (E_NOINTERFACE)。 我也尝试将ApartmentState更改为MTA但没有成功。

无法将“MyCOMLib.MyCalcServerClass”类型的COM对象强制转换为接口类型“MyCOMLib.IMyCalcServer”。 此操作失败,因为对IID为“{B005DB8C-7B21-4898-9DEC-CBEBE175BB21}”的接口的COM组件的QueryInterface调用由于以下错误而失败:不支持此类接口(HRESULTexception:0x80004002(E_NOINTERFACE)) 。

有人可以解释我在这里做错了什么吗?

您明确要求COM为主线程创建实例,然后将其传递给另一个线程。 当然在某些情况下它是允许的(例如将MyCalcServer声明为multithreading)。

但在你的情况下,它看起来你需要为另一个线程创建代理。 在常规COM客户端中,它由CoMarshalInterThreadInterfaceInStream完成。 有一篇大文章来澄清它http://www.codeproject.com/KB/COM/cominterop.aspx

我设法得到了这个决心。

由于我是COM新手,我对Proxy / Stub知之甚少,而且他们需要在STA和STA之间编组。 创建了一个新的ATL项目并确保我已经勾选了“Merge Proxy / Stub”。 问题消失了。

我发现此页面中的信息很有用: 为什么我要将Proxy / Stub代码与我的DLL项目合并。

代理/存根为组件提供标准编组。 在许多情况下,基于DLL的组件可能不需要代理/存根,因为它在其客户端的相同上下文中运行,并且该选项最初可能看起来无用。 但是,COM使用编组过程在multithreading情况下同步对组件的访问。 因此,基于DLL的组件至少在两种情况下需要代理/存根DLL:

  • 它正在运行multithreading客户端,需要在公寓(STA到STA或MTA到STA)之间传递接口指针。

  • DCOM可以为基于DLL的组件提供代理过程,以便可以在分布式环境中访问它。 在这种情况下,需要代理/存根来编组机器之间。

通过将代理/存根代码与您的实现合并,您不必分发两个DLL,只需一个。

我将@ Dewfy的答案标记为接受,因为他已经阐明了Proxy主题。