【问题标题】:How can I share an interface between VB6 and C#?如何在 VB6 和 C# 之间共享接口?
【发布时间】:2020-08-06 01:29:58
【问题描述】:

我希望能够编写一个可以在 C# 和 VB6 类中实现的类接口,以便可以在 VB6 代码中以相同的方式处理这些类,但我无法做到这一点。

在 VB6 中,我希望使用 Implements 关键字来实现类 VB6Class 来实现一些接口 ISharedInterface。

在 C# 中,我想要一些其他类 C#Class,我可以将它们公开给 COM,同时实现 ISharedInterface。

目标是 VB6 代码将能够通过 ISharedInterface 对 VB6Class 和 C#Class 进行操作,而无需关心这些类是用哪种语言构建的。

我想使用这种技术作为一种从 VB6 迁移的方法,通过在 VB6 端连续重写所有内容,如果我可以在 C# 中实现我已经在 VB6 中拥有的接口,这将是理想的。但是如果失败了,即使我必须在 C# 中重写一个接口以共享回 VB6 仍然有用(即,我什至不在乎该接口是用 C# 编写并暴露给 COM 还是用 COM 编写并由C#,只要语言障碍的两边都可以引用同一个接口)。

我发现这非常困难。我可以在 C# 中引用来自 COM 的接口,但我不能将它作为 COM 可见接口导出回 COM。作为替代方案,如果我尝试在 C# 本身中创建一个接口,我还没有找到直接通过 COM 看到它的方法以及我尝试间接使用它的各种解决方法,比如创建一个存根类来实现接口并公开当我尝试实现暴露的存根类(即使它们编译)时,作为 COM 可见只会在 VB6 中引发运行时错误。

我目前对此没有好的解决方案,只有一个非常笨拙的解决方法是在 C# 和 VB6 中实现单独的接口,将 C# 方法直接暴露给 COM,并在 VB6 中创建一个包装类,将接口方法简单地重定向到底层的真实方法。

