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

C# 无法从其他STA线程调用从STAThread创建的COM对象,c#,multithreading,com,interop,marshalling,C#,Multithreading,Com,Interop,Marshalling,我是COM的新手,试图理解STA和MTA之间的区别。我试图创建一个示例,说明COM可以管理对在STA中创建的对象的调用,该对象不是线程安全的 MyCalcServer这里的类是使用ATL简单对象创建的。使用的设置与中的相同: 线程模型:单元 聚合:否 界面:自定义 MyCalcServerCOM对象用于另一个C#项目,即: class Program { [STAThread] static void Main(string[] args) { MyC

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

MyCalcServer
这里的类是使用ATL简单对象创建的。使用的设置与中的相同:

  • 线程模型:单元
  • 聚合:
  • 界面:自定义
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的代码中出现
无效的CastException
(E_NOINTERFACE)。我也尝试过将ApartmentState更改为MTA,但没有成功

无法强制转换类型为的COM对象 “mycmlib.MyCalcServerClass”到 接口类型 “mycmlib.IMyCalcServer”。这 操作失败,因为 COM上的QueryInterface调用 与IID接口的组件 “{B005DB8C-7B21-4898-9DEC-CBEBE175BB21}” 由于以下错误而失败:否 支持此类接口(例外情况) 来自HRESULT:0x80004002 (E_NOINTERFACE))


谁能解释一下我在这里做错了什么吗?

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


但在您的情况下,似乎需要为另一个线程创建代理。在常规COM客户端中,它由CoMarshalInterThreadInterfaceInStream完成。有一篇大文章澄清了这一点

我设法解决了这个问题

由于我是COM新手,我对代理/存根了解不多,而且它们是在STA和STA之间封送数据所必需的。创建一个新的ATL项目后,确保勾选了“合并代理/存根”。问题消失了

我发现此页面中的信息非常有用:

提供标准的代理/存根 组件的封送处理。在许多方面 在某些情况下,基于DLL的组件可能无法 需要代理/存根,因为它正在运行 在其客户的相同上下文中,以及 这个选项一开始可能看起来没用。 但是,COM使用封送处理 同步对数据库的访问的进程 多线程系统中的组件 情况。因此,一个基于DLL的组件 至少需要一个代理/存根DLL 两个案例:

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

  • DCOM可以为基于DLL的组件提供代理进程,因此 它可以在一个 分布式环境。在这种情况下 需要一个代理/存根来封送 在机器之间

通过将代理/存根代码与 你的实现,你不必 分发两个DLL,仅分发一个


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

也许JIT认为您没有使用“实例”,并提前发布了它。尝试在连接之后放置Marshal.ReleaseComObject(实例)。@adrianm仍然不工作,但感谢您尝试将第一行更改为mycmlib.IMyCalcServer instance=new mycmlib.MyCalcServer();我认为线程之间只能封送接口(而不是类)。@adiranm仍然得到相同的异常。幸运的是我解决了这个问题。无论如何谢谢:)+1的问题,我只是击中了同样的东西。我知道这是由于对一个公寓对象的跨线程调用,但我不明白为什么异常会抱怨抛出该对象。现在我知道了-它必须在强制转换期间查找编组支持,但没有找到,因此给出了一个一般性错误。可能更有帮助…Net包装器会自动在线程之间进行封送处理(否则终结器将无法工作)。如果自动封送处理不起作用,则错误将是E\u错误线程(8001010E)