Аргументы взаимодействия VB6 .NET, переданные как строки - PullRequest
1 голос
/ 04 ноября 2011

У меня есть .NET интерфейс

<System.Runtime.InteropServices.GuidAttribute("0896D946-8A8B-4E7D-9D0D-BB29A52B5D08"), _
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface IEventHandler
    Sub OnEvent(ByRef sender As Object, ByRef e As Object)
End Interface

в экспортированной библиотеке типов.

Код VB6 ссылается на этот tlb и реализует этот интерфейс.

Код VB6 создает экземпляр своей реализации и передает его в .NET.

.NET вызывает OnEvent.

VB6 прекрасно подхватывает событие ... но значения аргумента отправителя и e являются строками, а не объектами, когда он туда попадает ... Значения строк - это полные имена типов ...

Код VB6:

Implements Interop.IEventHandler

Private Sub IEventHandler_OnEvent(ByRef sender As Variant, ByRef e As Variant)
    Dim id
    id = e.Person.Id

    ' The weird thing here:
    ' e = "XYZ.Tasks.PersonTaskEventArgs"
    ' sender = "XYZ.Tasks.PersonUIManager"
    ' The values of the arguments are the NAMEs of the actual object values' types...
End Sub

Код, который запускает событие, довольно тривиален. У меня есть класс COM со словарем, который регистрирует обработчики и запускает события.

<ComClass(ComRegistrar.ClassId, ComRegistrar.InterfaceId, ComRegistrar.EventsId>
Public Class ComRegistrar

   Private Shared ReadOnly _eventHandlers As New Dictionary(Of String, List(Of IEventHandler))


   ' This is called by .NET code to fire events to VB6
   Public Shared Sub FireEvent(ByVal eventName As String, ByVal sender As Object, ByVal e As Object)
        For Each eventHandler In _eventHandlers(eventName)
                eventHandler.OnEvent(sender, e)
        Next
   End Sub

   Public Sub RegisterHandler(ByVal eventName As String, ByVal handler As IEventHandler)
        Dim handlers as List(Of IEventHandler)
        If Not _eventHandlers.TryGetValue(eventName, handlers)
             handlers = New List(Of IEventHandler)
             _eventHandlers(eventName) = handlers
        End If
        handlers.Add(handler)
   End Sub

End Class

.NET-код выглядит как

Public Class PersonEventArgs
     Inherits System.EventArgs


' Some properties
End Class

Public Class MyControl
     Inherits UserControl

' Stuff
End Class


ComRegistrar.FireEvent("PersonSelected", Me, New PersonEventArgs With { Some stuff })

Если я подключу тот же код с помощью класса .NET, который реализует IEventHandler, аргументы передаются без проблем.

ОБНОВЛЕНИЕ: если я изменю свои параметры ByRef для OnEvent на ByVal, это не имеет значения. Я уверен, что два типа, которые я пытаюсь передать, относятся к сборке, помеченной как ComVisible.

Что здесь не так?

1 Ответ

1 голос
/ 09 ноября 2011

ОК, так как мы подтвердили, что параметры поступают как объекты (COM), а не как строки, последняя загадка, которую нужно решить, - это почему вы получаете ошибку 13, несоответствие типов.

Я быисследовать эту проблему с двух сторон: во-первых, используйте TypeName (), чтобы получить объект, который VB6 считает своим (например, TypeName (e.Person)).Затем подумайте, действительно ли у VB6 есть средства для доступа к членам этого объекта - для начала лучше всего обратиться к Обозревателю объектов в IDE VB6.Если класс объекта обнаруживается в этом, но не предоставляет никаких членов (даже с включенным Show Hidden Members), то этот класс, вероятно, не отображается должным образом как COM-объект.

==== Предыдущий ====

Для хихиканья, когда вы остановитесь или остановитесь в Sub IEventHandler_OnEvent (), зайдите в окно Immediate VB6 IDE и посмотрите, допустимы ли они:

?sender.ToString
?e.ToString

Кроме того, отказаться от использования ByRef и перейти с ByVal;если вы действительно не пытаетесь изменить значение ссылки в зависимости от состояния экземпляра, вам не нужен ByRef.

Я подозреваю, что вы действительно получаете объекты;видимая строка, которую вы видите, является результатом члена ToString (), который каким-то образом сделан как свойство COM по умолчанию.

Один из способов проверить вышеупомянутое подозрение - попробовать их в окне Immediate VE6 IDE (когда выполнение остановлено в IEventHandler_OnEvent ()):

?typename(sender)
?typename(e)

Typename должно показывать String, еслилюбой из них действительно является строкой.

==== Предыдущий ====

Джефф, пожалуйста, дайте мне знать, если эти проекты VB6 и .NET являются верным примером вашей проблемы.

В .NET я создал проект DLL, имеющий один интерфейс и один класс, оба с экспозицией COM:

==== .NET, файл IComFun.vb

Imports System.Runtime.InteropServices

<GuidAttribute("b3f1ab4f-dc99-4990-ade1-8a4833d8bcab"), _
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface IComFun
    Sub OnFoo(ByVal sender As Object, ByVal e As Object)
End Interface

==== .NET, файл ComFun.vb

<ComClass(ComFun.ClassId, ComFun.InterfaceId, ComFun.EventsId)> _
Public Class ComFun

#Region "COM GUIDs"
    ' These  GUIDs provide the COM identity for this class 
    ' and its COM interfaces. If you change them, existing 
    ' clients will no longer be able to access the class.
    Public Const ClassId As String = "257a4ed2-1f27-4d8e-bcf2-f90828ae649b"
    Public Const InterfaceId As String = "4431a515-3c01-4fff-b281-20ec8ad0c0b6"
    Public Const EventsId As String = "9b44258f-142e-4dd0-bc22-6d24d1f58657"
#End Region

    ' A creatable COM class must have a Public Sub New() 
    ' with no parameters, otherwise, the class will not be 
    ' registered in the COM registry and cannot be created 
    ' via CreateObject.
    Public Sub New()
        MyBase.New()
    End Sub


    Public Sub Ping(ByVal sender As Object, ByVal e As Object)
        DirectCast(sender, IComFun).OnFoo(sender, e)
    End Sub

End Class

В VB6 я создал стандартный проект EXE со ссылками на COM-библиотеку на основе .NET через файл TLB, имеющий одинформа (содержащая одну кнопку) и один класс, например:

==== VB6, файл Form1.frm

Dim x As Class1
Dim y As Class1
Dim z As ComFun.ComFun

Private Sub cmdPing_Click()

  If x Is Nothing Then
    Set x = New Class1
  End If

  If y Is Nothing Then
    Set y = New Class1
  End If

  If z Is Nothing Then
    Set z = New ComFun.ComFun
  End If

  z.Ping x, y

End Sub

==== VB6, файл Class1.cls

Implements ComFun.IComFun

Private Sub IComFun_OnFoo(ByVal sender As Variant, ByVal e As Variant)

  Stop

End Sub

Я сделал вышеупомянутые проекты и скомпилировал их, а затем запустил VB6 EXE (в IDE) и нажал кнопку.Когда я добрался до оператора Stop в IComFun_OnFoo, я получал реальные объекты, а не строки - другими словами, он работал так, как вам нужно, чтобы он работал.Я также посмотрел на стек вызовов (в VB6 IDE), чтобы убедиться, что вызывается неосновной код.

Итак, я действительно не решил вашу проблему, но надеюсь лучше понять ситуацию.

...