Quantcast
Channel: VB Migration Partner - VB.NET
Viewing all 16 articles
Browse latest View live

Bugs that drove us crazy

$
0
0

We all know that writing software requires a vast assortment of skills and, above all, a lot of patience. You usually need the latter to feel comfortable when you realize that you've wasted hours and days over someone else's bug.  Here's the story.

Like many commercial applications, VB Migration Partner has a splash screen. No big deal. VB Migration Partner is written in VB2005, therefore we could use VB.NET application framework. As a matter of fact, defining a splash screen in VB.NET is as easy as selecting an item in a combobox in the Application tab of the My Project designer.



During the beta test period a few users reported that at times VB Migration Partner threw an exception at launch, immediately after closing the splash screen. We never managed to reproduce the problem on our computers. Fortunately the exception occured quite rarely. Until a user running Windows 2000 reported that he was seeing the exception at nearly every launch. After installing VB Migration Partner on Windows 2000 in our lab, we could indeed verify that we had a problem.

 

It took a while to find out that we weren't  the only ones suffering from this issue. As a matter of fact, Microsoft recognizes that it is a VB.NET bug. Fortunately, they also offer a workaround. According to whis workaround you must create an invisible form from inside the Form_Load event of your splash screen, something like this:

   Private Sub Form_Load(ByVal sender As Object, ByVal e As EventArgs) _
          Handles MyBase.Load
     Dim frm As New Form
      frm.Opacity = 0
      frm.ShowInTaskbar = False
      frm.FormBorderStyle = FormBorderStyle.None
      frm.Show()
      Return frm
   End Function

Unbelievably, this code makes the problem vanish away! You can find more details in this Microsoft KB article.

We realized that *all* applications written in VB.NET could suffer from this issue, thus we added a method to our support library, named SplashScreenBugFix6, which creates the invisible form for you. You just need to add an InsertStatement pragma in the original VB6 splash screen form, so that a call to this method is automatically generated anytime you convert the VB6 project:

    ' in the VB6 project
    Private Sub Form_Load()
      '## InsertStatement SplashScreenBugFix6()
      ' more code in the Load event
   End Sub


Refactoring VB.NET code with regular expressions

$
0
0

One of the key features of VB Migration Partner is migration pragmas, and possibly the most powerful pragma is PostProcess, which enables you to modify the VB.NET being generated. The PostProcess pragma is based on regular expressions and works much like the Regex.Replace method.

The PostProcess pragma can really do wonders in the hands of a developer who is familiar with regexes. As a matter of fact, we have already uploaded several KB articles that show how you can refactor your code using PostProcess pragmas:

[HOWTO] Modify project-level options in VB.NET programs
[HOWTO] Move the declaration of a variable into a For or For Each loop
[HOWTO] Transform “Not x Is y” expressions into “x IsNot y” expressions
[HOWTO] Replace App6 properties with native VB.NET members
[HOWTO] Get rid of warnings related to Screen.MousePointer property
[HOWTO] Convert While loops into Do loops
[HOWTO] Simplify code produced for UserControl classes
[HOWTO] Change the base class of a VB.NET class

[HOWTO] Avoid compilation errors caused by unsupported designers

In this post I am going to show you how you can use the PostProcess pragma in a creative way to optimize common Boolean expressions. The regex patterns I will show aren't exactly simple and I won't explain each in depth. But trust me, they work and they work great!

Important note #1: like all substitutions based on regular expressions, there is a small probability that the techniques discussed in this article might suffer from false matches and, therefore, might produce invalid VB.NET code. Ensure that you add these pragmas to your VB6 code only after you have checked that the conversion works well and be prepared to remove these pragmas if you notice that they produce invalid VB.NET code.

Important note #2: most regex patterns in this article are too long for the page width and will be split in two or more lines. If you are in trouble, at the bottom of this KB article you will find a link to a text file that gathers all of the PostProcess pragmas I am introducing here.

1. Comparisons with True and False

Some VB6 developers like to explicitly compare a Boolean variable with True or False, as in this example:
    Dim x As Integer, res As Boolean, res2 As Boolean
    ' ...
    If res = True And res2 = False Then x = 0


Explicit comparison of a Boolean variable with True or False adds a little overhead, which can be avoided by re-rewriting the expression as follows:
    If res And Not res2 Then x = 0

Here are the two PostProcess pragmas that do the trick, the former accounting for comparisons with True and the latter for comparisons with False:

'## PostProcess "(?<=\n[ \t]*)(?<key>If|ElseIf)\b(?<pre>.+)\b(?<cond>\S+)
    [ \t]*=[ \t]*\bTrue\b(?<post>.+)\bThen\b",
    "${key}${pre}${cond}${post}Then", True

'## PostProcess "(?<=\n[ \t]*)(?<key>If|ElseIf)\b(?<pre>.+)\b(?<cond>\S+)
    [ \t]*=[ \t]*\bFalse\b(?<post>.+)\bThen\b",
    "${key}${pre}Not ${cond}${post}Then", True


The condition can appear also inside Do, Loop, and While statements, thus we need two more PostProcess pragmas:

'## PostProcess "(?<=\n[ \t]*)(?<key>(Do|Loop)[ \t]+(While|Until)|While)\b
    (?<pre>.+)\b(?<cond>\S+)\s*=\s*\bTrue\b",
    "${key}${pre}${cond}", True

'## PostProcess "(?<=\n[ \t]*)(?<key>(Do|Loop)[ \t]+(While|Until)|While)\b
    (?<pre>.+)\b(?<cond>\S+)\s*=\s*\bFalse\b",
    "${key}${pre}Not ${cond}", True


2. Not keyword in Do While and Loop While statements

If a Do While or Loop While statement is followed by a condition that is prefixed by the Not keyword, you can drop the Not keyword and transform the statement into Do Until or Loop Until, respectively. For example, the following code
    Do While Not res
        ' ...
    Loop

can be transformed into:
    Do Until res
        ' ...
    Loop


This is the PostProcess pragma that can perform this substitution for you:

'## PostProcess "(?<=\n[ \t]*)(?!.*\b(And|Or|Xor)\b)(?<key>Do|Loop)
    [ \t]+While[ \t]+Not[ \t]+(?<cond>.+)", "${key} Until ${cond}", True


Notice that the regex pattern fails if the condition contains an And, Or, or Xor operator. This check is necessary to avoid to mistakenly match an expression such as this:
    Do While Not res And x = 10

3. Not keyword in Do Until and Loop Until statements

If a Do Until or Loop Until statement is followed by a condition that is prefixed by the Not keyword, you can drop the Not keyword and transform the statement into Do While or Loop While, respectively. For example, the following code
    Do Until Not res
        ' ...
    Loop

can be transformed into:
    Do While res
        ' ...
    Loop


The PostProcess pragma that can perform this substitution is as follows:

'## PostProcess "(?<=\n[ \t]*)(?!.*\b(And|Or|Xor)\b)(?<key>Do|Loop)
    [ \t]+Until[ \t]+Not[ \t]+(?<cond>.+)", "${key} While ${cond}", True


4. Assigning True/False to a variable in a If...Then...Else block

A common (yet inefficient) coding pattern among developers is to use an If…Then…Else block to assign True or False to a Boolean variable. For example, consider the following VB6 code:
    Dim x As Integer, res As Boolean, res2 As Boolean
    ' ...
    If x <> 0 Then
        res = True
    Else
        res = False
    End If            

    If x > 10 And x < 20 Then
        res2 = False
    Else
        res2 = True
    End If


It is evident that it can be simplified (and optimized) as follows:

    res = (x <> 0)
    res2 = Not (x > 10 And x < 20)


Creating a regular expression pattern that allows you capture and transform the If…Then…Else block isn’t a trivial task, also because we need a different PostProcess pragma to account for single-line If statements:

'## PostProcess "(?<=\n[ \t]*)If[ \t]+(?<cond>.+?)[ \t]+Then[ \t]*\r?\n[ \t]
    *\b(?<var>(Return\b|\S+[ \t]*=))[ \t]*True[ \t]*\r?\n[ \t]*Else
    [ \t]*\r?\n[ \t]*\k<var>[ \t]*False[ \t]*\r?\n[ \t]*End[ \t]+If
    [ \t]*\r?\n", "${var} (${cond})\r\n", True

'## PostProcess "(?<=\n[ \t]*)If[ \t]+(?<cond>.+?)[ \t]+Then[ \t]+
    (?<var>(Return\b|\S+[ \t]*=))[ \t]*True[ \t]+Else[ \t]+\k<var>
    [ \t]*False[ \t]*\r?\n", "${var} (${cond})\r\n", True


The next two PostProcess pragmas account for the cases when the variable is assigned False when the condition is true. Again, we need a second pragma to account for single-line IF statements.

'## PostProcess "(?<=\n[ \t]*)If[ \t]+(?<cond>.+?)[ \t]+Then[ \t]*\r?\n[ \t]
    *\b(?<var>(Return\b|\S+[ \t]*=))[ \t]*False[ \t]*\r?\n[ \t]*Else
    [ \t]*\r?\n[ \t]*\k<var>[ \t]*True[ \t]*\r?\n[ \t]*End[ \t]+If
    [ \t]*\r?\n", "${var} Not (${cond})\r\n", True

'## PostProcess "(?<=\n[ \t]*)If[ \t]+(?<cond>.+?)[ \t]+Then[ \t]+
    (?<var>(Return\b|\S+[ \t]*=))[ \t]*False[ \t]+Else[ \t]+\k<var>
    [ \t]*True[ \t]*\r?\n", "${var} Not (${cond})\r\n", True


Notice that the patterns that follow account both for variable assignements and for the Return keyword used to return a value from a function (such Return keyword is used by VB Migration Partner if possible).

5. Assigning different values to a variable inside an If...Then...Else block

Another common coding pattern - which is actually a variant of the previous one - is to use an If…Then…Else block to assign one of two possible values to the same variable, as in these examples:
    If x <> 0 Then
        y = x + 10
    Else
        y = 20
    End If

    ' single-line variant
    If x <> 0 Then y = x + 10 Else y = 20


There is a more concise – though not necessarily more efficient – way to perform the same assignment:
    y = IIf(x <> 0, x + 10, 20)

If you are interested in performing this substitution automatically, you can use the following two pragmas, where the latter works for single-line IF statements:

'## PostProcess "(?<!\bEnd[ \t]+)\bIf[ \t]+(?<cond>.+?)[ \t]+Then[ \t]*\r?\n
    [ \t]*\b(?<var>(Return\b|\S+[ \t]*=))[ \t]*(?<v1>[^\r\n]+)\r?\n
    [ \t]*Else[ \t]*\r?\n[ \t]*\k<var>[ \t]*(?<v2>[^\r\n]+)[ \t]*\r?\n
    [ \t]*End[ \t]+If[ \t]*\r?\n",
    "${var} IIf(${cond}, ${v1}, ${v2})\r\n", True

'## PostProcess "(?<!\bEnd[ \t]+)\bIf[ \t]+(?<cond>.+?)[ \t]+Then[ \t]+
    (?<var>(Return\b|\S+[ \t]*=))[ \t]*(?<v1>[^\r\n]+)\bElse[ \t]+\k<var>
    [ \t]*(?<v2>[^\r\n]+)\r?\n",
    "${var} IIf(${cond}, ${v1}, ${v2})\r\n", True


---------------------------

That's it, folk! If you ever wondered why you should care about regular expressions, I hope I gave you something to think about Wink

Sharp enough to convert from VB6 to C#

$
0
0

Every now and then I visit the competition's web site (as much they visit ours) and recently stumbled into this post, which discusses whether VB6ers should migrate to C# rather than VB.NET. The sentence that hit my attention was

By the way, we’ve seen a few people suggesting a double path approach for those who chose to migrate their VB6 applications to C#. This idea mostly comes from those who offer a solution that only converts to VB.NET, and they even say it’s irrational to think about jumping from VB6 to any .NET language other than VB.NET. Well, the immense set of differences between VB6 and C# or VB.NET are accurately resolved by <name of their conversion tool>.

I had a friendly discussion with a couple of the author’s colleagues some time ago on this topic, therefore I assume that the “unnamed competitor” is Code Architects and our VB Migration Partner tool.

The first part of the sentence is absolutely correct: VB Migration Partner tool doesn’t generate C# code and we don’t plan to add this feature in the foreseeable future.

The reason is simple: our tool can generate VB.NET programs that are 100% equivalent to the original VB6 code, either at the first attempt or after adding a few migration pragmas. It converts Gosubs, On Goto/Gosubs, As Any parameters, As New variables and arrays with true auto-instancing semantics, default member references even in late-bound mode, deterministic finalization for IDisposable objects, arrays with any LBound, all 60+ controls in VB6 toolbox, ADO/RDO/ADO databinding, graphic methods, user-defined system coordinates, OLE drag-and-drop events, and many other features that the Upgrade Wizard doesn’t support.

Generating C# code would be a step in the wrong direction for us, because we couldn’t support several of the above features and we would therefore deliver lower quality code. We aren’t interested in that.

UPDATED (June 3, 2011): never say never!... two years after this post was written, Visual C# 2010 introduced many interesting new features that make the migration from VB6 less error-prone. The first one is a (limited) form of late-binding (sort of), based on dynamic variables; the second one is the support for optional parameters. As a matter of fact, we have changed our mind and are working at a very innovative VB6-to-C# converter that works around the residual C#-related migration issues and still generate maintainable and efficient code. Stay tuned!

The second part of the sentence isn't correct, though. We don't say that it is irrational to convert a VB6 application to C# rather than VB.NET. We only claim that doing it in one single step using an automated conversion tool isn't a great idea for many reasons. Explaining these reasons requires a lengthy and detailed article, thus I thought I’d better post it here than just replying to the original post.

