问题
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.
In VB6 I want to have come class VB6Class using the Implements key word to implement some interface ISharedInterface.
In C# I want to have some other class C#Class which I can expose to COM as also implementing 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.
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. But failing that, even if I have to rewrite an interface in C# to share back to VB6 that would still be useful (i.e. 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).
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. 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).
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.
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?
回答1:
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?
Write the interface in IDL and compile into a type lib which is referenced in VB and imported (tlbimp
) into .NET. (You need to avoid the regeneration of IIDs that VB6 will do if you use it to define the interface.)
You could define the interface in .NET, but that would involve more steps.
回答2:
I'm not sure if I'm misunderstanding the issue but why would you want to implement the interface in 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. If you're working on migrating away from VB6 you would want to limit the amount of VB6 code you write.
I'm currently working with a large VB6 application and all new development is being done in C#. I have about a dozen C# assemblies and only one of them is COM exposed. It's just a small assembly that exposes the methods I need through COM so I can use then directly in my VB6 project. You can create a wrapper class in VB6, like you said you've done, to centralize the calls to the C# assembly. 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. It's all done in my COM interface assembly. 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. 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.
回答3:
Create a C# class library (in this case it is called DemoComInterface) and be sure 'Make assembly COM-visible is unchecked.' (As a reminder, the GUIDs in the following code snippets should be replaced with unique GUIDs of your own.)
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:
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. 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.
View the generated type library (in the project's bin output folder) and you should see something like this:
// 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.
To implement the shared interface in a VB6 project, add a reference to 'Demo C# exported interfaces', and implement it as in the following:
Option Explicit
Implements ISharedInterface
' Implementation of Question.
Public Sub ISharedInterface_Question()
MsgBox ("Who is number one?")
End Sub
来源:https://stackoverflow.com/questions/36335382/how-can-i-share-an-interface-between-vb6-and-c