在C#中加载COM对象会引发异常“;无法强制转换类型为';系统。共对象';至接口类型…”;,但是C++;或者不是 我需要在非托管C++中建立COM服务器,在C语言中需要COM客户端。我在C++中找到了教程com Hello World。佩奇是捷克语的。COM服务器在从IHello接口调用函数Print()后显示带有文本“Hello world”的MessageBox。源代码在这里:。存档包含C++中的COM服务器和COM客户端的源代码,并且工作。

在C#中加载COM对象会引发异常“;无法强制转换类型为';系统。共对象';至接口类型…”;,但是C++;或者不是 我需要在非托管C++中建立COM服务器,在C语言中需要COM客户端。我在C++中找到了教程com Hello World。佩奇是捷克语的。COM服务器在从IHello接口调用函数Print()后显示带有文本“Hello world”的MessageBox。源代码在这里:。存档包含C++中的COM服务器和COM客户端的源代码,并且工作。,c#,c++,com,com-interop,C#,C++,Com,Com Interop,但是我的C#COM客户端不工作。它是一个C#控制台应用程序,参考“Interop.Hello.dll”。我使用以下命令创建互操作dll: tlbimp Hello.tlb /out:Interop.Hello.dll C#代码: 但C#客户端抛出异常: Unable to cast COM object of type 'System.__ComObject' to interface type 'Interop.Hello.CHello'. This operation failed bec

但是我的C#COM客户端不工作。它是一个C#控制台应用程序,参考“Interop.Hello.dll”。我使用以下命令创建互操作dll:

tlbimp Hello.tlb /out:Interop.Hello.dll
C#代码:

但C#客户端抛出异常:

Unable to cast COM object of type 'System.__ComObject' to interface type
'Interop.Hello.CHello'. This operation failed because the QueryInterface call on the
COM component for the interface with IID '{B58DF060-EAD9-11D7-BB81-000475BB5B75}' 
failed due to the following error: No such interface supported (Exception from 
HRESULT: 0x80004002 (E_NOINTERFACE)).
我也尝试从VisualBasic加载COM服务器。它是有效的。我在VB中参考“Interop.Hello.dll”创建了控制台应用程序

VB代码:

Module Module1
    Sub Main()

        Dim ic As Interop.Hello.CHello

        ic = CreateObject("MyCorporation.Hello")
        ic.Print()

    End Sub
End Module
当从C#客户端加载COM服务器时,我调试了它。当变量“riid”为IHello接口guid时,方法QueryInterface()返回S_OK

你知道为什么C代码不起作用吗?

正确的C代码:

不支持此类接口

错误消息不明确。每个人都会认为是他们的接口不受支持,IHello在你的例子中。但事实并非如此,错误信息也没有足够清楚地说明这一点。它是不受支持的

COM负责.NET所没有的编程细节,它不会忽略线程。线程是出了名的难以获得正确的,有很多代码不是线程安全的。NET允许您在工作线程中使用此类代码,并且不会反对出错,通常会产生一个很难诊断的错误。COM设计者最初认为线程太难了,不可能正确,应该由聪明人来处理。并且内置在基础设施中,使用在辅助线程中不是线程安全的代码来实现线程安全。它工作得很好,解决了95%的典型线程问题。然而,最后的5%往往会让你头疼。像这个

COM组件(如您的组件)可以发布从注册表中的线程使用是否安全。注册表值名称为“ThreadingModel”。一个非常常见的值,也是缺少时的默认值,是“公寓”。解释公寓有点超出了这个答案的范围,它实际上意味着“我不是线程安全的”。COM基础设施确保对对象的任何调用都是从创建该对象的同一线程发出的,从而确保线程安全

然而,这需要一些魔力。将调用从一个线程封送到另一个特定线程是一件非常重要的事情。NET使用Dispatcher.BeginInvoke和Control.BeginInvoke这样的方法使它看起来很简单,但这隐藏了一个相当大的代码冰山,它的99%都在水下。COM很难做到这一点,它缺少了一个.NET特性,使它更容易实现,它不直接支持反射

首先,需要在目标线程上构建一个堆栈框架,以便进行调用。这需要知道方法的参数是什么样的。COM在这方面需要帮助,它不知道它们是什么样子,因为它不能依赖反射。所需的是两段代码,称为代理和存根。代理确实知道参数的外观,并将方法的参数序列化为RPC数据包。该代码由COM自动调用,使用一个与原始接口完全相同的虚拟接口,但每个方法都进行代理调用。在目标线程上,存根代码接收RPC数据包,构建堆栈帧并进行调用

这在.NET术语中听起来可能很熟悉,这正是.NET远程处理和WCF的工作方式。除了.NET可以通过反射自动创建代理和存根之外。在COM中,它们需要由您创建。有两种基本的方法,一般的方法是用一种叫做IDL的语言描述COM接口,并用midl.exe工具编译。它可以根据IDL中的接口描述自动生成代理和存根代码。或者有一种简单的方法,当您的COM服务器将自身限制为自动化子集并且可以生成类型库时,就可以使用这种方法。在这种情况下,您可以使用内置于Windows中的代理/存根实现,它使用类型库来确定参数的外观。这真的很像反射。另外,还需要在注册表中注册HKCR\Interfaces键,以便COM能够找到所需的代码

因此,异常消息的真正含义是COM无法找到封送调用的方法。它在注册表中查找,但找不到代理/存根的注册表项。然后,它通过查询IMarshal询问COM对象“您知道如何封送自己吗?”。答案是否定的!这就结束了,给你留下了一条异常信息,很难解释。错误报告是COM的致命弱点


接下来,我需要关注为什么COM决定将调用封送到您的COM服务器,这是您不希望发生的事情。对调用COM对象的线程的一个基本要求是,它需要告诉COM它为封送调用提供了何种支持。这是除了构建堆栈框架之外很难做的第二件事,调用需要在一个非常特定的线程上进行,这个线程创建了COM对象。实现线程的代码需要实现这一点,这不是一件小事。它需要解决软件工程中的一般问题。其中,“生产者”是发出调用的线程,“消费者”是创建对象的线程

因此,线程必须告诉COM的是“我为生产者/消费者p实现了一个解决方案
Module Module1
    Sub Main()

        Dim ic As Interop.Hello.CHello

        ic = CreateObject("MyCorporation.Hello")
        ic.Print()

    End Sub
End Module
 [STAThread]
 static void Main(string[] args)
 {
     Interop.Hello.IHello Hello = new Interop.Hello.CHello();
     Hello.Print();
 }