FRANCESCO BALENA ON VB MIGRATION

 

Exploring version 1.10 - If merging

clock November 19, 2008 18:51

If merging is VB Migration Partner's ability to merge two or more nested If...End If blocks using the AndAlso operator. This feature is enabled by means of the MergeIfs  pragma. To see how this feature works, consider the following VB6 code:

'## MergeIfs True
Sub Test(ByVal x As Integer, y As Integer, z As Integer)
    If x > 0 Then
        If y = 0 Then
            ' do something here
            ' …

            If z <> 0 Then
                If x < 100 Then
                    ' do something here
                    ' …

                End If
            End If
        End If

        If z = 0 Then If x = 10 Then Beep
    End If
End Sub

This is the VB.NET code that VB Migration Partner produces:

Public Sub TestX(ByVal x As Short, ByRef y As Short, ByRef z As Short)
    ' UPGRADE_INFO (#0581): Two nested If...End If blocks have been merged.
    If (x > 0) AndAlso (y = 0) Then
        ' do something here
        ' ...

       
        ' UPGRADE_INFO (#0581): Two nested If...End If blocks have been
        ' merged.

        If (z <> 0) AndAlso (x < 100) Then
            ' do something here
            ' ...

        End If

        ' UPGRADE_INFO (#0581): Two nested If...End If blocks have been
        ' merged.
         If (z = 0) AndAlso (x = 10) Then  Beep()
    End If
End Sub


The effect of this pragma is to simplify the structure of the code, reduce nesting level, and indirectly make it more readable. As you see, the MergeIfs pragma also affects nested single-line Ifs. A warning is automatically added, so that you can quickly revise all points where this optimization was applied.

By default, test expressions are enclosed between parenthesis, so that you easily see how each If statement contributed to the compound expression. If you don’t like these extra parenthesis, just specify False as the pragmas’s second argument:

    '## MergeIfs True, False

The MergeIfs pragma can have project-, file-, and method-level scope, therefore you can precisely define where this optimization should be applied. In most cases you can safely specify project-level If merging by including the following line in the VBMigrationPartner.pragmas file:

    '## project:MergeIfs True

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Speed up your VB.NET collections

clock November 7, 2008 22:24

The VB6 Collection is quite a versatile object: it allows you to add and remove elements by their index like in an array, but without requiring to initialize it to a fixed number of elements. It also allows you to associate a string key to an element, so that you can quickly retrieve (or delete) that element without having to scan the collection one element at a time. (An important detail: string keys are compared in case-insensitive mode.)

Both the Upgrade Wizard and VB Migration Partner convert VB6 Collection objects into the Microsoft.VisualBasic.Collection objects, a choice that ensure the highest degree of compatibility and functional equivalence. A few articles around the Internet suggest that you should try to use a "pure" .NET collection, if possible. 

Unfortunately, this is easier said that done, because no other .NET collection exactly mimicks the behavior of the VB6 Collection. If you never use string keys you can translate a VB6 Collection using the ArrayList or the List(Of T) objects, except these objects have their lower index equal to 0 instead of 1, a detail that would force you to carefully scrutinize and fix your code as necessary. On the other hand, a VB6 collection that is only used with string key might be converted to VB.NET using the HashTable or the Dictionary(Of T), but be careful because these .NET collections deal with string key in case-sensitive mode and therefore aren't perfectly equivalent to the original VB6 Collection object. (TIP: you can use the CollectionUtils.CreateCaseInsensitiveHashtable method to create a case-insensitive dictionary.)

The main reason for replacing the VB.NET Collection with a native .NET collection is performance. The following table recaps the timing (in milliseconds) to perform a given operation 10,000 times. As you see, while the VB.NET collection is marginally faster than the VB6 collection in most cases, it is also much slower than the ArrayList collection at retrieving and deleting elements. (It is also slower than the Hashtable object, but the difference in absolute terms is virtually negligible.)

Test  VB6   VB.NET   ArrayList   Hashtable   VB6Collection  
Add(item) 0 1 0 n/a 2
Add(item,key) 47 7 n/a 4 6
Item(ndx) 203 215 0 n/a 0
Item(key) 16 7 n/a 0 9
For Each 15 7 0 0 1
Remove(ndx) 609 539 46 n/a 247
Remove(ndx) (*)    140 131 46 n/a 70
Remove(key) 16 12 n/a 3 3820
  (*) if items aren't associated with keys

 

The table suggests that, if you are sure that you never use keys, then you should replace a collection with an ArrayList object. Likewise, if you are sure that the collection is always going to contain elements of a specific type - for example, strings - you can improve performance and code robustness by replacing the collection with a List(Of T) object. However, if your code uses all the features of the VB6 Collection - namely, numeric indexes and string keys, you apparently have no choice other than using the standard VB.NET collection, right?

Well, not if you use VB Migration Partner. Starting with forthcoming version 1.11, you can replace a VB.NET collection with the new VB6Collection object, provided in our support library. As shown in previous table, this custom object is remarkably faster than the VB.NET collection in all tests (except when passing a key to the Remove method), and is as fast as the ArrayList object at retrieving elements by their numeric index.

