简体   繁体   English

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

[英]System.InvalidCastException trying to build a COM client and a COM server in C#

I'm trying to build a COM client which has to instantiate a in-process COM server in C#.我正在尝试构建一个 COM 客户端,它必须在 C# 中实例化一个进程内 COM 服务器。 I made two C# projects: one for the client and one for the server.我做了两个 C# 项目:一个用于客户端,一个用于服务器。 Both projects use x86 target platform and the latter has the option "Register for COM interop" set.两个项目都使用 x86 目标平台,后者设置了“注册 COM 互操作”选项。 I'm using Visual Studio 2013 on a 64bit Windows 7 and compiling with .net 4.我在 64 位 Windows 7 上使用 Visual Studio 2013 并使用 .net 4 进行编译。

This is the server code:这是服务器代码:

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;
        }
    }
}

And this is the client:这是客户端:

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();
        }
    }
}

The istruction new ComRibbonApplicationMenuServerClass();结构new ComRibbonApplicationMenuServerClass(); throws an InvalidCastException HResult=-2147467262 .抛出InvalidCastException HResult=-2147467262

What can I do?我能做什么?

Edit:编辑:

Thanks Hans for your answer.感谢汉斯的回答。

If I understand correctly, as I suspected, it is not allowed to create a dotnet COM client for a dotnet COM server without some hacking.如果我理解正确,正如我所怀疑的那样,不允许在没有一些黑客攻击的情况下为 dotnet COM 服务器创建 dotnet COM 客户端。

As you have guessed, what I'm trying to do is to test a dotnet program which I've found to work properly when executed as a standalone application, but that crashes (only in Windows XP) when executed by another application via COM.正如您已经猜到的那样,我正在尝试做的是测试一个 dotnet 程序,我发现该程序在作为独立应用程序执行时可以正常工作,但是当由另一个应用程序通过 COM 执行时会崩溃(仅在 Windows XP 中)。

In order to reproduce the problem I need to build a simple test program that has to start the server and call a method which executes some instructions which, probably, are causing a stack overflow exception.为了重现这个问题,我需要构建一个简单的测试程序,它必须启动服务器并调用一个方法来执行一些可能导致堆栈溢出异常的指令。 Both programs are using a GUI which maybe part of the problem.这两个程序都使用 GUI,这可能是问题的一部分。

To keep things simple I tried first to make winform program for the COM client, but, reading your anwser, I'm thinking that I have to make a MFC application for testing the COM scenario.为了简单起见,我首先尝试为 COM 客户端制作 winform 程序,但是,阅读您的回答后,我想我必须制作一个 MFC 应用程序来测试 COM 场景。 What do you think?你怎么认为?

throws an InvalidCastException HResult=-2147467262.抛出一个 InvalidCastException HResult=-2147467262。

You are getting a much more detailed exception message, it will tell you that the cast failed due to the E_NOINTERFACE error return.你得到一个详细异常信息,它会告诉你,中投失败,原因是E_NOINTERFACE错误返回。 Which is normally pretty hard to diagnose, except in this case, there really is no interface.这通常是,相当难以诊断,除了在这种情况下,实在没有界面。 The declarations you used in the client are a gross mismatch with the ones used in the server:您在客户端中使用的声明与服务器中使用的声明严重不匹配:

  • the ComRibbonApplicationMenuServerClass declaration in the client doesn't implement any interface.客户端中的 ComRibbonApplicationMenuServerClass 声明没有实现任何接口。 Since you arbitrarily omitted [ClassInterface(ClassInterfaceType.None)], .NET will auto-generate one.由于您随意省略了 [ClassInterface(ClassInterfaceType.None)],.NET 将自动生成一个。 It has an random [Guid] that will never match the server's, thus generating the E_NOINTERFACE error.它有一个永远不会匹配服务器的随机 [Guid],从而产生 E_NOINTERFACE 错误。

  • you arbitrarily gave the client side interface declaration the [InterfaceType(ComInterfaceType.InterfaceIsDual)] attribute.你任意给了客户端接口声明 [InterfaceType(ComInterfaceType.InterfaceIsDual)] 属性。 The server omits it and thus uses the default which is ComInterfaceType.InterfaceIsIDispatch.服务器忽略它,因此使用默认值 ComInterfaceType.InterfaceIsIDispatch。 Such an interface can only be called late-bound, the client has to use IDispatch.这样的接口只能称为后期绑定,客户端必须使用IDispatch。 Which means that if you fix the problem in the first bullet, it will still fail because the server doesn't actually implement the interface.这意味着如果您在第一项中修复了问题,它仍然会失败,因为服务器实际上并未实现该接口。 In C#, you'd have to use the dynamic keyword to use such a server.在 C# 中,您必须使用dynamic关键字才能使用这样的服务器。

Clearly it is absolutely essential that the client uses the exact same declarations as the server, normally ensured by using Tlbexp.exe on the server to generate a type library.显然,客户端使用与服务器完全相同的声明是绝对必要的,通常通过在服务器上使用Tlbexp.exe来生成类型库来确保。 It is somewhat guessable why you got into this pickle, the IDE will refuse to let you add a reference to the type library, it can see that the server was implemented in .NET and tell you to add a normal .NET reference instead.你进入这个pickle的原因有点可想而知,IDE将拒绝让你添加对类型库的引用,它可以看到服务器是在.NET中实现的,并告诉你添加一个普通的.NET引用。 Which is rather good advice, it doesn't make sense to use COM at all when the normal .NET way already works in a much superior way.这是相当不错的建议,但它不是在所有意义的使用COM当正常.NET方式在一个优越得多的方式已经运作。

You can fool the machine by using a decompiler on the interop library that Tlbexp.exe generates and copy/paste them into the client, thus making sure you have an exact match.您可以通过在 Tlbexp.exe 生成的互操作库上使用反编译器来欺骗机器,并将它们复制/粘贴到客户端,从而确保您有完全匹配。 Or by using late-binding with the dynamic keyword in the client app, required anyway because the server uses ComInterfaceType.InterfaceIsIDispatch .或者通过在客户端应用程序中使用带有动态关键字的后期绑定,无论如何都需要,因为服务器使用ComInterfaceType.InterfaceIsIDispatch Also much less painful since you don't have to repeatedly do the copy/paste step when you alter the server.也不那么痛苦,因为您不必在更改服务器时重复执行复制/粘贴步骤。

But you do need to keep in mind that you are not actually using COM when you do this, the CLR itself is smart enough to see that there's .NET code talking to .NET code and will skip creating the RCW and CCW.但是您确实需要记住,您在执行此操作时实际上并未使用 COM,CLR 本身足够智能,可以看到 .NET 代码与 .NET 代码对话,并且将跳过创建 RCW 和 CCW。 So if you are doing this for testing, do keep in mind that you are not actually testing the server the way it is going to be used in a real client app.因此,如果您这样做是为了测试,请记住,您实际上并没有按照在真实客户端应用程序中使用的方式测试服务器。 You might as well test it by actually adding a .NET reference.您不妨通过实际添加 .NET 引用来测试它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM