Upgrading vb6 To VB Net Ebook
Upgrading vb6 To VB Net Ebook
PUBLISHED BY
Microsoft Press
A Division of Microsoft Corporation
One Microsoft Way
Redmond, Washington 98052-6399
All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form or by any means
without the written permission of the publisher.
Microsoft Press books are available through booksellers and distributors worldwide. For further information about
international editions, contact your local Microsoft Corporation office or contact Microsoft Press International directly at
fax (425) 936-7329. Visit our Web site at www.microsoft.com/mspress. Send comments to [email protected].
ActiveX, IntelliSense, Microsoft, Microsoft Press, MSDN, the .NET logo, Visual Basic, Visual C++, Visual C#, Visual
FoxPro, Visual InterDev, Visual Studio, Win32, Windows, and Windows NT are either registered trademarks or
trademarks of Microsoft Corporation in the United States and/or other countries. Other product and company names
mentioned herein may be the trademarks of their respective owners.
The example companies, organizations, products, domain names, e-mail addresses, logos, people, places, and events
depicted herein are fictitious. No association with any real company, organization, product, domain name, e-mail address,
logo, person, place, or event is intended or should be inferred.
—Ed
—Mike
To my loving family and friends who have offered unconditional support throughout… and to Cathy
—Ian
Just as the computing landscape has transformed itself over the last decade, so too has Microsoft Visual Basic. For more
than ten years, Visual Basic has fueled the growth of client/server applications, 32-bit programming, and component
development. It is the backbone of thousands if not millions of critical business systems throughout the world. Now, as the
computing industry looks to address a new set of challenges focusing on Internet, mobile, and Web service development,
a new version of Visual Basic—Visual Basic .NET—is once again at the forefront of technology, ready to fuel the
growth of the next generation of application development.
Like Visual Basic 1, Visual Basic .NET is both evolutionary and revolutionary. It builds on the key strengths of previous
versions of Visual Basic and extends rapid application development to the server and to the Web with XML Web services,
Web Forms, trickle-down deployment of Windows applications, and the ability to write Windows services. Visual
Basic .NET also delivers some of the most asked-for features: real inheritance and the ability to cleanly implement object-
oriented designs, better integration with other languages, seamless deployment, and versioning.
These enhancements to the Visual Basic language, the shared Visual Studio .NET IDE, and the new underlying .NET
Framework all introduce discontinuities between Visual Basic 6 and Visual Basic .NET. These discontinuities, while
essential to the evolution and importance of Visual Basic as a programming tool, require you to use a particular set of
technologies and strategies to facilitate a smooth upgrade to Visual Basic .NET.
In Upgrading Microsoft Visual Basic 6.0 to Microsoft Visual Basic .NET, Ed Robinson, Mike Bond, and Ian Oliver
provide this vital information in clear, concise language. In addition to covering the many new concepts and constructs in
the Visual Basic .NET language, they discuss the extensive capabilities of the Visual Basic .NET Upgrade Wizard as an
essential instrument in your upgrade toolbox. Further, they describe strategies for addressing common upgrade issues,
interoperating with existing Visual Basic 6 components, and adding value to your upgraded Visual Basic .NET
applications. Finally, this book also serves as a handy reference guide, full of numerous before-and-after code and object
model comparisons.
Iâ™ve had the pleasure of working with Ed, Mike, and Ian for the past year on Visual Basic .NET. In this book, they
bring you their unsurpassed knowledge of both product and customer in a highly digestible format. Armed with this
knowledge and Visual Basic .NET, you will be well on your way to upgrading and enhancing your existing Visual Basic
applications and taking the next big evolutionary step in application development.
Ari Bixhorn
Product Manager
Introduction
Welcome to the world of Microsoft Windows .NET, especially to Visual Basic .NET. Visual Basic is now a .NET
development tool. .NET is a platform for developing both Windows and Web applications with seamless integration and
universal connectivity through XML Web services.
Many articles have appeared in the press about Visual Basic .NET being radically different from prior versions of Visual
Basic. Unfortunately, most of the articles leave you with more questions than answers. What exactly are the changes in
Visual Basic .NET? Will it be possible to upgrade existing applications? Why does it break compatibility with Visual
Basic 6? Visual Basic .NET includes an Upgrade Wizard, which upgrades up to 95 percent of your projectâ ™s code, but
what about the other 5 percent? What are the common upgrade issues? How do you resolve them and get your project
working in Visual Basic .NET? What about XML Web services and ADO.NET data access—how can existing programs
take advantage of these new features?
In this book, we answer these questions and more. We provide a complete guide to upgrading, with both technical and
conceptual information. To upgrade your applications, you will need to learn some new skills. We teach these skills and
discuss the hows, whats, and whys of upgrading: how to recognize the micro-issues that need a one-line fix, what to do
about macro-issues that involve redesign of your application, and why Microsoft made the changes to Visual Basic.
Along the way, weâ™ll take a few interesting digressions. Because we worked on the team that created Visual
Basic .NET, weâ™ll share a few behind-the-scenes stories about Visual Basic and the development of Visual
Basic .NET. These stories are scattered throughout this book, which is divided into four parts:
Part I, Introduction to Upgrading What the differences are between Visual Basic 6 and Visual Basic .NET, how
to prepare projects for upgrading, what approach to use, and when to leave applications in Visual Basic 6
Part II, Upgrading Applications How to use the Upgrade Wizard, what the wizard does and doesnâ ™t do, what
errors the Upgrade Wizard generates, and how to interoperate with COM components from VB.NET
Part III, Getting Your Project Working How to fix the common problems with forms, language, data access,
MTS, and COM+ services; how to get your ActiveX controls and COM components to work; and how to deal
with issues common to VB Application Wizard projects
Part IV, Techniques for Adding Value How to redesign multiÂtiered applications to use .NET remoting or XML
Web services, how to replace ActiveX controls with Windows Forms controls, how to integrate or replace ADO
with ADO.NET
In addition to discussing how to upgrade applications, this book is also an upgrading reference. Chapter 8 includes the
complete list of errors and warnings that the Upgrade Wizard generates. Chapter 19 maps the Microsoft Windows
common controls to equivalent Windows Forms controls. Appendixes A and B give the complete Visual Basic function
and object model mappings from Visual Basic 6 to Visual Basic .NET.
This book is written for the Visual Basic developer who wants to upgrade applications from Visual Basic 6 to Visual
Basic .NET. Although we focus on Visual Basic 6, most of the information is also relevant for applications written in
earlier versions of Visual Basic, since Visual Basic 6 is essentially a superset of Visual Basic versions 1 through 5.
The content of this book ranges from intermediate to advanced. For example, the chapter on fixing problems with VB
Application Wizard projects will probably appeal to more intermediate developers, whereas the one on upgrading COM+
services will be of interest to highly experienced developers.
Two areas of upgrading that we donâ™t discuss are user controls and WebClasses. As this book went to press, Microsoft
had not yet added this functionality to the Upgrade Wizardâ ”itâ ™s coming sometime in the future. If youâ ™re
upgrading these technologies, we hope youâ™ll still find the information in this book useful, since most of the forms
material applies equally to user controls, and the chapters on language apply universally to every project you upgrade.
Microsoft Visual Basic .NET (Professional or Enterprise edition), or Visual Studio .NET. We recommend that
you not use the standard edition of Visual Basic .NET, since it doesnâ ™t include the Upgrade Wizard.
Notice that we recommend installing both Visual Basic 6 Service Pack 5 and Visual Basic .NET on the same machine.
This is actually a requirement for many upgrades, since the Upgrade Wizard needs to refer to your projectâ ™s COM
controls and type libraries during the upgrade. The simple rule of thumb is to make sure the program works in Visual
Basic 6 on the same machine before upgrading.
This book presents before-and-after code samples in Visual Basic 6 and Visual Basic .NET. To help you tell the
difference, they are formatted differently. Visual Basic 6 samples are in normal type:
The CD that accompanies the book also contains a searchable, electronic version of the text. Copy the e-book to your hard
drive, and youâ™ll have a complete upgrade guide at your fingertips.
Acknowledgments
The authors wish to thank the Visual Basic .NET and .NET Framework teams for creating such an awesome product for
us to write about; our co-workers at Microsoft who helped resolve numerous issues; the team at ArtinSoft S.A. in Costa
Rica for all their hard work and dedication in creating a world-class upgrade tool; Ari Bixhorn and the Visual Basic .NET
product managers, who through their boundless energy helped us to see that there are no problems, only unfulfilled
opportunities; Rob Copeland, Mike Blaylock, and Rick Nasci for agreeing to let us use our personal time to write this
book; Danielle Bird, Barbara Moreland, Rebecca Pepper, Marzena Makuta, and Marc Young at Microsoft Press; and our
friends and families, who supported us through the nights and long weekends we spent researching and writing.
This document is created from a CHM file automatically by an unregistered copy of
CHM-2-Word.
The content of this chapter is skipped, please register CHM-2-Word to get full
features.
For registration information, please refer to: http://www.macrobject.com
Why did Microsoft redesign and restructure the language? Why couldnâ ™t it add these new features and still keep
compatibility with Visual Basic 6? There are several reasons for this, as we discuss in the sections that follow.
Some of the new features in Visual Basic .NET could not have been added without a redesign. Adding visual inheritance
and accessibility support to the forms package required redesigning the forms object model. Adding Interface statements
and attributes to the language made the language more powerful by enabling a greater degree of fine-tuning but required
changing the language and file formats. Fixing “DLL hell†meant that versioning and deployment had to be
redesigned.
By far the biggest reason for the changes, however, was the need to integrate Visual Basic with the .NET platform. Cross-
language inheritance, debugging, and unfettered access to the underlying APIs required the standardization of data types
across languages, which meant changing arrays to be zero based and removing fixed-length strings from the language.
Redesigning the Web and data access classes to be more scalable meant even more changes from Visual Basic 6.
Visual Basic has grown over time, and as the language has been extended, some areas have become inconsistent and
problematic. A good example of such an area is default properties. The rules for when an assignment is to be a default
property and when it is to be an object have become inconsistent. Consider the following Visual Basic 6 example, where
Form1 is a form in the current project:
Dim v As Variant
v = Form1
This code causes an error because Visual Basic 6 tries to assign the default property of the form (the controls collection)
to the variable v. Contrast this behavior with the following Visual Basic 6 code:
Dim v As Variant
Set v = Form1
In this example, v is assigned the value Form1. In both examples, the right side of the expression is exactly the same, yet
the value changes depending on the context. To anyone who didnâ ™t write the code, itâ ™s unclear from looking at the
code what is being assigned: the object or the default property of the object. In Visual Basic .NET, parameterless default
properties are not supported and must be resolved.
Another example of an inconsistent feature is the New statement. Consider the following Visual Basic 6 code:
At first glance, the two lines seem to do exactly the same thing. Both c1 and c2 are being set to new instances of Class1.
Yet the two lines have quite different behavior. The statement
Dim c1 As New Class1
means that the variable will be re-created if it is set to Nothing and subsequently reused, whereas the effect of
is that c2 is created once. If c2 is set to Nothing, it will not be re-created automatically if it is referenced again. This subtle
difference in behavior can lead to hard-to-find bugs. In Visual Basic .NET, both statements cause one instance of the class
to be created. If the class is destroyed, it is not automatically re-created if it is referenced again.
Another reason for breaking compatibility is to modernize the language. For example, the meaning of Long is now 64 bits,
Integer is 32 bits, and the keyword Type has been changed to Structure. Some of these changes we can probably attribute
to the “floodgate effect.†Once Microsoft opened the floodgates to new features and changes to fix the language, it
became more acceptable to make other changes that were not quite as critical.
Despite the changes, programmers will still recognize the Visual Basic they know and love. Letâ ™s now look at what
changes you will expect to see moving to Visual Basic .NET.
Visual Basic .NET has been rebuilt for the .NET platform. What does this statement mean? It means that the product has
been rewritten from the ground up. One of the side effects of rewriting Visual Basic is that any similarities with previous
versions of the language had to be added intentionallyâ”you donâ ™t get them for free, as you do when you simply add
new features to an existing code base. A programming language is composed of a million subtle nuances: the behavior of
the Format function, the order of events on a form, and the undocumented hacks that are possible, like subclassing a
formâ™s message loop. Some of these subtleties are not exactly the same in Visual Basic .NET, and after upgrading an
application, you may find small differences in the way the application works.
A good example is the Currency data type. In Visual Basic 6, the Currency data type has 4 digits of precision. In Visual
Basic .NET, the Currency data type is renamed Decimal and has 12 digits of precision. If you run the following line of
code in Visual Basic 6:
MsgBox( CCur(10/3) )
it produces 3.3333. If you run the equivalent line of code in Visual Basic .NET,
MsgBox( CDec(10 / 3) )
the result is 3.333333333333. This is not a huge change, but it underlies a principle of upgrading: Visual Basic .NET is
subtly different from Visual Basic 6, and therefore upgraded applications will be different from their Visual Basic 6
counterparts in subtle ways. In most cases you will not notice the difference, yet itâ ™s important to be aware of the
changes and to test your applications thoroughly after upgrading them. Chapter 2 examines the differences between
languages in greater depth. For now, letâ™s turn our attention to upgrading.
When did Microsoft decide to break compatibility with Visual Basic 6? It was actually in early December 1999, during
the development of Visual Basic .NET. Until that time, Visual Basic .NET was being developed to support the notion of
“Visual Basic 6 sourced†projects that allowed you to edit and compile Visual Basic 6 projects in Visual Basic .NET.
These projects would have a compatibility switch turned on, meaning that the language would be backward compatible
with Visual Basic 6 and would even have access to the old Visual Basic 6 forms package.
By the end of 1999, it was obvious that this strategy wasnâ™t working. Little differences were slipping through: The old
forms package could not be fully integrated into .NET, and the Visual Basic 6 sourced projects could not use some of the
new features of the .NET platform. At that point Microsoft made the decision to break compatibility and instead
concentrate on ensuring that people could upgrade their projects from Visual Basic 6 to Visual Basic .NET.
The effect of the changes and subtle differences in Visual Basic .NET is that, unlike previous versions of Visual Basic,
most real-world projects cannot be upgraded 100 percent automatically. To understand why, consider that for a 100
percent upgrade there has to be a one-to-one correlation between every element of Visual Basic 6 and a corresponding
element in Visual Basic .NET. Unfortunately, this correlation does not exist. The upgrade process is closer to 95 percent,
meaning that the Visual Basic .NET Upgrade Wizard upgrades 95 percent of your application, and you modify 5 percent
of the application to get it working. What does 5 percent mean? If it took you 100 days to write the original Visual Basic 6
application, you might expect to take 5 days to upgrade it. This number is not set in stone—some applications are easier
to upgrade than others, and the experience of the person doing the upgrade is an important factor. To prepare yourself,
make sure you familiarize yourself with Part IV. It discusses how to design your Visual Basic 6 applications to make the
upgrade process much smoother.
Part III of this book is devoted to helping you learn how to make the 5 percent modifications, since this is the essential
skill youâ™ll need for upgrading. Once youâ™ve gotten your application working, Visual Basic .NET has a bunch of
exciting new features that you can add to your application straight away. Chapter 18 of this book discusses some common
ways you can add value to your upgraded application. In fact, we encourage you to think of the upgrade as occurring in
three steps:
1. Use the Upgrade Wizard to bring your application into Visual Basic .NET.
2. Make the modifications to get your application working.
3. Start adding value with the great new features of Visual Basic .NET.
Conclusion
Whewâ”itâ™s time to breathe. Weâ™ve covered a lot of ground in this chapter. First we established that Visual Basic
.NET is not 100 percent backward compatible with Visual Basic 6. We then took a lightning tour of the history of Visual
Basic and saw that, although it is redesigned and restructured, Visual Basic .NET is part of the natural progression of
Visual Basic. We looked at some of the differences between Visual Basic 6 and Visual Basic .NET and discussed some of
the new features you can add to your upgraded applications. We also covered how you can add value to your upgraded
applications and why you should continue to use Visual Basic.
The next chapter takes a deeper look at what the .NET platform is and outlines the significant differences in Visual
Basic .NET. Later chapters go further into the upgrading options and describe what you can do to prepare your application
for the upgrade to Visual Basic .NET. If you feel like jumping straight into upgrading, you may want to skip to Part II,
which starts with a walkthrough of upgrading an application. Welcome to Visual Basic .NET, the future of Visual Basic.
Chapter 2
Visual Basic 6 and Visual Basic .NET: Differences
More than three years ago, the Microsoft Visual Basic team set out to create Visual Basic .NET. At that time managers
would kid the development team by saying that they were making only three “simple†changes to Visual Basic 6: a
new runtime system, a new development environment, and a new compiler. The Visual Basic development team spent the
next three years working on one of these changes: the new compiler. Two other teams provided the development
environment and runtime. As we pointed out in Chapter 1, the end result is not a new version of Visual Basic 6 but an
entirely new product: Microsoft Visual Basic .NET. The name is important for two reasons. First, Visual Basic is still
Visual Basic. Second, Visual Basic .NET is not Visual Basic 7.
This chapter describes the three “simple†changes made to create Visual Basic .NET, including changes to the
runtime, the development environment, and the compiler. Microsoft also added other features to Visual Basic .NET along
the way, including a new forms package and a new debugger, and these are also discussed in this chapter.
Although Visual Basic 6 shipped as part of Microsoft Visual Studio 6, it did not share a common infrastructure with its
siblings C++, Visual InterDev, and Visual FoxPro. The only sharing came in the form of ActiveX components and in
designers such as the DataEnvironment. Although Visual Studio 6 shipped with a common integrated development
environment (IDE) called MSDev, Visual Basic 6 did not participate in MSDev and instead came with its own IDE called
VB6.exe.
Visual Studio .NET ships with a single IDE that all languages built on the .NET Framework share called Devenv.exe. The
Visual Studio .NET IDE is a host for common elements such as the Windows and Web Forms packages, the Property
Browser, Solution Explorer (also known as the project system), Server Explorer, Toolbox, Build Manager, add-ins, and
wizards. All languages, including Visual Basic .NET and C#, share these common elements.
Although the Visual Studio .NET IDE provides a common environment for different languages, the various languages are
not identical or redundant. Each language maintains its own identity in the syntax, expressions, attributes, and runtime
functions you use. When you write code behind a form in a common forms package such as Windows Forms or Web
Forms, the code behind the form is represented by the language you are using. If you use Visual Basic, the events for the
form are represented using Visual Basic syntax and have event signatures almost identical to those you are accustomed to
using in Visual Basic 6. If you use C#, all of the Windows Forms event signatures appear in the syntax of the C#
language.
What happened to the common tools that you have grown to love or hate in Visual Basic 6? They have all been rewritten
for Visual Studio. NET, as youâ™ll see next.
Menu Editor
Do you really want to keep using the same clunky Menu Editor that has been around since Visual Basic 1, shown in
Figure 2-2? We doubt it. So youâ™ll probably be pleased to know that you wonâ ™t find it in the Visual Studio .NET
environment. Instead, you create menus by inserting and editing the menu items directly on a Windows form.
Figure 2-2
Visual Basic 6 Menu Editor.
To insert a new menu in the .NET environment, you drag a MainMenu component from the Toolbox and drop it on the
form. Then you select the MainMenu1 component in the component tray, below the form, and type your menu text in the
edit box that says “Type Here†just below the title bar for your form. Figure 2-3 shows the Visual Basic .NET menu
editor in action.
Figure 2-3
Visual Basic .NETâ™s in-place menu editor.
Toolbox
The Visual Studio .NET Toolbox is similar to the Visual Basic 6 Toolbox in appearance and use. A difference you will
notice right away is that the Visual Studio .NET Toolbox contains the name of each Toolbox item in addition to the icon.
Also, depending on the type of project selected, the Toolbox displays a variety of tabs containing different categories of
controls and components that you can add to a form or designer. For example, when you are editing a Windows Forms
project, the Toolbox will contain categories titled Data, Components Windows Forms, Clipboard Ring, and General. Each
tab contains ADO .NET data components such as DataSet and OleDBAdaptor; system components such as
MessageQueue and EventLog; and Windows Forms controls and components such as Button, TextBox, Label, and
TreeView.
A subtle difference between the Visual Basic 6 Toolbox and the Visual Basic .NET Toolbox relates to references. In
Visual Basic 6, any ActiveX control you add to the Toolbox is also added as a reference within your project. The
reference exists whether you use the ActiveX control on a form or not. In Visual Basic .NET, the items you add to the
Toolbox are not referenced by default. It is not until you place the control on a Windows form or designer that a reference
to that component is added to your project.
Because a reference to an ActiveX control automatically exists when you place the control on the Toolbox in Visual Basic
6, you can use the reference in code. For example, suppose you add the Masked Edit ActiveX control to the Toolbox but
donâ™t add an instance of the control to the form. You can write code to add an instance of the Masked Edit ActiveX
control to a form at runtime, as follows:
Dim MyMSMaskCtl1 As MSMask.MaskEdBox
Set MyMSMaskCtl1 = Controls.Add("MSMask.MaskEdBox", "MyMSMaskCtl1")
MyMSMaskCtl1.Visible = True
If you attempt to place a Masked Edit ActiveX control on a Visual Basic .NET Toolbar, you will find that if you declare a
variable of the ActiveX control type, the statement will not compile. For example, if you attempt to declare the Masked
Edit control, using Visual Basic .NET equivalent syntax, the statement wonâ ™t compile, as follows:
Dim MyMSMaskCtl1 As AxMSMask.AxMaskEdBox
To declare a variable of
the ActiveX control type, you need to place the ActiveX control on a
form. You will then be able to dimension variables of the ActiveX control type.
After you place an ActiveX control on a Visual Basic .NET form, you will find that you can declare variables of the
control type. However, you will not be able to use Controls.Add, as demonstrated in the Visual Basic 6 code above.
Controls.Add is not supported in Visual Basic .NET.
Property Browser
The Visual Studio .NET Property Browser is, for the most part, identical in terms of appearance and use to the Visual
Basic 6 Property Browser. One minor difference is that the default view for the Property Browser in Visual Studio .NET is
Category view, meaning that related properties are grouped under a descriptive category. Alphabetical view is also
supported. The Visual Basic 6 Property Browser, on the other hand, defaults to listing properties alphabetically, although
it supports a categorized view.
The Visual Studio .NET Property Browser can list all of the properties associated with a control or component. This is not
the case when you are using the Visual Basic 6 Property Browser. For example, the Visual Basic 6 Property Browser
cannot list object or variant-based properties. It can display properties for a limited number of objects, such as Picture or
Font, but it cannot represent an object property such as the ColumnHeaders collection of a ListView control. Instead the
Visual Basic 6 Property Browser relies on an ActiveX control property page to provide editing for object properties such
as collections.
The Visual Studio .NET Property Browser allows direct editing of an object property if a custom editor is associated with
the property or the property type. For example, the Visual Studio .NET Property Browser provides a standard Collection
Editor for any property that implements ICollection. In the case of the ColumnHeaders collection for a ListView control, a
ColumnHeader Collection Editor, based on the standard Collection Editor, is provided for you to edit the ColumnHeaders
collection for the ListView. Figure 2-4 shows an example of editing the ListView Columns property.
Figure 2-4
Visual Basic .NET ColumnHeader Collection Editor in action.
Tab Layout Editor
Your days of clicking a control, setting the TabIndex property, and then repeating the process for the several dozen
controls on your form are over. Welcome to the Visual Studio .NET Tab Layout Editor. The Tab Layout Editor allows
you to view and edit the tab ordering for all elements on the form at once. To view your tab layout for the current form,
select Tab Order from the View menu. A tab index number displays for each control on the form. You can start with the
control that you want to be first in the tab order, and then click the remaining controls in the tab order that you want. The
tab index numbers will correspond to the order in which you click the controls. Figure 2-5 illustrates the Tab Layout
Editor.
Figure 2-5
Visual Studio .NET Tab Layout Editor in action.
Forms Packages
The forms package that you use in Visual Basic 6 to create standard .exe projects or ActiveX control projects is essentially
the same package that has been in existence since Visual Basic 1. Visual Basic .NET offers a brand new forms package
called Windows Forms. In addition, Visual Basic .NET gives you a second forms package to help in creating Web
applications: the Web Forms package.
A significant difference between Visual Basic .NET and Visual Basic 6 is that the forms you use with Visual Basic .NET
can be used in any type of .NET project. For example, you can use the same forms with both a Visual Basic application
and a C# application.
The forms package found in Visual Basic 6 is local to that environment. You can use Visual Basic 6 forms only in Visual
Basic 6. Microsoft has tried in the past to create a single, standard forms package that could be shared across multiple
products such as Visual Basic, C++, and Office. The initiative, called Forms3 (pronounced Forms Cubed), never realized
this goal. Forms3 is alive and well in Office but was never made fully compatible with the Visual Basic forms package.
The Windows Forms package reignites some hope of having a single forms standard applied across various Microsoft
products—at least for client applications based on the .NET platform. The ideal of having a single, universal forms
package, however, will need to wait; Visual Studio .NET also introduces a separate forms package for Web applications.
The Upgrade Wizard will upgrade your client-based applications to use Windows Forms and will upgrade your
WebClasses-based applications to use Web Forms.
Visual Basic .NET shares the same debugger with all .NET languages in Visual Studio .NET. This debugger works much
the same as the one in Visual Basic 6 in that you can step through code and set breakpoints in the same way. However,
there are some differences that you should be aware of. These are discussed in the following sections.
What percentage of your Visual Basic 6 application would you say is developed when you are debugging your application
in what is commonly referred to as break mode? Ten percent? Forty percent? Ninety percent? Whatever your answer, the
number is likely above zero. Any problems you encounter while debugging your Visual Basic 6 application are quite easy
to fix while in break mode. This is a great feature that allows you to create applications more quickly. You will miss this
ability in Visual Basic .NET.
The Visual Studio .NET common debugger does not allow you to edit your code while in break mode. Any time you
encounter code that you want to change or fix, you need to stop debugging, make the change, and then start the
application again. Doing so can be a real pain.
The Visual Basic .NET team recognizes that this is not what you would call a RAD debugging experience. The team
hopes to offer an updated debugger that supports edit and continue in a future release of Visual Studio .NET. Until then,
prepare to break, stop, edit, and rerun your application.
If an error or exception occurs while you are running your application, the Visual Basic .NET debugger will stop at the
point where the exception occurred. However, unlike Visual Basic 6, in the Visual Basic .NET debugger you cannot fix
your code or step around the code that is causing the error. If you attempt to step to another line, the application will
terminate and switch to Design view. You will need to determine the source of the exception, fix your code, and then
rerun the application.
When debugging your application using Visual Basic .NET, you will find that your form does not repaint. In fact, if you
place another window over it while you are in break mode, you will find that the form image does not update at all. The
Visual Basic .NET debugger does not allow any events or code to run while you are in break mode.
One benefit of the Visual Basic .NET debugger is that you can debug your paint code and watch your form update as each
statement that paints the form executes. It allows you to pinpoint the exact statement in your code that is causing a
problem with the display. Because the Visual Basic 6 debugger allows the form to repaint constantly, it is difficult to
pinpoint painting problems using the Visual Basic 6 debugger.
Conclusion
As you can see, quite a bit is involved in the three “simple†changes that the teams made to create Visual Basic .NET.
Despite all of these changes, you should find the development environment, compiler, and language familiar. The skills
that you have acquired using Visual Basic 6 are not lost when you upgrade to Visual Basic .NET. The way you create,
run, and debug a Visual Basic .NET application is nearly identical to the process you are already familiar with. After all,
Visual Basic is still Visual Basic. The spirit is alive and well in Visual Basic .NET.
Upgrading Is Optional
When evaluating your arsenal of applications, it is important to keep in mind that upgrading to Visual Basic .NET is
purely optional. It is highly likely that you will have at least some applications that you will never upgrade. Not all
applications will benefit from the new features in Visual Basic .NET. Some applications work perfectly already and there
is no reason to change them. When evaluating an application for upgrading, the following four upgrade options are
available to you:
Donâ™t Upgrade
Leaving an application in Visual Basic 6 is by far the easiest option. Although you forgo the benefits of Visual
Basic .NET and the .NET Framework, you still have an application that runs on a supported platform. This option can be a
long-term or short-term one, depending on your business requirements. You may want to put off upgrading for the
immediate future, or you may decide that there will never be a need to do it. The one important drawback, worth
repeating, is that .NET is the future of the Windows platform. Keeping an application in Visual Basic 6 effectively
relegates it to an obsolete platform.
Partial Upgrade
When your application consists of at least two distinct projects (an executable file and one or more ActiveX DLL projects,
for example), you might perform a partial upgrade. A partial upgrade occurs when one or more components of an
application are upgraded, but not the entire application. This partial upgrade is often a good first step toward a full
upgrade, and it gets you many of the benefits without the effort of a complete upgrade.
A partial upgrade involves using the COM interop layer to communicate between the Visual Basic 6 COM and Visual
Basic .NET managed components. An example would be a two-tier application using a client-side forms-based project
with an ActiveX DLL component containing business logic and data access. You could upgrade the user interface while
leaving the ActiveX DLL in Visual Basic 6. The components in the DLL are then accessed by a COM reference in Visual
Basic .NET that looks like any other Visual Basic .NET component. Donâ ™t let this fool you, though. COM interop
does a lot under the covers to make this all possible.
Throughout this chapter we use the term managed. Managed is the common term used to describe any code that runs
under the common language runtime. It refers to the fact that the runtime automatically “manages†memory, security,
versioning, array bound checking, and other run-time services—a capability programming languages traditionally have
not provided. For example, both Visual Basic .NET and C# create managed executables.
Unmanaged or native code is any code that runs outside the runÂtime. Visual Basic 6 creates “unmanaged†or
“native†executables, that is, code that runs outside the common language runtime. C ++ .NET is unique in that it can
create managed executables, unmanaged executables, and executables that contain both managed and unmanaged code.
A partial upgrade is the most likely scenario for your large-scale legacy applications. You may have components that you
do not want to change or that cannot easily upgrade to the managed world. By performing a partial upgrade, you get the
benefit of improved performance and scalability in the components that require it. A partial upgrade is also the fastest
upgrade path because you can choose to leave the difficult-to-upgrade code as is. This approach preserves your investment
in existing code and minimizes churn in your application. It does have a cost, however, in that the performance
improvements will not be as noticeable as in a fully managed upgrade because of the added overhead of the COM interop
marshaling layer. COM interop between .NET components and COM components is slightly slower than managed
components working together or COM objects working together. This performance impact is not noticeable in most
applications—highly scalable Web sites and applications that do thousands of calls per second to COM methods are the
most affected. Chapter 9 gives you more information on COM interop, including how it works and factors that affect its
performance.
Applications with a dependency on COM components are harder to deploy and maintain than applications fully upgraded
to .NET since COM objects need to be registered and have versioning issues that .NET objects do not.
Complete Upgrade
A complete upgrade is a purebred. It also requires the most effort. It entails not only upgrading code to Visual Basic .NET
but also upgrading all of the technologies to their .NET equivalents. An application that qualifies as a complete (or fully
managed) upgrade does not use any COM objects; it uses only the .NET Framework. This option is probably the most
attractive for developers, and yet it is also the most elusive for larger, more complex applications. More often than not,
with a large code base, portions of your application will need to be rewritten or accessed through the COM interop layer.
Interoperability is a good middle-of-the-road option. It usually means that your application has been upgraded to Visual
Basic .NET, while core technologies remain in COM components. Whether the core code resides in a custom DLL of your
own making or in other components, like ActiveX Data Objects (ADO) or Microsoft XML Parser (MSXML), the COM
interop layer still comes into play. In other words, even though your entire application is in Visual Basic .NET, it is not a
pure managed application, and, again, there are certain implications for performance and scalability.
The Upgrade Wizard imports your Visual Basic 6 applications into Visual Basic .NET. The wizard is activated when you
open a Visual Basic 6 project from Visual Basic .NET. Unlike moving from previous versions of Visual Basic to Visual
Basic 6, upgrading to Visual Basic .NET is hardly a simple operation involving only minor changes. The Upgrade Wizard
has to make significant structural and functional changes to your code. These changes are necessary because of a number
of differences in the structure of Visual Basic .NET. You should not expect the Upgrade Wizard to produce an application
that works perfectly. In the vast majority of applications, you will need to make modifications and fixes to get everything
compiled and running smoothly.
Many of the technologies used in your applications will upgrade easily to their equivalents in the .NET Framework via the
Upgrade Wizard. Certain Visual Basic 6 project types are not supported, however. Two project types that do not map to
Visual Basic .NET and are not supported by the Upgrade Wizard are ActiveX DHTML page projects and ActiveX
document projects. You will have to either rewrite or manually convert these project types to another project type before
upgrading.
The Upgrade Wizard produces a report that highlights problem areas in your code that it encountered during the upgrade
process. This report includes items that the wizard left alone because they could not be upgraded. The wizard also inserts
comments into your code to help guide you through the remainder of the upgrade and provides pointers to additional
documentation. Figure 3-1 shows a sample upgrade report.
Figure 3-1
An upgrade report.
Not Just a One-Trick Pony
Without a doubt, the Upgrade Wizard is the best way to move applications from Visual Basic 6 to Visual Basic .NET.
Although it is possible to do the work by hand, the Upgrade Wizard can do most or all of the grunt work for you. It
enables you, the developer, to focus on a smaller number of details rather than trying to figure out how to cram Visual
Basic 6 classes, modules, and forms into a Visual Basic .NET equivalent.
In addition to making the upgrade process easier to manage, the Upgrade Wizard can also be used to analyze existing
applications so that you can improve them beforehand, smoothing the eventual upgrade. The upgrade report highlights all
of the issues that your application will face. The issues you discover will also help you determine the effort that will be
required to complete the upgrade before you start down the upgrade path.
Weâ™ve talked about Visual Basic 6. What about backward compatibility with versions 5 and earlier of Visual Basic?
The answer is simple. If you are upgrading any projects from Visual Basic 5 and earlier, youâ ™re on your own. The
Upgrade Wizard only supports upgrading Visual Basic 6 projects. Interestingly, the format of Visual Basic 5 and Visual
Basic 6 projects is virtually identical. Although the Upgrade Wizard does understand and will attempt to upgrade
Visual Basic 5 projects, this is not a supported scenario—some Visual Basic 5 ActiveX controls will not work in
Visual Basic .NET. The Upgrade Wizard team has focused solely on testing for Visual Basic 6 projects. If the Upgrade
Wizard works well with your Visual Basic 5 project, consider it a bonus. Not everyone will be so lucky.
While it is possible to open Visual Basic 5 projects with the Upgrade Wizard, converting them to Visual Basic 6 first can
make the move much easier. To do this, open the Visual Basic 5 project in Visual Basic 6, choosing the Update Controls
option. This changes all of your controls to Visual Basic 6 controls (if they are available). Making this step also means
that the Upgrade Wizard will generate better and more predictable results.
For projects older than Visual Basic 5, the best option is to upgrade them to Visual Basic 6 first. When you have them
working, use the Upgrade Wizard to upgrade the Visual Basic 6 version of the project to Visual Basic .NET.
Selecting projects for upgrading is generally a straightforward process. Some applications are no-brainers (they will be
upgraded no matter what); others are trivial enough that the upgrade process can be handled in a matter of days. On the
other hand, more-complex applications can require much more thought and consideration. Multitiered applications using a
wide variety of technologies or applications that are just really big can require an upgrade process that is accomplished in
stages over an extended period of time.
1. Evaluate the benefits of upgrading the application to Visual Basic .NET. What features do you need or would
you like to take advantage of?
2. Evaluate the effort required by the upgrade. What will have to be rewritten or modified?
3. Develop an upgrade plan. Will the upgrade be handled in stages, or will it be done all at once? Will some or all
COM objects be upgraded to their Visual Basic .NET equivalents?
To evaluate the benefits of upgrading, you must carefully examine your application and its architecture, components, and
business requirements and determine an upgrade strategy. Ask yourself if it is worth just upgrading to Visual Basic .NET
without moving from ADO to ADO.NET or moving from the SOAP toolkit to XML Web services, for example. Decide
whether upgrading in stages makes sense.
Whatever governs the project selection process, it is important to keep “value†at the forefront of your mind. Ask
yourself whether the upgrade will enable your application to support new features and technologies that provide benefits.
Ultimately, if there is no value in either the upgrade experience or the result, you are probably dealing with a project that
will live on perfectly happy as a Visual Basic 6 application until it is replaced or circumstances change. The five most
common reasons to upgrade to Visual Basic .NET are
While using standardization as a justification for upgrading a project may seem somewhat facetious, it is often important
to larger organizations. Itâ™s not unrealistic to assume that a good portion of new Visual Basic development within your
organization will be done using Visual Basic .NET. If this is the case, it makes sense to move applications to a single
consistent platform. Savings on training is one of the most obvious reasons, but using one platform also enables
developers to move between applications without having to switch between their Visual Basic 6 and Visual Basic .NET
hats.
Letâ™s face it: We all have pet projects. In fact, the majority of critical business applications started as somebodyâ ™s
pet project at one point or another. Most of us want our âœpetsâ to survive, grow, and thrive.
While it may have some detractors, Visual Basic .NET is pretty cool. It includes a whole host of features that finally make
Visual Basic a player in the object-oriented world. Sometimes the benefits of upgrading to Visual Basic .NET are
immediate and obvious. A case in point is inheritance. We are all familiar with the practice of implementing common
interfaces and code within Visual Basic:
The problem is that this approach generates a whole lot of duplicate code and is a maintenance headache. Inheritance in
Visual Basic .NET offers a much better solution.
We found that implementing inheritance in one project containing a host of similar classes that represented various related
functional tasks resulted in a 75 percent reduction in overall code size. The benefits were immediate and tangible. Bug
fixes were simplified and required less work to implement. (They needed to be fixed in only one place instead of in
many.) Upgrading also made it possible to implement a consistent error-handling scheme across all classes, resulting in a
much cleaner project.
Do you have a performance-critical application? Does your application need to be scalable, and is performance key to the
success of the application? If so, you will definitely want to upgrade your application to Visual Basic .NET. Visual
Basic .NET and the .NET Framework have features that can improve scalability and performance in most applications
when used correctly. The ADO.NET data access object model, for example, provides in-memory local data caches and a
data reader object that is optimized for speed. Also consider that ASP.NET pages are compiled and have improved
caching mechanisms and dramatically enhanced session state management capabilities.
Two examples of major contributors to improved application performance are the SqlDataReader and StringBuilder utility
classes. Letâ™s take a quick look at what these features can do for you.
The SqlDataReader class is analogous to the Forward-Only cursor Recordset from ADO. It is a network-level data
provider that offers a low-overhead object model and fast queries to Microsoft SQL Server databases. The result is that
you can often double your data performance just by substituting SqlDataReader for a classic ADO Recordset. For more
information on upgrading your existing ADO code to ADO.NET, see Chapter 20.
String Concatenation
Not everyone realizes that a major problem area for performance in Visual Basic 6 is string concatenation. This is a very
wasteful operation with no real alternatives, other than rolling your own solution (usually as a C++-based COM
component). In Visual Basic .NET, it is still possible to do the same old string concatenation, but there is a new utility
object that provides fast and efficient string manipulation: StringBuilder. StringBuilder is a class found in the System.Text
namespace.
How Fast Is Fast?
Here is an easy example that demonstrates the performance difference between string concatenation and StringBuilder:
Sub StringTest()
Dim before, after As DateTime
Dim diff As Double
before = Now()
SlowString()
after = Now()
Dim i As Integer
Return result
End Function
Dim i As Integer
Return result.ToString()
End Function
When we ran this code on a laptop, the results showed that using StringBuilder was more than 200 times faster than
simple string concatenation. In other words the FastString() method can be called approximately 200 times in the same
time period it would take SlowString() to complete just once. An added benefit is that the FastString() method required
less memory than the SlowString() method.
When youâ™re concerned about scalability and performance, Visual Basic .NET definitely has a lot to offer.
Plenty More Where That Came From
Of course, the performance improvements are not limited to the SqlDataReader and StringBuilder classes. They are
merely two examples from a vast application framework that offers much, much more. Isolate the performance-critical
areas of your application, list the technologies that are used, and then research the .NET equivalents. Most of the time,
options exist that can dramatically improve the applicationâ™s performance.
Because of the architectural differences in the common language runtime, ASP.NET, and ADO.NET, it is possible,
with a fully upgraded application, to see up to a fivefold increase in performance over an equivalent ASP/Visual
Basic 6/ADO application based on real-world systems. A twofold gain is far more typical, however.
Weâ™ve talked about the performance advantages of Visual Basic .NET, but the .NET Framework also has many
compelling features that are not exclusively performance related. XML Web services are an excellent example of such a
feature. Others are remoting, reflection, and a GDI+ graphics model, for starters.
Moving On
That sums up the benefits of upgrading to Visual Basic .NET. If youâ ™re still planning to forge ahead, itâ ™s time to
move on to evaluate the effort that the upgrade will require.
Four main physical factors determine an applicationâ ™s suitability for an upgrade:
Architecture
Code quality
Technologies
Size
Application Architecture
The overall application architecture will play a large part in the determination of whether and how to upgrade an
application. An application that is Web based, has a simple architecture, and demands high availability and responsiveness
would be a good candidate for a complete upgrade. On the other hand, if the application is form based and has any or all
of the following attributes, you might prefer either to take a tiered approach to the upgrade or to forgo the upgrade
altogether:
Although many variations are possible, the following are the four general types of Windows application architectures.
ASP Web applications This type of application is usually performance driven and needs to be scalable to support hundreds
or thousands of concurrent users across the Internet or intranets. It is one of the simplest kinds of applications to upgrade
to ASP.NET because of the similarities between the two technologies. ASP components on the client end can, with some
effort, be converted to ASP.NET using any of the managed languages. Middle-tier components written in Visual Basic 6
can be upgraded using the Visual Basic .NET Upgrade Wizard, the data access components can be converted to
ADO.NET, and any third-party components can be accessed through the COM interop layer. By their nature, these
applications depend on performance and scalability—two things the .NET Framework delivers. All other factors being
constant, this type of application will probably see the biggest benefit from an upgrade. The one downside to upgrading
ASP applications is that there is no upgrade tool to do the grunt work for you. The middle-tier components can take
advantage of the Upgrade Wizard, but the ASP pages all need to be upgraded by hand.
Forms-based client/server applications A typical application that uses this architecture consists of a fat client, possible
middle-tier COM components with business logic, a data access layer, and a connection to a data store. These applications
are different from thin client/server applications in that some of the processing and business logic resides in the client. Fat-
client systems come in all shapes and sizes, some with ActiveX controls and active documents embedded in a Web
browser and others with client-installed executables, COM components, and third-party controls. Unless their design is
fairly straightforward, these applications require more work to upgrade to the .NET Framework. Since the middle-tier
components are much the same as in a thin-client system, the upgrade of the middle tier will not pose any more of a
problem than upgrading a thin-client system. However, upgrading the client may be more difficult than the transition from
ASP to ASP.NET for thin-client systems. The difficulty in upgrading the client lies in the fact that Visual Basic has been
completely redesigned for Visual Basic .NET, and the functions and controls do not map exactly to their counterparts in
Visual Basic 6.
Enterprise architecture with legacy or distributed systems Applications that contain back-end legacy systems, such as
mainframes, can greatly benefit from Visual Basic .NET and its new features. Rewriting portions of the system to take
advantage of the new XML Web services application architecture can improve code modularity and maintainability and
can also increase accessibility to those legacy systems. With XML Web services, it is possible to expose and consume the
functionality and data of the legacy system essentially by writing a wrapper component that exposes its interfaces through
an XML Web service. This allows local and remote clients or components to easily interact with and program against the
legacy system. The problem here is that any performance or scalability benefits would be hard to achieve, since the legacy
systems are more often than not the performance bottlenecks. Although there might not be a huge performance benefit,
your application can benefit architecturally by becoming more distributed and more flexible. XML Web services enable
this by using SOAP as the underlying implementation, which is more flexible than DCOM in many ways.
Stand-alone applications Stand-alone applications are typically form-based applications such as games, graphics
programs, controls, or general-purpose utilities. Simple applications are typically easy to upgrade.
Code Quality
Code quality will have a great deal of influence on an upgrade strategy. Some of the factors that will make your Visual
Basic 6 application easier to upgrade include a modular, object-oriented design and good programming etiquette. This is
not to say that applications that do not follow these principles are not upgradable, but the task will involve more effort. As
a test, you can run the Upgrade Wizard and see what issues it discovers. See Chapter 4 for a more in-depth treatment of
good coding practices.
Technologies
The technologies or Visual Basic 6 features that an application uses also affect the amount of effort required to upgrade an
application. A typical area of difficulty involves the use of either many Win32 declared functions or the Visual Basic 6
graphics model. The Upgrade Wizard canâ™t really do anything with the declare statements, so it will leave them alone.
But they should still work, with a few exceptions (which will be highlighted by the Upgrade Wizard). The Visual
Basic .NET graphics model, on the other hand, is so different from what was available in Visual Basic 6 that there is no
direct or indirect equivalent. Any code that uses the Visual Basic 6 drawing model will have to be rewritten. There is just
no way around this.
This point may be self-evident: a larger code base will take more time to upgrade. When youâ ™re becoming familiar
with the .NET Framework, it is best to start with smaller applications. That will give you a chance to become acquainted
with the new constructs, and debugging problems will be easier. Take the code size into consideration when screening
applications to upgrade.
Conclusion
You have a lot of choices facing you. Not all applications are well suited to being moved to Visual Basic .NET. Others
practically compel the move. What you should take away from this chapter is that you should consider applications for
upgrading on a case-by-case basis. Of course, there are many good reasons for upgrading applications; just donâ ™t feel
that you are being forced to do so (unless, of course, you are). Upgrade only if and when it makes sense. When it does
make sense, develop your upgrade plan and determine where you need to focus your efforts.
Chapter 4
Preparing Your Project for the Upgrade to Visual Basic .NET
Now that you have decided to upgrade to Microsoft Visual Basic .NET, itâ™s time to make the modifications necessary
to smooth out the upgrade process. In doing so, you need to focus on issues that could get in the way of a straightforward
upgrade. If you followed the guidelines in Chapter 3, you should already have a good idea of the potential problem areas
in your application. This chapter will help you prepare for your upgrade project by identifying common coding practices
that can cause upgrade issues, above and beyond the issues highlighted by the Upgrade Wizardâ ™s upgrade report.
Keep in mind that rather than addressing how to upgrade, this chapter focuses on identifying changes that you can make to
existing applications to help the upgrade go more smoothly. There are two parts to this topic. First weâ ™ll discuss the
use of up-to-date language and platform features. Then weâ ™ll look at good coding practices and common traps that
Visual Basic developers fall into. Where necessary, examples are provided for clarity.
Visual Basic 6 is almost a wonder of compatibility; it maintains code compatibility for many language features going all
the way back to Visual Basic 1. These features include elements like DefInt, VarPtr, and GoSub…Return that still work,
and work reasonably well. But just because they work does not mean that you should use them. These features and others
are deprecated in Visual Basic 6, and they have been removed completely from Visual Basic .NET. This section covers
the obsolete and deprecated Visual Basic features that you should remove from or replace in your Visual Basic 6
application before upgrading.
As we just mentioned, certain language elements are no longer supported in Visual Basic .NET. Some were eliminated
because they lead to confusing and cryptic coding styles and are not appropriate for the kind of large-scale distributed
application development that is becoming dominant in todayâ ™s business environment. Others are just no longer
meaningful in Visual Basic .NET. Take, for example, VarPtr. Pointers no longer have any meaning in the .NET world. In
fact, pointers defeat the purpose of having a runtime environment like .NET in the first place. The runtime frees the
developer from various tasks, and any Visual Basic language features that conflict with not only the runtime but also the
goal of a scalable language for the enterprise have been eliminated.
These changes are intended to move the language (and the platform as a whole) forward in the most expeditious, and
ultimately beneficial, way possible. The following Visual Basic language features are handled by the Upgrade Wizard but
are not recommended for use in your applications:
DefInt, DefStr, DefObj, DefDbl, DefLng, DefBool, DefCur, DefSng, DefDec, DefByte, DefDate, DefVar
GoTo [LineNum]
Imp, Eqv
The following features are simply not handled, and eliminating them is left as a task for the developer:
GoSub…Return
LSet (for user-defined types)
Given these changes, where should you start? First you must search out areas in your application that use these language
elements and either replace them, or leave them as they are and resolve the issues after you use the Upgrade Wizard. Both
approaches are valid. It is often more efficient to fix a problem area before upgrading than to try to sort out the mess
afterwards. There are times, however, when no alternatives exist in Visual Basic 6 and the most effective approach is to
wait and solve the problem using Visual Basic .NET. This decision is ultimately one that you, the developer, need to make
on a case-by-case basis. If you are unsure, it doesnâ™t hurt to run the Upgrade Wizard just to see what it does, but that is
a subject covered in a later chapter. (See Chapter 5)
Over the years, Microsoft has introduced several data access technologies. Starting with Data Access Objects (DAO) and
followed by Remote Data Objects (RDO), Microsoft started to bring data access to the masses with an accessible COM-
based approach. The big breakthrough was the introduction of ActiveX Data Objects (ADO), with its flexible provider
scheme. ADO was designed to provide a generic replacement for both DAO and RDO. It uses OLE DB to interface to
various data providers, from comma-delimited text files to Microsoft Access databases to high-end database servers.
Visual Basic .NET introduces ADO.NET as the next generation of data access. Why does Microsoft continue to introduce
new data access technologies? It reflects the fact that data access itself is evolving. Writing scalable Web sites is a
different programming problem from querying a Microsoft Access database on the local machine. For this reason,
ADO.NETâ™s n-tier disconnected data access is very different from DAO data binding to an Access database.
Youâ™ll be pleased to know that Visual Basic .NET supports DAO, RDO, and ADO data access code. However, Visual
Basic .NET does not support DAO or RDO data binding. What this means is that if your application has DAO or RDO
data binding, you should either rewrite it as ADO data binding in Visual Basic 6 or rewrite it as ADO.NET data binding
after upgrading the project to Visual Basic .NET.
Good Visual Basic 6 Coding Practices
Now that we have covered the deprecated features in Visual Basic 6, letâ ™s move on to identify the areas where
programming practices can interfere with a smooth upgrade. You may need to adjust your code to make it more
compatible with the Visual Basic Upgrade Wizard. discusses how to use the Upgrade Wizard as a tool to identify and fix
issues in your application. For now we will look at common problematic coding practices that you can correct prior to the
actual upgrade. The Upgrade Wizard can identify most of these issues.
Visual Basic is an interesting language in that it has a default data type—the Variant type. What this means is that any
variable, method parameter, or function return type that is not explicitly specified is considered a Variant. Although this
default frees you from having to worry about types when coding—the Variant type can store all the intrinsic types as well
as references to COM objects—using it is not the greatest programming practice.
One of the most significant impacts of relying on the Variant type is that you lose compile-time validation. Because
Visual Basic cannot know what type you really wanted, it has to generate code to try to coerce your variable to the proper
type at run time. It is not always successful, especially if the type you are using cannot be coerced to the expected data or
object type.
Good programming practice aside, not explicitly declaring your variable types can generate a significant amount of
upgrading work for the developer. This is unfortunate because given explicit types, the Upgrade Wizard can often upgrade
variables and controls to their new Visual Basic .NET equivalents. If the meaning of your code is unclear, the Upgrade
Wizard will not try to guess your intentions. It will simply leave the code as is and generate a warning. This leaves the
task of upgrading the code exclusively to the developer. Why waste time, when the wizard will do the work for you?
Is this method doing string concatenation? Addition? If you canâ ™t be sure, the Upgrade Wizard certainly wonâ ™t be.
Testing the function shows that it is possible to use a variety of different inputs:
An alternative, and more proper, way to define the function would look like this:
or this:
Function MyFunction( var1 As Integer, var2 As Integer ) As Integer
MyFunction = var1 + var2
End Function
With this explicit definition style, we can see exactly what the programmer intends to happen. We also get the benefits of
compile-time validation: The compiler will generate an error if you pass an invalid type to the function (unless it is a
Variant). In addition, the Upgrade Wizard will be able to generate the proper equivalent Visual Basic .NET code without a
problem.
Abstraction
Abstraction is a fairly simple concept. The idea is to produce an implementation that separates the usage of an object,
type, or constant from its underlying implementation. This concept is at the heart of every object-oriented programming
environment. Abstraction insulates the developer from changes in the underlying implementation. When you violate this
principle, you risk making your application more fragile and prone to errors. There are two common areas where Visual
Basic developers get themselves into trouble: constants and underlying data types.
Visual Basic 6 defines a whole host of constants. COM libraries make even more constants and enumerated types
available through IntelliSense. The standard Visual Basic constants are really just pretty names that hide underlying
integer values. Table 4-1 shows an excerpt from the Microsoft Developer Network (MSDN) documentation on mouse
pointer constants.
vbDefault 0 Default
vbArrow 1 Arrow
vbCrosshair 2 Cross
vbIbeam 3 I beam
vbIconPointer4 Icon
vbSizePointer5 Size
vbSizeNS 7 Size N, S
You use these constants to change the mouse pointer, as in the following two examples:
Screen.MousePointer = vbIbeam
Screen.MousePointer = 3
These two examples achieve the same result. They are equivalent in that they both change the cursor to the I beam.
Although they achieve the same result, the second line is hard to read. Unless you are intimately familiar with the
MousePointer constants, it is hard to tell at a glance what result that line of code will actually produce. Using a named
constant makes your code more readable and keeps the intent clear.
The Upgrade Wizard is unable to guess what you intended if you use an underlying value in place of a constant. When it
encounters such a value, it leaves the code in place and inserts a special comment known as an upgrade warning (see
Chapter 8 which you then have to resolve on your own. When constants are used properly, the Upgrade Wizard can
upgrade code to the Visual Basic .NET equivalent (if it exists) with no warnings.
A side issue to the notion of underlying values is that constant values can conflict. Remember that the Visual Basic
standard constants are actually implemented with integers, and thus constants with unrelated function may have the same
value. The problem is that you can often use constants interchangeably. While Visual Basic 6 doesnâ ™t really careâ ”it
has no notion of strict constant or enumerated type enforcement, and one integer is as good as anotherâ ”the Upgrade
Wizard will either change the constant definition to a possibly incompatible type in Visual Basic .NET or will not upgrade
the offending line of code at all (leaving it in place with a warning).
The main lesson here is that when you use constants, make sure you are using them in the correct context. The following
example illustrates this point:
Sub DefaultExample()
Screen.MousePointer = vbIconPointer
Screen.MousePointer = adCmdStoredProc
End Sub
Here vbIconPointer is defined as a MousePointer constant, and adCmdStoredProc is defined as an ADO Command object
constant used for specifying stored procedures (totally unrelated to the MousePointer and not meaningful in this context).
Both of these constants have a value of 4, and it is possible to use them interchangeably in Visual Basic 6. The Upgrade
Wizard, however, will attempt to upgrade the constants to their .NET equivalent. Visual Basic .NET performs strict type
checking for enumerated constants, and the upgraded code will not compile. Getting this right to start with will avoid this
whole class of problems.
Another problematic coding style involves the use of underlying (or implementation) data types instead of proper types. A
case in point is the Date data type in Visual Basic 6. The Date data type is implemented as a Double. What this means is
that you can coerce a Date to a Double and/or store the contents of a Date in a Double. Some developers may have used a
Double directly instead of the Date data type. While this will work just fine in Visual Basic 6, it will cause problems for
the Upgrade Wizard (and therefore for you) and will categorically not work in Visual Basic .NET. In the Microsoft .NET
Framework resides a new Date object, and the implementation is significantly different (and hidden from the developer).
The new Date object supports much larger date ranges and higher-precision time storage than the Visual Basic 6 Date
type. The only way to ensure that your code upgrades properly is to always use the Date data type and the date/time
manipulation functions. Resist using the underlying Double value.
Date variables are stored as IEEE 64-bit (8-byte) floating-point numbers (which is the definition of a Double). The Double
is capable of representing dates ranging from January 1, 100, to December 31, 9999, and times from 0:00:00 to 23:59:59.
The following diagram shows how a Date is stored within said Double.
Try out the following example in Visual Basic 6:
Dim dt As Date
Dim dbl As Double
dbl = 37094.776724537
dt = dbl
Debug.Print dt
Debug.Print dbl
7/22/2001 6:38:29 PM
37094.776724537
You can adjust the date by adding or subtracting any Integer value to the double, and you can alter the time by adding or
subtracting any Decimal value less than 1.
Taking advantage of implementation details rather than using the abstracted types defeats the purpose of abstraction in the
first place. Abstraction is of significant use to any application because it allows you to change implementation details over
time without requiring interface or coding changes. When you violate this rule of abstraction and take advantage of the
underlying implementation, you risk having your application crumble to pieces when the implementation changes (as it is
likely to do over time).
Binding refers to the way that objects and methods are “bound†by the compiler. You can think of the different
binding types in the context of variable declaration and usage. Early binding is the most explicit form of variable
declaration and usage. Late binding is the least explicit form of variable declaration. Soft binding is somewhere in the
middle. Confused yet? Read on.
Early Binding
Early binding refers to the use of strongly typed variables. Strongly typed means that each variable type is defined
explicitly and that the Variant type is never used. Specifying the type of variables explicitly relieves Visual Basic of the
task of second-guessing your work when you compile your application. It can distinguish the variable types and thus
generate more optimized code (code that is faster and less resource intensive). It also enables the programmer to catch
programming problems at compile time because the compiler will generate errors when nonexistent object methods are
called or when parameter and return types arenâ ™t compatible.
Late binding occurs when the Variant type is used (either implicitly or explicitly). When you declare a variable as Variant,
the compiler cannot know your exact intentions. The compiler then inserts additional logic to bind the method or property
to the object during program execution. It becomes the responsibility of the runtime environment to catch any binding
errors. Execution is less efficient because the runtime environment must resolve the variable types before it can perform
an operation or method call. Take, for example, the following Visual Basic 6 method, which sets the Caption property
value of a given label:
The compiler must insert code to evaluate whether lbl is an object and, if so, determine its default property. Furthermore,
the runtime must check that the contents of value are valid for the default property of lbl. This step adds processing
overhead to a single line of code. While the performance consequences are not obvious in a small application, large-scale
enterprise or Web applications will notice the difference.
In preparation for using the Upgrade Wizard, you should strongly type anything that you can. Review your code and
explicitly specify function parameters and return types. Inspect every instance that uses a Variant, and ask yourself
whether it is possible to use a strict data type instead. For example, consider the following function:
This function implicitly takes a Variant parameter (intended to be a form) and explicitly returns an Integer. From the
standpoint of good programming practice alone, it is important to ensure that your code clearly identifies the expected
variable types. It becomes even more important with an application like the Visual Basic Upgrade Wizard. Look at what
the Upgrade Wizard does with the code:
An upgrade warning occurred, indicating that the wizard was converting Boolean to numeric.
Part of the problem would have been avoided if lbl were explicitly declared as an instance of a Label control. Doing so
would have ensured that the Caption property upgraded to Text in Visual Basic .NET.
Simple modifications to the code make the result of the Upgrade Wizard more predictable:
The modifications to the original Visual Basic 6 code took very little work and also added a level of clarity to the original
application. Granted, it is not always possible to make these modifications, but it is the ideal case.
Soft Binding
Soft binding is the last of the binding types and is often the most insidious. Imagine a situation involving a form
(MyForm) with a label (Label1). Consider the following example, in which you pass the form and new text for your
control:
Sub Form_Load()
SetLabelText Me, "This is soft binding!"
End Sub
Notice that everything is strongly typed. There is certainly no obvious late binding going on, but something more subtle is
happening. The parameter frm is declared as an object of type Form, while the form being passed to it is of type MyForm.
The type Form does not have a property or a control called Label1. Visual Basic is implementing a form of late binding on
a strongly typed variable. This is what we call soft binding. You have a couple of options for working around issues with
soft binding:
or this:
SetLabelText1 is the preferred option because it will always prevent the wrong type of form from being passed to the
function. SetLabelText2, while better than the original (and while it will upgrade properly), is not as robust because the
assignment of frm to f could fail if the types are not compatible.
Two keywords in Visual Basic 6, Empty and Null, could cause problems when you upgrade your application. Empty is
typically used to indicate that a variable (including a Variant) has not been initialized. Null is specifically used to detect
whether a Variant contains no valid data. If you ever test for either Null or Empty in your application, be sure to use the
IsNull and IsEmpty functions, rather than the = operator. This is a minor change that should be easy to implement and will
minimize upgrade headaches.
Another issue regarding Null involves the way in which various Visual Basic functions handle Null propagation. Take the
following example:
As you can see, a value of Null is passed to the Left function. The standard Left function in Visual Basic 6 is designed to
propagate Null values. So the value of str after Left is called is Null. The problem is that in Visual Basic .NET, functions
such as Left will not propagate Null. This disparity might introduce errors into your application without your realizing it.
The Left$ function provides a way around this problem:
Running this code will cause a run-time error, however (just as its Visual Basic .NET equivalent would). You can resolve
the conflict (without actually testing for Null) by using the string concatenation operator:
Many of you are probably familiar with the Visual Basic 6 As New syntax. It can be used to reduce coding by allowing
the developer to write more compact code. Take, for instance, the following two examples:
' Example 1
Dim c As ADODB.Connection
Set c = New ADODB.Connection
c.Open connStr
Set c = Nothing
c.Open connStr ' Runtime error
' Example 2
Dim c As New ADODB.Connection
c.Open connStr
Set c = Nothing
c.Open connStr ' No error
Example 2 shows how As New simplifies your code by taking care of the type declaration and object instantiation
simultaneously. But the examples also demonstrate a behavioral difference between the two forms of variable declaration.
The first example will produce an error; the second will not. Whatâ ™s going on here? Although declaring variables in
this fashion can be considered equivalent, these examples are by no means equal. We were surprised to find out how As
New had worked in the past. Looking at Example 2, you would think that the first line performs two tasks: creating the
variable and instantiating the object. In fact, it does only the former. It turns out that Visual Basic 6 tries to be smart about
creating the object. The code in Example 2 will not actually instantiate the object until the object is accessed, while
Example 1 explicitly instantiates the object. Also in Example 1, when the connection object is destroyed by setting c to
Nothing, the object is gone, and no object will be created in its place unless the application does so explicitly. In the
second example, the connection object is created only when the Open method is called. The connection object is destroyed
when c is set to Nothing but is created again (by the Visual Basic runtime) when Open is called the second time.
This issue is not a problem in Visual Basic 6. After all, even if you didnâ ™t know how it handles this situation, it would
be safe to assume (as we did) that Visual Basic creates the object when told to. But it is important to note that Visual
Basic .NET changes the behavior of As New to instantiate the object at the same time that the variable is defined. It also
will not create new objects implicitly. If you set a reference to Nothing, you must create a new instance. Visual
Basic .NET will not do it for you. This approach would seem to be the more logical (because it involves less uncertainty
regarding object instantiation and lifetimes), but it could have consequences for your code. Example 3 demonstrates the
behavior of As New in Visual Basic .NET.
' Example 3
Dim c As New ADODB.Connection()
c.Open( connStr )
c = Nothing
c.Open( connStr ) ' Runtime Exception
If you are using this syntax and relying on behavior specific to Visual Basic 6, you need to change your approach.
Everyone else can forget about this scenario and move on—move on, that is, to a discussion of another form of the As
New syntax:
This is where matters get tricky. Notice that we need to create this array of controls only once. If we resize the array, each
additional element acts like the first. Visual Basic 6 will create a new instance of Form when we access the Title property
for any index of this array (provided, of course, that it falls within the bounds of the array). While this ability can be
convenient, it can also cause problems. Imagine a situation in which a bug accidentally causes you to access another
element (which might otherwise be empty). Visual Basic 6 will merrily create the object for you and at the same time hide
a bug in your application. Depending on how your application is written, the bug may not be easily discovered.
To make a long story short, it is best not to define arrays in this way (especially in large-scale applications). Instead, you
can use the following syntax to define the array in a way that is compatible with Visual Basic .NET:
Letâ™s look at the upgrade issue associated with this technique. Visual Basic .NET does support As New, but only for a
single object declaration, in part because of one of the new features in Visual Basic .NET: construction. Every object in
Visual Basic .NET has a constructor of some sort. When you create an object, that constructor is called. (Standard object-
oriented programming concepts work here.) COM objects in Visual Basic 6 did not support construction, and as a result
the runtime could create objects without any real worry; you still needed to do the work of initializing the object (by
opening a connection, creating a file, or whatever). In Visual Basic .NET, those kinds of assumptions cannot be made.
Some objects require parameterized constructors, which further impedes the use of As New in the array case.
What all this means is that in Visual Basic .NET, you must explicitly declare your objects. As New thus becomes an
explicit form of variable declaration and loses any implicit behaviors. If you want to create an array of objects, you must
first create the array and then manually populate it with the objects you need. Although this does remove a shortcut from
the toolbox of the Visual Basic programmer, it increases the clarity of your code. No object will exist until you create it,
which makes for greater predictability all round.
Part II
Upgrading Applications
Chapter 5
Your First Upgrade
In Part I, we explored the differences between Microsoft Visual Basic 6 and Visual Basic .NET, looked at the different
upgrading options, and covered how to prepare your Visual Basic 6 application for the upgrade to Visual Basic .NET.
Now itâ™s time to begin talking about the how-to of upgrading. This chapter walks you through the process of using the
Upgrade Wizard to upgrade a simple project and examining what your new Visual Basic .NET project looks like. It also
introduces you to techniques for fixing problems with the upgraded project and discusses some advanced upgrading
techniques such as upgrading project groups and using the VB6 Snippet Upgrade add-in.
This document is created from a CHM file automatically by an unregistered copy of
CHM-2-Word.
The content of this chapter is skipped, please register CHM-2-Word to get full
features.
For registration information, please refer to: http://www.macrobject.com
The Upgrade Wizard upgrades only projects. But what about project groups? In Visual Basic 6, you could create a project
group that contained several projects. When you pressed F5, all of the projects in the group were compiled together, and
the debugger could step from code in one project into code in another. Project groups are useful when one project
references another.
The Visual Basic .NET equivalent of a project group is a solution. Each solution contains one or more projects. As in
Visual Basic 6, they are all compiled together when you press F5, projects can reference other projects in the same group,
and the debugger can step from code in one project into code in another. Not all projects in a solution have to be Visual
Basic .NET projects. A solution can contain projects written in different languages, such as Visual Basic .NET, C#, and
Visual C++.
To upgrade a project group to Visual Basic .NET, you need to upgrade one project at a time, starting with the most
“downstream†project and working your way up the dependency hierarchy. For example, if your project group
contains an EXE project that references a DLL project, you should upgrade the EXE first, followed by the DLL. Letâ ™s
walk through an example to see how this is done. First we will create a project group in Visual Basic 6, and then we will
upgrade it one project at a time to Visual Basic .NET. Our project group will be simple: it will show â œHello Worldâ
in a message box when you click a CommandButton on a form.
1. Open the project you created earlier in Visual Basic 6 (prjFirstÂUpgrade.vbp). Add a new DLL project by
choosing Add Project from the File menu and selecting ActiveX DLL from the Add Project dialog box. A DLL
project called Project1 is added to the Project Explorer. The two projects are now part of a project group.
2. Visual Basic 6 automatically adds a new class in Project1, Class1. Add the following code to the class:
3. Sub sayHelloWorld()
4. MsgBox "Hello World"
End Sub
5. Close the Class1 Code Editor, and save all the files by choosing Save Project Group from the File menu. Make
sure you save all the files in the same directory as the first project. Accept the default filenames; the project
group will be called Group1.vbp, and the new project will be called Project1.vbp.
6. Now we will make prjFirstUpgrade reference Project1. Select prjFirstUpgrade in the Project Explorer, and
choose References from the Project menu. In the References dialog box, select Project1, and click OK. Figure 5-
12 shows the References dialog box.
Figure 5-12
7. Letâ™s use the Project1 Class1 from prjFirstUpgrade. Open prjFirstÂUpgrade.Form1 in the Form window,
and double-click the Command1 CommandButton to edit the Click event code. Change the Click event code to
the following:
8. Private Sub Command1_Click()
9. Dim c As New Class1
10. c.sayHelloWorld
End Sub
After youâ™ve added this code, the project group will look like Figure 5-13.
Figure 5-13
11. Letâ™s test it out. Press F5 to run the project group. When you click the CommandButton Command1, a
message box pops up with the text âœHello World.â
12. Weâ™ll do two more things before we upgrade the group. First, save the group by choosing Save Project
Group from the File menu. Next, compile the group by choosing Make Project Group from the File menu.
Compiling is necessary because the Upgrade Wizard checks the timestamp of the Project1 project against the
timestamp of the Project1 DLL. If the project file is newer than the DLL, the wizard assumes that the DLL is
out of date and asks you to recompile it.
Now that weâ™ve created a project group in Visual Basic 6, letâ ™s upgrade it to Visual Basic .NET. First weâ ™ll
upgrade the EXE prjFirstUpgrade. Then weâ™ll upgrade the DLL Project1. Finally, weâ ™ll change prjFirstUpgrade so
that it references the upgraded Project1.
1. Start Visual Basic .NET, and upgrade prjFirstUpgrade. (If you need a recap on how to upgrade a project, see the
section âœUpgrade Walkthroughâ earlier in this chapter.) You wonâ ™t need to make any modifications to
the upgraded code, since we replaced Timer.Interval = 0 with code that shows a message box.
2. Now letâ™s upgrade Project1 to be part of this solution. In Visual Basic .NET, from the File menu choose
Add Project and then Existing Project. This opens the Add Existing Project dialog box. Select Project1.vbp and
click the Open button to upgrade Project1. Accept the default upgrade options, as you did with prjFirstUpgrade.
3. Is that all there is to do? Not quite. The project prjFirstUpgrade still references the Visual Basic 6 version of
Project1. We need to delete the reference to the Visual Basic 6 Project1 DLL and add a reference to the
upgraded Visual Basic .NET Project1. First letâ™s remove the reference. Expand the prjFirstUpgrade
references node and remove the reference Interop.Project1_1_1 by right-clicking it and selecting Remove. Note
that in your project, this reference might be called something slightly different. Look for the reference with
“Project1†in the name. For information on adding and removing references, see Chapter 6.
4. Add a reference to the upgraded Project1 by right-clicking References, selecting Add Reference, and then
selecting Project1 in the Add Reference dialog box.
5. Thatâ™s it! Press F5 to run the solution. When you click the Command1 button, you will see a message box,
as shown in Figure 5-14.
Figure 5-14
You now know the basic procedure for upgrading project groups. If your Visual Basic 6 project group contains many
projects, you simply repeat these steps for every project in the group. Start with the most downstream project, and work
your way up the dependency hierarchy.
What is a snippet? A snippet is a fragment of code, such as a procedure or even just two or three lines of code. A Visual
Basic 6 snippet is a fragment of Visual Basic 6 code. It may be sample code that you received by e-mail, or it might be a
function you use all the time. The VB Snippet Upgrade add-in is designed for upgrading snippets. It is useful when you
want to upgrade only a few lines of code, not an entire project. To use it, you must have a Code Editor window open in
Visual Basic .NET. Choose VB Snippet Upgrade from the Tools menu. The VB Snippet Upgrade window opens, as
shown in Figure 5-15.
Figure 5-15
The VB Snippet Upgrade add-in.
Simply type or paste Visual Basic 6 snippet code into this window, and click the Upgrade button. Letâ ™s try upgrading
a simple example. Type the following into the window:
Dim x As Integer
Debug.Print x
Now click the Upgrade button. The snippet is upgraded and inserted into the Code Editor at the current insertion point:
Dim x As Short
System.Diagnostics.Debug.WriteLine(x)
The VB Snippet Upgrade add-in is a great way to familiarize yourself with how code upgrades from Visual Basic 6 to
Visual Basic .NET. The add-in can upgrade several lines of code, a function, or even a whole module. The only limitation
is that the snippet canâ™t refer to functions or objects that are not included as part of the snippetâ ”the VB Snippet
Upgrade add-in needs to examine all of the objects and functions you use in that snippet. If they are defined elsewhere, the
add-in wonâ™t know how to interpret them. You can add references to the snippet, however. A list box on the
References tab allows you to add or remove COM libraries that your snippet references. Any libraries you add here are
also added to your Visual Basic .NET project.
Getting Updates
As this book went to press, the VB Snippet Upgrade add-in was still in development, and Microsoft had not yet decided
whether it would be ready to ship with Visual Basic .NET or be made available later as a download. If it is not installed
with Visual Basic .NET, you will be able to get it from the Visual Basic Web site, http://msdn.microsoft.com/vbasic/.
From time to time this Web site is updated with new versions of the Upgrade Wizard, the VB Snippet Upgrade add-in,
and other resources for upgrading. These updates may contain bug fixes, whitepapers, or new upgrade capabilities.
Conclusion
This chapter has covered the basic mechanics of upgrading projects, project groups, and code snippets. Doing each is
easy, once you know how. The next chapter takes a more detailed look at how to work with your upgraded project in
Visual Basic .NET.
Chapter 6
Common Tasks in Visual Basic .NET
In the previous chapter, we walked through how to upgrade an application. If youâ ™re new to Visual Basic .NET, some
parts of the integrated development environment (IDE) may seem a little foreign. The purpose of this chapter is to
familiarize you with some of the basics of working with applications in Visual Basic .NET. Weâ ™ll start by building a
simple Visual Basic .NET application from scratch. Weâ ™ll use this application to introduce the new IDE and explain
how to create new Visual Basic projects, and then weâ ™ll follow with a discussion of troubleshooting and debugging
techniques and tactics. If youâ™re already familiar with Visual Studio .NET, you might want to skip to the section on
problem solving later in this chapter.
Miscellaneous Items
The Visual Basic .NET IDE has a whole host of new features. It also introduces some new behaviors that will take time
for Visual Basic developers to get used to. This section walks you through some of those behaviors.
If your project contains compile errors, when you build it Visual Basic .NET shows the dialog box seen in Figure 6-7.
Figure 6-7
Your reward for trying to run a project containing errors.
The only purpose of this dialog box is to let you choose between running a stale build of your solution and fixing the
problems and trying again. We canâ™t really see why youâ ™d want to make changes and then run a previous build.
The vast majority of the time you are going to try to fix the problems first. And thatâ ™s why you have the Task List.
The Task List has several different uses. First and foremost, it reports compilation errors. Unlike compiling in Visual
Basic 6, you can now view a list of all the errors and fix them in whichever order you prefer, instead of having the order
dictated by the compiler, and you can do so without having an annoying dialog box pop up for every error that the
compiler comes across. Figure 6-8 displays the Task List for a sabotaged sample application. Double-clicking any of these
items brings you to the related line of code. When the issue is fixed, the item in the Task List automatically goes away.
Figure 6-8
Task List.
Of course, the Task List has many other purposes. In the Options dialog box, displayed by choosing Options from the
Tools menu, you can select Environment and then select Task List. This dialog box, shown in Figure 6-9, allows you to
customize what is displayed in the Task List and gives you the option of adding custom tokens. This feature can be a
useful way of marking parts of your application and prioritizing feature areas. For example, you can create tokens that
indicate the phase in which features will be added. You could create tokens like Beta1, Beta2, and Beta3 and filter based
on the phase of the project you are currently in. You could also create tokens like Bill, Bob, or Janice that indicate areas
that individuals on your team need to address. This approach is often useful when implementing large applications.
Figure 6-9
Visual Studio .NET Options dialog box.
'TODO: Do this
'Beta2: This feature needs to be implemented later
'Janice: Can you deal with this?
Using Breakpoints
Breakpoints work the same way they did in Visual Basic 6. Find a line of code you want to break into the debugger on,
and either click to the far left of the line in the editing window or right-click the line and select Insert Breakpoint from the
shortcut menu. You can remove breakpoints by clicking the red circle or right-clicking the line of code and selecting
Remove Breakpoint from the shortcut menu.
References
Visual Basic .NET has two types of references that you can add to your project: a standard reference and a Web reference.
A standard reference is used to import the namespace of either a COM type library or a .NET reference. Web references
are used exclusively for importing a Web service.
Standard References
As Figure 6-10 shows, three kinds of standard references are available to you through the Add Reference dialog box:
COM references, .NET references, and project references. Use .NET references and project references for referencing
managed components in your project. Adding a COM reference causes the COM type library to generate a managed type
library, thereby enabling the use of COM objects within your Visual Basic .NET project as if they were managed classes.
To open the Add Reference dialog box, right-click the References node in the Solution Explorer, and choose the Add
Reference menu item. This opens the Add Reference dialog box as seen in Figure 6-10.
Figure 6-10
Add Reference dialog box.
To remove a reference, open the References node in the Solution Explorer, right-click the reference you want to remove,
and choose the Remove menu item.
Web References
To enable your application to consume Web services, you add a Web reference. Adding a Web reference generates a local
proxy class that enables you to call methods on the Web service. You add Web references by right-clicking the Reference
node in the Solution Explorer and choosing the Add Web Reference menu item. This opens the Add Web Reference
dialog box, as seen in Figure 6-11.
Figure 6-11
Add Web Reference dialog box.
Problem-Solving Techniques
You need to master a number of skills to become effective at debugging. This section explores some of those skills.
The System.Diagnostics namespace has three important classes: Debug, Trace, and Debugger. The Debug and Trace
classes are related in that you can use both of them to generate debugging information at run time, which is helpful in
diagnosing various application problems. Although their functionality is slightly different, both classes support similar
interfaces, making it easy to move back and forth between them. However, you might also be confused about when to use
one instead of another. The official word from the Microsoft Developer Network (MSDN) documentation is that the Trace
class can be used to instrument release builds. Instrumentation allows you to monitor the health of your application
running in real-life settings, isolate problems, and fix them without disturbing a running system. You can use the Debug
class to print debugging information and check your logic with assertions; you can make your code more robust without
affecting the performance and code size of your shipping product.
The Debugger class is somewhat different. It enables communication between your application and an attached debugger.
This can be useful for inserting code into your application that assists in debugging complex problems or error scenarios.
In addition, some application types (such as Windows services) can be notoriously difficult when youâ ™re trying to
trace the root of failures. You might find these two particular methods of the Debugger object useful:
The Log method enables the application to post messages to the attached debugger.
The Break method enables the application to signal the debugger to break into that line of code. Using this
method is analogous to using conditional breakpoints in Visual Basic .NET, except that Break will work with
any attached debugger.
Using CorDbg
CorDbg is a managed command-line debugger. It can be extremely useful for handling problems with deployment
machines that donâ™t have the Visual Basic .NET debugger installed. We donâ ™t recommend this debugger to the
casual developer, but the simple commands explained in this section will provide a useful introduction to command-line
debugging.
CorDbg is located in the %SYSVOL%\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin directory when
the Framework Software Development Kit (SDK) is installed. You can use the Visual Basic .NET installer to install the
SDK, or you can download the SDK from the MSDN Web site.
First you need to start the debugger. If you add the path to the directory containing CorDbg.exe to your environment, you
can start the debugger by typing cordbg at the command prompt. The following sequence of commands is typical:
When an exception occurs, CorDbg breaks to the prompt and reports the exception type. At this stage you can evaluate
where the exception occurred, get stack trace information, and inspect variable values.
If command-line debuggers arenâ™t your style, the Framework SDK also provides a GUI debugger (DbgCLR, in the
%SYSVOL%\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\GuiDebug directory) that provides a similar
debugging experience to the Visual Basic .NET debugger.
Trying to figure out the cause of an exception in a large compound statement can be a real challenge. When you encounter
obtuse errors (either compile or run-time errors), it can be helpful to break down the offending line of code into smaller,
isolated statements. Thereâ™s no penalty for increasing the line count of your application, so why worry? Breaking
down complex single-line expressions into several separate expressions can also improve your ability to add run-time
error handling to your application.
Letâ™s look at an example of simplifying complex expressions. The following Visual Basic .NET line is a complex
expression because there are several statements on one line:
If a line like this causes errors and you canâ™t figure out where the error occurs, try simplifying it by putting each
statement on a separate line and storing the results of each statement in a temporary variable. The following sample shows
how to do this:
If an error occurs after youâ™ve made the change, itâ ™s simple to determine which statement actually caused the
error. We are not suggesting you do this with all your code, but itâ ™s a useful technique for tracking down the cause of
an error when all you know is that it happens somewhere on a particular line.
This document is created from a CHM file automatically by an unregistered copy of
CHM-2-Word.
The content of this chapter is skipped, please register CHM-2-Word to get full
features.
For registration information, please refer to: http://www.macrobject.com
Chapter 7
Upgrade Wizard Ins and Outs
This chapter focuses on the Visual Basic .NET Upgrade Wizard. Specifically, weâ™ll look at the approach the Upgrade
Wizard takes to upgrade your Visual Basic 6 application to Visual Basic .NET. This chapter will address questions such
as, What will my code look like after it has been upgraded? What types of projects are upgraded? How will my forms,
classes, modules, controls, and ActiveX controls be upgraded? What happens when a component, property, method, event,
or language statement canâ™t be upgraded?
Upgrade Philosophy
When the Visual Basic .NET team decided to not support 100 percent Visual Basic 6 compatibility, the challenge to create
a tool to upgrade any arbitrary Visual Basic 6 project to Visual Basic .NET was enormous. There was much debate within
the team about what approach to take. Ultimately, the Visual Basic team adopted two overriding goals that can be
summed up with the following statements:
The first goal of the Upgrade Wizard is to make the changes to your Visual Basic 6 forms and code as unobtrusive as
possible. You should be able to recognize your code after the wizard has upgraded it. If the wizard transforms your code
into an unintelligible pile of junk, youâ™ll probably grab the closest person, point at your code, and say, â œLook what
Microsoft did to my code. Iâ™m better off rewriting this whole thing myself.â To avoid this situation, the Upgrade
Wizard was designed with the goal of preserving your code as much as possible.
All of your comments should be preserved and in the same place they were before the upgrade. If you include #Ifâ ¦Then
directives, these will also be included in your upgraded project, and you should find them exactly where you put them,
relative to everything else. The wizard tries to upgrade each element of your code—language statements, controls,
components, and ActiveX references—to the elements in Visual Basic .NET that most closely match.
If, for example, the MsgBox statement exists in both Visual Basic 6 and Visual Basic .NET, the wizard will upgrade your
code to use MsgBox. If there is no direct match, the wizard finds a language statement or component that most closely
matches the element you are using. For example, all references to a CommandButton Caption property in code are
upgraded to the .NET Framework equivalent, Button.Text property. If no element in Visual Basic .NET represents the
same element in Visual Basic 6, the code is left as is, an UPGRADE_ISSUE comment is inserted above the nonupgraded
code, and an issue is logged to the upgrade report.
The Upgrade Wizard strives to upgrade all your code from Visual Basic 6 to Visual Basic .NET in a way that allows you
to run the upgraded application immediately after upgrade. This works for simple applications or applications that make
use of only Visual Basic features that the wizard can upgrade. For larger, more complex applications, youâ ™ll need to
fix some compiler errors and run-time issues that the Upgrade Wizard canâ ™t resolve.
This goal fits hand in hand with the âœItâ™s your codeâ objective. The best way to guarantee that your application
will run the same after upgrade is to upgrade the application to use language statements, controls, and objects that behave
identically in both Visual Basic 6 and Visual Basic .NET. For example, the Upgrade Wizard could choose to replace all
instances of the Microsoft ListView ActiveX control in your application with the .NET Framework ListView control, but
it doesnâ™t. The wizard doesnâ™t make this upgrade because by reusing the exact same component, that
componentâ”and thus the applicationâ”has a better chance of behaving the same as before. If the wizard were to replace
your 20 ActiveX controls with 20 noncompatible .NET Framework controls, you would spend more time changing code
to run your application. You can get your application up and running more quickly when the upgraded code contains
language statements, controls, and objects that are compatible and most familiar to you. Once you get the application
running, you can choose to incrementally replace code or components with .NET equivalents, and then test each change as
you go.
If you own Visual Studio .NET, the best way to get a sense of what the Upgrade Wizard can do for you is to run it. To
start the wizard, run Visual Studio .NET and open the .vbp file for your favorite Visual Basic 6 project. The wizard will
greet you. Make your way through each step, selecting the appropriate option (defaults work fine in most cases), and then
sit back and watch the status for each upgraded item. After the upgrade is complete, look through the upgraded Visual
Basic .NET application. As you look over your code, see if you can match up the changes that were made as described in
the following sections.
Wizard Methodology
Before we get into the thick of specific changes that the Upgrade Wizard applies to your code, letâ ™s take you up to
39,000 feet and look down across the broad landscape of what the Upgrade Wizard does. The wizard will upgrade the
following items:
Your Visual Basic 6 project to the equivalent Visual Basic .NET project type
Your Forms to .NET Framework Windows Forms equivalents
Your ActiveX control and object references to use the same ActiveX control and object references
Your Visual Basic language statements to use the same or equivalent language statements provided by Visual
Basic .NET or the .NET Framework (or in some cases the Visual Basic .NET compatibility library)
Figure 7-1 illustrates how the elements of a form are upgraded. Figure 7-2 illustrates how code is upgraded.
Figure 7-1
How a form is upgraded.
Figure 7-2
How code is upgraded.
The lack of support for certain features in Visual Basic .NETâ ”such as Dynamic Data Exchange (DDE) and ActiveX
Documentsâ”means that certain types of Visual Basic 6 applications arenâ ™t well suited to a Visual Basic .NET
upgrade. These types of applications you can consider in the â œoutâ category when it comes to upgrade. However,
just because an application is in the âœoutâ category doesnâ ™t necessarily mean that you canâ ™t use it in Visual
Basic .NET. You might still be able to communicate with your Visual Basic 6 application from Visual Basic .NET
without changing it. For example, rather than upgrading your ActiveX DLL project you may want to leave it as is. You
can create a Visual Basic .NET application that interoperates with the existing ActiveX DLL.
By reusing existing ActiveX components in your Visual Basic .NET applications you can create the Visual Basic .NET
applications more quickly. The general ability of Visual Basic .NET applications to communicate with ActiveX
components is known as COM interop. You may want to take advantage of COM interop when upgrading your
applications so that you donâ™t need to upgrade the application and all of its COMâ ”generally known as
ActiveXâ”dependencies at once. See Chapter 9 for more information on using COM interop.
The Upgrade Wizard will either not upgrade or provide only partial support for the following items:
ActiveX EXE projects
Designers such as Add-In Designer, DHTML Page Designer, DataÂReport, and DataEnvironment
Project Upgrade
Visual Basic 6 and Visual Basic .NET have the same basic concept of a project: a repository in which you can group all
the needed source and resource files to create a specific output file, such as an EXE or a DLL. This section focuses on
three project-related areas—types, attributes, and filenames—and discusses how the Upgrade Wizard handles each.
Project Types
Where possible, the Upgrade Wizard will upgrade your Visual Basic 6 project to an equivalent project type in Visual
Basic .NET, as shown in Table 7-1. Equivalent Visual Basic .NET projec t types exist for common project
types such as Standard EXE, ActiveX DLL, and UserControl.
The version of the Upgrade Wizard that shipped with Visual Basic .NET doesnâ ™t include the ability to upgrade
UserControl or WebClass projects. However, future versions of the Update Wizard that have this capability will be
available for download from the Visual Basic Web site, http://msdn.microsoft.com/vbasic/.
Table 7-1 Visual Basic 6 and Equivalent Visual Basic .NET Project Types
ActiveX EXE No equivalent (Choose between upgrade to a Windows application or a Class Library project.)
A Visual Basic 6 project contains a number of attributes, including Name, command-line arguments, conditional
compilation constant settings, Help settings, version information, and compiler settings. Letâ ™s look at how the
Upgrade Wizard handles each of these attributes.
Project name
The project name for a Visual Basic 6 application is upgraded to be the Assembly Name and Root Namespace name for
the Visual Basic .NET application. The Assembly Name is the name that other applications or tools use to load the EXE
or DLL containing your project. The Root Namespace name is the name that other applications and tools use to refer to
your Visual Basic .NET application or library. The Root Namespace name is roughly equivalent to the project name found
in Visual Basic 6. Since the Visual Basic 6 project name is applied to your Visual Basic .NET project, anywhere in your
code that you fully reference a type by prefacing it with the project name, the code will continue to work as is, without
change.
Command-line arguments
If you create a Visual Basic 6 EXE project that relies on command-line arguments, you can test your application in the
Visual Basic 6 debugger by setting command-line arguments on the Make tab for Project Properties, as shown in Figure 7-
3.
Figure
7-3
Visual Basic 6 command-line setting in the Project Properties dialog box.
The command-line settings arenâ™t upgraded with your project. To edit them in Visual Basic .NET, select the project in
the Solution Explorer, and then select Properties from the Project menu. Within the Properties dialog box, select
Debugging under the Configuration Properties folder, as shown in Figure 7-4.
Figure 7-4
Visual Basic .NET command-line setting in Configuration Properties, Debugging.
Conditional compilation constant settings
If you use #If…Then conditional statements in your code, you can set the value for the condition as a global project
setting. In Visual Basic 6 the setting is located on the Make tab of the Project Properties dialog box, as shown in Figure 7-
5.
Figure
7-5
Visual Basic 6 conditional compilation statement setting in the Project Properties dialog box.
The constant values that you set will be upgraded to Visual Basic .NET. To edit your settings, select the project in the
Solution Explorer and then choose Properties from the Project menu. Within the Properties dialog box, select Build under
the Configuration Properties folder, as shown in Figure 7-6.
Figure 7-6
Visual Basic .NET custom constants setting under Configuration Properties, Build.
Help settings
In Visual Basic 6 you were allowed to have one Help file per project. You specify the Help file name on the General tab
of the Project Properties dialog box, as shown in Figure 7-7. You could also set a project-level Help context ID.
Figure
7-7
Visual Basic 6 Help file setting in the Project Properties dialog box.
Creating Help to go along with your Visual Basic .NET application is different. A Visual Basic .NET application has no
concept of a project-level Help file. Instead, you request Help on a per-form basis by including a HelpProvider component
on each Windows Form that you want to support Help. Because no top-level Help file is associated with a Visual
Basic .NET application, the Upgrade Wizard doesnâ ™t upgrade this project setting.
For more information on how to add Help to your Visual Basic .NET application, refer to the following Help topic:
ms-help://MS.MSDNVS/vbcon/html/vbconHelpSupportChangesInVisualBasic70.htm. You can find the Help topic
by expanding the following nested Help topics:
 Â
Visual Studio .NET; Visual Basic and Visual C#; Upgrading Applications; Upgrading from Visual Basic 6.0;
Introduction to Visual Basic .NET for Visual Basic Veterans; Forms Changes in Visual Basic .NET; Help Support
Changes in Visual Basic .NET.
Version information
The Upgrade Wizard upgrades most version information associated with your Visual Basic 6 application. As illustrated in
Figure 7-8, the following attributes are available on the Make tab of the Visual Basic 6 Project Properties dialog box:
Comments
Company Name
File Description
Legal Copyright
Legal Trademarks
Product Name
Revision
Figure 7-8
The attributes are upgraded to a separate file in your project called AssemblyInfo.vb, which contains project-level
attributes for your project. For example, after you upgrade a Visual Basic 6 project, the version-related attributes are set in
the AssemblyInfo.vb file as follows:
<Assembly: AssemblyTitle("Wow! My upgraded application")>
<Assembly: AssemblyDescription("I love upgrading to VB.Net")>
<Assembly: AssemblyCompany("Microsoft")>
<Assembly: AssemblyProduct("MyUpgradedApp")>
<Assembly: AssemblyCopyright("2001")>
<Assembly: AssemblyTrademark("")>
The revision number that appears in the version number is not upgraded, because Visual Basic 6 and Visual
Basic .NET have different revision number formats. In Visual Basic 6 the revision number is a single part of the
version number. The Visual Basic .NET version number is a two-part entry composed of a revision number and a
build number.
Compiler settings
The Upgrade Wizard doesnâ™t upgrade any Visual Basic compiler options to Visual Basic .NET. Because the Visual
Basic 6 and Visual Basic .NET compilers generate different types of instructions, the compiler options for each product
have nothing in common. The native code optimization settings, such as Remove Array Bounds Checks, donâ ™t make
sense for the Visual Basic .NET compiler. The Visual Basic .NET compiler is required to generate secure code. Therefore,
you donâ™t have the option of turning off certain safety checks, such as array bound checks; all safety checks need to be
in place.
Project Filenames
The filename of your Visual Basic 6 .vbp project file is applied to your new Visual Basic .NET .vbproj file. For example,
if the name of your Visual Basic 6 project is MyProject.vbp, the name of your Visual Basic .NET project will be
MyProject.vbproj.
Each file contained within a Visual Basic 6 project retains the same base filename after upgrade. The file extension for all
upgraded files is set to .vb. For example, if you have a form file in your project called Form1.frm, it will be upgraded to
Form1.vb. What if you have two files with the same base filename, such as Main.frm and Main.bas? Since both files
canâ™t upgrade to the same filename (Main.vb, in this case), the Upgrade Wizard avoids the conflict by renaming one
of the files with its internal name. For example, if the name of module Main.bas is modMain, the file will be renamed
modMain.vb to avoid conflicting with the form file Main.vb.
The Upgrade Wizard upgrades forms and intrinsic controls, such as CommandButton, TextBox, OptionButton, and
CheckBox, contained on the form to the new .NET Framework Windows Forms package equivalent. After the upgrade
process is complete, youâ™ll find that the size and layout of your forms are preserved. If a control cannot be upgraded, a
red label is inserted on the upgraded form to alert you of the problem. Table 7-2 shows the mapping of Visual Basic 6
intrinsic controls to their Windows Forms equivalents.
Table 7-2 Mapping Between Visual Basic 6 Intrinsic Controls and .NET Windows Forms ControlsÂ
Visual Basic 6
Visual Basic .NET Control Class Upgrade Notes
Control Class
System.Windows.-
VB.CheckBox
Forms.Checkbox
System.Windows.-
VB.ComboBox
Forms.ComboBox
Visual Basic 6
Visual Basic .NET Control Class Upgrade Notes
Control Class
DirListBox in Microsoft.Visual-
VB.DirListBox
Basic.Compatibility.VB6
DriveListBox in Microsoft.Visual-
VB.DriveListBox
Basic.Compatibility.VB6
FileListBox in
VB.FileListBox Microsoft.VisualBasic.-
Compatibility.VB6
System.Windows.-
If the frame has a border, the control upgrades to a GroupBox; it
VB.Frame Forms.GroupBox or
upgrades to a Panel if there is no border.
System.Windows.Forms.- Panel
System.Windows.-
VB.HScrollBar
Forms.HScrollBar
For vertical and horizontal lines a label is used. The label is set
No equivalent or
VB.Line to the width of the line. The back color of the label is set to the
System.Windows.- Forms.Label
line color. Diagonal lines do not map.
In some cases you can replace the control with the Microsoft
VB.OLE No equivalent
Web Browser control and use the Navigate property.
System.Windows.-
VB.OptionButton
Forms.OptionButton
System.Windows.-
If the PictureBox has child controls, it upgrades to a Panel;
VB.PictureBox Forms.PictureBox or
otherwise, it upgrades to a PictureBox.
System.Windows.- Forms.Panel
VB.TextBox System.Windows.-
Table 7-2 Mapping Between Visual Basic 6 Intrinsic Controls and .NET Windows Forms ControlsÂ
Visual Basic 6
Visual Basic .NET Control Class Upgrade Notes
Control Class
Forms.TextBox
System.Windows.-
VB.VScrollBar
Forms.VScrollBar
Layout and Design-Time Property Settings
The design-time layout and properties you set on a Visual Basic 6 form and the controls contained on the form are saved
in a special section of the .frm file. The form and controls are saved in property description blocks, as illustrated in Figure
7-9.
Figure 7-9
Visual Basic 6 .frm file in text view.
Windows Forms stores layout and property settings differently. All settings are saved as part of your code in a hidden
block called Windows Form Designer Generated Code. The design-time settings for a Windows form can be found in the
InitializeComponent subroutine within this hidden block.
The Upgrade Wizard maps your Visual Basic 6 design-time settings to generated code in InitializeComponent. For
example, suppose you have a Visual Basic form containing a button named MyButton, as shown in Figure 7-10. (You can
find the MyButton.vbp project on the companion CD.)
Figure 7-10
Visual Basic 6 form with a button on it.
The Visual Basic .frm file for the form will contain this description:
If you upgrade the preceding Visual Basic .frm, the generated Visual Basic .NET code will be
Me.AcceptButton = Me.cmdMyButton
Me.cmdMyButton.Text = "MyButton"
Me.cmdMyButton.TabIndex = 0
Me.AcceptButton = Me.cmdMyButton
Me.cmdMyButton.Size = New System.Drawing.Size(97, 33)
Me.cmdMyButton.Location = New System.Drawing.Point(72, 48)
Me.cmdMyButton.Name = "cmdMyButton"
Me.Controls.Add(cmdMyButton)
End Sub
Weâ™ve removed superfluous settings from the .frm and InitializeÂComponent listings to focus attention on how
the design-time settings of a form and control are generally upgraded. Weâ ™ve selected properties that
demonstrate the more complex mappings that the Upgrade Wizard supports. Most other settings are one-to-one
mappings from (for example) Property X to Property X.
Table 7-3 shows how the original .frm description and the generated Visual Basic .NET code match up line for line.
Table 7-3 Mapping Between Visual Basic 6 .frm Settings and Visual Basic .NET CodeÂ
Begin Me.cmdMyButton = New In Visual Basic 6, the run time takes care of creating the form and
VB.CommandButton System. Â- controls spelled out in the .frm file. In Visual Basic .NET, declaration
cmdMyButton Windows.Forms.- Button and allocation of each control is explicit.
Me.cmdMyButton.- Text =
Caption = "MyButton" Caption is upgraded to Text.
"MyButton"
Figure 7-11
Upgraded Visual Basic .NET form.
Letâ™s take a look at some cases in which the design-time settings donâ ™t upgrade. Weâ ™ll place a Data control on
the Visual Basic 6 form with the MyButton control. Weâ™ll also set the UseMaskColor property of the MyButton
control to True, resave the Visual Basic 6 application, and upgrade it again. The first issue is that the Data control (as
listed in Table 7-2 on page 128) canâ™t be upgraded. A red label on the form represents the Data control. Red labels on
your form indicate that the form didnâ™t fully upgrade.
An interesting characteristic of this upgraded project is that if you build it, you wonâ ™t get any compiler errors. How
can this be true if there is no property equivalent to the CommandButtonâ ™s UseMaskColor property? If the Upgrade
Wizard emitted the design-time property setting in the InitializeComponent section, it would lead to a compiler error. Any
compiler errors that occur within InitializeComponent will prevent you from viewing the design-time form. Since it is
important that you be able to view your form after upgrade, the wizard intentionally omits any design-time properties that
donâ™t map. Instead, it logs an error to the upgrade report.
If you open the _UpgradeReport.htm file included with your upgraded project, youâ ™ll see a summary of the properties
that werenâ™t upgraded for each control. In this example, youâ ™d see an entry for the CommandButtonâ ™s
UseMaskColor property. Figure 7-12 shows the upgrade report that is produced. The report that documents the issue
provides a link to Help, and, where possible, it offers suggestions on how to work around or solve the issue.
Figure 7-12
Visual Basic .NET upgrade report.
Control Arrays
Control arrays are a long-standing Visual Basic feature. A control array allows several controls to share the same events
and initial property settings, allowing you to apply the same behavior to the controls in the control array. You can also
create control arrays in Visual Basic .NET. However, the way you do so is very different from the way you create them in
Visual Basic 6. Think of a control array in Visual Basic .NET as a standard array containing controls. To create a control
array in Visual Basic .NET, you need to write code to declare an array of the particular control type, and then add the
controls to the array. You write additional code to hook the same set of events to each member of the control array. If
youâ™re dealing with a dozen or more control array elements on your form, writing all that code is a time-consuming
process.
The Visual Basic .NET compatibility library introduced earlier in this chapter includes support for control arrays. The
compatibility library contains an array class for each control type. For example, the control array class for Button is called
ButtonArray. The control array class includes methods such as Item and Load that allow you to access control array
elements or add new members to the control array. In addition the control array class includes all of the events for the
underlying control. The events are shared for all elements of the Visual Basic .NET control array, as you would expect
based on how Visual Basic 6 control arrays behave.
The Upgrade Wizard upgrades your Visual Basic 6 control arrays to target control array classes defined in the Visual
Basic .NET compatibility library. Control arrays and all associated code are upgraded as is, with minor syntactical
changes in some cases, as illustrated in the following example.
Consider a Visual Basic 6 form that has two command buttons in a control array called Command1. The following Visual
Basic 6 code loads Command1(2) as a new control array element:
Hereâ™s the Visual Basic .NET code after the Upgrade Wizard has upgraded it:
Command1(2).Visible = True
End Sub
Focus your attention on the usage of Command1, and ignore the pixel-to-twips conversions; youâ™ll see that the code
you need to use control arrays is nearly identical, with a couple of notable differences:
The Load statement becomes a Load method on the base control array element Command1â”for example,
Command1.Load(2).
The control array index is not passed directly as a parameter to the Command1_Click event. The event
parameter is instead obtained from the base control array object Command1 by calling GetIndex and passing
GetIndex the source of the event. The event source will be the member of the control array that you clicked.
Despite these differences, the good news is that Visual Basic .NET and the Upgrade Wizard both support control arrays.
The general approach to upgrading ActiveX controls and references is to reuse the exact same controls and references in
your upgraded Visual Basic .NET application. Although in theory your ActiveX components should work the same as
they did prior to the upgrade, you might encounter some issues. These issues stem from differences between the Visual
Basic 6 and Visual Basic .NET ActiveX control hosting environments. Issues can also stem from the fact that .NET
supports ActiveX components by communicating with them through a COM interop layer. (These issues are discussed in
Chapter 9 and Chapter 13.) This section focuses on some of the exceptions to the rule that ActiveX controls and
references are upgraded to use the same component. In some cases, an ActiveX control or reference is upgraded to use a
native .NET component replacement.
You can place the ActiveX ADO Data control (ADODC) shipping with Visual Basic 6 on a Windows form, but you
canâ™t use it to bind to Windows Forms controls. For example, you canâ ™t place an ADODC control on a Windows
form and hook a TextBox up to it because the ADODC control has no way to communicate the bound data to the
Windows Forms TextBox control. The ADODC control can communicate only with other ActiveX controls, not
with .NET Windows Forms controls.
To resolve this issue, the Visual Basic .NET compatibility library contains a .NET version of the ADODC control. You
can use the ADODC .NET control to bind data to Windows Forms controls and other ActiveX controls. The Upgrade
Wizard automatically upgrades any instances of ADODC ActiveX controls to ADODC .NET controls. The
ADODC .NET control was designed to be a compatible replacement for the ADODC ActiveX control.
How does this help you? If you use the ADODC ActiveX control in your Visual Basic 6 applications, you should expect
your ADO data-bound applications to continue working after you upgrade to Visual Basic .NET.
The Visual Basic .NET compatibility library provides an MSBind .NET class as a compatible replacement for the
ActiveX MSBind object found in Visual Basic 6.
Visual Basic 6 allows you to link a control such as a TextBox with the ADODC control by setting the DataSource
property of the TextBox control. Although the DataSource property appears on the TextBox control, the property is added
and managed by Visual Basic and not by the control itself. In fact the control does not even know that it has a DataSource
property. When you set the DataSource property, Visual Basic creates an instance of MSBind and associates the TextBox
with a field in a database. The database field is specified by the DataField property, which is also added to the TextBox
control and managed by Visual Basic.
The problem is that Windows Forms doesnâ™t provide any support for the DataSource and DataField properties. In fact,
when you place a TextBox (or any other control) on a Windows form, you wonâ ™t find a DataSource or DataField
property. How do you get data binding to work in Visual Basic .NET? You use an instance of a binding collection
(MSBind).
You can add items to the binding collection to associate a property on a control with a field in a database. To bind a
controlâ™s property to a field in a database, you add the control and the property where you want the database data to be
stored to the MSBind collection. If you want to implement custom data binding or binding to nontraditional properties on
a control, use MSBind in your Visual Basic 6 application.
Because there is no DataSource or DataField property, the Upgrade Wizard translates the binding information stored in
the DataSource and DataField properties to binding information set into an MSBind .NET binding collection. Suppose
youâ™re upgrading an application with a TextBox (Text1) bound to an ADODC (Adodc1) control. The TextBox
displays the CompanyName contained in the Customers table. The Upgrade Wizard automatically generates the following
code to hook up data binding:
In Visual Basic 6, the binding collection is called BindingCollection. In Visual Basic .NET, the binding collection
is called MBindingCollection. The M stands for managed, meaning the managed code version of the
BindingCollection.
VB6_AddADODataBinding is called from Sub New so that the data bindings are set up as soon as the form is created.
VB6_RemoveADODataBinding is called when the form is disposed. Data bindings are removed when the form is
destroyed.
In general, if you have an ADODC-based data-bound application in which all the data binding is set up at design time, the
Upgrade Wizard takes care of generating the necessary Visual Basic .NET code to make it work. Because there are no
properties you can set in the Property Browser, updating the application after upgrade requires considerably more work.
Youâ™ll need to update VB6_AddADODataBinding to add or remove bound controls and associated database fields.
SSTab Control
The Upgrade Wizard treats the SSTab control as a special case because the SSTab ActiveX control requires a special
interface called ISimpleFrame. This interface enables the control to contain other controls. Without this interface, you
canâ™t place other controls within the SSTab. If youâ™ve used the SSTab control, you know that it would be pretty
useless if you couldnâ™t place other controls inside it. The control reaches the height of uselessness when you place it
on a Windows form. Windows Forms doesnâ™t support the ISimpleFrame interface, so you canâ™t place any controls
on the tabs within the SSTab.
The Upgrade Wizard recognizes this situation and rectifies it by automatically upgrading your instances of SSTab controls
to instances of Windows Forms TabControls. The Windows Forms TabControl is similarâ ”but not identicalâ ”in
behavior to the SSTab control. Youâ™ll find that after upgrade, the TabControl has a similar appearance to the SSTab
control. All the controls contained in the SSTab control will be found on their respective tabs in the Windows Forms
TabControl. However, youâ™ll notice differences when you write code to operate the TabControl. Most of these
differences stem from the TabControl having a TabPage collection object. You need to manually update the code that sets
array properties, such as TabCaption, to instead set the caption on the specific tab within the TabPage collection.
UpDown Control
The UpDown control uses internal interfaces supported by Visual Basic 6 to buddy up with another control. These
interfaces arenâ™t supported in Windows Forms. Furthermore, because the control is an ActiveX component, it
canâ™t link to a non-ActiveX component such as a Windows Forms Button control. If you place an ActiveX UpDown
control on a Windows form, you should set the ÂBuddyControl property to another control on the form, and then run the
application. The UpDown control doesnâ™t update the buddy control.
The Upgrade Wizard wonâ™t upgrade UpDown controls found on a form. Instead, you must replace each UpDown
control with the Windows Forms NumericUpDown control.
The Visual Basic .NET language is largely a superset of the Visual Basic 6 language. You use the same keywords,
conditional statements, and expressions that you use when writing a Visual Basic 6 application. The Upgrade Wizard will
upgrade most of your code that relates to the core Visual Basic language without making any changes to it. Chapter 11
covers issues related to upgrading code in more detail. This section focuses on how the Upgrade Wizard upgrades your
Visual Basic 6 code.
The Upgrade Wizard upgrades your Visual Basic 6 language statements using a variety of means, which are explained in
the following sections.
Most Visual Basic core language statements, such as If…Then, For…Next, Select Case, And, Or, Do…Loop, On Error
GoTo, and MsgBox fall into this category. The code you see in Visual Basic 6 is the code you get in Visual Basic .NET.
Dim b As Boolean
Dim v As Variant
b = IsEmpty(v)
b = IsObject(v)
b = IsMissing(v)
b = IsNull(v)
Dim b As Boolean
Dim v As Object
'UPGRADE_WARNING: IsEmpty was upgraded to IsNothing and has
'a new behavior.
b = IsNothing(v)
'UPGRADE_WARNING: IsObject has a new behavior.
b = IsReference(v)
'UPGRADE_NOTE: IsMissing() was changed to IsNothing()
b = IsNothing(v)
'UPGRADE_WARNING: Use of Null/IsNull() detected.
b = IsDbNull(v)
Mapping with coercion
Visual Basic .NET disallows direct assignments of incompatible types to each other without an explicit conversion. For
example, you canâ™t directly assign an integer a String value or a Date to a Double. You need to do an explicit
conversion using a function such as CInt or CShort. Some types contain conversion methods, such as ToString or
ToOADate. As an example of how conversion functions are added after upgrade, consider the following Visual Basic 6
code:
Dim i As Integer
Dim TodaysDate As Date
Dim d As Double
i = "7"
TodaysDate = Now
d = TodaysDate
Dim i As Short
Dim TodaysDate As Date
Dim d As Double
i = CShort("7")
TodaysDate = Now
d = TodaysDate.ToOADate 'Converts to an OLE Automation compatible
'Date value
If you need to convert between types in your Visual Basic .NET code, look at the methods implemented by
the .NET System.Convert class.
Dim i As Integer
i = Abs(-1)
This code upgrades to the following Visual Basic .NET code, using the equivalent function from the System.Math class:
Dim i As Short
i = System.Math.Abs(-1)
Mapping to a function provided by the Visual Basic .NET compatibility library
The LoadResString, LoadResData, LoadResPicture, SendKeys, and some App object methods fall into this category. The
following Visual Basic 6 code:
upgrades to
This category includes cases that canâ™t be automatically upgraded. Instead, the wizard generates an upgrade issue and
possibly an UPGRADE_TODO item. The binary string functions, such as AscB, LeftB, MidB, and RightB fall into this
category. For example, the following Visual Basic 6 code:
Several UPGRADE_ messages are emitted in this case. The UPGRADE_ISSUE messages relate to byte-related support
not being available. The UPGRADE_TODO message warns you that the .NET conversion function used might not have
the behavior you desire.
When you use the Option Explicit statement in Visual Basic 6, the compiler forces you to declare all variables with a Dim,
Private, Public, or Static statement (for example). If youâ ™re not using the Option Explicit statement in Visual Basic 6,
the Upgrade Wizard includes the Option Explicit On statement in all forms and modules and takes care of declaring any
undeclared variables for you. If youâ™ve been planning to go through and explicitly declare all of your variables, or
youâ™ve avoided doing it because of time constraints, your wait has been rewarded. The Upgrade Wizard takes care of
this for you.
DefType Is Upgraded
If you use the DefType statement to determine the variable type for unqualified variable declarations, your DefType
declarations will be upgraded to the appropriate type. For example, the following Visual Basic 6 code:
Sub Main
Dim s As String
s = "I'm a String"
End Sub
Data Types Are Upgraded to Fit
The storage size for Visual Basic intrinsic types such as Integer and Long has changed in Visual Basic .NET. In Visual
Basic 6 an Integer is 16 bits and a Long is 32 bits. In Visual Basic .NET an Integer is 32 bits and a Long is 64 bits. Most
of the time it doesnâ™t matter whether you use an Integer instead of a Long. If you have a loop index that goes from 0 to
10, you can nitpick over the performance implications of using one size or another, but generally it makes no difference.
The program works the same way.
Sometimes, however, the size difference matters. For example, if youâ ™re calling Windows API functions that require a
32-bit integer argument, you had better use a Declare statement that declares the argument as 32 bits.
To keep your application running smoothly after upgrade, the Upgrade Wizard declares all of your numeric types to use
the correct Visual Basic .NET equivalent based on size. This means that an Integer variable is upgraded to Short. A
variable of type Long is upgraded to type Integer. In the case of the Variant type, the Upgrade Wizard maps to the closest
equivalent type found in Visual Basic .NET: Object. Table 7-4 gives a mapping of types between Visual Basic 6 and
Visual Basic .NET. The table provides mappings where the name of the type is different between Visual Basic 6 and
Visual Basic .NET. All other types, such as Byte, Single, Double, and String, map as is.
Table 7-4 Mapping of Types Between Visual Basic 6 and Visual Basic .NET
Integer Short
Long Integer
Variant Object
Currency Decimal
ByRef Added to Unqualified Parameters
The default calling convention in Visual Basic 6 is to pass all unqualified parameters as ByRef. Visual Basic .NET, on the
other hand, requires you to qualify all parameters as ByVal or ByRef. Because the Visual Basic 6 default calling
convention is ByRef, the Upgrade Wizard upgrades and qualifies all unqualified parameters with the ByRef keyword.
For example, the following Visual Basic 6 code:
Code-behind forms refers to code that makes reference to a form, control, property, method, or event. Although this is
normally code you write behind a form, you can also reference controls from other project items, such as a class module
or .bas module. We will limit this discussion to code that references a property, method, or event for any form or control
in the project.
Code that references a form or control property is upgraded in a similar fashion to a design-time property setting, as
discussed earlier in the section “Layout and Design-Time Property Settings.†The main difference is that for
properties you refer to in code, the Upgrade Wizard will leave any code that cannot be upgraded as is. In many cases this
will lead to a compiler error. In addition, a comment that begins with UPGRADE_ (such as UPGRADE_ISSUE) will be
inserted on the line immediately above the nontranslated statement. For example, letâ ™s say you attempt to upgrade the
following code:
cmdMyButton.UseMaskColor = True
The Upgrade Wizard leaves the code in as is—without omitting it or commenting it out—because doing so makes the
issue stand out like a sore thumb. You are forced to recognize and deal with the problem before you can run your
upgraded application. The philosophy here is that itâ™s best to get all the bad news up front. Itâ ™s generally much
easier to resolve a problem as soon as you discover it, rather than finding it down the road after youâ ™ve made a bunch
of code changes. If the Upgrade Wizard were simply to emit the UPGRADE_ISSUE command and avoid the compiler
error by commenting out the offending line, you might never pay attention to the issue. If a problem related to the issue
surfaced months later, you might end up spending a significant amount of time tracking down the line of code that the
Upgrade Wizard conveniently commented out for you.
Global Objects
The Upgrade Wizard provides minimal support for upgrading global objects such as App, Printer, Clipboard, and Screen.
In fact, the wizard upgrades some methods for the App and Screen objects, and thatâ™s all. You will need to manually
upgrade the code related to the other global objects. For example, to upgrade your Printer objectâ“related code, use
the .NET System.Drawing.Printing.PrintDocument class. Chapter 12 contains more information on upgrading global
objects.
Each class module and user control upgrades to an equivalent class module and user control in Visual Basic .NET. Table
7-5 lists the upgrades for class attributes of class modules and user controls.
Table 7-5 Class Attribute Mappings
Private Friend
Multiuse Public
The ReadProperties and WriteProperties events for persistable classes and user controls are not upgraded because
the .NET Framework doesnâ™t support the same notion of data persistence that ActiveX supports. The .NET
Framework requires that clients of a .NET server persist the properties of the server by generating code to set server
properties to initial property values. Clients persist ActiveX servers, on the other hand, by passing either a PropertyBag or
a binary stream to the ActiveX server, requesting that the server save the state of its properties to the PropertyBag or
stream. When the server is a Visual Basic–authored ActiveX component, the client will invoke the ReadProperties and
WriteProperties events of the component. After the server has been upgraded to a .NET server, the client directly obtains
the property values for the ActiveX component without invoking events such as ReadProperties and WriteProperties.
The bottom line is that you donâ™t need ReadProperties and WriteProperties to create a persistable Visual Basic .NET
server class. You can remove this code after upgrade.
DataSource Classes
For a Visual Basic 6 class in which the DataSourceBehavior property is set to vbDataSource, the class is upgraded to
implement the DataSource interface. Your code contained within the GetDataMember event is upgraded to the
GetDataMember method of the implemented DataSource interface. You can test your upgraded class by assigning an
instance of it to the DataSource property of a control such as the Visual Basic 6 DataGrid. The DataGrid should display
the data returned by your upgraded Visual Basic .NET DataSource class.
If youâ™ve written a Visual Basic 6 application based on ADO, it will upgrade as is, including code related to ADO data
binding. In addition, when upgrading your ADO-based application, the Upgrade Wizard will promote all references to
ADO versions 2.6 or earlier to ADO version 2.7. If you based your application on DAO or RDO, the DAO- and RDO-
related elements of your application will also upgrade as is as long as you are not using DAO or RDO data binding.
Data Binding
To support data binding, any Microsoft ADODC ActiveX controls you use will automatically be upgraded to use a .NET
ADODC control. The .NET version is designed to be compatible with the ActiveX version.
Visual Basic 6 provides internal support for DAO and RDO data binding. There are neither internal supports nor public
classes in Visual Basic .NET to support DAO or RDO data binding. If your application requires data binding, you should
manually replace your DAO- or RDO-related elements with ADO-related elements. You can make the changes to your
Visual Basic 6 application, test the changes, then upgrade your ADO-based Visual Basic 6 application to Visual
Basic .NET.
DataEnvironment
If you use a DataEnvironment designer in your Visual Basic 6 application, the wizard will create a Visual Basic .NET
DataEnvironment class—contained in its own .vb file—and add it to your upgraded Visual Basic .NET project. The
DataEnvironment class will contain all settings and objects found in the original Visual Basic 6 DataEnvironment. Any
code that references the DataEnvironment will continue to work. You should be able to get your upgraded application to
work with the generated DataEnvironment class at run time.
Visual Basic .NET doesnâ™t provide a way for you to edit DataEnvironment properties at design time. To change
property settings, you need to edit the code contained in the generated DataEnvironment class file.
Designers
Visual Basic .NET doesnâ™t include designers such as the DHTML, WebClass, DataEnvironment, and DataReport. In
fact, none of the designers are included with Visual Basic .NET, nor is there any support to load a designer in Visual
Studio .NET.
In some cases the code and design-time settings related to a designer are upgraded to work in the upgraded Visual
Basic .NET application. For example, code associated with your WebClass is upgraded to Web Forms;
DataEnvironmentÂ-related settings and code are upgraded to a generated DataEnvironment class. The CrystalReports
package in Visual Studio .NET supports reading reports stored in DataReport designer format.
Chapter 8
Errors, Warnings, and Issues
This chapter looks at the different errors, warnings, and issues the Upgrade Wizard generates when you upgrade your
Visual Basic 6 projects to Visual Basic .NET.
Each time you upgrade a project, the wizard upgrades most of the code and objects to Visual Basic .NET, but some items
are not automatically changed. These items require manual modifications after the wizard finishes. To understand why,
consider this example. In Visual Basic .NET, the Caption property of a label is now called the Text property.
Label.Caption maps to Label.Text. The Upgrade Wizard knows about this and changes all Label.Caption references to
Label.Text. But what happens if the wizard canâ™t determine whether a particular object is indeed a label? When the
wizard isnâ™t able to determine what data type an object is, it inserts a warning into your code. The following code is a
good example. Assume Form1 is a form, and Label1 is a Label control:
Dim o As Variant
Set o = Form1.Label1
o.Caption = "VB Rocks"
In this example, the variable o is declared as a variant and then assigned to Label1. The Caption property is then set to the
string “VB Rocks.†Because o is a variant and is late-bound, Visual Basic 6 doesnâ™t know what type of object it is
at design time. (This is why you donâ™t get IntelliSense for variants at design time.) Like the Visual Basic 6 Code
Editor, the Upgrade Wizard also has a design-time view of the code. Weâ ™ll follow what the wizard does as it walks
through the three lines of code in our simple example:
Dim o As Variant
The first line is easy; the wizard knows to change Variant data types to Object, so it upgrades the first line as follows:
Dim o As Object
Set o = Form1.Label1
This is a Set statement, so it has to be an object assignment rather than a default property assignment. The variable o is
assigned to the control Form1.Label1 and upgraded as follows:
o = Form1.DefInstance.Label1
The first two lines were upgraded automatically. The final line, however, causes problems:
Because o is late-bound, the Upgrade Wizard doesnâ™t know what to do with the Caption property. Perhaps o is a label,
but perhaps it is another type of control. When the Upgrade Wizard finds code that canâ ™t be resolved at design time, it
inserts a warning. The final line upgrades to the following:
What do you do now? If you run the code, it causes a run-time exception. Often, the best solution is to change the variable
declaration to be strongly typed. In this example, change the first line to
Dim o As Label
After this change, the compiler will report that Caption is not a property of Label by generating a Task List entry and by
highlighting the code with a green underline. You should then change the property to Text. Another good solution that
avoids the problem entirely is to declare o as a label in Visual Basic 6. Declaring it as a label makes it strongly typed, and
the wizard will have no trouble changing Caption to Text. Here is how to rewrite the code in Visual Basic 6 to be strongly
typed:
Dim o As Label
Set o = Form1.Label1
o.Caption = "VB Rocks"
Dim o As System.Windows.Forms.Label
o = Form1.DefInstance.Label1
o.Text = "VB Rocks"
This is a simple example; we can figure out from looking at the code that o is a label. However, the wizard doesnâ™t
know this. It has a set of rules for converting Visual Basic 6 to Visual Basic .NET that it follows for each property,
method, and event of a particular object type. When a variable is late-bound, the wizard doesnâ ™t know how to interpret
its properties.
Couldnâ™t the Upgrade Wizard simply guess the object type? Other examples show the futility of this strategy. In the
following example, even the original author of the code wouldnâ ™t be able to determine what type of object o will be
set to at run time. Letâ™s assume that Label1 is a label and Text1 is a text box:
Dim o As Object
If someVariable = 1 Then
Set o = Me.Text1
Else
Set o = Me.Label1
End If
Itâ™s impossible to know whether o will be a label or a text box until the code actually executes.
EWI is the common term for the errors, warnings, and issues the Upgrade Wizard inserts into your code. EWIs alert you
to problems like the preceding one. EWI comments are inserted on the line before the problem. The comment text is in
two parts: the message and the link to Help. In the following code, the underlined part of the text is actually a hyperlink to
the appropriate Help topic:
All EWIs are associated with Help topics that show you how to fix the problem. Because the EWI is inserted as a
comment, it doesnâ™t affect the compilation or execution of your project.
Does the Upgrade Wizard detect every problem during an upgrade? Is every problem marked in your code with easy-to-
follow guidance? Unfortunately, itâ™s not quite as simple as that. Some problems might have only a 0.01 percent
chance of occurring, so what should the wizard doâ”mark every occurrence of the code, generating false alarms in 99.99
percent of the cases, or ignore a potential problem and let it generate a compile or run-time error? The Upgrade Wizard
walks a fine line in determining what it should treat as an error and what it shouldnâ ™t. It warns about most problems it
finds, but as youâ™ll see later in this chapter, there are some that it doesnâ ™t warn about.
There are six types of EWIs. Letâ™s look at the most serious type first.
Upgrade Issues
Upgrade issues are inserted into your code whenever the wizard meets code that will cause a compile error. Upgrade
issues mark items that you need to fix before the program will run. A good example is the OLEDrag method. This method
has no direct equivalent in Windows Forms, so the following code
Text1.OLEDrag
upgrades to
Because the OLEDrag method is not a member of the TextBox object, the Text1.OLEDrag statement causes a compile
error. The compile error shows in the Task List. The upgrade issue does not. Why not? If it did, you would have two Task
List entries for every compiler error instead of one. As youâ ™ve learned, at the end of every EWI comment is a link to a
Help topic describing how to fix the problem. (In this case, you need to reimplement drag-and-drop capability using the
new Windows Forms objects.) Weâ™ve removed the hyperlinks from most of the other examples in this book to save
space.
Upgrade ToDos
Upgrade ToDos let you know when code has been partially upgraded and needs finishing before it will run. This type of
issue commonly arises when you declare a variable of a type that contains a fixed-size array. For example, the following
code defines a type called myType that contains a fixed-size array. It then creates an instance of myType called
myVariable.
Type myType
myArray(10) As String
End Type
Sub Main()
Dim myVariable As myType
End Sub
In Visual Basic 6, this code works without a problem. In Visual Basic .NET, the Type keyword is renamed Structure;
arrays defined with the Structure keyword arenâ™t initializedâ”you have to initialize them yourself. When upgrading
this example, the wizard does what it can: It changes the Type keyword to Structure and upgrades the variables within the
structure to Visual Basic .NET. It creates a Sub to initialize the arrays. In Visual Basic .NET, structures can now have
methods, so the Upgrade Wizard also creates an Initialize method that initializes the arrays for you. The hard work is
done. The only step remaining is to write code that calls the Initialize method. This is where you come in. The Upgrade
Wizard inserts a ToDo EWI that tells you what you need to do. The upgraded code looks like this:
Structure myType
Dim myArray() As String
'UPGRADE_TODO: "Initialize" must be called to initialize instances
'of this structure.
Public Sub Initialize()
ReDim myArray(10)
End Sub
End Structure
Public Sub Main()
Dim myVariable As myType
End Sub
To make the code work, you need to add a line in Sub Main to call the Initialize method. The following code shows the
modified Sub Main:
After you make the modification, the code runs as it did in Visual Basic 6. Like upgrade issues, code marked with ToDo
comments must be fixed before the program will run.
Run-Time Warnings
Run-time warnings alert you to behavior differences between Visual Basic 6 and Visual Basic .NET. A behavior
difference is code that doesnâ™t cause a compile error but might act differently at run time. For example, the Dir
function is used to return the list of files or directories inside a particular directory. In previous versions of Visual Basic,
Dir also returned . and .. (indicating current directory and parent directory). In Visual Basic .NET, the Dir function
doesnâ™t return the . and .. directories. When the following code is upgraded:
Some programs will work perfectly; some will have problems. If you have code that relies on Dir returning . and .., you
need to make modifications. As with other EWIs, a link to Visual Basic .NET help explains the difference and tells you
what to do next. Upgrade warnings show up in the Task List.
Design Issues
Design issues identify differences in the design of a form. For example, suppose you have a project with a form, and
youâ™ve set the formâ™s OLEDropMode property to Manual. When you upgrade the project, the OLEDropMode
property is not upgraded, since there is no direct equivalent in Windows Forms. What happens to OLEDropMode? It is
simply ignored and doesnâ™t appear in the upgraded form. Because the code has no appropriate place to put this EWI,
the Upgrade Wizard puts the following entry in the Form1 section of the upgrade report: â œForm property
Form1.OLEDropMode was not upgraded.â The EWI is associated with a Help topic that explains how to implement
drag-and-drop capability in Windows Forms. Because design issues are recorded only in the upgrade report, they do not
appear in the Task List.
Weâ™ve just looked at the four most common types of EWI. There are two other less common types of EWI, neither of
which shows up in the Task List.
Upgrade notes are inserted as friendly memos whenever code is substantially changed.
Global warnings are inserted into the upgrade report to alert you to major issues, such as differences in data
binding.
Now that weâ™ve discussed the types of EWI that will be generated during upgrade, letâ ™s look at how to use EWIs
to get your project working. At this point, you might have a few questions: How many issues should I expect to see in my
upgraded project? How do I know when Iâ™ve made the right fix? How do I keep track of what is left to fix?
The number of issues depends entirely on your programming style and the size of the project items. If youâ ™ve
followed all the recommendations in Chapter 4 you might minimize the number of EWIs to fewer than 10, or perhaps 0,
per file. As a rule of thumb, forms usually have more issues than classes and modules, since forms have a rich object
model and must be changed significantly when upgraded to Windows Forms.
The best idea is to get your project running as quickly as possible, leaving the run-time differences, noncritical work, and
improvements until later. Fix the compile errors and ToDo items first. That way youâ ™ll be working with a project that
is âœrunnableâ as quickly as possible. You can filter the Task List to show all the EWIs and compile errors or to show
only the compile errors. Start by filtering the Task List to show only compile errors. For information on filtering the Task
List, see Chapter 6.
A good method for working with EWIs is to fix a problem and then remove the EWI in the code so it stops showing up in
the Task List. This ensures that the Task List shows only the issues left to resolve. Some compile errors can also be
postponed. Your application might have code that performs a noncritical function, such as drawing a picture onto a
PictureBox. You could decide to comment this out and add a remark such as the following:
These ToDo comments will show up in the Task List when you change the filter to show all tasks. You can take care of
noncritical functionality later, after youâ™ve done the important work. The main advantage of getting the project into a
runnable state as soon as possible is psychological: itâ™s a satisfying feeling knowing that your project is basically
working, even if you have a few modifications left to make.
How do you know which modification you need to make for each EWI? The Visual Basic .NET Help will assist you with
this. In the beginning, the modifications might take quite some time to fix, since youâ ™re learning about upgrading and
Visual Basic .NET at the same time. The good news is that after youâ ™ve fixed a problem the first time, youâ ™ll find
that fixing the same problem again is much quicker, since you know what youâ ™re doing.
Open an old Visual Basic 6 project. Which EWIs do you think would be generated if you upgraded it to Visual
Basic .NET? If only you had a way to know before upgrading! Perhaps you would change late-binding code or other easy-
to-fix problems before upgrading. In these situations a two-pass upgrade is useful. The concept of a two-pass upgrade is
simple: upgrade the project twice. The purpose of the first upgrade is simply to generate an upgrade report to identify
where the problems are. You discard the first upgraded project after examining the report. Using the upgrade report, you
fix some of the issues in Visual Basic 6 before upgrading a second time. The second pass is the real upgrade. When you
do the second upgrade, you replace the first upgraded project, and you should have fewer issues to fix than in the first
pass. In many cases, the two-pass approach significantly speeds upgrading.
The Upgrade Wizard can generate 50 different EWIs. Letâ ™s examine each one, grouped by EWI type.
Upgrade Issues
<statement> <statementname> is not supported Marks the Calendar statement, Load <formname> statement,
GoSub, or On <expr> GoSub. The workaround for Calendar is to create a class that inherits from the
System.Globalization.Calendar class. You can replace Load <formname> with code that creates a new instance
of the form. GoSub is best fixed by reimplementing the routines as procedures.
<functionname> function is not supported Added when the following functions are used: CVErr; IMEStatus;
AscB, MidB, ChrB, and the other B functions; VarPtr, ObjPtr, and StrPtr.
DoEvents does not return a value Added when code uses the return value of DoEvents. (In Visual Basic 6 it
returns the number of open forms.) Although .NET provides no way to get the number of open forms, you can
implement your own forms collection to do this. See Chapter 15 for more details.
Declaring a parameter As Any is not supported Added when an API has a parameter with a type of As Any. See
Chapter 11for tips on fixing API problems.
Declaration type not supported: <declaration type> Added to variable declarations not supported in Visual Basic
.NET: arrays of fixed-length strings, references to a private enumerator from within a public enumerator,
enumerator values assigned to color constants, arrays with more than 32 dimensions, arrays with negative upper
bounds, and Font objects declared WithEvents.
Constant <constantname> was not upgraded Some constants canâ™t be upgraded to Visual Basic .NET.
Unable to determine which constant to upgrade <constantname> to Added when a constant could upgrade to
one of several Visual Basic .NET constants and the wizard canâ ™t determine from the context which constant
is appropriate.
LSet cannot assign one type to another Added when code uses LSet to assign a user-defined type from one type
to another. The solution is to add a custom CopyTo method to the structure that copies the property in the source
structure to corresponding properties in the destination structure.
COM expression not supported: Module methods of COM objects Added when code uses a module method.
This issue is discussed in Chapter 13
Objects of type vbDataObject are not supported Added to the VarType function when the Upgrade Wizard
detects that youâ™re testing for VbVarType.vbDataObject (value 13). Objects that donâ™t support the
IDispatch interface canâ™t be used in Visual Basic .NET. This is a rare issue.
<objecttype> object <object> was not upgraded Added to code that uses the Printers collection, Forms
collection, or Scale-modeConstants.
Property <object>.<property> was not upgraded Added when a property cannot be upgraded automatically. See
Appendix A for a complete list of properties and their mappings from Visual Basic 6 to Visual Basic .NET.
<objecttype> <object> could not be resolved because it was within the generic namespace <namespace> Added
when code uses members of a variable declared as Form or Control. For information on fixing this type of soft-
binding issue, see Chapter 10.
Unload <object> was not upgraded Added when Unload is used with a soft-bound form or control.
A string cannot be used to index the <variablename> control collection In Visual Basic 6, all collections could
be indexed by a name or by a numeric key. In Visual Basic .NET, only hash tables and the VB collection can be
indexed by a name. Most other collections, such as the Windows Forms controls collection, have only a numeric
key index.
Event parameter <parameter name> was not upgraded Added when a form has an Unload or QueryUnload
event. Windows Forms doesnâ™t support the Cancel property for Unload or the UnloadMode property for
QueryUnload.
Form property <formname>ScaleMode is not supported In Visual Basic .NET, you canâ™t set the scale mode
at run time.
<objecttype> property <variable>.<property> is not supported at run time Added when code sets a property that
is read-only at run time.
ListBox│ComboBox property <control>.NewIndex was not upgraded Added when code uses the NewIndex
property of a ComboBox or ListBox control. NewIndex is not supported in Windows Forms.
<control> was upgraded to a Panel, and cannot be coerced to a PictureBox PictureBoxes are upgraded to Panels
if they contain child controls. This EWI is added when the Panel is then passed to a method that accepts a
PictureBox parameter.
<controltype> Property <control>.<property> does not support custom mouse pointers Added when code sets a
custom mouse pointer. Custom mouse pointers are not automatically upgraded to Visual Basic .NET. You can
rewrite your custom mouse pointer logic to use cursors. You can then load a cursor from a .cur file or a .resx file
and set the cursor for a particular control.
Upgrade ToDos
Uncomment and change the following line to return the collection enumerator Added to collection classes. The
Upgrade Wizard upgrades most of the collection class code, but you need to finish the upgrade by adding the
collection enumerator. In most cases, this means simply uncommenting one line.
Code was upgraded to use <function> which may not have the same behavior Added to code that assigns a
ByteArray to a string. In Visual Basic .NET, strings with an odd number of bytes will cause an exception
because strings are stored in Unicode and therefore need two bytes per character. You should modify your code
to ensure that the byte array is of an even length.
“Initialize†must be called to initialize instances of this structure As we discussed previously, this EWI is
added to types (upgraded to structures) when the type contains a fixed-size array. When declaring a variable of
this type, you will need to call the Initialize method to initialize the array.
Add a delegate for AddressOf <methodname> Added to code that uses AddressOf. See Chapter 11for more
information on using AddressOf in Visual Basic .NET.
LoadResStrings method may need to be replaced The Visual Basic 6 AppWizard often generated a
LoadResStrings function. This function is not automatically upgraded to Visual Basic .NET. If your code uses
the unmodified AppWizard function, you can replace it with the suggested code in Help.
Upgrade Warnings
As <variable type> was removed from ReDim <variable> statement Arrays can be redimensioned using the
ReDim statement only as the type they were originally defined as.
Arrays canâ™t be declared with New You canâ™t declare arrays of classes with New. Variable declarations
of the form Dim x(10) As New Class1 are not allowed in Visual Basic .NET. The workaround is to initialize the
classes when they are declared or as they are needed.
Structure <variable> may require marshalling attributes to be passed as an argument in this Declare statement
Added to APIs when a structure is being passed. For information on marshalling attributes, see Chapter 11.
Arrays in structure <structure variable name> may need to be initialized before they can be used If a COM
interface defines a structure with a fixed-size array, the array might need to be initialized before it can be used.
Array â˜xxxâ™ may need to have individual elements initialized If an array of structures has been declared or
redimensioned using ReDim, and the structure contains a fixed-length array, you will need to call Initialize for
each member of the array.
Lower bound of array <variable> was changed from <lowerÂbound> to 0 Arrays in Visual Basic .NET must
have a lower bound of 0. If the array has a lower bound of anything other than zero, it is changed to 0 and the
wizard generates this EWI.
Canâ™t resolve the name of control <controlname> When the Upgrade Wizard needs to resolve the name of a
control within a control array and it canâ™t because the index is a variable, this warning is added.
ParamArray <parameter> was changed from ByRef to ByVal In Visual Basic .NET, ParamArrays can be passed
only by ByVal.
Use of Null/IsNull detected Null is not supported in Visual Basic .NET, so it is upgraded to the closest
equivalent, System.DBNull.Value. IsNull is changed to IsDBNull. You canâ™t rely on DBNull having the
same behavior as Null in Visual Basic 6. Also, Visual Basic 6 functions that accepted Null as a parameter and
could return Null—such as Left and Right—now work only with strings. Null propagation is no longer
supported in Visual Basic .NET. If Null is used in arithmetic expression, it will cause a run-time exception.
<functionname> has a new behavior Some functions have different behaviors in Visual Basic .NET, and this
EWI warns you of the difference. Here are two of the key differences:
IsObject(<object>) is upgraded to IsReference(<object>) and, unlike its behavior in Visual Basic 6, returns True
if the object is empty and True if the variable is a string. The Dir function no longer returns the directory
entries . and ..
Class instancing was changed from <instancing type> to public Single-use classes are changed to public classes
during an upgrade.
Sub Main wonâ™t be called at component startup If a DLL has a Sub Main in it, it wonâ™t be called when
the first class is created.
Application will terminate when Sub Main() finishes In Visual Basic 6, the application finishes when the End
statement is called, or when all forms and objects are unloaded or destroyed. In Visual Basic .NET, the
application ends when the End statement is called or when the startup object stops running. If your application
has Sub Main as its startup object, the application will end when Sub Main ends. Several workarounds exist,
including the use of System.Windows.Forms.Application.Run to create forms from within Sub Main. Forms
loaded in this manner keep the application running once Sub Main finishes.
<object> event <variable>.<event> was not upgraded Some events canâ™t be upgraded to Visual Basic .NET.
These events are changed into procedures and wonâ™t be fired as they were in Visual Basic 6. You have to
call them yourself if you want to run the code. This applies to the OLEDrag and OLEDragOver events and to all
Font events.
Event <object>.<event> may fire when form is initializedSome events will be fired as the component is
initialized. For example, if a check box has a default state of being selected, when the form is initialized, the
CheckBox.CheckStateChanged event will fire. This leads to subtle differences if your event
code refers to other controls that arenâ™t fully created when this event is fired.
You should modify your code to cope with this new behavior. This EWI is added
for the following events:
OptionButton.Clickupgraded to CheckChanged
ComboBox.Changeupgraded to TextChanged
DriveListBox.Clickupgraded to SelectedIndexChanged
Couldnâ™t resolve default property of object â˜<objectname>â™ In some projects, this is the most common
EWI you will see. It is added whenever a late-bound variable is set to a value. The Upgrade Wizard canâ ™t
determine whether the assignment is to the variable or to the default property of the variable. Because it is late-
bound, the wizard knows nothing about the variable and therefore generates this EWI.
<function> parameter â˜<parameter>â™ is not supported, and was removed The Wait parameter of
AppActivate, the HelpFile and Context parameters of MsgBox, and InputBox are not supported in Visual
Basic .NET. These parameters are removed during an upgrade.
Assignment not supported: KeyAscii to a non-zero value In Visual Basic .NET, the keypress parameter
KeyAscii (now KeyPressEventArgs.KeyChar) canâ™t be changed. If you have code that sets KeyAscii to 0,
this is upgraded to KeyPressEventArgs.Handled = True. Code that sets it to any value other than 0 is marked
with this warning.
Timer property <controlname>.Interval cannot have value of 0Timer.Interval = 0 deactivated a timer control in
Visual Basic 6. In Visual Basic .NET, use the Enabled property to enable or disable the timer. Setting Interval to
0 causes an exception.
Design Errors
Design errors apply to forms and controls and are usually inserted only into the upgrade report. Weâ ™ve noted the EWIs
that can be inserted into code.
<controltype> control <controlname> was not upgraded This EWI is added to the report for controls that
arenâ™t supported in Windows Forms. The control is replaced with a red label placeholder. This EWI will
occur with the following controls: OLE Container, Non horizontal/vertical lines, DAO Data, RDO Data,
UpDown ActiveX, and the Shape control with the shape set to Oval or Circle.
<controltype> property <controlname>.<propertyname> was not upgraded Added when a property or method
does not map from Visual Basic 6 to Visual Basic .NET. This EWI is also added to code if the property or
method is used at run time.
<controltype> property <controlname>.<propertyname> has a new behavior Added to the report when a
property, method, or event behaves differently in Visual Basic .NET. This issue is added for the following
properties, methods, and events:
o Control.Keypress event
o ComboBox.Change event
o Form.Activate event
o Form.Deactivate event
o Form.Picture property
o Form.QueryUnload event
o Form.Terminate event
o Form.Unload event
o Form.ZOrder property
o HScrollBar.Max property
o Image.Stretch property
o ListBox.Columns property
o ListBox.ItemCheck event
o OptionButton.Click event
o PictureBox.AutoSize property
o Screen.MousePointer property
o TextBox.TextLength property
o UserControl.Picture property
o VScrollBar.Max property
PictureBox property <controlname>. Picture will be tiled Added for a PictureBox with child controls. If a
PictureBox contains child controls, it is upgraded to a panel because PictureBoxes in Windows Forms are not
control containers. The background image of a Panel is always tiled.
ListBox│ComboBox property <control>. ItemData was not upgraded Added when ListBoxes and
ComboBoxes have ItemData set at design time. The ItemData property is not upgraded. (Note that run-time use
of ItemData is upgraded.) The solution is to set the ItemData information for the ListBox or ComboBox at run
time.
Only TrueType and OpenType fonts are supported in Windows Forms Windows Forms doesnâ™t support
raster fonts. If a control has its Font property set to a raster font, the font is changed to the default Windows
Forms font (8-point Microsoft Sans Serif). This issue doesnâ™t apply to ActiveX controls, which maintain
their own fonts.
Global Warnings
<objectname> is not a valid Visual Basic 6.0 file and was not upgraded The project item (form, module, or other
item) has an invalid file format—perhaps it is corrupt or a Visual Basic 4 file. This file is ignored during the
upgrade.
Could not load referenced component <reference>. It is recommended you install Visual Basic 6.0, with all
referenced components, and ensure the application compiles and runs before upgrading. Added when a
reference, such as an ActiveX control or class library, cannot be found. The best way to avoid this issue is to
ensure that the project runs on the machine before upgrading. The Upgrade Wizard needs to load all your
projectâ™s references to examine the type libraries, create wrappers, and instantiate ActiveX controls. If this
problem occurs, the upgrade is canceled.
Could not find file: <projectitemtype> <filename>. Please make sure this file is present before upgrading this
project. Added when a file canâ™t be found. This warning can be issued if a file is located on a network share
that is not available. If this problem occurs, the upgrade is canceled.
<projectitemtype> <filename> is a Visual Basic 5.0 file. For best results, upgrade the <projectitemtype> to
Visual Basic 6.0 before upgrading it to Visual Basic. NET. Added to the upgrade report whenever a project with
a Visual Basic 5 form, class, and so on is upgraded.
The referenced component <reference> is missing a design time license Added when the design-time license for
a control is not installed. This warning usually occurs because a control is installed as part of a setup, without its
design-time license. The Upgrade Wizard needs the design-time license because it instantiates ActiveX controls
during the upgrade so that they can retrieve their properties for a Visual Basic 6 form. The Visual Basic .NET
installation includes a registry file with the licenses for all the Windows Common controls. These licenses are
stored in a registry file in the Extras directory; they are not installed with Visual Basic .NET.
The referenced project <projectname> is either not compiled or out of date. Please re-compile the project.
Occurs when a project references another project that hasnâ ™t been compiled. The solution is to compile the
referenced project to a DLL. If this problem occurs, the upgrade is canceled.
MTS/COM+ objects were not upgraded If a project has references to the Microsoft Transaction Server Type
Library or the COM+ Services Type Library, they are removed during upgrade. These references will not work
in Visual Basic .NET and should be replaced with .NET COM+ services objects. See Chapter 16 for more
details.
The following line couldnâ™t be parsed Added to a code module when a line could not be upgraded because
of syntax errors. The line is copied as is into the Visual Basic .NET module and marked with this EWI.
<objecttype> <objectname> was not upgraded Applies to ActiveX documents and property pages. These project
items canâ™t be automatically upgraded.
Upgrade Notes
There may be differences in databinding behavior Added to the upgrade report whenever an application has
ADO data binding. The warning has a hyperlink to a Help topic that gives general advice about fixing issues
with ADO data binding.
Def <variabletype> statement was removed. Variables were explicitly declared as type <type>. Added to the
declaration section of modules that had statements such as DefInt and DefStr. The variables are changed to be
explicitly declared. This message is purely informative.
<variable> was changed from a Constant to a Variable A constant was changed to a variable because the data
type canâ™t be stored as a constant in Visual Basic .NET. This message is added for constants declared as
colors.
NewEnum property was commented out When collection classes are upgraded, the Upgrade Wizard comments
out the old NewEnum property and inserts a ToDo comment describing how to implement the collection
enumerator.
Object <variable> may not be destroyed until it is garbage collected Added to code that sets an object to
Nothing. Objects set to Nothing arenâ™t actually destroyed until the .NET Garbage Collector performs its
next collection.
IsMissing(<variable>) was changed to IsNothing(<variable>) The IsMissing function was replaced with
IsNothing.
#If #EndIf block was not upgraded because the expression <expr> did not evaluate to True or was not evaluated
If your code has conditional compilation statements, code in the False branch will not be upgraded. The Visual
Basic 6 code will be copied as is into your upgraded application.
Remove the next line of code to stop form from automatically showing Added to multiple document interface
(MDI) child forms when the Visual Basic 6 MDIForm has the property AutoShowChildren = True. The
Upgrade Wizard inserts the line Me.Show into the child form to give it the same behavior as in Visual Basic 6.
The following line was commented to give the same effect as VB6 Code that does nothing in Visual Basic
6—but would have an effect in Visual Basic .NET—is commented out. This applies to code that sets
Form.BorderStyle at run time.
<control>.<event> was changed from an event to a procedureAdded to these events: HScrollBar.Change,
HScrollBar.Scroll, VScrollBar.Change, VScrollBar.Scroll, and MenuItem.Click. The event was changed to a
procedure and called from another event. This change is made because Windows Forms combines some events
that used to be separate events into a single event.
<statement> was upgraded to <statement> Added when a statement is upgraded to a corresponding statement
with a different name. This note is added for the Erase statement, which is upgraded to System.Array.Clear.
Conclusion
EWIs are a valuable part of your upgrade. They alert you to issues in your code and show you how to fix the problems. In
a perfect world we wouldnâ™t need themâ”everything would upgrade without problems. The next best thing is to be
aware of problems and know how to fix them. Looking over the list, youâ ™ll see that many errors can be avoided
entirely by writing your Visual Basic 6 code in a way that prepares it for Visual Basic .NET. Doing a two-pass upgrade is
a great way to see what types of EWIs your Visual Basic 6 program will have when it is upgraded to Visual Basic .NET.
Chapter 9
Using Visual Basic 6 with Visual Basic .NET: COM Interop
This chapter focuses on making your Visual Basic 6 and Visual Basic .NET applications work together. The mechanism
that makes interoperability between the two products possible is known as COM interop. Weâ ™ll start by looking at the
various ways you can create or use existing components that communicate across COM and .NET component boundaries.
Weâ™ll also show you how to debug across calls between Visual Basic 6 and Visual Basic .NET authored components.
Finally, weâ™ll discuss the role of binary compatibility in Visual Basic .NET.
If youâ™ve been creating multitiered applications using Visual Basic 6, your application has likely evolved into a large
system spanning multiple components. Letâ™s say, for example, that you have an application composed of a Visual
Basic standard EXE front end containing ActiveX controls talking to a middle-tier Visual Basic DLL. The Visual Basic
DLL in turn talks to a back-end SQL Server database. Upgrading such an application to Visual Basic .NET in one shot is
nearly impossible. This is where COM interop swoops in to save the day.
COM interop allows you to upgrade one component at a time while keeping the system alive. For example, you can
upgrade your Visual Basic 6 middle-tier component to Visual Basic .NET independently of the user interface (UI)
component. Once you have tested your new Visual Basic .NET component with your Visual Basic 6 UI client, you can
update the client to take advantage of the new Visual Basic .NET server component. At a later date you may decide to
upgrade your Visual Basic 6 client components to Visual Basic .NET Âcomponents. An ActiveX control vendor may
offer a .NET upgrade to your favorite ActiveX control, leading you to replace all ActiveX versions of the control in your
application with the .NET version. Eventually your entire system evolves to .NET, smoothly and without interruption.
You do not need to look far for an example of COM interop at work. If youâ ™re running Visual Studio .NET, COM
interop is right under your nose. The Property Browser is written in C#, a language built on the .NET Framework. Most of
the designers you will find, such as the Windows Forms designer, are written in a language supported by .NET. All of the
wizards are written in either C# or Visual Basic .NET. The Visual Studio .NET environment is a traditional client
application written in C++ that interoperates with these other .NET components using COM interop. The Upgrade Wizard
relies heavily on COM interop to accomplish its tasks. The wizard is a .NET component that calls out to the upgrade
engine, an out-of-process COM EXE server, to upgrade your Visual Basic 6 project. The upgrade engine in turn calls back
to the wizard to provide status. As your application is being upgraded, the status text and progress bar updates you see are
brought to you by way of COM interop.
We look forward to the day when 100 percent of our Visual Studio .NET components are written in Visual Basic .NET.
Until that day, COM interop will be silently at work keeping Visual Studio .NET humming along.
It would be nice to be able to upgrade your entire application to .NET, but in some cases it may not be feasible. For
example, what if your application relies on a COM component for which there is no .NET equivalent? A good example is
a Visual Basic 6 ActiveX document or DHTML page designer for which there is no equivalent component in .NET. In
such cases COM interop can help keep things running without hindering you from moving other parts of your system
forward to Visual Basic .NET.
Although weâ™ve been talking about interoperation among Visual Basic components, the concept of interoperation
applies to all COM components. For example, your application may be composed of a Visual Basic front end talking to a
C++ authored middle-tier component. As long as the components that make up your application are based on COM, your
Visual Basic .NET application can continue to talk to them. Similarly, your Visual Basic 6 application can continue to talk
to a .NET component—authored in any language supported by .NET—as if it were a COM component, without any
changes in your Visual Basic 6 application.
Suppose you have a Visual Basic 6 ActiveX DLL that acts as a language translator, translating a phrase in one language to
another language. Your component is currently limited to rudimentary Spanish. The companion CD includes two Visual
Basic 6 sample applications: TranslatorServer and TranslatorClient. In the following section we will demonstrate how to
call the TranslatorServer component from a Visual Basic .NET client application. First, however, you need to build the
Visual Basic 6 server component by following these steps:
3. Open TranslatorServer.Cls, and you will find that it contains the following code:
To test your application in Visual Basic 6, run Visual Basic 6 and open the Visual Basic 6 TranslatorClient application
provided on the companion CD. Open Module1.Bas and youâ™ll find the following code to call the TranslatorServer
component.
Sub Main()
End Sub
Now select References from the Project menu, click the Browse button, and locate the TranslateServer.Dll that you built
by following the previous steps. Press F5 to run the project. You should see "hola mundo" displayed, as shown in Figure
9-3.
1.
Figure 9-3
You may be wondering what the point of all this is. So far, all weâ ™ve done is call a Visual Basic 6 component from
Visual Basic 6. Now, however, weâ™ll upgrade each component separately to see how to call a Visual Basic 6
component from Visual Basic .NET and vice versa. Finally, weâ ™ll see how to tie the upgraded client and server
components together to form a complete Visual Basic .NET solution.
Letâ™s create a .NET client that talks to a COM server. Since the whole point of Visual Studio .NET is to help you
create applications quickly for the Web, letâ™s create a Web client that calls our COM server. You might want to do
this, for example, if you have business logic stored internallyâ ”say, a Visual Basic 6 ActiveX DLL function that returns a
list of your companyâ™s productsâ”that you want to make available for viewing by external partners or customers.
3. Select Visual Basic ASP.NET Web Application, name the application MyAmazingTranslator, and click OK.
4. Right-click References on the Solution Explorer tab, and choose Add Reference.
7. Open WebForm1.aspx.
8. Drag and drop the following controls from the Toolbox to WebForm1: A Label, a TextBox, and a Button.
11. Double-click the Translate button and insert the following code in the Button1_Click event handler:
15. Type hello world into the text box, and click the button. See Figure 9-4 for an example of the output.
You have now taken a simple desktop application and made it available to the world. Hola mundo indeed.
Figure 9-4
My amazing English-to-Spanish translator at work on the Web.
Debugging Between the Visual Basic .NET Client and Visual Basic 6 Server
When upgrading Visual Basic 6 code to Visual Basic .NET, it is critical that you be able to debug the changes that you
made yourself or that were made by the Upgrade Wizard. The Microsoft Visual Studio .NET development environment
makes it possible to debug between Visual Basic 6 applications and Visual Basic .NET. However, you need to make a few
changes to your Visual Basic 6 code in order to debug it using the Visual Studio .NET debugger.
Preparing Your Visual Basic 6 Project to Debug Using Visual Studio .NET
To be able to set breakpoints in your Visual Basic 6 source code, you need to build your Visual Basic 6 project with
debugging symbols turned on. To do so, perform the following steps:
1. Run Visual Basic 6 and load the application you want to debug. In this case, load TranslatorServer.vbp.
2. From the Project menu, choose TranslatorServer Properties.
3. Click the Compile tab and select Create Symbolic Debug Info.
At this point itâ™s a good idea to turn on binary compatibility so that you do not need to update your .NET
client application.
Now rebuild your application. Choose Make TranslatorServer.dll from the File menu, click OK, and click Yes to replace
the existing file, if prompted to do so.
Figure
9-6
Setting binary compatibility in Visual Basic 6.
1. Run Visual Studio .NET and open the MyAmazingTranslator Web project you created earlier.
2. Select the MyAmazingTranslator project in Solution Explorer.
13. Click the Translate button. Execution should break on the following line:
15. Step over the line, and execution should break in your Visual Basic 6
TranslatorServer.cls code file. You can now step through your Visual Basic 6 code
to ensure that everything is working properly.
In order to debug an ASP.NET application on your local machine, you need to be added as a member of
the Debugger Users group. If you do not have administrative privileges on the machine, you also need to
change the Machine.config file for Aspnet _wp.exe to run Aspnet_wp.exe with User privileges rather
than System account privileges. See Visual Studio .NETâ™s Help system for more details.
In some cases, you will want to upgrade your Visual Basic 6 server and make it available to your Visual Basic 6 or other
COM client applications. For example, since Visual Basic .NET allows you to create multithreaded components, you may
want to upgrade your Visual Basic 6 component to a .NET component. You can then register the component to take
advantage of a multithreaded environment such as Microsoft Transaction Server (MTS) in order to use object pooling, for
example.
Letâ™s take the Visual Basic 6 TranslatorServer component located on the companion CD and upgrade it to Visual
Basic .NET by performing the following steps:
3. Step through the Upgrade Wizard by clicking Next and selecting the default options as you go.
Letâ™s change the name of the .NET server so that it doesnâ ™t conflict with the server name of its COM
predecessor.
Note that the upgraded code, specifically the contents of the Translate function, is exactly the same as the Visual Basic 6
code. The point is that you can create a Visual Basic .NET server in exactly the same way that you create a Visual Basic 6
ActiveX DLL server. Now for the gotcha. Although you can build the upgraded Visual Basic .NET server, you cannot call
it from Visual Basic 6. Why not? Because by default a Visual Basic .NET server is meant to be called by other .NET
components, not by a COM component. To call a Visual Basic .NET server from a COM application such as Visual Basic
6, you need to register the server for COM. The simple way to expose the component to COM is to turn on the Register
For COM Interop attribute by doing the following:
4. Select the Register For COM Interop check box, as shown in Figure 9-7, and click OK to close the dialog box.
5. From the Build menu, choose Build Solution, and click the Save button to save the Solution file. This step
creates the Visual Basic .NET DLL and also registers it for COM.
Figure 9-7
Registering a .NET component for COM interop does not necessarily mean that only COM clients can use the
component. Instead, it opens that component up to the world of both COM and .NET. Exposing your .NET server
to COM allows you to upgrade old clients or to create new clients in .NET that use the COM server. At the same
time you can make a quick modification to other COM clients to allow them to work with your .NET server.
Now you are ready to call the Visual Basic .NET TranslatorServer component from Visual Basic 6. This process involves
the same steps used to create the Visual Basic 6 TranslatorClient application located on the companion CD. To call the
Visual Basic .NET TranslatorServer component from Visual Basic .NET, follow these steps:
1. Run Visual Basic 6 and open the TranslatorClient application located on the companion CD.
2. Choose References from the Project menu.
5. Open TranslatorClient.bas.
to
Dim Ts As New TranslatorServer_net.Translator
If you want to add features to your upgraded Visual Basic .NET component, you should add COM attributes to
ensure binary compatibility. Binary compatibility causes your Visual Basic .NET component to look, act, and smell
like existing COM clients no matter what new features you add to the component. If you break compatibility, your
COM clients will not be able to find the .NET component. You will be forced to recompile your COM client
against the updated .NET server and redistribute it. For an example of how to add COM attributes to ensure binary
compatibility, see “Replacing COM with .NET: Binary Compatibility†later in this chapter.
Earlier we showed you how to debug both Visual Basic 6 and Visual Basic .NET code using the Microsoft Visual
Studio .NET debugger. Now weâ™re going to debug our Visual Basic 6 client application and .NET server, using both
the Visual Basic 6 debugger and the Visual Studio .NET debugger to step across the call from Visual Basic 6 code to
Visual Basic .NET code. Letâ™s start in the Visual Studio .NET development environment.
1. Run Visual Studio .NET and open the TranslatorServer application you upgraded to Visual Basic .NET earlier.
2. Open TranslatorServer.vb and place a breakpoint within the Translate function on the following line:
7. Select Start External Program, click the Browse (“…â€) button, and search for VB6.exe.
8. From the Debug menu, choose Start. Visual Basic 6 will launch.
Execution will break in the Visual Basic 6 application. Step over the line of code.
The Visual Studio .NET debugger will appear, and execution will break in the Visual Basic .NET server application. You
can step through and debug your Visual Basic .NET server application code. When execution returns from the Visual
Basic .NET server, function execution resumes in the Visual Basic 6 debugger. Pretty cool, eh?
In creating Visual Basic versions 4, 5, and 6, the Microsoft Visual Basic development team worked hard to allow you to
create Visual Basic COM components that are backward compatible. This feature was enabled by an innocent-looking
check box on the Component tab of the Project Properties dialog box, shown earlier in Figure 9-6. Binary compatibility in
Visual Basic 6 enabled you to create version 2 of a COM component that is a compatible replacement for version 1 of that
component. What this means is that the version 2 component contains exactly the same public objects, properties,
methods, and events as the version 1 component. In addition, it means that the properties, methods, and events appear in
exactly the same order as in the version 1 component. The version 2 component also understands how to initialize itself
using property settings saved by the version 1 component.
Although the version 2 component needs to look, act, and smell like a version 1 component, the version 2 component can
include additional objects, properties, and methods that improve upon the version 1 component. No matter the
improvements, however, in order to keep clients of the version 1 server component working with the version 2
component, the version 2 component must expose some subset of itself as a version 1 component. If the version 2
component doesnâ™t expose itself as a version 1 component, you need to do one of the following:
1. Invest in fixing the version 2 component to behave identically to the version 1 component and then invest
further in testing the component to ensure that no existing version 1 clients are broken by the changes.
2. Rename the version 2 component and change its attributes in such a way that it exposes itself as a completely
new component. Clients that are using the version 1 component continue to use that component and are not
disturbed by the distribution of the version 2 component.
3. Recompile all clients that use the version 1 component to use the version 2 component and then distribute the
client and a version 2 server component as a matched pair.
Which option makes more sense? Option B is generally the recommended approach if you have widely distributed clients
that rely on version 1 of the server component. If there is any doubt in your mind about whether you can update an
existing component or create a new component as a direct replacement of a version 1 component, donâ ™t do it. Instead,
go with option B. Itâ™s the safe bet.
When it comes to creating Visual Basic .NET components that are direct replacements of your Visual Basic 6
components, Visual Basic .NET adopts the option B approach. Simply put, you canâ ™t do it, although if the truth be
told, itâ™s possible but not easy. Youâ™ll sleep much better if you simply assume that you canâ ™t directly replace a
Visual Basic 6 component with a Visual Basic .NET one.
The Visual Basic team deliberately chose not to include features that would make it easy for you to mark a Visual
Basic .NET component as being a compatible replacement for a Visual Basic 6 component. Weâ ™ll let the following
Visual Basic code speak for itself:
Visual Basic .NET takes an indirect approach in creating Visual Basic .NET components that are replacements of your
Visual Basic 6 COM components. This approach means that you can create a component that looks and smells like a
Visual Basic .NET component but doesnâ™t necessarily act 100 percent like the Visual Basic 6 component itâ ™s
âœreplacing.â Since to take advantage of new version 2 component features you need to update your existing clients,
why not live a little and roll those features into a Visual Basic .NET component? Putting the new features in a separate
component has the advantage of not disturbing the version 1 component. Clients that are running against the version 1
component continue to run against it, unaffected by the changes in the version 2 component. When you are ready to move
your clients to the version 2 component, you can update and redistribute them as needed.
By using the Visual Basic Upgrade Wizard, you can quickly upgrade your Visual Basic 6 components to Visual
Basic .NET components. Although the wizard doesnâ™t give you a full, 100 percent binary-compatible replacement for
your Visual Basic 6 component, what you end up with is pretty close. All properties, methods, and events are brought
forward, preserving the public interface of your component. Once you have worked out all of the upgrade-related
comments and issues in the upgrade report, you can effectively replace your Visual Basic 6 component with the .NET
version by making a quick change to the Visual Basic 6 client application so that it uses your .NET component instead of
the Visual Basic 6 component. You then need to rebuild and redeploy your Visual Basic 6 application to take advantage of
the new Visual Basic .NET server component.
If you want to expose your upgraded Visual Basic .NET server component to a COM client and you plan on making
changes to the Visual Basic .NET server component over time, we strongly suggest that you do the following to ensure
compatibility:
Add attributes to the class to declare ID attributes for the class, interface, and event interface.
Change the class declaration so that it implements both the programmable and event interfaces.
Letâ™s step through an example that demonstrates how to enable binary compatibility in a Visual Basic .NET class.
Doing so will ensure that a COM client that uses the class will continue to work with it even after you have added new
functionality to the class.
Weâ™ll start with a Visual Basic .NET class that was upgraded from Visual Basic 6. Assume that the filename for the
class is Class1.vb.
' Note to self: Find someone who speaks Spanish and VB who
' is willing to expand this component to translate any common
' English phrase
If LCase(LanguageFrom) = "english" And _
LCase(LanguageTo) = "spanish" Then
Select Case LCase(SentenceFrom)
Case "hello world"
Translate = "hola mundo"
fSuccess = True
End Select
End If
End Class
1. Choose Add New Item from the Project menu and select COM Class. This gives you a template class for
creating binary compatible objects.
2. Copy and paste the public event TranslationError and the public method Translate to the new COM class. Paste
the code after Sub New.
3. Right-click Class1.vb in the Solution Explorer and delete it. We no longer need it.
4. View the code for ComClass1 and expand the #Region “COM GUIDs,†since you will need to use these
values later.
6. Add the following Imports clause to the top of the file as follows. COM attributes such as Guid and ComVisible
will come from this namespace.
Imports System.Runtime.InteropServices
7. Replace the ComClass attribute in the Translator class with the following attributes:
8. <Guid(ClassId), ComVisible(True)> _
Public Class Translator
9. Define the programmable interface containing the Translate method. Define the interface after the #End Region
for “COM GUIDs†as follows:
10. <Guid(InterfaceId)> _
11. Interface ITranslate
12. <DispId(1)> _
13. Function Translate(ByVal SentenceFrom As String, _
14. ByVal LanguageFrom As String, _
15. ByVal LanguageTo As String) As String
End Interface
16. Immediately following the programmable interface, insert the declaration for the event interface as follows:
17. <Guid(EventsId)> _
18. Interface ITranslateEvents
19. Event TranslationError(ByVal ErrorMessage As String)
End Interface
20. Add an Implements clause after the Public Class declaration to implement the programmable and event
interfaces you just declared as follows:
21. Public Class Translator
Implements ITranslate, ITranslateEvents
22. Modify the Public Event declaration to implement the interface event as follows:
23. Public Event TranslationError(ByVal ErrorMessage As String) _
Implements ITranslateEvents.TranslationError
24. Modify the public Translate method to implement the ITranslate.Translate interface method as follows:
25. Public Function Translate(ByVal SentenceFrom As String, _
26.                           ByVal Language
From As String, _
27.                           ByVal Language
To As String) _
28. As String _
                          Implements ITr
anslate.Translate
When completed, you should have the following class with all the necessary COM attributes defined. You can rebuild the
class without breaking compatibility with existing COM clients. The only way you will break compatibility is if you
consciously change GUIDs, change a method or event signature, or change the order of events or methods defined in an
interface. To maintain compatibility from this point on, you must restrict yourself to adding new methods to the end of an
interface. You can never remove or change an existing method, nor can you can change the order of the methods.
Imports System.Runtime.InteropServices
<Guid(ClassId), ComVisible(True)> _
Public Class Translator
Implements ITranslate, ITranslateEvents
<Guid(InterfaceId)> _
Interface ITranslate
<DispId(1)> _
Function Translate(ByVal SentenceFrom As String, _
ByVal LanguageFrom As String, _
ByVal LanguageTo As String) As String
End Interface
<Guid(EventsId)> _
Interface ITranslateEvents
Event TranslationError(ByVal ErrorMessage As String)
End Interface
' Note to self: Find someone who speaks Spanish and VB who
' is willing to expand this component to translate any common
' English phrase
If LCase(LanguageFrom) = "english" And _
LCase(LanguageTo) = "spanish" Then
Select Case LCase(SentenceFrom)
Case "hello world"
Translate = "hola mundo"
fSuccess = True
End Select
End If
End Function
End Class
In order to control the binary compatibility of components, many of you asked for a Visual Basic feature to expose
COM attributes such as Guid and method IDs in a Visual Basic class. Visual Basic 6 offers a simple way to enable
binary compatibility—the Binary Compatibility option—but does not allow you to define or edit the COM
attributes that it automatically generates for you. Visual Basic .NET does not offer any simple solution to binary
compatibility; there is no binary compatibility option that you can turn on. Instead, you must write code manually
defining the interfaces and COM attributes to enforce binary compatibility. The ability to specify COM attributes
comes at a cost: more code. After you have âœbeautifiedâ your code by manually adding these attributes, you
may ask what you have gotten yourself into. Be careful what you ask forâ ”particularly if youâ ™ve requested
more control in specifying COM attributes; you just might get it.
Part III
Getting Your Project Working
Chapter 10
Ten Common Upgrade Problems
Chapter 4 looked at common issues with Microsoft Visual Basic 6 code and discussed how you could resolve them before
upgrading. This chapter also focuses on resolving common upgrade issues—but this time we look at the issues found
after the wizard has finished upgrading your project. To this end, we describe ten very common upgrade problems and
how to fix them. Some of these problems are due to language differences and new control behaviors. Others are related to
the different run-time behavior of the .NET Framework. Youâ ™ll learn how to avoid these issues in the first place and
how to resolve them if you encounter them after you begin upgrading. Youâ ™ll also find sample applications on the
companion CD that illustrate some of the situations discussed here.
In Visual Basic 6, the AddItem method is used to add a new entry to a ListBox or ComboBox control. In Visual
Basic .NET, the equivalent operation involves calling the Add method of the Items collection for the ComboBox or
ListBox Windows Forms controls. This method takes an object as a parameter, instead of a string. Because only strings
can be displayed in a ListBox, the Add method implicitly calls the ToString method on the passed object. For variables
that have a primitive value, such as a string or a number, the method returns the value as a string. For other objects,
however, the ToString methodâ™s behavior varies significantly from class to class.
Officially, Visual Basic .NET no longer supports implicit default properties. Unofficially, however, this is not
universally true. All classes in the .NET Framework derive from System.Object. System.Object defines several
methods, one of the most important of which is the ToString method. In some ways, ToString is emerging as a de
facto default property. For instance, ComboBoxes display strings in their lists, yet the ComboBox.Items.Add
method accepts an object, not a string. How does the Add method populate the list? It calls the objectâ ™s
ToString method with the expectation that the ToString method of any object returns the actual information that is
desired.
Consider the following example of adding items to a ListBox from a TextBox in Visual Basic 6. This example is
contained in the ListBoxExample sample project on the companion CD. It adds the contents of a TextBox control to a
ListBox control. The key here is that we are relying on the default method of the TextBox control (Text) being called
when the control is passed to the AddItem method.
Option Explicit
The problem with this code is its use of the default property of the TextBox object when the object is late-bound. When a
TextBox is used in this context, Visual Basic assumes that you want the default property specified in the COM
objectâ™s type library. In this case, the Text property of the TextBox object is defined as its default property. This
means that the following two lines of code are functionally equivalent:
List1.AddItem Text1
List1.AddItem Text1.Text
As we mentioned previously, the use of a Variant for handling the TextBox object in the AddText method prevents the
Upgrade Wizard from resolving the default property. The following example shows what the code looks like after
upgrading:
What happens? The code runs as is and does not generate an exception at run time. However, the TextBoxâ ™s ToString
method is called. Unfortunately, the TextBox does not return just the displayed text; it also prepends the string with its
class path. So, for example, if the TextBox displays the string “Hello World,†the ToString method returns
“System.Windows.Forms.TextBox, Text: Hello World.†To fix the problem, you need to modify your code to
eliminate the use of default properties. Doing so would change the line in which the items are added to the ListBox to the
following:
' This explicitly passes the contents of the Text property to the
' ListBox Add method.
List1.Items.Add(text_Renamed.Text)
It is important to emphasize that if the original sample had not used late binding, no modifications would have been
required.
Garbage collection is a new concept to Windows platform development. It is quite a departure from the COM memory-
management mechanism of reference counting and offers significant benefits. In COM, objects use reference counting to
keep track of the number of active references to determine their lifetime. When an objectâ ™s reference count reaches
zero, the object is destroyed. Reference counting presents several problems within COM because of issues with potential
reference leaks or circular references. However, it is, for the most part, predictable. Reference counting makes it possible
to determine when and where an object will be destroyed and, as a result, to manipulate the lifetime of the object in a
deterministic way—hence the term deterministic finalization.
Visual Basic 6 made COM programming much simpler because it hid a great deal of the COM implementation details.
But you could rely on Visual Basicâ™s behavior with respect to COM object lifetimes. The following example
illustrates how reference counting in Visual Basic 6 can influence an objectâ ™s lifetime:
Dim x As Connection, y As Connection
Set x = new Connection 'Create an object and set refcount = 1
Set y = x 'set refcount = 2
Set x = Nothing 'set refcount = 1
Set y = Nothing 'set refcount = 0; and destroy the object
It is not necessary to explicitly dereference your objects in Visual Basic 6. When a variable goes out of scope, Visual
Basic 6 automatically dereferences the object for you:
Sub MySub()
Dim x As Connection, y As Connection
Set x = New Connection 'Create an object and set refcount = 1
Set y = x 'set refcount = 2
End Sub
In Visual Basic .NET, the picture is drastically different. For the most part, it is no longer possible to determine the exact
lifetime of a given object because Visual Basic .NET does not use reference counting. Instead, it handles memory
management through garbage collection. Putting aside the implementation details, this change produces distinct
behavioral differences that will have implications for your applications. First of all, setting a reference to Nothing will not
cause that object to be immediately destroyed. Garbage collection guarantees that the object will be destroyed at some
point, but there is no guarantee as to when it will be destroyed.
The effects of this change are best illustrated by the following code in Visual Basic .NET:
You might ask why Microsoft chose to make this change from a deterministic memory-management scheme to a
nondeterministic one such as garbage collection. The general consensus was that too much of COM programming
was related to memory-management issues. Garbage collection frees the developer from many of these arduous
tasks, enabling the developer to focus more on designing program logic then on memory management. It does
require a slightly different programming approach when dealing with objects that represent physical system
resources. For the most part, though, you donâ™t really need to worry about it. Garbage collection is quite a
positive change and should result in fewer problems with memory leaks in your applications.
In Visual Basic 6, we could rely on setting the conn reference to Nothing to close the connection and destroy the
underlying Connection object. In Visual Basic .NET, setting conn to Nothing simply marks the object for collection. The
underlying physical connection to the database remains open, consuming both memory and network resources. Thus, it is
imperative when working with objects that represent real physical resources (such as files, graphics handles, and database
connections) that you explicitly release those resources before you dereference the object (or allow it to go out of scope).
A proper way to handle this would be as follows:
The Close method ensures that the database connection is released immediately. This is by far the best way to handle any
physical resource. Whenever you deal with a physical resource, follow this general rule: open the resource as late as
possible and release it as soon as possible. This practice will ensure the most efficient use of system resources.
Bringing a Little Determinism to the Party
It is possible to ensure that your objects are destroyed before your application proceeds. To do so, you need to cause a
garbage collection to occur and then wait for the collection to complete. You can accomplish these tasks by calling the
following two methods.
GC.Collect()
GC.WaitForPendingFinalizers()
It is pretty obvious that the first method causes a collection to occur. You need to realize, however, that the Collect
method is an asynchronous call (hooray, free threading!), meaning that your application will not wait for the collection to
complete before continuing. That is why it is important to call WaitForPendingFinalizers, which will block your
application until the collection has completed.
Now that youâ™ve seen how forcing garbage collection can be done, here is why you should almost never do it:
Garbage collection is, by definition, an expensive operation. For this reason, it takes place relatively infrequently, instead
of whenever and wherever an object is dereferenced. If you have code that needs to work no matter what, then by all
means take advantage of Collect and WaitForPendingFinalizers. Ultimately, however, this technique is just a quick fix.
For the long term, you should probably investigate how to eliminate this dependency.
Dim…As New
You will encounter upgrade issues with the As New syntax in two types of cases. The first is when you use As New to
declare a variable. The second is when you use it to declare an array. The use of As New to declare a variable is still
supported in Visual Basic .NET, but it no longer supports implicit declaration. Array declaration with As New is not
supported at all. The following code shows how the Upgrade Wizard handles both types of declarations.
Notice that the Upgrade Wizard leaves the array declaration alone. When you first upgrade the project, there will be a
compiler error on this line because As New is no longer supported for arrays. The best way to deal with this situation is
fairly simple: initialize your array in a loop.
The Upgrade Wizard will help you isolate these problems. First it will insert a warning wherever you have declared an
array with As New. It will also generate another warning wherever you set an object to Nothing. You can use these
warnings to track down spots in your code where you might run into difficulties. The only way to resolve these problems
is to explicitly create a new instance of Object1 or to test for Nothing whenever you access a variable that might have
been set to Nothing.
As New is an example of a language feature that differs significantly in functionality from Visual Basic 6 to Visual
Basic .NET. It can present a host of issues in an application that relies too closely on its previous behavior. Watch
yourself.
In Visual Basic .NET, your applicationâ™s lifetime is now defined differently than it was in Visual Basic 6. When the
startup object finishes, the application ends. In Visual Basic 6, an application remained active as long as there were dialog
boxes or windows open. This represents a change in how your applicationâ ™s life cycle is determined. For the most
part, however, you probably will not have to worry about this change, unless your application closes its main form but
expects to continue running. The application model assumes the use of a main form that runs on the main thread. When
that form terminates, your application terminates by default.
There are two main startup scenarios for Visual Basic applications. The first is when the startup object is a form. The
second is when the startup object is a Sub Main method. Sub Main is typically used in two situations. You may implement
a Sub Main method if you need to customize the loading process or if you used the
Application Wizard to create your application.
The Upgrade Wizard attempts to treat Application Wizard projects as a special case, but if the Sub Main method is
too heavily modified, it will not be able to do much for you. See Chapter 17for more on upgrading VB Application
Wizard projects.
As a general rule, when you are opening a form from Sub Main, use the System.Windows.Forms.Application.Run method
to start your application. This method begins running a standard Windows application messaging loop on the current
thread and makes the specified form visible. If youâ™re creating another form that may replace the original form, you
should create it on a separate thread to ensure that your application does not terminate prematurely. Otherwise, when the
specified form is closed, the application will terminate immediately. The following example demonstrates the use of the
Application.Run method in a fictional Sub Main.
Bad Constants
In Visual Basic 6, you can create a set of named constants, known as an enumeration. Each member of the enumeration is
assigned an integer value; in code, the member name evaluates to its assigned integer. Because enumerations in Visual
Basic 6 represent integer values instead of distinct types, the process can verify only their underlying values.
Unfortunately, this arrangement allows developers to use constants intended for different purposes interchangeably and
also to use simple integers (representing the underlying values) in place of the constants. Visual Basic .NET is more strict
and requires the correct enumeration constants because enumerations are now actual types, not just collections of friendly
names for integers. The most common example of the use of bad constants is in relation to the Screen.MousePointer
property. Constants such as vbNormal are frequently used in place of vbDefault (a proper constant for the MousePointer
property). It is also fairly common for developers to use the underlying values for constants. The following example from
Visual Basic 6 demonstrates three equivalent statements that work just fine:
When you upgrade your project, however, the Upgrade Wizard will not be able to upgrade the vbNormal constant because
it is not defined for the MousePointer and you will be left with an upgrade warning and a compile error. On the other
hand, the Upgrade Wizard can work with an underlying constant, if used, and can translate it to a proper constant in
Visual Basic .NET. The following results from the Upgrade Wizard illustrate this scenario:
Drag-and-drop capability is an important function of most modern applications. There are a number of important
differences between Visual Basic 6 and Visual Basic .NET with respect to drag and drop. To better understand how these
changes will affect your application, letâ™s take a quick overview of this feature.
Visual Basic 6 supported two kinds of drag-and-drop operations. Standard drag and drop is intended to support drag and
drop between controls within a single form. OLE drag and drop was designed to support drag-and-drop capability between
applications and is the focus of this section.
The standard Visual Basic 6 controls have varying degrees of support for OLE drag-and-drop operations. Table 10-2 lists
the standard Visual Basic 6 controls and their support for manual and automatic drag-and-drop operations.
Table 10-2 Visual Basic 6 Controls Grouped According to Their Support for OLE Drag and Drop
For the sake of brevity, this section deals exclusively with a manual drag-and-drop application. To better illustrate how the
upgrade process affects OLE drag-and-drop operations, we have provided a sample application named OLEDragAndDrop
on the companion CD. It permits the user to drag and drop image files to and from the application and implements OLE
drag and drop in full manual mode. This sample should give you a better idea of exactly what changes you will need to
make in your own application. Figure 10-3 shows an image from the sample OLEDragAndDrop application.
Figure 10-3
OLEDragAndDrop application.
Figure 10-4 outlines the life cycle of an OLE drag-and-drop operation in Visual Basic 6. The next section moves on to
how things are done in Visual Basic .NET.
Figure 10-4
Life cycle of a Visual Basic 6 drag-and-drop operation.
Drag and Drop in Visual Basic .NET
In Visual Basic .NET, the drag-and-drop operations have been consolidated into a single framework. Drag and drop
between controls is handled in exactly the same manner as drag and drop between applications. This change simplifies the
programming burden for new development but requires the rewriting of drag-and-drop code for existing applications.
Figure 10-5 outlines the life cycle of a drag-and-drop procedure in Visual Basic .NET. Note that this cycle applies to both
control-to-control and application-to-application drag and drop.
Figure 10-5
Life cycle of a Visual Basic .NET drag-and-drop operation.
The OLEDragAndDrop sample application contains the code necessary to implement the drag-and-drop operation in
Visual Basic 6. The following is an excerpt from the Form1.frm file in that project:
Data.Files.Add FileList
Data.SetData , vbCFFiles
AllowedEffects = vbDropEffectCopy
End Sub
The changes necessary to make the drag-and-drop code work in Visual Basic .NET are fairly significant. Due to the
changes in the drag-and-drop programming model, the Upgrade Wizard cannot automatically make the modifications for
you. It simply leaves the methods alone, leaving it to you to implement the logic in the Visual Basic .NET drag-and-drop
event model.
Instead of upgrading the Visual Basic 6 code, we have provided a new Visual Basic .NET implementation of the same
features. This helps demonstrate how the drag-and-drop event model has changed and how to properly pass information
back and forth. The following code shows how to implement the equivalent drag-and-drop operation from the
OLEDragAndDrop sample application in Visual Basic .NET. (This code is also included on the companion CD.)
Use the life-cycle diagram in Figure 10-5 to help gain an understanding of how to integrate drag and drop into your Visual
Basic .NET applications. This example should be a good introduction to the issues you will encounter when dealing with
drag and drop.
Conclusion
This chapter focused on ten common problems involving features within your upgraded application. Hopefully, the
discussion has given you a better handle on how to resolve similar issues in your own applications. The samples provided
on the companion CD should allow you to see how isolated feature areas can be upgraded. The next chapter covers
changes in the Visual Basic language and describes how to fix problems arising from those changes.
Chapter 11
Resolving Issues with Language
The Basic programming language has been around for a long time in a variety of forms—GW-BASIC, QuickBasic,
Visual Basic, and Visual Basic for Applications (VBA), to name just a few Microsoft PC-based varieties. Each product
provides its own brand of the Basic programming language, supporting or not supporting certain types and language
constructs. Each product has also made an attempt to clean up or simplify the language. Microsoft Visual Basic .NET
offers yet another version of the Basic language, adding its own types and language constructs and omitting others.
In Visual Basic .NET you will not find Type…End Type, GoSub…Return, While…Wend, nonzero-based arrays, the
Currency type, static subroutines, or fixed-length strings. You will, however, find new language elements such as
Structure…End Structure, Inherits, Overloads, Try…Catch, SyncLock, the Char type, the Decimal type, and assignment
shortcuts such as += and -= to increment or decrement a value. New statements such as Inherits, Overloads, Try…Catch,
and SyncLock provide support for new features—inheritance, structured exception handling, and multithreading—not
found in previous versions. Other changes such as the removal of GoSub…Return, the change from Type…End Type to
Structure…End Structure, and the addition of the Char type are intended to modernize the language. Still other
changes—removal of support for nonzero-based arrays and fixed-length strings—are the result of trade-offs needed to
make Visual Basic .NET work with other .NET languages.
This chapter discusses upgrade issues related to your code. In particular, it focuses on language elements in Visual Basic 6
that the Upgrade Wizard doesnâ™t automatically upgrade (or upgrades only partially) for you.
Visual Basic .NET offers most of the same types as Visual Basic 6. You will find the same basic types, such as Byte,
Integer, Single, and String. You can also define the same types, such as Enum, user-defined types (called Structure in
Visual Basic .NET), and Class. Visual Basic .NET also offers some new types that you can use—Short, Char, and
Decimal. It also gives you a new Interface type that you can define or use. This section focuses on Visual Basic 6 types
for which support has changed in Visual Basic .NET.
The Variant data type has existed in the Visual Basic language since Visual Basic 2. It is part of the Visual Basic
franchise. If you do not specify a type for a variable, you can always rely on Variant being the default type. The beauty of
the Variant data type is that you can assign any numeric, string, array, or object reference value to it. It can also be in
various states of nothingness—missing, Null, or Nothing.
The same characteristic that makes the Variant data type easy to use is also the one that will get you into trouble. The
compiler will not and cannot step in to save you if you assign variants containing incompatible types to each other. For
example, if you attempt to assign a variant containing an object to a variant containing a simple type such as an Integer,
you will encounter a run-time error.
Visual Basic .NET introduces the Object type. It is a combination of the Object and Variant types found in Visual Basic 6.
Like the Visual Basic 6 Object type, a Visual Basic .NET object can be assigned to an object instance, such as a control,
and you can make late-bound calls to the object. The Variant-like behavior of the Object type is that you can assign any
type to it, ranging from Integer, Array, and Structure to class objects.
IsMissing Is Lost
One characteristic that the Visual Basic .NET Object type lacks compared to the Visual Basic 6 Variant type is a value
that means missing. Other than being set to an actual value, the only other state a Visual Basic .NET Object type can be
set to is Nothing. The following Visual Basic 6 code demonstrates how this can be a problem if you are testing for a
missing optional parameter:
End Sub
Because a Visual Basic .NET object cannot be set to a value representing “missing,†the IsMissing function is not
available in Visual Basic .NET. The closest approximation is IsNothing. The Upgrade Wizard will replace all of your calls
to IsMissing with calls to IsNothing.
Visual Basic .NET requires that you specify a default value for all optional parameters to be used if no argument is passed
in. The wizard will automatically initialize parameters to reasonable initial values depending on the type; a string is set to
an empty string, a numeric is set to 0, and an object parameter is set to Nothing.
Letâ™s suppose that in the code just given you wanted to pass in Nothing as the value for the Picture parameter. Your
intent is to erase any current picture. Inspect the following upgraded code, and you will see that the logic cannot
distinguish between a missing parameter and a parameter for which you explicitly pass in Nothing. The Picture parameter
will always be set to Nothing in either case. If you explicitly pass in Nothing to erase the current picture, the Not
IsNothing(Picture) test will fail and the picture member (m_Picture) will not be set to Nothing; in other words, the picture
will not be erased.
How should you modify your code to cope with this situation? You need to reintroduce the concept of IsMissing into your
code. One approach is to set the initialization value to a unique value that will not be passed in under normal
circumstances. For example, in the previous code, you could set the parameter initialization values to “<Missing>â€. If
you see that a parameter value is set to “<Missing>â€, you can assume that the caller did not specify the argument
when making the call. The logic will be compatible with the way it was before. If the values are not specified, the current
value is left intact, and if the caller passes in a value—including Nothing or an empty string—the current value will be
replaced by the passed-in value. Applying “<Missing>†to the parameter initialization values and fixing up the code
to test for “<Missing>†yields the following:
As we discussed in the previous section, an Object can be in only one of two states—set to a value or to Nothing. A
Visual Basic Variant, on the other hand, can contain other states, such as empty, Null, or missing. We can work around
the absence of the missing state by using a special value that you create to mean “missing.†When dealing with Null,
you do not need to create your own value. Rather, the .NET Framework provides the System.DBNull.Value object as the
equivalent to a Visual Basic 6 Null value.
Why is the name of the value DBNull.Value? Why not just Null? The reason is that the DB in DBNull is short for
database. Since it is most common to deal with null values when reading values from or writing values to database fields,
DBNull.Value is intended to be used when you want to set or check for null values in a database table. When you are
setting an object variable to a field contained in an ADO.NET dataset or to a field in a traditional ADO recordset, if the
field value is Null, the object variable will be set to System.DBNull.Value by convention.
Null Propagation
Many Visual Basic 6 functions—such as Left, Mid, Chr, and Hex—deal with Variant values containing Null. These
functions return a Variant containing Null. Returning a Null value in response to a passed-in Null value is known as null
propagation. The null-propagating functions all accept Variant values and return Variant values. A set of sibling functions
in the Visual Basic 6 language accepts strings and returns strings. These functions—such as Left$, Mid$, Chr$, and
Hex$—will result in an error if you attempt to pass in a value of Null.
By supporting null propagation, Visual Basic 6 allows you to write working code that does not need to check for Null. For
example, if you have code that converts a customer name—taken from a field in a database—to lowercase, the code will
work even if the customer name is not provided and is Null. The following Visual Basic 6 code will execute without error
even though vCustomerMiddleName is Null.
This code leads to a run-time exception: âœNo accessible overloaded â ˜Strings.LCaseâ ™ can be called without a
narrowing conversion.â This message is an overly technical, wordy way of saying that the LCase function does not
accept System.DBNull.Value as a legal argument.
How can you fix this problem? The obvious fix is to insert a check before the call to every Visual Basic .NET function to
make sure that the passed-in argument is not System.DBNull.Value. We could modify the code as follows:
This fix will work great if you are making a couple of calls to LCase in your code, but what if you are making hundreds or
thousands of calls to the function? Adding checks for System.DBNull.Value before all calls to your Visual Basic .NET
functions will be too time-consuming. Another solution is to declare your own LCase function that handles null
propagation. For example, you can create the LCase function in a module—where it can be accessed from anywhere in
your code—as follows:
If you place this function in a module named Module1, it is a simple matter to search for and replace all your calls to
LCase with ones to Module1.LCase.
Arrays
Visual Basic .NET supports two array types—strongly typed and generic System.Array. Strongly typed arrays are the
arrays you are accustomed to using in Visual Basic 6. Anytime you use the Dim statement to declare an array variable of a
specific type, you are creating a strongly typed array. Visual Basic .NET—actually the .NET Framework—offers a new
System.Array class. This class offers a set of methods so that you can define an array of any type and any number of
dimensions at run time. An important difference between the two array types is that strongly typed arrays do not support
nonzero lower bounds, whereas the System.Array class does.
Because Visual Basic .NET offers primary support for strongly typed arrays, it is not possible to declare a strongly typed
array with a nonzero lower bound. Visual Basic .NET in turn does not support nonzero-bound array-related features such
as array declarations including the To statement and Option Base 1. It does support LBound, but when LBound is used on
a strongly typed array, it will always return 0.
The Upgrade Wizard will upgrade your arrays to strongly typed Visual Basic .NET arrays. If you are using Option Base 1,
the wizard will issue an UPGRADE_WARNING message: “Lower bound of array <ArrayName> was changed from 1
to 0.†The UPGRADE_WARNING is added for each array declaration found that does not specify a lower bound. If you
are using a positive value for the arrayâ™s lower bound, the wizard will remove the lower bound from the declaration
and issue the same UPGRADE_WARNING. If you specify a negative value for the lower bound in your array declaration,
the wizard will leave the declaration as is and will include an UPGRADE_ISSUE comment: â œDeclaration type not
supported: Array with lower bound less than zero.â The following code samples demonstrate how the wizard upgrades
your Visual Basic 6 array declarations. The Visual Basic 6 code
Option Base 1
…
Dim OptionOneBasedArray(10) As Integer
Dim PositiveLBoundArray(10 To 20) As Long
Dim NegativeLBoundArray(-10 To 10) As Variant
If you are using positive lower bounds in your array declaration, the upgraded declaration will compile and run. However,
if your code uses hard-coded references to the lower bound of the array, the array will have more elements than you need
or will use. For example, suppose that you had code that accessed each element of the PositiveLBoundArray, as follows:
Dim i As Integer
For i = 10 To 20
PositiveLBoundArray(i) = i
Next
This code will work without any problem. The only issue is that there are now 10 elements of the array, 0 through 9, that
are not being used.
If, in Visual Basic 6, you declared your array with a negative lower bound, the wizard will leave the declaration as is. The
declaration will result in a compiler error in Visual Basic .NET, however. You will need to change your array to use a 0
lower bound, and you may need to adjust your code to work with the new array bound. For example, you could change the
NegativeLBoundArray declaration to use a 0 bound and the same number of elements as follows:
You would then need to update your code that accesses the elements of NegativeLBoundArray. Suppose, for example,
that you have code such as the following:
Dim i As Integer
For i = LBound(NegativeLBoundArray) To UBound(NegativeLBoundArray)
NegativeLBoundArray(i) = i
Next
The For…Next loop is acceptable because you are using LBound and UBound, and so the code automatically picks up the
new lower and upper bounds for the array. The value that is being set into the array, however, is incorrect. The intent was
to seed the array with values from –10 through 10, but the upgraded code is now seeding the array with values from 0
through 20.
To fix this problem, you could introduce an adjustment factor into your code and apply it to any code that is using hard-
coded values to access an array element. The example above uses a calculated value i based on the array index. We could
adjust i by subtracting 10. If we define our adjustment value in a constant, the updated code is
Dim i As Integer
Const NegativeLBoundArray_Adjustment = 10
For i = LBound(NegativeLBoundArray) To UBound(NegativeLBoundArray)
NegativeLBoundArray(i) = i - NegativeLBoundArray_Adjustment
Next
With the adjustment in place, the code once again seeds the array with values from –10 through 10.
There is another way to solve this problem. If itâ™s easier for you to use negative array bounds, or simply any nonzero
lower bound, consider using the .NET Framework System.Array class. This class includes a method called
CreateInstance, which allows you to create a generic System.Array with nonzero lower bounds. If we start with the
original Visual Basic 6 array declarations given earlier:
we can convert, in Visual Basic .NET, the declarations to System.Array and include calls to CreateInstance to initialize
the type and dimensions of the arrays as follows:
' Arrays to define the array length and LBound for each dimension
' Since we are only dealing with single dimension arrays, we only
' need one element
Dim ArrLens(0) As Integer
Dim LBounds(0) As Integer
ArrLens(0) = 10
LBounds(0) = 1
OptionOneBasedArray = System.Array.CreateInstance(GetType(Short), _
ArrLens, LBounds)
ArrLens(0) = 11
LBounds(0) = 10
PositiveLBoundArray = System.Array.CreateInstance(GetType(Integer), _
ArrLens, LBounds)
ArrLens(0) = 21
LBounds(0) = -10
NegativeLBoundArray = System.Array.CreateInstance(GetType(Object), _
ArrLens, LBounds)
Dim i As Integer
For i = LBound(NegativeLBoundArray) To UBound(NegativeLBoundArray)
NegativeLBoundArray(i) = i
Next
A caveat when using System.Array is that you cannot assign a System.Array variable containing a nonzero lower bound to
a strongly typed array. For example, the following code will result in a “Specified cast is not valid†exception. The
exception occurs on the assignment of the strongly typed array variable StrongTypeArray to the System.Array variable
NegativeLBoundArray.
You will not find the Visual Basic 6 Type…End Type, commonly referred to as a user-defined type, in Visual
Basic .NET. Instead you will find Structure…End Structure, a superset of Type…End Type. Within a structure, you can
define members of any type. In addition, you can define methods and properties. You will find that a Visual Basic .NET
structure works much like a class. One difference is that you cannot create instances of a structure, and all structure
members implicitly include the Shared attribute. This means that a structure can access shared members of the class or
module where it is defined, but it cannot access instance members.
Member Initialization
A difference between Visual Basic 6 and Visual Basic .NET user-defined types is that Visual Basic 6 supports fixed-
length array declarations within a user-defined type. In Visual Basic .NET, however, you cannot specify the size of the
array contained within a user-defined type. To work around this limitation, you can define a constructor for your user-
defined type in which you initialize the array to a fixed size. Consider the following Visual Basic 6 code:
Sub Main()
Dim mt As MyType
MsgBox (mt.MyStringArray(0))
End Sub
The wizard has done a few things here to create the equivalent Visual Basic .NET code. It has added the VBFixedArray
attribute to the structure, specifying the size of the array. This attribute is useful if you are passing the structure to a Visual
Basic file function such as Get or Put. The Visual Basic file function will use the attribute to determine whether it needs to
read or write additional header information associated with the array. Another change the wizard has made is to insert a
public method within the structure called Initialize. The code contained within the Initialize method includes ReDim calls
to initialize fixed-length arrays contained within the structure. Finally, the wizard has inserted some comments to let you
know that you should include a call to the Initialize method before attempting to use the structure.
If you run the code as upgraded, you will encounter a NullReferenceException, “Object reference not set to an instance
of an object,†on the following line:
MsgBox(mt.MyStringArray(0))
The reason is that the structure member MyStringArray does not contain any array elements. To initialize the array with
elements, you need to call the Initialize method on the structure. The best place to call Initialize is right after you declare a
structure variable. In the example above, you should call Initialize right after the declaration of mt. After you apply this
change, the updated code will be as follows:
The LSet statement can be used in Visual Basic 6 to assign the contents of one user-defined type to another. The
assignment will work even if the two user-defined types do not contain the same fields. LSet works by copying the
memory for the source user-defined type to the target user-defined type.
An example of where you might use LSet is to provide two different ways to view or access the same data. For example,
suppose you want to store a version number in a 32-bit integer. The version number consists of 4 parts, each 1 byte in
length—major, minor, revision, and build version numbers. To simplify creating the 32-bit integer, you can create a user-
defined type containing each part of the version number. For the purpose of storing the version in a 32-bit integer, you can
use LSet to assign a user-defined type containing a 32-bit integer to the user-defined type containing the version parts. The
following Visual Basic 6 code demonstrates how this can be done.
Type VersionPartsType
BuildVer As Byte
RevisionVer As Byte
MinorVer As Byte
MajorVer As Byte
End Type
Type VersionType
Version As Long
End Type
Sub Main()
Dim vp As VersionPartsType
Dim vt As VersionType
vp.MajorVer = 7
vp.MinorVer = 0
vp.RevisionVer = 1
vp.BuildVer = 2
LSet vt = vp
MsgBox "32-bit version number is " & Hex(vt.Version)
End Sub
Visual Basic .NET does not support the LSet statement to assign one type to another. The Upgrade Wizard will place the
following comment before calls to LSet in the upgraded code.
To solve this problem, you can create a copy function that you use to copy each member of one user-defined type to
another user-defined type. In this case, you will need to write a copy function that combines four members of
VersionPartsType into a single member of VersionType. Here is an example of a copy function you can use to take the
place of the LSet statement:
' Calculate the 32-bit version number by placing each version part
' within each byte of the 32-bit integer. Major version goes into
' the top byte and build goes into the lowest byte.
vt.Version = vp.MajorVer * 2 ^ 24 + vp.MinorVer * 2 ^ 16 + _
vp.RevisionVer * 2 ^ 8 + vp.BuildVer
End Sub
Note The LSet statement plays two roles: it left-aligns a string within a string, and it also sets a type to another type.
The first role, left-aligning a string within a string, is supported in Visual Basic .NET.
Threading issues may affect Visual Basic 6 ActiveX DLLs or UserControls that you upgrade to Visual Basic .NET,
especially since the Visual Basic Upgrade Wizard does not make your code thread-safe when upgrading it. You will need
to make manual changes to your code—as explained later in this section—to make it thread-safe.
Such threading issues are uncommon, since components created in Visual Basic 6 are thread-safe by nature. Thread safety
is accomplished in Visual Basic 6 by letting only a single thread access a component. Even if multiple threads are
attempting to gain access to the component, all requests are synchronized through the single accessor thread associated
with the component.
Visual Basic .NET components (including UserControls), on the other hand, are not thread-safe. Multiple threads are
allowed to access the component simultaneously. There is no synchronization of threads on behalf of a Visual Basic .NET
component. As a result, code that is thread-safe in Visual Basic 6 becomes unsafe after being upgraded to Visual
Basic .NET. This change will affect you if you are running your component in a multithreaded environment such as
Internet Explorer, Internet Information Services, or COM+. If you are not sure whether your component will be run in a
multithreaded environment, it is always best to err on the side of caution and ensure that your component is thread-safe.
When more than one thread is executing your code, each thread has access to the same shared data: member variables and
global variables. Allowing more than one thread to manipulate shared data at the same time can lead to problems. For
example, if two threads are attempting to increment a count of items in a collection simultaneously, the count may be end
up being incremented by 1 and not 2. This will happen if both threads obtain the current count at about the same time and
add 1. Neither thread will see what the other thread is doing. We call this a synchronization problem, since it involves a
situation in which only one thread at a time should be allowed to perform the operation.
For example, a synchronization problem can occur when one thread caches a global or member variable value in a local
variable and performs a calculation on that variable. Letâ ™s take a look at the following Visual Basic .NET code.
End Function
End Class
This example is pretty straightforward. A client application calls Withdraw. Withdraw takes the current customer balance
and deducts the requested withdrawal amount. To ensure that the customerâ ™s account does not become overdrawn by
the transaction, the function checks to make sure that the customer has enough money to cover the withdrawal amount.
This works great when only one thread at a time is executing the function. However, you can run into problems if two
threads are executing the function simultaneously.
Letâ™s suppose that the first thread has executed up to the following line:
The customerâ™s balance checked out, so the thread is ready to deduct the Amount. Assume that the customer has just
enough money ($20) to cover the withdrawal request of $20. Just before the first thread executes the above statement, a
second thread comes along, requesting an amount of $20 and completing the following statement:
The second thread also sees that the bank balance, $20, is sufficient to cover the withdrawal request and proceeds to the
next line:
At this point, the customer is rewarded with an account overdraft and the fees that accompany it. The first thread subtracts
the withdrawal, leaving a balance of $0. The second thread, having successfully bypassed the sufficient-balance-to-cover-
withdraw-amount check, then executes the same line, leaving a net balance of –$20. Here code that on the surface
appears to be perfectly legitimate leads to unanticipated results—although the customer would need to submit two
withdrawal requests simultaneously to encounter the problem.
To fix this synchronization problem, you need to make sure that only one thread at a time has access to your shared
member or global variables. In other words, you need to synchronize access to your data, letting only one thread at time
execute certain blocks of code that manipulate shared data. You can do this by using the new Visual Basic .NET keyword
SyncLock.
A SyncLock block ensures that only one thread at a time is executing code within the block. In the earlier code example,
we can fix our negative balance problem by placing a SyncLock around the contents of the Withdraw function:
SyncLock GetType(GreedyBank)
End SyncLock
If there is any chance that your Visual Basic .NET component or UserControl will run in a multithreaded environment, we
highly recommend using SyncÂLock blocks to protect your shared data. Taking the extra time to do this work up front
can save you from grief down the road.
Conclusion
Visual Basic .NET embodies the spirit of the Basic language by supporting most of the Visual Basic 6 language as is and
by extending the language to embrace new features such as inheritance, structured exception handling, and free threading.
As this chapter has demonstrated, alternate solutions exist for each language construct that has been retired or changed in
meaning. You can address each language change by adding new attributes, by creating helper functions or overloaded
functions that are equivalent to the Visual Basic 6 functionality, or by using the .NET Framework to achieve equivalent
behavior. Ultimately, you can take your upgraded code and make it behave as it did before. Once you have your upgraded
application working, you can update it to take advantage of the expansive set of features offered by the Visual Basic .NET
language and the .NET Framework.
Chapter 12
Resolving Issues with Forms
In 1988, Alan Cooper demonstrated a prototype called Ruby to Bill Gates. Ruby provided a form designer that allowed
you to drag and drop controls, then known as gizmos, to quickly and easily create composite forms—such as dialog
boxes, entry forms, and report forms. Microsoft took Cooperâ™s Ruby product and combined it with Basic to create
Microsoft Visual Basic 1. Microsoft has since shipped a version of Ruby with every version of Visual Basic, versions 1
through 6. With every version, that is, until Visual Basic .NET.
Visual Basic .NET provides a new forms package called Windows Forms. Although the Windows Forms package was
designed using the same basic principle as Ruby—it is a form designer that allows you to drag and drop controls and set
properties—it was never meant to be an extension of, nor to be compatible with, Ruby. Therefore, there are fundamental
differences between the two forms packages that affect the way you create Visual Basic applications.
This chapter focuses on some of the fundamental differences between the Ruby and Windows Forms packages.
Specifically, it discusses issues that the Upgrade Wizard does not handle for you. Before we get into the differences,
however, letâ™s look at what Windows Forms and Ruby have in common.
This document is created from a CHM file automatically by an unregistered copy of
CHM-2-Word.
The content of this chapter is skipped, please register CHM-2-Word to get full
features.
For registration information, please refer to: http://www.macrobject.com
General Issues
Whether you are editing an upgraded application or writing a new Visual Basic .NET application from scratch, you will
encounter certain general issues. These issues apply across forms, controls, and components. For example, you need to be
aware of some basic differences between the Visual Basic 6 and Visual Basic .NET property, method, and event (PME)
models. Understanding these fundamental differences should give you a good base to start from when you are trying to
understand issues you encounter involving a specific form or control.
The component models for Visual Basic 6 and Visual Basic .NET are similar in the sense that objects are composed of the
same pieces: properties, methods, and events. You use properties, methods, and events in exactly the same way in Visual
Basic .NET as you do in Visual Basic 6. The main difference is that the specific properties, methods, and events that make
up any given object do not always match up. Letâ™s explore some of the general PME model differences.
One difference between the Visual Basic 6 and Visual Basic .NET PME models is that in some cases they use different
names to refer to the same thing. For example, Visual Basic .NET does not contain a Caption property. Instead,
every .NET control and component uses the Text property. In many other cases, properties, methods, or events have been
renamed. For example, SetFocus in Visual Basic 6 is Focus in Visual Basic .NET, and the Visual Basic 6 GotFocus and
LostFocus events are known as Enter and Leave in Visual Basic .NET.
If you are upgrading an application, you do not need to be too concerned about property naming differences. The Upgrade
Wizard will automatically upgrade your code to use the new names. Your challenge will be to recognize and use the
renamed properties when you start to modify your upgraded project or write new code. Appendix A contains a complete
mapping of all Visual Basic 6 properties, events, and methods to their counterparts in Visual Basic 6.
In some cases, the Upgrade Wizard will map more than one property to constructor parameters on a object. For example,
an equivalent way of setting a formâ™s Left and Top properties is to use the Location property. To set the Location
property, you must first create an instance of the System.Drawing.Point object and initialize System.Drawing.Point with
the left and top position of the component. For example, you can replace the following Visual Basic 6 code:
Command1.Left = 10
Command1.Top = 20
Button1.Location = New System.Drawing.Point(10, 20)
In a similar manner, the Visual Basic 6 Width and Height properties can be represented by the Size property in
Visual Basic .NET.
The Left, Top, Width, and Height properties are available in Visual Basic .NET. We use Location and Size as an
example because if you look in the code generated by the Windows Forms Designer in the “Windows Form
Designer generated code†#Region section of the file, you will see that the designer uses the Location and Size
properties to set the position and size of controls on the form. The Upgrade Wizard will automatically map
properties such as Left and Top to constructor parameters on an object such as Point.
In some cases you will find that there is no equivalent Visual Basic .NET counterpart for a particular property, method, or
event in Visual Basic 6. This will be the case for one of two reasons: support is provided in a different manner, or the
feature is considered out of date.
You wonâ™t find the Visual Basic 6 properties and methods related to drawing and graphics in Visual Basic .NET. At
least, you wonâ™t find them supported in the same way. These properties and methods, such as AutoRedraw, Line, and
Circle, do have equivalents in Visual Basic .NET. The equivalent functions are provided in the .NET Framework
System.Drawing.Graphics class.
In addition, global objects such as Clipboard and Printer do not have matching objects in Visual Basic .NET. However,
you will find a Clipboard class and a PrintDocument class in the .NET Framework that you can use to accomplish the
same task. Table 12-1 provides a list of Visual Basic 6 statements and objects for which equivalent functionality can be
found in the .NET Framework.
Table 12-1 User Interface Components and Their Visual Basic .NET Equivalents
The Visual Basic Upgrade Wizard does not upgrade objects, properties, methods, or events for which there is no
equivalent in Visual Basic .NET. It therefore will not upgrade any properties, methods, or events related to the areas listed
in Table 12-1. The wizard simply preserves your Visual Basic 6 code related to these areas as is. You need to upgrade
your code manually to take advantage of the equivalent functionality provided by Visual Basic .NET or the .NET
Framework.
Weâ™ll discuss graphics statements in more detail shortly. For information on how to add replacements for the
Clipboard object, see Chapter 15. OLE drag and drop was discussed in Chapter 10.
Out-of-Date Features
Features such as Dynamic Data Exchange (DDE) and Visual Basic drag and drop, not to be confused with OLE drag and
drop, are considered to be out of date. Neither Visual Basic .NET nor the .NET Framework supports these features. The
Upgrade Wizard will preserve any Visual Basic 6 code you have written related to out-of-date objects, properties,
methods, or events as is. You need to either change your code to adopt a more up-to-date technology or implement
support for the technology in .NET by calling the Windows API functions that relate to the technology. For example, you
can replace your DDE code with COM, or you can implement DDE support in your Visual Basic .NET application by
declaring and calling the DDE-related API functions. The next section talks in more detail about ways to move code that
uses out-of-date technologies to Visual Basic .NET.
Technology Differences
Since its inception in 1991, the focus of Visual Basic has been to enable Windows technologies for the developer. It
allows you to quickly create Windows applications that use data, graphics, COM, and a host of other technologies. At the
same time, Visual Basic has emphasized compatibility. This means that Visual Basic supportâ ”language statements and
object modelsâ”for various technologies has accumulated over the years. Visual Basic 6 supports all technologies,
regardless of their relevance in todayâ™s world. Objects and statements to support old technology coexist alongside the
new: DDE with COM and the data objects DAO and RDO with ADO.
Visual Basic .NET, on the other hand, starts anew, wiping the technology slate clean. Visual Basic .NET and the .NET
Framework provide first-class .NET support for the latest technologies, such as the .NET component model and
ADO.NET. You can still use some of the existing technologies via COM interop, but you will not find .NET classes
representing existing technologies such as ADO and DDE. Visual Basic .NET and the .NET Framework also offer
renovated object models for technologies such as graphics and forms.
What does this all mean to you? It means that the Visual Basic code you write to take advantage of a particular technology
is different from before. New .NET component models represent these technology areas. In many cases, you will find that
you can accomplish the same tasks as before, but you will need to use different components, properties, methods, and
events to do so. This section discusses changes to technology areas related to components and forms.
Graphics
Visual Basic 6 supports a number of graphics methods on the form, such as Line and Circle, and it supports PictureBox
and Image controls that allow you to draw lines, circles, and text on a form or control. Visual Basic .NET does not support
any methods on the form or PictureBox controls that let you draw. Instead you must respond to the Paint event, where you
are passed a System.Drawing.Graphics object. You can use the System.Drawing.Graphics object and related objects to
draw exactly as you have drawn before.
The Visual Basic .NET Upgrade Wizard does not automatically upgrade your code to respond to the Paint event because
the model for drawing in Visual Basic 6 is dramatically different from that in Visual Basic .NET. Visual Basic 6 allows
you to call a drawing method from any event or function in your code. Visual Basic .NET, on the other hand, requires any
drawing code to be located in the Paint event or in subroutines called from the Paint event. It is therefore impossible for
the wizard to pull calls to drawing methods, scattered about in various parts of your code, and arrange the code so that it
will continue to work. Even if the wizard were able to pull off such a feat, you probably wouldnâ ™t recognize the
resulting code. Therefore, the wizard leaves the calls to the Visual Basic 6 graphics methods in your upgraded Visual
Basic .NET code as is. You need to manually port your graphics-related code to use the .NET equivalent graphics
functions given in Table 12-2. For examples of manually upgrading Visual Basic 6 graphics statements such as Line and
Circle to Visual Basic .NET, see Chapter 15.
Table 12-2 .NET Framework Equivalents for Visual Basic 6 Graphics Statements
Visual Basic
Visual Basic .NET
6
Visual Basic
Visual Basic .NET
6
Dynamic Data Exchange (DDE) was at one time one of the few ways you could pass data between applications without
using a shared file. Visual Basic support for DDE dates back to Visual Basic 1, which included support for DDE on
Windows 3. Visual Basic support for DDE, and the underlying Windows support for DDE, has remained largely
unchanged since 1990. DDE, however, has since been replaced by COM.
COM allows you to share data and invoke methods between applications in a more efficient and scalable manner. Since
most Windows applications that expose public data and methods do so via COM, DDE is generally not the primary way
that you retrieve public data and invoke public methods on a Windows application. You use COM as a means of
communicating with most Windows applications.
After upgrading your application to Visual Basic .NET, it is highly recommended that you replace all DDE
communication with COM. For example, if you have code that calls LinkExecute to invoke a method on a DDE server,
look at the documentation or type library for the server and see whether there is a COM equivalent for the same method. If
there is, add a COM reference to the Windows application and write Visual Basic code to call the method.
Neither Visual Basic .NET nor the .NET Framework provides support for DDE. If you absolutely must communicate with
another application using DDE, you will need to implement DDE in your Visual Basic .NET application in one of two
ways: by creating and interoperating with an ActiveX EXE server built in Visual Basic 6 that manages the DDE
conversation or by declaring and implementing the DDE-related Windows API functions.
If you want to reuse your Visual Basic 6 DDE code, you can create a Visual Basic 6 ActiveX EXE project and add a
public class to exchange the DDE-related information you need with your Visual Basic .NET application. For example, if
you want to perform a DDE LinkExecute operation in your Visual Basic .NET code, you create a DDE helper class
written in Visual Basic 6 as follows:
6. Dim f As New Form1
7.
8. f.Text1.LinkMode = 0                'None
9. f.Text1.LinkTopic = ServerTopic
10. f.Text1.LinkMode = 2                'Manual
f.Text1.LinkExecute ExecCommand
To perform the LinkExecute operation in Visual Basic .NET, add a COM reference to Project1.dll to your Visual
Basic .NET project References list and enter the following code:
   Dim DDEClientHelper As New Project1.Class1Class()
   DDEClientHelper.LinkExecute("MyDDEServer│MyTopic", "MyCommand")
If you want to implement DDE in your Visual Basic .NET application by calling the DDE-related API functions, you will,
at a minimum, need to declare and call DdeInitialize and DdeUninitialize. To call DdeInitialize, you need to implement
DdeCallbackProc. However, this is just a start. You will also need to call a number of other DDE-related API functions to
establish a connection and send data. To implement functionality to perform LinkExecute, for example, requires calling at
least eight other DDE-related API functions. This task also requires a deep understanding of Windows messaging
architecture and memory management, not to mention a knowledge of how to represent Windows types—such as handles
and structures—in Visual Basic code. This undertaking is not for the faint of heart. To learn more about the DDE-related
Windows API, search for “Dynamic Data Exchange Management Functions†in the Microsoft Developer Network
(MSDN). For more information on issues related to using Declare statements in your code, refer to Chapter 11.
Visual Basic drag and drop has been available since Visual Basic 1. This feature allows you to drag and drop a control at
run time onto a form or onto any other control on a form. The result of the drag-and-drop action depends on the code you
write for the DragDrop event for a form or control. For example, you could write code for a CommandButton that would
check to see whether the control being dropped was a TextBox and, if so, copy the TextBox text to the CommandButton
caption. At run time, you drag and drop the TextBox onto the CommandButton, and the button caption changes to the
TextBox text.
Although Visual Basic drag and drop is useful in creating a rich user interface, it has some serious limitations. Its
functionality is limited to dragging and dropping forms within the same application; you cannot drag and drop between
applications. In addition, if you set DragMode to automatic, there is no result for the drag-and-drop operation unless you
write code to define the drop action. This means that you need to write code in the DragDrop event for the form and all
controls that are to support drag and drop. Finally, when a control is dragged over a particular form or control, there is no
concept of a no-drop icon. You need to write code in the DragOver event and set the DragIcon property to a no-drop icon
for all controls that you do not want to allow to be dropped.
Visual Basic OLE drag and drop was introduced in Visual Basic 5. It provides several advantages over Visual Basic drag
and drop. First, OLE drag and drop is a COM standard, meaning that you can drag and drop data between any applications
that support OLE drag and drop and the data that you are dragging. In addition, OLE drag and drop is automatic. You can
enable it by setting the OLEDragMode and OLEDropMode properties of the form and controls that you want to have
support OLE drag and drop. You are not required to write any code to enable basic drag-and-drop operations. Finally, in
OLE drag and drop, the concept of a no-drop icon is built in. This means that by default the form and controls do not
advertise themselves as targets of a drag-and-drop operation. You set the OLEDropMode property or write code for the
controls that you want to have support OLE drag and drop.
Because Visual Basic drag and drop is considered an outdated feature, the Upgrade Wizard does not upgrade your
DragDrop-related code. Instead, it preserves the Visual Basic 6 drag-and-drop code as is in the upgraded Visual
Basic .NET project. The code will not compile, so you need to either delete it and remove the drag-and-drop functionality
or change the code to use OLE drag and drop. For information on how to implement drag and drop in Windows Forms,
see Chapter 10.
Letâ™s start our discussion of forms by looking at the differences between a Visual Basic 6 form and a Visual
Basic .NET form. Fortunately, these differences are relatively minor. The basic concept of a form, for example, is the
same between Visual Basic 6 and Visual Basic .NET. The form serves as a surface where you can place controls to create
a composite form. The way in which you create a form, set properties, and add controls to it using the Visual Basic .NET
designer is also essentially the same as in Visual Basic 6. You should find yourself at home when you create and edit
Visual Basic .NET forms.
A subtle difference between Visual Basic 6 and Visual Basic .NET is that some events do not fire in the same relative
order. For example, in Visual Basic 6 the Load event is the first event you expect to fire. This is not true for Visual
Basic .NET, however, where the Resize event occurs before the Load event. Also, depending on the control properties you
set at design time, a number of other events can occur before the Load event. Table 12-3 lists the events that can occur
before the Form.Load event.
Table 12-3 Visual Basic .NET Events That Can Occur Before the Form.Load Event
Object Event
Form Move
Form Resize
CheckBox Click
ComboBox Change
OptionButton Click
TextBox TextChanged
Problems generally occur when you write your code in such a way that code in one event relies on values or objects being
initialized or set in another event. For example, suppose that you create an element of a control array in the Form.Load
event and write code to position the control array element in the Form.Resize event. If the Resize event fires after the
Load event, everything works as planned. If, on the other hand, the Resize event fires before the Load event, an error will
occur in your Resize code. The reason is that you will be attempting to access a control array element that does not yet
exist.
To illustrate this problem, consider the following code for a Visual Basic 6 standard EXE application, in which Form1
contains a CommandButton with the Index property set to 0.
Private Sub Form_Load()
   ' Load a new element of the Command1 control array
   Load Command1(1)
   Command1(1).Visible = True
End Sub
Private Sub Form_Resize()
   ' Position control array element 1 below control array ele
ment 0
   Command1(1).Top = Command1(0).Top + Command1(0).Height + 3
End Sub
If you use the Upgrade Wizard to upgrade the application to Visual Basic .NET, the upgraded code will be as follows:
Private Sub Form1_Load(ByVal eventSender As System.Object, _
                       ByVal eventArgs As Syste
m.EventArgs)Â _
                       Handles MyBase.Load
   ' Load a new element of the Command1 control array
   Command1.Load(1)
   Command1(1).Visible = True
End Sub
'UPGRADE_WARNING: Event Form1.Resize may fire when form is initia
lized.
Private Sub Form1_Resize(ByVal eventSender As System.Object, _
                         ByVal eventArgs As S
ystem.EventArgs)Â _
                         Handles MyBase.Resize
   ' Position control array element 1 below control array ele
ment 0
   Command1(1).Top = _
      VB6.TwipsToPixelsY(VB6.PixelsToTwipsY(Command1(0).Top) + _
      VB6.PixelsToTwipsY(Command1(0).Height) + 3)
End Sub
When you run the upgraded Visual Basic .NET application, an error occurs on the assignment of Command1(1).Top. The
error is âœControl array element 1 doesnâ™t exist.â It occurs because the Resize event takes place before the Load
event.
You can fix this problem, and others like it, by using a class-level Boolean variable to keep track of whether the Load
event has been executed. If it has not executed and is not in the process of executing, you should not execute any code in
the current event. In the example just given, you can declare a new form-level variable called m_Loaded as follows:
Private m_Loaded As Boolean = False
In the Form1_Resize event procedure, check m_Loaded and do not execute any code unless it is True:
If m_Loaded Then
   ' Position control array element 1 below control array ele
ment 0
   Command1(1).Top = _
      VB6.TwipsToPixelsY(VB6.PixelsToTwipsY(Command1(0).Top) + _
      VB6.PixelsToTwipsY(Command1(0).Height) + 3)
End If
' Load a new element of the Command1 control array
Command1.Load(1)
Command1(1).Visible = True
Because the Resize event fires after the Load event in Visual Basic 6, you need to emulate this behavior in Visual
Basic .NET. You do this by calling the Resize event before exiting the Form1.Load event procedure, as follows:
m_Loaded = True
' Load a new element of the Command1 control array
Command1.Load(1)
Command1(1).Visible = True
' Emulate Visual Basic 6 behavior by invoking the Resize event
Form1_Resize(Me, New System.EventArgs())
Heed Upgrade Warnings
The Form1_Resize event procedure for the upgraded Visual Basic .NET code includes the comment
“UPGRADE_WARNING: Event Form1.Resize may fire when form is initialized.†The Upgrade Wizard includes this
type of warning for any upgraded event that is known to fire before the Load event. To be safe in situations such as this,
you may want to implement the same types of changes that we applied to the Resize event in the previous example.
Visual Basic 6 supports the notion of a default form. A default form is the default instance of the form that Visual Basic
creates for you automatically. Having a default form instance allows you to write code such as the following:
Form1.Caption = "Caption for my default form"
Visual Basic .NET does not support a default form instance. If you write the equivalent code in Visual Basic .NET, as
follows:
Form1.Text = "Caption for my default form"
you will run smack into a compiler error informing you that there is no shared member called Text on Form1. One way to
solve this problem is to add a shared method to your form that returns the default instance—in other words, the first
created instance—of the form. For example, for a default Visual Basic .NET Windows application project, you could add
the following code to Form1:
Public Shared m_DefInstance As Form1
Public Shared ReadOnly Property DefInstance() As Form1
   'If no form instance exists, create one
   'Note: if the form is the startup form it willÂ
   'already exist
   If m_DefInstance Is Nothing Then
      m_DefInstance = New Form1()
   End If
   Return m_DefInstance
End Property
This code sets up the DefInstance property but does not initialize the m_DefInstance variable to the first form instance
created. To accomplish this initialization, you need to initialize the variable in the Sub New constructor for the form. The
Sub New constructor exists on every form and can be found in the #Region code block labeled “Windows Form
Designer generated code.†Expand the #Region code block and insert the following assignment in Sub New at the end of
the subroutine:
If m_DefInstance Is Nothing Then
   ' Initialize m_DefInstance to the first form created
   m_DefInstance = Me
End If
Now you can write code that accesses the default instance of a form as follows:
Form1.DefInstance.Text
The default form is an example of a Visual Basic 6 semantic that does not exist in Visual Basic .NET. However, as we
have demonstrated here, you can write code to cause your Visual Basic .NET forms to adopt the same semantic.
It is important to understand the purpose of the DefInstance property because you will likely see it used in your upgraded
application. It is used when you access properties or controls using the name of the form. The Upgrade Wizard
automatically implements the DefInstance shared method on all upgraded forms. However, the implementation that the
wizard applies to your upgraded form is much more complicated than the simple example given here. The generated code
takes into account whether the form is a startup or not. It also contains logic to initialize the default instance variable
correctly when the form is a multiple document interface (MDI) form or an MDI child form. You can examine and even
modify the code that the wizard generates. The DefInstance-related code is generated within the #Region blocks
“Windows Form Designer generated code†and “Upgrade Support.â€
Chapter 10 introduced differences in application lifetime between Visual Basic 6 and Visual Basic .NET. Letâ ™s look
into this area in more detail. An application in Visual Basic 6 application terminates when the last form is unloaded. A
Visual Basic .NET application, on the other hand, terminat es when the startup form associated with the
application is unloaded. This means that your Visual Basic .NET application can terminate
when one or more forms is still showing.
The way you specify the startup form in Visual Basic .NET is similar to the way you do so in a Visual Basic 6
application. To set the startup form in Visual Basic .NET, right-click the project name in the Solution Explorer and
select Properties from the shortcut menu. You are presented with the project settings dialog box, which groups
various settings under a number of categories. Select the General category under Common Properties, and choose
the desired startup form from the Startup Object list. Figure 12-3 shows where you can find the Startup Object list.
Figure 12-3.
Startup Object list in the Visual Basic .NET project settings dialog box.
When you create a new Visual Basic .NET application or upgrade a Visual Basic 6 application, the lifetime of the startup
form determines the lifetime of the application. If you unload the startup form, the application terminates no matter what
other forms are currently open. If the startup form is your main form, this termination will most likely not be an issue. If,
on the other hand, the form that determines the application lifetime is a secondary form, such as an MDI child form, you
will probably need to make some changes to your application.
The simplest way to deal with this issue is to identify a form in your application that you consider to be the main form.
For example, if your application is composed of an MDI form with a number of MDI child forms, you would identify the
MDI form as the main form. You should set this form as the startup form for the application.
If you want your application to terminate when the last form is closed, you need to write code that manages the lifetime of
your application. Letâ™s look at a Visual Basic 6 application that has two forms: Form1 and Form2, in which Form2 is
shown from Form1â™s Load event. In Visual Basic 6, the application terminates when you close both forms, regardless
of the order in which you close them. Form1 contains the following code in its Form_Load event procedure to show
Form2:
Private Sub Form_Load()
   Form2.Show
End Sub
If you upgrade the project to Visual Basic .NET, the application will terminate as soon as you close Form1, even if Form2
is still showing. Suppose you want to change the behavior of the Visual Basic .NET application so that it behaves like the
Visual Basic 6 application. The reason Form1 determines the lifetime for the Visual Basic .NET application is that the
compiler includes an invisible piece of code that starts up your application and shows the startup form. The code the
compiler includes is
System.Windows.Forms.Application.Run(New Form1())
Application.Run is a .NET Framework function that loops internally processing messages and waits for the form instance
that is passed to it to terminate. If you do not want your application to be associated with any particular form, you can
substitute a version of Application.Run that takes no arguments as follows:
System.Windows.Forms.Application.Run()
This version loops until you call Application.Exit from somewhere in your program. You can control the execution of
your application by calling this version of Application.Run from Sub Main. For example, you could show Form1 and then
call Application.Run from Sub Main as follows:
Sub Main()
   Form1.DefInstance.Show()   ' Equivalent to Form1.ShowÂ
                              ' in Visual
 Basic 6
   System.Windows.Forms.Application.Run()
End Sub
Create Sub Main in a new code module called Module1, and then set Module1 as the startup object for the project.
Since the application is no longer tied to the lifetime of any particular form, you need to add code that calls
Application.Exit when the last form is unloaded. One way to do this is to have each form increment a global form count
every time a form instance is created and then decrement the form count when the instance is destroyed. When the
application form count is decremented and reaches 0, meaning that no more forms are loaded, call Application.Exit. To
increment and decrement the global form count, you can add two functions to Module1 called AddForm and
RemoveForm. You also need to declare a module-level variable that will keep track of the form count. Here is an example
of the code you need to add:
Dim FormCount As IntegerÂ
Sub AddForm()
   FormCount += 1  'Equivalent to FormCount = FormCount + 1
End Sub
Sub RemoveForm()
   FormCount -= 1  'Equivalent to FormCount = FormCount - 1
   'At least one form remains, keep going
   If FormCount > 0 Then
      Exit Sub
   End If
   'No more forms – end the application
   'Note: Make sure this call is the last statement in theÂ
Sub
   'System.Windows.Forms.Application.Exit()
End Sub
You then need to add calls to AddForm and RemoveForm to both Form1 and Form2. You can add the call to AddForm to
the Form_Load event procedure and a call to RemoveForm to the Form_Closed event procedure. Here is the resulting
code for Form1:
Private Sub Form1_Load(ByVal eventSender As System.Object, _
                       ByVal eventArgs As Syste
m.EventArgs)Â _
                       Handles MyBase.Load
   AddForm()
   Form2.DefInstance.Show()
End Sub
Private Sub Form1_Closed(ByVal sender As Object, _
                         ByVal e As System.Ev
entArgs)Â _
                         Handles MyBase.Closed
   RemoveForm()
End Sub
Private Sub Form2_Load(ByVal sender As System.Object, _
                       ByVal e As System.EventA
rgs)Â _
                       Handles MyBase.Load
   AddForm()
End Sub
Private Sub Form2_Closed(ByVal sender As Object, _
                         ByVal e As System.Ev
entArgs)Â _
                         Handles MyBase.Closed
   RemoveForm()
End Sub
As long as Module1 is set as your startup object, the application will behave like the Visual Basic 6 application. You can
close Form1 and Form2 in any order. When the last form is closed, Application.Exit is called and the application
terminates.
MDI Forms
Both Visual Basic 6 and Visual Basic .NET support MDI parent and child forms. The difference between the two
environments lies in how you create the MDI parent form and show the MDI child form. To create an MDI application in
Visual Basic 6, you add a special MDIForm to your application to act as the MDI parent form. Visual Basic .NET has no
concept of an MDIForm as a separate form type. Instead, you set the IsMdiContainer property of a Visual Basic .NET
form to True.
In Visual Basic 6, you specify MDI child forms by setting the MDIChild property of a form to True. When the MDI child
form is shown, it always displays within the MDI parent form. Visual Basic .NET does not work this way. In Visual Basic
.NET, there is no MDIChild property. Instead, you must write code to set the MdiParent property of a form to the MDI
parent form. For example, if you want Form2 to be an MDI child form of MDI parent Form1, you need to write the
following code:
Dim MyForm2 As New Form2
MyForm2.MdiParent = Me
MyForm2.Show
In the code example given here, assume that the code is written in the module for Form1. For example, we can include
this code in the Form1_Load event procedure to show the MDI child Form2 from Form1.
As we discussed in the previous section, the lifetime of a Visual Basic .NET application is tied to the startup form. The
same is true of MDI form applications, but with an interesting twist. Visual Basic 6 allows you to specify an MDI child
form as the startup form in an MDI form application. Specifying an MDI child form as the startup form has the advantage
that you donâ™t have to write any code to show the MDI parent and MDI child form by default. When the MDI startup
child form is shown, it checks to see whether the MDI parent form is showing. If it is not, the MDI parent is shown
automatically and the MDI child form is displayed within the MDI parent.
If you upgrade an application in which an MDI child form is the startup form, you will find that it will terminate when you
close the MDI child form. To prevent this, you need to specify the MDI parent form, not the child form, as the startup
form.
Chapter 13
Upgrading ActiveX Controls and Components
If youâ™re on old hand at Microsoft Visual Basic programming, you likely have vivid memories of the last great
migrationâ”the one between Visual Basic 3 and 4. It involved the great leap forward from 16 bits to 32 bits and the
introduction of OLE components now known as ActiveX components.
The transition from Visual Basic 6 to Visual Basic .NET is reminiscent of those days, but on a much grander scale. Once
again Microsoft is introducing a new component model to open doors to a whole new world of applications: Web
applications and XML Web services. This new component model is the foundation for the .NET Framework.
This is all fine and wonderful, you might say, but are you kidding me? Do I need to update all of my ActiveX controls
to .NET controls, just as I was required to replace all of my VBXs in Visual Basic 4 32 bit? Fortunately, the answer is no
to both questions. You can still use your existing ActiveX components with Visual Basic .NET.
One difference between the transition from Visual Basic 6 to .NET and that from Visual Basic 3 to 4 is that Visual
Basic .NET still supports the old component model. This was not true of Visual Basic 4. Migrating your VBX components
to ActiveX was an all-or-nothing proposition. If you wanted to continue to use VBXs, you had to keep your application in
the 16-bit world. If the vendor for your favorite control did not have a 32-bit ActiveX control replacement for your
favorite VBX control, you were out of luck. You had to either wait it out and hope the vendor released a compatible
control or use an ActiveX component provided by a different vendor. In any case, you were likely to run into issues when
upgrading your application, whether the ActiveX component claimed full compatibility or not.
In Visual Basic .NET, you have the choice of using either an ActiveX component or an equivalent .NET component in its
place. Unlike Visual Basic 4, which had a control migration feature that automatically upgraded your VBX controls to
ActiveX controls, Visual Basic .NET has no such feature. Furthermore, Microsoft is not introducing any of the new .NET
controls and components as direct replacements of similarly featured ActiveX controls. For example, even though there is
a .NET TreeView control, it is not fully compatible with the ActiveX TreeView control. In most cases, you will need to
make modifications to your code to use the new .NET controls. Weâ ™ll discuss how to replace ActiveX controls with
Windows Forms controls in Chapter 19. For now, letâ™s concentrate on using ActiveX controls in Visual Basic .NET.
Since one of the overriding goals of the Upgrade Wizard is to ensure compatible behavior after you upgrade your
application to Visual Basic .NET, the Visual Basic team decided to allow your project to use the same ActiveX controls
and components as before. There are a couple of exceptions to this rule, but in general your Visual Basic .NET application
will run against the same set of ActiveX components after the upgrade. This strategy greatly increases the chances that
your application will perform exactly as it did in Visual Basic 6. Once you are assured that everything is behaving the way
you expect it to, you can replace ActiveX controls and components with equivalent .NET components as you see fit.
When you place an ActiveX control on a Windows form, something interesting happens. The control becomes part
ActiveX, part .NET—something we call an ActiveX .NET control. The ActiveX portion is the core control that defines
the behavior of the control you know and love. The .NET part is a translation layer that is wrapped around the control to
help make it fit in with other .NET controls and objects. This section describes how ActiveX controls are supported
in .NET.
For Windows Forms to be able to position and display your ActiveX control, additional properties, events, and methods
are added to the control. You can think of these as extended properties, methods, and events (PMEs) of your control.
Figure 13-1 illustrates how an ActiveX control is extended to include properties such as Location, Tag, and Visible. These
extended PMEs are combined with the public PMEs of your ActiveX control to form a new wrapper class. When you
write code against the control, you are actually writing it against the wrapper class. The wrapper class in turn delegates to
the ActiveX control. If you are setting a public property that is available on the ActiveX control, the wrapper class simply
passes the property setting straight through to the control. If you are setting an extended property not found on the
ActiveX control, the wrapper takes care of performing the operation on the control, such as toggling visibility or changing
its size.
Figure 13-1
An Ax wrapper encapsulates and extends an ActiveX control.
The Ax (short for ActiveX) wrapper is officially known as the Windows Forms Wrapper (WFW). It is called this because
the wrapper is specifically generated when the control is placed on a Windows form. If you place an ActiveX control on
any other type of .NET form, such as a Web form, the Ax wrapper is not generated.
The concept of a wrapper class is not new. Visual Basic 6 wraps ActiveX controls in a
similar fashion. However, the way controls are wrapped in the two environments is quite
different. This leads to a number of interesting issues when the code is hosted on a Visual
Basic .NET form.
If you create a new Visual Basic .NET Windows application project and place an ActiveX control on the form, the
control name is prefixed with Ax. For example, if you place an ActiveX TreeView control on the form, the
resulting name of the control is AxTreeView. As the name implies, when you write code against AxTreeView you
are actually programming against the Ax wrapper class for the control, not the control itself.
One purpose of the Windows Forms ActiveX control wrapper is to expose ActiveX properties in such a way that they can
be naturally assigned to other .NET component properties. For example, in Visual Basic 6, the Picture property is based
on the StdPicture object, which implements the IPicture and IPictureDisp interfaces. In Visual Basic .NET, however, the
Picture property for .NET components is type System.Drawing.Image. The problem is that you cannot directly assign a
property of type StdPicture to a System.Drawing.Image property. To help alleviate this problem and avoid nasty-looking
conversion code around every property assignment, the wrapper automatically exposes your ActiveX controlâ ™s Picture
property as System.Drawing.Image. Figure 13-2 illustrates how the Ax wrapper exposes an ActiveX property such as
Picture as a .NET type such as System.Drawing.Image.
Figure 13-2
Picture property translated to System.Drawing.Image.
In a similar manner, the Windows Forms Ax wrapper exposes other common ActiveX properties, such as Font and Color,
as the respective .NET type. Table 16-1 lists the ActiveX property types that the Windows Forms Ax wrapper exposes as
the appropriate .NET type.
OLE_COLOR System.Drawing.Color
Font System.Drawing.Font
Take, for example, the following Visual Basic 6 code, which assigns a Picture to the Picture property of a ListView
ActiveX control:
ListView1.BackColor = vbRed
Set ListView1.Picture = _
   LoadPicture(Environ("WINDIR") & "\Prairie Wind.bmp")
Set Picture1.Picture = ListView1.Picture
ListView1.BackColor = System.Drawing.Color.Red
ListView1.Picture = _
   System.Drawing.Image.FromFile(Environ("WINDIR") & _
   "\Prairie Wind.bmp")
Picture1.Image = ListView1.Picture
Variant and Object Types: Mapping—Not!
As you can see, you can assign native .NET types such as System.Drawing.Color.Red or System.Drawing.Image to
ActiveX control properties such as BackColor or Picture, respectively. This strategy works great when the ActiveX
control property or method parameter is strongly typed. In this case Windows Forms sees that the property type for
BackColor is OLE_COLOR and the type for Picture is IPictureDisp. Seeing these types, Windows Forms automatically
maps the properties to their .NET equivalents when creating the wrapper. What happens when Windows Forms cannot tell
the property type? For example, what happens when a property or method parameter is of type Variant or Object?
If an ActiveX control property or method parameter is not strongly typed—that is, if it is a generic type such as Variant
or Object—the type is mapped to a .NET Object type in the wrapper. This commonly occurs when the ActiveX control
exposes a property or a method that contains optional parameters. In order for a parameter to be optional it must also be of
type Variant. If, for example, you are calling an ActiveX control method that takes an optional Picture, Color, or Font, you
must explicitly pass the underlying ActiveX type as the parameter value. This means that you will need to convert a .NET
type to the equivalent ActiveX type before making the call. Doing so can lead to some ugly-looking, but necessary, code.
Consider the following Visual Basic 6 code, which adds an image to an ActiveX ImageList control:
ImageList1.ListImages.Add , , _
   LoadPicture(Environ("WINDIR") & "\Zapotec.bmp")
If you right-click the line of code in Visual Basic 6 and choose Object Browser and then navigate to the ListImages type,
you will see the following declaration:
Function Add([Index], [Key], [Picture]) As ListImage
Each parameter contained in square brackets is an optional parameter of type Variant. Although the third parameter is
named Picture, it is of type Variant, so Visual Basic .NET does not know to map this property to System.Drawing.Image.
If you upgrade this code to Visual Basic .NET, you end up with the following:
ImageList1.ListImages.Add( ,  , _
   VB6.ImageToIPictureDisp(System.Drawing.Image.FromFile( _
   Environ("WINDIR") & "\Zapotec.bmp")))
If the Picture parameter had been treated as a System.Drawing.Image type, the call to System.Drawing.Image.FromFile
would be sufficient. In this case, however, the Picture parameter is mapped to Object and requires the underlying ActiveX
type IPictureDisp. The compatibility library function ImageToIPictureDisp is required to convert the Image object to an
IPictureDisp—the underlying interface type for Picture—and the resulting IPictureDisp is passed as the Picture
argument to the Add method. Without this conversion you will encounter a run-time COMException returned from the
ImageList control, containing the error “Invalid picture.â€
Fortunately, as in the case just described, the Upgrade Wizard handles most of these conversions for you. In rare cases
you will need to call a conversion helper function to convert a .NET type to the equivalent ActiveX type.
Standard Component Wrappers and ActiveX Control Subobjects
ActiveX controls that contain one or more subobjects have a split personality. The top-level set of control properties,
methods, and events are wrapped and handled by an Ax wrapper. A simple component wrapper wraps all subobjects and
other types defined by the control. A simple component wrapper is also applied to all standard ActiveX components that
are not ActiveX controls. For example, if you reference ADO, a simple component wrapper is applied to expose the
properties, methods, and events of all ADO objects. The objects are exposed using the .NET types that most closely
represent the underlying type contained in the ActiveX component.
In the case of the TreeView control, the Ax wrapper is applied to the TreeViewâ ™s top-level properties, methods, and
events. Properties such as Appearance, Font, and Style are wrapped and handled by the Ax wrapper. The Ax wrapper
exposes the Font property as a System.Drawing.Font, for example. If TreeView had a BackColor property, it would be
exposed as a System.Drawing.Color property.
Now letâ™s take a look at subobjects that the TreeView control exposes, such as Nodes and Node. The BackColor
property of a Node gets exposed to you as a System.UInt32 type. But isnâ™t BackColor type OLE_COLOR? Why
UInt32â‰â‰? UInt32 is chosen for two reasons:
The type most closely matches the type OLE_COLOR, which itself is a UInt32.
Neither the simple component wrapper nor the Ax wrapper supports type aliases. In the original COM type
library, OLE_COLOR is an alias for UInt32.
Consider the following Visual Basic 6 example, which sets the ForeColor property on both the ListView ActiveX control
and one of its ListItem subobjects:
Dim li As ListItem
Set li = ListView1.ListItems.Add(, "Item1Key", "Item1")
ListView1.ForeColor = vbRed
li.ForeColor = vbRed
ListView1.ForeColor = System.Drawing.Color.Red
li.ForeColor = _
   System.Convert.ToUInt32(System.Drawing.ColorTranslator.ToOle( _
   System.Drawing.Color.Red))
Although ForeColor is the same exact type for ListView and the ListItem object, the code needed to assign the value of
Red is radically different. The ListView ForeColor property is exposed by the Ax wrapper as type System.Drawing.Color,
so itâ™s quite natural to assign a System.Drawing.Color.Red object to the property. The ListView Ax wrapper takes
care of translating the System.Drawing.Color.Red object value to the equivalent UInt32 color value.
In the case of the ListItem.ForeColor property, the simple component wrapper exposes the type as System.UInt32, leaving
it up to you to figure out how to convert a System.Drawing.Color.Red object value to a numeric UInt32 color value. To
make this conversion, you first need to convert the color object to a numeric value by using
System.Drawing.ColorTranslator.ToOle. The ToOle function returns a signed long integer or System.Int32. You then
need to convert the Int32 value to a UInt32 value by using the System.Convert class.
Fortunately, the Upgrade Wizard handles most of these conversions for you. The downside is that you may end up with
some rather unfamiliar-looking conversion code. Table 16-2 provides a list of conversion helper functions so that
youâ™ll understand how the conversion works when you see one of these in code. You can also use these functions
when writing new code to help make assignments between ActiveX and .NET types.
Table 16-2 Useful Conversion Functions for Common ActiveX Object Types
Allow you to convert from virtually any .NET type to another .NET type. Use
System.Convert methods System.Convert to convert 32-bit signed integers (System.Int32) to 32-bit
unsigned integers (System.UInt32).
System.Drawing.ColorTranslator
Allow you to convert between OLE color, Windows color, and .NET color types.
methods
Certain lines of your upgraded code may contain type mismatch assignments. For example, you may find code in which
the MousePointer property of an ActiveX control is being assigned to a System.Windows.Forms.Cursors property. The
problem is that an ActiveX MousePointer property is usually a MousePointerConstants enumeration type defined by the
control—in other words, a numeric type. Each System.Windows.Forms.Cursors property, such as IBeam, returns a
Cursor object. Attempting to assign a Cursor object to a numeric type is a recipe for a compiler error. In this case, you
receive a descriptive compiler error telling you that you cannot assign a Cursor to a numeric type.
Compiler errors are not the type of errors that you need to worry about. Although annoying at times, they are in-your-face
error messages that point directly to a problem in your code. You can easily locate the problem and, in most cases,
especially with the help of IntelliSense, find a quick fix. A more insidious problem that will give you fits is an exception
that occurs at run time. The two most common exceptions that relate to the assignment of incompatible types at run time
are InvalidCastException and COMException.
InvalidCastException
The InvalidCastException is a standard .NET exception that occurs when the .NET Framework is unable to cast one type
to another at run time. This exception commonly occurs when you attempt to assign a .NET type to an ActiveX type. For
example, if you attempt to assign a .NET collection to an ActiveX component method that returns a Visual Basic 6
Collection object, this exception will occur.
COMException
A COMException can occur any time a property or method of an ActiveX control or component is called. The exception
generally occurs because the Visual Basic .NET code is passing a .NET object when it should be passing an ActiveX
object. For example, if you attempt to assign an Ax wrapped ImageList control to the ImageList property of an Ax
wrapped TreeView control, as in the following line of code, it will bark back at run time with a COMException.
AxTreeView1.ImageList = AxImageList1
The problem happens in this case because you need to assign the underlying ActiveX control object to the ImageList
property. You can obtain the underlying ActiveX control object by calling GetOcx on the Ax wrapped control. The
following code works and does not bark at you:
AxTreeView1.ImageList = AxImageList1.GetOcx()
This section discusses common issues that you may encounter when using an ActiveX control or component in your
Visual Basic application after upgrading to Visual Basic .NET.
The Visual Basic .NET compiler introduces a new semantic when passing properties ByRef to a function. If the property
has write access, it is set to the return value of the ByRef parameter. In Visual Basic 6 this is not the case. If you pass a
property ByRef, the property is never set.
As an example, consider the following Visual Basic 6 code contained in Form1 of a default standard EXE project:
Private Sub Form_Load()
   SetString Me.Caption
   MsgBox Me.Caption
End Sub
Private Sub SetString(ByRef str As String)
   str = "String changed by SetString"
End Sub
When this code is run, the message box displays “Form1.†Me.Caption is not changed by calling the function
SetString.
Consider the following code that has been upgraded to Visual Basic .NET:
Private Sub Form1_Load(ByVal eventSender As System.Object, _
                       ByVal eventArgs As Syste
m.EventArgs)Â _
                       Handles MyBase.Load
   SetString(Me.Text)
   MsgBox(Me.Text)
End Sub
Private Sub SetString(ByRef str_Renamed As String)
   str_Renamed = "String changed by SetString"
End Sub
If you run the Visual Basic .NET version, you will see that the Text property of the form gets set to âœString changed by
SetStringâ as a result of calling SetString.
This is both good and bad. Itâ™s good that Visual Basic .NET offers this new semantic so that properties passed ByRef
are set the way you would expect. Itâ™s bad, however, in that this change could lead to subtle, hard-to-find problems in
your upgraded code.
This issue does not end with your own code. You may run into it when attempting to call an ActiveX component that
takes ByRef parameters. Consider the following Visual Basic .NET code written against a ListView control located on
Form1 in a default Windows application project:
Dim lvItem As MSComctlLib.ListItem
AxListView1.ListItems.Add(, , "Item1")
lvItem = AxListView1.ListItems(AxListView1.ListItems.Count)
Run this code and you will encounter a COMException on the last line, telling you that “Property is read-only.†What
property is read-only? you might ask. How am I going to figure this one out?
The problem in this case is that the ListItems default property Item takes a ByRef Variant argument.
AxListView1.ListItems.Count is passed as an argument to this ByRef parameter. Shouldnâ™t this be okay? Isnâ™t the
Count property read-only, so the compiler wonâ™t try to set it? Yes and no. The Count property is implemented with
both a Get and a Let. Therefore, the compiler thinks the property can be written. When it tries to write to the property,
however, the Let implementation for the Count property throws an exception.
The reason the ListView ListItems subobject has a settable Count property is a long, twisted story stretching back three
versions of Visual Basic. To work around these types of issues, you can include parameters that you donâ ™t want to be
set in parentheses. Using parentheses tells the compiler to pass the property read-only. For example, if you change the
code in the previous example to the following, it will work. Note the extra parentheses around
AxListView1.ListItems.Count:
' Pass AxListView1.ListItems.Count ByVal to avoid Set being calle
d
lvItem = AxListView1.ListItems((AxListView1.ListItems.Count))
When a Collection Is Not a Collection
Just because a .NET collection looks like a collection does not mean that it really is a collection—at least not in the
Visual Basic 6 sense. A .NET collection is almost the same as a Visual Basic 6 collection. It has the same methods, such
as Add, Remove, and Item, but you cannot assign a .NET collection to a Visual Basic 6 collection. Why would you need
to use a Visual Basic 6 collection? Isnâ™t this .NET? Donâ ™t you want all of your types to be declared in .NET?
Isnâ™t the .NET collection a new and improved Collection object? Yes on all counts, but there is one case in which you
will need to use a Visual Basic 6 collection: when you call a COM object that takes a Visual Basic 6 Collection object as a
parameter or that returns a Visual Basic 6 Collection object. A .NET collection will not do in this case.
Take, for example, the following Visual Basic 6 code declared in a public class module of an ActiveX DLL called
RetCollection.dll:
Option Explicit
Private m_Collection As New Collection
Public Function ReturnCollection() As Collection
   Set ReturnCollection = m_Collection
End Function
Private Sub Class_Initialize()
   m_Collection.Add "MyItem1", "MyKey1"
   m_Collection.Add "MyItem2", "MyKey2"
End Sub
Now consider the following Visual Basic .NET code, which calls the ReturnCollection function:
Dim c As Collection
Dim rc As New RetCollectionLib.RetCollection()
c = rc.ReturnCollection
MsgBox(c.Item(1))
The code compiles, runs—up to a point, at least—and throws an InvalidCastException on the attempt to assign c to
rc.ReturnCollection. The problem is that a .NET collection—represented by the variable c—cannot be assigned to the
Visual Basic 6 Collection object returned by the function ReturnCollection.
To fix this problem, you need to add a reference to the Visual Basic 6 runÂtime Msvbvm60.dll. Follow these steps to do
so:
1. Right-click the References list in Solution Explorer, and choose Add Reference.
2. Select the COM tab.
3. Select Visual Basic For Applications Version 6.0 from the list.
4. Change your Visual Basic .NET code to use the Visual Basic 6 ÂCollection object as follows:
        Dim c As VBA.Collection
The variable c now matches the type returned by the ReturnCollection function, so everything will now work as expected.
Nonzero-Bound Arrays
You may encounter problems when attempting to call a COM object method that returns an array defined with a nonzero-
bound lower dimension, such as –10 or 1. Take, for example, the following code defined in a Visual Basic 6 ActiveX
server DLL:
Option Base 1
Public Function RetStringArray() As String()   Â
   Dim i As Long
   'Array is 1-based. We're using Option Base 1
   Dim s(10) As String
  Â
   For i = LBound(s) To UBound(s)
      s(i) = i
   Next
  Â
   RetStringArray = s   Â
End Function
Suppose that you are trying to call the RetStringArray function, using the following Visual Basic .NET code:
Dim rs As New RetStringArrayLib.RetStringArray
Dim s() As String
s = rs.RetStringArray
MsgBox(s(1))
The code will compile, but you will encounter an InvalidCastException at run time when trying to assign s the return
value of rs.RetStringArray. The problem occurs because you are attempting to assign a nonzero-based string array to a
zero-based string array variable. To fix this, you need to change the declaration of s from a strongly typed String
array—always zero based—to a generic System.Array type, as follows:
Dim s As System.Array
The generic System.Array type can represent a nonzero-based array but cannot be assigned to a strongly typed array
unless it is zero bound.
Type aliasing is declaring a new type based on an existing type. A common example is the OLE_ types defined in the
Standard OLE Automation type library (StdOle2.tlb). The type OLE_COLOR, for example, is an alias for an unsigned 32-
bit long integer. When you reference a COM object in .NET, type aliases are lost. Instead you need to use the base type.
For example, you need to use the .NET System.UInt32 type instead of OLE_COLOR.
Neither Visual Basic 6 nor Visual Basic .NET allows you to declare a type that is an alias of another type. In the
case of Visual Basic 6, however, you can use aliased types from other type libraries. For example, you can create a
Visual Basic 6 component with a public BackColor property of type OLE_COLOR. If you attempt to use the
component in .NET, the property type will show up as System.UInt32.
If you have a Visual Basic 6 project that references a COM component that exports module methods, the module methods
are not available to be called by a .NET client. For example, the DirectX Visual Basic type library Dx8vb.dll contains a
large number of module methods that can be called from Visual Basic 6. When you upgrade Visual Basic 6 code that calls
module methods, the calls are left in the code, but the code doesnâ ™t compile.
Since module methods delegate to exported functions within a DLL, the trick to solving this problem is finding the DLL-
exported function that the module method calls. To do this, you can dump the list of exported functions for the DLL, but
first you need to find the DLL containing the module method you want to call. The easiest way to find it is to load the
original Visual Basic 6 project in Visual Basic 6 and find the DLL reference within the References list. To view the
References list, choose References from the Project menu. Once you have located the reference within the list—for
example, DirectX 8 For Visual Basic Type Library—note the location of the DLL for that reference. Open a DOS
command window and dump the exported functions contained in the DLL by executing the following command:
DumpBin /Exports DX8VB.DLL > Exports.Txt
Launch Notepad and open Exports.txt. Look for a function that matches the name of the module method that your Visual
Basic 6 code calls. For example, if your Visual Basic 6 code calls the DirectX function D3DXColorAdd, you will find the
following entry in the DLL exports list:
        105   15 0002DB8F VB_D3DXColorAdd
You can use this information to declare the API entry point in your Visual Basic code. The most useful information in this
cryptic entry is the entry ID contained in the first column and the name of the function. In this case the entry ID is 105 and
the function name is VB_D3DXColorAdd.
Suppose, for example, that you have upgraded a Visual Basic 6 function that calls the DirectX function D3DXColorAdd
and you end up with the following Visual Basic .NET code:
Dim COut As DxVBLibA.D3DCOLORVALUE
Dim CRed As DxVBLibA.D3DCOLORVALUE
Dim CBlue As DxVBLibA.D3DCOLORVALUE
CRed.r = 255
CBlue.b = 255
'UPGRADE_ISSUE: COM expression not supported: Module methods of
'COM objects. Click for more: 'ms-help://MS.VSCC/commoner/redir/
'redirect.htm?keyword="vbup1060"'
DxVBLibA.D3DXMATH_COLOR.D3DXColorAdd(COut, CRed, CBlue)
This code leads to a compiler error, since D3DXColorAdd, as suggested by the UPGRADE_ISSUE comment, is not
available. Based on the Exports.txt DLL export information obtained earlier, you can declare the function as follows:
Private Declare Function D3DXColorAdd Lib "dx8vb.dll" _
   Alias "#105" (ByRef COut As DxVBLibA.D3DCOLORVALUE, _
   ByRef C1 As DxVBLibA.D3DCOLORVALUE, ByRef c2 As _
   DxVBLibA.D3DCOLORVALUE) As Integer
In this case weâ™re using the entry ID 105 as the name of the function. You could also declare the function to use the
API function name VB_D3DXColorAdd in the Alias clause. To obtain the full declaration for the function, you can load
your original Visual Basic 6 project in Visual Basic 6 and start the Object Browser. With the Object Browser running, you
can search for the module method declaration for D3DXColorAdd. The module method declaration will match the API
declaration you need to create using the Declare statement.
Conclusion
The good news is that ActiveX controls and components are supported in Visual Basic .NET. Although there are some
limitations in the support that is provided, you should not be hindered in upgrading Visual Basic 6 applications that are
good candidates for Visual Basic .NET. For example, if you have a Visual Basic 6 middle-tier component that makes
heavy use of ActiveX-based ADO objects, you can upgrade your component to a Visual Basic .NET class library
component. All of your code that talks to the ActiveX ADO components will continue to work as is. You can then quickly
take advantage of Visual Basic .NET by exposing some of your methods as WebMethods to be called over the Internet,
for example.
If Visual Basic .NET did not allow you to make use of your existing ActiveX components, you would be devoting all of
your energy to finding or creating .NET replacements for everything you are using. You would get bogged down just
trying to get your application working again in the .NET environment. The ability to use ActiveX components frees you to
focus only on those parts of your application that are critical to get working in .NET. This ability enables you to get up
and running in .NET quickly.
Data access is made up of three components: code that manipulates data objects, data binding, and design-time tools such
as the ADO data environment. The users of your applications may regard the run-time behavior as another component of
data access; but we will consider the run-time behavior as an element of each of the three components. Letâ ™s look
quickly at the three components and see where they differ between Visual Basic 6 and Visual Basic .NET.
Code
DAO, RDO, and ADO are implemented as COM libraries, so most code works exactly the same in Visual Basic .NET as
it did in Visual Basic 6. For example, the following ADO code opens the Northwind database and then executes a Select
statement that returns the list of employees. The first name and last name of the employee is then shown in a message box:
Dim cn As New Connection
Dim rs As Recordset
cn.Open "Provider=Microsoft.Jet.OLEDB.3.51;Data Source=c:\temp\nwind.mdb"
Set rs = cn.Execute("Select * From Employees")
MsgBox rs!FirstName & " " & rs!LastName
rs.Close
cn.Close
As weâ™ve seen in other examples elsewhere in this book, after it is upgraded the code looks essentially the same, just
more explicit. It works perfectly in Visual Basic .NET. Likewise, most RDO and DAO code works perfectly after
upgrading, with a few differences that we will discuss later in this chapter.
One major difference across all the data access technologies is how you access fields of a Recordset (or Resultset for
RDO). In Visual Basic 6, it is common to access fields using the shorthand coding convention RecordsetName!
FieldName. In Visual Basic .NET, this code needs to be expanded in order to resolve the default properties. The Upgrade
Wizard does this for you, but the code looks a little different after upgrading. Letâ ™s look at an example:
Dim rs As Recordset
Dim myString As String
myString = rs!Lastname
This code assigns the string myString to the field Lastname of the Recordset rs. It upgrades to the following:
Dim rs As ADODB.Recordset
Dim myString As String
myString = rs.Fields("Lastname").Value
Notice that rs!Lastname was expanded to rs.Fields(“Lastnameâ€).Value. The Upgrade Wizard will handle all of these
cases for you.
Data Binding
Data binding allows you to bind the value of a control on a form to a field in a database. Visual Basic 6 supports data
binding using ADO, DAO, and RDO. You can bind a control to an ADO data environment, an ADO Data control, a DAO
Data control, or an RDO Data control. Visual Basic .NET, however, supports ADO data binding only, not DAO or RDO
data binding. This distinction comes from the way in which the different types of data binding are implemented.
DAO and RDO are both older data access technologies. DAO data binding was introduced in Visual Basic 3, and RDO
data binding debuted in Visual Basic 4. When the Visual Basic development team first implemented these older forms of
data binding, they built them into the forms package. This implementation allowed a seamless integration, but it also tied
the data binding technology to Visual Basic Forms. In Visual Basic .NET, Visual Basic Forms has been replaced with
Windows Forms—a redesigned forms package. The designers of Windows Forms decided not to build DAO and RDO
data binding into Windows Forms, and therefore DAO and RDO data binding are not supported. ADO data binding is
supported because it is not built into the forms package; instead it is implemented in the COM library MSBind.dll. This
library manages ADO data binding in Visual Basic 6, and the updated library, called
Microsoft.VisualBasic.Compatibility.Data, manages data binding in Visual Basic .NET. The Upgrade Wizard upgrades
data binding to both the ADO Data control and the ADO data environment.
The Visual Basic 6 ADO data environment lets you visually design database connections and database commands using
the ADO Data Environment Designer. Although Visual Basic .NET does not support the ADO data environment, the
Upgrade Wizard upgrades the connections, commands, and Recordsets to a class that has the same run-time behavior as
the Visual Basic 6 data environment.
The components that canâ™t automatically be upgraded to Visual Basic .NET cause errors in the upgraded project. For
example, if a Visual Basic 6 form has a DAO Data control, the control is removed during upgrade and EWIs are inserted
into the upgrade report. Any code that references the control will cause a compile error.
What should you do if your project uses RDO or DAO data binding? The only solution is to reimplement the data binding
in Visual Basic .NET, using ADO or ADO.NET data binding. The best choice is generally ADO.NET. As we discuss in
the next section, the design-time experience for ADO.NET is better than for ADO.
Figure 14-1 gives an overview of the different data access technologies in Visual Basic 6 and shows how they upgrade to
Visual Basic .NET. Technologies on the left side without an arrow do not automatically upgrade to Visual Basic .NET.
Figure 14-1
How data access technologies upgrade.
ADO.NET Is the Future
The future of data binding and data access is with ADO.NET, not with DAO, RDO, or ADO. In Visual Basic .NET
Windows Forms and Web Forms, ADO.NET offers a rich editing experience, with designers for connections, schemas,
and data binding. Unfortunately, the ADO editing experience is not as rich as that of either ADO.NET in Visual
Basic .NET or ADO in Visual Basic 6. Although ADO data binding is useful for maintaining existing applications, we
advise you to use ADO.NET for new applications that use data access, especially those that use data binding.
If ADO.NET is the future, you may ask, why doesnâ™t the Upgrade Wizard upgrade DAO, RDO, and ADO to
ADO.NET? Unfortunately, there is no one-to-one mapping to ADO.NET. ADO.NET is a disconnected architecture, and it
is distinct enough that automatic upgrading to it is not possible. For example, the Recordset object in ADO has the
concept of a current row; this is a cursor that points to the current row as you step through a Recordset using the
MoveFirst, MoveNext, MoveLast, and MovePrevious methods. The closest equivalent to a Recordset in ADO.NET is a
DataTable (or DataSet, a collection of DataTables). A DataTable does not have a current row, and there is no equivalent
to the Move method of a Recordset. For this reason, an automatic upgrade from DAO, RDO, and ADO is not possible.
For an introduction to working with ADO.NET, see Chapter 20.
This document is created from a CHM file automatically by an unregistered copy of
CHM-2-Word.
The content of this chapter is skipped, please register CHM-2-Word to get full
features.
For registration information, please refer to: http://www.macrobject.com
Visual Basic 6 introduced the ADO data environment, which provides a visual way to add and edit data objects. With the
ADO Data Environment Designer, you can add connections, commands, and child commands. At run time, the ADO data
environment acts like a class, with collections of connections, commands, and Recordsets.
Because Visual Basic .NET does not support the ADO data environment, the Upgrade Wizard upgrades data
environments to a class that has the same run-time behavior. Since itâ ™s a class, not an ADO data environment, the
editing experience is less rich than in Visual Basic 6. The programming model for the upgraded class is almost identical to
Visual Basic 6. For example, suppose your project has a data environment named myDataEnvironment, with a command
called myCommand. The following code would assign a Recordset to the result of the command:
Dim rs As Recordset
Set rs = myDataEnvironment.rsmyCommand
Dim rs As ADODB.Recordset
rs = myDataEnvironment.rsmyCommand
With a few exceptions that we will discuss now, the upgraded data environment behaves like the original Visual Basic 6
version.
Calling Dispose
When your application shuts down, itâ™s important to dispose of the upgraded ADO data environment. This closes all
Recordsets, commands, and connections. To dispose of the ADO data environment, call the Dispose method. For
example, suppose your application has an ADO data environment named DataEnvironment1. You would dispose of it
with the following line of code:
DataEnvironment1.Dispose
You should put this line somewhere in your application shutdown code.
Initialize Event
In Visual Basic 6, the ADO data environment supports an Initialize event and a Finalize event. When your application is
upgraded, the code in these events is upgraded, but as procedures. The events wonâ ™t be fired when the application is
run. You should move the code in the Initialize procedure into the New event and the code in the Finalize procedure into
the Dispose event.
In Visual Basic 6, you could set the cursor location of an ADO connection object to adUseServer or adUseClient,
signifying a server-side or client-side cursor location, respectively. If the database is a Microsoft Access database, the
property is ignored and a client-side cursor is always used. In Visual Basic .NET, trying to use a server-side cursor with a
Microsoft Access database causes a run-time exception, since the value is invalid. This situation may cause problems in
upgraded applications if you have inadvertently set the cursor to be server-side. The solution is to remove the line of code
in the Visual Basic .NET project that sets the cursor location. For example,
Me.Adodc1.CursorLocation = ADODB.CursorLocationEnum.adUseServer
This rule applies to the ADO data environment, the ADO Data control, and connections created in code.
With ADO data binding, you can bind controls on forms to fields in a database. In Visual Basic 6, you can bind a control
to one of three ADO objects:
The Upgrade Wizard can upgrade the first two data binding technologies, but it cannot upgrade data binding to ADO data
source classes. Before we look at how data binding is upgraded, letâ ™s take a minute to review how it works in Visual
Basic 6.
Each Visual Basic 6 control supports either complex or simple binding. Complex binding occurs when a control binds to
an entire Recordset. The control manages its own data binding. The Microsoft DataGrid ActiveX control is a good
example of complex binding. Complex binding is the simplest to upgrade, since itâ ™s only a matter of setting the
controlâ™s DataSource to a Recordset and then letting the control manage its own binding. The Upgrade Wizard
manages this without problems.
Simple binding is more involved. Simple binding occurs when a control binds its value to the current record in a
Recordset. Binding a TextBox Text property to an ADO Data control is a good example of simple binding. Simple
binding in Visual Basic 6 is controlled by four properties of the control. Table 14-1 lists these properties. In Visual Basic
6, these four properties are “magic†properties. When you set them to valid entries, Visual Basic 6 adds an entry to an
MSBind binding collection. If you change the properties at run time, Visual Basic 6 removes the old entry from the
binding collection and adds a new entry.
Property Description
DataSource The name of the ADO Data control or ADO data environment to bind to
DataMemberFor ADO data environments only, the name of the command to bind to
Windows Forms controls do not have these properties, so the Upgrade Wizard adds code to the form to manage the
binding collection. For example, suppose that a form has a TextBox that is bound to a field of an ADO Data control.
When it upgrades the form, the Upgrade Wizard does the following:
Inserts a form variable that manages the control binding. The variable is declared in the formâ ™s declarations
section. If the ADO Data control is called Adodc1, the binding variable is named ADOBind_Adodc1.
Inserts a method that sets up the binding collection. This method is named VB6_AddADODataBinding and is
called from the Form.New event.
Inserts a method that removes the bindings and cleans up the objects when the form closes. This method is
named VB6_RemoveADODataBinding and is called from the Form.Dispose event.
There are some data binding elements that the upgrade canâ ™t handle automatically. Letâ ™s look at them now.
Control arrays of ADO Data controls cannot automatically be upgraded from Visual Basic 6 to Visual Basic .NET. If you
have a form with a control array of ADO Data controls, the control arrays will lose their settings during the upgrade, and
the controls will not be bound. The best solution is to remove the controls from the control array in Visual Basic 6 before
upgrading.
Many projects that use data binding have code that adjusts data binding properties at run time. There are several reasons
for this: you may need to change the database location, depending on whether the application is being run against a
development database or a production database; or you may need to dynamically change the field a control is bound to.
Code that changes data binding properties at run time may need some modifications after upgrading. The reason for
requiring these modifications is that the underlying data binding mechanism has changed—you no longer use the
DataSource and DataField properties to set up data binding. Instead, you add and remove binding entries of a data binding
variable.
Letâ™s walk through an example to see how to upgrade code that adjusts binding properties at run time. On the
companion CD, you will find a project called prjADORuntime. This Visual Basic 6 project has a TextBox bound to an
ADO Data control. The binding is changed at run time to point to the Northwind database. In the Form_Load event, the
code changes the connection string for the ADO Data control and then changes the data binding for the TextBox. When it
is run, the form looks like Figure 14-6.
Figure
14-6
Setting up data binding dynamically at run time.
Here is the relevant code in Form_Load that changes the data binding:
Changing the properties of the Data control is fine, but the code that sets up the binding for a particular control also needs
to be modified. This code should be changed from
to the following:
ADOBind_dcEmployees.Remove("txtLastname")
ADOBind_dcEmployees.Add(txtLastname, "Text", "Lastname", _
Nothing, "txtLastname")
The ADOBind_dcEmployees variable is the global binding variable. This code removes the entry for the TextBox
txtLastname and then adds a new entry to bind txtLastname to the Lastname field. Once this modification has been made,
the code works in Visual Basic .NET.
If you want, you can go one step further and adjust the architecture of the form so that binding is set up only after the
ADO Data control properties have been set at run time. If you look inside the Form_New event, you will find a line that
reads
VB6_AddADODataBinding()
Remove this line of code and add it to the frmMain_Load event, so that the Load event looks like the following:
What we have done here is set up the correct connection string for the ADO Data control before adding the binding. The
Visual Basic .NET data binding code may be more verbose than that in Visual Basic 6, but it also offers more control over
how and when the data binding is set up.
If you are adjusting the data binding at run time, it is important to set some binding at design time first. The Upgrade
Wizard adds the binding variables and procedures only if it detects that a form has data binding. If the binding is set only
at run time, the Upgrade Wizard will not create the binding variable and procedures.
Chapter 15
Problems That Require Redesign
As you get used to upgrading applications and become more familiar with the modifications needed to get them working
in Microsoft Visual Basic .NET, you start noticing that some problems can be remedied with a one-line fix, whereas
others require you to recode the problem area or even redesign it. This chapter looks at the most common of the redesign
problems and shows you how to get each working in Visual Basic .NET.
If you give 50 different programmers the same problem to solve, youâ ™ll get 50 different solutions. We all do things
differently, and any given programming problem can be solved in many different ways. For each of the problems in this
chapter, we provide a sample solution that you can use as is in your own code. However, the Microsoft .NET Framework
often provides several different mechanisms that have the same effect. You may find that another mechanism is more
suitable for the particular problem youâ™re trying to solve. Where possible, we try to make you aware of these
mechanisms and direct you to sources where you can learn more about them.
The OLE Container control was first introduced in Visual Basic 3 and is used to dynamically add objects to a form at run
time. In Visual Basic 6, the OLE Container control is commonly used in three ways.
To create a control that contains an embedded object, such as a Microsoft Word document. When your
application is compiled, its state (the contents of the object) is compiled into your program. If the user changes
the object at run time, you can programmatically save the updated state to a file using the SaveToFile method
and subsequently reload it using the ReadFromFile method.
To create a control that contains a linked object, such as a Word document. The difference between a linked
object and an embedded object is that a linked object is linked to a file on disk instead of being compiled into
the application. In the case of a linked document, any changes you make are immediately reflected in the
document file, which can subsequently be loaded by Word.
To bind to an OLE object in a databaseâ”typically to a picture. The OLE Container control can be data-bound
to a field in a database using the DAO Data control or the RDO Remote Data control. The most common usage
is to display a picture.
Visual Basic .NET does not support the OLE Container control. What happens to the control during an upgrade? Letâ ™s
look at an example. Figure 15-1 shows a Visual Basic 6 form at design time, with an OLE Container control that has a
link to a Word document stating, âœVB Rocks!!â
Figure 15-1
OLE Container control with linked object in Visual Basic 6.
When we upgrade the project, the OLE control is replaced with a bright red label, indicating that the control could not be
upgraded. Figure 15-2 shows the upgraded form. If you look at the upgrade report, you will find an entry saying that the
control was not upgraded, and any code that manipulates the control will be marked with an upgrade warning similar to
the following:
Letâ™s work on replacing the OLE control and getting our upgraded application to show the Word document as it did in
Visual Basic 6. In many cases, we can use a WebBrowser ActiveX control to produce the same effect as a linked object
within an OLE Container control. The WebBrowser resembles Microsoft Internet Explorer and can display documents,
spreadsheets, and HTML pages using the Navigate method. It also allows you to edit these objects and save them to their
original files. The WebBrowser is a lot like the linking part of OLE (which, youâ ™ll recall, is short for â œobject
linking and embeddingâ). Using the WebBrowser control, we can open the Word document and display it on the form,
as shown in Figure 15-3.
Figure 15-3
Word document displayed using the WebBrowser control.
Here are the steps required to replace an OLE control with the WebBrowser control:
3. In Form_Load (or wherever you want to bind to the document), add the following code:
Me.AxWebBrowser1.Navigate("C:\SampleCode\LinkedDoc.doc")
You may want to substitute the filename in this example for the location of the object to which you are linking.
4. Press F5, and that should do the trick—the document should appear in the form. This method works for most
objects that Internet Explorer can display, such as HTML files, documents, spreadsheets, GIFs, and JPEG files.
The other common use for the OLE Container control is to bind a control to an image in a database. In Visual Basic 6, this
is done using the DAO and RDO Data controls. When such a project is upgraded to Visual Basic .NET, youâ ™ll see the
red control mentioned previously, since neither the OLE Container control, the DAO Data control, nor the RDO Data
control is supported in Visual Basic .NET.
Letâ™s look at how you can achieve the same effect using ADO and a helper function in your Visual Basic .NET
program. The following steps show how to bind a PictureBox to the Photo field of the Employees table in the Northwind
database. It assumes that the NWind.mdb database is located in the root C:\ directory. If it is located somewhere else, you
will need to change the file location in the following source. Here are the steps:
Thatâ™s it! Press F5 and notice that the PictureBox retrieves the photo from the database, as shown in Figure 15-4.
Figure 15-4
Binding a PictureBox to an OLE image in a database.
Letâ™s look at an example. Suppose you have a Visual Basic 6 form with two PictureBox controls on it,
sourcePictureBox and destinationPictureBox. The following code sets and retrieves the text “VB Rocks†and then
copies a picture from one PictureBox to the Clipboard and into a second PictureBox:
When this code is upgraded, the Clipboard code is left as is and is marked with upgrade warnings:
The upgraded code causes compile errors in Visual Basic .NET. To get the same functionality, we have to delete the
upgraded code and replace it with code that uses the System.Windows.Forms.Clipboard objects:
The Visual Basic .NET code improves upon the Visual Basic 6 code by checking for the existence of a compatible
Clipboard format before setting the value of the string and the PictureBox image.
To add and remove controls at run time in Visual Basic 6, you can use the controls collection. For example, the following
code adds a TextBox dynamically at run time:
Figure 15-7
Adding a TextBox to a Visual Basic 6 application dynamically at run time.
It is also easy in Visual Basic 6 to remove a control dynamically. The following line of code removes the TextBox we just
added:
Me.Controls.Remove "myControl"
One of the annoying things about the Visual Basic 6 model is that the controls collection has no IntelliSense, so you have
to remember the methods, parameters, and ProgID of the control to add. There is no automatic upgrade for the controls
collection, but it is easy to add and remove intrinsic controls in Visual Basic .NET. Here is the code for adding a TextBox
to a form:
Dim c As Control
c = New TextBox()
c.Name = "myControl"
Me.Controls.Add(c)
In Windows Forms, controls are indexed by number, not by name. To remove a control by name, you have to iterate
through the controls collection, find the control, and remove it. The following code shows how to remove the newly added
control by name:
Dim c As Control
For Each c In Me.Controls
If c.Name = "myControl" Then
Me.Controls.Remove(c)
Exit For
End If
Next
Adding ActiveX controls dynamically at run time is a bit more work. As we discussed in Chapter 13, Visual Basic .NET
creates wrappers for ActiveX controls. These wrappers must exist before a control can be added. In addition, most
ActiveX controls have design-time licenses that must be present before the control can be created on the form. Visual
Basic .NET compiles the license into the executable. These factors mean that the control must already be present in the
project before it can be added dynamically at run time. One way to do this is to add a dummy form to the project and put
all ActiveX controls that will be added dynamically onto this form. After youâ ™ve created this dummy form, you can
add the ActiveX control dynamically to any form in your project. The following code shows how to add a Windows
Common Controls TreeView control dynamically to a form (the project already has a dummy form with a TreeView
added):
The control can be removed dynamically in a manner similar to removing an intrinsic control:
Dim c As Control
For Each c In Me.Controls
If c.Name = "myTreeView" Then
Me.Controls.Remove(c)
Exit For
End If
Next
Visual Basic 6 offers a simple way to print forms using the Form.PrintForm method. PrintForm is not perfect: some
controls donâ™t print well, and the quality is not always picture-perfect. But for many people, itâ ™s good enough.
Unfortunately, Visual Basic .NET has no direct equivalent of PrintForm, and thus the Upgrade Wizard canâ™t upgrade
PrintForm code. Donâ™t worry, though; weâ™ve found a replacement for you, using a PrintForm helper class. (For the
source code to this class, see the PrintForm application on the companion CD.) Here are the steps to implement PrintForm
behavior in Windows Forms:
3. Add a Button to the default form, and in the Button_Click event insert the following code:
5. Run the application, and click the button. Bingo! Your form will be sent to the printer.
You should be aware of one limitation: the form to be printed must be the topmost form and must be entirely visible, since
the class takes a “snapshot†of the form and prints it. In many cases this technique will be good enough, especially
when you simply want to print the visible layout of the current form. As a bonus, the helper class also contains another
function, getImageFromForm. This method copies the image of the form to an image object. You can use this to, say,
copy the image from one form to another. Letâ™s see how this works by extending the previous example:
8. Run the application, and click Button2. The application paints the image of Form1 onto Form2, as shown in
Figure 15-8. The controls painted onto Form2 wonâ™t work, since weâ™ve simply painted the form image.
Nevertheless, itâ™s a convincing illusion.
Figure 15-8
The printing model in the .NET Framework is quite different from that in Visual Basic 6. If youâ ™re looking for the
printing functions, youâ™ll find them in the System.Drawing.Printing namespace. For help with printing
programmatically from your application, search for the topic â œPrinting with the PrintDocument Componentâ in the
Visual Basic .NET Help system.
Suppose youâ™re writing an image list user control, and you want to give users a way to add a collection of images.
How do you do it? In Visual Basic 6, you would add a property page to your control. In the property page, you would
write code that manages the collection. Property pages are useful because they allow you to work around the limitations of
the Visual Basic 6 Property Browser, which can edit only strings, enums, numbers, colors, pictures, and fonts. Editing a
collection of bitmaps is out of the question for the poor old Visual Basic 6 Property Browser. Youâ ™ll be pleased to
know that Visual Basic .NET has a Property Browser that can edit any .NET variable type or classâ ”property pages are
no longer needed.
Property pages in user controls are not upgraded automatically. To enable your control to edit the values that used to be
exposed in the controlâ™s property pages, you need to add properties to your user control. The good news is that the
Visual Basic .NET Property Browser automatically recognizes the types of your controlâ ™s properties and allows users
to edit them in the Property Browser. In the case of a bitmap collection, you simply add the property to your user control,
and the Visual Basic .NET Property Browser provides a bitmap collection editor. Letâ ™s try this out. Add the following
code to a Windows Forms user control:
If you add the control to a form, you will notice that it has a new property, bitmapCollection, and that you can edit this
property using a collection editor. Figure 15-9 shows this editor. Itâ ™s thatâ ™s simple.
Figure 15-9
Adding a bitmap collection to your user control.
There are property page editors for every .NET class. For example, if you add a targetDate property of type Date to your
user control, the Property Browser automatically provides a date editor. Try putting this property code into your user
control:
The Property Browser automatically uses the date editor for the property, as shown in Figure 15-10.
Figure 15-10
Date editor provided by the Property Browser.
The advantage of showing properties in the Property Browser instead of in property pages is that in the Property Browser
all of the properties for your control are visible and easily discoverable. Compare this with Visual Basic 6 property pages,
which hide functionality from people who use your controls.
The Windows Forms Property Browser can edit any .NET class or variable type. But what happens if you write a new
class, say a Customer class, and you want users to be able to edit it in the Property Browser? Windows Forms gives you a
way to provide this option by allowing you to write your own custom property editor that will be used whenever people
edit properties of type Customer. To learn more about writing custom property editors, search for the following two topics
in the Visual Basic .NET Help system: “Attributes and Design-Time Support†and “Implementing a UI Type
Editor.â€
Conclusion
This chapter has looked at eight problems that require some redesign work when moving from Visual Basic 6 to Visual
Basic .NET, and it has shown you how to make the necessary changes to your code. In all of the cases, the redesign is
straightforward—you just have to know how to do it. The next chapter looks at more situations that require redesign, all
related to upgrading MTS and COM+ services applications.
Chapter 16
Upgrading COM+ Components
COM+ services and Microsoft Transaction Server components are the backbone of most major Microsoft Visual Basic
business applications. This chapter starts with an introduction to implementing COM+
services in Microsoft .NET and then moves on to describe how to upgrade basic COM+
components. Although the emphasis is on transactional objects and object construction, the
information here applies to the upgrading of all kinds of COM+ components. From this
chapter you should take away a basic understanding of how COM+ components are
implemented in Visual Basic .NET, and you should have an idea of the effort required to
upgrade your existing components.
This chapter is definitely not for someone who is not familiar with COM+. It makes no attempt to introduce the
concepts behind the code. For that, a wealth of resources is available through Microsoft Press publications and the
MSDN documentation, both online and included with Visual Basic .NET.
To frame the discussion, letâ™s start by taking a look at COM+ applications. According to the Microsoft Developer
Network (MSDN), there are three basic types of user-creatable COM+ applications. Each has particular features,
advantages, and restrictions that make it appropriate for specific application architectures. The three types are as follows:
Server application A COM+ application that runs in its own process. Server applications can support all COM+
services.
Library application A COM+ application that runs in the process of the client that creates it. More specifically,
the components in a library application are always loaded into the process of the creator. Library applications
can use role-based security but do not support remote access or queued components.
Application proxy A set of files containing registration information that allows a client to remotely access a
server application. When run on a client computer, an application proxy file writes information about the COM+
server application—including its CLSID, ProgID, RemoteServerName, and marshaling information—to the
client computer. The server application can then be accessed remotely from the client computer.
Server applications differ from library applications in how they are instantiated. A library
applicationâ™s components are created in the same process as the calling application. A server
applicationâ™s components are created out of process. When you attempt to instantiate an object
remotely, it is not possible to create that component in the callerâ ™s process; therefore, only server
applications are capable of instantiation by remote applications, due to their out-of-process nature.
The vast majority of COM+ applications fall into the first two categories, and those will be the focus of the development
content in this chapter. The third, application proxy, is a specialization of the server application and can be treated as a
logical extension of the discussion contained here.
Unfortunately, because a Visual Basic .NET COM+ services application has to be implemented in quite a different
manner than a Visual Basic 6 COM+ services application, the Upgrade Wizard cannot do much for your Visual Basic 6
COM+ components. It upgrades the business logic, database code, and other parts of your application, but it doesnâ ™t
upgrade the transaction attributes. The implementation differences are not a one-to-one mapping, so upgrading the parts
dealing with COM+ support is best left to the developer. This leaves you with a fair bit of work to do on your own. To
better illustrate what is going on here, letâ™s look at a Visual Basic 6 sample called SimpleTransaction, included on the
companion CD. This is an ActiveX DLL project with a single class, SimpleClass, that is marked as
RequiresNewTransaction. The SimpleClass.cls code looks like this:
Implements ObjectControl
Implements IObjectConstruct
conn.Open connStr
conn.Execute sqlCmd
ctxt.SetComplete
Exit Sub
HandleError:
ctxt.SetAbort
End Sub
After running this sample project through the Upgrade Wizard, you have a Visual Basic .NET class that looks like this:
   conn.Open(connStr)
   conn.Execute(sqlCmd)
   ctxt.SetComplete()
Exit Sub
HandleError:
ctxt.SetAbort()
End Sub
End Class
As you can see, the Upgrade Wizard leaves the work of implementing the COM+ parts of the class to you. While this task
is quickly accomplished for this simple example, you should be aware that bigger applications will take time if the classes
have a large set of transactional components. You will need to refer to the Visual Basic 6 version of each class to ensure
that you transfer the correct transactional attributes to Visual Basic .NET. Otherwise, you may see unexpected and
potentially undesirable behavior.
Using the previous example, letâ™s walk through the steps necessary to get the class working:
3. Add the necessary COM+ attributes to the class (Transaction, ConstrutionEnabled, ObjectPooling, and so on).
4. Change the COM+ interface methods Activate, Construct, and Deactivate to be member methods that override
base methods on ServicedÂComponent.
5. Add the AutoComplete attribute to the DoTasks method. Remove the explicit commit and abort code.
After youâ™ve completed all of these steps, the class looks like this:
Imports System
Imports System.EnterpriseServices
<Transaction(TransactionOption.RequiresNew), ConstructionEnabled(),
ObjectPooling()> Class SimpleClass
Inherits ServicedComponent
conn.Open(connStr)
conn.Execute(sqlCmd)
Notice that the DoTasks method does not explicitly commit or abort the transaction except when a logical condition is
true. This helps to demonstrate that you can still explicitly commit or abort your transactions. If you encounter a method
in which the commit and abort logic is too tangled to reasonably remove, you can forgo using the AutoComplete attribute
altogether and commit your transactions manually.
Previous chapters have discussed issues regarding COM interop and how best to handle it in your applications. Although
these discussions were directed at standard COM components, much of the information also applies to using COM+
applications from .NET. It is possible to import server, library, and proxy applications into your Visual Basic .NET
projects. The imported objects are used just as they are in any other COM interop scenario. In addition, if your application
has a ServicedComponent class that calls into the imported COM+ application, the context will propagate transparently
across the interop boundary. You need to take into account some special considerations if you intend for your .NET
serviced components to work with COM clients:
Chapter 17
Upgrading VB Application Wizard Projects
This chapter looks specifically at upgrading projects created with the VB Application Wizard. Because the wizard
generates the same forms and modules for the applications it creates, each project will have the same problems that can be
fixed in the same way. Of course, the wizard creates shell projects that are used as a basis for development. Here weâ ™ll
discuss only the issues common to these shell projects generated by the wizard—not ones that may occur with code you
have subsequently added.
When you create a new project in Microsoft Visual Basic 6, the New Project dialog box gives you a number of choices as
to the type of project to create. The most common choice is Standard EXE, followed by ActiveX DLL. These create
empty projects, to which you have to add menus, toolbars, and the corresponding logic. Since many projects have the
same basic components, Visual Basic 6 provides a VB Application Wizard that generates many of these components for
you. This wizard starts when you select VB Application Wizard as the project type in the New Project dialog box, as
shown in Figure 17-1.
Figure 17-1
Starting the VB Application Wizard from the New Project dialog box.
The VB Application Wizard asks you a number of questions about the project you want to create, such as the type of
interface you want the project to have, the menus and submenus you need, whether strings will be stored in a resource file,
and what data access forms the application will have. After you answer these questions, the wizard creates a project that is
prepopulated with commonly used forms and classes. This project then becomes a template for your application. You
create more forms and classes and add the business logic. Table 17-1 lists the project items the wizard creates.
Table 17-1 Project Items Created by the VB Application WizardÂ
frmAbout form frmAbout is the form that opens when users choose About from the Help menu.
frmLogin form frmLogin is a simple username/password login form that opens when the application first starts.
Each project created with the VB Application Wizard has a form called frmMain. This is the primary
frmMain form
form of the application.
frmSplash form frmSplash is a splash screen that shows after the frmLogin form but before frmMain opens.
An application for which you specify Internet connectivity has a frmBrowser form. This form contains a
frmBrowser
WebBrowser ActiveX control.
Data forms Data forms are added to display and edit information from a database.
Each project created with the VB Application Wizard has a module called Module1. This module has a
Module1 module
Sub Main method, the startup object that opens the frmMain form.
frmDocument
Each MDI interface project has a form called frmDocument; this is the MDI child form.
form
frmOptions is a tabbed dialog form for setting application options. Because this form upgrades without
frmOptions form
issues, we wonâ™t discuss it any further in this chapter.
Your project created with the VB Application Wizard may have all or some of the project items listed in Table 17-1,
depending on the options you selected in the wizard. Each of these project items has its own unique set of upgrade issues.
Letâ™s look at these items and see what you have to do to get them working in Visual Basic .NET.
App.Revision
Although App.Revision isnâ™t actually a project item, the VB Application Wizard projects use it in several places, so
weâ™ll discuss it right at the beginning. In Visual Basic 6, it is common to concatenate the three App object
propertiesâ”App.Major, App.Minor, and App.Revisionâ”together to create a version number for the application. For
example, in frmAbout, you will find the following line of code in Form_Load:
lblVersion.Caption = "Version " & App.Major & "." & App.Minor & _
"." & App.Revision
Visual Basic .NET does not have an App object, so the Upgrade Wizard chooses the most appropriate upgrade for each
property. App.Major is upgraded to
System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
   ).FileMajorPart
App.Minor is upgraded to
System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
   ).FileMinorPart
However, there is no equivalent for App.Revision, so the Upgrade Wizard leaves it as is, which creates a compile error in
Visual Basic .NET. For example, the Visual Basic 6 line
lblVersion.Caption = "Version " & App.Major & "." & App.Minor & _
"." & App.Revision
upgrades to
There are two ways to fix the compile error. The first and easiest is simply to remove the App.Revision part of the line:
After this change, only the major and minor parts of the version will be shown. Here is what the code just shown looks
like after the App.Revision part is removed:
While this is a good option, Visual Basic .NET also has a property that returns the entire version number of the
application in 0.0.0.0 format. This property is the best way to create a version number in Visual Basic .NET, since it gives
the full version information. Here is the replacement code:
Youâ™ll also notice that this line is shorter than the Visual Basic 6 version. You can use this line as a replacement for
any code that concatenates together App.Major, App.Minor, and App.Revision to generate a version number.
Visual Basic 6 has three versioning properties: App.Major, App.Minor, and App.Revision. You can choose to make
App.Revision auto-increment so that the revision number increases each time you compile the project. Although this is a
simple model, it is out of step with Windows versioning, which supports four versioning properties: Major, Minor,
Revision, and Build. When a Visual Basic 6 project is compiled, App.Revision is mapped to the Windows Build property
and the Windows Revision property is left blank. Visual Basic .NET supports the four Windows versioning properties.
The version number is set using the AssemblyVersion attribute in the AssemblyInfo.vb file. By default, the revision and
build are set to be auto-incrementing based on the time of compilation—when combined they are a timestamp giving the
date and time of compilation.
frmLogin Form
The frmLogin form is added to your project when you choose the option to add a login dialog box to the application. This
form works perfectly after upgrading.
frmMain Form
The applicationâ™s primary form is frmMain. For multiple document interface (MDI) applications, it is an MDIForm;
for single document interface (SDI) and Explorer applications, it is a standard form. Most of the modifications you have to
complete for upgraded VB Application Wizard projects involve this form.
All projects generated by the VB Application Wizard contain an API Declare statement for OSWinHelp. MDI
applications also contain a Declare statement for the SendMessage API. Here are the two Declare statements in Visual
Basic 6:
After upgrading, both of these Declare statements cause compile errors, since each of them passes variables as As Any.
Here are the upgraded Declare statements:
The SendMessage API is not used in VB Application Wizard projects, so you can simply remove the declaration for
SendMessage. The As Any parameter for OSWinHelp should be changed to String, since it is used to pass the name of the
Help file. After removing the SendMessage API and changing the OSWinHelp API, the declare statement for the API
looks like this:
Although not discussed here, the correct Visual Basic .NET declaration for SendMessage is:
If you havenâ™t added an About box to the application, the event for the About menu item, mnuHelpAbout_Click, has
code that opens a message box with the application version:
MsgBox "Version " & App.Major & "." & App.Minor & "." & App.Revision
After upgrading, this code causes an error because it uses App.Revision to generate the application version. It upgrades to
As we discussed in the section on App.Revision, you should replace this line with the following:
App.HelpFile is used in several places in the frmMain form. Windows Forms has a new system for displaying Help that is
not compatible with Visual Basic 6. Because of this incompatibility, the App.HelpFile property is not upgraded and causes
a compile error after upgrading. If your application uses context-sensitive Help, you will have to reimplement it in Visual
Basic .NET. If your application simply shows Help contents and a Help index, you can modify your application to show
them. Simply replace the instances of App.HelpFile with the name of the Help file.
is upgraded to
App.HelpFile generates a compile error. Suppose that your applicationâ ™s Help file is called C:\MyProject\
MyProject.hlp. You would modify the line to read as follows:
If your application doesnâ™t contain a Help file, you can replace all instances of App.HelpFile with an empty string. For
example, you would modify the line just shown to the following:
An easy way to make this change is to use the Find And Replace dialog box to replace all instances of
“App.HelpFile†with “â€, the empty string.
 Â
Implementing context-sensitive help in Visual Basic .NET applications is outside the scope of this book. If you want
to learn more, search for the following topics in the Visual Basic .NET help: “Help and User Assistance in
Windows Applications†and “Introduction to the Windows Forms HelpProvider Control.â€
ActiveMdiChild in MDI Projects
In MDI applications, code in event procedures in the frmMain form, such as tbToolBar_ButtonClick, often uses soft
binding to access controls on the active MDI child form. Visual Basic .NET detects and generates compile errors for
controls that are accessed in this way.
Before discussing the solution, letâ™s look a little closer at the reason that soft binding is used. When a user clicks a
toolbar button or chooses a menu command from the MDI parent form, the code often acts upon the currently selected
MDI child form. If an MDI application has many child forms, the active form is determined only at run time; therefore, it
is common to use the ActiveForm object to access controls on the current form. For example, the following extract from
the tbToolBar_ButtonClick event procedure shows how to change the text alignment of the selected text inside a control
called rtfText on the active form:
For MDI applications, the Visual Basic .NET equivalent of Visual Basic 6â ™s ActiveForm is ActiveMdiForm. The code
just shown upgrades to the following:
Notice that the Upgrade Wizard has inserted an EWI because it cannot resolve rtfText. Because the active form is defined
dynamically at run time, the wizard has no way of knowing what control rtfText actually refers to. Visual Basic .NET has
stronger type checking than Visual Basic 6, so it generates a compile error for this line of code. All the compiler knows is
that ActiveMdiChild is a Form object, and rtfText is not a valid control or property of a standard Form object. To fix this
error, you should either strongly type the form or force the code to use late binding. When possible, you should strongly
type the form because doing so gives you IntelliSense and type checking at compile time. Here is how to modify the code
to use strong type checking.
Notice that since our property is of type frmDocument (the MDI child form), code can access properties, methods, and
controls of the MDI child form. The compiler knows that rtfText is a control of frmDocument, so this code compiles and
runs.
You will have to make this change for every procedure that uses controls of ActiveMdiChild. The easiest way to do this is
to use the Find And Replace dialog box to replace all instances of â œActiveMdiChildâ with
âœGetActiveMdiChildâ. When youâ™re doing the find and replace, be careful not to rename the newly added
GetActiveMdiChild property.
What about MDI applications that have several different form types? What if the active child form could be one of a
number of different forms? In most cases, the different child forms wonâ ™t all have controls with the same name, but
you can still fix the code to work. Defining GetActiveMdiChild as type Object will force the code to use late binding, and
the control will be resolved only at run time. To do this, change the property to the following:
Where possible, however, you should avoid late binding and instead use the strongly typed version of the property. When
the property is strongly typed, you get IntelliSense and type checking, and the code executes quicker.
In the Form_Unload event of SDI and Explorer applications, the VB Application Wizard generates code that unloads each
open form:
This code upgrades to the following. Not shown here is that Form_Unload is renamed frmMain_Closed during the
upgrade.
As we discussed in Chapter 15, the forms collection cannot be upgraded automatically. Fixing this problem is simple; you
can remove this entire section of code, since Visual Basic .NET applications automatically unload all their child forms
when the main form closes.
MDI applications have three event procedures for the Clipboard object: Cut, Copy, and Paste. They are created as follows:
To make looking at the upgraded Clipboard code simpler, letâ ™s focus on the two lines of Clipboard code that set and
get the Clipboard text, respectively:
Clipboard.SetText ActiveForm.rtfText.SelRTF
ActiveForm.rtfText.SelRTF = Clipboard.GetText
After the upgrade, these two lines cause errors in Visual Basic .NET, since the Clipboard object cannot be upgraded
automatically (see Chapter 15).
You can fix the problem by adding two helper methods to Module1. These helper methods get and set the Clipboard text:
Function ClipboardGetText()
Try
Dim sClipText As String
Dim stringData As IDataObject
Dim getString As New DataObject(DataFormats.StringFormat)
stringData = Clipboard.GetDataObject()
If stringData.GetDataPresent(DataFormats.Text) Then
sClipText = stringData.GetData(DataFormats.Text, True)
Return sClipText
End If
Catch ex As Exception
MsgBox("Exception getting clipboard text: " & ex.Message)
End Try
End Function
Now you just need to modify the set and get code to call these functions:
ClipboardSetText(GetActiveMdiChild.rtfText.SelRTF)
GetActiveMdiChild.rtfText.SelRTF = ClipboardGetText()
Notice that weâ™ve also changed ActiveMdiChild to GetActiveMdiChild, as we recommended earlier.
frmBrowser Form
When creating a project with the VB Application Wizard, if you indicate that you want your users to be able to access the
Internet from your application, the wizard adds the frmBrowser form to the application. Choosing Web Browser from the
View menu then opens the form. The code generated in the mnuViewWebBrowser_Click event in frmMain looks like
this:
If this is an MDI project, you need to adjust the behavior of the frmBrowser form. Open the code window for frmBrowser
and search for the line Me.Show. This statement is in the Sub New method within the “Windows Form Designer
generated code†hidden region. Remove the Me.Show line.
The reason for removing the line is to avoid an event ordering issue. This line causes the Form_Load event to fire before
the mnuViewWebBrowser method has assigned the StartingAddress property of the form. After you remove the Me.Show
line, the frmBrowser form will navigate to the default Web site when it is first opened.
Data Forms
The VB Application Wizard has an option to add data access forms to the project. Each data access form allows users to
add, update, and delete data in a database table. The forms use ADO data binding and can be set up to use binding with
the ADO Data control, code, or a data class.
The Upgrade Wizard can upgrade only projects with ADO Data control data binding. Forms that accomplish data binding
via code will not work in Visual Basic .NET because the controls do not support the DataSource property. Data binding
by using a data class cannot be upgraded for two reasons: the controls do not support the DataSource property, and the
DataSource behavior of classes cannot be upgraded automatically. If your application uses either of these two types of
data binding, you will need to reimplement the data binding, using ADO or ADO.NET.
Forms with ADO Data control data binding are upgraded to Visual Basic .NET. Data binding has its own set of issues,
and these are covered in Chapter 14.
Conclusion
This chapter has focused on upgrading projects created with the VB Application Wizard. Itâ ™s important to note that the
applications generated by this wizard are not complete. They are a starting point from which you continue to add your
own business logic and project items. Weâ™ve examined how to fix problems with the initial project items and code as
they are generated by the wizard. Obviously, there will be other issues that occur as a result of code you add to these base
projects. Youâ™ll find solutions to these general upgrade issues in other chapters of this book.
DynamicApp is a Microsoft Windows application with three forms. The primary form, Dashboard, is a dashboard from
which you can launch the other features of the application: a registry editing form and a snappy graphics form, as seen in
Figure 18-1.
Figure 18-1
Dashboard opens the registry editing form and the snappy graphics form.
What is interesting is the architecture of the application. Each form is in a different application; the dashboard is in the
main EXE, while the graphics form and registry editing form are in two separate DLLs. When the dashboard opens, it
searches its directory for DLLs and then examines each DLL it finds for Windows forms. It dynamically builds a list of
available Windows forms and shows them as buttons on the dashboard. When you click a button on the Dashboard form,
the appropriate DLL is loaded dynamically and the form is shown. These actions are performed using some of the new file
functions in Visual Basic .NET.
In Visual Basic 6, it is awkward to read the contents of a directory: you have to call the Dir$ function repeatedly. This
function returns the name of the first file in the directory, and then the next, and so on. You canâ ™t examine two
directories at the same time because Dir$ tracks only the file it has enumerated for the current directory. In Visual
Basic .NET, getting the list of files in a directory is much simpler. Use the System.IO.Directory.GetFiles method to return
an array of filenames. The following sample code is from the Dashboard.GetSubForms method in the DynamicApp
project:
After this code runs, the filePathArray variable is filled with an array of file paths, such as
C:\DynamicShell\bin\GraphicsFeatures.dll
The System.IO namespace contains many other useful functions as well. For example, System.IO.Path.GetDirectoryName
returns the directory name from a file path. The code
MsgBox(System.IO.Path.GetDirectoryName( _
"C:\DynamicShell\bin\GraphicsFeatures.dll"))
shows C:\DynamicShell in the message box. In a similar vein, the System.IO.Path.GetFileName method returns the
filename of a file path. For example, the code
MsgBox(System.IO.Path.GetFileName( _
"C:\DynamicShell\bin\GraphicsFeatures.dll"))
Once an application has found a DLL, how can it dynamically determine what forms it contains? The enabling technology
is called reflection. Reflection encompasses a set of classes that allow you to examine the contents of a DLL at run time.
Letâ™s see how it works, using a simplified version of the Dashboard.GetSubForms method in DynamicApp. The
following sample shows how to list all of the types (type is the .NET term for class, module, or form) in the current
application. Try it out. Create a new Windows application, and enter the following code in the Form_Load event:
There is only one type in this application: Form1. So when you run this code, Form1 is shown in the message box.
Whatâ™s happening here? Remember that assembly is the .NET term for an EXE or a DLL. We declare a variable of
type Assembly and then use it to load the current application. Next we retrieve all of the types in the assembly into an
array. We then loop through the array and show the name of each type in a message box. Simple, isnâ ™t it?
DynamicApp uses this mechanism to get the list of forms inside every DLL in the bin directory where the application is
located. Instead of showing the form name in a message box, it stores the list of forms in an array and uses that array to
create a set of buttons on the Dashboard form. When the user clicks a button, DynamicApp shows the appropriate form.
Once you know the name of the DLL and the name of the form you want to show, the rest is quite simple. You create an
instance of the form, using the Assembly.CreateInstance method, and then call the Show method on the form. The
following code snippet is a simplified version of the code in the DynamicÂApp Dashboard.Button_Click_Handler
method. It demonstrates how to load a DLL called MyDll.dll, create an instance of a form within the DLL Form1, and
show the form.
Notice with the CreateInstance method that we have to refer to the form as MyDll.Form1, using the
<namespace>.<formname> format. Also be aware that when you write code that loads a DLL dynamically, all names,
including the filename and type name, are case sensitive.
While weâ™re on the subject of the new file functions, letâ ™s take a short digression and quickly discuss how to write
to and read from files using the new .NET Framework methods. You can still use the existing Visual Basic file functions,
but the .NET Framework file methods, although a little more complicated, offer more flexibility when working with files.
Weâ™ll create a simple console application that creates a file, writes â œHello Worldâ to it, closes the file, and then
reopens it and shows the contents in a message box. This sample is on the companion CD, and it is called
FileReadAndWrite.sln. The project contains one module. Here are the contents of that module:
Sub Main()
'* Create a file and write to it
Dim outFile As System.IO.FileStream
outFile = New System.IO.FileStream("C:\tempFile.txt", _
IO.FileMode.Create, IO.FileAccess.Write)
Dim fileWriter As New System.IO.StreamWriter(outFile)
fileWriter.WriteLine("Hello World")
fileWriter.Close()
outFile.Close()
To open a file for writing, you have to perform two steps: create a stream object and then create a StreamWriter object to
write to the stream. You can then write to the file using the StreamWriter objectâ™s Write and WriteLine methods.
Reading from a file involves a similar process: create a stream object, and then create a StreamReader to read from the
stream. To determine whether there is anything to read from the file, use the StreamReader objectâ™s Peek method,
which returns the value of the next byte in the file, or â “1 if there is nothing left to read. The StreamReader objectâ™s
Read, ReadBlock, ReadLine, and ReadToEnd methods are used to read the contents of the file.
Letâ™s take one more digression and look at dynamic properties. With Windows Forms you have the ability to change
the properties of the form at run time, using a configuration file. Figure 18-2 shows the DynamicApp solution open in
Visual Basic .NET. Notice that the solution has a file called App.config, and notice also that in the Property Browser, the
Form.Text property has a small blue icon next to the name. This indicates that the Form.Text property is retrieved at run
time from the App.config file.
Figure 18-2
The App.config file and the Form.Text dynamic property.
If you double-click the App.config file to open it, you will see that it is an XML file and that it has the following line in
the body:
This is the value of the Text property, which is used to set the title bar text of the application. When the project is
compiled, this file is deployed alongside the EXE, with the name DynamicApp.exe.config. If you open this file in
Notepad and change the setting to something like
“Hello World†will show in the title bar the next time the application is run. It is called a dynamic property because
the value is not stored in the compiled application but instead in an editable XML file that the application reads at run
time.
Why is the Text property dynamic? Are all properties dynamic? When you create a form, by default none of the properties
are dynamic. To make a particular property dynamic, you use the (Dynamic Properties) property in the Property Browser;
you use the (Advanced) builder to select the properties that you want to make dynamic, as shown in Figure 18-3.
Figure 18-3
Making properties dynamic.
Dynamic properties are valuable for database connection strings, Internet addresses, directory names, and any other
property that might need to be changed after the application is compiled.
XCopy Deployment
Do upgraded applications have any limitations? Most upgraded applications contain COM controls and ActiveX
references. Most of these controls have to be registered before they can be used. Visual Basic .NET components donâ ™t
have this limitationâ”they are self-describing. The information that would go into the registry for a COM component is
compiled into the .NET application.
It is often said that .NET components support XCopy deployment (referring to the DOS multiple copy XCopy command).
What this means is that you can install any Visual Basic .NET application simply by copying the EXE and any dependent
DLLs from a directory on one machine to a directory on another machine (assuming that the .NET Software Development
Kit [SDK] is already installed on the client machine). No registration, no extra installation steps. Plus, the application is
isolated—the DLLs it installs do not overwrite DLLs used by other applications.
XCopy deployment is extremely useful for client applications that are to be installed on perhaps thousands of client
machines. With Visual Basic 6 applications, it is usually during one of these installations that something goes
wrongâ”your application installs a DLL that overwrites another DLL or another DLLâ ™s registration, which then
breaks another application. These types of DLL conflicts are frustrating for both you, the developer, and your users.
Visual Basic .NET applications donâ™t have this problem because an installation doesnâ ™t overwrite other files
(unless you explicitly decide to overwrite an existing file), and other installations donâ ™t affect your applicationâ ™s
files. However, if your application uses COM components—ActiveX controls or COM libraries—these components
must be installed and registered, just as they must be in Visual Basic 6. This means two things. First, for each Visual Basic
.NET application that contains COM components you must register the COM components, and second, if one of these
COM components is overwritten, your application will stop working. If easy XCopy deployment is important to you,
itâ™s a good idea to remove the COM components from your application and replace them with .NET components. This
step is especially relevant for applications that will be installed to multiple client machines.
How do you replace COM components with .NET components? In the next chapter, youâ ™ll see how you can do this by
replacing common ActiveX controls with Windows Forms controls.
Conclusion
This chapter has looked at several ways you can extend your upgraded applications. These are important techniques
because most of them would be either impossible or very difficult to achieve in Visual Basic 6. This point underscores one
of the important concepts of upgrading: when you move your application to Visual Basic .NET, the real benefit comes
when you add value with some of the great features of Visual Basic .NET and the .NET Framework.
The next three chapters look at more ways to add value to your applications. In addition to learning how to replace
ActiveX controls with Windows Forms controls, youâ™ll see how to integrate your ADO data access code with
ADO.NET data access code, and youâ™ll learn techniques for redesigning distributed applications.
Upgrading from ActiveX controls to Windows Forms controls has certain benefits. As youâ ™ll see in this section, these
benefits include 100 percent Microsoft .NET compatibility, improved versioning, and simpler deployment.
Windows Forms controls have an inherent advantage over ActiveX controls in that they are native to the .NET
environment, and thus they are more tightly integrated into that environment. For example, a Windows Forms control can
support custom property editors—such as a TreeView nodes collection editor—for use with the Property Browser.
Because ActiveX controls are based on COM and are not native to the .NET environment, you may encounter some issues
with them. For example, custom property editors are not available for ActiveX controls. An ActiveX control may depend
on functionality provided by the Visual Basic 6 environment, which does not exist in Windows Forms. Thus, the
capabilities of the ActiveX control may degrade when placed on a Windows form. For example, the ActiveX SSTab
control depends on a specific host interface in order to support child controls. Windows Forms does not support the host
interface SSTab is looking for, so the control cannot accept child controls when it exists on a Windows form.
Improved Versioning
You can think of ActiveX controls and components as being global across all applications. When an ActiveX control is
registered on your machine, all applications in which the control is used will share the control from the same location. If
you download an update for the ActiveX control, all of your applications automatically use the updated control. This
global use of controls can be good and bad. Itâ™s good if the updated control fixes bugs that affect one or more of your
applications, but itâ™s bad if the updated control introduces new bugs or incompatibilities that break your applications.
Because accepting an update for an ActiveX control is an all-or-nothing proposition, the potential for doing more harm
than good is high. The updated control might benefit some applications while breaking others.
Applications that are built with Windows Forms controls do not risk being broken when you download an update for a
control. The reason for this is that Windows Forms controls are local to each application, meaning that each application is
bound to a particular version of the control. If an updated control becomes available on a system, the application does not
automatically pick it up. Instead, it continues to use the version of the control that it was originally built against. If you
want the application to pick up the latest version of the control automatically, you can change the policy for the
application to cause it to do so. You can do this on an application-by-application basis to ensure that applications that will
benefit from the updated control will use it, whereas ones that wonâ ™t benefit or that risk being broken continue to use
the version of the control known to work with the application.
This change means that if you have an application based purely on Windows Forms controls that you have built, tested,
and deployed, you can count on the application to run on a machine without disruption. No future control updates installed
on the machine can affect it. The application will continue to run with the controls that it was tested against.
Simpler Deployment
When you deploy an application based entirely on Windows Forms controls and .NET components, you will find that
fewer files are needed. If your application contains ActiveX controls, more files must be deployed, since an extra DLL is
generated for each ActiveX control. The extra DLL is the COM interop assembly needed to facilitate the communication
between Windows Forms and the ActiveX control.
If you are using Windows Forms controls that are equivalent to the ActiveX controls provided by Visual Basic 6 in your
application—such as RichText, ProgressBar, StatusBar, Toolbar, TreeView, and ListView—the controls are provided as
part of the Windows Forms assembly. No additional DLLs are required when you use these controls. If you are using
controls provided by an outside vendor, you can deploy the control by copying the control assembly (DLL) to your
application directory—the directory where your EXE file lives. No registration of the Windows Forms control is
required.
4. Rename the Windows Forms control to match the name of the ActiveX control.
Letâ™s take a look at a simple example of replacing the ActiveX ProgressBar control with the Windows Forms
ProgressBar control. First you need to start with a Visual Basic 6 application that uses the ProgressBar control.
The companion CD includes a sample Visual Basic 6 application called PBar that serves as the basis for demonstrating
how to manually upgrade an ActiveX control to its Windows Form equivalent control. Specifically, it demonstrates how
to manually upgrade the ActiveX ProgressBar control to the Windows Forms ProgressBar control. The application
consists of a form containing a ProgressBar ActiveX control and a command button. The command button click event
(Command1_Click) contains the following code, which weâ™ll use to demonstrate how to upgrade code associated with
an ActiveX control:
Dim i As Integer
Dim StartTime As Single
StartTime = Timer
Do While Timer - StartTime < 0.01
Loop
ProgressBar1.Value = i
ProgressBar1.Refresh
Next
The sample application also includes the following ProgressBar Click event for the purpose of demonstrating how to
upgrade an event associated with an ActiveX control:
Figure 19-1
Visual Basic 6 ProgressBar application.
Now youâ™ll upgrade the application to Visual Basic .NET. To do this, copy the PBar project from the companion CD
to your hard drive, run Visual Basic .NET and open the project file for the Visual Basic 6 applicationâ ” PBar.vbp. The
Upgrade Wizard appears. Step through each of the wizardâ ™s pages, selecting the default options. The upgraded
application will be loaded in Visual Basic .NET. Test it by choosing Start from the Debug menu and clicking the Show
Progress button. The application should run as it did in Visual Basic 6.
In the next section, we will replace the ProgressBar ActiveX control, following the steps outlined earlier.
Design-time settings are stored in two places: in the upgraded form file and in the original Visual Basic 6 form file. The
upgraded form file contains the extended design-time settings for the control. These are the settingsâ ”common to all
controls on the formâ”that define attributes such as the controlâ ™s name, its size and position on the form, and its tab
order. The Visual Basic 6 form file contains custom property settings for the form. In the case of the ProgressBar, these
settings include the Max and Min property settings.
Letâ™s start by copying the upgraded extended property settings found in the upgraded form file. To see these property
settings, view the code for the Visual Basic .NET form file and expand the â œWindows Form Designer generated
codeâ region. The property settings for the ProgessBar control are located in the InitializeComponent subroutine as
follows:
Select all property settings and copy the code to the Clipboard. Run Notepad and paste the code into it. Notepad will serve
as a temporary holding place for this code. Delete the line containing the OcxState setting. OcxState represents the
internal, saved state of the ActiveX control and cannot be applied to the ProgressBar Windows Forms control. It is easier
to get the OcxState information from the original Visual Basic 6 form file, as we will demonstrate in the next section.
Copy the control-specific property settings
Now letâ™s copy the property settings found in the original Visual Basic 6 form file. Run another instance of Notepad,
and open PBar.frm (copied previously from the companion CD). Look for the following section in the FRM file:
Letâ™s copy the ActiveX control-specific property settings. In this case, the control-specific property settings are
Appearance, Min, and Max. Custom property settings appear in the FRM fileâ™s property settings block for a control,
after the extended property settings. Extended property settings relate to properties that Visual Basic 6 maintains on behalf
of the ActiveX control. The following list contains the extended properties supported by Visual Basic 6. You do not need
to copy these settings when replacing the ActiveX control with a Windows Forms control. The reason is that the
equivalent Visual Basic .NET property settings can be found in the InitializeComponent subroutine of the
upgraded form file. It is easier to copy the upgraded extended property settings found in the
Visual Basic .NET form file than it is to copy the ones found in the original Visual Basic 6
FRM file, as we demonstrated in the previous section.
DataBindings HelpContextIDWhatsThisHelpID
DataField
Copy the following settings to the original instance of Notepad containing the extended property settings. Paste them after
the extended property settings.
Appearance = 1
Min = 1
Max = 200
After youâ™ve copied the settings, Notepad should contain the following:
Modify the settings in Notepad by converting each of the settings youâ ™ve just copied to Visual Basic code in the form
Me.<controlname>.<propertyÂname> = <value>. Your code should appear as follows after modification:
The next step is an easy one. Switch to Design view for the form, and delete the ProgressBar ActiveX control from the
upgraded Visual Basic .NET form, Form1.
Select the Windows Forms ProgressBar control on the Windows Forms tab of the Toolbox. Place the control on Form1.
You donâ™t need to worry about placing the control in exactly the same position as the previous control. The position
and size will be restored later when you copy the code from Notepad.
Rename the Windows Forms Control to Match the Name of the ActiveX Control
Change the Name property of the control to ProgressBar1 to match the name of the original ActiveX control.
All that monkeying around you did in Notepad will now pay off. Copy the property settings you created in Notepad to the
InitializeComponent subroutine in Form1, and replace the existing property settings for ProgressBar that you find. The
InitializeComponent subroutine is located within the hidden block labeled âœWindows Form Designer generated
code.â
Compiler errors will display for any property settings that the compiler doesnâ ™t recognize. For example, a compiler
error will occur on the following three lines of code. It occurs because the property cannot be found in the Windows
Forms ProgressBar control. You will need to either eliminate property settings for properties that are no longer supported
or change the property name to the equivalent property found in the Windows Forms control.
Me.ProgressBar1.Appearance = 1
Me.ProgressBar1.Min = 1
Me.ProgressBar1.Max = 200
Using IntelliSense, you can quickly get a feel for what properties the Windows Forms ProgressBar control does and
doesnâ™t support. To see a list of all properties associated with the ProgressBar control, type the name of the control in
the code window, followed by a period. A drop-down list of the available properties and methods displays. You will find
that the Appearance property is no longer supported, Min maps to Minimize, and Max maps to Maximize. Making the
appropriate changes causes the compiler errors related to design-time settings to disappear. The resulting
InitializeComponent code is as follows:
Me.ProgressBar1.Minimize = 1
Me.ProgressBar1.Maximize = 200
Resolve Errors in the Code
Compiler errors are displayed for any code in which a property cannot be resolved. In the Command1_Click event, you
will see three compiler errors related to the use of the properties and methods Min, Max, and CtlRefresh. In line with the
changes you made to the design-time properties, change Min to Minimum and Max to Maximum. The CtlRefresh method
for the ActiveX ProgressBar control corresponds to the Refresh method for the Windows Forms ProgressBar control. The
ActiveX control property is renamed CtlRefresh in Windows Forms to avoid conflicts with the extended Refresh property
applied to all controls. The Windows Forms ProgressBar control contains a single method called Refresh. Rename
CtlRefresh to Refresh.
In addition to fixing up properties and methods, you need to update your event code. Specifically, when you delete the
ActiveX control, the events for the control become disassociated and are converted to ordinary subroutines. You need to
reassociate the events with the Windows Forms control. In our example, code is written in response to the ProgressBar
Click event. When the ProgressBar ActiveX control is deleted, the following subroutine exists but is not associated with
the Windows Forms ProgressBar control:
The easiest way to reassociate the Click event with the ProgressBar control is to act as though you were writing new code
for the Click event. Select the Click event for the ProgressBar from the Event drop-down list to create the
ProgressBar1_Click event handler subroutine, as follows:
Cut and paste the body of the ProgressBar1_ClickEvent subroutine to the ProgressBar1_Click event subroutine, and then
delete the original ProgressBar1_Click event subroutine, leaving the following code:
Re-creating the event subroutines in this manner causes the correct event parameters to be created in turn. You then need
to resolve any compiler errors that result. For example, you may have code that references a method of an ActiveX
controlâ™s event argument object, but the method may not exist or may have a different name in the event argument
object of the corresponding Windows Forms control.
With these changes in place, all compiler errors should be resolved. Run the application and test it out. It should behave
exactly like the original Visual Basic 6 application.
Conclusion
Although the Upgrade Wizard makes life easier by having your upgraded Visual Basic 6 applications use the same
ActiveX controls, keeping the ActiveX controls means that you do not reap any of the benefits of an application that is
based 100 percent on .NET controls and components. Unfortunately, the Upgrade Wizard does not give you the choice of
upgrading the ActiveX controls in your project to the equivalent Windows Forms controls.
This chapter has demonstrated how you can manually replace ActiveX controls with equivalent Windows Forms controls
after your project has been upgraded. If the ActiveX control you need to replace has a flat object model, it is a relatively
straightforward process to map its properties, methods, and events to those of the equivalent Windows Forms control. If,
on the other hand, you want to replace an ActiveX control that has a rich object model with the equivalent Windows
Forms control, you will need to restructure your code so that it fits the object model defined by the Windows Forms
control.
Chapter 20
Moving from ADO to ADO.NET
Letâ™s face itâ”most Microsoft Visual Basic applications have some sort of data access. If your application uses
ActiveX Data Objects (ADO), you probably want to know what improvements you can make now that you are upgrading
to Visual Basic .NET. The .NET Framework offers a whole new set of controls and services, including ADO.NET.
Naturally, you will want to take advantage of some or all of these new features. From the developerâ ™s standpoint, you
have three options available:
Adapt your usage of ADO to better interoperate with the .NET Framework objects.
Upgrade your ADO code to ADO.NET.
Do both.
This chapter introduces the concepts and class hierarchy of ADO.NET. Then it provides an overview of how to use your
existing ADO code within your .NET application to bind data to controls and support ADO Recordsets with XML Web
services. For the more adventurous among you, the last part of this chapter offers some guidance on how to migrate your
existing ADO code to ADO.NET.
This document is created from a CHM file automatically by an unregistered copy of
CHM-2-Word.
The content of this chapter is skipped, please register CHM-2-Word to get full
features.
For registration information, please refer to: http://www.macrobject.com
Believe it or not, ADO has certain capabilities that enable it to be compatible with .NET applications and the .NET
Framework. It is possible to preserve your existing investment in ADO while still taking advantage of .NET controls and
performing such tasks as data binding and marshaling Recordsets through XML Web services. To this end, the .NET
Framework includes some features that support transforming the old ADO Recordset into something consumable by a
Visual Basic .NET application. This section discusses those features.
Although it is certainly possible to use ADO within your Visual Basic .NET application, your mileage will vary as far as
performance is concerned. This approach may not give the best performance, but it is functional and quick to implement,
and it allows you to reuse your existing business objects. Later you may choose to change to a solution based solely on
ADO.NET. The compatibility features discussed here span the chasm between the two technologies and make it possible
to combine ADO with ADO.NET.
.NET provides a wide range of controls for both Web applications and Microsoft Windows–based form applications.
Many of these controls support data binding with ADO.NET data sources. The question is how to take advantage of your
existing ADO infrastructure and still use the new controls. The answer is simple: all you need to do is convert your
Recordset to either a DataSet or a DataÂTable. Once you have one of these managed data objects, data binding is trivial.
As we mentioned previously, the OleDbDataAdapter has the ability to handle this task. Check out the following example:
In this way it is possible to take any Recordset and convert it to a managed data structure (a DataTable). Once you have
done the conversion, you can bind the DataTable directly to a control, or you can add it to a DataSet and work with the
table just as you would any other DataTable (including creating a DataView for the data, which we will get to a bit later
on).
Figure 20-3
DataGrid before and after data binding.
Using ADO with XML Web Services
XML Web services are the cornerstone of Microsoftâ™s .NET initiative. They offer a simplified mechanism for
implementing distributed, consumable services using SOAP, an XML-based protocol that transmits over HTTP. SOAP
has significant advantages over technologies such as Remote Data Service (RDS) and Distributed Common Object Model
(DCOM). XML Web services will go through Internet firewalls with no problems, and the applications are loosely
coupled, eliminating issues related to component registration or changing GUIDs. These features can lead to a more
robust system that is not as sensitive to changes as a COM-based system. For more information on using XML Web
services, consult Chapter 21.
Suppose youâ™ve decided that XML Web services are cool and you want to replace your middle tier with one. If you
use the middle tier to handle data access and pass Recordsets, however, you have a problem: you cannot pass any COM
object through an XML Web service. COM objects lack the self-descriptive capabilities necessary to support XML
serialization. How can you get the middle tier to work with Visual Basic .NET?
You actually have two options available, as Figure 20-4 illustrates. The one you choose depends entirely on your
applicationâ™s architecture.
Option 1: Leave your existing architecture in place and serialize your Recordset to and from XML. This is a
good idea if you have a lot of ADO manipulation on both the client and the server tiers.
Option 2: Convert your Recordset to a DataTable or DataSet (as appropriate) and pass that back through the
XML Web service. (The marshaling will be handled for you.) This option is good if the client code is used
mainly for presentation or data binding and can readily be replaced with managed code.
Figure 20-4
The standard way to pass managed objects back and forth through XML Web services is by serializing the objects to and
from XML (which is usually done for you). It is also possible to do this with Recordsets, although you have to handle the
serialization yourself—sort of. Check out the following two Visual Basic .NET functions:
By using the built-in ability to serialize an ADODB Recordset to an XML string, and by using the ADODB Stream object,
you can freely pass Recordsets between tiers in your application. In addition you get the added benefits of using XML
Web services, which is a versatile replacement for DCOM, especially when you need to communicate over the Internet
and build loosely coupled distributed applications. Again, if you are interested in this topic, turn to Chapter 21 for details
on how to implement the XML Web service itself.
Building on the previous discussion, letâ™s now see how to adapt your existing code to use the new managed data
objects in place of ADO. On the whole, it is a fairly simple process. Weâ ™ll start by going over generic technology
mappings to give you an idea of where to begin, and then weâ ™ll move on to some implementation details.
In general, the most direct mapping for ADO is to the OleDb namespace under System.Data, although if you expect to be
using Microsoft SQL Server, the SqlClient providers will offer the best performance. An early beta version of the .NET
Framework had an ADO provider namespace. It was decided, however, that this name would be too confusing, since the
only real commonality was the use of the underlying OLEDB database providers. Therefore, the namespace was renamed
to OleDb. This change was meant to emphasize to developers that the OleDb provider is to be used to access the various
OLE DB database providers (as was ADO). Remember, however, that ADO is a COM-based interface to OLE DB data
providers. The OleDb namespace contains managed interfaces to OLE DB data providers and actually lacks certain
features found in ADO (hierarchical Recordsets come to mind).
For the purposes of this discussion, we will limit comparisons to those between ADO and OleDb. Keep in mind, however,
that every example using classes from the OleDb namespace has a direct equivalent in the SqlClient namespace (which is
specific to SQL Server). So if you are using a SQL Server database, moving between the two managed providers is often
as easy as changing the class nameâ™s prefix from OleDb to Sql (that is, OleDbCommand becomes SqlCommand).
Easy, right?
The Connection and Command objects still exist in ADO.NET. There are, however, significant differences between the
ADO and ADO.NET classes. In ADO the Connection object not only represents your connection to a database but also
can be used to execute database commands. In addition, other ADO objects (Command and Recordset) had
ActiveConnection properties that accepted either an existing Connection object or a provider string, which would cause
the object to create a new Connection object. This arrangement led to confusion because it was possible to execute
database commands using all three of these objects, leading to wildly different coding practices from method to method
(let alone from project to project).
ADO.NET is much more straightforward. Connection objects are provider specific (such as OleDbConnection) and are
used only to represent a connection to a database. There is no way to use them to execute a statement against a database.
That is the job of the OleDbCommand class. This class is comparable to the ADO Command class in that it can be used to
execute run-time queries and stored procedures. Setting properties such as CommandText and CommandType should be
instantly familiar to the seasoned ADO developer in this context. There is an important difference, however, in that it is
possible to be explicit about the kind of query being performed. The simple Execute method of the ADO Command object
has been replaced with four separate methods that indicate expectations regarding any given query. The methods specify
what data type you want back, if any:
What this change means is that if you are using the Execute method on your ADO Connection objects to perform queries
you will need to create an OleDbCommand object to do the actual work. This separation of functionality will hopefully
lead to a more consistent coding style across applications. Although it can be painful to make the necessary adjustments in
your code, we consider this an improvement over classic ADO. The following code illustrates what changes are needed to
upgrade a simple ADO example:
Not all Recordsets are created equal. Depending on the settings you use, they can take several different forms, leading to
various consequences for application performance. As we discussed in the previous section, ADO.NET seeks to separate
the different types of functionality into specialized classes, both so that there is never any question as to the kind of data
you are working with (disconnected, forward-only “firehose,†and so on) and so that the classes can be optimized for
a particular kind of data, rather than having to be general-purpose.
Forward-Only Recordsets
The forward-only Recordset is by far the most common type. It is the kind of Recordset that ADO generates by default
(which is probably why it is the most common, performance advantages aside). Often this type of Recordset is referred to
as the firehose Recordset because the data is read in a continuous stream, and once it has been read, itâ ™s gone.
ADO.NET has a direct equivalent to the firehose: the DataReader object. The DataReader is always forward only, and
unlike a Recordset, a DataReader can never be disconnected. Also, it doesnâ™t support random access under any
circumstances. To simplify reading data from streams and to standardize on a common way to read data, the DataReader
shares many interfaces and implementation details with other stream reader objects in the .NET Framework.
There are other implementation differences as well that can cause some initial confusion for developers. When a
DataReader object is first returned, the record pointer starts before the first record in the data stream (instead of starting at
the first record in the stream). A call to the Read method must be made before the first record is loaded, and successive
calls to Read are necessary to continue moving through the data stream until the end is reached (signaled when Read
returns False). Here is a quick comparison of the use of ADO firehose Recordsets and the ADO.NET DataReader (note
the differences in the loop structures):
Set rs = conn.Execute(statement)
Do Until rs.EOF
Dim str As String
Dim i As Integer
For i = 0 To rs.Fields.Count - 1
str = str & rs.Fields(i).Value
Next
Debug.Print str
rs.MoveNext
Loop
End Sub
' Visual Basic .NET DataReader Example
Sub VBNETDataReaderExample(ByVal connStr As String, _
ByVal statement As String)
Dim conn As New OleDbConnection(connStr)
Dim cmd As New OleDbCommand(statement, connStr)
conn.Open()
Dim reader As OleDbDataReader = cmd.ExecuteReader()
While reader.Read()
Dim str As String = "", i As Integer
For i = 0 To reader.FieldCount - 1
str &= reader(i)
Next
Debug.WriteLine(str)
End While
End Sub
Disconnected Recordsets and Dynamic Cursors
A disconnected Recordset is retrieved in much the same way as a forward-only Recordset. So is a Recordset that uses a
dynamic cursor. This is not the case with a DataSet, which requires, at least in the following example, a DataAdapter to
handle the data population. Thus, while ADO had common methods for creating different kinds of Recordsets, ADO.NET
has specific methods for creating different forms of data repositories.
Set rs = conn.Execute(statement)
Set VB6DynamicRSExample = rs
Set rs = Nothing
End Function
' This is a Visual Basic .NET DataSet Example
Function VBNETDataSetExample(ByVal connStr As String, _
ByVal statement As String) As DataSet
Dim adapter As New OleDbDataAdapter(statement, connStr)
Dim ds As New DataSet()
adapter.Fill(ds)
Return ds
End Function
The other major difference between the disconnected Recordsets and DataSets lies in how iteration is handled. The
disconnected or dynamic Recordsets can use any of the Move functions (Move, MoveFirst, MoveLast, MoveNext, and
MovePrevious) or can access rows using an ordinal reference or column name. A DataTable (contained within a DataSet)
can be iterated using either an ordinal reference or For Each, as in the following example.
Dim i As Integer
For i = 0 To ds.Tables(0).Rows.Count - 1
row = ds.Tables(0).Rows(i)
' Do other stuff
Next
Data Binding
Earlier in this chapter, we demonstrated data binding using a Recordset (imported into a DataTable). However, that was
only a small example of what is possible. This section looks at data binding in more detail, covering data binding in
different environments, Web Forms controls vs. Windows Forms controls, and the kinds of objects that are supported for
binding.
The .NET Framework brings a wide variety of controls to the developer. The Windows Forms controls are found in the
System.Windows.Forms namespace, and the vast majority of them support data binding. In general, data binding to a
control is far from challenging. It requires that you use an in-memory data store and can usually accept either a DataTable
or a DataSet.
The mechanism is very simple. Take a look at the following example, using a DataTable:
Notice in this example that instead of passing the entire DataSet to the DataGrid, weâ™ve chosen to bind only a single
table. This is often necessary with controls for which multiple sets of tables donâ ™t make sense. The DataGrid is not
one of these controls, however. It is capable of a multitable display (try it) and likes the DataSet just fine.
Data binding with Web Forms controls requires an additional step. Web Forms controls reside under the System.Web.UI
namespace, and because they are destined to run only within the ASP.NET world, some of their behaviors are, by
necessity, different from those of their System.Windows.Forms counterparts. Data binding is just one example of such a
behavior. The Windows Forms controls examples apply to Web Forms controls, but with a caveat: you need to explicitly
tell the control to bind to the data source you specified, as follows:
' Bind a DataTable from the DataSet to the Web Form DataGrid
DataGrid1.DataSource = ds.Tables("Customers")
DataGrid1.DataBind()
Notice that this example is exactly the same as the Windows Forms DataSet example just given, with one additional line
of code. Calling DataÂBind on the Web Form DataGrid forces the binding to occur—a necessary step, without which the
control would not be bound to the specified data.
Performance is tricky. Tweaking an application to get it running under heavy load is an expensive and time-consuming
task. From the standpoint of data access, however, it is possible to ensure a high level of performance as long as you
follow these two coding conventions:
These two guidelines seem simple, yet their importance cannot be overstated. Connections to databases represent real
physical resources on your machine, and hogging those resources will inevitably result in poor performance. For
performance-sensitive applications, it is absolutely essential that you explicitly close all of your database connections as
soon as you are done with them. This is necessary for two reasons. First, the connection will not automatically be closed
for you as soon as the reference goes out of scope. Garbage collection is an indeterministic process, and it is impossible to
guarantee the timing of a collection (unless you force it, which is a horribly expensive solution). Second, if a connection is
left to be collected, it might not make it back into the connection pool, requiring the creation of a new connection. This
adds more overhead to an application. Ideally, you want to get a connection from the connection pool (by calling Open on
your connection object) and then return it to the pool immediately when you are done with it (by calling Close).
On another performance note, you should make sure that you choose your managed data provider carefully. Although the
OleDb providers enable you to access virtually any database or data source (including SQL), Microsoft has also developed
a SQL-specific managed provider that does not use OLE DB. In fact, the SqlClient namespace provides significant
performance gains when accessing SQL Server–based databases because it is a network-level provider and
communicates directly with a SQL server through low-level network access. The OleDb managed provider uses the OLE
DB database providers, not only introducing an additional layer for OLE DB but also running it through the COM interop
layer, further increasing overhead. When performance is critical, use only the OleDb provider for data sources other than
Microsoft SQL Server (unless other companies have produced their own managed database providers by the time you read
this).
If you need to do a lot of database work (such as making multiple sequential queries), it is important to make sure that you
do not hold on to your database connections throughout the series of calls. If possible, you should release the connections
between queries. This does add some additional overhead to your method (because it has to get connections from and
return them to the connection pool more frequently), but it increases churn in the pool and ensures that connections will
always be available to handle your applicationâ™s needs. Take it from usâ ”when your application runs out of
connections, its performance craps out big time (yes, that is a technical term), and thatâ ™s putting it mildly. The
following example illustrates good database etiquette:
Function DBTest()
Dim conn As New SqlConnection(connStr)
Dim cmd As New SqlCommand("Select * From Customers", conn)
While dr.Read()
' Do some processing
End While
While dr.Read()
' Do some other processing
End While
Chapter 21
Upgrading Distributed Applications
Until now we havenâ™t really talked about upgrading large-scale distributed applications. But building these types of
applications is, after all, the cornerstone of the entire Microsoft .NET initiative. The .NET Framework is designed from
the ground up to support new first-class distributed technologies. Knowing how these technologies are implemented is
critical in assessing how to upgrade a given application. Unfortunately, so many possibilities and variations exist that it is
impossible to address all or even a significant portion of them in a meaningful way.
This chapter gives an overview of the core concepts behind large-scale distributed applications. It then discusses how
these concepts are implemented in Visual Basic .NET. We highlight the use of these technologies and describe how to
implement them in your applications. Although this chapter covers some interoperability issues (such as how to build
wrappers and marshal objects such as ADO Recordsets from the server to the client), there are others you
may face if you get creative with your application. Consult Chapter 13 for more
information regarding COM interoperability.
Some of the examples in this chapter use the Northwind database Northwind.mdb (included on the companion CD)
as a basis for demonstrating various forms of data access.
Since our topic is building large-scale distributed applications in Visual Basic .NET, now would be a good time to take a
step back and provide an overview of the concepts that are important for understanding the benefits and trade-offs of the
possible approaches. Writing smaller applications usually does not require a great deal of planning or design work. Issues
such as calling overhead and latency donâ™t typically rear their heads in smaller applications. Larger-scale applications,
however, are a far different beast. For the purposes of this chapter, we define a distributed application as an application in
which one or more of the logical tiers are located on remote machines. It is critically important to be aware of the design
issues that can potentially make or break such an application. To this end, this section looks at three important concepts in
large-scale distributed application development:
Tightly coupled systems, on the other hand, impose a significant amount of customized overhead to enable
communication and require a greater understanding between the systems (think Distributed Common Object Model
[DCOM] or remoting). A tightly coupled application requires that there be a greater level of system integration and
dependence. A loosely coupled system is far less prone to problems with system setup, as long as the minimum
communication requirements are met.
The other side to coupling is the overhead requirement. A loosely coupled system imposes greater marshaling overhead.
The native binary format must be translated to something that both systems can understand. It is then the responsibility of
the target system to parse that message and convert it to a format that it understands. A tightly coupled application does
not need to translate to and from a universal format. Typically the network format is binary and is well understood by both
parties, requiring less translation and, by extension, the use of fewer processing resources.
When you are developing a distributed application, you need to be aware of the limitations of this type of application. In a
standard application, you typically donâ™t worry about the processing overhead of function calls. These calls are
typically limited only by the speed of your computer. Moving to the network introduces more variables into the equation.
Figure 21-1 demonstrates the relative differences in the overhead of method calls, depending on where the target resides.
As you can see, in-process method invocation has very little calling overhead. Out-of-process method invocation
introduces more overhead, due to marshaling across the process boundaries. This type of invocation involves
communicating with another application on the local computer (such as a Microsoft Windows service or a COM+ server
application). Remote process method invocation brings into play not just process-boundary marshaling but also network
latency. This typically means a vastly greater calling overhead than any of the other options and implies the need for a
different approach to the applicationâ™s architecture.
Figure 21-1
Overhead of method calls in different contexts.
So while we can call into local methods without any concern for overhead, and even out-of-process overhead isnâ ™t too
bad, we need to be extra careful of not only how often we call remote methods but also how much work these methods do.
Think of it this way: since the overhead is greater, we need to make the method calls worthwhile. Consider the following
example to help demonstrate what we mean. It shows the difference between setting properties on an object—a typically
simple operation with low overhead—and using a single method to set all the properties at once.
This example uses two different approaches, but it does not really clarify why one is better than the other. For this, look to
Figure 21-2. It should help you visualize the differences between the approaches and understand how they affect an
applicationâ™s performance. Approach 1 is âœchattyâ in that each property access involves a separate network
operation, whereas approach 2 is âœchunkyâ because it accomplishes its task with a single network operation.
Figure 21-2
Remote objects and the effect of network latency.
At this point, it should be clear that the design of your remote objects and interface is vitally important to your
applicationâ™s scalability and performance. When you look at the performance of approach 1, you should also
understand that when you access any remote object you are consuming resources on the remote server, which means that
fewer connections are available to other applications. The more efficient your design, the better your application will
perform and the better it will scale up in an enterprise environment.
Componentization is an extremely important concept for any large-scale application. It is more than just a separation
based on the functional tiers of your application (such as user interface, business logic, and the data tier). It is also the
separation of functional areas within individual tiers into specific components. This separation has several benefits.
Imagine a customer management application. You might divide the server-side components into database, business logic,
and general utility components, as shown in Figure 21-3. This structure gives you the ability to concentrate all of the
database logic into a single area of the application and manage database changes through versions in a more feasible
fashion.
Figure 21-3
Hypothetical componentized application.
From the standpoint of upgrading, logical separation of code enables a smoother approach to upgrading larger
applications. It gives you more choices regarding what to upgrade and how to upgrade your application within a given
application. So before you undertake any upgrading tasks, it makes a lot of sense to analyze your application and look at
ways to partition the logic into separate containers and to consider the option of leaving some code in Visual Basic 6
while upgrading elements that could derive immediate benefits.
The Internet is quickly evolving from the Web sites of today that deliver only pages to Web browsers to the next
generation of programmable Web-based technologies that directly link organizations, applications, services, and devices
with one another. These programmable Web sites are not passive—they are reusable, intelligent Web services. The
common language runtime provides built-in support for creating and exposing XML Web services, using a programming
abstraction that is consistent and familiar to both ASP.NET Web Forms developers and existing Visual Basic users. The
resulting model is both scalable and extensible and embraces open Internet standards—HTTP, XML, SOAP, and Web
Services Description Language (WSDL)—so that it can be accessed and consumed from any client or Internet-enabled
device.
Support for XML Web services is provided by ASP.NET, using the .asmx file extension. An .asmx file contains code
similar to an .aspx file. In fact, much of the ASP.NET programming model is available to XML Web service developers,
with the exception of the user interface classes. For a Web service, the user interface has no real meaning. It is, after all, a
middle-tier technology. There is a caveat, however. ASP.NET projects and ASP.NET Web service projects are not
fundamentally different, and there is no required separation. You can add an XML Web service file to a Web project, and
you can also add a Web form to an XML Web service project. These files are then URI addressable, in the same way
that .aspx files are. Also like .aspx files, .asmx files are compiled automatically by the ASP.NET runtime when a request
to the service is made. (Subsequent requests are serviced by a cached precompiled object.)
On a more fundamental level, XML Web services enable the exchange of data and the remote invocation of application
logic using XML messaging to move data through firewalls and between heterogeneous systems. Although remote access
of data and application logic is not a new concept, XML Web services enable it to take place in a loosely coupled fashion.
The only assumption between the XML Web service client and the XML Web service is that recipients will understand
the messages they receive. As a result, programs written in any language, using any component model, and running on
any operating system can access XML Web services.
When you create and/or use an XML Web service, you are taking advantage of SOAP. You may be aware of the SOAP
Toolkit that was published for Visual Basic developers. While you can think of XML Web services as an alternative to the
SOAP Toolkit, they are far more powerful, thanks to the .NET Framework. The SOAP Toolkit required a lot of work on
the part of the developer, and Visual Basic imposed limitations related to serialization of objects. Visual Basic .NET
removes these limitations and provides a far more powerful framework for developing SOAP applications by eliminating
the need to implement features such as serialization and proxy generation. These features are already implemented in
Visual Basic .NET and can be leveraged without any additional effort. It is possible, of course, to implement serialization
yourself to provide any custom features that your application requires. For the vast majority of applications, however, the
built-in serialization capabilities of the .NET Framework are more than sufficient.
Implementing a basic XML Web service in any application is a straightforward process. The XML Web Service project
type (selected in the New Project dialog box) contains all the necessary elements for implementing an XML Web service.
If you are running Visual Basic .NET on a machine with Internet Information Services (IIS) installed, it is a trivial task to
create a new project and implement an XML Web service. The companion CD contains a sample called Simple Web
Service, as well as a setup document that discusses the steps necessary to get the example up and running.
The basic steps for creating an XML Web service are as follows:
3. Add a method to the XML Web service class, and mark the method with the WebMethod attribute.
The most important part here is the use of the WebMethod attribute. This attribute tells the compiler to generate the XML
contract for the Web service, register it for discovery, and produce the implementation code necessary for marshaling
parameters and return objects. It also enables you to filter the methods of your XML Web service class and publish only
the methods you want to make publicly available over the Internet. Think of this filtering as taking access protection a
step further. Youâ™re already familiar with creating public, private, and friend methods. But there will probably be
situations in which your application architecture requires a public method on your XML Web service class that you do not
want to make available through the XML Web service itself. In such cases, the power of the WebMethod attribute really
shines. It gives you absolute control over what you publish over the Internet without requiring you to sacrifice good
component design in your XML Web service classes. The following excerpt from the SimpleService.asmx.vb file (part of
the SimpleWebService sample project) demonstrates the WebMethod attribute in action:
Now is a good time to talk about restrictions on parameters and return values for XML Web services. An XML Web
service is not a bidirectional object. This means that you cannot pass objects by reference; they must always be passed by
value. In other words, your parameters and return values must be serializable. Most, if not all, of the intrinsic data types in
Visual Basic .NET and the .NET Framework support serialization as long as the objects themselves are self-
containedâ”that is, as long as they donâ™t represent or contain any physical system resources, such as database
connections or file handles. For your custom classes you have two options. You can implement the ISerializable interface
for your class, but this is the more manual process. Alternatively, you can use the Serialize class attribute. This option
enables your class to serialize itself. No work is necessary on your part unless one or more of the types contained in your
class do not support serialization. In such cases, you have the choice of adding the Serializable attribute to those classes (if
they are under your control) or implementing the serialization yourself with the ISerializable interface.
The Simple Web Service example demonstrates the basic elements of an XML Web service. Figure 21-4 illustrates what
the compiled service looks like in a Web browser. You can see all the methods exposed by the service and view the
complete XML service description. We will leave the latter for you to pursue if youâ ™re interested. It is really used only
by the Web reference mechanism in Visual Basic .NET, and you donâ ™t need to be familiar with the contents of the
service description to make use of an XML Web service.
Figure 21-4
The SimpleService.asmx XML Web service as it appears in a Web browser.
Testing Your XML Web Service
As you have already seen, when you build an XML Web service, the Visual Basic IDE creates a test page for that service.
This page allows you to inspect all the methods that are exposed, and it also gives you the ability to invoke the methods
manually. This should be your first step in testing the service, to ensure that it is doing what you expect. In our sample
service, you can click the HelloWorld method in the service description page to get more information about it. Figure 21-5
shows what this method looks like.
Figure 21-5
The SimpleWebServiceâ™s HelloWorld method.
As you can see, a great deal of information is available through this page. You can also invoke the method (using the
Invoke button) and view the SOAP result message. Additionally, if the service accepts parameters, this page will have
input boxes for each of the parameters. There are some limitations to this, however. If your methods require parameters in
a binary format (a Byte array, for example), you will need to invoke the method programmatically.
The result of invoking the HelloWorld method is displayed in a separate window and looks like this:
Pretty neat, huh? Now we need to look at how you can use this method in an application.
Now that an XML Web service exists on the target machine, it is possible to add a Web reference to a client project. An
XML Web service client can be any kind of project, from a command-line or form-based application to a full-blown Web
site or COM+ component. In this case, we have gone with a simple form-based application, but feel free to experiment by
adding Web references to other kinds of projects.
Recall our discussion of different types of references in Chapter 6. A Web reference is simply a process by which the
Visual Basic IDE goes out to the specified XML Web service, gets the XML contract for the service, and builds a proxy
class that implements the contract. This process occurs transparently, and it is flexible enough that references can be
“refreshed†in the IDE. In other words, it is possible to have the IDE regenerate the proxy class (which is usually
desirable if there are new features that you want to take advantage of or if there have been interface implementation
changes).
This proxy is not tightly coupled to the complete interface of an XML Web service. It will use methods and properties
according to how they were defined the last time the proxy was generated. If the specified methods have not changed their
signature—that is, the parameter types, parameter count, or return type—your application will not break, regardless of
any other changes that might have been made to the XML Web service itself.
Creating this proxy is simple, as we have already mentioned. You create a simple Windows Forms project and add a Web
reference by right-clicking the References collection in the Solution Explorer, specifying the path to the XML Web
service that you have already created, and clicking OK. After a short delay, a new XML Web service proxy class should
be added to your application. Now all that is necessary is to use it. Our sample solution has a Web Service Tester project
that implements this sample XML Web service. This is a one-button application that does the following two things in
response to a button click:
2. Call the HelloWorld method, and display a message box with the result.
You can see the results in Figure 21-6. This sample project helps demonstrate how XML Web services simplify the
development of distributed applications. XML Web Services are a powerful tool that enable the development of
fundamentally different kinds of applications, due to its platform- and language-independent nature.
Figu
re 21-6
Web Service Tester application in action.
Moving On
This is all well and good, you may be thinking, but what about my existing applications? How should I implement XML
Web services in an existing COM or managed application? Does it require redesign, or are there alternatives? Read on….
Supporting Web Services in Your Existing Applications
If you have an existing application for which you want to implement XML Web services without a major rewrite, you can
take the approach of implementing the service as a wrapper around your existing code. Doing so allows you to handle the
necessary serialization work in the service without having to modify the original applicationâ ™s code. This can be an
effective way to add support for XML Web services to existing COM applications without making any radical changes. It
also gives the developer the opportunity to rethink the applicationâ ™s interface to the outside by hiding methods or
doing additional work that was not previously done on the server side.
This approach does raise some questions. If you are wrapping existing COM applications, you need to be aware of return
types and whether you will need to implement serialization for them. This concern arises as a potential problem only when
you need to pass variables that are specific COM types. Intrinsic data types will have no problems; it gets tricky only
when you have complex custom data types, such as COM objects. What it ultimately boils down to is that you cannot use
a COM component as either a Web service parameter or a return type. You must first serialize a COM component to an
intrinsic data type (an XML string or a Byte array). Of the commonly used Microsoft COM objects, only ADO supports
serialization to and from XML, and it is a manual process—there is no such thing as implicit serialization for COM
objects. If you create your own COM components, you may wish to add support for XML serialization. Including support
is essential if you want to support passing such a component to and from an XML Web service.
There is also the possibility that you cannot modify the original COM object to add serialization capabilities. In this
instance, you would be required to create code to perform the necessary serialization work in Visual Basic .NET, using it
on both the server and client tiers. However, it may not be possible to work around some issues and you may be required
to make some modifications to your original application. From a middle-tier standpoint, one of the most common objects
passed around, and thus a likely candidate for inclusion in an XML Web service, is the ADO Recordset object, which is
the subject of the next section.
As we mentioned earlier, it is possible to wrap an XML Web service around an existing COM object. If you are passing
only the intrinsic types (such as strings, integers, and arrays of variables), you simply need to call the methods directly.
The .NET Framework already knows how to serialize these types of variables. If you need to pass ADO Recordsets back
and forth, however, you have some work to do.
In ADO 2.1, Microsoft added XML support to the ADO Recordset object. This object provides support for serialization to
and from XML. The only caveat is that the support for serialization is not implicit. In other words, the Recordset does not
support automatic or implicit serialization, but you can do the work explicitly. The key here is the Save method on the
Recordset object and the use of the ADODB Stream object. Figure 21-7 illustrates the process of wrapping a COM object
with an XML Web service, where the result is an ADO Recordset, and shows how the Recordset is marshaled from server
to client.
Essentially, the figure depicts the process by which the Recordset is persisted to XML on the server and is returned to the
client. The client then reconstructs the original Recordset. Notice that the reconstructed Recordset is not exactly the same
as the original. The new Recordset does not contain any connections to a database, nor does it understand where it came
from. For this reason, we say that this Recordset is disconnected.
Figure 21-7
Passing a Recordset in an XML Web service.
To give you a better feel for how this process works, weâ™ve provided a sample on the companion CD called Web
Services and ADODB. The sample consists of two projects: an XML Web service (NorthwindDatabase) and a Windows
Forms client application (Northwind Viewer). The design of the sample is simple. One method is available through the
XML Web service that allows the client to specify a SQL query string and returns the result as an XML representation of
the resulting Recordset.
The following is the implementation code for the XML Web service. The Query method does all the work. You can see
that this method takes a query string as a parameter and returns a string. As we have discussed previously, this return
string contains the XML representation of the Recordset returned by the specified query. This project references ADODB
and uses the ADO Connection object directly to execute the query. It would also work just fine with a regular COM object
that returns a Recordset; we are just trying to make the sample as clear and concise as possible.
Imports ADODB
Imports System.Web.Services
<WebService(Namespace:="http://tempuri.org/")> _
Public Class NorthwindDatabase
Inherits System.Web.Services.WebService
Try
' You will need to specify the local path to your copy
' of the Northwind.mdb file. This is just an example.
conn.Open( "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\Northwind.mdb")
Catch e As Exception
Return Nothing
End Try
Dim rs As Recordset
Try
rs = conn.Execute(queryString)
Catch e As Exception
conn.Close()
Return Nothing
End Try
str.Close()
rs.Close()
conn.Close()
End Function
End Class
After the query has been executed, we use the ADODB Stream class to serialize the contents of the Recordset. (In Chapter
20, we touched on XML serialization for Recordsets and introduced the concept with the RsToString and StringToRS
methods.) Notice that there are no particular requirements as to the type of Recordset used. This example uses a forward-
only Recordset (which is more than sufficient), but it would work equally well with any kind of client or server-side
cursor. Also notice that we close out all the ADO classes (Stream, Recordset, and Connection). Chapter 10 discusses why
this step is crucial.
Now that we have the XML Web service, we need something to test it. The test sample, Northwind Viewer, has a couple
of interesting features. Not only does it deserialize a Recordset from XML, but it also uses the OleDbDataAdapter to
convert the Recordset to an ADO.NET DataTable. It then sets the DataSource property of the QueryDataGrid using the
newly created table. Thatâ™s it, aside from additional initialization code in the formâ ™s Load event. We should point
out one thing here. We instantiate the XML Web service proxy only once in the Load event, rather than on every button
click. This practice is perfectly acceptable because the proxy class does not represent an active network connection.
Connections are made only when a method on the class is invoked, so we donâ ™t bother creating the same proxy over
and over. Creating it once is sufficient. Again, notice that we clean up the ADO objects by calling Close. Always play
nice with your COM objects and clean up afterward.
Imports ADODB
Imports System.Data
Imports System.Data.OleDb
xml = nwdb.Query(QueryComboBox.Text)
If xml Is Nothing Then
QueryDataGrid.DataSource = Nothing
MsgBox("The query failed!")
Exit Sub
End If
da.Fill(table, rs)
QueryDataGrid.DataSource = table
rs.Close()
str.Close()
End Sub
End Class
Figure 21-8 demonstrates the test application in action. You can see that the user selects a query from the ComboBox and
then clicks the Execute button. Another neat feature of this application is that the ComboBox is editable at run time, so
you can further customize the query.
Figure 21-8
Northwind Database Viewer application in action.
You should now be able to see how you might get started wrapping your application with XML Web services. Depending
on how your application is implemented, it is quite possible to handle the serialization issues without a great deal of
disruption. An advantage is that it is extremely easy to create a test XML Web service to discover how your application
will behave and what is feasible.
Remoting
As we mentioned earlier, remoting is the process of communication between different operating system processes,
regardless of whether they are on the same computer. It simplifies communication between objects living in different
application domains, possibly on different computers, and between different contexts, possibly in different application
domains. The remoting framework is built into the common language runtime and can be used to build sophisticated
distributed applications. Some of the features provided by .NET remoting are as follows:
Proxy objects
Object passing
Activation models
Lease-based lifetime
Currently, remoting supports two transport protocols: TCP/IP and HTTP. Each has its advantages, depending on your
requirements. TCP/IP offers the better performance by far. It is basically a low-level binary protocol that enables virtually
direct communication with the remote objects. This protocol tends to work well in an intranet environment in which you
have unrestricted TCP port usage. Using TCP/IP becomes problematic when you need to support clients across multiple
sites or over the Internet. Most corporations have firewalls that block anything but the standard TCP/IP ports, which
would render a remoting application using a custom TCP/IP port unusable.
The solution is the HTTP protocol. Firewalls are almost always configured to allow traffic over TCP/IP port 80 (the port
used by the World Wide Web). Using HTTP, your remoting applications can communicate freely over the span of the
Internet. The downside is that HTTP is nowhere near as efficient as TCP/IP. When you specify HTTP as your remoting
transport protocol, you basically end up with XML over HTTP. Serializing your objects into XML is not as compact as
using a binary format. It increases the size of the payload, increases processing resources on both the client and server,
and takes longer to transmit. This extra overhead means that a method call over HTTP costs more than one over TCP/IP.
Will you notice the difference in casual use? Not really. But it does have implications for larger-scale applications. Itâ ™s
nothing to worry about, just something to keep in mind.
As far as requirements for objects capable of remoting go, there is only one that is significant: they must derive the
MarshalByRefObject class. Notice that this is a built-in feature of COM+ classes because the ServicedComponent class
has MarshalByRefObject as a base class. Now is a good time to look at an example.
The simple example in this section helps demonstrate the lifetime of remoting within your applications and illustrates
some of the architectural considerations. Called Simple Remoting and included on the companion CD, it consists of a host
process and a client process. The host process is a lightweight application that opens a TCP channel for listening to
remoting requests and registers a type with the common language runtime. Once it is registered, the type is available for
remote instantiation through the specified channel. The example uses the type SimpleClass (derived from
MarshalByRefObject) as the remoting object and contains two separate projects (a server project and a client project).
Before we go any further, letâ™s take a look at SimpleClass:
From the applicationâ™s perspective, the critical part is the server project, which is responsible for registering the
remoting object with the host runtime. The code for doing this is fairly simple and looks like the following:
Figure 21-9
Life cycle of the Simple Remoting example.
As you can see, we need to keep this process running in some form or another; otherwise, our client is out of luck.
Speaking of the client, here is what it looks like.
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Tcp
Public Class Form1
Inherits System.Windows.Forms.Form
Dim sc As SimpleClass
Private Sub DateTimeButton_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles DateTimeButton.Click
If Not Initialize() Then Return
MsgBox(sc.GetDateTime(), MsgBoxStyle.DefaultButton1, _
         "GetDateTime()")
End Sub
If sc Is Nothing Then
MsgBox("Could not initialize the remoting client. " & _
"Check your configuration.")
Return False
End If
End If
Return True
End Function
End Class
From this example, you can see how the client initializes the SimpleClass proxy by making the request of the Activator
class. An important configuration requirement that is not obvious here is that both the client class and the server class
must reside in the same namespace. That is why both the server code and the client code refer to Server.SimpleClass. Both
projects have a copy of the class, and their namespaces match. This allows the client to know the fully qualified type of
the SimpleClass that is also known to the server process. The application itself should look like Figure 21-10.
Figure 21-
10
Simple Remoting sample in action.
While this example will work, it is not the most desirable way to go about implementing remoting in an
application—especially in a larger-scale application. This observation leads us to a discussion of the best architecture for
an application that uses remoting.
The .NET Framework SDK has many decent remoting examples, but they are not in-depth enough to help you decide on
an implementation strategy. Calling remote objects is by definition an expensive process, requiring you to think very
carefully about how a remote object is used. Often when we use objects in our applications, we donâ ™t worry about
whether we are using properties or methods, and we are not usually concerned with when and how often we manipulate
those objects. In a distributed application, however, these are extremely important considerations. With XML Web
services, it is possible to make methods available selectively. With remoting, it is not quite so simple. You are working
with a proxy for the original class, and by definition all public methods are available to the client process. However, there
is a way around this issue: interfaces.
Interfaces are an important way to implement a class for remoting. Programming against a limited interface prevents
unintentional use of an object. You can do this by creating a separate assembly that contains the public interfaces intended
only for use by remoting clients. Figure 21-11 illustrates this concept.
Figure 21-11.
Suggested remoting architecture.
Programming against interfaces is important for larger-scale applications because it allows you to further control which
methods are exposed to down-level clients. This technique gives you a greater level of control than just access protection
for methods on any given class. Weâ™ve provided another sample program on the companion CD, called Architecting
for Remoting, that demonstrates how to use this technique. There are three main components to the application, as
outlined in Figure 21-11: the INorthwindDatabase interface, the Database class (which implements the
INorthwindDatabase interface), and the MyClient project. The critical difference between this example and the previous
one is that the client application has awareness only of the INorthwindDatabase interface. It does not have a copy of the
target class and does not know how the class is implemented. Here is the interface we are talking about:
The next code example is the actual database class that was created to implement the INorthwindDatabase interface.
Notice that the class contains other public methods that do not exist in the interface. As far as the class is concerned, these
are utility methods that the client should not be able to call (the ExecuteQuery method, for example).
Requiring the client to use the interface, instead of the actual class definition, has two effects. First, it prevents the client
from using methods it should not have access to. Second, it allows you to create other database classes based on the same
interface and use them all interchangeably without having to make any modifications to the client application. The
implementation class, in this example database, is derived from ServicedComponent instead of directly from
MarshalByRefObject, for the sake of demonstrating how easy it is to support COM+ and remoting simultaneously.
Imports MyInterfaces
Imports System.Data
Imports System.Data.OleDb
Imports System.EnterpriseServices
The server process is not very interesting. It registers the ServerProcess.Database class with the runtime and then sits
there. The code is really no different from the server project in the Simple Remoting example. The client, on the other
hand, is quite different. The client requests an object from the remoting channel that is based solely on the
INorthwindDatabase interface. It has no reference to or knowledge of the underlying Database class and thus cannot call
any methods that are not exposed by the INorthwindDatabase interface. Here is what the code looks like:
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Tcp
Return True
End Function
Conclusion
At this point you should have at least a basic understanding of how to implement the core distributed technologies and
where the relative strengths of remoting and XML Web services lie. Traditionally, developing distributed applications has
required a lot of work on the part of the developer. Microsoft started to make things a great deal easier with the
introduction of Microsoft Transaction Services (MTS) and RDS. The .NET platform takes the process to a whole new
level. With objects that support the innate capability of serialization and protocol abstraction, you are free to concentrate
more on the architecture and design of your applications rather than spend time on network and marshaling issues.
Moving forward, you need to think carefully about what type of solution will fit well with your application. It is possible,
and sometimes desirable, to maintain some components in Visual Basic 6 rather than moving the entire application to
Visual Basic .NET. Donâ™t feel pressured to make unnecessary changes just to be â œpureâ .NET. Visual
Basic .NET provides the groundwork for developing whole new classes of distributed applications, but this need not be at
the expense of your existing applications.
App
No direct equivalent to upgrade to. Individual properties and methods are upgraded as shown in the table.
anyName PropertySystem.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.Location).CompanyNameMeth
System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.-
escription Property Meth
Location).FileDescription
System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.-
Copyright Property Meth
Location).LegalCopyright
System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.-
Trademarks Property Meth
Location).LegalTrademarks
r PropertySystem.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.- Location).FileMinorPartMeth
l Basic 6 Type Visual Basic .NET Type
equestPendingMsgTextPropertyNo mapping
equestPendingMsgTitl
PropertyNo mapping
Property Meth
No mapping for Set
Upgrades to System.Windows.Form.CheckBox.
Alignment PropertyCheckAlign
Appearance PropertyFlatStyle
Visual Basic 6 Type Visual Basic .NET
BackColor PropertyBackColor
Caption PropertyText
CausesValidation PropertyCausesValidation
Container PropertyParent
Enabled PropertyEnabled
Font PropertyFont
ForeColor PropertyForeColor
Height PropertyHeight
HWnd PropertyHandle
Left PropertyLeft
MousePointer PropertyCursor
Name PropertyName
OLECompleteDragEvent No mapping
Parent PropertyFindForm
Picture PropertyImage
RightToLeft PropertyRightToLeft
Style PropertyAppearance
TabIndex PropertyTabIndex
TabStop PropertyTabStop
Tag PropertyTag
Top PropertyTop
Value PropertyCheckState
Visible PropertyVisible
Width PropertyWidth
Upgrades to System.Windows.Forms.Button.
BackColor PropertyBackColor
Caption PropertyText
CausesValidation PropertyCausesValidation
Container PropertyParent
Default PropertyAt design time, Form.AcceptButton At run time, VB6.GetDefault, VB6.SetDefault methods
Enabled PropertyEnabled
Font PropertyFont
Height PropertyHeight
hWnd PropertyHandle
Left PropertyLeft
MousePointer PropertyCursor
Name PropertyName
OLECompleteDragEvent No mapping
Parent PropertyFindForm
Picture PropertyImage
RightToLeft PropertyRightToLeft
TabIndex PropertyTabIndex
TabStop PropertyTabStop
Tag PropertyTag
Top PropertyTop
Value PropertyNo mapping for Get PerformClick method if value is nonzero for Set
Visible PropertyVisible
Width PropertyWidth
Upgrades to Microsoft.VisualBasic.Compatibility.VB6.DirListBox.
BackColor PropertyBackColor
Visual Basic 6 Type Visual Basic .NET
CausesValidation PropertyCausesValidation
Container PropertyParent
Enabled PropertyEnabled
Font PropertyFont
ForeColor PropertyForeColor
Height PropertyHeight
HWnd PropertyHandle
IMEMode PropertyImeMode
Left PropertyLeft
List PropertyDirList
ListCount PropertyDirListCount
ListIndex PropertyDirListIndex
MousePointer PropertyCursor
Name PropertyName
OLECompleteDragEvent No mapping
Parent PropertyFindForm
Path PropertyPath
Visual Basic 6 Type Visual Basic .NET
TabIndex PropertyTabIndex
TabStop PropertyTabStop
Tag PropertyTag
Top PropertyTop
TopIndex PropertyTopIndex
Visible PropertyVisible
Width PropertyWidth
FileListBox
Upgrades to Microsoft.VisualBasic.Compatibility.VB6.FileListBox.
Archive PropertyArchive
Visual Basic 6 Type Visual Basic .NET
BackColor PropertyBackColor
CausesValidation PropertyCausesValidation
Container PropertyParent
Enabled PropertyEnabled
FileName PropertyFileName
Font PropertyFont
ForeColor PropertyForeColor
Height PropertyHeight
Hidden PropertyHidden
hWnd PropertyHandle
Visual Basic 6 Type Visual Basic .NET
IMEMode PropertyImeMode
Left PropertyLeft
List PropertyItems
ListCount PropertyItems.Count
ListIndex PropertySelectedIndex
MousePointer PropertyCursor
MultiSelect PropertySelectionMode
Name PropertyName
Normal PropertyNormal
OLECompleteDragEvent No mapping
Parent PropertyFindForm
Path PropertyPath
Pattern PropertyPattern
ReadOnly PropertyReadOnly
System PropertySystem
TabIndex PropertyTabIndex
TabStop PropertyTabStop
Tag PropertyTag
Top PropertyTop
TopIndex PropertyTopIndex
Visible PropertyVisible
Width PropertyWidth
Visual Basic 6 Type Visual Basic .NET
Upgrades to System.Windows.Forms.Form.
ActiveControl PropertyActiveControl
BackColor PropertyBackColor
BorderStyle PropertyFormBorderStyle
Caption PropertyText
ControlBox PropertyControlBox
Controls PropertyControls
Count PropertyControls.Count
Enabled PropertyEnabled
Font PropertyFont
ForeColor PropertyForeColor
Height PropertyHeight
HWnd PropertyHandle
Icon PropertyIcon
KeyPreview PropertyKeyPreview
Left PropertyLeft
MaxButton PropertyMaximizeBox
At design time, if value is True, then MDIParent property is set in the New event At run time,
MDIChild Property
maps to expression Not f.MDIParent Is Nothing
MinButton PropertyMinimizeBox
MousePointer PropertyCursor
Name PropertyName
OLECompleteDragEvent No mapping
Picture PropertyBackgroundImage
PrintForm Method Not mapped (See Chapter 15 for how to implement PrintForm.)
RightToLeft PropertyRightToLeft
ShowInTaskbar PropertyShowInTaskBar
StartUpPosition PropertyStartPosition
Tag PropertyTag
Top PropertyTop
Visible PropertyVisible
WhatsThisButton PropertyHelpButton
Width PropertyWidth
WindowState PropertyWindowState
HScrollBar
Upgrades to System.Windows.Forms.HScrollBar.
CausesValidation PropertyCausesValidation
Container PropertyParent
Enabled PropertyEnabled
Height PropertyHeight
HWnd PropertyHandle
LargeChange PropertyAt design time, LargeChange At run time, three lines of code to calculate new value
Left PropertyLeft
Max PropertyAt design time, Maximum At run time, value calculated from LargeChange
Min PropertyMinimum
MousePointer PropertyCursor
Name PropertyName
Parent PropertyFindForm
RightToLeft PropertyRightToLeft
Scroll Event Scroll event, or Scroll_renamed method called from Scroll event if Change event exists
SmallChange PropertySmallChange
TabIndex PropertyTabIndex
TabStop PropertyTabStop
Tag PropertyTag
Top PropertyTop
Value PropertyValue
Visible PropertyVisible
WhatsThisHelpIDPropertyNo mapping
Width PropertyWidth
Upgrades to System.Windows.Forms.PictureBox.
Visual Basic 6 Type Visual Basic .NET
BorderStyle PropertyBorderStyle
Container PropertyParent
Enabled PropertyEnabled
Height PropertyHeight
Left PropertyLeft
MousePointer PropertyCursor
Name PropertyName
OLECompleteDragEvent No mapping
Parent PropertyFindForm
Picture PropertyImage
Stretch PropertySizeMode
Tag PropertyTag
Top PropertyTop
Visible PropertyVisible
Width PropertyWidth
Line
Upgrades to System.Windows.Forms.Label if horizontal or vertical line. Diagonal lines are not upgraded.
Visual Basic
Type Visual Basic .NET
6
BorderColor PropertyBackColor
Container PropertyParent
Name PropertyName
Parent PropertyFindForm
Tag PropertyTag
Visible PropertyVisible
BackColor PropertyBackColor
CausesValidation PropertyCausesValidation
Container PropertyParent
Enabled PropertyEnabled
Font PropertyFont
ForeColor PropertyForeColor
Height PropertyHeight
hWnd PropertyHandle
IMEMode PropertyImeMode
IntegralHeight PropertyIntegralHeight
Left PropertyLeft
ListCount PropertyItems.Count
ListIndex PropertySelectedIndex
MousePointer PropertyCursor
MultiSelect PropertySelectionMode
Name PropertyName
OLECompleteDragEvent No mapping
Parent PropertyFindForm
RightToLeft PropertyRightToLeft
Sorted PropertySorted
TabIndex PropertyTabIndex
TabStop PropertyTabStop
Visual Basic 6 Type Visual Basic .NET
Tag PropertyTag
Text PropertyText
Top PropertyTop
TopIndex PropertyTopIndex
Visible PropertyVisible
Width PropertyWidth
Menu
Caption PropertyText
Checked PropertyChecked
Enabled PropertyEnabled
Name PropertyName
Visual Basic 6 Type Visual Basic .NET
NegotiatePositionPropertyNo mapping
Parent PropertyGetMainMenu.GetForm
Shortcut PropertyShortcut
Visible PropertyVisible
WindowList PropertyMDIList
OptionButton
Upgrades to System.Windows.Forms.RadioButton.
Alignment PropertyCheckAlign
Appearance PropertyFlatStyle
BackColor PropertyBackColor
Caption PropertyText
CausesValidation PropertyCausesValidation
Container PropertyParent
Enabled PropertyEnabled
Visual Basic 6 Type Visual Basic .NET
Font PropertyFont
ForeColor PropertyForeColor
Height PropertyHeight
hWnd PropertyHandle
Left PropertyLeft
MousePointer PropertyCursor
Name PropertyName
OLECompleteDragEvent No mapping
Parent PropertyFindForm
Picture PropertyImage
RightToLeft PropertyRightToLeft
Style PropertyAppearance
TabIndex PropertyTabIndex
TabStop PropertyTabStop
Tag PropertyTag
Top PropertyTop
Value PropertyChecked
Visible PropertyVisible
Visual Basic 6 Type Visual Basic .NET
Width PropertyWidth
Shape
Visual Basic
Type Visual Basic .NET
6
BackColor PropertyBackColor
Container PropertyParent
Height PropertyHeight
Left PropertyLeft
Name PropertyName
Visual Basic
Type Visual Basic .NET
6
Parent PropertyFindForm
Tag PropertyTag
Top PropertyTop
Visible PropertyVisible
Width PropertyWidth
Upgrades to System.Windows.Forms.TextBox.
Alignment PropertyTextAlign
BackColor PropertyBackColor
CausesValidation PropertyCausesValidation
Container PropertyParent
Enabled PropertyEnabled
Font PropertyFont
ForeColor PropertyForeColor
Height PropertyHeight
HideSelection PropertyHideSelection
HWnd PropertyHandle
IMEMode PropertyIMEMode
Left PropertyLeft
Locked PropertyReadOnly
MaxLength PropertyMaxLength
MousePointer PropertyCursor
MultiLine PropertyMultilLine
Name PropertyName
OLECompleteDragEvent No mapping
Visual Basic 6 Type Visual Basic .NET
Parent PropertyFindForm
PasswordChar PropertyPasswordChar
RightToLeft PropertyRightToLeft
ScrollBars PropertyScrollBars
SelLength PropertySelectionLength
SelStart PropertySelectionStart
SelText PropertySelectedText
TabIndex PropertyTabIndex
TabStop PropertyTabStop
Tag PropertyTag
Text PropertyText
Top PropertyTop
Visible PropertyVisible
Width PropertyWidth
VScrollBar
Upgrades to System.Windows.Forms.VScrollBar
CausesValidation PropertyCausesValidation
Container PropertyParent
Enabled PropertyEnabled
Height PropertyHeight
hWnd PropertyHandle
Visual Basic 6 Type Visual Basic .NET
LargeChange PropertyLargeChange at design time; three lines of code at run time to calculate new value
Left PropertyLeft
Max PropertyAt design time,Maximum At run time, value calculated from LargeChange
Min PropertyMinimum
MousePointer PropertyCursor
Name PropertyName
Parent PropertyFindForm
RightToLeft PropertyRightToLeft
Scroll Event Scroll event, or Scroll_renamed method called from Scroll event if Change event exists
SmallChange PropertySmallChange
TabIndex PropertyTabIndex
TabStop PropertyTabStop
Tag PropertyTag
Top PropertyTop
Value PropertyValue
Visible PropertyVisible
WhatsThisHelpIDPropertyNo mapping
Width PropertyWidth
Ed Robinson is a program manager in the Visual Basic .NET team at Microsoft. His responsibilities include designing the
upgrade technologies built into Visual Basic. NET, including the Visual Basic .NET Upgrade Wizard. Ed also works with
customers preparing to move their applications to .NET. With more than eleven years of industry experience in New
Zealand and the United States, he has a good understanding of the challenges and opportunities facing Visual Basic
developers. Ed is a regular speaker at development conferences throughout the world, he has published a number of white
papers on the Microsoft Developers Network (MSDN), and he plays an active role at Microsoft in developing training
material for Visual Studio .NET.
Michael Bond
Michael Bond is the Visual Basic .NET development lead for the Visual Basic .NET Upgrade Wizard. Mike has made a
career of working on Visual Basic. He started at Microsoft more than eleven years ago in Product Support Services (PSS)
supporting QuickBasic 4.5, BASIC PDS 7, and Visual Basic 1. He acted as the product support lead for Visual Basic 2
and 3, working with internal software vendors (ISVs) creating VBX controls; he later moved to the Visual Basic Test
Team, where he functioned as a test lead for Visual Basic 4. In 1995 he joined the Visual Basic development team to
design and develop ActiveX controls and form-related features for Visual Basic 5 and 6 and to work with third-party
vendors who provided ActiveX controls and designers. Mike is coauthor of the VBX to OCX Migration Toolkit.
Robert Ian Oliver is a seasoned Visual Basic developer with a background in C/C++ and Java, currently working as a
development engineer in the Microsoft Visual Studio group. In his two years with Microsoft, Ian has concentrated on
porting enterprise customer applications to Visual Studio .NET and the .NET Framework. His previous experience varies
widely, from being an independent Web consultant to working at Intel as a component design engineer on the first Itanium
chipset. Ian has been involved in Web development as far back as 1995 and has implemented many e-business solutions
throughout the years.