我是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))。
有人可以解释我在这里做错了吗?
答案 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的答案标记为接受,因为他已经阐明了代理主题。