When version 1.11 is released, you can replace one or more instances of the VB.NET Collection with our custom VB6Collection type by means of a ReplaceStatement pragma:

    '## ReplaceStatement Dim col As New VB6Collection
    Dim col As New Collection

Even better: we provide also a generic, strong-typed version of this collection, named VB6Collection(Of T). If you are sure that the collection is going to contain only element of a certain kind - integers, for example - you can write better code by using the generic version of this collection:

    '## ReplaceStatement Dim col As New VB6Collection(Of Integer)
    Dim col As New Collection

The VB6Collection class is 100% compatible with the original VB6 collection and the VB.NET collection, therefore you don't need to edit the VB.NET source code in any other way.

Currently rated 4.8 by 4 people

  • Currently 4.75/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Exploring version 1.10 - Structured Exception Handling

clock November 5, 2008 20:09

Starting with version 1.10, VB Migration Partner Enterprise Edition can replace old-styled On Error Goto statements into structured exception handling (SEH) based on the Try-Catch block. You can activate this feature by means of the UseTryCatch pragma, which can have project-, file-, and method-level scope. For example, the following VB6 code:

    '## UseTryCatch

    Sub Test()
        On Error Goto ErrorHandler
        ' method body
    ErrorHandler:
        ' error handler
    End Sub

is translated to the following VB.NET code:

    Sub Test()
        Try
            ' IGNORED: On Error Goto ErrorHandler
           ' method body

        Catch _ex As Exception
            ' IGNORED: ErrorHandler:
            ' error handler

        End Try
    End Sub

In case you are wondering why you need a pragma to tell VB Migration Partner to perform this transformation, please consider that this refactoring feature can't be performed in a number of occasions. More precisely, VB Migration Partner can insert a Try-Catch block only if ALL the following conditions are met:

 

  1. The method contains only a single On Error GoTo <label> method.
  2. The On Error GoTo <label> statement doesn’t appear inside a conditional block such as If, For, Select Case, and isn’t preceded by a GoTo statement.
  3. The method doesn’t contain GoSub, Resume, or Resume Next statements. (Resume <label> statements are acceptable, though)
  4. There is no Goto statement in the method body that points to a label that is defined in the error handler. (Notice that it is ok if a GoTo in the error handler points to a label defined in the method body.)
  5. If the method contains one or more On Error Goto 0 statements, such statements must immediately precede a statement that causes exiting from the current method, e.g. Exit Sub or End Function.
  6. If the current method is a Property Let procedure, there must not be a Property Set procedure for the same property. Likewise, If the current method is a Property Set procedure, there must not be a Property Let procedure for the same property.

For example, the following VB6 code:

    '## UseTryCatch
    Sub Test()
        On Error Goto ErrorHandler
        ' method body
        If x > 0 Then GoTo ErrorHandler2

    ErrorHandler:
        ' error handler
    ErrorHandler2:
        ' do something here
    End Sub

can't be converted to VB.NET using a Try-Catch block because a GoTo statement in the method body points to a label that is defined in the error handler.

By default, VB Migration Partner inserts a Try-Catch also if the method contains the On Error Resume Next statement plus another executable statement. For example, the following VB6 code:

    Function Reciprocal(ByVal x As Double) As Double
        '## UseTryCatch True
        On Error Resume Next
        Reciprocal = 1 / x
        ' returns zero if an error occurs (e.g. X is zero)
    End Function


contains only two executable statements (including On Error Resume Next) and is converted to:

    Function Reciprocal(ByVal x As Double) As Double
        Try
            Return 1 / x
            ' returns zero if an error occurs (e.g. X is zero)
        Catch
            ' Do nothing if an error occurs
        End Try
    End Function


Methods with three or more executable statements can be converted using Try Catch if you specify a value higher than 2 in the second argument for the UseTryCatch pragma. For example, all methods with up to 5 executable statements under the scope of the following pragma:
        '## UseTryCatch True, 5
are converted using a Try-Catch block.

Notice that the UseTryCatch pragma can be used together with the AutoDispose Force pragma, in which case VB Migration Partner generates a complete Try-Catch-Finally block. Combining the AutoDispose and UseTryCatch pragma can lead to complex Try-Catch-Finally blocks that can handle your errors and correctly dispose of your variables at the same time. For example, consider this VB6 method:

'## AutoDispose Yes
'## UseTryCatch

Sub Test()
     On Error Goto ErrHandler
     Dim cn As New ADODB.Connection
     Dim rs As New ADODB.Recordset
     '  opens the connection and the recordset
     cn.Open MyConnString
     rs.Open "SELECT * FROM Employees", cn
     ' …
     Exit Sub
ErrHandler:
     MsgBox "An error has occurred"
End Sub

