VB Migration Partner vs. manual porting from VB6

clock January 20, 2009 02:31

More and more VB6 and VB.NET developers are discovering VB Migration Partner and are realizing that it *is* possible to convert VB6 legacy apps in a fraction of the time (and money) that is required when following other approaches or using other tools. However, a casual visit to VB6/VB.NET forums shows that many false myths and misconceptions about migration are still strong.

For this reason, every now and then I like to partecipate to these discussion and throw in my own point of view. I think it makes sense to republish it here. The following text is adapted from a longer comment I posted toa thread entitled Conversion from VB6 to VB.NET 2005 on DevX Forums.


I believe that the confusion about automatic code translators raises because most people expect that they work magically without any human intervention. Alas, this is the approach used by the Upgrade Wizard inside VS and this explains why it performs so poorly. In most real-world cases you can't simply run the tool on a piece of contorted VB6 code and have fully working VB.NET code at the first attempt. Instead, a real-world migration process is an iterative task.

A code translator should migrate your code according to *your* personal style and preferences. VB Migration Partner allows this by means of so-called "migration pragmas", which are special remarks that you embed in the original VB6 code. For example, this pragma tells VBMP that special code must be emitted so that the VB.NET can correctly handle default members even in late-bound mode (which is one of the main limitations of Upgrade Wizard):
    '## project: DefaultMemberSupport True
VBMP supports 60+ different pragmas to automate virtually every adjustment that might be needed after an initial quick-and-dirty migration. Pragmas are essential because they allow you to repeat the migration many times and still keep the VB6 and VB.NET code in sync, even if the migration process takes months.

VB Migration Partner can handle many recurring migration issues quite nicely. In most cases it can do it without human assistance, in the worst case it requires that you add a pragma.

Quoting a previous forum comment: "If you are crazy enough to spend 3 months to make a conversion work with a loss of performance compare to the VB6 application, while a 2 months rewrite would do the job and give you something that easier to maintain, go ahead."

With small projects this approach can make sense, provided that you can afford the extra time/cost and that you have a team that is adequately skilled in both VB6 and VB.NET. One of our customers is tackling one "monster" app of about 15 million lines scattered nearly 1500+ VB6 projects; the customer has estimated that it would take about 20 years/man to rewrite it from scratch, which they can't afford. They also estimated that VB Migration Partner will cut cost and time-to-market by at least 70-75%, which made their choice obvious. [NOTE: it later became clear that the actual savings was closer to 85-90%. The migration should be completed in a few months.]

The real value of a good converter such as ours is that an individual developer (or a team) can handle the migration process even if they aren't expert in either the source or the target language. VB Migration Partner contains a huge database of "good programming" rules as well as "obscure bugs" that are unknown to most developers, including a few expert ones. Let me make one simple example:

    Dim buffer As String
    buffer = Space(256)
    ' GetSystemDirectory API returns # of chars in the result string
    buffer = Left(buffer, GetSystemDirectory(buffer, 256))

This code works well in VB6 but delivers bogus results when translated as-is to VB.NET. VB Migration Partner correctly points to the issue and its solution. The question is: How many VB developers are aware of this subtle difference between the two languages? You might object that this code isn't representative of good VB programming, yet there are tons of code like it out there, and most "human converter" aren't skilled enough to find and fix it. (Here I talk from personal experience: it took hours to track down the problem the first time we bumped into it.)

Let me make one more example. In VB6 the default parameter passing mechanism is ByRef, which means that most methods take a byref argument only because the original developer forgot to add an explicit ByVal keyword. The problem is, VB.NET should use ByVal whenever possible because this coding style prevents a lot of compile and runtime errors. (You can have problems when passing an ADODB object to a byref parameter, and there are many other similar cases)

A developer doing the port or the rewrite, therefore, should carefully check each byref parameter and see whether it can be converted into ByVal without changing the code behavior, but this task takes days on a real-world app, because you'd have to check whether *each* parameter is modified, or passed to a method that modifies it, and so on recursively. VB Migration Partner does it automatically in a matter of seconds, emits a warning, and can optionally convert the ByRef into ByVal for you.

The code analysis engine in VB Migration Partner can do many other interesting things. It can remove unused methods, Declare, and Const statements; replace Exit Function with Return, merge DIM and assignments (eg Dim x As Integer = 1), suggest when a StringBuilder object could replace a set of concatenations, and so forth. Once again, all refactoring actions are optional and can be controlled via pragmas. An average developer can surely perform these task manually, but in practice it happens quite seldom because he or she can hardly afford the extra time that these activities require.

I could continue with more examples of how an automatic code converter can do a better job than developers. Not in all cases, but in a large percentage at least. If you are interested, you can find all docs, KB articles, videos, and code samples at www.vbmigration.com. (We provide both the original VB6 code and the resulting VB.NET code.)

As far as I know, we are the only migration tool authors who dare to expose all this information to the public *before* they purchase the product. I hope potential customers or just programmers who are curious about conversion technology will appreciate this "transparency" policy.

[WHITE PAPER] 17 Reasons for using a support library in migration scenarios

clock December 31, 2008 07:04

Unlike the majority of the language conversion products available on the market – which mainly attempt to convert code from one language to another – VB Migration Partner both converts the code and uses a support library to decrease the gap between VB6 and VB.NET and to ensure that the converted code really behaves as expected. When combined with its superior code parser and generation engine, the support library becomes an essential factor in VB Migration Parter's superior performance and high success ratio.

Using a support library in a migrated project isn't different from, say, using a 3rd-pary ActiveX control or a .NET control in your code. VBers have used custom controls for years, even if their source code wasn't available or consisted of poorly documented C++ code. VB developers have used 3rd-party custom controls because they didn't want to reinvent the wheel, they didn't want to spend months just to build a powerful grid or chart control, and preferred to invest their time and money on developing the real application, not just the infrastructure.

A support library offers pretty much the same advantages: it allows you to cut the cost of the migration process and bring the migrated VB.NET to the market earlier. As a matter of fact, a support library provides at least 17 important benefits.

Interestingly, our competitors felt the urge to comment our approach, in the attempt to convince potential customers that a library is a very scary thing. Their arguments are a bit lame, though, if you consider that their tool uses a support library for specific tasks, except their library isn't as complete as ours.

The truth is quite different, of course: a rich support library provides so many advantages that it would be silly NOT to use it. The advantages are so many and so important that we wrote a detailed white paper to describe them. Read it, compare it with what our competitors opinion, and then decide for yourself.

Have a great new 2009! Cool

More on binary compatibility

clock December 20, 2008 06:11

A reader of a previous post on binary compatibility asked for clarification about my claim about VB Migration Partner being the the only conversion tool on the market offering this important feature, mentioning that at least another tool from our competitors also claim to offer this feature, which they obtain by adding ComVisible, ProgID, and ClassInterface attributes.

I am aware there is some margin for ambiguity and for uncorrect information, so I decided to drill a bit deeper on this topic.

You have binary compatibility between a COM component a .NET component if you can unregister the COM component and install the .NET component in its place, without having to recompile the COM clients that were using the COM component. We call it "binary compatibility" because it's the kind of compatibility we used to enforce under VB6, to ensure that a new version of a given component was perfectly compatible with its previous releases.

The ability to generate a VB.NET component that is binary-compatible with the original VB6 component is greatly useful, for many reasons:

  • You can migrate a complex N-tier application starting with its innermost DLLs, typically those that access the database, without having to recompile or modify in any way existing COM clients. 
  • The ability to convert only a subset of existing DLLs without being forced to migrate the entire application allows you to focus on those components that suffer most from the inherent scalability and performance limitations imposed by COM. This is the so-called phased migration.
  • Just as important, you can immediately debug the converted DLLs because you can use existing COM clients and their user interface - no need to write small throw-away .NET projects with the only purpose of testing the DLLs you just converted.
  • Finally, if your organization is very serious about testing, odds are that you have defined unit tests, use cases, and maybe scripts that rely on commercial testing tools. The great news is that, in most cases, binary compatibility allows you to test the converted DLLs using existing unit tests and scripts, thus saving you even more time.

As you see, binary compatibility can be a tremendous time-saver when migrating a VB6 application that is split in many DLLs, and it's no surprise that a migration tool vendor would like to claim that its product offers this feature.

Unfortunately for our competitors, to generate a .NET class that is binary-compatible with the original VB6 class you need much more than three attributes. You have to extract GUIDs from the original DLL and apply them correctly to the generated .NET class, you must handle fields, enums, types, events, and interfaces with special care, and you have to pay attention to many special cases and exceptions.

More precisely if you just add the ComVisible, ProgID, and ClassInterface attributes, the best you can get is compatibility with existing VBScript and ASP clients, not with compiled COM clients 

The bottom line: based on what I can read and what I heard from customers, I can only reinforce my claim that only VB Migration Partner offers true backward binary-compatibility with existing COM clients and fully supports phased migrations of N-tier VB6 apps.

Exploring version 1.10 - Gosubs Refactoring

clock October 13, 2008 09:02

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

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

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

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

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

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

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

    '## project:ConvertGosubs True

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

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

    GetValue = x + Len(s) + Len(name)
End Function

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

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

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

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

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

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

    '## ConvertGosubs True, True

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

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

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

    '## PostProcess "Gosub_GetValue_BuildResult", "AssignValueResult"

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

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

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

Exploring version 1.10 - The rename engine

clock October 10, 2008 07:50

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

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

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

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

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

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

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

    '## ApplyRenameRules  c:\myrules.xml

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

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

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


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

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

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

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

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

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

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

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


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

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

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

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

Public Class MyRenameRules
   Inherits SpecialRuleBase

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

It couldn't be simpler, uh?

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

Announcing VB Migration Partner 1.10 - the refactoring release!

clock September 21, 2008 10:26

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

code migration that just works

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Comparing VB Migration Partner with Upgrade Wizard

clock August 27, 2008 08:27

A few potential customers asked for a detailed comparisons of our VB Migration Partner with Upgrade Wizard, the conversion tool provided for free inside Microsoft Visual Studio, in terms of compilation errors and warnings. Instead of just providing a series of unverifiable numbers we run both tools against a group of open source code samples which offer a variety of challenges, including rarely-used controls, data-binding, graphic methods, and drag-and-drop.  We benchmarked the Upgrade Wizard installed with Microsoft Visual Studio 2005 and the forthcoming release 1.00.06 of VB Migration Partner, on a Intel Core Duo 7800 @ 2.66GHz running Microsoft Windows Vista 64-bit SP1.

The results are available in a Microsoft Excel spreadsheet for all to see.

A summary of the results:

  • VB Migration Partner generates nearly 5x fewer compilation errors than the Upgrade Wizard before adding a single pragma. In most cases just one or two pragmas are sufficient to generate an error-free application.
  • Considering all the pragmas used in all samples delivers we get an average of one pragma every 430 line of code, but in a real application you need even fewer pragmas because a single pragma can affect all the files and statements in the project.
  • The Upgrade Wizard processes about 46 LOCs per second on the average, VB Migration Partner runs at 187 LOCs per second, i.e. 4 times faster. However, these timings are misleading, because at the end of the process VB Migration Partner generates code statistics and reports that the Upgrade Wizard doesn't.
It should be noted that compilation errors and warnings tell only a part of the story, because even a VB.NET project with zero compilation errors might raise one or more runtime errors. This means that even the samples that show few compilation errors after the migration with the Upgrade Wizard (e.g. CodeLibrary or AldInvaders) might require a lot of additional work to run correctly. By comparison, the column labeled as Number of pragmas indicates how many pragmas were neeed to have a fully functional VB.NET application that raises no runtime errors

Laughable business practices - Part 2

clock August 15, 2008 16:22

In case you haven’t read my last post, here’s a short recap. Some days ago Mauricio Rojas of Artinsoft publicly claimed that my knowledge in code translation is “disappointing”. Mauricio didn’t provide any fact to support his words, however he announced a new series of articles named “VB Migration not for the weak of mind” where he would prove his point.

Mauricio has finally published the first two articles of the series, one on the translation of On Error Resume Next statement to C# and the other on implementing late-binding in C#. Both articles are filled with mistakes, so a few hours ago I posted a comment for each of them.

Neither comment has appeared yet on Mauricio’s blog, but I hope it’s just a matter of time. If you publicly denigrate someone you should give him the opportunity to answer and illustrate his point of view. I am sure Mauricio doesn't ignore that transparency and intellectual honesty is the very essence of blogging.

In the meantime, I’ll expose my ideas and criticisms here. The text here is slightly different from my original (yet unpublished) comments to account for the different context.

Update (Aug 25): not only Mauricio never published my comments, he went as far as deleting one of the two articles. You can read the whole story and read an offline copy of his posts here.


My comment to “VB Migration (not for the weak of mind) Part 2

I read with interest Mauricio’s funny stories about human language dialects. I am sure that everybody agrees that it's easier to translate from Russian to Ucranian rather than to English or Spanish, even though both tasks can be successfully completed by a human that is skilled enough.

But the analogy is pointless in this specific context: Mauricio’s article was supposed to focus on *automatic* translation between *programming* languages, not manual translation between natural languages. It's like comparing apples to bicycles, for those who like analogies.

Mauricio announced he wanted to prove me wrong and that he knows how to automate the translation of *any* VB6 method to a piece of C# that is functionally equivalent. I don't see anything like that in this article. I only see some advice about what you should or shouldn't do in VB6 or C#, which is a completely different matter.

For example, we all know that On Error Resume Next can bring to hard-to-find bugs, nevertheless millions of VB6 projects use this statement and a conversion tool should account for this fact. The article was supposed to show how a VB6-to-C# translator can do it. We'll have to wait for a long time, I am afraid.

The real pearl in this article is where the author states that:

When you do a translation, you should try to make it as native as possible. So why will you keep using a COM function when there is an equivalent in .NET. So why not use System.IO.File.CopyFile("sourcefile1", "destfile1", true); instead?

I am sorry to inform Mauricio that he cannot replace the FileSystemObject.CopyFile method with a System.Io.File.CopyFile method for at least three reasons:

  1. the System.IO.File class doesn't even expose such a method Wink... or maybe he was thinking of the Copy method? (Tip: never trust your memory when writing a technical article...)
  2. the System.IO.File.Copy method doesn't accept wildcards in its first argument, whereas the FileSystemObject.CopyFile method does.
  3. the System.IO.File.Copy method requires a complete file name in its 2nd argument, whereas the FileSystemObject.CopyFile method also accepts a folder name.

If you buy a conversion tool converts calls to the FileSystemObject.CopyFile method into calls to the System.IO.File.Copy method, then you have to carefully check that the original VB6 code never uses wildcards or folder names. If the method is located inside a library or in a source file that is shared by hundreds different VB6 projects you have to scrutinize each and every project. In a real-world application this job alone can take hours.

We at Code Architects are aware of these subtle differences, which Mauricio apparently ignores. To spare our customers’ time and effort, our VB Migration Partner tool converts the CopyFile method into a call to a helper object that behave *exactly* like the original FileSystemObject class, except it's fully .NET and has no COM dependencies. If you use a conversion tool that takes a different approach we can only be sorry for you.


My comment to “Late-binding migration to C#

In this article Mauricio introduces the following VB6 code snippet to show how a converter can translate VB6 late-bound methods:

Sub ChangeValue(x As Object, newValue As Object)
    x.Value = newValue
End Sub

Sub Test()
    ChangeValue Me.Button1, False
End Sub

A VB6-to-C# converter can translate late-bound code in basically two ways: by using .NET reflection or by generating an overloaded method for each possible argument type. Mauricio shows the output of a code translator that takes the latter approach:

void ChangeValue(Button x, object newValue)

void Test()
    ChangeValue(this.Button1, false);

Unfortunately for Mauricio, this is the poorest example he could ever think of. In fact, the only thing that it proves is that people should never write - or be allowed to write - technical articles if they are too lazy to double-check every single code snippet that they publish. In fact

  1. If he had tested the VB6 code he would have realized that it cannot run at all, because you can’t pass False to the newValue Object parameter. (Of course, that parameter must be declared as Variant, not Object.)
  2. The original VB6 code never triggers the button's Click event, because the Value property is assigned False. If he had tested the equivalent (!) C# code he would have realized that it behaves exactly in the opposite way, in that it always raises the event! A converter which blindly converts all assignments to the Button.Value property into a PerformClick method has a severe bug that should be fixed as soon as possible.


Regardless of this specific (and flawed) example, let me comment on the technique itself.

Using overloads of C# methods to simulate late-binding is surely feasible, but for completeness' sake it should be explained what happens when applying this technique to real-world apps instead of toy examples.

If you have a method whose object argument can receive any of the 30 control types used in the project, then this technique generates as many as 30 methods. If the method takes 2, 3 or more object arguments, then you have to account for all possible combinations of argument types. In a real-world program you might end up with hundreds methods.

Maintaining this redundant code over the years is a nightmare. For example, what if you later want/need to change one statement and forget to propagate the changes to all other overloaded methods?

These additional methods significantly increase the memory footprint of the application and slow down execution because each method must be JIT-compiled separately. If a given overload is used sparingly, this overhead can offset the speed advantage of early-binding.

If a late-bound object doesn't expose a given method you receive runtime error #438 both under VB6 and VB.NET. Expert VB developers often leverage this behavior and the On Error Resume Next statement to write code that adapts itself to different object types. During the conversion to C# you lose functional equivalence, which means more work for those developers who have to check that the converted code works like the original one.

Sharp enough to convert from VB6 to C#

clock July 28, 2008 01:36

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.


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();
        fso.CopyFile("sourcefile1", "destfile1", true);
        if (throwIfError)
        fso.CopyFile("sourcefile1", "destfile1", true);
        if (throwIfError)
        fso.CopyFile("sourcefile1", "destfile1", true);
        if (throwIfError)
    // 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
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
        System.Reflection.BindingFlags.SetProperty, null, obj,
        new object[] { newValue });
    // call the Refresh method
        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) +
        newValue = Convert.ToDouble(value) +
    // assign back to Value property
        System.Reflection.BindingFlags.SetProperty, null, obj,
        new object[] { newValue });
    // call the Refresh method
        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
    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;
    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;
        If ( found )
    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.


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.


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.

VB6 Bulk Analyzer, a command-line tool to explore multiple VB projects

clock June 11, 2008 10:26

We routinely spend a lot of time at our customers' site, to help them in their migration needs. If you haven't written an application's source code, understanding what the migration challenges are is a difficult and time-consuming process. It gets quickly much worse if the application is scattered over dozens or hundreds VB6 projects, as is often the case with N-tier (DNA) Visual Basic applications. In such cases, even getting a broad idea of how large the application is, how many forms it has, which controls it uses, and which technologies it relies on is a nightmare. On top of that, if the application is really huge, few people in the company (if any) have a 360° view of the entire application.

For this and other reasons, this week I decided to lock my office door and build the VB6 Bulk Analyzer tool. It's a simple command-line utility that gathers a lot of information about all the VB6 projects and source files inside a specified directory tree and then creates a concise but quite thorough report. You can download the beta build here.

Using this tool is quite easy: just open a command window, move to the root of the directory tree that contains all the VB6 source files that you want to parse, and run the utility. I will show the name of the files being processed, and then a summary of all the code it has parsed. If you parse more than a few dozens files, odds are that the relevant information will scroll away from the window, but you also have a permanent copy in the file named VBAnalyser_Report.txt, in the current directory. The tool supports a few options too, such as /quiet for omitting file names, /out to select a different report file, and /help to display a short explanation. You can also specify multiple folder names, in case your source code is scattered in directories that don't have a common root, as in this example:
       VB6ANALYZER c:\firstapp c:\secondapp  /out:c:\report.txt

The report file contains much useful info, including:

  • number of projects and source files, grouped by type
  • statistics about the source code (code lines, empty and remark lines, etc.), number of methods, properties, etc.
  • statistics about ActiveX classes (grouped by their Instancing property, number of MTS classes, etc.)
  • all the type libraries used in all projects, with how many times the typelib is referenced
  • names of all used controls and components, with a count beside each control
  • list of all the Declare definition, with number of occurrences of each declare
  • list of problematic keywords (GoSub, On...Goto/Gosub, VarPtr, ObjPtr, StrPtr, etc.), with number of times the keyword is used
  • list of problematic data types (Variant, fixed-length strings, etc., with number of times the keyword is used
  • list of problematic control properties, methods, events (e.g. members related to classic Drag-and-drop or DDE)
  • list of problematic constants (e.g. adOpenDynamic and adOpenKeyset means that a piece of ADO code can't be easily ported to ADO.NET)
  • list of COM classes that are instantiated via CreateObjects
  • list of OLEDB data providers explicitly mentioned in a connection string

VB6Analyzer is quite precise, especially if you consider that it doesn't really interpret the VB6 source code. It uses plain regular expressons, yet it doesn't suffer from "false matches" (eg. a keyword that appears in a remark or in a quoted string). Regular expressions make it very fast: on a 3GHz system it analyzes over 600K characters per second, or nearly 9,000 lines per second. 

Please keep in mind that this build is just a beta version which I rolled out in a couple days. Please write me if you find any problem or inconsistent result.

PS: if you are interested in our VB Migration Partner or our migration services, just run VB6 Bulk Analyzer on your project and then send its report from the Contact Us page.