Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to expose an enum defined in a COM library via interop as the return type of a c# function

Hopefully the question is clear but to pad it out for clarity:

I have a VB6 dll that defines an enum that I am referencing in my C# dll. The C# dll defines a CCW in the correct way with an idispatch interface that declares a function who's return type is the enum.

When running regasm I get a warning that the enum is not COM visible and so the function is not exported. Since it's defined in my VB6 library I would have thought it is already COM visible since it's defined in a COM dll.

I realise I can stop messing about and use an int to pass the enum and just do casting either end but it's proper frustrating and I would like to know if a way exists.

As requested here is some sample code:

VB6 dll defines an enum

Public Enum myEnum
    first = 0
    second = 1
End Enum

This gets imported through interop into the C# and if you view the metadata it looks something like this

[Guid("EnumGUID")]
public enum myEnum
{
    first = 0,
    second = 1        
}

Then the CCW interface is defined

[ComVisible(true)]
[Guid("InterfaceGuid")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyCCWInterface
{
    [DispId(1)]
    myEnum myFunction();
}

Regasm complains that myEnum is not com visible. I have to agree with it since the metadata view doesn't have the com visible attribute. Strangely if I use other types defines in the VB dll for arguments of the function I get no complaints, it just appears to be the enum and I'm guessing it's because I'm actually exposing the interopped implementation of the VB6 enum and not the actual enum.

So I think I understand the issue, what I would like to know is if there's a way to get this working using enums that doesn't involve hacking any intermediate or auto generated code.

like image 298
Akuma Avatar asked Jul 25 '12 10:07

Akuma


2 Answers

It looks like the solution is to set the "Embed Interop Types" property to False for the imported COM assembly in the C# project.

To test this I created a VB COM dll as StackOverflow.ExampleCom with the following code in it

Public Enum EThing
    eThingOne = 1
    eThingTwo = 2
End Enum
Private mThing As EThing
Private Sub Class_Initialize()
    mThing = eThingOne
End Sub
Public Property Let Thing(newVal As EThing)
    mThing = newVal
End Property
Public Property Get Thing() As EThing
    Thing = mThing
End Property

Then I created a C# class project and imported this StackOverflow COM library. The following code in C# then creates a COM object that re-exposes the enumerated type defined in the VB code creating the same situation described by the OP.

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using StackOverflow;

namespace EnumDemo
{
    [ComVisible(true)]
    [Guid("c30d35fe-2c7f-448b-98be-bd9be567ce70")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IEnumDemo
    {
        [DispId(1)]
        EThing Thing
        {
            get;set;
        }
    }

    [ComVisible(true)]
    [Guid("af328c82-08e3-403e-a248-8c46e27b48f3")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("StackOverflow.EnumDemo")]
    public class EnumDemo
    {
        private EThing mThing = EThing.eThingOne;
        public EThing Thing { get { return mThing; } set { mThing = value; } }
    }
}

If we build this then try to create a typelib from this assembly using regasm /tlb:EnumDemo.tlb bin\Debug\EnumDemo.dll then I get a warning about the use of non-COM visible value types. However once the reference to the VB COM dll has the "Embed Interop Types" set false, the warning disappears and checking the generated typelib with OleView shows that the type is being used and importlib has been added to reference the original dll.

library EnumDemo
{
    // TLib :     // TLib :  : {D482D5CB-EE6C-455A-A28A-D26A5AC579D5}
    importlib("StackOverflow.dll");
    // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
    ...
    interface IEnumDemo : IDispatch {
        [id(0x00000001), propget]
        HRESULT Thing([out, retval] EThing* pRetVal);
        [id(0x00000001), propput]
        HRESULT Thing([in] EThing pRetVal);
    };
    ...
like image 174
patthoyts Avatar answered Nov 15 '22 00:11

patthoyts


You're right. Problem is when you import the enums into your code, that allows you to use those enums within your code - inside your assembly. You wouldn't be able to use them directly. You need to define enumerations in .NET and made them ComVisible.They'll get exposed as EnumType_myEnum instead of just myEnum (see this)

like image 27
Jacob Seleznev Avatar answered Nov 15 '22 01:11

Jacob Seleznev