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

时间:2016-03-31 13:50:07

标签: c# com vb6 vb6-migration

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

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

在C#中,我希望有一些其他类C#类,我可以向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#中)的最佳方法是什么,两种语言都可以引用,而不必复制接口定义?

3 个答案:

答案 0 :(得分:4)

  

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

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

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

答案 1 :(得分:0)

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

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

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

答案 2 :(得分: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互操作”构建属性,或使用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项目中实现共享接口,请添加对“ Demo C#导出接口”的引用,并按以下方式实现:

Option Explicit

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