ComInterfaceType .InterfaceIsIDispatch, раннее связывание и VB6

#.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 добавить события, и я могу объявить в VB6 Dim WithEvents controllerDispatch As ControllerIDispatch и написать обработчик для него — это не должно завершиться неудачей?

4. @HansPassant Я внес обновление в код. Я расширяю пример с помощью события com, и он все еще работает, поэтому у меня есть сомнения, изменил ли VB6 раннее связывание на позднее связывание. С другой стороны, я проверил .tlb oleview.exe и классы, которые реализуют интерфейсы, помечены как coclass , так что, похоже, я создаю здесь конкретные классы. Как это связано с атрибутами на интерфейсах? — Я пока не знаю.

5. События в VB6 всегда связаны с опозданием, т.Е. Используют dispinterfaces. Вам нужны двойные интерфейсы для вызовов на основе vtbl (так называемое раннее связывание). Intellisense в среде IDE VB6 работает как с удаленными интерфейсами (только вызовы с поздней привязкой), так и с двумя интерфейсами (возможны как ранние, так и поздние вызовы), поэтому это ничего не доказывает.