This is how VB Migration Partner converts this code to VB.NET:

    Public Sub Test()
        Dim cn As New ADODB.Connection
        Dim rs As New ADODB.Recordset
        Try
            ' IGNORED: On Error GoTo ErrHandler
           '  opens the connection and the recordset
           cn.Open(myconnstring)
           rs.Open("SELECT * FROM Employees", cn)
           ' …
           Exit Sub
        
        Catch _ex As Exception
           ' IGNORED: ErrHandler:
           MsgBox6("An error has occurred")
        Finally
           ' Clean up all disposable variables
           SetNothing6(cn)
           SetNothing6(rs)
        End Try
    End Sub

The SetNothing6 method is defined in VB Migration Partner support library. In addition to setting the variable to Nothing, this helper method invokes its Dispose method and - just as important - if the variable points to a COM object (as in this case), it removes the object from memory. (Many developers believe that you just need a call to Marshal.ReleaseComObject method to release a COM object, but it isn't always true because of a little-known bug: the SetNothing6 method takes all the necessary steps to ensure that the COM object is actually released).

Interestingly, VB Migration Partner has automatically recognized that the Recordset and Connection objects are disposable objects and require a call to SetNothing6.

In case you aren't familiar with .NET and the so-called undeterministic finalization issue, here is the short story: if you instantiate a disposable object - that is, an object that takes system resources such as a file, a database connection, a Windows API handle, etc - you MUST invoke its Dispose method before setting it to Nothing. If you fail to do so, you might introduce a noticeable overhead in your application (in the best case) or even a resource leakage that might crash your code in the long run (in the worst case).

As far as we know, VB Migration Partner is the only VB6 conversion tool that can automatically handle disposable objects in such a safe way.

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Code Architects appointed Microsoft NXT world-wide partner

clock November 4, 2008 19:25

 

Microsoft has recently included Code Architects in the NXT Partner Program

The NXT Program includes a very restricted number of companies that specialized in migrating legacy applications, possibly based on non-MS platforms, towards more recent Microsoft technologies, such as the .NET Framework, ASP.NET, Windows Presentation Fundation, SharePoint, and BizTalk. If you are converting or modernizing a legacy software application, working with an NXT Partner ensures that you are working with the most skilled experts in Microsoft technologies and products.

Qualyfing for the NXT Program isn't easy. As of this writing there are only about 40 partners in the program, 11 of which are located in Europe. We applied for the program many months ago, before we officially launched VB Migration Partner (which explains why our flagship product isn't mentioned in the company description). In addition to being the only partner in Italy, Code Architects is the only NXT Partner who specializes in migrating VB6 applications to VB.NET.

Currently rated 3.4 by 5 people

  • Currently 3.4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Exploring version 1.10 - Gosubs Refactoring

clock October 13, 2008 03:02

If you read virtually all books and articles related to VB6 migration, you'll learn that VB.NET doesn't support the Gosub keyword, therefore you had to manually refactor your VB6 apps and turn those Gosubs into calls to separate method.

All these articles and books were clearly written before VB Migration Partner made its debut, because we have demonstrated that the inability to convert Gosub keywords to VB.NET was only a limitation of the conversion engine used by the Upgrade Wizard that comes with Visual Studion .NET.

In fact, since its early beta versions VB Migration Partner has always been able to handle Gosub keywords and its On...Gosub variant. Translating a Gosub keyword isn't at all difficult if you think that a Gosub/Return pair can be simulated by a Goto plus another Goto that brings execution back to the statement following the Gosub. It's a simple technique and I show how to implement it during my Tips, Tracks, and Techniques in Migrating VB6 Applications to .NET 75-minute speech. (BTW, if you are attending VSLive! in Las Vegas, don't miss this session... it's tomorrow at 11 am.) Actually, it's such a simple technique that I always wondered why Upgrade Wizard didn't implement it.

Rendering Gosub/Return by means of Goto pairs ensures full functional equivalence between the original VB6 code and the generated VB.NET code, but has one defect: it makes your code less readable than it should. For these reason, a few users asked for a better way of dealing with this VB6 syntax.

The obvious solution was to extract the code portion pointed to by the Gosub keyword into a separate method and replace the Gosub with a call to that external method. The code in the external method must be able to access some (or all) variables in the original code, therefore it is necessary to pass these variables as ByRef parameters to the external method. Finally, if the original method included an On Error Resume Next statement, this statement has to be present in the external method as well.

Implmenting all these features wasn't a breeze, but we managed to do it and we are very proud of the result. As far as we know, VB Migration Partner is the first and only VB conversion tool that has this feature

Enabling Gosub refactoring is as easy as adding a ConvertGosubs pragma. Like most pragmas, it can have project-, file-, and method-level scope, therefore you can decide where exactly it has to be applied. In most cases, the best approach is adding it to the master VBMigrationPartner.pragmas file, so that all your migration projects will use it. 

    '## project:ConvertGosubs True

(You can disable this feature inside a specific file or method by simply inserting another pragma with narrower scope and False in its argument.) Consider the following VB6 code:

Function GetValue(x As Integer, id As String) As Integer
    '## ConvertGosubs True
    On Error Resume Next
   
    Dim s As String, name As String
    s = "ABC"
   name = "Code Architects"
    GoSub BuildResult
    Exit Function

BuildResult:
    GetValue = x + Len(s) + Len(name)
    Return
End Function

VB Migration Partner detects that the code that begins at the BuildResult label (a.k.a. the target label) can be safely refactored to a separate method that receives four arguments. The result of the conversion is therefore:

    Public Function GetValue(ByRef x As Short, ByRef id As String) As Short
        Dim s As String
        Dim name As String
        On Error Resume Next

        s = "ABC"
        name = "Code Architects"
        Gosub_GetValue_BuildResult(x, GetValue, s, name)
        Exit Function
    End Function

    Private Sub Gosub_GetValue_BuildResult( _
            ByRef x As Short, ByRef GetValue As Short, _
            ByRef s As String, ByRef name As String)
        On Error Resume Next
        GetValue = x + Len6(s) + Len6(name)
    End Sub

Notice that the external method contains an On Error Resume Next statement, because the original GetValue method also contains this statement. Also notice that the ID parameter isn't passed as an argument to the external method because it isn't referenced by the code inside it.

All arguments to the new Gosub_GetValue_BuildResult method are passed by reference, so that the caller can receive any value that has been modified inside the method (as is the case with the GetValue parameter in previous example.) You can have VB Migration Partner optimize this passing mechanism and use ByVal if possible by passing True as the second argument to the ConvertGosubs pragma:

    '## ConvertGosubs True, True

If this optimization is used in the above code, the separate method is rendered as follows:

    Private Sub Gosub_GetValue_BuildResult( _
           ByVal x As Short, ByRef GetValue As Short, _
           ByVal s As String, ByVal name As String)
        On Error Resume Next
        GetValue = x + Len6(s) + Len6(name)
    End Sub

If you don’t like the name that VB Migration Partner assigns to the automatically-generated method, you can easily change it by means of a PostProcess pragma:

    '## PostProcess "Gosub_GetValue_BuildResult", "AssignValueResult"

It is important to keep in mind that the conversion of a Gosub block of code into a separate method isn’t always possible. More precisely, VB Migration Partner can perform this conversions only if all the following conditions are met:

a) The method doesn't contain any Resume statement. (On Error Resume Next is OK, though).
b) The target label isn't located inside a If, Select, For, Do, or Loop block.
c) The target label isn't referenced by a Goto, On Goto/Gosub, or On Error statement.
d) The target label must be preceded by a Goto, Return, End, Or Exit Sub/Function/Property statement.
e) The code block must terminate with an unconditional Return, or an End Sub/Function/Property statement.  (Blocks that terminate with Exit Sub/Function/Property statements aren't converted.)
f) The block of code between the target label and the closing Return/End statement doesn't contain another label.

If the conversion of a specific Gosub isn't possible, VB Migration Partner reverts to the default way of converting Gosub/Return (i.e. using Goto pairs).

Currently rated 4.7 by 7 people

  • Currently 4.714286/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Exploring version 1.10 - The rename engine

clock October 10, 2008 01:50

Yesterday we finally released VB Migration Partner 1.10 BETA, which registered users can download from our site. Being a beta, we decided to keep the more recent "official" version (1.00.06) online, so that customers can have a choice.

The new version includes so many exciting refactoring features that allow you produce the kind of high-quality VB.NET code that experienced .NET develors can write, only much much faster. For example, you can now reliably merge nested IF blocks or convert On Error Goto and On Error Resume Next statements into Try-Catch blocks by just adding a new project-level pragma.

In this article I show how to leverage the brand-new symbol rename engine. A few customers asked for the ability to rename classes, methods, and controls during the migration process so that the VB.NET code would abide by their naming coding standards. Being the authors of a successful book entirely devoted to coding standard, we couldn't ignore the request.

Earlier versions of VB Migration Partner already included a rename engine - necessary to rename VB6 members that happened to be named after VB.NET keywords, among other things - and more expert users were able to hook into that engine by writing a VB Migration Partner Extender, i.e. a separate DLL that interacts with our tool during the three stages of the migration process (parsing, interpreting, code generation).

DECLARATIVE RULES
Writing an extender requires some familiarity with VB Migration Partner's object model, something most customers don't want toget involved into. (VB Migration Partner already comes with a few extenders that allow you to do complex auxiliary tasks, such as polishing UserControl classes and generating cumulative migration reports when migrating multiple projects in batch mode.)

Thus we decided to expose the inner rename engine to the outside and provide our users with the ability to write declarating rename rules based on regular expressions and stored in an XML file. Thanks to the internal rename engine the entire process was very simple and took a couple of days, most of which were spent to write a series of unit tests and a sample XML file containing a set of standard rename rules for you to use, study, and modify as you see fit.

Enabling this new feature is as simple as adding one ApplyRenameRules pragma anywhere in the VB6 project's source code:

    '## ApplyRenameRules  c:\myrules.xml

