The Book of VB .NET: Migrating to Visual Basic .NET, Part 1, Page 2
The Wizard makes some other modifications. For example, it converts theform's MousePointer property using the equivalent .NET code, shown here:
Me.Cursor = System.Windows.Forms.Cursors.WaitCursor
This trick is also fairly impressive. Clearly, the Wizard needs to draw on acomprehensive database of some sort that stores VB 6 properties, and their.NET equivalents, because the .NET class library really has little in common withthe traditional Forms engine.
Another change appears in the Resize event handler, which now usesslightly more cumbersome code to convert the hard-coded values to pixels, themeasurement used in VB .NET. A sample line is shown here:
grid.Width = VB6.TwipsToPixelsX(VB6.PixelsToTwipsX(Me.Width) - 350)
So far, the Upgrade Wizard has done a great job. If you try running theprogram, you'll be surprised to find that it works perfectly. The old-fashionedresizing code and database code works without a hitch. Some better options areavailable for improving your program to take advantage of VB .NET, particularlywith the resizing code, but the original code still works as designed.
COM Components in .NET
Perhaps the most impressive aspect of this conversion is the fact that the ADOcomponents work exactly as they did in Visual Basic 6, even though they areCOM-based. If you look at the references for this project, you'll find that anADO reference has been added (as shown in Figure 14-5). This reference usesthe ADODB.dll wrapper distributed with .NET.
Figure 14-5: The ADODB reference
This file isn't a part of the original project-in fact, it's part of the .NETframework. When you use an ADO object in your .NET code, you are reallyusing an object from the ADODB.dll file. This file is a normal, managed .NETassembly. Behind the scenes, it uses .NET's COM Interop features to create thecorresponding COM object and pass along instructions according to your program'sactions. In other words, ADODB.dll provides a thin translation layerbetween your program and traditional ADO, bridging the gap between COMand .NET. In most cases, this trick works perfectly well, causing only a slight performancedegradation. The translation layer ensures that you can continueusing COM components for the foreseeable future.
Of course, you don't need to migrate a project in order to use this built- incompatibility layer. Visual Studio .NET makes it just as easy as adding a reference.First, right-click on References in the Solution Explorer, and then choose AddReference. Next, select the COM tab (as shown in Figure 14-6). (There will probablybe a slight delay while Visual Studio .NET loads all the system information.)
Figure 14-6: Adding a COM reference
Double-click on an item to add it to the selected list. When you click onOK, .NET will search for a primary Interop assembly, which is a .NET translationassembly created by the appropriate vendor. If it can't find one, you willreceive an ominous warning message and be given the option to create yourown wrapper automatically. The DLL wrapper file will then be added to yourproject's support files in the obj subdirectory.
Believe it or not, this COM interoperability is almost seamless, and it willsave you a lot of headaches. Just remember that when you want to install yourprogram on another computer, you will need to copy these Interop files. (Youwill also need to install and registered the appropriate COM files; .NET setupprojects won't detect these components or set them up automatically.)
TIP It's perfectly reasonable to create your own Interop assemblies for third-party components when developing and testing an application. However, when you deploy the application, you should use the Interop assembly provided by the appropriate vendor (like the ADODB.dll assembly provided by Microsoft, and included in the .NET framework). Thisway, you are guaranteed that the .NET layer works properly with all aspects of thecomponent.
ActiveX Components in .NET
ActiveX components work in essentially the same way as COM objects, sinceevery ActiveX component is really just a special type of COM object. You'll recallthat our original VB 6 test program uses an MSFlexGrid control, which is anActiveX control with no obvious .NET equivalent. The .NET framework doesnot include a primary Interop assembly for the MSFlexGrid control, so theUpgrade Wizard creates one automatically.
To witness what's really happening, select Project Show All Files from themain menu. Now if you expand the bin or obj directory, you'll find a file with aname like AxInterop.MSFlexGridLib_1_0.dll. The "Ax" at the beginning of thename identifies the fact that this Interop assembly derives from the System.Windows.Forms.AxHost class, as do all the Interop files for ActiveX controls. It's anordinary .NET managed assembly that has the built-in smarts to talk to the originalActiveX component through COM (Figure 14-7).
Figure 14-7: The .NET wrapper for MSFlexGrid
TIP You can find more information about the generated wrapper assembly by double-clickingon it. Expand the tree until you reach the last (version) element, and double-click on thatfor information about the current version of the file:
This wrapper is slightly different from the ADO wrapper, because it representsthe actual .NET control that is placed on your Windows form. You can verifythis fact by examining the automatically generated designer code:
Public WithEvents grid As AxMSFlexGridLib.AxMSFlexGridMe.grid = New AxMSFlexGridLib.AxMSFlexGrid
This control quietly communicates with the original ActiveX control, andmimics its behavior on the form. You can see this in action by examining the followingdesigner code, in which the wrapper class retrieves state informationfrom the ActiveX control instance that it contains:
grid.OcxState = CType(resources.GetObject("grid.OcxState"), _System.Windows.Forms.AxHost.State)
Interestingly, this control is a blend of the old and the new. Because AxHostinherits from the base Control class, it supports all the properties that a typicalcontrol does. For example, the designer code sets the size and location like this:
Me.grid.Size = New System.Drawing.Size(297, 137)Me.grid.Location = New System.Drawing.Point(8, 8)
The dual nature of the control also explains why the properties and methodsin some portions of code now have slightly modified names:
grid.set_ColWidth(1, 3000) ' This was grid.ColWidth(1) = 3000grid.set_ColAlignment(0, 1) ' This was grid.ColAlightment(0) = 1
This syntax would obviously not be valid in Visual Basic 6. However, theAxMSFlexGrid control is a real .NET control. It just requires the use of anActiveX control behind the scenes. This also means that you can use .NETevent-handling procedures, including the Handles keyword and the AddHandlerstatement, without any difficulty. Of course, you might have trouble determiningthe correct parameter list, because there won't be any information on thenewly created control in the .NET MSDN help library. To track down this information,select View / Other Windows / Object Browser. You can then take alook inside the corresponding AxInterop.MSFlexGridLib_1_0.dll assembly, andfind out exactly what .NET members Visual Studio .NET has created for you, asshown in Figure 14-8.
Figure 14-8: The Object Browser
The end result of our migration example is an MSFlexGrid control thatworks perfectly well. Keep in mind that in many cases, it might be better to usea true .NET control instead, to ensure best performance and easiest deployment.However, in this case the ActiveX control does not cause any obviousproblems.
In the future, there will certainly be a flood of .NET controls. Until then,you may need to rely on these built- in Interop features in order to reuse yourexisting ActiveX controls. For example, there are currently no equivalents to theold Microsoft Charting and Internet Explorer Browser components. In somecases, it might be easy to build better versions of these controls using the functionsand features available in the .NET class library, or even to substitute similar.NET controls. But having the Interop features is a huge advantage, especiallywhen using unusual or one-of-a-kind controls.
To add an ActiveX control to one of your projects, right- click on the toolboxand select Customize Toolbox (Figure 14-9). Then select the COM Componentstab, find the appropriate control on the list, and put a checkmark next to it.
Figure 14-9: Adding a wrapped ActiveX control
The Interop assembly won't actually be created until you place the controlon a form. Then you can work with the control as though you were using VisualBasic 6. For example, when you resize the ActiveX control or change its properties,its appearance will be automatically updated. Similarly, you can configureproperties through a custom tabbed property window, if such a window is providedwith the control (Figure 14-10).
Figure 14-10: Design-time support for ActiveX controls remains
It's important to remember that the COM and ActiveX Interop features canbe used even when you're not migrating a project. In fact, it often makes mostsense for you to use these features in order to continue using as many elementsas you can from your existing programs without rewriting them. Creating a.NET program that makes heavy use of existing COM components will oftenwork much better than trying to migrate the code for all these components intothe .NET world.
Migrating a Sophisticated Project
The scenario we've worked with so far is ideal in many respects. While VisualStudio .NET's migration features are impressive, they rarely work as well asdescribed with a real application. In fact, for many programs they will be essentiallyunworkable.
The next sample project is a typical midsize Visual Basic 6 program. It consistsof about three-dozen forms, most of which are used to provide differentproduct listings. The user selects any combination of products to create a pricedorder, and then either emails the order to the appropriate company (by meansof a built-in COM component for MAPI email), or creates a professional lookingprintout that can be faxed or kept for reference. Other options are available,such as the ability to preview the report with a RichText control, and the abilityto switch between different price sets (US dollars and UK pounds, for example).We'll call this program VB6OrderMaker.
Figure 14-11 shows it in action:
Figure 14-11: The VB6OrderMaker
This program is far too long to be reproduced in its entirety, and youwouldn't gain much from seeing the details. Overall, it's an intricate program(as it has a lot of details), but it is fairly straightforward. Best practices are notalways followed, but the design is not overly haphazard. It doesn't make use ofclasses or interfaces. Ultimately, it's a good example of a typical, slightly old-fashioned Visual Basic 6 program.
Unfortunately, it's difficult to tell exactly how much of the program's functionalityhas made the jump to the .NET world. There are so many .NET incompatibilitieswoven into the fabric of the program that the migrated projectwould never compile, even if hours were spent reworking it. It's safe to say thatre-creating this program from scratch would be far more successful than migratingthe existing program.
The migration report contains 360 errors, 2889 warnings, and 3249 totalissues, making for an average of about 80 issues per module (Figure 14-12shows a partial list). Even more amusing, the Upgrade Wizard has spent severalhours struggling to make the changes needed for .NET. The only file that hassurvived without any problems is the module used to run functions from theWindows API.
Rather than trying to step through this mess, it makes sense to consider themajor issues that have derailed this migration attempt, and examine how theycan be solved or avoided. (You'll find that similar issues will occur with manycomplex migration projects.)
Figure 14-12: Partial list of VB6OrderMaker errors
Before we get into the details of this troublesome project, let's review sometypes of programs that really are not fit for any type of migration to .NET. Generally,you shouldn't (or can't) even try to migrate a project if it falls under oneof these categories:
- A complex distributed application with several different layers of objectscommunicating through COM. Despite Microsoft's optimism, this type ofprogram will rarely survive the transition. However, on the good side, youcan probably start by creating a .NET client that interacts with some or allof the other COM components, allowing you to ease into the migrationprocess slowly, and continue conversions one component at a time.
- A Visual Basic 5 program that hasn't made the transition to Visual Basic 6.For all its differences, VB 6 is still one step closer to VB .NET than anyearlier release of Visual Basic. Make the change to Visual Basic 6 beforegetting more ambitious.
- An Internet project using Web Classes, ActiveX Documents, or DHTML.None of these development technologies is supported in .NET. Projectsbased on Web Classes can be upgraded to ASP .NET Internet projects, butare likely to provide many additional headaches.
- A database project based on the Data Environment, which is also no longersupported.
- A database project that uses significant data binding to other controls.These features can be upgraded under some circumstances, but willdefinitely fail with the DAO and RDO data access technologies.
- An ActiveX control or ActiveX DLL project. While you can create the .NETequivalents of these COM-based types of programs, you will lose all of theirexisting COM features. If you have controls or components that are stillbeing shared among numerous applications, it will probably be easier to usethem in .NET or to make additional .NET versions, rather than trying tomigrate them and replace the originals.
Part 2 will appear tomorrow.
Matthew MacDonald, an author, educator and MCSD developer, has worked with Visual Basic and ASP since their inceptions. He is the author of The Book of VB .NET (No Starch Press) and ASP.NET: The Complete Reference (Osborne McGraw-Hill) and co-author of Programming .NET Web Services (O'Reilly).