سؤال

I'm trying to build a COM client which has to instantiate a in-process COM server in C#. I made two C# projects: one for the client and one for the server. Both projects use x86 target platform and the latter has the option "Register for COM interop" set. I'm using Visual Studio 2013 on a 64bit Windows 7 and compiling with .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(); throws an 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.

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.

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.

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. What do you think?

هل كانت مفيدة؟

المحلول

throws an 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. 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. Since you arbitrarily omitted [ClassInterface(ClassInterfaceType.None)], .NET will auto-generate one. It has an random [Guid] that will never match the server's, thus generating the E_NOINTERFACE error.

  • you arbitrarily gave the client side interface declaration the [InterfaceType(ComInterfaceType.InterfaceIsDual)] attribute. The server omits it and thus uses the default which is ComInterfaceType.InterfaceIsIDispatch. Such an interface can only be called late-bound, the client has to use 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.

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. 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. 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.

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. Or by using late-binding with the dynamic keyword in the client app, required anyway because the server uses 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. 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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top