The argument is the path of the XML file containing all the rename rules in declarative format. If you omit the argument, the standard RenameRules.xml file that comes with VB Migration Partner 1.10 is used. Instead of adding an ApplyRenameRules pragma in each VB6 project you can store it in a VBMigrationPartner.pragmas file shared by one or more projects. Even better, you can store it in the master pragmas file located in VB Migration Partner's install folder, in which case all yourmigrated projects will use it. Here's a fragment of the XML file that comes with the standard installation:

 <?xml version="1.0" encoding="utf-8" ?>
<NamingRules>
  
   <!-- Components section -->
   <Symbol kind="Component" type="VB.Label" >
      <!-- ensure that we have a "lbl" prefix -->
      <Rule pattern="^(lbl)?" replace="lbl" />
      <!-- drop the "Label" suffix -->
      <Rule pattern="Label$" replace="" />
   </Symbol>

   <!-- more rules for all common controls -->

</NamingRules>

The kind attribute is used first to determine the kind of symbol the rule applies to. Valid names are Form, Class, Module, Type, UserControl, Enum, Component, Method (includes subs and functions), Field, Property, Event, EnumItem (the members of an Enum declaration), Constant, and Variables (includes, local variables and parameters.)

If kind is equal to Component or Variable, you can optionally include a type attribute, that specifies the VB6 type of the member to which the rule applies.In the above example, the type attribue is used to specify that the rule applies to all components of type VB.Label (the standard VB6 label). But you aren't confined to renaming controls: for example, the following rule renames all Integer variables named “i" into “index”

   <Symbol kind="Variable" type="Integer" >
      <Rule pattern="^i$" replace="index" ignoreCase="false" />
   </Symbol>

Renaming rules are based on regular expressions. The pattern attribute is used to decide whether the rule applies to a given member, and the replace attribute specifies how the member should be renamed. The ignoreCase optional attribute should be false if you want the pattern to be applied in case-sensitive mode (by default comparisons are case-insensitive). Finally, the changeCase optional attribute allows you to change the case of the result and can be equal to “lower”, “upper”, “pascal” (first char is uppercase), or “camel” (first char is lowercase).

Being Visual Basic a case-insensitive language, in most cases the default behavior is fine. However, consider the following renaming requirement: many VB6 developers preferred to use “C” as a prefix for all class names, but this guideline has been deprecated in VB.NET. Here’s a rule that drops the leading “C”, but only if it is followed by another uppercase character:

   <Symbol kind="Class" >
      <Rule pattern="^C(?=[A-Z])" replace="" ignoreCase="false" />
   </Symbol>

The changeCase attribute is useful to enforce naming rules that involve the case of identifiers. For example, a few developers like all-uppercase constant names; here's a rule that enforce this guideline:

   <Symbol kind="Constant" >
      <Rule pattern="^.+$" replace="${0}" changeCase="upper" />
   </Symbol>

 

There are a few other options that I don't cover here, if you are interested just have a look at the Renaming Program Members section of our manual. 

PROGRAMMATIC RULES
Declarative rules are easy to write and test - if you are familiar with regexes, at least - but there are times when you want to be in full control of how a symbol or a category of symbols are renamed. Achieving this feat with VB Migration Partner is quite easy:

1) Create a Class Library project inside Visual Studio 2005 or 2008 (you can use any .NET language, including VB.NET or C#)
2) Add a reference to the CodeArchitects.VBMigrationEngine.Dll
3) Create a public class, name it as you prefer, and make it inherit from CodeArchitects.VBMigrationLibrary.SpecialRuleBase
4) Override the GetSymbolNetName method
5) write the code that analyzes the properties of the VBSymbol passed as an argument and return its new name.
6) compile the DLL and drop it in VB Migration Partner's install folder.

For example, let's say that you want to replace the "lng" prefix with the "int" for all 32-bit integer variables defined inside a method named MyMethod. Here's the code that does it:

Public Class MyRenameRules
   Inherits SpecialRuleBase

   Public Overrides Function GetSymbolNetName(ByVal symbol As VBSymbol, _
         ByVal netName As String) As String
      If symbol.Kind = SymbolKind.LocalVar _
            AndAlso symbol.TypeName = "Long" _
            AndAlso symbol.ParentSymbol.Name = "MyMethod" Then
         If netName.StartsWith("lng") Then netName = "int" & netName.Substring(3)
      End If
      Return netName
   End Function
End Class


It couldn't be simpler, uh?

A word of caution: when you supercede VB Migration Partner's rename engine you are responsible for generating names that are both valid and unique. For example, if you drop either a prefix or a suffix, you should ensure that the new name is still unique, otherwise the generated VB.NET project won't compile. 

Currently rated 4.9 by 7 people

  • Currently 4.857143/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Subtle array cloning issues

clock October 5, 2008 04:46

As you may know, VB6 performs a copy of the array when assigning an array to another array variable. After the assignment, the source and destination arrays are distinct objects, as this code demonstrates:

Dim source(10) As Integer
source(1) = 111
Dim dest() As Integer
dest() = source()
Debug.Print source(1), dest(1)    ' displays "111,111"
' changing an element in either array doesn’t affect the other
source(1) = 222
Debug.Print source(1), dest(1)    ' displays "222,111"

Conversely, an array assignment under VB.NET just copies the array address:, the source and destination array variables point to the same set of elements. In other words, changing an element through either variable would affect the value seen by the other array variable.

For these reasons, both the Upgrade Wizard and earlier versions of VB Migration Partner automatically force a copy operation when converting an array assignment. This is how Upgrade Wizard converts the above VB6 code snippet:

Dim source(10) As Integer
source(1) = 111
Dim dest() As Integer  
dest = source.Clone()
Debug.Print source(1), dest(1)    ' displays "111,111"
' changing an element in either array doesn’t affect the other
source(1) = 222
Debug.Print source(1), dest(1)    ' displays "222,111"

It seems that adding a call to the Clone method solves the problem once and for all, but unfortunately this isn’t the case. As a matter of fact, there are several cases when the Upgrade Wizard (and probably other VB6-to-VB.NET conversion tools) doesn’t preserve functional equivalence with the original VB6 code.

For startes, a plain Clone method doesn't work correctly if the array you are assigning is an array of arrays (Under VB6, an array of arrays is a Variant array whose individual elements are arrays.) The problem here is that the Array.Clone method performs a shallow copy and doesn’t correctly copy nested arrays and the converted VB.NET application doesn’t work as it should. For this reason, starting with version 1.10, VB Migration Partner supports the CloneArray6 helper method, which can perform both a shallow copy and a deep copy (if the second argument is True, also nested arrays are copied):

   Dim arrayOfArrays() As Variant
    ' …
    Dim dest() As Variant
    dest() = CloneArray6(arrayOfArrays(), True)

The CloneArray6 method is available both in VB6 and VB.NET, but the VB6 version is a do-nothing version and doesn't change the application behavior. The CloneArray6 method can correctly handle arrays of arrays of any nesting level, arrays of structures, and more in general arrays of any ICloneable object.

Another case when UpgradeWizard generates bugged VB.NET code is when assigning a structure that contain one or more arrays. Consider this VB.NET code, generated by Upgrade Wizard when converting an assignment between two structures:

Structure MyUDT
    Public arr() As Integer
End Structure

Sub Test()
    Dim udt As MyUDT
    Dim udt2 As MyUDT
    udt2 = udt
    ' udt and udt2 point to different structures, but
    ' they share the same set of array elements
    ' …
End Sub

It is obvious that the assignment between structures doesn’t work as in the original VB6 code, because the udt and udt2 structures are pointing to the same array: changing one element in the array in one structure affects the array pointed to by the other structure.Unlike Upgrade Wizard and other conversion tools, VB Migration Partner works around this issue by rendering the assignment as follows:

Structure MyUDT
    Public arr() As Integer

    Function Clone() As MyUDT
        Dim copy As MyUDT = Me
        copy.arr = CloneArray6(Me.arr)
       Return copy
    End Function

End Structure

Sub Test()
    Dim udt As MyUDT
    Dim udt2 As MyUDT
    udt2 = udt.Clone()
    ' udt and udt2 are now completely disjoint objects, and share no arrays
    ' …
End Sub

You might bump into another elusive bug when invoking methods that store a reference to the array passed as an argument. The simplest example of this problem is when you store an array into multiple items of a Collection, as this VB6 code demonstrates:

Dim col As New Collection
Dim values(1) As String
values(0) = "Francesco": values(1) = "Balena"
col.Add(values)
' notice that here we reuse the same array…
values(0) = "Giuseppe": values(1) = "Dimauro"
col.Add(values)
Debug.Print col(1)(0) & "," & col(2)(0)   ' => "Francesco,Giuseppe"

The Upgrade Wizard converts this code verbatim, but the resulting VB.NET code just doesn’t work as expected: both col(1) and col(2) elements actually point to the same array, therefore the text displayed in the Output window is not the one you hoped for:

Debug.WriteLine(col(1)(0) & "," & col(2)(0))  ' => "Giuseppe,Giuseppe"

A solution to this problem is to explicitly ReDim the array after each Add method, as follows:

Dim col As New Collection
Dim values(1) As String
values(0) = "Francesco": values(1) = "Balena"
col.Add(values)
' allocate a new memory block for array elements
ReDim values(1)
values(0) = "Giuseppe": values(1) = "Dimauro"
col.Add(values)

However, this workaround adds unnecessary overhead to the VB6 code. Starting with VB Migration Partner version 1.10 you can avoid this overhead and have the most efficient behavior in both VB6 and VB.NET by using the CloneArray6 method:

Dim col As New Collection
Dim values(1) As String
values(0) = "Francesco": values(1) = "Balena"
col.Add(CloneArray6(values))
values(0) = "Giuseppe": values(1) = "Dimauro"
col.Add(CloneArray6(values))

