[英]System.InvalidCastException trying to build a COM client and a COM server in C#
我正在尝试构建一个 COM 客户端,它必须在 C# 中实例化一个进程内 COM 服务器。 我做了两个 C# 项目:一个用于客户端,一个用于服务器。 两个项目都使用 x86 目标平台,后者设置了“注册 COM 互操作”选项。 我在 64 位 Windows 7 上使用 Visual Studio 2013 并使用 .net 4 进行编译。
这是服务器代码:
using System.Runtime.InteropServices;
namespace ComRibbonApplicationMenuServer
{
[ComVisible(true)]
[Guid("CCF43AAC-0822-4C36-90FD-2AFF7B94E71D")]
public interface IComRibbonApplicationMenuServer
{
[DispId(1)]
int OpenWindow();
}
[ComVisible(true)]
[Guid("38B1DE85-BC15-48E1-AFAF-4A7EA506256B")]
[ClassInterface(ClassInterfaceType.None)]
public class ComRibbonApplicationMenuServerClass : IComRibbonApplicationMenuServer
{
public ComRibbonApplicationMenuServerClass()
{
// Needed for COM
}
public int OpenWindow()
{
return 33;
}
}
}
这是客户端:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ComRibbonApplicationMenuClient
{
[Guid("CCF43AAC-0822-4C36-90FD-2AFF7B94E71D")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
interface IComRibbonApplicationMenuServer
{
void OpenWindow();
}
[ComImport, Guid("38B1DE85-BC15-48E1-AFAF-4A7EA506256B")]
class ComRibbonApplicationMenuServerClass
{
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ComRibbonApplicationMenuServerClass comServerClass = new ComRibbonApplicationMenuServerClass();
IComRibbonApplicationMenuServer comServer = (IComRibbonApplicationMenuServer)comServerClass;
comServer.OpenWindow();
}
}
}
结构new ComRibbonApplicationMenuServerClass();
抛出InvalidCastException HResult=-2147467262
。
我能做什么?
编辑:
感谢汉斯的回答。
如果我理解正确,正如我所怀疑的那样,不允许在没有一些黑客攻击的情况下为 dotnet COM 服务器创建 dotnet COM 客户端。
正如您已经猜到的那样,我正在尝试做的是测试一个 dotnet 程序,我发现该程序在作为独立应用程序执行时可以正常工作,但是当由另一个应用程序通过 COM 执行时会崩溃(仅在 Windows XP 中)。
为了重现这个问题,我需要构建一个简单的测试程序,它必须启动服务器并调用一个方法来执行一些可能导致堆栈溢出异常的指令。 这两个程序都使用 GUI,这可能是问题的一部分。
为了简单起见,我首先尝试为 COM 客户端制作 winform 程序,但是,阅读您的回答后,我想我必须制作一个 MFC 应用程序来测试 COM 场景。 你怎么认为?
抛出一个 InvalidCastException HResult=-2147467262。
你得到一个更详细的异常信息,它会告诉你,中投失败,原因是E_NOINTERFACE错误返回。 这通常是,相当难以诊断,除了在这种情况下,实在是没有界面。 您在客户端中使用的声明与服务器中使用的声明严重不匹配:
客户端中的 ComRibbonApplicationMenuServerClass 声明没有实现任何接口。 由于您随意省略了 [ClassInterface(ClassInterfaceType.None)],.NET 将自动生成一个。 它有一个永远不会匹配服务器的随机 [Guid],从而产生 E_NOINTERFACE 错误。
你任意给了客户端接口声明 [InterfaceType(ComInterfaceType.InterfaceIsDual)] 属性。 服务器忽略它,因此使用默认值 ComInterfaceType.InterfaceIsIDispatch。 这样的接口只能称为后期绑定,客户端必须使用IDispatch。 这意味着如果您在第一项中修复了问题,它仍然会失败,因为服务器实际上并未实现该接口。 在 C# 中,您必须使用dynamic关键字才能使用这样的服务器。
显然,客户端使用与服务器完全相同的声明是绝对必要的,通常通过在服务器上使用Tlbexp.exe
来生成类型库来确保。 你进入这个pickle的原因有点可想而知,IDE将拒绝让你添加对类型库的引用,它可以看到服务器是在.NET中实现的,并告诉你添加一个普通的.NET引用。 这是相当不错的建议,但它不是在所有意义的使用COM当正常.NET方式在一个优越得多的方式已经运作。
您可以通过在 Tlbexp.exe 生成的互操作库上使用反编译器来欺骗机器,并将它们复制/粘贴到客户端,从而确保您有完全匹配。 或者通过在客户端应用程序中使用带有动态关键字的后期绑定,无论如何都需要,因为服务器使用ComInterfaceType.InterfaceIsIDispatch 。 也不那么痛苦,因为您不必在更改服务器时重复执行复制/粘贴步骤。
但是您确实需要记住,您在执行此操作时实际上并未使用 COM,CLR 本身足够智能,可以看到 .NET 代码与 .NET 代码对话,并且将跳过创建 RCW 和 CCW。 因此,如果您这样做是为了测试,请记住,您实际上并没有按照在真实客户端应用程序中使用的方式测试服务器。 您不妨通过实际添加 .NET 引用来测试它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.