A new video on the Convert-Test-Fix cycle

clock November 20, 2007 10:15

Pragmas and the Convert-Test-Fix cycle is one of the most innovative features of our conversion tool. The ability to reduce the number of compile and runtime errors at each iteration through VB Migration Partner is essential in migrating complex, real-world applications.

Last weekend I prepared a video on this topic, both in Italian and in English. The two versions are basically identical and show how you can go apply the Convert-Test-Fix cycle to convert a real VB6 application, which you can download from our code samples page or directly from its original URL. Among the other techniques, the video shows how you can use a single PostProcess pragma to get rid of statements that reference DataReport objects (which VB Migration Partner doesn’t support) and how you can detect and solve runtime exceptions caused by auto-instancing variables.



Setting the default value for enumerated properties in VB.NET

clock November 17, 2007 09:56

When defining a property of a Windows Forms control, you typically need to define its default value. Such a default value fulfills two goals: first, it reduces the amount of code that Visual Studio generates in the *.Designer.vb file, which in turn speeds up form instantiation; second, it allows developers to quickly reset the property to its default value by right-clicking on the property inside Visual Studio’s Property Grid and then selecting the Reset menu command. All property values that aren’t equal to the default value appear in boldface, therefore the Reset command restores a non-boldface value.



You can declare the default value of a property by means of the System.ComponentModel.DefaultValue attribute, as in:

    Private m_ScaleLeft As Single = 0
    <DefaultValue(0.0!)> _
    Public Property ScaleLeft() As Single
          Get
                Return m_ScaleLeft
          End Get
          Set(ByVal Value As Single)
                m_ScaleLeft = Value
          End Set
    End Property

Unfortunately, VB.NET has a bug and doesn’t allow you to select a default value for enumerated properties. For example, consider the ScaleMode enumerated property, which is exposed by the VB6Form in VB Migration Partner’s support library:

    Private m_ScaleMode As ScaleModeConstants = ScaleModeConstants.vbTwips

    <DefaultValue(ScaleModeConstants.vbTwips)> _
    Public Property ScaleMode() As ScaleModeConstants
          Get
                Return m_ScaleMode
          End Get
          Set(ByVal value As ScaleModeConstants)
                m_ScaleMode = value          
          End Set
    End Property