Comparing languages is a fascinating topic and I'd like to thank the author of the original post for giving me the excuse to explore it in depth. Please notice that I am not making any assumption on our competitor's VB6-to-C# converter: I never used it and they don't publish any converted VB.NET or C# application, as we do in our Code Samples section. Therefore this article is just an overview of some obvious issues that you have when converting VB6 code directly to C#.

An important disclaimer:  Let me emphasize that I don’t mean to compare the power of VB6/VB.NET vs. C#. The only purpose of this article is to demonstrate that there are VB6 peculiarities that can’t be easily migrated to C# code without loosing either readability or functional equivalence, especially if you use an automated code converter. In the best case you end up bloated and unreadable C# code, in the worst case you get C# code that doesn’t behave exactly like the VB6/VB.NET code you start with.


COMPARING VB6, VB.NET AND C#

VB.NET and C# languages aren't perfectly equivalent, therefore if you convert directly from VB6 to C# you nearly always lose something. (I wrote seven books on VB6, VB.NET and C# for Microsoft Press, thus I know what I am saying...) The best examples of this non-equivalence are the On Error statements and late binding. For example, consider this simple VB6 method that copies 10 files using the FileSystemObject type library:

Sub CopyFiles(ByVal throwIfError As Boolean)
    If Not throwIfError Then On Error Resume Next
    Dim fso As New FileSystemObject
    fso.CopyFile "sourcefile1", "destfile1"
    fso.CopyFile "sourcefile2", "destfile2"
    fso.CopyFile "sourcefile3", "destfile3"
    ' seven more CopyFile method calls …
End Sub

VB.NET fully supports the On Error Resume Next statement, therefore this code translates to VB.NET as-is, except for the parenthesis around method arguments. Let’s see now the “equivalent” C# implementation:

void CopyFiles(bool throwIfError)
{
    Scripting.FileSystemObject fso = new Scripting.FileSystemObjectClass();
    try
    {
        fso.CopyFile("sourcefile1", "destfile1", true);
    }
    catch
    {
        if (throwIfError)
        {
            throw;
        }
    }
    try
    {
        fso.CopyFile("sourcefile1", "destfile1", true);
    }
    catch
    {
        if (throwIfError)
        {
            throw;
        }
    }
    try
    {
        fso.CopyFile("sourcefile1", "destfile1", true);
    }
    catch
    {
        if (throwIfError)
        {
            throw;
        }
    }
    // seven more try-catch blocks
}

We started with a VB6 method containing 12 executable statements and ended up with a C# method of 111 statements, nearly 10x bigger! You can’t always have concise C# code if your main goal is functional equivalence with the original VB6 code, sorry!

Also, notice that the C# code can’t omit the third (optional) argument in the FileSystemObject.CopyFile method, which makes the code even more verbose. Just imagine what happens when working with methods that have many optional parameters, such as those exposed by Microsoft Office type libraries! (Note that I purposely avoided to convert the FileCopy method into System.IO.File.Copy, because the focus here is on error handling, not file operations.) 

Don’t miss another important point: I translated the VB6 code to C# manually. An automatic translator tool might fail to deliver code that is truly equivalent and might, for example, bracket all the statements in a single try-catch block and add a warning about the different semantics and behavior. The text of such a warning may vary, but it would be a variation of the following sentence:  “My job ends here, it’s up to you ensuring that I didn’t mess things up.”Laughing

You might believe that the problem surfaces only with the On Error Resume Next statement, and that converting the On Error Goto <label> command can be easily translated to equivalent C# code. Alas, this isn't the case if you have multiple On Error Goto statements in one method, or if you use the Resume Next statement, or if you turn error handling on and off by alternating On Error Goto <labal> and On Error Goto 0 commands. Code of this sort can be a nightmare even for the bravest developer, and I doubt a VB6-to-C# convert can handle it correctly. Of course, you don't have any problem if you convert to VB.NET, because all these variants are fully supported by VB.NET.

By the way, if you are curious about how often you’ve used On Error statements it’s time to run our free VB6 Bulk Analyzer tool. You’d be surprised!

Let’s now tackle late binding. You can use late-binding in both VB6 (always) and in VB.NET (if Option Strict is Off), therefore translating the following VB6 code to VB.NET is a trivial task for both humans and automatic translators:

Sub IncrementValue(ByVal obj As Object, ByVal valueBeingAdded As Object)
    obj.Value = obj.Value + addedValue
    obj.Refresh
End Sub

C# doesn’t support late binding, thus there is no easy way to render this code into C# short of using reflection. This is how I’d translate it, after adding a variable and a few remarks to make it more readable:

void IncrementValue(object obj, object valueBeingAdded)
{
    // read Value property
    object value = obj.GetType().InvokeMember("Value",
       
System.Reflection.BindingFlags.GetProperty, null, obj, null);
    // increment it (assumes it's a double)
    object newValue = Convert.ToDouble(value) + Convert.ToDouble(valueBeingAdded);
    // assign back to Value property
    obj.GetType().InvokeMember("Value",
        System.Reflection.BindingFlags.SetProperty, null, obj,
        new object[] { newValue });
    // call the Refresh method
    obj.GetType().InvokeMember("Refresh",
        System.Reflection.BindingFlags.InvokeMethod, null, obj, null);
}

At least, this C# code is perfectly equivalent to the original VB6 code, right?

Wrong. In fact, the VB6 "+" operator also works with strings, therefore I can’t blindly assume that the Value property is numeric. As a matter of fact, when working with late binding you can’t assume anything about the objects you work with. Here’s the new version that works with strings and numeric properties:

void IncrementValue(object obj, object valueBeingAdded)
{
    // read Value property
    object value = obj.GetType().InvokeMember("Value",
        System.Reflection.BindingFlags.GetProperty, null, obj, null);
    // either increment it (assumes it's a double) or append the string
    object newValue;
    if (value is string)
    {
        newValue = Convert.ToString(value) +
            Convert.ToString(valueBeingAdded);
    }
    else
    {
        newValue = Convert.ToDouble(value) +
            Convert.ToDouble(valueBeingAdded);
    }
    // assign back to Value property
    obj.GetType().InvokeMember("Value",
        System.Reflection.BindingFlags.SetProperty, null, obj,
        new object[] { newValue });
    // call the Refresh method
    obj.GetType().InvokeMember("Refresh",
        System.Reflection.BindingFlags.InvokeMethod, null, obj, null);
}

Do we now have a piece of C# that is perfectly equivalent to the original VB6 code? The answer is again no, because there are many other details that I should account for, including:

a) I assumed that Value is a property – the C# code fails if Value is a class field, unlike VB6 and VB.NET
b) I assumed that Refresh has no parameters – the VB6 and VB.NET code works correctly even if Refresh has one or more optional arguments, but the C# code fails in this case
c) I didn’t care about error codes: if an overflow occurs or if the object doesn’t expose the Value or Refresh members, I should ensure that the caller code sees the same error code that it would see under VB6 or VB.NET.

