简体   繁体   English

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

[英]How can I share an interface between VB6 and C#?

I would like to be able to code a class interface which I can implement in C# and VB6 classes so that these classes can be handled in the same way within VB6 code but I can't make this work.我希望能够编写一个可以在 C# 和 VB6 类中实现的类接口,以便可以在 VB6 代码中以相同的方式处理这些类,但我无法完成这项工作。

In VB6 I want to have come class VB6Class using the Implements key word to implement some interface ISharedInterface.在VB6中,我想使用Implements关键字来实现类VB6Class来实现一些接口ISharedInterface。

In C# I want to have some other class C#Class which I can expose to COM as also implementing ISharedInterface.在 C# 中,我想要一些其他类 C#Class,我可以将它们公开给 COM,同时实现 ISharedInterface。

The goal is that VB6 code will then be able to operate on VB6Class and C#Class via ISharedInterface without caring which language the classes were built in.目标是 VB6 代码将能够通过 ISharedInterface 对 VB6Class 和 C#Class 进行操作,而无需关心这些类是用哪种语言构建的。

I want to use this technique as a way to migrate away from VB6 by successively rewriting everything at the VB6 end and if I can implement interfaces I already have in VB6 within C# this would be ideal.我想使用这种技术作为一种从 VB6 迁移的方法,通过在 VB6 端连续重写所有内容,如果我可以在 C# 中实现我在 VB6 中已有的接口,这将是理想的。 But failing that, even if I have to rewrite an interface in C# to share back to VB6 that would still be useful (ie I don't even care whether the interface is written in C# and exposed to COM or written in COM and consumed by C#, so long as both sides of the language barrier can reference the same interface).但是失败了,即使我必须用 C# 重写一个接口以共享回 VB6,这仍然很有用(即,我什至不关心接口是用 C# 编写并暴露给 COM 还是用 COM 编写并由C#,只要语言不通的双方都能引用同一个接口即可)。

I'm finding this surprisingly hard.我发现这出奇地难。 I can reference an interface in C# that comes from COM but I can't export it back to COM as a COM visible interface.我可以在 C# 中引用来自 COM 的接口,但我无法将其作为 COM 可见接口导出回 COM。 If, as an alternative, I try to create an interface in C# itself I haven't found a way to see it directly over COM and various workarounds I have tried to use it indirectly, like creating a stub class to implement the interface and exposing that as COM visible just raise run time errors in VB6 when I try to implement the exposed stub class (even though they compile).作为替代方案,如果我尝试在 C# 本身中创建一个接口,我还没有找到直接通过 COM 查看它的方法,以及我尝试间接使用它的各种解决方法,例如创建一个存根类来实现接口并公开当我尝试实现公开的存根类(即使它们编译)时,COM 可见只是在 VB6 中引发运行时错误。

I currently have no good solution for this and only a horribly clunky work around which is to implement separate interfaces in C# and VB6, expose the C# methods directly to COM and create a wrapper class in VB6 that simply redirects the interface methods to the underlying true methods.我目前对此没有好的解决方案,只有一个非常笨拙的工作,即在 C# 和 VB6 中实现单独的接口,将 C# 方法直接公开给 COM 并在 VB6 中创建一个包装类,该类简单地将接口方法重定向到底层 true方法。

