We have a large application with a lot of GUI code in VB6 and the logic mainly in .NET Framework 4.7.2 libraries. Now we want to move to .NET 8 and having difficulties exposing the libraries to VB6 using COM:
Both issues seem to be related to exposing the classes with(out) the [ClassInterface(ClassInterfaceType.AutoDual)] attribute.
We are using dscom32 (tlbexport/tlbregister) and are aware of CA1408.
How do we overcome our issues (1 and 2) without rewriting a lot of VB6 code in a type-unsafe way and without creating an extra .NET Framework 4.7.2 layer?
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<EnableComHosting>True</EnableComHosting>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<Target Name="ServerUsage" AfterTargets="Build">
<Message Importance="High" Text="Register:%0a regsvr32.exe "$(ProjectDir)$(OutputPath)$(ComHostFileName)"" />
<Message Importance="High" Text="Unregister:%0a regsvr32.exe /u "$(ProjectDir)$(OutputPath)$(ComHostFileName)"" />
</Target>
</Project>
using System.Runtime.InteropServices;
namespace TestCOM
{
[ComVisible(true)]
[Guid("A5447236-9520-4596-B4C6-D40175EBDA10")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITestCOM
{
string GetString();
}
[ComVisible(true)]
[Guid("3FCD4A46-F73D-495F-9DE3-85D50924ED06")]
//[ClassInterface(ClassInterfaceType.AutoDual)]
public class TestCOM : ITestCOM
{
string ITestCOM.GetString() => "Test";
}
}
Sub Main()
' This is not working:
' Dim oTest As TestCOM
' Set oTest = New TestCOM
'
' Requires ClassInterfaceType.AutoDual
'
' dscom32 tlbexport "%cd%\Test.dll"
' -> Failed to export type library. Dual class interfaces not supported!
' This is working:
Dim oTest As ITestCOM
Set oTest = CreateObject("TestCOM.TestCOM")
Debug.Print oTest.GetString()
End Sub
Both issues are now solved for me.
using System;
using System.Runtime.InteropServices;
namespace TestCOM
{
internal delegate void OnGetStringCOM(EventArgsCOM args);
[ComVisible(true)]
[Guid("E6BCA6A0-70F3-4218-B344-3632F638D51A")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IEventArgsCOM
{
string Message { get; set; }
}
[ComVisible(true)]
[Guid("77414A17-1CA5-4E45-940E-F26C8F50BE3E")]
[ClassInterface(ClassInterfaceType.None)]
public class EventArgsCOM(string message) : EventArgs, IEventArgsCOM
{
public string Message { get; set; } = message;
}
[ComVisible(true)]
[Guid("891E4AFB-8CD3-4611-80F6-F3AC755EF3B4")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITestCOMEvents
{
[DispId(1)]
void OnGetStringCOM(EventArgsCOM args);
}
[ComVisible(true)]
[Guid("A5447236-9520-4596-B4C6-D40175EBDA10")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ITestCOM
{
string GetString();
}
[ComVisible(true)]
[Guid("3FCD4A46-F73D-495F-9DE3-85D50924ED06")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ITestCOMEvents))]
public class TestCOM : ITestCOM
{
private event OnGetStringCOM OnGetStringCOM;
public string GetString()
{
OnGetStringCOM?.Invoke(new EventArgsCOM("test raised"));
return "test returned";
}
}
}
Private WithEvents moTest As Test.TestCOM
Private Sub Class_Initialize()
Set moTest = New TestCOM
End Sub
Public Function PrintString()
Debug.Print moTest.GetString()
' test returned
End Function
Private Sub moTest_OnGetStringCOM(ByVal args As Test.IEventArgsCOM)
Debug.Print args.Message
' test raised
End Sub
Try annotating the class (not the interface!) with [ClassInterface(ClassInterfaceType.None)]
. You don't want .NET to autogenerate a COM interface for the class also - it will get in the way.
For the interface, try [InterfaceType(ComInterfaceType.InterfaceIsDual)]
or InterfaceIsIDispatch
. I don't recall if VB6 can consume custom (i. e. not derived from IDispatch
) interfaces, but IDispatch
based automation was always Visual Basic's native mode of operation.
Then rebuild, then reestablish a reference from the VB project. I don't think VB6 picks up typelib changes on build.