The bottom line: pay close attention to how arrays are assigned and copied in your VB.NET project and carefully scrutinize the code that your VB6-to-VB.NET migration tool generates in these cases. Else you might be spending hours trying to track down these elusive bugs.

Currently rated 4.3 by 7 people

  • Currently 4.285715/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


A better cure for a common VB6 misconception

clock October 2, 2008 20:48

In an article I wrote about two months ago I showed how you can fix a programming mistake that is quite common among VB6 developers, who tend to believe that the following declaration

     Dim x1, y1, x2, y2 As Integer

declares four integer variables, whereas it actually declares only one integer (y2) and three Variant variables. The solution I proposed required one PreProcess pragma for statements declaring two variables, another PreProcess pragma for statements declaring three variables, and so forth.

Yesterday Marco Giampetruzzi - from the VB Migration Partner Team - surprised me with a better solution that uses just one pragma to account for all Dim, Public, Private, and Static statements, regardless of the number of variables they declare:

    '## PreProcess "(?<=\b(Dim|Public|Private|Static)\b.*)
                    (?<!\b(Sub|Function|Property)\b.*)
                    (?<!As\s+)(?<var>\b\w+\b)(?=,.*\bAs\s+(?<type>\w+))",
                    "${var} As ${type}", True

Let me briefly explain how it works. The first regex tells VB Migration Partner to search an identifier and name it as "var" (?<var>\w+). This identifier must preceded on the same line by one of the declaration keywords (?<=\b(Dim|Public|Private|Static)\b.*). To prevent that parameters on a method declaration be mistakenly matched, we also check that the identifier not be preceded by a keyword used in method and property declarations (?<!\b(Sub|Function|Property)\b.*). Next, the regex ensures that the indentifier be immediately followed by a comma and (after some characters) by an As clause (=?,.*\bAs\b\s+(?<type>\w+)). Notice that above pragma doesn't support array declarations.

Very smart indeed... thanks Marco!

You can build similar PreProcess pragmas to account for similar cases. For example, the following pragma adds an As clause to parameters inside method declarations:

    '## PreProcess "(?<=\b(Sub|Function|Property)\b.*)
                    (?<!As\s+)(?<var>\b\w+\b)(?=,.*\bAs\s+(?<type>\w+))",
                    "${var} As ${type}", True

If you aren't familiar with regexes, drop a line to our tech support and we'll cook one for you!

Currently rated 5.0 by 6 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Fun in Las Vegas (... and lots of migration tricks)

clock October 2, 2008 20:37

What: a 75-minute session about VB6-to-VB.NET migration tips, tricks, and techniques... by yours truly

When:Tuesday, October 14, at 11 am

Where: Mirage Hotel, Las Vegas

 

Currently rated 5.0 by 4 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Announcing VB Migration Partner 1.10 - the refactoring release!

clock September 21, 2008 04:26

We released VB Migration Partner 1.0 in May and the product has been an instant success, thanks to its great features that were long waited for by all VB6 developers wishing to move to the .NET world. In these months we have fixed some residual bugs but, even more important, we took note of all the requests from our customers. The result is the brand-new VB Migration Partner 1.10 version, which introduces many new features that help you to produce better and faster VB.NET. For this reason we have internally dubbed it as the refactoring release.

code migration that just works

Note: all the following features will be available only to registered users of the Enterprise Edition, except support for SS** controls.

GoSub refactoring
VB Migration Partner has always fully supported the conversion of the Gosub keyword, by interspersing the code with additional labels and a simulated return stack. This approach guarantees perfect functional equivalence with the original VB6 code, but admittedly delivers VB.NET code that isn’t very readable.

VB Migration Partner 1.10 goes further by letting you convert Gosub into call to separate private methods, i.e. the same kind of refactoring a skilled developer would perform. The private method receives all the parameters it requires, and these parameters are passed with ByRef if the callee actually modifies them.

We are very proud of this exciting feature. No other migration tool on the market supports anything similar, as far as we know. You can enable it by means of the new ConvertGosubs pragma, which can be scoped at the project, file, and method level.

Once again, we privilege functional equivalence over “beautiful” code, therefore the pragma has no effect if the Gosub can’t be refactored correctly, such as when the method contains one or more Resume statements or the target label is also referenced by a Goto or On Error statement.

Structured Exception Handling (SEH)
While VB.NET fully supports the On Error and Resume keywords, it is true that VB.NET programmers should use Structured Exception Handling (SEH) and the Try-Catch block whenever possible. You can now use the UseTryCatch pragma to have perform the substitution automatically. The pragma can be scoped at the project, file, and method scope, therefore you can precisely state where you want it and where On Error statements should be left in code.

The conversion isn’t possible in some cases – for example, if the method contains multiple On Error Goto statements or if the statement appears inside a conditional block (e.g. If or Select block) – thus VB Migration Partner performs the conversion only if the functional equivalence between VB6 and VB.NET can be fully preserved.

Type inference
VB Migration Partner 1.10 is capable, in most cases, to correctly deduce a less generic data type for Variant local variables, parameters, class fields, functions and properties. For example, if a Variant local variable is always assigned a floating point value, it can be automatically rendered as a Double variable instead of a more generic Object variable.