In the (very unlikely) hypothesis that you never, ever used On Error Resume Next statements and late binding calls, let’s see an example that uses two nested loops and leverages the fact that the Exit statement allows you to specify whether you want to leave the Do loop or the For loop. (I could have made the example more complex by adding a While…Wend loop.)

Sub FindElement(arr() As Integer, ByVal value As Integer)
    Dim row As Integer, col As Integer
    ' outer loop
    For row = 0 To UBound(arr)
        ' inner loop
        col = 0
        Do While col <= UBound(arr, 2)
            If arr(row, col) >= value Then Exit For
            col = col + 1
        Loop
    Next
    Debug.Print "FOUND AT ROW=" & row & ", COL=" & col
    ' additional code here...
End Sub

This code converts to VB.NET without any problem, but requires some extra work to convert to C#:

void FindElement(int[,] arr, int value)
{
    int row = 0;
    int col = 0;
    // outer loop
    row = 0;
    while (row < arr.GetUpperBound(0))
    {
        // inner loop
        col = 0;
        while (col < arr.GetUpperBound(1))
        {
            if (arr[row, col] >= value)
            {
                goto ExitOuterLoop;
            }
            col++;
        }
        row++;
    }
ExitOuterLoop:
    System.Diagnostics.Debug.WriteLine("FOUND AT ROW=" + row.ToString()
        + ", COL=" + col.ToString()); 
    // additional code here...
}

Aarghh! The dreaded GoTo! I like concise and readable code, but I surely don’t want my code to be infested by Goto statements, therefore I rewrote the code the way most C# developers would do:

void FindElement(int[,] arr, int value)
{
    int row = 0;
    int col = 0;
    bool found = false;
    // outer loop
    row = 0;
    while (row < arr.GetUpperBound(0))
    {
        // inner loop
        col = 0;
        while (col < arr.GetUpperBound(1))
        {
            if (arr[row, col] >= value)
            {
                found = true;
                break;
            }
            col++;
        }
        If ( found )
        {
            break;
        }
        row++;
    }
    System.Diagnostics.Debug.WriteLine("FOUND AT ROW=" + row.ToString()
        + ", COL=" + col.ToString()); 
    // additional code here...
}

Not counting remarks, we started from a 11-line VB6 method and ended up with a C# method containing 26 lines, a 236% “improvement.” 

To recap: I picked three simple VB6 examples that use common features – error handling, late-binding, and nested loops- and in all cases I ended up with more verbose and less readable C# code, in spite of all my attempts to make the C# code look like “native” C#. Never forget that the situation can only get worse if the translation is performed by means of a software tool.

The bottom line: converting from VB6 to “equivalent” C# often delivers ugly, verbose, unmaintainable code or code that isn’t perfectly equivalent to the original VB6 code.

In some cases you can come up with C# code that approximates the original VB6 code’s behavior, but that’s a completely different story. When migrating an application with 1 million code lines, I don’t want to worry about the thousands of occurrences of On Error statements or late-bound calls. Not to mention other important differences such as arrays with any LBound, auto-instancing (As New) variables and arrays thereof, fixed-length strings, and so forth.


USING VB6-TO-C# CONVERTERS (aka ONE DOUBLE-JUMP)

Once again, it is important to bear in mind that in all previous examples I made a manual translation from VB6 to C# and came up with the most efficient and less verbose C# that is equivalent to the original VB6 code. Sure, you can drop a few curly braces and merge a couple of lines into a single statement, but I would be surprised if it were possible to significantly improve the C# code seen above.

Actually, I would be very surprised if a VB6 conversion tool could be able to generate the kind of C# code that a human developer would write. My guess is that in the best cases you’d see many calls to support methods – e.g. to work around late-binding deficiencies or simulate methods in the VB library – and a lot of code that just doesn’t “look like” C#.

In the worst (and more frequent) cases, you’d obtain C# code that isn’t perfectly equivalent to the original VB6 code and that forces you to carefully test each and every method that uses error handling, late binding, auto-instancing (As New) variables, arrays, file operations... in other words, virtual every single statement in your application.

If you are switching to C# with the purpose of having well-structured and readable code that performs at least as well as the original VB6 code, you are not going to achieve this goal by means of an automatic VB6-to-C# converter.

USING VB.NET-TO-C# CONVERTERS (aka TWO SINGLE-JUMPS)

At times you really need to convert from VB6 to C#. My recommendation is that you should try hard to convince your (internal or external) customer that translating to C# gives you more headaches than benefits. …but for the sake of discussion, let’s say that you have no choice and that C# is the target language.

Even in such case, a double-jump from VB6 to C# using an automatic converter isn’t the best option, both for technical and business reasons. It is much more rational jumping from VB6 to VB.NET and then using a VB.NET-to-C# converter to get the final result.

I built my opinion on many facts:

a) translating to VB.NET is simpler and faster – either using the free Upgrade Wizard that comes with Visual Studio or a commercial tool such as our VB Migration Partner. You can have a prototype sooner and you can bring a first version to the market in a fraction of the time it takes to just have a C# project that compiles without errors.

b) if you have a group of VB6 developers, odds are that they are already familiar with VB.NET. If they aren’t, they can learn VB.NET quickly because the two languages are similar. By comparison, becoming proficient with C# is a longer and more expensive process. By converting to VB.NET first you leverage the experience of your developers and reduce overall migration costs. (Don’t forget that C# developers usually ask for more money.)

c) There are several VB.NET-to-C# converters on the market, including the exceptional Instant C# (by Tangigle Software Solutions) and C-Sharpener for VB by Elegance Technologies. They deliver the best C# code that a software can automatically do. The companies behind these tools have just one mission: translating from VB.NET to C# in the best way, and in fact they are often updated and produce code that is more and more optimized and each new release. I use one of them and I am entirely satisfied.

d) from the business perspective, it is more convenient to purchase two separate tools, a VB6-to-VB.NET converter and a VB.NET-to-C# converter, because you can use the latter in other circumstances. If you have a tool that converts from VB6 to C# in one step, you can't use it to convert the VB.NET code that you happen to have already (for example ASP.NET pages) or that had to be translated manually from VB6 because of some deficiency in the migration tool.

e) speaking of convenience, the Standard Edition of both Instant-C# and C-Sharpener for VB cost less than 200$, which is a real bargain considering how useful they can be in many situations. This price is negligible if compared with the total cost of the migration of a real-world VB6 business application.

One might object that using a single integrated tool is better than using two distinct converters. But consider this: our VB Migration Partner converts at about 18,000 lines per minute; one of these converters splits out code at about 15,000 lines per minute; both of them come with a batch version, therefore you can easily automate your VB6-to-C# conversions. By combining them you can migrate a 100,000 line VB6 project to C# in about 12 minutes, 2x or 3x faster than what it takes to the Upgrade Wizard to perform just the first jump to VB.NET.

