我正在尝试构建一个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();
}
}
}
istruction new ComRibbonApplicationMenuServerClass();
会抛出InvalidCastException
HResult=-2147467262
。
我该怎么办?
编辑:
感谢汉斯的回答。
如果我理解正确,正如我所怀疑的那样,不允许为没有黑客攻击的dotnet COM服务器创建一个dotnet COM客户端。
正如您所猜测的那样,我正在尝试做的是测试一个dotnet程序,我发现它作为一个独立的应用程序执行时可以正常工作,但是当被另一个应用程序执行时崩溃(仅在Windows XP中)通过COM。
为了重现这个问题,我需要构建一个简单的测试程序,它必须启动服务器并调用一个执行某些指令的方法,这些指令可能导致堆栈溢出异常。两个程序都使用GUI,这可能是问题的一部分。
为了简单起见,我首先尝试为COM客户端创建winform程序,但是,阅读你的anwser,我想我必须制作一个MFC应用程序来测试COM场景。你觉得怎么样?
答案 0 :(得分:2)
抛出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引用。这是一个相当好的建议,当正常的.NET方式已经以很多优越的方式工作时,使用COM是没有意义的。
您可以使用Tlbexp.exe生成的互操作库上的反编译器来欺骗机器,并将其复制/粘贴到客户端,从而确保您具有完全匹配。或者通过在客户端应用程序中使用 dynamic 关键字进行后期绑定,无论如何都需要,因为服务器使用ComInterfaceType.InterfaceIsIDispatch。同样不那么痛苦,因为您在更改服务器时不必重复执行复制/粘贴步骤。
但是你需要记住,当你这样做时你实际上并没有使用COM,CLR本身足够智能,可以看到.NET代码与.NET代码对话并且将跳过创建RCW和CCW。因此,如果您正在进行测试,请记住您不实际测试服务器的方式将在真实客户端应用程序中使用。您也可以通过实际添加.NET引用来测试它。