У нас есть большое приложение с большим количеством кода графического интерфейса на VB6 и логикой, в основном, в библиотеках .NET Framework 4.7.2. Теперь мы хотим перейти на .NET 8 и испытываем трудности с доступом к библиотекам VB6 с помощью COM:
Обе проблемы, похоже, связаны с предоставлением классов с атрибутом [ClassInterface(ClassInterfaceType.AutoDual)].
Мы используем dscom32 (tlbexport/tlbregister) и знаем CA1408.
Как нам преодолеть наши проблемы (1 и 2), не переписывая много кода VB6 небезопасным с точки зрения типов способом и не создавая дополнительный уровень .NET Framework 4.7.2?
<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
Обе проблемы теперь решены для меня.
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
VB6 — это Visual Basic 2006 до того, как Net Library объединила Visual Basic и C# в одну библиотеку Net 3.0. Microsoft попыталась сделать Active X устаревшим. Поэтому любой объект Active X вам приходилось регистрировать вручную с помощью Regsrv32.
Microsoft пыталась устаревшие компоненты ActiveX, начиная с VB5 (~ 2005 г.). Microsoft заменяла Win95 на XP, а также готовилась к выпуску Net 3.0 (быстро замененной на Net 3.5 из-за большого количества ошибок). На работе мы попытались обновить Win95 до XP и обнаружили, что ActiveX не работает. У нас был элемент управления счетчиком ActiveX, а в Net не было эквивалентного объекта. Мы нашли решение — вручную установить библиотеки ActiveX с помощью Regsrv32.
@jdweng Я думаю, у тебя там какие-то даты перепутаны. VB5 был выпущен в 1997 году и в значительной степени поддерживал ActiveX. VB6 был выпущен в 1998 году. .NET 3.0 был выпущен в 2006 году, через много лет после XP, который был в 2001 году. ActiveX все еще работает в Windows 11, если вы действительно этого хотите. В 2006 году не было версии VB.NET, была версия 2005 года, которая также называлась VB8.
По моему опыту, нет ничего плохого в использовании AutoDual, если вы сами используете библиотеки. Если вы всегда перестраиваете клиент и сервер вместе, используйте AutoDual и облегчите себе жизнь. Я говорю из .NET framework.experience, я не пробовал dscom.
Является ли проблема использованием regsvr32? В .NET framework вы никогда не сможете этого сделать — вместо этого это сделали VS или msbuild. Если dscom создает файлы .TLB, вам следует ссылаться на файлы из VB6 (а не на библиотеки DLL). Однако я не уверен, что делает dscom для регистрации классов, указанных в TLB?
Кажется, dscom не поддерживает AutoDual - github.com/dspace-group/dscom?tab=readme-ov-file#limitations
И вместо regsvr32, похоже, вам следует использовать команду dscom tlbregister. github.com/dspace-group/dscom?tab=readme-ov-file#usage
Попробуйте аннотировать класс (не интерфейс!) с помощью [ClassInterface(ClassInterfaceType.None)]
. Вы не хотите, чтобы .NET также автоматически генерировал COM-интерфейс для класса — это будет мешать.
Для интерфейса попробуйте [InterfaceType(ComInterfaceType.InterfaceIsDual)]
или InterfaceIsIDispatch
. Я не помню, может ли VB6 использовать пользовательские (т. е. не производные от IDispatch
) интерфейсы, но автоматизация на основе IDispatch
всегда была родным режимом работы Visual Basic.
Затем перестройте, а затем восстановите ссылку из проекта VB. Я не думаю, что VB6 улавливает изменения библиотеки типов при сборке.
существует два типа объектов: 1) Раннее связывание 2) Позднее связывание. Объект раннего связывания разрешается во время компиляции. Объект позднего связывания разрешается во время выполнения. Решение выглядит так, будто оно использует позднее связывание. Использование Regsrv32 будет использовать раннее связывание и создание объектов Com библиотек, которые можно будет скомпилировать в проект.
Кстати, VB6 может использовать пользовательские производные интерфейсы IUnknown.
Вам определенно не понадобится .NET Framework, чтобы заставить VB6 взаимодействовать с .NET 8. Есть ли у вас где-нибудь полностью воспроизводимый компилируемый проект, чтобы мы не теряли время на детали?