A common VB6 misconception... and how to cure it

$
0
0

One of our users is facing the migration of a large VB6 application and found that the coders often declared local variables using this "concise" syntax:

    Dim x1, x2, y1, y2 As Integer

It is evident that the original intention was to declare four Integer variables. The truth is, this statement declares only y2 as an integer variable, whereas the type of x1, x2, and y1 variables is affected by Defxxx statements (e.g. DefInt or DefDbl). If the current file contains no Defxxx statement, then the type of these variable is Variant. This was a common misconception among less experienced VB6 developers or developers that grew up with C or C++.

Both the Upgrade Wizard and VB Migration Partner convert the first three Variant variables into VB.NET "As Object" variables, which is formally correct but isn't what the original developer meant. Our user asked whether there was anything he could do about it. Quite surprisingly, VB Migration Partner offers a neat and elengant solution to this issue.

In fact, if you are migrating a VB6 application written by developers who consistently applied this sloppy coding practice, you can help VB Migration Partner to restore the intended data type by means of one or more PreProcess pragmas. More precisely, you need one pragma to account for Dim statements with two variables, another pragma to account for Dim statements with three variables, and so forth:

'## PreProcess "\b(?<kw>Private|Public|Dim|Static)\b\s+(?<v1>\w+)\s*,
\s*(?<v2>\w+)\s+As\s+(?<type>\w+(\.\w+)?)",
"${kw} ${v1} As ${type}, ${v2} As ${type}", true

'## PreProcess "\b(?<kw>Private|Public|Dim|Static)\b\s+(?<v1>\w+)\s*,
\s*(?<v2>\w+)\s*,\s*(?<v3>\w+)\s+As\s+(?<type>\w+(\.\w+)?)",
"${kw} ${v1} As ${type}, ${v2} As ${type}, ${v3} As ${type}", true

'## PreProcess "\b(?<kw>Private|Public|Dim|Static)\b\s+(?<v1>\w+)\s*,
\s*(?<v2>\w+)\s*,\s*(?<v3>\w+)\s*,
\s*(?<v4>\w+)\s+As\s+(?<type>\w+(\.\w+)?)",
"${kw} ${v1} As ${type}, ${v2} As ${type}, ${v3} As ${type}, ${v4} As ${type}", true

'## PreProcess "\b(?<kw>Private|Public|Dim|Static)\b\s+(?<v1>\w+)\s*,
\s*(?<v2>\w+)\s*,\s*(?<v3>\w+)\s*,\s*(?<v4>\w+)\s*,
\s*(?<v5>\w+)\s+As\s+(?<type>\w+(\.\w+)?)",
"${kw} ${v1} As ${type}, ${v2} As ${type}, ${v3} As ${type},
 ${v4} As ${type}, ${v5} As ${type}", true

Notice that these pragmas account for variables and class fields declared with Dim, Static, Public, and Private keywords. You can easily create similar PreProcess to account for more than five variables in a single line.

When these pragmas are used, the above Dim statement is expanded into the following VB6 code immediately before the migration begins:
    Dim x1 As Integer, x2 As Integer, y1 As Integer, y2 As Integer
which, of course, is translated to VB.NET as:
    Dim x1 As Short, x2 As Short, y1 As Short, y2 As Short

Important Note: Keep in mind that these pragmas affect may mistakenly affect the type of variables that SHOULD remain Object variables. Always double check that the resulting VB.NET code works as intended.

Problem solved! 

Update (October 3, 2008): Marco Giampetruzzi (from the VB Migration Partner Team) found a simpler and better way to solve the same problem, as you can read here.

Subtle threading issues and how to fix them (automatically) with VB Migration Partner

$
0
0

One customers brought an issue to our attention. He was using a migrated VB.NET component in a heavy multi-threaded environment (the component is called by ASP.NET pages) and found that occasionally the program crashed with an unexplainable error message. A quick look at the stack trace confirmed the initial suspect: the error was caused by two threads that initiates a call to the same method at the same time. We have fixed the problem in our library in a few minutes (the fix will appear in version 1.10.03, due in a few days), but we realized that the problem isn't confined to our library. To explain why, a short digression is in order.

VB6 components use Single-Threaded Apartment (STA) model: each thread lives in an “apartment” of its own. In plain English, this means that each thread works with a different set of global variables. For example, assume that the ActiveX DLL project contains a BAS module with the following variable declaration:

    ' ...(in the UserInfo.bas file)
    Public UserName As String


Thanks to STA threading model, each client – running in a different thread – can write this variable and read it back, without worrying about another client (in another thread) overwriting the variable with a different value. As you see, STA makes multi-threading programming virtually as easy as single-thread (i.e. traditional) programming. Of course,when writing multi-threaded apps you have to take care of many other potential concurrency issues – for example, two threads working on the same temporary file or registry key – but at least you don’t need to worry about global variables.

By contrast, components written in VB.NET – and all .NET languages, for that matter – use free-threading, which means that all threads can access all variables at the same time. Therefore, the VB.NET component exposes the same UserName field to all threads, which means that any thread can overwrite the value stored there by another thread.

To simplify programming in free-threaded contests, the .NET Framework supports the ThreadStatic attribute. You can apply this attribute only to static class fields – either fields defined in modules or fields defined in classes and marked with the Shared kewword:

    <ThreadStatic()> _
    Public UserName As String


All fields marked with the ThreadStatic attribute are stored in the Thread Local Storage (TLS) area. The separation among threads is automatic, because each thread owns a different TLS.

In theory, when migrating a VB6 DLL meant to be used by free-threaded clients, you should scrutinize each and every variable in BAS modules and decide whether to tag them with the ThreadStatic attribute. To make this task as easy as possible, as well as to avoid any manual edit of the resulting VB.NET code, starting with version 1.10.03 VB Migration Partner supports the ThreadSafe pragma, which can be applied at the project-, file-, method- and variable-level scope. In most cases, therefore, all you need to do is using a single pragma that affects all the variables in the current project:

    '## project:ThreadSafe True
    Public UserName As String

Accomplishing the impossible: VARPTR in VB.NET!

$
0
0

VB6 supports three functions – VarPtr, StrPtr, and ObjPtr – that were never officially supported by Microsoft, yet they have been extensively used by many expert VB6 developers. In fact, you badly need these functions when calling some especially complex Windows API functions. Of these functions, the most useful one is VarPtr, which returns the address of the memory location where the value of a variable is stored.

Neither the Upgrade Wizard nor any other VB6 migration tool supports the VarPtr keyword, thus I decided that our VB Migration Partner *had* to correctly convert it. Thanks to Google, I found out that the problem had been discussed at length in many forums, but no definitive solution has ever been found yet. You can write an implementation in unsafe C#, or an unmanaged DLL written in C or Delphi, but these solution would force us to distribute a separate DLL with VB.NET apps converted by VB Migration Partner, which I’d rather not to.

