如何通过COM从VB6将未初始化的数组传递到.NET?

4
我希望用VB.NET编写一个新组件来替换使用VB6编写的组件。新组件需要与其他VB6应用程序配合使用,直到这些应用程序也被替换为止。 我在使用一个接受字符串数组作为参数的函数时遇到了麻烦。
我正在使用一个包含此函数的VB6类来重现这个问题:
Public Function MyMethod(arr() As String, arr2() As String, Result$) As Integer     
    Result = Join(arr, ", ")
    MyMethod = 0        
End Function

我可以从测试程序(VB6)成功调用它,如下所示,它会显示“Hello, World”:

    Dim obj As Object
    Dim arr() As String
    Dim arr2() As String
    Dim result As String

    Set obj = CreateObject("MyHelloWorld.MyClass")
    'Set obj = CreateObject("HelloWorldCOMNet.MyNetClass")

    arr = Split("Hello World", " ")
    'ReDim arr2(0)
    result = ""

    If Not obj.MyMethod(arr, arr2, result) Then
        MsgBox result
    End If

我无法修改实际的VB6应用程序,但我想要用.NET编写的类来替换VB6 ActiveX组件。新类的外观如下:

<ComClass("cd74ab4a-76ca-4c84-9f49-147e6f6ac01f", "b3314f71-cb8d-48ea-bfe6-9d1995aa4f58", "40c30052-0cc8-4ef6-b1e8-92e4ddbcb515")> _
Public Class MyNetClass

    Public Sub New()
        MyBase.New()
    End Sub

    Public Function MyMethod(ByRef arr() As String, ByRef arr2() As String, ByRef result As String) As Short
        result = String.Join(", ", arr) + " from .NET"
        Return 0
    End Function

End Class

我用测试应用程序替换了Set obj = CreateObject("MyHelloWorld.MyClass")Set obj = CreateObject("HelloWorldCOMNet.MyNetClass")来测试它。
我在.NET类的构造函数中使用断点来验证已执行该函数。
当测试应用程序调用MyMethod时,会发生FatalExecutionEngineException
我将错误缩小到第二个参数arr2。当我使用ReDim arr2(0)初始化变量时,.NET代码可以运行。所以我的猜测是这个问题与该变量未初始化有关。不幸的是,我无法修改实际的VB6应用程序。
我使用OleWoo比较了方法签名,它们似乎是相同的(除了id)。
如何修改.NET函数的签名以接受未初始化的字符串数组?
编辑:OleWoo的输出结果:
[id(0x00000001)]
short MyMethod(
   [in, out] SAFEARRAY(BSTR)* arr,
   [in, out] SAFEARRAY(BSTR)* arr2,
   [in, out] BSTR* result
);

1
基于 https://dev59.com/EFsW5IYBdhLWcg3wQVb2 和 https://stackoverflow.com/q/39351476/11683,我会尝试将其声明为 Array 并/或应用 marshaling 属性。 - GSerg
我希望有一个类似于这个的解决方案。不幸的是,我无法让它工作。使用 ByRef arr2 As Array 会出现“无效的过程调用或参数 (错误5)”的错误,而 <MarshalAs(UnmanagedType.SafeArray, SafearraySubType:=VarEnum.VT_BSTR)> ByRef arr2 As Array 会导致与之前相同的异常。我本来期望最后一个可以工作。另外,ByRef 或 ByVal 没有区别。我已经更新了问题,并附上了 OleWoo 的输出结果。 - BenZinra
当您正在寻找解决方案时,您可以使用预期签名在VB6中编写一个dll,该dll接收数组,根据需要进行重新定义大小,并调用.NET中的实际实现。但是我很惊讶marshaller无法将空数组进行封送 - 至少作为“Nothing”。 - GSerg
我在VBA(最接近VB6的语言)中重现了您的情况,并得到了相同的错误。如果我添加对.Net程序集的引用并将obj声明为MyNetClass,那么代码就会正常工作。因此,问题似乎出现在后期绑定的封送处理中。 - TnTinMn
1个回答

1
我认为这是.NET Framework中的一个错误。
我将调试器附加到VB6应用程序上。这是崩溃时的堆栈跟踪: 堆栈跟踪,DispatchInfo :: IsVariantByrefStaticArray在顶部 这是该位置的反汇编代码: 反汇编代码显示在偏移量2处取消引用指针 那就是函数DispatchInfo :: IsVariantByrefStaticArray。它取消引用(*V_ARRAYREF(pOle))的结果而不检查它是否为NULL。成员fFeatures位于偏移量2处,在地址0x00000002处导致访问冲突。

我认为这就是我遇到的问题。我想我必须找到另一种解决原始问题的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接