如何在VB6和C#之间共享接口?

5
我想编写一个类接口,可以在C#和VB6类中实现,以便这些类可以在VB6代码中以相同的方式处理,但我无法做到这一点。
在VB6中,我希望使用Implements关键字来实现某些接口ISharedInterface,使其成为VB6Class类。
在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中创建一个包装器类,仅将接口方法重定向到底层真正的方法。
有没有一种在不必复制接口定义的情况下,使两种语言都可以引用的单个接口的最佳方法?

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

4
什么是创建单个接口的最佳方法(可以在VB6或C#中实现),而无需我复制接口定义?
将接口编写在IDL中并编译成类型库,然后在VB中引用并使用tlbimp导入到.NET中。(如果使用VB6来定义接口,则需要避免其重新生成IID。)
您也可以在.NET中定义接口,但这需要更多步骤。

谢谢指点。这是一个我之前不知道的方法,它确实打开了一个新的调研途径。看起来我需要做很多阅读和实验,因为我不熟悉IDL,但如果我能让它工作,那么它可能正是我在寻找的东西。我接受这个作为答案。 - DMFW
1
@DMFW 我已经很久没有做这个了(两份工作之前),所以我恐怕手头没有例子。COM仍然是Windows的核心部分,因此SDK(包括VS)中仍然有IDL编译器midlc.exe - Richard

0

我不确定是否误解了问题,但为什么要在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代码,并逐步迁移旧的代码。 - DMFW
这种方法看起来很天真,你可以从C#引用定义IBusObj接口的VB6 DLL,并创建继承自它的类。代码将编译通过。但我一直无法弄清楚如何(或是否可以)以这样的方式将C#类型库公开给COM,使得COM将C#类视为另一个IBusObj。如果我能做到这一点,我就不必编写包装类了。 - DMFW

0
创建一个C#类库(在本例中称为DemoComInterface),并确保“使程序集COM可见”未选中。(提醒一下,在以下代码片段中的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的“引用”浏览器中找到。你可以直接编辑文件或在程序集信息对话框中填写“描述”字段来完成此操作。

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")]

注册此类库,可以通过检查项目的“为COM互操作注册”构建属性或在命令提示符中手动注册它来完成。

查看生成的类型库(位于项目的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项目中实现共享接口,需要添加对“Demo C# exported interfaces”的引用,并按照以下方式实现:

Option Explicit

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接