#.net #com #vb6 #com-interop
#.net #com #vb6 #com-взаимодействие
Вопрос:
Я пытаюсь понять, как ComInterfaceType.InterfaceIsIDispatch
это работает, и, вероятно, я что-то неправильно понимаю. У меня есть приложение VB6, некоторые современные элементы, которые я внедряю в .NET, и я экспортирую его в COM. Я объявляю интерфейсы и применяю InterfaceType
, но у меня есть сомнения, правильно ли я это делаю. Для меня это проблематично ComInterfaceType.InterfaceIsIDispatch
. В документации для ComInterfaceType Enum говорится:
InterfaceIsIDispatch — 2 — указывает, что интерфейс предоставляется COM как dispinterface, что позволяет выполнять только позднюю привязку.
Я привел пример, и когда я добавлю ссылку (tlb) в проект VB6, я смогу также использовать класс, который реализует InterfaceIsIDispatch
раннее связывание.
После некоторых тестов я добавил также событие, и оно работает для обоих классов.
Чего мне не хватает?
// .NET
[Guid("138A3402-98AF-403D-B24D-0AB08FD79082")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IControllerIDispatch
{
[DispId(1)]
void ShowMessage();
}
[Guid("0F0D68FA-C7C4-4112-A2FB-DC3BFED77058")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[ComVisible(true)]
public interface IControllerDual
{
[DispId(1)]
void ShowMessage();
}
[Guid("745B2476-E82C-4ACF-BC79-D0BFA77D9F2E")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface PingEvents
{
[DispId(1)]
void Ping();
}
[Guid("653D8882-CFE5-4316-8B2A-395865CCBA05")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(PingEvents))]
[ComVisible(true)]
public class ControllerIDispatch : IControllerIDispatch
{
public event Action Ping;
public void ShowMessage()
{
MessageBox.Show(nameof(ControllerIDispatch));
Volatile.Read(ref Ping)?.Invoke();
}
}
[Guid("57C081B6-2607-4543-834A-77C1C9ECAAE8")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(PingEvents))]
[ComVisible(true)]
public class ControllerDual : IControllerDual
{
public event Action Ping;
public void ShowMessage()
{
MessageBox.Show(nameof(ControllerDual));
Volatile.Read(ref Ping)?.Invoke();
}
}
// VB6 example which works - both classes created using early binding
Dim WithEvents controllerDispatch As ControllerIDispatch
Dim WithEvents controllerDual As controllerDual
Private Sub Form_Load()
Set controllerDispatch = New ControllerIDispatch
Set controllerDual = New ControllerDual
End Sub
Private Sub Command1_Click()
Call controllerDispatch.ShowMessage
End Sub
Private Sub Command2_Click()
Call controllerDual.ShowMessage
End Sub
Private Sub controllerDispatch_Ping()
MsgBox "Dispatch Pong"
End Sub
Private Sub controllerDual_Ping()
MsgBox "Dual Pong"
End Sub
Комментарии:
1. Вы используете раннее связывание не потому, что у вас есть intellisense в ShowMessage. Это просто означает, что есть tlb.
2. Редактор VB6 вводит вас в заблуждение, он знает, как использовать предоставленную вами библиотеку типов. Во время выполнения он ведет себя так, как если бы вы написали Dim controllerDispatch как новый объект. Таким образом, Command1_Click() не использует раннее связывание. Один из способов увидеть это — вызвать его миллион раз (не используйте MessageBox) и измерить, сколько времени это займет. Позднее связывание выполняется намного медленнее.
3. @HansPassant интересно. Сегодня я провел еще один тест, поэтому я добавил
ComSourceInterfaces
добавить события, и я могу объявить в VB6Dim WithEvents controllerDispatch As ControllerIDispatch
и написать обработчик для него — это не должно завершиться неудачей?4. @HansPassant Я внес обновление в код. Я расширяю пример с помощью события com, и он все еще работает, поэтому у меня есть сомнения, изменил ли VB6 раннее связывание на позднее связывание. С другой стороны, я проверил
.tlb
oleview.exe
и классы, которые реализуют интерфейсы, помечены какcoclass
, так что, похоже, я создаю здесь конкретные классы. Как это связано с атрибутами на интерфейсах? — Я пока не знаю.5. События в VB6 всегда связаны с опозданием, т.Е. Используют dispinterfaces. Вам нужны двойные интерфейсы для вызовов на основе vtbl (так называемое раннее связывание). Intellisense в среде IDE VB6 работает как с удаленными интерфейсами (только вызовы с поздней привязкой), так и с двумя интерфейсами (возможны как ранние, так и поздние вызовы), поэтому это ничего не доказывает.