The new feature is enabled by means of the new InferType pragma. Like all other pragmas, you can set the scope for this action to the entire project, a specific file, or a given method, therefore it’s quite easy to decide exactly where type inference should be used. You can decide whether to limit type inference to only the members that lacks an explicit “As” clause or extend it to all Variant elements.

Unreachable code and unused member removal
Since its early betas, VB Migration Partner included a sophisticated code analysis engine that was able to spot both unreachable code (e.g. the code that immediately follows an Exit Sub statement, unless the code begins with a label referenced by a Goto o Gosub) and unused methods and properties. Developers could then decide to manually remove the code in question, to make the converted VB.NET project leaner and faster.

The new 1.10 version adds two pragmas – RemoveUnreachableCode and RemoveUnusedMembers – which eliminate the need for any manual action. Both pragmas let you specify whether the code in question should be completely removed or just commented out, therefore you can easily make your simulations and check whether the removal would have any consequence of your code.

Please notice that the code analysis engine might mistakenly flag as “unused” a method that is actually invoked via late-binding. You can quickly realize if this is the case, and if so you can use the MarkAsReferenced or MarkPublicAsReferenced pragmas to let VB Migration Partner know that the method shouldn’t be touched. (These pragmas have been available since early beta versions.)

If merging
VB Migration Partner has always supported the selective replacement of And/Or keywords with AndAlso/OrElse, but this feature had to be performed “semi-manually” by including a proper LogicalOps pragma.

VB Migration Partner 1.10 is capable of analyzing nested If blocks and decide whether it is safe to merge two nested If blocks into a single block whose condition concatenates the individual conditions by means of the AndAlso keyword. This feature is enabled by means of the MergeIfs pragma and you decide whether to scope it at the project, file, or method level. (In general, using project scope is always safe, therefore you can just place this pragma in the master VBMigrationPartner.pragmas file.)

There is no limit to the number of Ifs that can be automatically merged by this feature.
 
Naming guidelines enforcement
Since version 1.0, VB Migration Partner has allowed users to rename any member in the converted VB.NET by writing an extension DLL. However, in the forthcoming version 1.10 we made things even simpler by supporting rename rules stored in an XML file in a very simple syntax.

This new feature is enabled by means of the ApplyRenameRules pragma, which lets you specify the path of the XML file that holds the rename rules. We provide an XML file that contains the standard .NET naming guidelines and that you can extend or modify to match *your* guidelines. (Don’t forget that Giuseppe Dimauro and I are the authors of Practical Guidelines and Best Practices for VB.NET and C# Developers, thus we know what we are talking about.)

For example, a typical naming guideline might dictate that all push buttons should have a name with “btn” prefix and that any other prefix – such as the “cmd” prefix, often used in VB6 – should be dropped off. A guideline isn’t restricted to forms and controls, though, and you are in controls of how VB Migration Partner should rename variables, methods, fields, properties, parameters, and so forth. You can even decide to restrict rules to certain project items.

And yes, if declarative XML isn’t enough flexible for your renaming requirements, you can still implement custom rename rules using any .NET programming language.

Support for Infragistic’s ActiveThreed Plus library
Many VB6 applications use one or more SS** controls, namely SSCommand, SSOption, SSCheck, SSFrame, and SSPanel. These controls were originally included in earlier Visual Basic releases to support features such as 3-D effects. More powerful versions of those controls were later gathered by their manufacturer – Sheridan, and then Infragistics – into a commercial library named ActiveThreed Plus.

Not only does VB Migration Partner support all the original SS** controls that were included in Microsoft Visual Basic, it also supports other controls in the ActiveThreed Plus library, namely SSResizer, SSScroll, SSRibbon, and SSTransition. (It doesn’t support only two of the 11 controls included in the library: SSSplash and SSSplitter.) Notice that these controls are supported by means of fully managed .NET controls and you don’t need to deploy the original ActiveX controls with the converted VB.NET application (but you need the original controls when running the migration process.)

Preserve your investement! VB Migration Partner users can adopt the convert-test-fix methodology and solve all compile/runtime errors exclusively by means of pragmas and without any manual intervention on the generated VB.NET. This means that they can leverage all the new refactoring features by simply adding the new seven pragmas to the master VBMigrationPartner.pragmas file, and restart the migration process. With VB Migration Partner it's that simple! Laughing

That’s all for now, but I will post more details in the future. VB Migration Partner 1.10 will be released and made available to registered users in a couple weeks.

Currently rated 4.4 by 9 people

  • Currently 4.444445/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


VB Migration Partner - Subscribe to our feed RSS  blog RSS
 
AddThis Feed Button
 
 

Home blog
 
AddThis Social Bookmark Button


 

Sign in

Search

Calendar

<<  November 2008  >>
SuMoTuWeThFrSa
2627282930311
2345678
9101112131415
1617181921
232526272829
30123456

Archive