Previous | Index | Next 

[PRB] Using an array of UDTs causes a compilation error or doesn’t work correctly at runtime

VB Migration Partner must deal with arrays of UDTs in a special way, if the UDT requires to be initialized after creation. For example, consider the following VB6 code:

    Type MyUdt
        Number As Long
        Name As String * 30
    End Type

    Dim udt As MyUdt
    Dim arr() As MyUdt
    Dim arr2(10) As MyUdt
    Dim arr3(1 To 10) As MyUdt

    Sub Init()
        ReDim arr(5) As MyUdt
        arr3(1).Number = 1234
    End Sub

This is how the code is translated to VB.NET:

     Friend Structure MyUdt
        Public Number As Integer
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=30)> _
        Public Name As VB6FixedString
Public Sub New(ByVal dummyArg As Boolean) InitializeUDT() End Sub Public Sub InitializeUDT() Name = New VB6FixedString(30) End Sub End Structure Dim udt As New MyUdt(True) Dim arr() As MyUdt Dim arr2() As MyUdt = CreateArray6(Of MyUdt)(0, 10) Dim arr3() As MyUdt = CreateArray6(Of MyUdt)(1, 10) Sub Init() ReDim6(arr, 0, 5) arr3(1).Number = 1234 End Sub

Notice how the four variables are declared: 

  • The udt variable is instantiated through its nondefault constructor, which initializes it correctly. 
  • The arr variable is uninitialized and will be later initialized by means of a ReDim6 method, which detects that the MyUdt type requires initialization and behaves accordingly. 
  • The arr2 variable is initialized inline by calling the CreateArray6 method. 
  • The arr3 variable is also initialized inline by means of the CreateArray6 method; however, its lower index isn’t zero, therefore the CreateArray6 method throws an exception.

The problem with arrays of UDTs is that there is no way to implement such an array if its lower index is nonzero. You might think that you can solve this issue as you do with other array types, by means of a pragma that instructs VB Migration Partner to render the array as a VB6Array:

    '## arr3.ArrayBounds VB6Array
    Dim arr3(1 To 10) As MyUdt

Unfortunately, this approach can’t work: you can’t use the VB6Array type (or the VB6ArrayNew type) to render an array of structures, because VB.NET prevents you from assigning value types in the structure. As a matter of fact, this code:

    Dim arr3 As New VB6Array(Of MyUdt)(1, 10)
    arr3(1).Number = 1234

causes the following compilation error:

    Expression is a value and can’t be the target of an assignment.

To recap, an array of UDTs that require initialization and that has a nonzero lower index can’t be converted in a reliable manner because:

  1. by default, the array is initialized by a call to the CreateArray6 method,which cases a runtime error if the lower bound isn’t zero
  2. if you use an ArrayBounds pragma to force using the VB6Array type, then you get one compilation error for each statement that assigns value members in the structure.

The only viable solution is using an ArrayBounds ForceZero or ArrayBounds Shift pragma to force a zero lower index:

    '## arr3.ArrayBounds Shift
    Dim arr3(1 To 10) As MyUdt

which produces the following VB.NET code:

     Dim arr3() As MyUdt = CreateArray(Of MyUdt)(0, 9)

Remember to revise the code that accesses the array, to ensure that the change in lower and upper indices doesn’t cause any runtime error.


Previous | Index | Next