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

时间:2010-02-01 08:26:22

标签: c# multithreading com interop marshalling

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

这里的

MyCalcServer类是使用ATL Simple Object创建的。使用的设置与this article中的设置相同:

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

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但没有成功。

  

无法转换类型的COM对象   'MyCOMLib.MyCalcServerClass'来   界面类型   'MyCOMLib.IMyCalcServer'。这个   操作失败,因为   QueryInterface调用COM   与IID接口的组件   '{B005DB8C-7B21-4898-9DEC-CBEBE175BB21}'   由于以下错误而失败:否   支持这样的接口(例外   来自HRESULT:0x80004002   (E_NOINTERFACE))。

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

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

我设法得到了这个决心。

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

我发现此页面中的信息很有用:Why would I want to merge Proxy/Stub code with my DLL project.

  

代理/存根提供标准   编组你的组件。在很多   基于DLL的组件可能不会   需要代理/存根,因为它正在运行   在其客户的相同背景下,和   这个选项起初可能看起来毫无用处。   但是,COM使用编组   进程同步访问a   多线程中的组件   的情况。所以,一个基于DLL的组件   至少需要一个代理/存根DLL   两种情况:

     
      
  • 它正在运行一个多线程客户端,需要通过接口   公寓之间的指针(STA到STA   或MTA到STA)。

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

  •   
     

通过合并代理/存根代码   你的实施,你没有   分发两个DLL,只有一个。

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