Reprinted with permission from Visual Basic Programmer's Journal, June 2001, Volume 11, Issue 6, Copyright 2001, Fawcette Technical Publications, Palo Alto, CA, USA. To subscribe, call 1-800-848-5523, 650-833-7100, visit www.vbpj.com, or visit The Development Exchange.
Migrate Your VB6 Code to VB.NET
Find out what Upgrade Wizard does well, what it misses, and what to do about the differences.
by Stan Schultes
As a VB developer, you've always had it easy when new versions of VB came out. The new version was always backward-compatible, so your old code would build and run with minimal changes. This won't be the case when you move from VB6 to VB.NET, however. All but the most trivial applications will take a significant amount of effort to port from VB6 to VB.NET. Backward compatibility appears not to be one of Microsoft's high-priority goals with the new .NET environment.
The most profound changes arise from the fact that VB.NET is based on the new Common Language Runtime (CLR). The CLR is the execution platform for all .NET apps, including those developed in VB, managed C++, C#, and other languages. VB has undergone the most significant changes ever in the language as a result of the new programming model in the CLR. In fact, you should rewrite an app rather than port it to take best advantage of VB.NET's new features and structures.
But when rewriting isn't feasible, you can convert your VB6 code using VB.NET's Upgrade Wizard. I'll walk you through the conversion process using the Upgrade Wizard on the SettingsTest sample from my September column (he sample is included in this month's download). SettingsTest includes a test form (see Figure 1) and a single class module that allows you to save application settings to either the Windows Registry or a program INI file. These are still valid ways of storing settings in VB.NET, although you could also easily keep your settings in XML files.
I'm using VB.NET beta 1 as of this writing, and changes will likely occur with beta 2 and later versions. Start VB.NET and select the File | Open Project menu item. Browse to the location of the VB6 code you want to convert, choose the project's VBP file, and click on the Open button to start the Upgrade Wizard.
Take the defaults in the wizard's step two, and choose a path in step three. The default path resides in a subdirectory of your VB6 code. Click on the Next button; the wizard converts your project into VB.NET without touching your existing code. Click on the Finish button when the wizard is done. The conversion process can take a while, depending on the speed of your machine. The wizard converted the SettingsTest project in about two and a half minutes on my Pentium Pro 200 with 128 MB of memory, and SettingsTest is a small app.
You look at the wizard's upgrade report in the Solution Explorer window (View | Solution Explorer) by double-clicking on the UpgradeReport.htm item. The wizard doesn't generate any issues for the SettingsTest project, but it makes quite a few changes (see Table 1). Open your original project in VB6 to compare the code. The wizard's modifications account for changes in VB syntax and features, but the code structure doesn't change at all.
Everything's Fine ... Or is It?
When the app exits, you save a set of program values under the Settings section using the VB Registry functions GetSetting and SaveSetting. Two of these keys, MainLeft and MainTop, identify the form's location. If you move the form on the screen, then exit and restart the app, you'll see that VB doesn't remember the startup form location even though the Registry values change. Fix this problem by opening frmSettings in design mode and changing the form property StartPosition to Manual. This form property's default behavior differs from that of its corresponding VB6 form property, which was called StartUpPosition.
The wizard also converts the form position code with compatibility calls to PixelsToTwipsX/Y and TwipsToPixelsX/Y in LoadMySettings and SaveMySettings. You can remove these unnecessary calls, but they point out that the form position's default units have changed to pixels instead of twips. Make sure you delete the old values for MainLeft and MainTop in the Settings Registry section; otherwise, your form might appear off the screen.
Now try saving an Integer setting using the Integer Value checkbox. You'll get a message box saying you must enter a numeric value to save as Integer. The problem here: The Text property name is missing from the txtSet.Item() parameter in the IsNumeric function calls. In VB.NET, you must specify all property names, including those that were default properties formerly. Add the Text property qualifier inside the two IsNumeric calls to make the code work properly:
In the CSetting class SaveMode property declaration, you can see the wizard updated the code to the new Get/Set syntax and changed the return value to Integer from the VB6 code's enum value. Change the property type declaration back to the csModes enum:
In the Set property procedure, the Value keyword contains the incoming property value.
Switch to INI mode by clicking on the Use INI File checkbox, then on the Get button. You get thrown an error of type System.ArgumentException, which shows the On Error Goto exception-handling scheme in the SettingsTest code still works. The problem here lies in one of the parameters to the GetPrivateProfileString Windows API call. The fourth parameter, defined as ReturnedString in the CSettings' Declare statement, requires a fixed-length string. The fifth parameter, StringSize, identifies the string length.
The Upgrade Wizard converted the code to use the compatibility function VB6.FixedLengthString. This string class doesn't work for the API call because the parameter requires a string variable reference rather than a class method reference. You can fix this by declaring sBuf as a normal string and initializing it to a fixed length with the Space function:
The new VB.NET declaration syntax lets you declare and initialize the variable on the same line. You use the sBuf string's Length method to return its length. The function call to return an INI string now looks like this:
A minor problem with saving settings in the app's INI file comes to light as you look for the INI. In the VB6 version of SettingsTest, you use App.Path in the CSetting.Init method to locate the INI in the same directory as the source files. In VB.NET, the app's EXE file isn't located with the source files. App.Path is now dependent on the app's configuration properties (Project | Properties | Configuration Properties tab). VB.NET defines two configurations by default: debug and release.
Set a breakpoint in the CSetting Init method and look at the value of System.Winforms.Application.StartUpPath, which translates to the app's \bin\debug path when you're using the debug configuration. The easiest way to solve this: Put the app's INI file in the Windows System directory (\windows\system\ or \winnt\system32\ by default) and remove the path specification from the msININame variable assignment in CSetting.Init.
You've now worked the bugs out of the SettingsTest conversion process, and you can move on to making program structure changes. In the VB6 version of SettingsTest, you initialized a new instance of the CSettings class using the Init method with two optional parameters (see Listing 1). With VB.NET's new parameterized constructor syntax, you can initialize the class at declaration time. You create four versions of the CSetting class constructor for the Init method's two optional parameters to cover all the cases (see Listing 2). Change the CSetting class declaration in frmSettings to look like this:
This statement declares and initializes the cSet object at the same time. The enum value is now fully qualified with the enum name and value. Comment out the Init method, and comment out the calls to the old Init method in frmSettings.
You can continue converting the SettingsTest app to use other elements of VB.NET's new language syntax. Obvious changes involve using the new structured error-handling scheme (Try Catch Finally) in place of the On Error Goto-style error handling. More subtle changes might include replacing the GetSettingStr/GetSettingInt and SaveSettingStr/SaveSettingInt method calls with overloaded functions, similar to replacing Init with the overloaded constructors. This would reduce the four settings calls to two and clean up the code in the process.
Based on the changes required in this simple example, you've learned the VB6-to-VB.NET migration process won't be trivial. You'll encounter outright problems the Upgrade Wizard won't fix, and other issues it won't handle properly. Microsoft promises more complete wizard functionality in beta 2 and later, but that remains to be seen. Fixing language incompatibilities will probably be the smaller part of your app conversion process, however. You'll need to rewrite and rearchitect your code during the migration to best take advantage of the new tools.
You might want to consider leaving the bulk of your existing code in VB6 due to good interoperability between VB6 COM components and VB.NET components. Then as you write new code in VB.NET, you can call your library of existing code and focus your time on writing new code and applications with the new tools.
Stan Schultes is a project manager, IT manager, and VB/Web enterprise application architect and developer in Sarasota, Fla. An MCP in VB and a contributing editor for VBPJ, Stan spoke about VB development at Microsoft's DevDays conference. Reach him at Stan@VBExpert.com.