Search code examples
c#enumscom-interop

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.


Solution

  • 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);
        };
        ...