尝试在C#中构建COM客户端和COM服务器的System.InvalidCastException

时间:2014-03-01 13:02:14

标签: c# .net visual-studio com

我正在尝试构建一个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场景。你觉得怎么样?

1 个答案:

答案 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引用来测试它。