It took a while and a lot of thinking, but in the end I figure out a way to solve the problem. Yes, it is possible to write a VarPtr function in plain VB.NET, with only the help of a method exposed by the Windows API. Actually, you need just a few lines of code:

' -----------------------------------------------------------
' VARPTR implementation in VB.NET
' Part of VB Migration Partner’s support library
'
' Copyright © 2009, Francesco Balena & Code Architects
' -----------------------------------------------------------

Module VarPtrSupport
   ' a delegate that can point to the VarPtrCallback method
   PrivateDelegateFunction VarPtrCallbackDelegate( _
     
ByVal address AsInteger, ByVal unused1 AsInteger,_
      ByVal
unused2 AsInteger, ByVal unused3 AsIntegerAsInteger


   ' two aliases for the CallWindowProcA Windows API method
   ' notice that 2nd argument is passed by-reference
   PrivateDeclareFunction CallWindowProcLib"user32"_
      Alias
"CallWindowProcA" _
      (ByVal wndProc As VarPtrCallbackDelegate, ByRef var AsShort, _
      ByVal unused1 AsInteger,ByVal unused2 AsInteger,  _
     
ByVal unused3 AsInteger) AsInteger

   PrivateDeclareFunction CallWindowProc Lib"user32" _
      Alias"CallWindowProcA" _
      (ByVal wndProc As VarPtrCallbackDelegate, ByRef var AsInteger, _
      ByVal unused1 AsInteger,ByVal unused2 AsInteger, _
     
ByVal unused3 AsInteger) AsInteger
   ' ...add more overload to support other data types...

   ' the method that is indirectly executed when calling CallVarPtrSupport
   ' notice that 1st argument is declared by-value (this is the
   ' argument that receives the 2nd value passed to CallVarPtrSupport)

   PrivateFunction VarPtrCallback(ByVal address AsInteger, _
        
ByVal unused1 AsInteger, ByVal unused2 AsInteger,_
         ByVal unused3 AsInteger) AsInteger
      Return address
   EndFunction


  
' two overloads of VarPtr
   PublicFunction VarPtr(ByRef var AsShort) AsInteger
      Return CallWindowProc(AddressOf VarPtrCallback, var, 0, 0, 0)
   EndFunction

   PublicFunction VarPtr(ByRef var AsInteger)AsInteger
      Return CallWindowProc(AddressOf VarPtrCallback, var, 0, 0, 0)
   EndFunction

   ' ...add more overload to support other data types...
End Module

To understand how the trick works, let’s see what happens when you call the VarPtr method. The only line of code in this method invokes one of the overloads of CallWindowProc method. The CallWindowProc method takes five arguments, the first one of which is a delegate that must point to a method that takes four 32-bit values. CallWindowProc invokes the method pointed to by the delegate and passed the other four values to such a method.

The key point in this mechanism is that each CallWindowProc overload takes a value by-reference in its second argument – a Short and an Integer, respectively. This means that the CallWindowProc method (buried inside the User32.dll) receives the address of the Short or Integer variable. This address is a 32-bit integer and is passed verbatim to the VarPtrCallback method. This method in turn receives a 32-bit integer value with by-value semantics, which means that the address parameter now contains whatever value was pushed on the stack by the CallWindowProc method.

Let’s quickly recap: the VarPtr method pushes the address of the Short or Integer variable – that is, the value we are interested in – on the stack. This 32-bit integer is received by the CallWindowProc method (in User32.dll) and is sent to the VarPtrCallback method, which receives it in its first argument and returns it verbatim to the CallWindowProc method, which in turn returns it to the VarPtr method that can finally return it to the caller.

Notice that you might need to add more overloads for the VarPtr method (and the CallWindowProc method), to support data types other than Short or Integer. Just remember that you can’t use this technique with String, Objects, or other reference types. It doesn’t work with Boolean values, either.

Interestingly, you can use the VarPtr method with structures, provided that the structure doesn’t contain any String, Object, or Boolean elements. To get the address of a structure just use VarPtr on its first element, as in this example:

Structure POINTAPI
   Public x AsInteger
   Public y AsInteger

EndStructure

Dim
pnt As POINTAPI
Dim addr AsInteger = VarPtr(pnt.x) 

To prove that the VarPtr function works correctly, let’s use it together with the CopyMemory Windows API method to delete an element in an array by quickly shifting all the elements towards lower indices:

DeclareSub CopyMemory Lib"Kernel32.dll"Alias"RtlMoveMemory" _
   (ByVal dest AsInteger, ByVal source AsInteger, _
    ByVal numBytes AsInteger)


Sub
Main()
   Dim arr(1000) AsInteger
   ' initialize the array
  
For n AsInteger = 0 To UBound(arr) : arr(n) = n : Next
   ' ...
   ' delete first element by shifting all elements towards the beginning
   '
of the array, then clear last element
   CopyMemory(VarPtr(arr(0)), VarPtr(arr(1)), UBound(arr) * 4)
   arr(UBound(arr)) = 0
   ' check that it worked fine
   For n AsInteger = 0 To UBound(arr) - 1
      If arr(n)<> n + 1 Then Debug.Print("Wrong value at index {0}", n)
   Next

End Sub

In case you are wondering why you should use CopyMemory and VarPtr to shift all the elements of an array – instead of a plain For … Next loop – the answer is: execution speed. Under VB6 this technique was often used to significantly speed up array operations; the VB.NET compiler produces more efficient code and this technique is seldom necessary, nevertheless the ability to convert this code from VB6 without any major edits means that you don’t have to spend too much time trying to understand what the VB6 developer meant to do.

Please notice that this implementation of VarPtr works well in 32-bit applications only, and fails when running on 64-bit operating systems. To ensure that things work as expected and that 32-bit code is generated even when running on 64-bit versions of Windows, you must select the Target CPU = x86 option in the Advanced Compile Options dialog box, in the Compile tab of the My Project page. (Odds are that you have to select this option anyway when doing complex operations with pointers.)

Important warnings: working with memory addresses under .NET can be very dangerous, much more dangerous than under VB6. The reason is, the garbage collector can fire virtually anytime while the program is executing, therefore the address of an object can suddenly change and the unmanaged method (CopyMemory in above example) would receive the address of a memory area that doesn't contain the data any longer. The neat result would be either a wrong value or an application crash. When using this implementation of VarPtr under VB.NET keep the following points into account:

  1. VarPtr is absolutely safe only when used to return the address of simple local variables, such as Short, Integer, Single, Double, or Date variables. Using local variables is safe because local variables are allocated on the stack and don't move even if an unexpected garbage collection occurs immediately after the VarPtr method returns but before the unmanaged method complete its execution.
  2. Passing the element of a Structure is safe, but only if the Structure is held in a local variable (as opposed to a class field)
  3. In all other cases, VarPtr isn't  100% safe and might occasionally deliver wrong results or crash the application. For example, it isn't 100% safe to pass VarPtr a class field or an element of an array, because array elements are stored in the managed heap and can be moved by the garbage collector (regardless of whether the array is stored in a local variable). 
  4. In a single-thread application the probability that a garbage collection occurs unexpectedly are very low and might even be considered as negligible, but they can't be considered as equal to zero.
  5. You can further minimize the probability of an unexpected GC by avoiding calling methods and language functions (e.g. Left, Int, Abs) inside the call to the unamanged method but, again, you can't reduce this probability to zero.

To recap, except when you are in cases #1 and #2 above, the converted code will work most of the time, but it can't be guaranteed to work always.The only documented way to ensure that an object doesn't move in memory because of unexpected gargabe collection is by pinning the object, by means of methods exposed by the System.Runtime.InteropServices.Marshal class.

Even with this limitation, the VB.NET implementation VarPtr method is quite helpful when doing a quick-and-dirty migration - using VB Migration Partner or the Upgrade Wizard. You can use VarPtr to check that the converted code works as intended, but it is strongly recommended that you get rid of VarPtr before going to production. In the CopyMemory case see above, for example, you can do without the VarPtr by using a different overload of the CopyMemory method that takes by-reference arguments:

 

DeclareSub CopyMemoryByref Lib"Kernel32.dll"Alias"RtlMoveMemory" _
   (ByRef dest AsInteger, ByRef source AsInteger, _
    ByVal numBytes AsInteger)

(This code works because the .NET Framework automatically pins every object passed by reference to an external method.)

Even better, you should do your best to avoid unamanged calls altogether and replace them with calls to methods of pure .NET objects.

Can your VB6 conversion tool handle null propagation?

$
0
0

If you have massively used Variants in your VB6 apps, the migration to .NET might be a nightmare. The best that the Upgrade Wizard and other tools can do is translating each “As Variant” into “As Object”, on the assumption that the Object type can do everything that a Variant does.

You shouldn’t be surprised to learn that this is one of the false migration myths, or is a gross oversimplification of the truth, to say the least. In fact, the .NET Object type lacks what arguably is the most useful feature of Variants: support for Null values and Null propagation in expressions.

Under VB6 a Variant variable can hold the special Null value and – even more important – all the string and math expression that involve a Null operand also deliver a Null Variant value. Virtually all database-intensive applications deal with Null values and have used null propagation to an extent. Now, let’s consider this VB6 code:

' rs is an ADODB.Recordset
Dim realPrice AsVariant
realPrice = rs("Price")
realPrice = realPrice * (100 – rs("Discount")) / 100
If Not IsNull(realPrice) Then DisplayPrice(realPrice)

Under VB6, if either the Price or the Discount field is Null, the realPrice variable is assigned Null and the DisplayPrice method isn’t invoked. Now, consider the “canonical” VB.NET version of the above code:

Dim realPrice As Object
realPrice = rs.Fields("Price").Value
realPrice = realPrice * (100 – rs.Fields("Discount").Value) / 100
If Not IsNull(realPrice) Then DisplayPrice(realPrice)

Under .NET a Null database field returns a DBNull object – more precisely, the DBNull.Value element. The problem is, no math or string operation is defined for the DBNull type, something I consider one of the most serious mistakes Microsoft made in this area. As a result, the third statement throws an exception if either field is Null. So long, functional equivalence! Welcome, migration head-aches!

Null propagation is a serious problem not to be underestimated. One of our customers has found that, on the average, one out of 40 statements uses Variant expression and relies on null propagation. To put things in the right perspective, in a middle-sized real-world application you can expect to manually fix several thousand statements that use null propagation. What’s worse, there is no simple way to work around Null values in VB.NET, short of splitting all statements in portion and testing each and every subexpression for the DBNull.Value value.

If you use VB Migration Partner, you can solve the above problem in the most elegant and painless way. Just use the following project-level pragmas:

 '## project:NullSupport
 '## project:ChangeType Variant, VB6Variant

The NullSupport pragma tells VB Migration Partner to map some VB library functions – such as Left, Mid, Right, and a few others – to special methods that are defined in VB Migration Partner’s support library and that, not surprisingly, correctly return a Null value if their argument is Null (as they do under VB6).

The ChangeType pragma converts all Variant members – variable, fields, properties, methods, etc. – as the special VB6Variant type (also defined in VB Migration Partner’s support library) which redefines all the math, comparison, string, and logical operators to support null values.

Yes, this is all you need to add null propagation support to your VB.NET code! This feature alone can save you literally weeks in a real-world migration project. And don’t forget that VB Migration Partner is the only VB6 conversion software that supports Null values and Null propagation easily and automatically.

When you hear other vendors claiming that “our tool uses advanced artificial-intelligence-based techniques to ensure functional equivalence with the original VB6 code”, just ask them how it deals with Null values. Smile

Pay attention to orphaned ADODB objects

$
0
0

Don't take me wrong: COM Interop is a great piece of technology, and Microsoft developers made wonders with it. If you consider how different the COM and .NET worlds are, it's a miracle that they can communicate with each other in such a smooth way.

Actually, COM Interop is such a magic that it's easy to forget that all the COM objects that we manipulate from .NET aren't the "real" COM objects. Instead, they are .NET wrappers that redirect all calls to the actual COM object by means of COM Interop. This fact has many implications. Consider for example this VB6 code:

Function GetRecordCount(ByVal cn As ADODB.Connection, ByVal sql As String) As Integer
   Dim rs As New ADODB.Recordset
   rs.Open sql, cn
   GetRecordCount = rs.RecordCount
End Function

As you see, the Recordset object isn't closed explicitly, but it isn't a problem in VB6 because all COM objects are automatically closed when they are set to Nothing or go out of scope. However, if you translate this code to VB.NET you are in trouble, because nothing happens when the method exits. In other words, you'll have an open recordset hanging somewhere in memory and cause unexplainable errors later in the application. For example, you'll get an error when you'll later try to open another recordset on the same connection.

We know that some customers had to work around this issue by enabling the MARS option with SQL Server, which enables multiple active recordsets on any given connection. This might be a viable solution if you are working with SQL Server 2005 or 2008, but VB Migration Partner offers a better way to handle this case: use the AutoDispose pragmas.

In fact, when this pragma is used, all disposable objects are correctly disposed of when they go out of scope. In this case, VB Migration Partner emits the following code:

Function GetRecordCount(ByVal cn As ADODB.Connection, ByVal sql As String) As Integer
   Dim rs As New ADODB.Recordset
   Try
      rs.Open(sql, cn)
      GetRecordCount = rs.RecordCount
   Finally
      SetNothing6(rs)
   End Try
End Function

where the SetNothing6 helper method takes care of orderly invoking the Close or Dispose method, if necessary.


A .NET library to access Windows Vista and Windows 7 features

$
0
0

One of the reasons to migrate your code to .NET is create modern user interface that take advantage of the power of newer versions of Windows. On the other hand, the .NET Framework has been designed to work in the same way over a wide range of Windows versions, including versions that are about ten years old, and therefore it doesn't provide access to the most intriguing features of Vista and Windows 7.

The solution comes in a new library from Microsoft, named Windows API Code Pack for .NET Framework. Version 1.0 of this great tool has been just released, and already implements an impressive range of Windows features:

  • Windows 7 Taskbar Jump Lists, Icon Overlay, Progress Bar, Tabbed Thumbnails, and Thumbnail Toolbars.
  • Windows 7 Libraries, Known Folders, non-file system containers.
  • Windows Shell Search API support, a hierarchy of Shell Namespace entities, and Drag and Drop functionality for Shell Objects.
  • Explorer Browser Control.
  • Shell property system.
  • Windows Vista and Windows 7 Common File Dialogs, including custom controls.
  • Windows Vista and Windows 7 Task Dialogs.
  • Direct3D 11.0, Direct3D 10.1/10.0, DXGI 1.0/1.1, Direct2D 1.0, DirectWrite, Windows Imaging Component (WIC) APIs. (DirectWrite and WIC have partial support)
  • Sensor Platform APIs
  • Extended Linguistic Services APIs
  • Power Management APIs
  • Application Restart and Recovery APIs
  • Network List Manager APIs
  • Command Link control and System defined Shell icons.
  • Shell Search API support.
  • Drag and Drop functionality for Shell objects.
  • Support for Direct3D and Direct2D interoperability.
  • Support for Typography and Font enumeration DirectWrite APIs. 

The library comes with an extensive help file and all code samples are available in both VB.NET and C#. Last but not the least, the entire source code is provided.

Two migration e-books all serious developers should read

$
0
0

In our quest to provide useful info to our users and readers, we periodically surf the Internet looking for articles, books, whitepapers, tools, etc. related to VB6 to .NET migration.

This week the search has been quite fruitful, and we have added two great items to our Articles page:

Navigating your way through Visual Basic 6.0 to Visual Basic.NET Application Upgrades
This 62-page e-book is one of the best collection of problems & solutions you can find on the Internet, with tons of detailed examples and code samples. It is obviously based on first-hand experience of the author with Microsoft Upgrade Wizard, and the solutions he proposes are valid and functional.

Visual Basic .NET Enhancements Compared to Visual Basic 6.
This e-book focuses on many differences between VB6 and VB.NET and in many cases it goes much deeper than the other articles mentioned in this page. It’s not the best place to start from when learning VB.NET, but it’s surely a must-read for the serious developer facing a complex migration project. (148 pages).

Both e-books are by David Ross Goben and are written in a clear and bright style. A must-read for all serious VB6 and VB.NET developers.

NOTE: Interestingly, virtually all the issues that David found when migrating his VB6 apps are handled automatically by our VB Migration Partner. (More on this in a subsequente post.)

Basta! 2010 slides available

Vote for a VB6 migration session at TechEd Europe 2010

$
0
0

I submitted a BoF session at TechEd Europe 2010, entitled "VB6-to-.NET Migration: Myths, Truths, and Real-World Experiences".

There is a poll on these Birds-of-Feathers sessions, and only the ones that get most votes will be accepted.

Regardless of whether you attend TechEd, you can help by voting for my session. In addition to having me speak at TechEd, a lot of votes on this session will hopefully convince Microsoft that VB6-to-.NET migration is a hot topic and that they should invest more in that direction.

A library for all .NET developers, free!

$
0
0

David McCarter, from www.dotNetTips.com, has released a new version of his open source .NET library, containing a lot of useful classes and methods. This new releases includes new features and bug fixes.

You can download it from here.

VB Migration Partner will support Visual Studio 2012

$
0
0

A few customers asked about our support for the forthcoming Visual Studio 2012, so here is the official announcement:

VB Migration Partner will add support for Microsoft Visual Studio 2012 within a few weeks after VS official release. This includes code generation for both VB.NET and C# and support for .NET 4.5.

[NEW RELEASE] Version 1.51 is available to registered users

$
0
0

Since early 2013 we have been distributing version 1.50 to selected customers, gathered their feedback and suggestions, and the result here: VB Migration Partner 1.51 is now ready.

 

There are tons of improvements in this new release, but the most important one is surely full-featured C# code generation. While our competitors are compatible with the C# language found in Visual Studio 2008, we decided to focus on C# 2010, which allows us to support modern C# features were not available in earlier language versions, most notably the dynamic type and optional parameters. These two single features, together with the many enhancements that only VB Migration Partner offers, let our users to create the most efficient and readable code a VB6-to-C# converter can possibly generate.

Here is a short and incomplete list of C# features offered by VB Migration Partner (and in many cases only by VB Migration Partner!):

C# specific

  • support for the dynamic type => faster and concise code
  • support for optional parameters => more readable code
  • ByRef parameters rendered using the out keyword if possible => faster code
  • the ability to generate method overloads for ref/out parameters avoids the need for temporary variables in method calls => concise and readable code
  • support for WithEvents variables => less verbose code
  • ability to implement an interface either implicitly or explicitly => compliance with your own programming style
  • transformation of On Error Goto into try-catch blocks (with warnings when the transformation isn't perfectly equivalent) 
  • transformation of On Error Resume Next into lambda expressions => full functional equivalence
  • preserves error codes => no need for manual fixes after migration
  • transformation of And/Or operators into either bit-wise or logical C# operators 
  • transformation of Select blocks into either if or switch blocks => more logical and efficient code 
  • Exit For,Do,While keywords in nested loops => code works as expected
  • expressions and function calls in compiler constants
  • string and math native .NET methods used if possible => compliance with .NET programming style 
VB.NET and C#
  • code generation for VS 2010 and VS 2012
  • support for .NET 3.5 and 4.x
  • improved support for User Control serialization in code-behind sections of forms => faster .NET form loading
  • simplified design-time and runtime support for scaling User Control at different screen resolution and system font sizes => no-brains compliance with .NET coding guidelines
  • positional pragmas: it is now possible to keep all pragma (including method-specific pragmas) in separated text files => less cluttered source files
  • automatici generation of AccessibleName property for .NET or custom controls => .NET apps ready for visually-impaired users
  • support for external source file editors => edit VB6 or .NET code with your favorite tool
  • many bugs fixed (of course!)

Each of these features (and of the many features that I didn't mentioned) would require a separate discussion, and I will surely devote some posts to the most interesting ones. 

For now, you can learn more about C# specific support here.

 


[INTERVIEW] Yours truly on .NET Rocks!

$
0
0

A few weeks ago I had an interesting and entertaining conversation with Carl Franklin and Richard Campbell of .NET ROCKS, about VB6 migration, the effect that Windows XP retirement is having on companies that can't afford keeping their mission-critical VB6 apps on an unsupported operating system, and a lot more. Yesterday the interview went online, at this URL

Happy listening!

Viewing all 16 articles
Browse latest View live