【问题标题】:How to expose an enum defined in a COM library via interop as the return type of a c# function如何通过互操作公开 COM 库中定义的枚举作为 c# 函数的返回类型
【发布时间】:2012-07-25 10:23:58
【问题描述】:

希望问题很清楚,但为了清楚起见,请补充一下:

我有一个 VB6 dll,它定义了我在 C# dll 中引用的枚举。 C# dll 使用 idispatch 接口以正确的方式定义了 CCW,该接口声明了一个返回类型为枚举的函数。

运行 regasm 时,我收到一个警告,指出枚举不是 COM 可见的,因此不会导出该函数。由于它是在我的 VB6 库中定义的,我会认为它已经是 COM 可见的,因为它是在 COM dll 中定义的。

我意识到我可以停止搞砸并使用 int 来传递枚举并只在任一端进行强制转换,但这确实令人沮丧,我想知道是否存在方法。

这里有一些示例代码:

VB6 dll 定义了一个枚举

Public Enum myEnum
    first = 0
    second = 1
End Enum

这通过互操作导入到 C# 中,如果您查看元数据,它看起来像这样

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

然后定义CCW接口

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

Regasm 抱怨 myEnum 不可见。我必须同意它,因为元数据视图没有 com visible 属性。奇怪的是,如果我使用 VB dll 中定义的其他类型作为函数的参数,我没有得到任何抱怨,它似乎是枚举,我猜这是因为我实际上暴露了 VB6 枚举的互操作实现而不是实际枚举。

所以我想我理解这个问题,我想知道是否有一种方法可以使用不涉及破解任何中间代码或自动生成的代码的枚举来实现这一点。

【问题讨论】:

  • VB6 dll是COM dll吗?请向我们展示一些代码以了解问题。

标签: c# enums com-interop


【解决方案1】:

看起来解决方案是将 C# 项目中导入的 COM 程序集的“Embed Interop Types”属性设置为 False。

为了测试这一点,我创建了一个 VB COM dll 为 StackOverflow.ExampleCom,其中包含以下代码

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

然后我创建了一个 C# 类项目并导入了这个 StackOverflow COM 库。然后,C# 中的以下代码创建了一个 COM 对象,该对象重新公开了 VB 代码中定义的枚举类型,从而创建了 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; } }
    }
}

如果我们构建它,然后尝试使用regasm /tlb:EnumDemo.tlb bin\Debug\EnumDemo.dll 从这个程序集创建一个类型库,那么我会收到关于使用非 COM 可见值类型的警告。但是,一旦对 VB COM dll 的引用将“嵌入互操作类型”设置为 false,警告就会消失,并且使用 OleView 检查生成的类型库表明正在使用该类型,并且已添加 importlib 以引用原始 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);
    };
    ...

【讨论】:

  • 出色的工作。我真的不想回到运送互操作文件的麻烦,因为这会产生各种其他问题,当我们开始嵌入互操作类型时这些问题就消失了。我想因此在返回枚举并在客户端处理它之前,我可能不得不求助于将枚举转换为适当的类型。
【解决方案2】:

你是对的。 Problem 是当您将枚举导入代码时,它允许您在代码中使用这些枚举 - 在程序集中。您将无法直接使用它们。您需要在 .NET 中定义枚举并使它们成为 ComVisible。它们将被公开为 EnumType_myEnum 而不仅仅是 myEnum(请参阅this

【讨论】:

  • 这是可以使用 C++/MIDL 完成的。您使用 importlib 将类型从一个库引入您自己的类型库。然后,您可以声明从其他库返回枚举类型的方法,一切正常。但是,我看不到任何看起来有助于声明一个类型来自另一个类型库的 InteropServices 属性类。一个很好的谜题。
猜你喜欢
  • 2015-06-29
  • 1970-01-01
  • 2012-07-19
  • 2013-06-22
  • 1970-01-01
  • 1970-01-01
  • 2018-09-04
  • 2010-12-14
  • 1970-01-01
相关资源
最近更新 更多