What is the best way to create a single interface (either in VB6 or C#) which both languages can reference without me having to duplicate the interface definition?创建两种语言都可以引用的单个接口(在 VB6 或 C# 中)的最佳方法是什么,而我不必复制接口定义?

What is the best way to create a single interface (either in VB6 or C#) which both languages can reference without me having to duplicate the interface definition?创建两种语言都可以引用的单个接口(在 VB6 或 C# 中)的最佳方法是什么,而我不必复制接口定义?

Write the interface in IDL and compile into a type lib which is referenced in VB and imported ( tlbimp ) into .NET.在 IDL 中编写接口并编译成一个类型库,该库在 VB 中引用并导入( tlbimp )到 .NET 中。 (You need to avoid the regeneration of IIDs that VB6 will do if you use it to define the interface.) (如果您使用它来定义接口,则需要避免 VB6 将执行的 IID 的重新生成。)

You could define the interface in .NET, but that would involve more steps.您可以在 .NET 中定义接口,但这将涉及更多步骤。

I'm not sure if I'm misunderstanding the issue but why would you want to implement the interface in VB6?我不确定我是否误解了这个问题,但是您为什么要在 VB6 中实现该接口? If you have an interface that's implemented in VB6 and C# then you're potentially duplicating code that's essentially doing the same thing.如果您有一个在 VB6 和 C# 中实现的接口,那么您可能会复制基本上做相同事情的代码。 If you're working on migrating away from VB6 you would want to limit the amount of VB6 code you write.如果您正致力于从 VB6 迁移,您可能希望限制您编写的 VB6 代码量。

I'm currently working with a large VB6 application and all new development is being done in C#.我目前正在使用一个大型 VB6 应用程序,所有新的开发都是在 C# 中完成的。 I have about a dozen C# assemblies and only one of them is COM exposed.我有大约一打 C# 程序集,其中只有一个公开了 COM。 It's just a small assembly that exposes the methods I need through COM so I can use then directly in my VB6 project.它只是一个小程序集,通过 COM 公开我需要的方法,因此我可以直接在我的 VB6 项目中使用。 You can create a wrapper class in VB6, like you said you've done, to centralize the calls to the C# assembly.您可以在 VB6 中创建一个包装类,就像您说的那样,以集中调用 C# 程序集。 This is what I did in our project so the wrapper can handle initializing the reference to the assembly when it's first used instead of doing it every time it's used.这就是我在我们的项目中所做的,因此包装器可以在第一次使用程序集时初始化对程序集的引用,而不是每次使用时都这样做。

So it sounds like your "clunky" workaround that your currently using is more along the lines of what I've done.因此,听起来您目前使用的“笨拙”解决方法更符合我所做的工作。 Maybe the main difference is that I don't expose any of my actual C# methods to COM.也许主要区别在于我没有将任何实际的 C# 方法公开给 COM。 It's all done in my COM interface assembly.这一切都在我的 COM 接口程序集中完成。 When it comes time to ditch the VB6 code, the COM interface assembly is just throw away and the rest of the C# code/assemblies have no ties to COM.当需要抛弃 VB6 代码时,COM 接口程序集就被扔掉了,其余的 C# 代码/程序集与 COM 没有关系。 We already have some other products sharing the same C# assemblies and they just reference them directly so once the COM interface is thrown out they won't be affected.我们已经有一些其他产品共享相同的 C# 程序集,它们只是直接引用它们,因此一旦 COM 接口被丢弃,它们就不会受到影响。

Create a C# class library (in this case it is called DemoComInterface) and be sure 'Make assembly COM-visible is unchecked.'创建一个 C# 类库(在本例中称为 DemoComInterface)并确保未选中“使程序集 COM 可见”。 (As a reminder, the GUIDs in the following code snippets should be replaced with unique GUIDs of your own.) (提醒一下,以下代码片段中的 GUID 应替换为您自己的唯一 GUID。)

Add an interface to the class library, like this:给类库添加一个接口,像这样:

using System.Runtime.InteropServices;

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

To demonstrate a C# class using the shared interface, update Class1 to implement the shared interface, and decorate it with the following attributes:要使用共享接口演示 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();
        }
    }
}

Now, modify your AssemblyInfo.cs file's AssemblyDescription attribute to something meaningful, so the type library can be found in the VB6 'References' browser.现在,将您的 AssemblyInfo.cs 文件的 AssemblyDescription 属性修改为有意义的内容,以便可以在 VB6 的“参考”浏览器中找到类型库。 This can be done by either editing the file directly or populating the Assembly Information dialog's 'Description' field.这可以通过直接编辑文件或填充装配信息对话框的“描述”字段来完成。

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 this class library, either through checking the project's 'Register for COM interop' build property, or registering it manually in a command prompt using REGASM.通过检查项目的“注册 COM 互操作”构建属性或使用 REGASM 在命令提示符中手动注册此类库,可以注册此类库。

View the generated type library (in the project's bin output folder) and you should see something like this:查看生成的类型库(在项目的 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();
    };
};

The shared interface is now implemented in a COM-visible C# class.共享接口现在在 COM 可见的 C# 类中实现。

To implement the shared interface in a VB6 project, add a reference to 'Demo C# exported interfaces', and implement it as in the following:要在 VB6 项目中实现共享接口,请添加对“Demo C# 导出接口”的引用,并按如下方式实现:

Option Explicit

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM