Exploring version 1.10 - Staged migrations and binary compatibility

clock November 25, 2008 04:33

Last week I blogged a lot about the new features in VB Migration Partner 1.10, including Gosub refactoring, dead code removal, type inference, and If merging. In this post I illustrate a new exciting feature that makes new version 1.10 even more powerful and versatile.

When migrating large N-tier applications you typically want to adopt the so-called staged migration or phased migration approach, in which you convert one component at the time while still keeping the bulk of the application in VB6. There are actually two different flavors of staged migration:

In-out: you start converting data (inner) components, then proceed outwardly with business components and finally with user interface components

Out-in: you start converting user interface (outer) components and then proceed towards business components, and finally data (inner) components

The approach you take usually depends on your priorities and the very reasons why you’re migrating the VB6 code. If you want to improve the user interface you’re likely to choose the Out-in option; on the other hand, if you are migrating to .NET to leverage its superior scalability, its ability to communicate via WCF, and its smaller memory footprint, odds are that you’d like to implement the In-out scenario.

All the migration tools on the market can handle the Out-In scenario quite easily: if you convert a VB6 client application that references one or more COM component you get a .NET Windows Forms application that preserves all the references to the original COM objects and will therefore work exactly like the original VB6 client. So far, so good.

Things become less simple if you wish (or need to) adopt the In-Out scenario. In this case you'd have to generate a VB.NET component that is binary-compatible with the original component being migrated, so that all the existing VB6 clients continue to work with the new .NET component as they did with the original COM component.

Well, the interesting thing is that neither the Upgrade Wizard nor any other tool on the market, as far as we know, can generate binary-compatible .NET components. No other tool other than VB Migration Partner, that is, because we just added this important feature to release 1.10, as a response to a request from a customer.

Making the migrated VB.NET component binary-compatible with the original VB6 component – and therefore compatible with all existing VB6 clients – is as easy as adding a project-level BinaryCompatibility pragma:

    '## project:BinaryCompatibility


VB Migration Partner generates a ComClass attribute for each public class in the generated VB.NET project; this attribute specifies the GUIDs of the original coclass, class interface, and event interface of the original VB6 class. In addition, a project-level Guid attribute in AssemblyInfo.vb is generated so that the .NET assembly has same GUID as the original type library. Finally, the Register for COM Interop setting is enabled in the MyProject property page. The neat effect of all these operations is that the .NET DLL is fully compatible with the original COM DLL and all existing VB6/COM clients can continue to work as before but use the new .NET DLL instead. (Clients don’t need to be recompiled.)

The BinaryCompatibility pragma is ignored and no warning message is emitted if the VB6 project type isn’t ActiveX DLL or if VB Migration Partner can’t find a COM DLL to be used to extract coclass and interface GUIDs. Notice that you can also use the BinaryCompatibility pragma at the file level, if you want to preserve binary compatibility with only a subset of the classes defined in the original COM component.

Interestingly, you might want to use the BinaryCompatibility pragma even if you aren't adopting the staged approach. In fact, even if the .NET components don't have to be compatible with the original COM components when the entire application is migrated, it is useful to preserve the compatibility until all the portions of the application have been converted, because it allows you to test a .NET component using existing COM clients.

Achieving binary compatibility requires more than just adding a few GUIDs and attributes, though. There are many subtle issues to be solved, for example enums, events, collection classes, apply the right MarshalAs attribute to UDT members, and a lot more. You need to wrap Public fields in properties and might need to transform a class into an interface before exposing it to COM clients.

In practice, to create a .NET component that is binary-compatible with an existing COM component you need to be a COM Interop guru and have a lot of time to spare… or you can just try out VB Migration Partner 1.10 Wink



Exploring version 1.10 - Type inference

clock November 23, 2008 04:19

VB Migration Partner is capable, in most cases, to correctly deduce a less generic data type for Variant local variables, parameters, class fields, functions and properties. This applies both to members that were explicitly declared as Variant and to members that lacked an explicit As clause. To see how this feature works, consider the following VB6 code:

'## DeclareImplicitVariables True

Private m_Width As Single

Property Get Width ()
    Width = m_Width
End Property

Property Let Width (value)
    m_Width = value
End Property

Sub Test(x As Integer, frm)
    Dim v1 As Variant, v2
    v1 = x * 10
    v2 = Width + x
    Set frm = New frmMain
    res = frm.Caption
End Sub

By default this code is converted to VB.NET as follows:

Private m_Width As Single

Public Property Width() As Object
    Get
        ' UPGRADE_WARNING (#0364): Unable to assign default member of 'Width'.
        ' Consider using the SetDefaultMember6 helper method.

        Return m_Width
     End Get
    Set(ByVal value As Object)
        ' UPGRADE_WARNING (#0354): Unable to read default member of 'value'.
        ' Consider using the GetDefaultMember6 helper method.

        m_Width = value
     End Set
End Property

Public Sub Test(ByRef x As Short, ByRef frm As Object)
    ' UPGRADE_INFO (#05B1): The 'res' variable wasn't declared explicitly.
    Dim res As Object = Nothing
    ' UPGRADE_INFO (#0561): The 'frm' symbol was defined without an
    ' explicit "As" clause.

    Dim v1 As Object = Nothing
    ' UPGRADE_INFO (#0561): The 'v2' symbol was defined without an
    ' explicit "As" clause.

    Dim v2 As Object = Nothing
    ' UPGRADE_WARNING (#0364): Unable to assign default member of 'v1'.
    ' Consider using the SetDefaultMember6 helper method.
    v1 = x * 10
    ' UPGRADE_WARNING (#0364): Unable to assign default member of 'v2'.
    ' Consider using the SetDefaultMember6 helper method.

    v2 = Width + x
    frm = New Form1()
    ' UPGRADE_WARNING (#0354): Unable to read default member of
    ' 'frm.Caption'. Consider using the GetDefaultMember6 helper method.
    ' UPGRADE_WARNING (#0364): Unable to assign default member of 'res'.
    ' Consider using the SetDefaultMember6 helper method.

    res = frm.Caption
End Sub

Variant variables – either implicitly declared or not – are translated into Object variables, which causes several warning to be emitted and, more important, adds an overhead at runtime. This overhead can be often avoided by declaring the variable of a more defined scalar type. You can have VB Migration Partner change the data type for a given member by means of the SetType pragma, but this operation requires a careful analysis of which values are assigned to the variable.

Users of VB Migration Partner 1.10 can have this analysis performed automatically, by inserting an InferType pragma at the project-, file-, method- or variable-level. For example, let’s assume that the previous VB6 code contains the following pragma at the file level:

'## InferType Yes

The Yes argument in this pragma makes VB Migration Partner attempt to infer the type of al the implicitly-declared local variables and of all the local variables, class fields, functions and properties that lack an explicit As clause:

Private m_Width As Single

Public Property Width() As Single
    Get
        Return m_Width
    End Get
    Set(ByVal value As Single)
        m_Width = value
    End Set
End Property

Public Sub Test(ByRef x As Short, ByRef frm As Object)
    ' UPGRADE_INFO (#0561): The 'frm' symbol was defined without an
    ' explicit "As" clause.
    ' UPGRADE_INFO (#05B1): The 'res' variable wasn't declared explicitly.

    Dim res As Object = Nothing
    Dim v1 As Object = Nothing
    ' UPGRADE_INFO (#0561): The 'v2' symbol was defined without an
    ' explicit "As" clause.

    Dim v2 As Single
    ' UPGRADE_WARNING (#0364): Unable to assign default member of 'v1'.
    ' Consider using the SetDefaultMember6 helper method.

    v1 = x * 10
    v2 = Height + x
    frm = New Form1()
    ' UPGRADE_WARNING (#0354): Unable to read default member of
    ' 'frm.Caption'. Consider using the GetDefaultMember6 helper method.
    ' UPGRADE_WARNING (#0364): Unable to assign default member of 'res'.
    ' Consider using the SetDefaultMember6 helper method.

    res = frm.Caption
End Sub

In this new version, VB Migration Partner can infer the type of the Width property from the type of the m_Width variable, which in turn permits to infer the type of the v2 local variable. VB Migration Partner attempts to infer neither the type of the v1 local variable (because it is explicitly declared As Variant) nor the type of the frm parameter (because by default method parameters aren’t under the scope of the InferType pragma).

You can extend type inference to members that were declared with an explicit As Variant by using the Force argument in the pragma. In addition, passing True in the second (optional) argument extends the scope to method parameters:

'## InferType Force, True

The result of the conversion is now the following:

Private m_Width As Single

Public Property Width() As Single
    Get
        Return m_Width
    End Get
    Set(ByVal value As Single)
        m_Width = value
    End Set
End Property

Public Sub Test(ByRef x As Short, ByRef frm As Form1)
    ' UPGRADE_INFO (#0561): The 'frm' symbol was defined without an
    ' explicit "As" clause.
    ' UPGRADE_INFO (#05B1): The 'res' variable wasn't declared explicitly.

    Dim res As String = Nothing
    Dim v1 As Integer
    ' UPGRADE_INFO (#0561): The 'v2' symbol was defined without an
    ' explicit "As" clause.

    Dim v2 As Single
    v1 = x * 10
    v2 = Width + x
    frm = New Form1()
    res = frm.Caption
End Sub

Notice that in this new version the type of frm parameter is inferred correctly, which in turn permits to infer the type of the res local variable.

Type inference works both with scalar types and object types. Deducing the correct type of an object often produces faster and more readable code. For example, consider the following VB6 code:

'## InferType
Public Sub SetAddress(ByVal address As String)
    Dim tb
    Set tb = Me.txtAddress  ' (txtAddress is a TextBox)
    tb = address
End Sub

VB Migration Partner can infer that the tb variable is of type TextBox and, consequently, it can correctly expand its default property:

Public Sub SetAddress(ByVal address As String)
    Dim tb As VB6TextBox = Me.txtAddress    
    tb.Text = address
End Sub


Extending the pragma scope to method parameters isn’t always a good idea, because the current version of VB Migration Partner infers the type of parameters by looking at assignments inside the method and doesn’t account for implicit assignments that result from method calls. For this reason, the type inferred during the migration process might not be correct in some cases.

Important note: Type inference isn’t an exact science and you should always double-check that the data type inferred by VB Migration Partner is correct. Better, we recommend that you use the InferType pragma only during early migration attempts and then you later assign a specific type to members by editing the original VB6 code or by means of the SetType pragma.



Exploring version 1.10 - Unused/unreachable code detection and removal

clock November 21, 2008 01:23

It’s no secret that a typical business application contains a lot of dead code that is never executed. This is especially true if the application has evolved over many years and has been authored by many developers. The percentage of useless code can vary, but is typical in the range 5% to 10%.

In a VB6 application this dead code consumes a little memory but doesn’t otherwise hurt the overall performance. However, things are quite different if you want to port that application to VB.NET – either by hand or automatically by means of a conversion tool.

In fact, if you have no way to spot where this dead code is, you might be wasting 5-10% of your time and money just to migrate code that will be never executed anyway. Considering how much migration initiatives may cost, this small percentage becomes a very high number in absolute terms.

VB Migration Partner has always included a very sophisticated code analysis engine that automatically spots portions of code that are unreachable – for example, the code that immediately follows an Exit Sub statement and that doesn’t begin with a label – as well as methods that aren’t referenced by code elsewhere in the same project or project group. VB Migration Partner distinguishes the two cases by means of different warning messages:

' UPGRADE_INFO (#0521): Unreachable code detected
' UPGRADE_INFO (#0501): The ‘XYZ’ member isn’t used anywhere in current application

The code analysis engine is smart enough to detect whether two methods call each other but not from the main application, or fields that are accessed only by methods that, in turn, are never invoked by other code. In this case the message is slightly different:

' UPGRADE_INFO (#0511): The ‘XYZ’ member is used only by members that
' haven’t found to be used in current application

Keep in mind that you can selectively disable warning messages by means of the DisableMessage pragma, as in:
    '## DisableMessage 521
or you can suppress all messages related to code analysis by means of the DisableMessages pragma:
    '## DisableMessages CodeAnalysis

Like all code analysis tools, VB Migration Partner may issue warnings related to members that appear to be unused only because the tool is analyzing one project at a time. For example, if you convert an N-tier application one component at the time, an analysis tool flags the majority of public members as “unused” even though they are used by client apps that haven’t been included in the code analysis. If the application isn’t too large, you can solve this issue by analyzing a project group that gathers all the individual projects, but this workaround isn’t practical for applications of dozens or hundreds different projects.

Another problem related to correctly spotting unused members is late-binding: if a given method or property is accessed by client code in late-bound mode, no code analysis tool is capable to detect that the member is actually used and shouldn’t be removed.

VB Migration Partner offers a “manual” solution to these problems in the form of two pragmas: the MarkAsReference pragma tells the code engine that a given member is actually referenced – for example, via late binding – and therefore the code analysis engine should omit warnings #0501 and #0511.

