FRANCESCO BALENA ON VB MIGRATION

 

Neat tricks for smooth migration of calls to Windows API methods

clock June 12, 2008 21:13
VB Migration Partner does a superb in dealing with Windows API calls. Here's a summary of the features that it supports and that are out of reach for the Upgrade Wizard included in Visual Studio 2005/2008:

  • converts As Any parameters, by creating all the necessary overloads
  • deals correctly with API methods that take a callback address (e.g. EnumWindows, EnumFonts)
  • provides recommendation about the .NET object/method that can effectively replace the API method; we cover 300+ different API calls. (Enterprise Edition only)
  • in a few cases, it automatically replace API calls with calls to the corresponding .NET object/method
  • ensures that string immutability doesn't prevent the VB.NET code from working correctly (see this article)
  • generates the correct MarshalAs attributes for elements in Type (Structure) blocks
  • correctly translates fixed-length strings inside Type blocks, so that they work correctly when passed to the Windows API method
  • automatically initializes static arrays inside Type blocks, so that you don't get unexpected crashes when invoking an API method that expects to find a buffer there
  • creates a wrapper method that ensures that orphaned delegates don't cause an unexpected runtime exception, an advanced programming technique discussed in this KB article (Enterprise Edition only)
  • includes the VB6WindowsSubclasser class that helps you correctly migrate subclassing-based techniques (as explained here)

In spite of all these innovations, there are cases when you still need to manually edit either the original VB6 code or the converted VB.NET. This happens, for example, if the original code uses the VarPtr, StrPtr, or ObjPtr functions to pass memory pointers to an external API method. These three functions aren't supported under VB.NET and there is no simple way to simulate them.

The good news is that in the vast majority of cases you don't need to deal with memory pointers under .NET, because the .NET Framework offer a valid "pure" alternative to the API method in question. In this article I'll illustrate how you can take advantage of VB Migration Partner features to reduce manual edits to the very minimum and take advantage of the convert-test-fix methodology.

For simplicity's sake, let's focus on one of the simplest API methods, the GetSystemDirectory Windows API. Here's a piece of VB6 code that displays the system directory path:

    ' Main.Bas module
    Public Declare Function GetSystemDirectory Lib "kernel32.dll" Alias "GetSystemDirectoryA" _
        (ByVal lpBuffer As String, ByVal nSize As Long) As Long

    Sub Main()
        Dim buffer As String, length As Long, windir As String
        buffer = Space(256)
        length = GetSystemDirectory(buffer, Len(buffer))
        winDir = Left(buffer, length)
        MsgBox winDir
    End Sub

The first step is in refactoring this code so that you make all the Declares private and move them to another BAS module, that exposes them by means of standard VB6 methods. (If you usually write tidy and maintainable VB6 code, odds are that you have already taken this step.)

    ' This is the APIHelpers.Bas file
    Private Declare Function GetSystemDirectory Lib "kernel32.dll" Alias "GetSystemDirectoryA" _
        (ByVal lpBuffer As String, ByVal nSize As Long) As Long

    ' returns the Windows directory
    Public Function SystemDirectory() As String
        Dim buffer As String, length As Long
        buffer = Space(256)
        length = GetSystemDirectory(buffer, Len(buffer))
        SystemDirectory = Left(buffer, length)
    End Function

The code that actually displays or otherwise uses the Windows directory path is now simpler. Notice that we explicitly include the module name (APIHelpers) in the method call. This tip reduces the odds that another method with same name exists elsewhere in the project, but the technique explained later works even if you don't include such a name:

    ' the Main.Bas module
    Sub Main()
        Dim windir As String
        winDir = APIHelpers.SystemDirectory
        MsgBox winDir
    End Sub

At this point you have a VB6 project that works exactly like the original one, but it is better organized and structured, with all Declares statements gathered in one single module. Let's see how to migrate this code to VB.NET and get rid of all dependencies from non-NET code.

First and foremost, we prepare a VB.NET module that exposes the same methods as the original APIHelpers.bas but doesn't use any Declare statement. Here's how we can render the SystemDirectory function using native .NET calls:

    ' This is the APIHelpers.vb file (VB.NET)
    Module APIHelpers
        Public Function SystemDirectory() As String
            Return Environment.SystemDirectory
        End Function
    End Module

Next, we use an ExcludeCurrentFile pragma to exclude the APIHelpers.bas VB6 module from migration process and we use an AddSourceFile pragma to add the APIHelpers.vb VB.NET file to the converted Visual Studio project. The neat result is that the code in Main now calls the .NET version of the method, which doesn't use any unmanaged calls:

    ' This is the APIHelpers.Bas file
    '## ExcludeCurrentFile
    '## AddSourceFile "c:\vbnet\modules\apihelpers.vb"

    Private Declare Function GetSystemDirectory Lib "kernel32.dll" Alias "GetSystemDirectoryA" _
        (ByVal lpBuffer As String, ByVal nSize As Long) As Long
    ' ... remainder of module as before...

This solution works great, but we can improve it. In fact, a (minor) problem is that the resulting VB.NET code still uses wrapper methods and doesn't look like the "native" .NET code that an experienced VB.NET developer would write. Fear not, because all you need is a project-level PostProcess pragma:

    ' This is the APIHelpers.Bas file
    '## ExcludeCurrentFile
    '## project:PostProcess "(APIHelpers\.)?SystemDirectory", "Environment.SystemDirectory"

Notice that I dropped the AddSourceFile pragma because you don't need the wrapper method any longer (at least in this simplified example). Using similar techniques you can provide a .NET equivalent for most methods that require API calls under VB6, including methods that take arguments.

One of the long-terms goals we have in Code Architects is to apply these concepts on a larger scale to create VB6 helper modules and their corresponding VB.NET versions, to help all VB6 developers to easily migrate their API-intensive applications. Just stay tuned, as usual!

Currently rated 4.7 by 3 people

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


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

clock June 11, 2008 04: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.

Currently rated 4.8 by 4 people

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


What are your options to survive in the next decade?

clock May 29, 2008 01:51

Well, I really mean "what are the options to have your VB6 application survive in the next decade?". After nearly 30 years in the field - I started my Computer Science studies in 1979, go figure! - many of which spent consulting for companies in Europe and United States, I have seen many tecnology revolutions, including the advent of IBM PC and of Windows 1.0. Each one thaught me a little piece of experience.

Let's quickly see the alternatives. 

1) DO NOTHING (AKA "IF IT AIN'T BROKE, DON'T FIX IT")
If the application is at the end of its life cycle, for example because it doesn't fullfil a widespread need any longer, you might decide to have it die of a natural death. There is no reason to invest in a dying creature, therefore you might stay with VB6 and just fix some major bugs when your users complain aloud. Don't add any new feature or extend it in any way, because it would be too expensive too.

2) EXTEND THE VB6 APPLICATION USING COM INTEROP AND THE FORMS INTEROP TOOLKIT
In this case you leave the main application in VB6 but extend it by invoking .NET components using COM Interop, or display .NET forms using Microsoft's Forms Interop Toolkit. Version 2.0 of this add-on is a major improvement, as it even supports hosting of .NET controls inside VB6 forms. Best of all, the toolkit is fully supported by Microsoft and comes with full source code. [Thanks to Rob Windsor for remainding me of this option, see comments]

While the toolkit is free, adopting it has some obvious and hidden defects and costs, though. First and foremost, this approach isn't really a solution to the problem, is more of a technique to soften the migration path and lessen its impact on your organization. If you need to migrate because your app doesn't work well under Windows Vista, or because it's slow, or because you don't want to rely on a language that Microsoft doesn't support any longer, using COM Interop and the Forms Interop Toolkit is just a way to postpone the time when you truly need to find a reliable solution. Second, the communication between the VB6 and VB.NET portions of the application is often awkward and leads you to inefficiencies (due to COM Interop) and complex roundtrips that don't make your code easily maintanable.

The bottom line: you should use the Forms Interop Toolkit only in the bigger picture of a phased migration (as Rob suggests in this comment). That is, while a group of developers is working on the porting from VB6, another group is already extending the application using VB.NET. Before taking this approach, you should compare it with the effort required by migrating it to VB.NET in one step using a *good* migration tool (ours, that is :-) )

3) CONVERT TO VB.NET, USING THE UPGRADE WIZARD AVAILABLE IN VISUAL STUDIO
Why not giving it a try? it's free! While your hard disk spins for hours trying to convert your real-world business app, you can have enough time to google for less-then-gentle comments about this tool from the VB community. Let's skip to next option.

4) CONVERT TO VB.NET, USING VB MIGRATION PARTNER
Using our conversion tool to migrate to VB.NET makes sense in many cases. First, when the original application works well and is thoroughly tested, but needs to be extended with new features and implementing these extensions with VB6 would be too difficult or expensive. Second, when the application is so large that rewriting it from scratch would require a very intensive test phase. Third, if time-to-market is essential to beat your competition and make your existing users happy. In these cases you can leverage VB Migration Partner higher speed and precision (as noted by our beta testers)

5) CONVERT TO VB.NET, USING ANOTHER 3RD-PARTY TOOL
There are other VB6-to-VB.NET migration tools on the market. Choosing a conversion tool is a critical decision, so you might want to ask our competitors for a trial version and compare it with our VB Migration Partner. Please do it! Please compare its feature, precision, and speed with VB Migration Partner and then let us know. If you don't have time to waste, however, ask yourself the following questions before considering another conversion tool:

a) how many VB Migration Partner's feature does this tool support? for example, does it fully support all 60+ VB6 controls, drag-and-drop, graphic methods, arrays with any LBound, Gosubs, As New semantics, IDisposable objects, As Any parameters and callbacks in Declares, etc. 
b) are there any significant features that this tool support and that VB Migration Partner doesn't?
c) why they don't make their entire documentation available online before you buy?
d) why don't they dare to publish real-world VB6 apps and the VB.NET code that their tool generates?

As far as we know, Code Architects is the only vendor who has uploaded dozens of VB6 code samples and the corresponding, converted VB.NET project, to let potential customers see and test the VB.NET applications we produce. We don't upload artificially-conceived tiny VB6 projects that highlights the tool's strengths and hide its weakness. Instead, our code sample section gathers open-source VB6 projects taken from the Internet, and we guarantee that we edited neither the original VB6 code (which is in fact downloadable from its original URL) nor the converted VB.NET.

Now ask yourself: why no other vendor took this obvious step to show the world how powerful their tool is?

6) MANUALLY RE-WRITE IT TO VB.NET
In some cases, manually re-writing the application to VB.NET is a viable solution. Well, let me be absolutely honest on this point: in (very) few cases, this is the solution we recommend to our customers. If the application is a huge amount of contorted spaghetti code, has a orrible architecture and a ugly user interface....well, in this case you don't want to spend more time and money on automatic porting, because you would also port these issues to VB.NET. 

When weighing a complete re-writing from scratch against automatic porting, you should take several factors into account, including:

a) rewriting often takes from 50% to 90% of the time and money that was necessary to write the original application. (Sorry, no official statistics on this, just our field experience...)
b) you need a team of developer who are experienced in both VB6 and VB.NET, and they also need to be familiar with issues related to the specific application, therefore you can't simply hire them from outside your company
c) how do you cope with continuous edits/improvements to the original VB6 code during the months required by the rewrite? In other words, can you count on something similar to our convert-test-fix methodology?
d) can really you afford the extra time required by a complete re-write? what will your competitor do in the meantime?

7) MANUALLY RE-WRITE TO C#, JAVA, OR ANOTHER LANGUAGE
I heard that a few ISVs are taking this path. In my opinion this is an irrational decision, often motivated by the anti-VB (or even anti-Microsoft) feeling that is so fashionable today. Let me explain why you shouldn't even think of jumping from VB6 to any .NET language other than VB.NET.

Rewriting a complex VB6 application into a different language has all the drawbacks of rewriting it to VB.NET (see point 6), plus the many problems that you have when switching from VB.NET to C# or Java. How do you implement On Error Resume Next in C#? How do you render a DTPicker control in Java without carefuly mapping each and every property, method, and event? But the decisive argument is: do you have enough developers who are equally familiar with VB6 and C# or Java and the application being migrated?

Briefly stated: taking this route is a technological suicide similar to point 1), except it takes longer, costs more, and is more agonizing. You can only hope that your competitors are in love enough with C# or Java to invest their energies in this option. If you hear that they are doing it, relax and be happy: you don't have anything to worry about for a long time :-)

8) CONVERT IT TO C# USING AN AUTOMATIC CONVERSION TOOL
Come on! To get an idea of how tough this "solution" (so to speak) is, take all the issues listed at point 5) and multiply them by all the problems mentioned at point 7). Then double the result to have a more reasonable estimate of the actual cost and effort.

More seriously, just think of this: there are a few commercial tools on the market that can automatically migrate VB.NET to C#, for example Instant C# or C-Sharpener for VB. I have actually used a couple of these tools, they do a great job. However, they still require a few manual edits after the conversion, to adjust minor differences between VB.NET and C#. The main reason is that the resulting C# code doesn't look like native C# and requires some fixes to be more readable and easily maintainable.

Before your consider this approach, ask yourself two questions:

a) if you believe that automatic conversion between two .NET languages (VB.NET and C#) is difficult, how can you expect that a tool be able to automatically convert from VB6 to C# in one single step?
b) on the other hand, if you think that conversion from VB.NET to C# can be fully automated, why not going from VB6 to VB.NET using our VB Migration Partner (or another tool, if you prefer) first and then using Instant-C, C-Sharpener for VB, or another similar tool for doing the final move to C#?

If you really want a C# app as the final deliverable of your conversion efforts, keep in mind that jumping from VB6 directly to C# doesn't give you more than switching from VB6 to VB.NET first and then from VB.NET to C#. Well, it actually does give you something more.... many more headaches, for precision's sake :-)

Currently rated 3.7 by 9 people

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


Project Dependency Collection tool

clock January 7, 2008 20:55
Many of the code samples on which we've tested our VB Migration Partner tool have been downloaded from Planet Source's All-Time Hall of Fame, a page that gathers hundreds of VB6 samples that have won Planet Source's monthly contest or that have proved to be superior quality code. Even if you don't care for VB6 any longer, you can find some compelling code in that page. Pay it a visit, it won't be a waste of time.
Today I stumbled into a nice and very useful utility, named Rutu's Project Dependency Collection tool. It analyzes a VB6 project, lists all the type libraries and DLLs it depends on (and highlights missing DLLs), optionally extracts them to a folder, and include a BAT file that registers all of them. It even comes with a Shell Menu extension that allows you to run the tool from inside Windows Explorer.

It's useful, it's free, and even comes with full VB6 source code!

Currently rated 5.0 by 1 people

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


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

Home blog
 
AddThis Social Bookmark Button


 

Sign in

Search

Calendar

<<  July 2008  >>
SuMoTuWeThFrSa
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

Archive

Categories

 

Microsoft Regional Director
Microsoft is a registered trademark of Microsoft Corporation in the United States and other countries and is used under license from Microsoft
 

My Blogs


My programming tools



VB Migration Partner


VBMaximizer for VB6


Form Maximizer for .NET

 

My books


Programming Microsoft Visual Basic 6


Programming Microsoft Visual Basic 2005: The Language


Practical Guidelines and Best Practices for Microsoft Visual Basic .NET and Visual C# Developers