Today I had to face a very subtle migration issue. I was refining the Archive Explorer sample application to make it ready for publishing on this site. This application extracts one or more compressed files from a .ZIP archive and works magnificently under VB6. The converted VB.NET program worked equally well, except for a minor but disturbing detail: I was able to extract a given file from the compressed .zip file only if it was the first file in the list of files to be uncompressed. In all other cases I had an IndexOutOfRange runtime exception.

It was clearly a problem related to how an array was created or initialized, but I had no clue about where the VB.NET diverged from the original VB6 application. Archive Explorer counts only 4,000 lines, yet it is such a complex application and for sure I am not an expert of compression techniques. I was lost, because I didn’t know where to start from. In a sense, this is the same situation in which you find yourself when migrating a complex VB6 business application that you didn’t write yourself and for which no thorough documentation exists.

It took me hours to narrow down the problem to two of the most innocent statements:

    LitLen = TempLit
    Dist = TempDist

The four variables LitLen, TempLit, Dist, and TempDist are all of the same UDT type:

     Friend Structure CodesType
           Public Lenght() As Integer
           Public Code() As Integer
     End Structure

Can you see what happened? When you assign a structure to another structure, VB6 makes an exact copy of each element in the structure; in this specific case it creates a distinct copy of the Length and Code arrays. Also VB.NET makes a copy of each element in the structure, except that .NET arrays are reference types, therefore only the pointer to the array is copied! In other words, after the assignment, the Length array in LitLen variable points to the same Length array defined by TempLit. If the application modifies an element of the TempLit.Length array after the assignment, the corresponding element of the LitLen.Length array is also affected! It was precisely what happened and explained the observed behavior.

Once the problem was clear, it took me only half an hour to have VB Migration Partner take this detail into account. In the release version, if a structure contains one or more arrays or fixed-length strings, the structure implements a special Clone method that performs a “real” copy:

     Friend Structure CodesType
           Public Lenght() As Integer
           Public code() As Integer

           Public Function Clone() As CodesType
                Dim copy As CodesType = Me
                copy.Lenght = Me.Lenght.Clone()
                copy.code = Me.code.Clone()
               
Return copy
           End Function
     End Structure

 

When translating an assignment statement, VB Migration Partner emits code that leverages the new Clone method:

     LitLen = TempLit.Clone()
     Dist = TempDist.Clone()

Two questions for you: Did you ever meet this problem in converting your VB6 apps? How does your conversion tool – if you use one – handle this issue?