The problem is, this code doesn’t work in VB 2005. More precisely, you can reset the ScaleMode property to its default value using the Reset command, but the property value is always displayed in boldface and is always serialized in the *.Designer.vb file. It turns out that all enumerated properties manifest this bug. (I consider it as a VB.NET bug because C# doesn’t exhibit this odd behavior.)

I tried all the techniques I was aware of to fix the problem, including adding a ShouldSerialize* method, as in:

    Public Function ShouldSerializeScaleMode() As Boolean
          Return Me.ScaleMode <> ScaleModeConstants.vbTwips
    End Function

I had always given up, when I discovered an overload of the DefaultValue attribute, which takes a System.Type and a string argument. To my great surprise, it worked!

    <DefaultValue(GetType(ScaleModeConstants), "1")> _
    Public Property ScaleMode() As ScaleModeConstants
          …
    End Property

Thanks to this fix, all the controls in VB Migration Partner’s library now serialize only the properties that have been set to a nondefault value. I haven’t checked yet whether Microsoft fixed this bug in VB2008, but for sure the VB2005 workaround is so counterintuitive that I felt the urge to document it here.



The (semi)public beta test program opens today!

clock November 15, 2007 14:32

Today is a great day here at Code Architects. For the first time we are making VB Migration Partner’s beta release available to a selected number of prospecting customers here in Italy and abroad. We will use their feedback to improve the product even further and fix all residual bugs.

The beta test program isn’t public, not yet at least. We want to keep the number of beta testers small enough so that we can follow them in an adequate manner. If you think you qualify for being a beta tester, please contact us.


Dealing with 3rd-party ActiveX controls

clock November 12, 2007 17:12

Another common question among our customers is: does VB Migration Partner handle 3rd-party ActiveX controls? Here’s a simple question that calls for an articulate answer.

First and foremost, VB Migration Partner can convert virtually all the controls in the VB6 toolbox, plus a few which aren’t distributed with VB6 but are quite common in VB6 applications, such as the ScriptControl and the WebBrowser controls. The only VB6 controls that VB Migration Partner doesn’t currently convert are the OLE container control, the Repeater control, and the Coolbar control.

It’s important to notice that most of the controls in the VB6 toolbox are migrated to “fully managed” Windows Forms controls defined in our support library. The exceptions to this rule can be gathered in two distinct groups: invisible controls (INet, MAPISession, MAPIMessage, MSComm, Winsock, ScriptControl) and visible controls (DataGrid, MSHierarchicalFlexGrid, MS Chart, and MM Control). Controls in the first group are rendered as wrappers around the original type library (using the TlbImp tool), whereas controls in the second group are substituted by .NET wrappers created using the AxImp tool.

By default, all the controls that VB Migration Partner doesn’t recognize are rendered as VB6Placeholder controls, which appear as red rectangles on VB.NET forms. In most cases you’ll see no compilation errors, because VB Migration Partner generates a “dummy” form-level Object variable named after the original control, therefore all control references are successfully resolved at compile time. (Needless to say, any reference to these variables would fail at runtime, but at least you can test all the forms in your apps that don’t contain unsupported ActiveX controls.)

If the ActiveX control is authored in VB6 and you own it’s source code, you can use our tool to migrate it to VB.NET. In this case VB Migration Partner generates all the attributes that VB Migration Partner itself needs to recognize the new VB.NET UserControl as a replacement for a VB6 control. All you need to do is recompile the VB.NET UserControl, deploy the DLL in VB Migration Partner’s main folder (C:\Program Files\Code Architects\VB Migration Partner by default), and try to migrate the main application again.

Let’s see now what your options are if your VB6 application contains ActiveX controls from other vendors and the control hasn’t been authored in VB6 or you don’t have the its source code.

The simplest and most cost-effective strategy is simply to replace the VB6Placeholder control with the .NET Framework control that most closely resembles the original ActiveX control. For example, you might decide to replace a 3-rd party grid with the DataGridView control and then fix all the references to properties, methods, and events. Needless to say, this strategy can take anything from a five minutes to a few hours, depending on how much the VB6 and VB.NET controls differ. An additional problem of this approach is that it doesn’t work well with the convert-test-fix cycle: if you later re-convert the VB6 application you’ll have to re-apply your fixes.

Alternatively, you can use the AxWrapperGen tool that we provide with VB Migration Partner. This utility generates the VB.NET source code of an “alias” class that wraps the ActiveX control and that is decorated with all the attributes that VB Migration Partner requires to recognize the class as a replacement for the ActiveX control. You must recompile this project and deploy the executable DLL in VB Migration Partner’s folder, as you’d do with UserControl classes migrated from VB6.

The last option requires you write a “pure” VB.NET alias control that replicates the same properties, methods, and events as the original ActiveX control – but doesn’t depend on the ActiveX control itself - and then decorate the class with the attributes that VB Migration Partner. In most cases, you can have the VB.NET class inherit from the .NET control that offers as many similarities to the original ActiveX control as possible. For example, an alias class for the Microsoft Calendar control should derive from the MonthView control:

     <VB6Object("MSACAL.Calendar")> _
     Public Class VB6Calendar
           Inherits MonthView
           Implements IVB6Control
          
     End Class

Alas, this option is also the most complex of the group, because you need to understand how VB Migration Partner’s support library worksand we haven’t completed this portion of the documentation yet.

To summarize, here’s what you can do:

  • Before doing anything else, check our Web site to see whether we’ve created an “alias” class for the control. We plan to add support for the most common 3-rd party controls when we have time and enough requests from our customers. The simplest way to be informed of our additions is subscribing to our newsletter.
  • If you have the control’s VB6 source, the strategy is clear: just convert it to VB.NET and deploy the compiled DLL to VB Migration Partner’s folder. This approach delivers a fully managed solution and supports the convert-test-fix cycle.
  • If you don’t have the source code, the AxWrapperGen utility offers the simplest and quickest strategy. The downside is that the control isn’t fully managed, it still depends on the original ActiveX control, and it might suffer from a few bugs in the COM Interop portion of the .NET Framework.
  • If you’ve used the control sparingly in your application, you can manually replace all ActiveX control occurrences after the migration with the VB.NET control that most closely resembles the original control. This option isn’t always possible or practical, and doesn’t support the convert-test-fix cycle.
  • A good compromise between the last two strategies: you use the AxWrapperGen utility to generate an “alias” class and then use such a class during the convert-test-fix cycle. When the VB.NET application is working fine and you don’t plan to run VB Migration Partner again, just throw away the “alias” control and manually replace all its occurrences with a fully managed controls.
  • If you have time, write your own “alias” class for the ActiveX control. (This option will be viable when we release detailed documentation about alias classes.)
  • If you don’t like any of these options, contact us for writing an “alias” control for you.


The devil is in the details

clock November 10, 2007 03:31

Today I had to face a very subtle migration issue. I was refining the Archive Explorer sample application to make it ready for publishing on this site. This application extracts one or more compressed files from a .ZIP archive and works magnificently under VB6. The converted VB.NET program worked equally well, except for a minor but disturbing detail: I was able to extract a given file from the compressed .zip file only if it was the first file in the list of files to be uncompressed. In all other cases I had an IndexOutOfRange runtime exception.

It was clearly a problem related to how an array was created or initialized, but I had no clue about where the VB.NET diverged from the original VB6 application. Archive Explorer counts only 4,000 lines, yet it is such a complex application and for sure I am not an expert of compression techniques. I was lost, because I didn’t know where to start from. In a sense, this is the same situation in which you find yourself when migrating a complex VB6 business application that you didn’t write yourself and for which no thorough documentation exists.

It took me hours to narrow down the problem to two of the most innocent statements:

    LitLen = TempLit
    Dist = TempDist

The four variables LitLen, TempLit, Dist, and TempDist are all of the same UDT type:

     Friend Structure CodesType
           Public Lenght() As Integer
           Public Code() As Integer
     End Structure

Can you see what happened? When you assign a structure to another structure, VB6 makes an exact copy of each element in the structure; in this specific case it creates a distinct copy of the Length and Code arrays. Also VB.NET makes a copy of each element in the structure, except that .NET arrays are reference types, therefore only the pointer to the array is copied! In other words, after the assignment, the Length array in LitLen variable points to the same Length array defined by TempLit. If the application modifies an element of the TempLit.Length array after the assignment, the corresponding element of the LitLen.Length array is also affected! It was precisely what happened and explained the observed behavior.

Once the problem was clear, it took me only half an hour to have VB Migration Partner take this detail into account. In the release version, if a structure contains one or more arrays or fixed-length strings, the structure implements a special Clone method that performs a “real” copy:

     Friend Structure CodesType
           Public Lenght() As Integer
           Public code() As Integer

           Public Function Clone() As CodesType
                Dim copy As CodesType = Me
                copy.Lenght = Me.Lenght.Clone()
                copy.code = Me.code.Clone()
               
Return copy
           End Function
     End Structure

 

When translating an assignment statement, VB Migration Partner emits code that leverages the new Clone method:

     LitLen = TempLit.Clone()
     Dist = TempDist.Clone()

Two questions for you: Did you ever meet this problem in converting your VB6 apps? How does your conversion tool – if you use one – handle this issue?



The need for a support library

clock November 6, 2007 00:24

When we demo VB Migration Partner to prospecting customers, one of the most frequent questions that come up is: Why does it require a runtime support library? It’s such a reasonable question, in fact, and deserves a detailed answer.

One of the first things that hit me when testing the Upgrade Wizard in Visual Studio is the abundance of the migration warnings it emits with even the simplest VB6 projects. Most of these warnings are basically like this: “This keyword/property/feature behaves differently in VB6 and VB.NET: read this help page for more information.” Alas, in many cases the help page is either missing or too generic. And if the page is not missing and is enough exhaustive, understanding it often requires an in-depth knowledge of the many subtleties of the .NET Framework. How can you explain all the nitty-gritty details of object finalization, GDI+, or drag-and-drop in one short page –to mention only three of the areas where VB6 and VB.NET differ in a significant manner?

When we began working on our own conversion tool, we were determined to dramatically cut out both the sheer number of migration warnings and the time an “average” developer would spend to fix each of them. Our reasoning is simple: if you are using our tool, chances are that you aren’t very familiar yet with VB.NET. Do you really want to set apart the actual migration job and study .NET each and every time you try to migrate a VB6 statement that doesn’t exactly behave as in VB6? Wouldn’t you prefer to have a wrapper method that hides all these differences and guarantees that the new program behaves like the old one?

The Format method is implemented very differently in the two languages, thus it provides a good example of the concept I am illustrating. The VB.NET version support neither named formats (e.g. “percent”) nor string alignments, and uses different placeholders for most values (e.g. “tt” instead of “AMPM”). As a matter of fact, you can rarely migrate a statement that uses the Format method without adjusting the pattern string. If the original VB6 application contains 100 calls to the Format method, you’ll probably spend a couple of hours just to fix the string patterns and test the result. Often even more than a few hours, because many of the differences between the two languages aren’t documented at all, therefore you have to learn them the hard way. To be honest, it took us a few days to discover all these tiny differences. Well, do you like wasting this time yourself or do you prefer using our Format6 replacement method?

But the support library does more than just providing a one-to-one substitution for language keywords and methods. For example, the way you implement the drag-and-drop feature under VB6 – either the manual and the automatic mode, as set by the OLEDragMode and OLEDropMode properties – is completely different from the way this feature works under VB.NET: member names and arguments are different, and even the sequence in which events fire is different. A developer might need hours to manually translate every single form that contains drag-and-drop code, not counting the time required to learn how drag-and-drop works under .NET in the first place.

A support library has other advantages, too. Let’s suppose that Microsoft releases a new version of Internet Explorer that makes the WebBrowser control behave slightly differently than it does today, for example it reverses the order in which the DocumentComplete and the NavigationComplete2 events fire. It could easily be a serious problem, because you have massively used the WebBrowser control in your VB6 projects and consequently in your migrated VB.NET applications.

What you do next depends on how you’ve migrated the VB6 application. If you have used a “classic” conversion tool you now need to re-test all your apps: if the change in IE does affect your code you must come up with a fix, recompile the source code, and redeploy the new executables to your customers. On the other hand, if you’ve used VB Migration Partner, you just need to download the new version of the support library and send it to your customers. Fast and clean! Basically, you are moving part of your maintenance costs to us. Can you ask for more?


The story behind VB Migration Partner

clock November 5, 2007 22:13

The story of VB Migration Partner begins in February 2006. I was reading Upgrading Visual Basic 6.0 applications to Visual Basic .NET and Visual Basic 2005, an e-book that had been just made available on Microsoft’s site. It’s a very good source of information and is a must-read if you are involved in any VB6 migration project. In fact I suggested my Italian publishing house to translate it and I wrote a preface for the Italian edition. The book is written by Artinsoft, the company who has written the Upgrade Wizard included in Visual Studio 2002 and all subsequent editions.

While progressing in reading, however, I realized that it was possible to create an automatic conversion utility capable to translate correctly most of the VB6 syntax clauses that the Upgrade Wizard fails to convert automatically. For example, I knew how to migrate As New variables that preserve the lazy-instantiation semantics they have in VB6; how to render Declare statements containing callback or “As Any” parameters; and how to initialize UDTs that contain arrays and fixed-length strings. I also had some good ideas for converting GoSub keywords, arrays with any lower index, graphic methods, control arrays, and other “impossible” statements.

Take the problem of deterministic finalization, for example. As you may know, .NET memory management – based on garbage collection - requires that you orderly invoke the Dispose method of objects that implement the Class_Terminate event or common COM objects such as ADO Recordsets and Connections.. Why can’t a conversion program insert the call to Dispose for you before an object variable is set to Nothing or goes out of scope? Dealing correctly with disposable and finalizable objects is indeed possible, and our VB Migration Partner can prove it. (More on this on a forthcoming blog post.)

To make a long story short, I came up with a working prototype in a couple of months. It included a parser, a code generator, and a support library that could handle the most common keywords and controls.  It was the evidence that the approach was promising, thus I abandoned most other activities and decided to focus mainly on the new tool. I rewrote the syntax analyzer – the most delicate portion of a conversion tool – from scratch, added code metrics and analysis features, completed the support library with new controls. Before long Marco Bellinaso joined the team and got in charge of the user interface module and of assessment features. Marco is a renowned writer and the author of best-selling ASP.NET books and of one of Microsoft’s official starter kits.

In spring 2007 we set up the test team, lead by Giuseppe Dimauro. Giuseppe is Code Architects’ co-founder as well as one of the two MSDN Regional Directors in Italy (the other being yours truly), and is also responsible for our consulting services. Since then the group has worked on hundreds of VB6 apps, stressed virtually every single VB6 feature, and tested the tool on many real-world business software from our Italian customers. The samples you can find here are just a little portion of what we’ve used in our tests.

In these two years we have bumped into some of the most weird bugs I ever faced in many years, and solved so many technical problems that range from trivial to overly complex. We learned how to convert some of VB6’s most esoteric features, such as ADO bindable classes or user controls with OLE_OPTEXCLUSIVE properties (ever heard of them?). We also realized that there are more undocumented differences between VB6 and VB.NET than documented ones. Perhaps we’d never started this project if we had known how complex it would have turned to be, but now we’re just very happy that we were so ignorant.



Welcome to vbmigration.com

clock November 4, 2007 16:10

I am writing this post when the site is open only a restricted number of beta testers, so odds are that you’ll read it only in a couple of weeks. Nevertheless, an introduction is in order, so here it is.

I am Francesco Balena, author of a few books on Visual Basic 6 and Visual Basic .NET for Microsoft Press. If you are a long-time VB developer, you might have read my articles on Visual Basic Programmers’ Journal (now Visual Studio Magazine) or attend my sessions at VS Live!, WinDev, or other technical conferences.  I don’t write as many books or speak as much as I used to, because I am more busy doing what I really like most: writing software applications.

My last project is also the most complex program I’ve written in my 25-year career. It took two years to complete VB Migration Partner, the most complete and innovative tool to migrate VB6 apps to VB.NET. I didn’t do all by myself, and in fact I could count on the support from some of the finest developers we have in Italy, and from a great and dedicated team at Code Architects.

We created the vbmigration web site to promote our tool and our services, but we also plan to offer plenty of articles and resources related to VB6 migration. We will keep a record of all the tricks and the conversion techniques that we’ve learned in these two years and that we implemented in VB Migration Partner. We want to prove you that our product is simply the best conversion utility on the market, but you can still use what we learned in your own migration projects even if you don’t use our tool.