The MarkPublicAsReferenced pragma is useful when analyzing and migrating ActiveX DLL and OCX projects: when this pragma is used, all public properties and methods are marked as referenced. In turn, if these public members reference private variables or methods, these private members will be marked as referenced, too. The MarkPublicAsReferenced pragma can be scoped at the project or file level. For example, when migrating an ActiveX project, you typically need one single pragma:

    '## project:MarkPublicAsReferenced

All the features discussed so far have been available in all editions of VB Migration Partner since version 1.0. The forthcoming version 1.10 goes further, because it offers the ability to automatically remove unreachable code and unused members.

In version 1.10 you can remove unreachable code by means of the RemoveUnreachableCode pragma, which takes one of the following arguments: On (or omitted, remove the unreachable code), Off (don’t remove the unreachable code), Remarks (comment the unreachable code). For example, let’s consider this VB6 code:

Sub Test()
    ' GoSub DisplayMessage
    ' …
    ' more code here …
    ' …

    Exit Sub
DisplayMessage:
    MsgBox "Inside Test method"
    Return
End Sub


The code that begins with the DisplayMessage label was originally the target of the GoSub statement; it is clear that the developer later remarked out the Gosub keyword but forgot to delete the MsgBox and Return statements. This is how VB Migration Partner migrates this code:

Public Sub Test()
    ' GoSub DisplayMessage
    ' …
    ' more code here …
    ' …

    Exit Sub

    ' UPGRADE_INFO (#0521): Unreachable code detected

DoSomething:
    MsgBox6("Inside Test method")
    ' UPGRADE_INFO (#05A1): The VB6 code contains a Return but no
    ' GoSub has been found in current method.

    Err.Raise(3)
End Sub


Before deleting unreachable code, you might want to check that the program works correctly, by just commenting out the code, by adding the following pragma:

'## RemoveUnreachableCode Remarks
Public Sub Test()
    ...


This is what you get now:

Public Sub Test()
    ' GoSub DisplayMessage
    ' …
    ' more code here …
    ' …

    Exit Sub

    ' UPGRADE_INFO (#06F1): Unreachable code removed
    ' EXCLUDED: DoSomething:
    ' EXCLUDED: Beep()
    ' EXCLUDED: Err.Raise(3)

End Sub

Not that you are sure that the program works well, you can use the RemoveUnreachableCode pragma with no arguments:

'## RemoveUnreachableCode


which produces this code:

Public Sub Test()
    ' GoSub DisplayMessage
    ' …
    ' more code here …
    ' …

    Exit Sub

    ' UPGRADE_INFO (#06F1): Unreachable code removed

End Sub


Notice that the above code contains a new warning, which alerts you on the fact that some code has been removed. As usual, you can get rid of this new warning by means of a suitable DisableMessage pragma.

If you like the idea of automatically removing unreachable portions of code, you’ll surely appreciate the capability of doing the same with whole unused members, that is all the members that would be flagged with either the #0501 or #0511 warnings. In this case you need a RemoveUnusedMembers pragma, whose argument can be On (or omitted), Off, and Remarks – exactly like the RemoveUnreachableCode pragma seen above.
As before, you might want to remark out unused members first, to see how the code works

    '## RemoveUnusedMembers Remarks

and then delete them completely with
 
    '## RemoveUnusedMembers

When removing variables and methods, VB Migration Partner inserts a warning in the migrated code:

' UPGRADE_INFO (#0701): The 'XYZ' member has been removed because it
' isn't used anywhere in current application.

When running the tool to get the definitive VB.NET version of your code, it’s a good idea to get rid of this message with

    '## DisableMessage 0701


One final note: The ability to selectively indicate which members are to be considered as referenced makes VB Migration Partner stand out among other analysis/migration tool that don’t offer the ability to remove dead code or that allow you to enable this feature only at the project-level.

In fact, when migrating large and complex N-tier application, you can rarely remove all unreferenced members, because they might be referenced in client apps that aren’t part of the same project group. In practice, if a tool doesn’t give you the option to selectively decide which members should be removed, this feature is rarely usable when migrating large applications.



Exploring version 1.10 - If merging

clock November 20, 2008 00: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



Speed up your VB.NET collections

clock November 8, 2008 04: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.10, 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.10 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.



Exploring version 1.10 - Structured Exception Handling

clock November 6, 2008 02:09

Starting with version 1.10, VB Migration Partner 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.



Code Architects appointed Microsoft NXT world-wide partner

clock November 5, 2008 01: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.