创建单个接口(VB6 或 C#)的最佳方法是什么,这两种语言都可以引用而无需我复制接口定义?

【问题讨论】:

  • 确实如此。迁移到 C# 是目标,我们已经有一个 C#“层”,它处理所有界面逻辑(我在这里指的是用户界面)。下一步是迁移业务逻辑。显然,一种选择是进行“大爆炸”,一次转换所有内容,可能使用转换工具进行处理,但是有很多 VB6 代码,我正在考虑逐步解决问题的方法。如果我可以与共享类接口进行互操作,那么就有大量代码可以进行独立迁移。

标签: c# com vb6 com-interop vb6-migration


【解决方案1】:

创建单个接口(VB6 或 C#)的最佳方法是什么,这两种语言都可以引用而无需我复制接口定义?

在 IDL 中编写接口并编译成在 VB 中引用并导入 (tlbimp) 到 .NET 的类型库。 (如果您使用 VB6 来定义接口,您需要避免重新生成 IID。)

您可以在 .NET 中定义接口,但这会涉及更多步骤。

【讨论】:

  • 感谢您的指点。这是一种我什至不知道的方法,它无疑开辟了一条新的调查途径。看起来我需要在这里做大量的阅读和实验,因为我不熟悉 IDL,但如果我能让它工作,那么它可能正是我正在寻找的。我接受这个作为答案。
  • @DMFW 自从我上次这样做(以及两份工作之前)以来已经有很长时间了,所以恐怕我不再有例子了。 COM 仍然是 WIndows 的核心部分,因此 VS 附带的 SDK 具有 midlc.exe IDL 编译器;。
【解决方案2】:

我不确定我是否误解了这个问题,但您为什么要在 VB6 中实现接口?如果您有一个在 VB6 和 C# 中实现的接口,那么您可能会复制本质上做同样事情的代码。如果您正在从 VB6 迁移,您可能希望限制您编写的 VB6 代码的数量。

我目前正在处理一个大型 VB6 应用程序,并且所有新的开发都是在 C# 中完成的。我有大约十几个 C# 程序集,其中只有一个是 COM 公开的。它只是一个小程序集,通过 COM 公开了我需要的方法,因此我可以直接在我的 VB6 项目中使用。您可以在 VB6 中创建一个包装类,就像您说的那样,集中对 C# 程序集的调用。这就是我在我们的项目中所做的,因此包装器可以在第一次使用时处理对程序集的引用,而不是每次使用时都这样做。

因此,您当前使用的“笨拙”解决方法听起来更像我所做的。也许主要区别在于我没有将任何实际的 C# 方法暴露给 COM。这一切都在我的 COM 接口程序集中完成。当需要放弃 VB6 代码时,COM 接口程序集就被丢弃了,其余的 C# 代码/程序集与 COM 没有任何联系。我们已经有一些其他产品共享相同的 C# 程序集,它们只是直接引用它们,因此一旦 COM 接口被抛出,它们就不会受到影响。

【讨论】:

  • 我不想在 VB6 中实现新的接口。更重要的是,我在 VB6 中已经存在一些关键接口,并且在那里被广泛引用,我希望在 C# 中重用它们。一种这样的接口称为 IBusObj。有很多应用程序类型的框架代码早期绑定到这个 IBusObj 接口。我希望我能够通过这个接口在 C# 中编写新代码,以便现有的 VB6 框架能够像看到 VB6 IBusObj 对象一样看到 C# 对象。然后我们可以停止为新的 IBusObj 类编写新的 VB6 代码,并逐步迁移旧的。
  • 天真的这种方法看起来可行。您可以从 C# 引用定义 IBusObj 接口的 VB6 DLL 并创建从它继承的类。代码将编译。但是我一直无法弄清楚如何(或者我是否可以)以 COM 将 C# 类视为另一个 IBusObj 的方式将 C# 类型库公开回 COM。如果我能做到这一点,我就不必编写包装类了。
【解决方案3】:

创建一个 C# 类库(在本例中称为 DemoComInterface)并确保未选中“使程序集 COM 可见”。 (提醒一下,以下代码 sn-ps 中的 GUID 应替换为您自己的唯一 GUID。)

向类库添加接口,如下所示:

using System.Runtime.InteropServices;

namespace DemoComInterface
{
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [ComVisible(true)]
    [Guid("01B0A84D-CACE-4EF1-9C4B-6995A71F9AB8")]
    public interface ISharedInterface
    {
        [DispId(0x60030040)]
        void Question();
    }
}

要演示使用共享接口的 C# 类,请更新 Class1 以实现共享接口,并使用以下属性对其进行装饰:

using System.Runtime.InteropServices;

namespace DemoComInterface
{
    [Guid("CC9A9CBC-054A-4C9C-B559-CE39A5EA2742")]
    [ProgId("DemoComInterface.Class1")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]
    public class Class1 : ISharedInterface
    {
        public void Question()
        {
            throw new NotImplementedException();
        }
    }
}

现在,将您的 AssemblyInfo.cs 文件的 AssemblyDescription 属性修改为有意义的内容,以便可以在 VB6 'References' 浏览器中找到类型库。这可以通过直接编辑文件或填充组装信息对话框的“描述”字段来完成。

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("DemoComInterface")]
// This is what will be seen in VB6's 'References' browser.**
[assembly: AssemblyDescription("Demo C# exported interfaces")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DemoComInterface")]
[assembly: AssemblyCopyright("Copyright ©  2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4294f846-dd61-418d-95cc-63400734c876")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

通过检查项目的“Register for COM interop”构建属性或使用 REGASM 在命令提示符中手动注册来注册此类库。

查看生成的类型库(在项目的 bin 输出文件夹中),您应该会看到如下内容:

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: DemoComInterface.tlb

[
uuid(4294F846-DD61-418D-95CC-63400734C876),
version(1.0),
helpstring("Demo C# exported interfaces"),
custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, "DemoComInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

]
library DemoComInterface
{
    // TLib :     // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
    importlib("mscorlib.tlb");
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface ISharedInterface;

    [
    uuid(CC9A9CBC-054A-4C9C-B559-CE39A5EA2742),
    version(1.0),
    custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "DemoComInterface.Class1")
    ]
    coclass Class1 {
        interface _Object;
        [default] interface ISharedInterface;
    };

    [
    odl,
    uuid(01B0A84D-CACE-4EF1-9C4B-6995A71F9AB8),
    version(1.0),
    dual,
    oleautomation,
    custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "DemoComInterface.ISharedInterface")    

    ]
    interface ISharedInterface : IDispatch {
        [id(0x60030040)]
        HRESULT Question();
    };
};

共享接口现在在 COM 可见的 C# 类中实现。

要在 VB6 项目中实现共享接口,请添加对“演示 C# 导出接口”的引用,并按如下方式实现:

Option Explicit

Implements ISharedInterface
    
' Implementation of Question.
Public Sub ISharedInterface_Question()
    MsgBox ("Who is number one?")
End Sub

【讨论】:

    猜你喜欢
    • 2019-07-23
    • 2021-04-15
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多