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

发布于 2024-08-19 16:04:07 字数 1552 浏览 3 评论 0原文

我是 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();

    }
}

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

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

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

I am new to COM and trying to understand the difference between STA and MTA. I tried to create an example that would show that COM can manage calls to object created in STA that is not thread-safe.

MyCalcServer class here is created using ATL Simple Object. The settings used are the same as in this article:

  • Threading Model: Apartment
  • Aggregation: No
  • Interface: Custom

MyCalcServer COM object is used in another C# project which is:

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();

    }
}

However, this always results in InvalidCastException (E_NOINTERFACE) raised inside t1's code. I have also tried changing ApartmentState to MTA with no success.

Unable to cast COM object of type
'MyCOMLib.MyCalcServerClass' to
interface type
'MyCOMLib.IMyCalcServer'. This
operation failed because the
QueryInterface call on the COM
component for the interface with IID
'{B005DB8C-7B21-4898-9DEC-CBEBE175BB21}'
failed due to the following error: No
such interface supported (Exception
from HRESULT: 0x80004002
(E_NOINTERFACE)).

Could anybody please explain what I am doing wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

怪我入戏太深 2024-08-26 16:04:07

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

但就您而言,您似乎需要为另一个线程创建代理。在常规 COM 客户端中,它是由 CoMarshalInterThreadInterfaceInStream 完成的。有一篇大文章来澄清它 http://www.codeproject.com/KB/COM /cominterop.aspx

You explicitly ask COM to create instance for main thread, then you pass this to another thread. Of course in some circumstance it is allowed (for example declare MyCalcServer as multithread).

But in your case it looks you need create proxy for another thread. In regular COM clients it is done by CoMarshalInterThreadInterfaceInStream. There is large article to clarify it http://www.codeproject.com/KB/COM/cominterop.aspx

怀里藏娇 2024-08-26 16:04:07

我设法得到了这个决心。

由于我是 COM 新手,所以我对 Proxy/Stub 不太了解,也不知道它们是在 STA 和 STA 之间编组内容所必需的。创建新的 ATL 项目并确保选中“合并代理/存根”后。问题就消失了。

我发现此页面中的信息很有用: 为什么我要合并代理/存根使用我的 DLL 项目编写代码。

提供标准的代理/存根
为您的组件编组。在许多
基于 DLL 的组件可能不会的情况
需要代理/存根,因为它正在运行
在其客户的相同上下文中,并且
这个选项乍一看似乎没什么用。
然而,COM 使用编组
同步访问的过程
多线程中的组件
情况。因此,基于 DLL 的组件
至少需要一个代理/存根 DLL
两种情况:

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

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

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

我会将 @Dewfy 的答案标记为接受,因为他对代理主题有了一些了解。

I managed to get this resolve.

As I'm new to COM, I don't know much about Proxy/Stub and that they're needed for marshaling stuffs between STA and STA. After created a new ATL project and make sure I have "Merge Proxy/Stub" ticked. The problem vanished.

I find the info from this page useful: Why would I want to merge Proxy/Stub code with my DLL project.

Proxy/stubs providing standard
marshaling for your component. In many
cases a DLL-based component may not
need proxy/stub because it is running
in the same context of its client, and
this option may seem useless at first.
However, COM uses the marshaling
process to synchronize access to a
component in multi-threaded
situations. So, a DLL-based component
will need a proxy/stub DLL in at least
two cases:

  • It's running a multi-threaded client and needs to pass interface
    pointer between apartments (STA to STA
    or MTA to STA).

  • DCOM can provide a surrogate process for a DLL-based component so
    that it can be accessed in a
    distributed environment. In this case
    a proxy/stub is needed to marshal
    between machines.

By merging the proxy/stub code with
your implementation, you don't have to
distribute two DLLs, just the one.

I will mark @Dewfy's answer as accept as he has shed some light on the Proxy topic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文