You are on page 1of 567

A04T61587x.

fm Page v Friday, November 16, 2001 10:17 AM

Table of Contents
Foreword xv
Introduction xvii

Part I Introduction to Upgrading


1 Visual Basic .NET Is More Than Visual Basic 6 + 1 3
Why Break Compatibility? 6
Adding New Features 6
Fixing the Language 7
Modernizing the Language 8
It Is Still Visual Basic 8
Expect Subtle Differences 8
Plan for a 95 Percent Automated Upgrade 9
Why Should I Upgrade? 10
New Language Features 10
Windows Forms 14
New Web Development Features 15
Better Development Environment 15
Is Visual Basic Still the Best Choice for Visual Basic Developers? 16
Conclusion 18
2 Visual Basic 6 and Visual Basic .NET: Differences 19
.NET Framework vs. ActiveX 19
.NET Framework 21
Memory Management 22
Type Identity 25
Threading Model 29
Differences in the Development Environment 29
Menu Editor 30
Toolbox 31
Property Browser 32
Tab Layout Editor 33

v
A04T61587x.fm Page vi Friday, November 16, 2001 10:17 AM

vi Table of Contents

Forms Packages 34
A Single Standard for Windows Forms 34
Two Forms Packages for the Price of One 35
Language Differences 35
All Subroutine Calls Must Have Parentheses 37
ByVal or ByRef Is Required 38
Is That My Event? 38
Arrays Must Have a Zero-Bound Lower Dimension 39
Fixed-Length Strings Are Not Supported 40
Variant Data Type Is Eliminated 40
Visibility of Variables Declared in Nested Scopes Is Limited 41
Changes in the Debugger 42
No Edit and Continue 42
Cannot Continue After an Error 42
No Repainting in Break Mode 42
Conclusion 43
3 Upgrading Options 45
Upgrading Is Optional 45
Don’t Upgrade 45
Partial Upgrade 46
Complete Upgrade 47
Upgrade with Interoperability 47
Role of the Upgrade Wizard 48
The Upgrade Report 48
Testing 49
Upgrading from Earlier Versions of Visual Basic 49
Selecting Projects to Upgrade 50
Evaluating the Benefits 51
Evaluating the Effort Required 55
Developing the Upgrade Plan 58
Conclusion 59
4 Preparing Your Project for the Upgrade to Visual Basic .NET 61
Why Change Anything? 61
Cleaning Up Legacy Code 62
VarPtr, DefInt, and Other No-Shows 62
DAO and RDO Data Binding 63
A04T61587x.fm Page vii Friday, November 16, 2001 10:17 AM

Table of Contents vii

Good Visual Basic 6 Coding Practices 64


Variants and Variables 64
Abstraction 66
Early Binding vs. Late Binding vs. Soft Binding 69
Watch Out for Null and Empty 73
Implicit Object Instantiation 74
Conclusion 76

Part II Upgrading Applications


5 Your First Upgrade 79
Upgrade Walkthrough 79
What Just Happened? 85
Language Changes 88
Other Files in Your Project 92
Upgrading Project Groups 94
Using the VB Snippet Upgrade Add-In 98
Upgrading Using the Command Line 99
Conclusion 100
6 Common Tasks in Visual Basic .NET 101
A Guide to Working in Visual Basic .NET 101
Creating a Visual Basic .NET Project 102
Getting to Know the Visual Studio .NET IDE 104
Running Your Project 106
A Quick Introduction to Debugging 107
Miscellaneous Items 108
Handling Build Errors 108
Using the Task List 109
Using Breakpoints 110
References 111
Problem-Solving Techniques 112
Using the System.Diagnostics Library 112
Using CorDbg 113
Simplifying Complex Expressions 114
Conclusion 115
A04T61587x.fm Page viii Friday, November 16, 2001 10:17 AM

viii Table of Contents

7 Upgrade Wizard Ins and Outs 117


Upgrade Philosophy 117
It’s Your Code 117
Just Make It Work 118
Compatibility Library 119
Upgrade Wizard Capabilities and Limitations 119
Wizard Methodology 119
Project Upgrade 121
Forms and Intrinsic Controls 128
ActiveX Controls and ActiveX References 137
Visual Basic Code 140
Global Objects 145
Class Modules and User Controls 145
Objects for Accessing Data 146
Designers 147
Conclusion 148
8 Errors, Warnings, and Issues 149
The Different Kinds of EWIs 152
Upgrade Issues 152
Upgrade ToDos 153
Run-Time Warnings 154
Design Issues 154
Upgrade Notes and Global Warnings 155
Understanding the Upgrade Report 155
Estimating Fix Time 157
Working with EWIs 159
The Different Upgrade EWIs 160
Upgrade Issues 160
Upgrade ToDos 163
Upgrade Warnings 164
Design Errors 167
Global Warnings 169
Upgrade Notes 170
Which Problems Are Not Detected? 172
Conclusion 174
A04T61587x.fm Page ix Friday, November 16, 2001 10:17 AM

Table of Contents ix

9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 175
Where COM Interop Comes into Play 177
ActiveX Controls 177
Communication Between a .NET Client and a COM Server Component 177
Communication Between a COM Client and a .NET Server Component 178
Upgrading a Visual Basic 6 Client/Server Application 178
Creating a .NET Client That Talks to a COM Server 180
Debugging Between the Visual Basic .NET Client and Visual Basic 6 Server 182
Exposing a Visual Basic .NET Component to Be Called by a
Visual Basic 6 Client 184
Debugging Between the Visual Basic 6 Client and .NET Server 187
Tying It All Together 188
Replacing COM with .NET: Binary Compatibility 189
Indirect Replacement Model 190
Enabling Binary Compatibility in Visual Basic .NET Classes 191
Conclusion 196

Part III Getting Your Project Working


10 Ten Common Upgrade Problems 199
Default Properties 199
AddItem and ToString with COM Objects 201
Deterministic Finalization and Garbage Collection 203
Bringing a Little Determinism to the Party 205
Generic Objects (Control/Form/Screen) 206
Dim…As New 207
Sub Main (or Default Form) 208
Font Disparities 209
Bad Constants 212
Drag and Drop 213
Drag and Drop in Visual Basic 6 213
Drag and Drop in Visual Basic .NET 215
Collection Classes 219
Conclusion 222
A04T61587x.fm Page x Friday, November 16, 2001 10:17 AM

x Table of Contents

11 Resolving Issues with Language 223


Language Elements 224
#If…#End If Precompiler Statements 224
Constants and Constant Expressions 224
Control Flow 227
File Functions 232
Types and Type Operations 238
Object Replaces Variant 238
Arrays 242
Structures 245
Making Your Code Thread-Safe 249
Windows API 251
Type Changes 252
As Any No Longer Supported 253
AddressOf Changes 254
Passing User-Defined Types to API Functions 258
ObjPtr and StrPtr Not Supported 261
Conclusion 263
12 Resolving Issues with Forms 265
Similarities in Form Structure 265
General Issues 267
Differences in Properties, Methods, and Events 267
Technology Differences 269
Issues Involving Forms 274
Event Firing Differences 274
The Default Form: DefInstance 277
Application Lifetime and Forms 278
MDI Forms 282
Conclusion 283
13 Upgrading ActiveX Controls and Components 285
ActiveX Controls Are Still Supported—Yes! 285
ActiveX Upgrade Strategy 286
Limitations of ActiveX Control Hosting 286
ActiveX .NET Controls: Best of Both Worlds 289
ActiveX Interop Ax Wrapper: The Windows Forms Wrapper 289
Property and Parameter Type Mappings 290
A04T61587x.fm Page xi Friday, November 16, 2001 10:17 AM

Table of Contents xi

Standard Component Wrappers and ActiveX Control Subobjects 293


Common Exceptions That Require Type Conversions 295
Name Collisions 297
Event Name Collisions 297
Using ActiveX Components from .NET 298
When ByRef Bites 298
When a Collection Is Not a Collection 299
Nonzero-Bound Arrays 301
Alias Types Are Not Supported 301
Module Methods Are Not Supported 302
Conclusion 303
14 Resolving Data Access Issues 305
Data Access in Visual Basic 306
Code 306
Data Binding 307
ADO Data Environment 307
Components That Don’t Upgrade 308
ADO.NET Is the Future 309
General Issues with Data Access Code 309
DAO and RDO Module Methods 309
ADO Version 2.7 311
Errors in Events 311
RDO Connection 313
Null, vbNullString, and vbNullChar 314
ADO Data Environment 315
Calling Dispose 315
Initialize Event 316
Cursor Location with Microsoft Access Databases 316
ADO Data Binding 316
Control Arrays of ADO Data Controls 318
Setting Data Binding Properties at Run Time 318
Conclusion 321
15 Problems That Require Redesign 323
Replacing the OLE Container Control 323
Replacing Painting Functions 328
Rewriting Clipboard Code 331
A04T61587x.fm Page xii Friday, November 16, 2001 10:17 AM

xii Table of Contents

Using the Controls Collection 333


Using the Forms Collection 335
Upgrading PrintForm Code 337
Replacing Property Pages 340
Eliminating ObjPtr, VarPtr, and StrPtr 343
Conclusion 345
16 Upgrading COM+ Components 347
COM+ Application Types 347
Using COM+ in Visual Basic .NET 348
COM+ Requirements in Visual Basic .NET 350
Inheriting from the ServicedComponent Class 351
Working with Attributes 352
Creating a Strong Name for Your Assembly 356
Registering COM+ Applications 358
Upgrading COM+ Components 360
Making .NET and COM Components Work Together 364
Conclusion 364
17 Upgrading VB Application Wizard Projects 365
App.Revision 367
frmAbout Form 369
frmLogin Form 370
frmMain Form 370
API Declare Statements 370
mnuHelpAbout_Click Event Procedure 371
App.HelpFile 371
ActiveMdiChild in MDI Projects 372
Forms Collection in frmMain_Closed 374
Clipboard in MDI Projects 375
frmSplash Form 376
frmBrowser Form 378
Data Forms 378
Module1 Module 379
LoadResStrings Method 379
Conclusion 381
A04T61587x.fm Page xiii Friday, November 16, 2001 10:17 AM

Table of Contents xiii

Part IV Techniques for Adding Value


18 Adding Value to Your Applications 385
Overview of the Sample Application 386
New File Functions 387
Reading the Contents of a Directory 387
Finding All the Forms in a DLL 388
Loading Forms Dynamically 389
Reading and Writing to Files 389
Using Dynamic Properties 390
New Windows Capabilities 392
Accessing the Registry 392
Control Anchoring 394
Graphics Features 394
Windows XP–Style Controls 398
XCopy Deployment 400
Conclusion 401
19 Replacing ActiveX Controls with Windows Forms Controls 403
Benefits of Upgrading Controls 404
100 Percent .NET Compatibility 404
Improved Versioning 404
Simpler Deployment 405
Process of Replacing Controls 405
Manually Upgrading a Control 406
Mappings for Visual Basic 6 ActiveX Controls 412
ActiveX Controls vs. Windows Forms Controls 413
Conclusion 416
20 Moving from ADO to ADO.NET 417
ADO.NET for the ADO Programmer 417
Overview of ADO.NET 418
DataSets 419
Integrating Your ADO Code into a Visual
Basic .NET Application 421
Binding Your ADO Recordset to .NET Controls 422
Using ADO with XML Web Services 423
A04T61587x.fm Page xiv Friday, November 16, 2001 10:17 AM

xiv Table of Contents

Mapping ADO Objects to ADO.NET 425


Connection and Command Objects 425
Recordset s 427
Using DataViews 429
Data Binding 430
Binding to Windows Forms Controls 430
A Note About Performance 432
Conclusion 434
21 Upgrading Distributed Applications 435
Important Concepts for Distributed Applications 436
Loosely Coupled vs. Tightly Coupled Applications 436
Overhead in Method Invocation 437
Componentization and Logical Organization 439
Distributed Technologies in .NET 440
XML Web Services 440
Creating a Simple XML Web Service 441
Supporting Web Services in Your Existing Applications 446
Remoting 452
A Simple Remoting Example 453
Architecture for Remoting 456
Distributed COM+ Applications 461
COM+ and Remoting 461
Using SOAP Services 461
COM+ Application Proxies in .NET 463
Conclusion 464

Part V Appendixes
Appendix A Object Mapping Reference 467
Appendix B Function Mapping Reference 515
Index 521
A06I61587x.fm Page xvii Friday, November 16, 2001 10:07 AM

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.

What Is This Book About?


Many articles have appeared in the press about Visual Basic .NET being radi-
cally 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 applica-
tions? 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 com-
plete 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 rec-
ognize 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

xvii
A06I61587x.fm Page xviii Friday, November 16, 2001 10:07 AM

xviii Introduction

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 Wiz-
ard, 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+ ser-
vices; how to get your ActiveX controls and COM components to
work; and how to deal with issues common to VB Application Wiz-
ard 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 warn-
ings that the Upgrade Wizard generates. Chapter 19 maps the Microsoft Win-
dows 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.

Who Should Read This Book


This book is written for the Visual Basic developer who wants to upgrade appli-
cations 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 ear-
lier 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 exam-
ple, the chapter on fixing problems with VB Application Wizard projects will
probably appeal to more intermediate developers, whereas the one on upgrad-
ing COM+ services will be of interest to highly experienced developers.
Two areas of upgrading that we don’t discuss are user controls and Web-
Classes. As this book went to press, Microsoft had not yet added this function-
ality 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
A06I61587x.fm Page xix Friday, November 16, 2001 10:07 AM

Introduction xix

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.

Using the Code Samples and CD


Throughout the text, we refer to code samples both in the book and on the
companion CD. To get the most out of this book, make sure you try these sam-
ples out for yourself. To do this, we recommend that you use a computer with
the following system configuration:

■ Pentium III, 500 MHz or faster


■ 256 MB of RAM or more
■ CD-ROM or DVD drive
■ Microsoft Windows NT 4, Windows 2000, or Windows XP
■ Microsoft Visual Basic 6, with Service Pack 5 or later
■ 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:
MsgBox “Hello from Visual Basic 6”

Visual Basic .NET samples are in boldface type:

MsgBox “Hello from Visual Basic .NET”

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 com-
plete upgrade guide at your fingertips.
A06I61587x.fm Page xx Friday, November 16, 2001 10:07 AM

xx Introduction

Let’s Begin
We hope you enjoy reading this book and, more importantly, we hope it helps
you with upgrading applications to Visual Basic .NET. We’ve aimed to keep
every chapter as self-contained as possible, so if a chapter is not relevant to
what you’re doing at the moment, feel free to jump forward to a later chapter
that is more appropriate. Where should you begin? We recommend that you
start by reading Chapter 1, as it lays much of the groundwork for the rest of the
book and introduces you to the core concepts you will need to upgrade Visual
Basic 6 to Visual Basic .NET.

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.
C0161587x.fm Page 1 Thursday, November 15, 2001 1:59 PM

Part I

Introduction to
Upgrading
1 Visual Basic .NET Is More Than Visual Basic 6 + 1 3
2 Visual Basic 6 and Visual Basic .NET: Differences 19
3 Upgrading Options 45
4 Preparing Your Project for the Upgrade to Visual Basic .NET 61
C0161587x.fm Page 2 Thursday, November 15, 2001 1:59 PM
C0161587x.fm Page 3 Thursday, November 15, 2001 1:59 PM

Visual Basic .NET Is More


Than Visual Basic 6 + 1
If you’re familiar with Visual Basic but aren’t familiar with Visual Basic .NET,
you may be wondering why we have written a book on upgrading. Surely
Visual Basic .NET will open Visual Basic 6 projects as effortlessly as Visual Basic
6 opens Visual Basic 5 projects. “How different can Visual Basic .NET be?” you
might ask. So before we start discussing the details of upgrading, let’s clear up
any confusion: Visual Basic .NET, the latest version of Visual Basic, is not
merely Visual Basic 6 with a few new features added on. Instead, Visual Basic
has been thoroughly redesigned and restructured. The language has been mod-
ernized, with new, richer object models for data, forms, transactions, and
almost everything else. The file formats have also changed.
Unfortunately, these changes mean that Visual Basic .NET is not entirely
backward compatible with Visual Basic 6. Projects from previous versions need
to be upgraded before they will compile and run in Visual Basic .NET. The
Upgrade Wizard handles much of this work for you, but most real-world
projects will require additional modifications before they can be run. Some peo-
ple consider moving applications from Visual Basic 6 to .NET to be a migration
rather than an upgrade, but the changes in the language are a logical step, and
they make Visual Basic more powerful than ever before.
This is an exciting time for Visual Basic developers. Sure, upgrading
applications takes some effort, but on the other hand Visual Basic .NET is
incredibly capable, extending Visual Basic’s Rapid Application Development
(RAD) model to the server and to the Web. Visual Basic .NET adds more fea-
tures to Visual Basic than did Visual Basic 2, 3, 4, 5, and 6 combined.
Microsoft made the changes because the focus of the language has shifted
from previous versions. Whereas Visual Basic 6 was primarily a Windows

3
C0161587x.fm Page 4 Thursday, November 15, 2001 1:59 PM

4 Part I Introduction to Upgrading

development tool, Visual Basic .NET is designed to leverage the .NET plat-
form, enabling the user to create Windows applications, console applications,
class libraries, NT services, Web Forms applications, and XML Web services—
all while allowing seamless integration with other programming languages.
Let’s look a little deeper at Visual Basic .NET to see where it differs from
Visual Basic 6. We will look at the following three issues:

■ The development environment


■ The syntax of the language and object models of the classes
■ The run-time behavior of the compiled components

In each of these three areas, Visual Basic .NET departs from the conventions of
Visual Basic 6. First, the integrated development environment (IDE) has been
redesigned to house all of the Visual Studio languages: Visual Basic, C#, and
Visual C++. Second, the language itself has been modernized, removing some
keywords, such as GoSub; adding new keywords, like Inherits; and changing
the meaning of other keywords, like Return and Integer. Finally, the run-time
behavior of the compiled components is different. .NET applications are free-
threaded; Visual Basic .NET projects are compiled to “assemblies” rather than to
familiar Win32 applications, and variables and objects are garbage-collected,
meaning that they lack a deterministic lifetime. A noticeable effect of this last
change is that the class Finalize event is not triggered until sometime after the
object is actually destroyed. We will talk more about these differences and
about what .NET is in Chapter 2. For now, let’s continue to look at what the
changes mean to the Visual Basic developer.
With so much that is new, how familiar will it all be to traditional Visual
Basic users? To what extent can you leverage your existing Visual Basic skills
when you move to Visual Basic .NET? To answer these questions, let’s take a
quick look at the history of Visual Basic.
In 1991, Microsoft released Visual Basic 1, which opened the doors to
Windows RAD. Visual Basic 1 was an instant success, and it’s easy to see why.
Before Visual Basic, developers had to write WndProc handlers, work with
pointers, and know when to apply the Pascal calling convention to methods.
Visual Basic took over the handling of all of these details, allowing developers
to concentrate on building business objects instead of writing the basic plumb-
ing in every program.
In Visual Basic versions 2 though 6, Microsoft kept the underlying archi-
tecture of the product the same and simply added new features. Visual Basic 2
and 3 introduced the property grid, Data Access Objects (DAO) database pro-
gramming, and object linking and embedding (OLE), resulting in a great set of
features for Windows 3.1 programming. In 1995, Microsoft released Visual Basic 4,
C0161587x.fm Page 5 Thursday, November 15, 2001 1:59 PM

Chapter 1 Visual Basic .NET Is More Than Visual Basic 6 + 1 5

which enabled developers to write 32-bit EXEs, ActiveX controls, and class
libraries. The year 1995 also saw the explosion of the Internet, and people
began wanting to build Web sites. With versions 5 and 6, Visual Basic added its
own flavor of Web development—WebClasses, ActiveX documents, and
Dynamic HTML (DHTML) pages—yet for the most part, it still remained a Win-
dows development tool.
It’s interesting to compare Visual Basic 1 with Visual Basic 6 to see how far
the language has come. Visual Basic 1 had no IntelliSense, no support for open
database connectivity (ODBC), no classes, limited debugging, no support for
COM components, no Property Browser, no Web development features, and it
created only EXEs. Visual Basic 6 had come a long way from the basic forms
and modules development of version 1, yet it still had the spirit of Visual Basic.
Visual Basic .NET also has that spirit: It has the same human-readable language,
case insensitivity, support for late binding, automatic coercions, and familiar
Visual Basic keywords, functions, and constructs, like Left$, MsgBox, and
On…Error…GoTo. If you’re a Visual Basic 6 programmer, it’s a comfortable
step to Visual Basic .NET. Yes, there are new concepts to learn, but your exist-
ing knowledge of Visual Basic is a great foundation.
After the release of Visual Basic 6, Microsoft was faced with a challenge.
Developer needs were changing. More and more programmers were develop-
ing for the Web, and the Web development capabilities built into Visual Basic 6
were not addressing their needs. Visual Basic’s DHTML pages and ActiveX
documents were client-side technologies, meaning that both the component
and the Visual Basic runtime had to be installed on client machines. Visual
Basic’s WebClasses, a server-based technology, stored state on the server and
wasn’t scalable. In addition, the design experience for both WebClasses and
DHTML pages could only be described as rudimentary! In short, the technolo-
gies were too limiting. Internet developers wanted “thin” clients, not Visual
Basic downloads. They wanted code that ran on the server. They wanted security,
and they needed scalability, since the more successful a Web site was, the more
people would use it concurrently, and therefore the more capacity it had to have.
Clearly, a better architecture was needed. Programmers had also been ask-
ing for some significant new language features: inheritance, easier access to the
underlying platform, and a solution for the many different component version-
ing problems that had collectively been labeled “DLL hell.” When looking for a
solution to these problems, Microsoft also saw the opportunity to create a uni-
fied framework for developing applications. To understand why such a frame-
work was desirable, consider that developers wanting to create forms for
Windows in Visual Basic 6, Visual C++, and Microsoft Office Visual Basic for
C0161587x.fm Page 6 Thursday, November 15, 2001 1:59 PM

6 Part I Introduction to Upgrading

Applications (VBA) had to learn a different forms package for each language. If
only there was a common forms package for all these products. Life would be
so much simpler! This objective and others led Microsoft to develop a common
framework available to all .NET languages.
One side effect of giving all languages access to a common framework is
that each language must support the same data types. This support prevents the
sort of headaches familiar to anyone who has tried to use Windows APIs from
Visual Basic 6. Once all languages support the same data types, it’s simple to
add cross-language interoperability: inheritance, debugging, security access,
and an integrated compilation process. As you can see, the benefits of such a
system would be amazing—and that system is exactly what the .NET platform
is: a multiple-language system with a common forms package, set of base
classes, and data types. For Visual Basic to be part of this revolution meant
more than just changing the language—it meant reconceptualizing it from the
ground up for the .NET platform.

Why Break Compatibility?


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.

Adding New Features


Some of the new features in Visual Basic .NET could not have been added with-
out a redesign. Adding visual inheritance and accessibility support to the forms
package required redesigning the forms object model. Adding Interface state-
ments 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 inte-
grate Visual Basic with the .NET platform. Cross-language inheritance, debug-
ging, 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. Rede-
signing the Web and data access classes to be more scalable meant even more
changes from Visual Basic 6.
C0161587x.fm Page 7 Thursday, November 15, 2001 1:59 PM

Chapter 1 Visual Basic .NET Is More Than Visual Basic 6 + 1 7

Fixing the Language


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. Con-
sider the following Visual Basic 6 example, where Form1 is a form in the cur-
rent 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:
Dim c1 As New Class1
Dim c2 As Class1: Set c2 = New Class1

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
Dim c2 As Class1: Set c2 = New Class1

is that c2 is created once. If c2 is set to Nothing, it will not be re-created auto-


matically 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.
C0161587x.fm Page 8 Thursday, November 15, 2001 1:59 PM

8 Part I Introduction to Upgrading

Modernizing the Language


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 key-
word Type has been changed to Structure. Some of these changes we can prob-
ably 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.

It Is Still Visual Basic


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.

Expect Subtle Differences


Visual Basic .NET has been rebuilt for the .NET platform. What does this state-
ment 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 pre-
vious 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 prin-
ciple of upgrading: Visual Basic .NET is subtly different from Visual Basic 6, and
therefore upgraded applications will be different from their Visual Basic 6 coun-
terparts in subtle ways. In most cases you will not notice the difference, yet it’s
C0161587x.fm Page 9 Thursday, November 15, 2001 1:59 PM

Chapter 1 Visual Basic .NET Is More Than Visual Basic 6 + 1 9

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.

The Decision to Break Compatibility


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 sup-
port 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 concen-
trate on ensuring that people could upgrade their projects from Visual
Basic 6 to Visual Basic .NET.

Plan for a 95 Percent Automated Upgrade


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 ele-
ment 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 origi-
nal 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 oth-
ers, 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 dis-
cusses how to design your Visual Basic 6 applications to make the upgrade pro-
cess much smoother.
C0161587x.fm Page 10 Thursday, November 15, 2001 1:59 PM

10 Part I Introduction to Upgrading

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. Chap-
ter 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.

Why Should I Upgrade?


If it requires work to upgrade your applications from Visual Basic 6 to Visual
Basic .NET, you may wonder, “Is upgrading worth the trouble? Why should I
bother to upgrade an application that requires modifications when it works in
Visual Basic 6 today?” The main reason for upgrading is to take advantage of
the new features of Visual Basic .NET. What are these new features? Listing
them all would be a book in itself. The following sections discuss some of the
features that people commonly add to their upgraded applications.

New Language Features


Visual Basic .NET adds a number of new language features that make the lan-
guage more powerful and will forever dispel the myth that Visual Basic is a
“toy” programming language.

Inheritance
For years we developers had been asking Microsoft to add “real” inheritance to
Visual Basic. Sure, Visual Basic 6 supports interface inheritance, but we wanted
more; we wanted implementation inheritance. We wanted to benefit from code
reuse and to truly implement object-oriented designs. Visual Basic .NET fully
supports inheritance, via the new Inherits keyword. You can inherit classes
from within your own application, from other applications, and from .NET com-
ponents written in other languages. You can even use inheritance in forms to
inherit the layout, controls, and code of another form. This is called visual
inheritance. The following code illustrates the use of the Inherits keyword:
C0161587x.fm Page 11 Thursday, November 15, 2001 1:59 PM

Chapter 1 Visual Basic .NET Is More Than Visual Basic 6 + 1 11

Public Class BaseClass


End Class

Public Class InheritedClass : Inherits BaseClass


End Class

Interfaces in Code
Along with “real” inheritance, Visual Basic .NET still supports interface inherit-
ance and improves on it by providing the Interface keyword. The Interface
keyword defines the interfaces in code. Your classes then implement the inter-
faces, as in the following example:

Interface myInterface
Function myFunction()
End Interface
Public Class myImplementedClass
Implements myInterface
Function myFunction() _
Implements myInterface.myFunction
'Some Code
End Function
End Class

Structured Exception Handling


In addition to supporting the familiar On Error GoTo error catching, Visual
Basic .NET provides a Try...Catch...End Try exception-handling block that adds
error handling. This construct allows you to embed code within an error-handling
block. A great use for this type of block is to create a global error handler for your
application by including a Try…Catch block in the startup object such as Sub
Main. In the following example, Sub Main opens a new instance of Form1 and
will catch and report any errors that are thrown anywhere in the application:

Sub Main()
Try
Windows.Forms.Application.Run( _
New Form1())
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
C0161587x.fm Page 12 Thursday, November 15, 2001 1:59 PM

12 Part I Introduction to Upgrading

Arithmetic Operator Shortcuts


All arithmetic operators in Visual Basic .NET now have shortcuts that let you
operate on and assign the result back to a variable. For example, in Visual Basic
6, you might write
Dim myString As String
myString = myString & "SomeText"

In Visual Basic .NET, you can write this in a much more elegant format:

Dim myString As String


myString &= "SomeText"

These expression shortcuts apply to &=, *=, +=, -=, /=, \=, and ^=. Note
that you can also use the old Visual Basic 6–style expressions.

Overloaded Functions
Visual Basic .NET introduces function overloading. With overloading, you can
declare multiple functions with the same name, each accepting a different num-
ber of parameters or accepting parameters of different types. For example, sup-
pose that you have a method that deletes a customer from a database. You
might want to create two versions of the deleteCustomer method, one to delete
a customer based on ID and one to delete a customer by name. You can do so
in Visual Basic .NET as follows:

Sub deleteCustomer(ByVal custName As String)


'Code that accepts a String parameter

End Sub
Sub deleteCustomer(ByVal custID As Integer)
'Code that accepts an Integer parameter
End Sub

Attributes
Visual Basic .NET also now includes attributes. Attributes give one the ability to
fine-tune how an application behaves. They modify the behavior of code ele-
ments and can be applied to methods, classes, interfaces, and the application
itself. You can use attributes to explicitly declare the GUID for a class or to
define how a variable should be marshaled when it is passed to a COM object.
Suppose, for example, that you have written a common utility function that you
want the debugger always to step over, rather than step into. The DebuggerHid-
den attribute allows you to do this:

<DebuggerHidden()> _
Function nToz(ByVal input) As Integer
If Not IsNumeric(input) Then input = 0
Return Input
End Function
C0161587x.fm Page 13 Thursday, November 15, 2001 1:59 PM

Chapter 1 Visual Basic .NET Is More Than Visual Basic 6 + 1 13

Multithreading
By default, your Visual Basic .NET applications are single threaded, but the lan-
guage has new keywords that allow you to spawn new threads. This ability can
be very useful if you have processes that take a long time to complete and that
can run in the background. The following example creates a new thread and
uses it to run the subroutine loadResultsFromDatabase:

Sub Main()
Dim myThread As New Threading.Thread( _

AddressOf loadResultsFromDatabase)
mythread.Start()
End Sub
Sub loadResultsFromDatabase()
'Some Code
End Sub

Reduced Programming Errors


Visual Basic .NET helps you reduce programming errors by supporting stronger
type checking. For example, if you use the wrong enum value for a property or
you assign a variable type to an incompatible type, the compiler will detect and
report it at design time. With ADO.NET, you can add strongly typed datasets to
your application, and if you refer to an invalid field name, it will be picked up
as a compile error instead of a run-time error. These features allow you to catch
errors as you write your program. For the ultimate in strong type checking, you
can use Option Strict On in your application, which prohibits late binding and
ensures that you use conversion functions whenever you assign a variable from
one type to another. This feature can be useful in applications that don’t use
late binding, but it enforces a stricter and more verbose coding standard than
many developers are used to.

The .NET Framework


In addition to the familiar VBA library of functions, such as Left$, Right$, and
Command$, and the Win32 APIs, Visual Basic .NET has access to the .NET
Framework, which is designed specifically for Visual Basic, C#, and the other
.NET languages. The .NET Framework is a collection of more than 3800 classes
and 28,000 methods for forms, graphics, XML, Internet development, file
access, transactions, and almost everything else you can think of. The set of
.NET classes you will most likely become familiar with first is the new forms
package—Windows Forms—which looks a lot like the familiar Visual Basic 6
forms but which is implemented as a set of .NET classes available to all lan-
guages. The next section describes the features of this package.
C0161587x.fm Page 14 Thursday, November 15, 2001 1:59 PM

14 Part I Introduction to Upgrading

Windows Forms
Windows Forms is a new forms development system that replaces the old
Visual Basic forms. Along with features that make it powerful and easy to use,
Windows Forms is available to all Visual Studio .NET languages.

Faster Development
Windows Forms in Visual Basic .NET has several features that speed up devel-
opment. An in-place menu editor and visual editing of tab orders make form
design easier. Control anchoring allows you to remove all your old resizing
code and instead visually anchor the controls so that they remain a fixed length
from the edge of the form and resize whenever the form resizes. Visual inher-
itance allows you to inherit the controls, properties, layout, and code of a base
form. A good use for this feature is to define a standard form layout for an
application, with a standard size, header, footer, and close button. You can then
inherit all forms from this standard form.

GDI+
GDI+ allows you to add rich visual effects to your application. For example, to
make a form semitransparent, you would place the following line of code in the
form load event:

Me.Opacity = 0.5

Figure 1-1 shows the effects of visual inheritance and GDI+ semitranspar-
ency in a Windows application.

F01km01

Figure 1-1 Visual inheritance and GDI+ semitransparency.


C0161587x.fm Page 15 Thursday, November 15, 2001 1:59 PM

Chapter 1 Visual Basic .NET Is More Than Visual Basic 6 + 1 15

Internationalization
Windows Forms has built-in support for internationalization. To add support for
other languages, you set the Localizable property of the form to True, set the
Language property to the desired language, and then change the Font and Size
properties of the form and controls. Every change that you make is saved as
specific to the current locale. For example, you can have different-sized con-
trols with different text for both Spanish and English.

New Web Development Features


Visual Basic .NET offers many enhancements to Web development. Two of the
most significant involve XML and Web Forms.

Better Support for XML


Visual Basic .NET has designers that allow visual editing of HTML documents,
XML documents, and XML schemas. In addition, there are .NET Framework
classes that support serializing and deserializing any .NET class to and from
XML. Visual Basic .NET can create XML Web services that use HTTP to pass
XML backward and forward to other applications. If your application uses XML,
Visual Basic .NET has great support for it. If you’re looking to add XML support
to your application (or to learn how to use XML), Visual Basic .NET is a great
tool for doing so.

Web Services and Web Forms


Visual Basic .NET allows you to add Web services to your application. As you
will see later in this book, in many cases you can actually convert your business
objects to Web services. You can also easily add a Web Forms presentation layer
that leverages your Visual Basic 6 or upgraded business objects. Visual Basic
.NET makes Web development as easy as Windows development.

Better Development Environment


Along with language, form, and Web development enhancements, the IDE in
Visual Basic .NET has a number of new features.

Cross-Language Interoperability
Visual Basic .NET is designed for cross-language interoperability. Because .NET
unifies types, controls and components written in one language can easily be
used in another. Anyone who has struggled to get a Visual Basic 6 user control
to work in a Visual C++ project will immediately recognize the benefits of this.
You can inherit from a class written in any other language built on the .NET
platform or create components in other languages that inherit from your Visual
Basic classes. Visual Studio .NET provides a single environment for developing
C0161587x.fm Page 16 Thursday, November 15, 2001 1:59 PM

16 Part I Introduction to Upgrading

and compiling multilanguage applications. Using the unified debugger, you can
step from a component written in one language into another. You can also
debug COM+ services and step into SQL Server stored procedures.

Background Compiler and Task List


Visual Basic .NET has a compiler that continually works in the background.
Compilation errors are flagged in code in real time with blue squiggle under-
lines. The Task List updates in real time with the list of compilation errors, and
it also shows any ToDo comments you add to your code. ToDo comments are
a great way to keep track of what you still need to do. For example, if you add
a button to a form, and plan to finish the click code later, you can add a com-
ment like

'TODO: Finish the code later

and the statement appears in the Task List. You can filter the Task List by task
type and even choose what sort of comments are shown using the Task List
pane. Figure 1-2 shows the Visual Basic .NET Task List.

F01km02

Figure 1-2 ToDo comments in the Task List.

Is Visual Basic Still the Best Choice for Visual Basic Developers?
If you are faced with learning the new features of Visual Basic .NET, you may
ask yourself, “Why should I stick with Visual Basic? Why don’t I choose another
language instead? Why not move to C# (a new language in Visual Studio .NET
C0161587x.fm Page 17 Thursday, November 15, 2001 1:59 PM

Chapter 1 Visual Basic .NET Is More Than Visual Basic 6 + 1 17

derived from C++)?” Although the choice is yours, we should point out that
Visual Basic .NET is now as powerful as C#, Visual C++, or any other language.
All the .NET languages have access to the same .NET Framework classes. These
classes are powerful, and they allow Visual Basic .NET to smash through the
“glass ceiling” of previous versions.
Visual Basic .NET also keeps the spirit of Visual Basic alive. It is designed
by Visual Basic developers for Visual Basic developers, whereas a language like
C# is designed for C developers. Each language has some unique features that
are not available in other languages. Let’s compare C# with Visual Basic to see
what makes each language unique.

Features Found in C# but Not in Visual Basic .NET


The C# language supports pointers. Pointers allow you to write “unsafe” code
that modifies memory locations directly. The following code shows how to use
pointers in C#:
unsafe static void Main(string[] args)
{
int myInt = 5;
int * myptr = & myInt;
* myptr = 55;
Console.WriteLine(myInt.ToString() );
}

Although Visual Basic .NET doesn’t support pointers in the language itself,
as you will see in Chapter 16, you can still access memory locations using meth-
ods of the .NET Garbage Collector class. C# also supports document comments
that allow you to embed self-documenting comments in your source code.
These comments are compiled into the metadata of the component and can be
extracted and built into help files.

Features Found in Visual Basic .NET but Not in C#


Visual Basic .NET’s most appealing feature is the human-readable Visual Basic
language, which is case insensitive with great IntelliSense. If you declare a vari-
able as MyVariable and then later change the case to myVariable, Visual Basic
.NET automatically changes the case of all occurrences in the code. C# and
Visual C++ don’t do this. In fact, C# treats MYVariable and myVariable as two
separate variables. Most Visual Basic programmers have grown to know and
become comfortable with case-insensitive behavior and will find Visual Basic
.NET the most natural language to use.
Visual Basic .NET also supports late binding and optional parameters. C#
supports neither of these. In addition, Visual Basic supports automatic coer-
cions between types. For example, in C#, you cannot assign a Long to an Inte-
ger without using a conversion method (since it may cause an overflow). Visual
C0161587x.fm Page 18 Thursday, November 15, 2001 1:59 PM

18 Part I Introduction to Upgrading

Basic allows narrowing conversions like this one, since in most cases overflows
don’t occur. If you want to prevent automatic coercions, you can use a new
compiler option, Option Strict On, that enforces C-like strict type coercion.
Visual Basic has richer IntelliSense and better automatic formatting than
any other language. It automatically indents code, corrects casing, and adds
parentheses to functions when you press Enter.
The result is a true Visual Basic experience, enhanced by background
compilation as you type. For example, if you misspell the keyword Function, as
in

Funtion myFunction

as soon as you move off the line, the compiler parses it and puts a compiler
error in the Task List. It also underlines the word “Funtion” with a blue squiggle
indicating the location of the compile error. As soon as you correct the line, the
compiler removes the Task List item and erases the underline. Background
compilation helps you write better code and is unique to Visual Basic—no
other language has a background compiler.
The language you use is a matter of choice. However, if you enjoy pro-
gramming in Visual Basic, you will find Visual Basic .NET a great experience
and the best upgrade ever.

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 differ-
ences 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 fur-
ther 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 walk-
through of upgrading an application. Welcome to Visual Basic .NET, the future of
Visual Basic.
C0261587x.fm Page 19 Thursday, November 15, 2001 2:02 PM

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 develop-
ment 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.

.NET Framework vs. ActiveX


As a Visual Basic developer, you will normally not be concerned with the run-
time systems that underlie your Visual Basic applications. Visual Basic 6, for
example, makes the details of how ActiveX works largely transparent. The
Visual Basic 6 runtime handles all of the messy details that come with imple-
menting an ActiveX-compliant component or application. Licensing, persistable
objects, Microsoft Transaction Server (MTS) transaction awareness, and binary

19
C0261587x.fm Page 20 Thursday, November 15, 2001 2:02 PM

20 Part I Introduction to Upgrading

compatibility are exposed as simple settings that you can turn on or off. In the
same vein, Visual Basic .NET does a good job of hiding the details of what hap-
pens under the hood. For example, you do not need to know that you are cre-
ating or using a .NET component. A .NET component is just like any other
component. It has properties, methods, and events just as an ActiveX compo-
nent does. Why should you care about the differences between ActiveX and
.NET if everything basically looks the same?
On the surface, it doesn’t matter whether you’re using ActiveX, .NET, or
your best friend’s component model—they all look about the same. When you
dig into the details, however, you need to understand the machine that lies
beneath.
If you have ever created an ActiveX control in Visual Basic 6, you may
have found that it behaves slightly differently from other ActiveX controls that
you bought off the shelf. For example, if you add a BackColor property to your
control, you’ll notice when you test it that the color picker is not associated with
your control. Digging deeper, you’ll find that you need to change the type of
the property to OLE_COLOR and set the Property ID attribute on the property to
BackColor. Only then will the property behave like a BackColor property. In
solving this problem, you needed to cross over from pure Visual Basic into the
world of ActiveX. Although Visual Basic attaches different terminology to
options and language statements, you end up being directly or indirectly
exposed to ActiveX concepts such as dispatch IDs (DISPIDs), what Visual Basic
refers to as property IDs, and OLE types such as OLE_COLOR. Visual Basic, as
much as it tries, cannot hide this from you. The more properties, events, methods,
and property pages you add to your Visual Basic 6 ActiveX control, the more
problems you encounter that require an ActiveX-related solution.
Visual Basic .NET works in much the same way. Most of the time, you are
just dealing with Visual Basic. However, when you need your application or
component to behave consistently with other types of applications, whether
they be standard Windows applications or Web service server objects, you will
need a detailed understanding of the environment in which you want your
application to run. In the case of .NET applications, you will need to under-
stand how .NET works. The more you know about the target environment, the
better equipped you are to create a component or application that behaves well
in that environment. So let’s dig a bit and uncover the machine that will run
your upgraded Visual Basic .NET application: the .NET Framework.
C0261587x.fm Page 21 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 21

.NET Framework
The .NET Framework is composed of two general parts: the common language
runtime and the Framework class library. The runtime is the foundation upon
which the .NET Framework is based. It provides the basic services on which all
.NET applications depend: code execution, memory management, thread man-
agement, and code security. The Framework class library provides building
blocks for creating a variety of .NET applications, components, and services.
For example, the Framework class library contains the base classes for creating
ASP.NET Web applications, ASP.NET XML Web services, and Windows Forms. It
defines all of the value types, known as System types, such as Byte, Integer,
Long, and String. It gives you complex structure classes such as Collection and
HashTable, as well as the interfaces such as ICollection and IDictionary so you
can define your own custom implementation of a standard Collection or Hash-
Table class.
The .NET Framework as a whole, since it works across all .NET languages,
can be thought of as an expanded version of the Visual Basic 6 runtime. The
common language runtime corresponds to the Visual Basic Language Runtime
in Visual Basic 6, which includes the byte code interpreter and memory man-
ager. The counterparts of the .NET Framework class library in Visual Basic 6
include the Visual Basic forms package, the Collection object, and global
objects such as App, Screen, Printer, and Clipboard.
The main difference between the two environments is that Visual Basic 6
is a closed environment, meaning that none of the intrinsic Visual Basic types,
such as Collection, App, Screen, and so on, can be shared with other language
environments, such as C++. Likewise, Microsoft Visual C++ is largely a self-con-
tained language environment that includes its own runtime and class libraries,
such as MFC and ATL. The MFC CString class, for example, is contained within
the MFC runtime and is not shared with other environments such as Visual Basic.
In closed environments such as these, you can share components
between environments only when you create them as ActiveX components, and
even then there are a number of limitations. ActiveX components need to be
designed and tested to work in each target environment. For example, an
ActiveX control hosted on a Visual Basic 6 form may work wonderfully, but the
same control may not work at all when hosted on an MFC window. You then
need to add or modify the interfaces or implementation of your ActiveX com-
ponent to make it work with both the Visual Basic 6 and MFC environments. As
a result, you end up duplicating your effort by writing specialized routines to
make your ActiveX component work in all target environments.
The .NET Framework eliminates this duplication by creating an environ-
ment in which all languages have equal access to the same broad set of .NET
types, base classes, and services. Each language built on the .NET Framework
C0261587x.fm Page 22 Thursday, November 15, 2001 2:02 PM

22 Part I Introduction to Upgrading

shares this common base. No matter what your language of choice is—Visual
Basic .NET , C#, or COBOL (for .NET)—the compiler for that language gener-
ates exactly the same set of .NET runtime instructions, called Microsoft Interme-
diate Language (MSIL). With each language distilled down to one base
instruction set (MSIL), running against the same runtime (the .NET common lan-
guage runtime), and using one set of .NET Framework classes, sharing and con-
sistency become the norm. The .NET components you create using any .NET
language work together seamlessly without any additional effort on your part.
Now that you have seen some of the differences between the Visual
Basic 6 ActiveX-based environment and the Visual Basic .NET environment,
let’s focus on various elements of the .NET Framework and see how each ele-
ment manifests itself in Visual Basic .NET. The elements we will be looking at
are memory management, type identity, and the threading model. Each of these
areas will have a profound impact on the way you both create new Visual Basic
.NET applications and revise upgraded Visual Basic 6 applications to work with
Visual Basic .NET.

Memory Management
Visual Basic .NET relies on the .NET runtime for memory management. This
means that the .NET runtime takes care of reserving memory for all Visual Basic
strings, arrays, structures, and objects. Likewise, the .NET runtime decides when
to free the memory associated with the objects or variables you have allocated.
This is not much different from Visual Basic 6, which was also responsible for
managing the memory on your behalf. The most significant difference between
Visual Basic 6 and Visual Basic .NET in terms of memory management involves
determining when an object or variable is freed.
In Visual Basic 6, the memory associated with a variable or object is freed
as soon as you set the variable to Nothing or the variable falls out of scope. This
is not true in Visual Basic .NET. When a variable or object is set to Nothing or
falls out of scope, Visual Basic .NET tells the .NET runtime that the variable or
object is no longer used. The .NET runtime marks the variable or object as
needing deletion and relegates the object to the Garbage Collector (GC). The
Garbage Collector then deletes the object at some arbitrary time in the future.
Because we can predict when Visual Basic 6 will delete the memory asso-
ciated with a variable, we refer to the lifespan of a variable in that language as
being deterministic. In other words, you know the exact moment that a vari-
able comes into existence and the exact moment that it becomes nonexistent.
The lifespan of a Visual Basic .NET variable, on the other hand, is indetermin-
istic, since you cannot predict exactly when it will become nonexistent. You
can tell Visual Basic .NET to stop using the variable, but you cannot tell it when
to make the variable nonexistent. The variable could be left dangling for a few
C0261587x.fm Page 23 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 23

nanoseconds, or it could take minutes for the .NET Framework to decide to


make it nonexistent. In the meantime, an indeterminate amount of your Visual
Basic code will execute.
In many cases it does not matter whether or not you can predict when a
variable or object is going to be nonexistent. For example, a simple variable
such as a string or an array that you are no longer using can be cleaned up at
any time. It is when you are dealing with objects that things get interesting.
Take, for example, a File object that opens a file and locks the file when
the File object is created. The object closes the file handle and allows the file to
be opened by other applications when the object is destroyed. Consider the fol-
lowing Visual Basic .NET code:

Dim f As New File


Dim FileContents As String
f.Open(“MyFile.dat”)
FileContents = f.Read(“MyFile.dat”)
f = Nothing
FileContents = FileContents & “ This better be appended to my file! “
f.Open(“MyFile.dat”)
f.Write(FileContents)
f = Nothing

If you run this application in Visual Basic 6, it will run without error. How-
ever, if you run this application in Visual Basic .NET, you will encounter an
exception when you attempt to open the file the second time. Why? The file
handle associated with MyFile.dat will likely still be open. Setting f to Nothing
tells the .NET Framework that the File object needs to be deleted. The runtime
relegates the object to the garbage bin, where it will wait until the Garbage Col-
lector comes along to clean it up. The File object in effect remains alive and
well in the garbage bin. As a result, the MyFile.dat file handle is still open, and
the second attempt to open the locked file will lead to an error.
The only way to prevent this type of problem is to call a method on the
object to force its handle to be closed. In this example, if the File object had a
Close method, you could use it here before setting the variable to Nothing. For
example,

f.Close
f = Nothing

Dispose: Determinism in the Face of Chaos


Despite all of the benefits that a garbage-collected model has to offer, it has one
haunting side effect: the lack of determinism. Objects can be allocated and
deleted by the hundreds, but you never really know when or in what order
C0261587x.fm Page 24 Thursday, November 15, 2001 2:02 PM

24 Part I Introduction to Upgrading

they will actually terminate. Nor do you know what resources are being con-
sumed or locked at any given moment. It’s confusing, even chaotic. To add
some semblance of order to this system, the .NET Framework offers a mecha-
nism called Dispose to ensure that an object releases all its resources exactly
when you want it to. Any object that locks resources you need or that otherwise
needs to be told to let go should implement the IDisposable interface. The IDispos-
able interface has a single method, Dispose, that takes no parameters. Any client
using the object should call the Dispose method when it is finished with the object.

One More Thing to Worry About


If you’ve been using Visual Basic 6, you’re not accustomed to calling Dispose
explicitly on an object when you write code. Unfortunately, when it comes to
Visual Basic .NET, you will have to get accustomed to doing so. Get in the habit
now of calling Dispose on any object when you are done using it or when the
variable referencing it is about to go out of scope. If we change the File object
shown earlier to use Dispose, we end up with the following code:

Dim f As New File


Dim FileContents As String
f.Open(“MyFile.dat”)
FileContents = f.Read(“MyFile.dat”)
f.Dispose
f = Nothing
FileContents = FileContents & “ This better be appended to my file! “
f.Open(“MyFile.dat”)
f.Write(FileContents)
f.Dispose
f = Nothing

Note The Visual Basic Upgrade Wizard does not alert you to cases
in which you may need to call Dispose. We advise you to review your
code after you upgrade to determine when an object reference is no
longer used. Add calls to the object’s Dispose method to force the
object to release its resources. If the object—notably ActiveX objects
that do not implement IDisposable—does not support the Dispose
method, look for another suitable method to call, such as Close. For
example, review your code for the use of ActiveX Data Objects (ADO)
such as Recordset and Connection. When you are finished with a
Recordset object, be sure to call Close.
C0261587x.fm Page 25 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 25

When You Just Want It All to Go Away


While your application runs, objects that have been created and destroyed may
wait for the Garbage Collector to come and take them away. At certain points
in your application, you may need to ensure that no objects are hanging
around locking or consuming a needed resource. To clean up objects that are
pending collection, you can call on the Garbage Collector to collect all of the
waiting objects immediately. You can force garbage collection with the follow-
ing two calls:

GC.Collect
GC.WaitForPendingFinalizers

Note The two calls to Collect and to WaitForPendingFinalizers are


required in the order shown above. The first call to Collect kicks off the
garbage collection process asynchronously and immediately returns.
The call to WaitForPendingFinalizers waits for the collection process to
complete.

Depending on how many (or few) objects need to be collected, running


the Garbage Collector in this manner may not be efficient. Force garbage col-
lection sparingly and only in cases where it’s critical that all recently freed
objects get collected. Otherwise, opt for using Dispose or Close on individual
objects to free up needed resources as you go.

Type Identity
Mike once played on a volleyball team where everyone on his side of the net,
including himself, was named Mike. What a disaster. All the other team had to
do was hit the ball somewhere in the middle. Someone would yell, “Get it,
Mike!” and they would all go crashing into a big pile. To sort things out, they
adopted nicknames, involving some derivation of their full names. After that,
the game went much better.
Like names in the real world, types in Visual Basic can have the same
name. Instead of giving them nicknames, however, you distinguish them by
using their full name. For example, Visual Basic has offered a variety of data
access models over the years. Many of these data access models contain objects
with the same names. Data Access Objects (DAO) and ActiveX Data Objects
(ADO), for instance, both contain types called Connection and Recordset.
C0261587x.fm Page 26 Thursday, November 15, 2001 2:02 PM

26 Part I Introduction to Upgrading

Suppose that, for whatever reason, you decided to reference both DAO and
ADO in your Visual Basic 6 project. If you declared a Recordset variable, the
variable would be either a DAO or an ADO Recordset type:
Dim rs As Recordset

How do you know which type of Recordset you are using? One way to tell
is to look at the properties, methods, and events that IntelliSense or the event
drop-down menu gives you. If the object has an Open method, it is an ADO
Recordset. If instead it has an OpenRecordset method, it is a DAO Recordset.
In Visual Basic 6, the Recordset you end up with depends on the order of the
references. The reference that appears higher in the list wins. In Figure 2-1, for
example, the Microsoft ActiveX Data Objects 2.6 Library reference occurs
before the reference to the Microsoft DAO 3.6 Object Library, so ADO wins and
the Recordset is an ADO Recordset type.

F01km01

Figure 2-1 ADO 2.6 reference takes precedence over DAO 3.6.

If you change the priority of the ADO reference by selecting it and click-
ing the down arrow under Priority, the DAO reference will take precedence.
Clicking OK to apply the change transforms your Recordset type to a DAO
Recordset.
Suppose you want to use both types of Recordset objects in your applica-
tion. To do so, you need to fully qualify the type name as follows:
Dim rsADO As ADODB.Recordset
Dim rsDAO As DAO.Recordset

As you can see, Visual Basic 6 is quite flexible when it comes to using types.
Indeed, you could argue that it is too flexible, since you could mistakenly
change the type for variables in your code simply by changing the order of a
reference.
C0261587x.fm Page 27 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 27

Visual Basic .NET is stricter about the use of the same types in an applica-
tion. The general rule is that you need to fully qualify every ambiguous type
that you are using. If you are referencing both ADO and DAO, for example, you
are forced to fully qualify your use of the types just as you would in Visual Basic 6:

Dim rsADO As ADODB.Recordset


Dim rsDAO As DAO.Recordset

Using Imports
To help you cut down on the number of words and dots that you need to type
for each reference, Visual Basic .NET allows you to import a namespace. You
can think of it as a global With statement that is applied to the namespace. (A
namespace is similar to a library or project name in Visual Basic 6.) For exam-
ple, type references can become quite bothersome when you are dealing with
.NET types such as System.Runtime.Interopservices.UnmanagedType. To sim-
plify the qualification of this type, you can add an Imports statement to the
beginning of the file in which it is used:

Imports System.Runtime

This statement allows you to reference the type as Interopservices.Unman-


agedType. You can also expand the Imports clause to

Imports System.Runtime.Interopservices.

and then simply refer to Unmanaged Type in your code.

Managing Conflicts
Imports works great until there is a conflict. As we indicated earlier, in Visual
Basic 6, the rule is that the type library that is higher in the precedence list takes
priority. Visual Basic .NET is different in that all conflicts are irreconcilable. You
have to either change your Imports clause to avoid the conflict or fully qualify
each type when it is used. Suppose that you add Imports statements for ADO
and DAO as follows:
Imports ADO
Imports DAO

Now suppose that you want to declare a variable of type Recordset. As in the
volleyball game described earlier, it’s as if you yelled out, “Recordset!” Both
ADO and DAO jump in. Crash! Big pile. Any attempt to use the unqualified type
Recordset will lead to an error that states, “The name ‘Recordset’ is ambiguous,
imported from Namespace ADO, DAO.” To resolve the problem, you need to
either fully qualify the type or remove one of the Imports statements.
C0261587x.fm Page 28 Thursday, November 15, 2001 2:02 PM

28 Part I Introduction to Upgrading

No More GUIDs
Each ActiveX type, whether it is a class, an interface, an enumerator, or a struc-
ture, generally has a unique identifier associated with it. The identifier is a 128-
bit, or 16-byte, numeric value referred to as UUID, GUID, LIBID, CLSID, IID, or
<whatever>ID. No matter what you call it, it is a 128-bit number.
Rather than make you think in 128-bit numbers, Visual Basic (and other
languages) associates human-readable names with each of these types. For
example, if you create a Visual Basic 6 class called Customer, its type identifier
will be something like {456EC035-17C9-433c-B5F2-9F22C29D775D}. You can
assign Customer to other types, such as LoyalCustomer, if LoyalCustomer imple-
ments the Customer type with the same ID value. If the LoyalCustomer type
instead implements a Customer type with a different ID value, the assignment
would fail with a “Type Mismatch” error. In ActiveX, at run time, the number is
everything; the name means little to nothing.
In .NET, on the other hand, the name is everything. Two types are consid-
ered the same if they meet all of the following conditions:

■ The types have the same name.


■ The types are contained in the same namespace.
■ The types are contained in assemblies with the same name.
■ The assemblies containing the types are weak named.

Note that the types can be in assemblies that have the same name but a
different version number. For example, two types called Recordset contained in
the namespace ADODB are considered the same type if they live in an assem-
bly such as Microsoft.ADODB.dll with the same name. There could be two
Microsoft.ADODB.dll assemblies on your machine with different version num-
bers, but the ADODB.Recordset types would still be considered compatible. If,
however, the Recordset types lived in different assemblies, such as
Microsoft.ADODB_2_6.dll and Microsoft.ADODB_2_7.dll, the types would be
considered different. You cannot assign two variables of type Recordset to
each other if each declaration of Recordset comes from an assembly with a
different name.
C0261587x.fm Page 29 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 29

Threading Model
Visual Basic 6 ActiveX DLLs and controls can be either single threaded or apart-
ment threaded; they are apartment threaded by default. Apartment threading
means that only one thread can access an instance of your Visual Basic 6
ActiveX component at any given time. In fact, the same thread always accesses
your component, so other threads never disturb your data, including global
data. Visual Basic .NET components, on the other hand, are multithreaded by
default, meaning that two or more threads can be executing code within your
component simultaneously. Each thread has access to your shared data, such
as class member and global variables, and the threads can change any data
that is shared.
Visual Basic .NET multithreaded components are great news if you want
to take advantage of MTS pooling, which requires multithreaded components.
They are bad news if your component is not multithread safe and you wind up
trying to figure out why member variables are being set to unexpected or ran-
dom values in your upgraded component.
There is certainly more to cover on this topic, but we’ll leave that for the
discussion of threading in Chapter 11where we discuss how to make your mul-
tithreaded Visual Basic .NET components multithread safe.

Differences in the Development Environment


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, includ-
ing 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
C0261587x.fm Page 30 Thursday, November 15, 2001 2:02 PM

30 Part I Introduction to Upgrading

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 syn-
tax 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.

F01km02

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.
C0261587x.fm Page 31 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 31

F01km03

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 Win-
dows Forms project, the Toolbox will contain categories titled Data, Compo-
nents 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 compo-
nents 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 refer-
ence to that component is added to your project.
C0261587x.fm Page 32 Thursday, November 15, 2001 2:02 PM

32 Part I Introduction to Upgrading

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.

Note 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 dif-
ference is that the default view for the Property Browser in Visual Studio .NET
is Category view, meaning that related properties are grouped under a descrip-
tive 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
C0261587x.fm Page 33 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 33

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 List-
View 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 Col-
lection 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.

F01km04

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 repeat-
ing 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
C0261587x.fm Page 34 Thursday, November 15, 2001 2:02 PM

34 Part I Introduction to Upgrading

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.

F01km05

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 Single Standard for Windows Forms


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.
C0261587x.fm Page 35 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 35

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.

Two Forms Packages for the Price of One


One of the appealing features of Visual Studio .NET is that you can create a
Web application more quickly and easily than you ever have before. This ease
stems from the marriage between the Web Forms package and Visual Basic
.NET. For the first time, you can create a Web application in the same manner
that you create a Windows client application. You drag and drop controls onto
a Web form and then write code to handle the form and control events. All of
the skills that you use to create Visual Basic Windows applications can now be
used to create Web applications.

Note The Upgrade Wizard will upgrade your client-based applica-


tions to use Windows Forms and will upgrade your WebClasses-
based applications to use Web Forms.

Language Differences
With each new version of Visual Basic, Microsoft has expanded the language by
offering new keywords, new syntactical elements, new conditional statements
or modifiers, new attributes, and so on. Visual Basic .NET is no exception. It
makes the same types of additions to the language as previous versions have,
but on a much grander scale than before. Table 2-1 gives a complete list of key-
words that have been added to the Visual Basic .NET language.
C0261587x.fm Page 36 Thursday, November 15, 2001 2:02 PM

36 Part I Introduction to Upgrading

Table 2-1 New Keywords in Visual Basic .NET


Visual Basic .NET Keyword Description
AddHandler and RemoveHandle Dynamically adds or removes event handlers
at runtime, respectively
AndAlso and OrElse Short circuited logical expressions that com-
plement And and Or, respectively
Ansi, Auto, and Unicode Declare statement attributes
CChar, CObj, CShort, CType, and DirectCast Coercion functions
Class, Interface, Module, and Structure Type declaration statements
Default Attribute for indexed property declarations
Delegate Declare pointer to instance method or shared
method
GetType Returns Type class for a given type
Handles Specifies event handled by a subroutine
Imports Includes given namespace in current code
file
Inherits Optional statement used with a class to
declare classes that inherit from another class
MustInherit Optional statement used with a class to
declare the class as an abstract base class
MustOverride Optional subroutine attribute that specifies an
inherited class must implement the subroutine
MyBase Refers to base class instance
MyClass Refers to the current class instance. Ignores a
derived class.
Namespace Defines a namespace block
NotInheritable Optional statement used with Class to indi-
cate the class cannot be inherited
NotOverridable Optional subroutine attribute which specifies
that a subroutine cannot be overridden in a
derived class
Option Strict Allows you to turn strict type conversion
checking on or off. Default is off.
Overloads Optional subroutine attribute that indicates
the subroutine overloads a subroutine with
the same name, but different parameters
Overridable Optional subroutine attribute which specifies
that a subroutine can be overridden in a
derived class
C0261587x.fm Page 37 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 37

Table 2-1 New Keywords in Visual Basic .NET continued


Visual Basic .NET Keyword Description
Overrides Optional subroutine attribute that indicates
the subroutine overrides a subroutine in the
base class
Protected Class member attribute that limits member
access to the class and any derived class
Protected Friend Same as Protected, but expands the scope to
include access by any other class in the same
assembly
ReadOnly and WriteOnly Attribute on a Property declaration to specify
the property is read-only or write-only
Return* Statement used to return, possibly with a
value from a subroutine
Shadows Attribute on class members to specify that a
class member is distinct from a same-named
base class member
Short 16-bit type known as Integer in Visual Basic 6
SyncLock Specifies the start of a thread synchronization
block
Try, Catch, Finally, and When Keywords related to structured error handling
Throw Keyword to throw an exception
* Existing keyword with different behavior.

Because the Upgrade Wizard generally does not modify or update your
code to take advantage of new Visual Basic .NET features, only a subset of the
new features come into play after an upgrade. Therefore, we will focus here on
some of the general language differences that affect your upgraded Visual Basic
6 application. Chapter 11covers how to deal with these and other language
changes in detail. The sections that follow describe the types of changes you
will notice when you look at your upgraded Visual Basic .NET code.

All Subroutine Calls Must Have Parentheses


Parentheses are required on all subroutine calls. If you write code that does not
use the Call keyword, as follows:
MsgBox “Hello World”

you are required to use parentheses in your Visual Basic .NET code, as follows:

MsgBox(“Hello World”)
C0261587x.fm Page 38 Thursday, November 15, 2001 2:02 PM

38 Part I Introduction to Upgrading

ByVal or ByRef Is Required


In Visual Basic .NET, all subroutine parameters must be qualified with ByVal or
ByRef. For example, instead of this Visual Basic 6 code:
Sub UpdateCustomerInfo(CustomerName As String)
End Sub

you will see the following Visual Basic .NET code:

Sub UpdateCustomerInfo(ByRef CustomerName As String)


End Sub

In this case, an unqualified Visual Basic 6 parameter has been upgraded to


use the ByRef calling convention. In Visual Basic .NET, the default calling con-
vention is ByRef.

Is That My Event?
Visual Basic 6 associates events by name, using the pattern <Object-
Name>_<EventName>. For example, the click event associated with a com-
mand CommandButton is
Private Sub Command1_Click()

If you change the name of the Visual Basic 6 event to the name of a sub-
routine that does not match any other event, it becomes a simple subroutine.
The name pattern, therefore, determines whether a subroutine is an event or not.

Handles Clause
Visual Basic .NET does not associate events by name. Instead, a subroutine is
associated with an event if it includes the Handles clause. The name of the sub-
routine can be any name you want. The event that fires the subroutine is given
in the Handles clause. For example, the click event associated with a Visual
Basic .NET button has the following signature:

Private Sub Button1_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) _
Handles Button1.Click

Because the event hookup is an explicit part of the event declaration, you can
use unique names for your events. For example, you can change the name of
your Button1_Click event to YouClickedMyButton as follows:

Private Sub YouClickedMyButton(ByVal sender As System.Object, _


ByVal e As System.EventArgs) _
Handles Button1.Click
C0261587x.fm Page 39 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 39

Event Parameters
Another interesting change related to events is that event parameters are differ-
ent between Visual Basic 6 and Visual Basic .NET. In Visual Basic 6, the event
subroutine contains the name and type of each parameter. In Visual Basic .NET,
the parameters are bundled up in an EventArgs object and passed in as a refer-
ence to that object. Also, the event subroutine for a Visual Basic .NET event
includes a reference to the object that fired the event.
As an example of the different handling of event parameters in the two
versions of Visual Basic, consider a form with a Listbox control on it, for which
you need to write code to show the checked item.
In Visual Basic 6, you would write the following code:
Private Sub List1_ItemCheck(Item As Integer)
MsgBox “You checked item: “ & Item
End Sub

The equivalent code in Visual Basic .NET is as follows:

Private Sub CheckedListBox1_ItemCheck(ByVal sender As Object, _


ByVal e As System.Windows.Forms.ItemCheckEventArgs) _
Handles CheckedListBox1.ItemCheck
MsgBox(“You checked item: “ & e.Index)
End Sub

Observe how the item that is checked is passed directly as a parameter in Visual
Basic 6. In Visual Basic .NET, it is passed as a member of the passed-in Item-
CheckEventArgs object e.

Arrays Must Have a Zero-Bound Lower Dimension


You cannot declare an array in Visual Basic .NET to have a nonzero-bound
lower dimension. This requirement also means that you cannot use Option Base 1.
In fact, you cannot specify a lower dimension in an array declaration, since it must
always be zero. The following types of declarations are no longer supported:
Dim MyIntArray(-10 To 10) As Integer ‘21 elements
Dim MyStringArray(1 To 100) As String ‘100 elements

Option Base 1
Dim MyOptionBase1Array(5) As Long ‘5 elements (1-5)

Instead, you must use zero-based lower bound arrays, and you need to adjust
the bounds to create an array with the same number of elements, such as

Dim MyIntArray(20) As Integer ‘21 elements (0-20)


Dim MyStringArray(99) As String ‘100 elements (0-99)

‘Option Base 1 ‘Not supported by VB .NET


Dim MyOptionBase1Array(4) As Long ‘5 elements (0-4)
C0261587x.fm Page 40 Thursday, November 15, 2001 2:02 PM

40 Part I Introduction to Upgrading

Refer to Chapter 11 for more information on how you can change your
array declarations in Visual Basic .NET to be compatible with your array decla-
rations in Visual Basic 6.

Fixed-Length Strings Are Not Supported


Visual Basic .NET does not support fixed-length strings. For example, the fol-
lowing type of declaration is not supported:
Dim MyString As String * 32

Instead, you can dimension the string as a fixed-length array of characters,


as follows:

Dim MyString(32) As Char

Or you can use a special class, VBFixedLengthString, defined in the Visual


Basic .NET compatibility library. If you use the VBFixedLengthString class the
declaration will be:

Imports VB6 = Microsoft.VisualBasic.Compatibility.VB6



Dim MyFixedLenString As New VB6.FixedLengthString(32)

To set the value of a FixedLengthString variable you need to use the Value
property as follows:

MyFixedLenString.Value = “This is my fixed length string”

Refer to Chapter 7 for more information about the Visual Basic .NET com-
patibility library.

Variant Data Type Is Eliminated


Visual Basic .NET eliminates the Variant data type. The main reason is that the
underlying .NET Framework does not natively support the Variant type or any-
thing like it. The closest approximation that the .NET Framework offers is the
Object type. The Object type works somewhat like the Variant type because
the Object type is the base type for all other types, such as Integer and String.
Just as you can with a Variant, you can assign any type to an Object. How-
ever, in Visual Basic .NET, to get a strong type back out of a Variant to assign, for
example, to an Integer or a String, you need to use a type-casting function, such
as CInt or CString. With Visual Basic 6, you can write code such as the following:
Dim v As Variant
Dim s As String
v = “My variant contains a string"
s = v
C0261587x.fm Page 41 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 41

When using Visual Basic .NET, however, you need to use type conversion
functions such as CStr, as follows:

Dim v As Variant
Dim s As String
v = “My variant contains a string"
s = CStr(v)

Refer to Chapter 11 for more information on differences between the


Visual Basic 6 Variant and Visual Basic .NET Object types.

Visibility of Variables Declared in Nested Scopes Is Limited


Variables that are declared in a nested scope, such as those occurring within an
If…Then or For…Next block, are automatically moved to the beginning of the
function. The Upgrade Wizard does this for compatibility reasons. In Visual
Basic 6, a variable declared in any subscope is visible to the entire function. In
Visual Basic .NET, this is not the case. A variable declared within a subscope is
visible only within that subscope and any scope nested beneath it.
Take, for example, the following Visual Basic code:
Dim OuterScope As Long

If OuterScope = False Then


Dim InnerScope As Long
End If

InnerScope = 3

This code works fine in Visual Basic 6, but it will lead to a compiler error in
Visual Basic .NET. The compiler error will occur on the last line, InnerScope =
3, and will indicate that the name InnerScope is not declared.

Note The Upgrade Wizard will upgrade your code so that no com-
piler error occurs. It does this by moving the declaration for Inner-
Scope to the top of the function along with all other top-level
declarations. Moving the variable declaration to the top-level scope
allows the variable to be used from any scope within the function. This
move makes the behavior compatible with Visual Basic 6. It is one of
the few cases in which the Upgrade Wizard changes the order of code
during upgrade.
C0261587x.fm Page 42 Thursday, November 15, 2001 2:02 PM

42 Part I Introduction to Upgrading

Changes in the Debugger


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. How-
ever, there are some differences that you should be aware of. These are dis-
cussed in the following sections.

No Edit and Continue


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.

Cannot Continue After an Error


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.

No Repainting in Break Mode


In Visual Basic 6, the form and all controls on it continue to display even when
you are in break mode. This happens because the Visual Basic 6 debugger lets
certain events occur and allows certain code to execute when you are in break
mode. For example, painting is allowed to occur.
C0261587x.fm Page 43 Thursday, November 15, 2001 2:02 PM

Chapter 2 Visual Basic 6 and Visual Basic .NET: Differences 43

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.
C0261587x.fm Page 44 Thursday, November 15, 2001 2:02 PM
C0361587x.fm Page 45 Thursday, November 15, 2001 2:09 PM

Upgrading Options
This chapter provides an overview of your upgrading options and serves as a
guide to evaluating your application portfolio. It will help you assess the rea-
sons for and against upgrading applications to Visual Basic .NET. It also
explains the role of the Upgrade Wizard and the issues that you need to be
aware of when considering upgrading any project. The last part of the chapter
describes the decision process for selecting projects to upgrade and criteria for
evaluating the suitability of your applications.

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 appli-
cations will benefit from the new features in Visual Basic .NET. Some applica-
tions 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:

■ Leave it in Visual Basic 6.


■ Perform a partial upgrade.
■ Perform a full upgrade.
■ Perform a partial or full upgrade with interoperability.

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

45
C0361587x.fm Page 46 Thursday, November 15, 2001 2:09 PM

46 Part I Introduction to Upgrading

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 repeat-
ing, 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 ben-
efits 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 refer-
ence 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.

What Is Managed Code?


Throughout this chapter we use the term managed. Managed is the com-
mon term used to describe any code that runs under the common lan-
guage 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 man-
aged 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.
C0361587x.fm Page 47 Thursday, November 15, 2001 2:09 PM

Chapter 3 Upgrading Options 47

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. Chap-
ter 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 technol-
ogies 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.

Upgrade with Interoperability


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 perfor-
mance and scalability.
C0361587x.fm Page 48 Thursday, November 15, 2001 2:09 PM

48 Part I Introduction to Upgrading

Role of the Upgrade Wizard


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 modifica-
tions 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 Report


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.

F03km01

Figure 3-1 An upgrade report.


C0361587x.fm Page 49 Thursday, November 15, 2001 2:09 PM

Chapter 3 Upgrading Options 49

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.

Testing
Testing is extremely important for any application. It is even more important
when an application has been heavily modified, such as when you use the
Upgrade Wizard. It is dangerous to assume that upgrading a functional Visual
Basic 6 application to Visual Basic .NET will result in an application that
behaves in exactly the same way after the upgrade. After all, the application is
now running in a different environment, and significant changes have been
made to the source code. Even though Visual Basic .NET controls and objects
are similar to their Visual Basic 6 equivalents, there are differences (in both
interfaces and behaviors). These differences place the onus on the developer to
thoroughly test the upgraded application to ensure that the desired result has
been achieved.
The key point here is to test, test, and test again.

Upgrading from Earlier Versions of Visual Basic


We’ve talked about Visual Basic 6. What about backward compatibility with ver-
sions 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 for-
mat of Visual Basic 5 and Visual Basic 6 projects is virtually identical.
C0361587x.fm Page 50 Thursday, November 15, 2001 2:09 PM

50 Part I Introduction to Upgrading

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 Wiz-
ard, 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 to Upgrade


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.
For most applications, the selection process goes like this:

1. Evaluate the benefits of upgrading the application to Visual Basic


.NET. What features do you need or would you like to take advan-
tage 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?

Figure 3-2 illustrates this process.


C0361587x.fm Page 51 Thursday, November 15, 2001 2:09 PM

Chapter 3 Upgrading Options 51

Are there benefits No


to upgrading? Don’t
upgrade
Yes

Is it worth No
the effort?

Yes

Develop
upgrade
plan

F03km02

Figure 3-2 Upgrade decision process.

Evaluating the Benefits


To evaluate the benefits of upgrading, you must carefully examine your appli-
cation and its architecture, components, and business requirements and deter-
mine 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

■ To standardize all your applications on a single platform


■ To upgrade a pet project
■ To take advantage of new language features and improved applica-
tion architecture
■ To increase performance and scalability
■ To take advantage of the .NET Framework
C0361587x.fm Page 52 Thursday, November 15, 2001 2:09 PM

52 Part I Introduction to Upgrading

The Value of Standardization


While using standardization as a justification for upgrading a project may seem
somewhat facetious, it is often important to larger organizations. It’s not unre-
alistic 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.

The Infamous Pet Project


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.

New Features, New Language


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:

1. Open a class file.


2. Select all and copy.
3. Create a new class file and paste.
4. Make your changes to adapt this copied class to your needs.

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 bet-
ter 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 tan-
gible. 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.

Performance and Scalability


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
C0361587x.fm Page 53 Thursday, November 15, 2001 2:09 PM

Chapter 3 Upgrading Options 53

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.

Fast SQL Server Access


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 Sys-
tem.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()

diff = (after.Ticks - before.Ticks) / Math.Pow(10, 7)


Debug.WriteLine(“Standard concat: “ & diff)

(continued)
C0361587x.fm Page 54 Thursday, November 15, 2001 2:09 PM

54 Part I Introduction to Upgrading

How Fast Is Fast? continued

before = Now()
FastString()
after = Now()

diff = (after.Ticks - before.Ticks) / Math.Pow(10, 7)


Debug.WriteLine(“String Builder: “ & diff)
End Sub

Const loopMax As Integer = 20000


Function SlowString() As String
Dim str As String = “MyString “

Dim i As Integer

Dim result As String = “"


For i = 0 To loopMax
result &= str
Next

Return result
End Function

Function FastString() As String


Dim str As String = “MyString “

Dim i As Integer

Dim result As New System.Text.StringBuilder()


For i = 0 To loopMax
result.Append(str)
Next

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 concate-
nation. In other words the FastString() method can be called approxi-
mately 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.
C0361587x.fm Page 55 Thursday, November 15, 2001 2:09 PM

Chapter 3 Upgrading Options 55

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 applica-
tion 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.

Note Because of the architectural differences in the common lan-


guage 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.

The .NET Framework


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 fea-
ture. 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 plan-
ning to forge ahead, it’s time to move on to evaluate the effort that the upgrade
will require.

Evaluating the Effort Required


Four main physical factors determine an application’s suitability for an upgrade:

■ Architecture
■ Code quality
■ Technologies
■ Size

The sections that follow discuss each of these factors in turn.

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
C0361587x.fm Page 56 Thursday, November 15, 2001 2:09 PM

56 Part I Introduction to Upgrading

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:

■ Has a small user base


■ Uses the Visual Basic 6 drawing model
■ Contains many unmanaged Win32 API calls
■ Contains old code that uses obsolete language elements

It may be that some or all of your Visual Basic 6 applications can take
advantage of the new features in Visual Basic .NET. You may even want to con-
vert your legacy client/server applications to a thin Web-based client model
with the improved Web controls.
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 embed-
ded in a Web browser and others with client-installed executables, COM com-
ponents, and third-party controls. Unless their design is fairly straightforward,
these applications require more work to upgrade to the .NET Framework. Since
C0361587x.fm Page 57 Thursday, November 15, 2001 2:09 PM

Chapter 3 Upgrading Options 57

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 rede-
signed 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 diffi-
culty 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
C0361587x.fm Page 58 Thursday, November 15, 2001 2:09 PM

58 Part I Introduction to Upgrading

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.

Application Size and Scope


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.

Developing the Upgrade Plan


Once you have determined which projects to upgrade, the rest of the process
becomes a whole lot easier. Keep one thing in mind when working out your
upgrade plan: If your application consists of a number of projects, some of
which depend on others, upgrade the client project first. Next upgrade the
project the client depends on. If this project in turn depends on another project,
upgrade the more fundamental one next, and so on. Doing the upgrade in this
order means you are moving up the dependency hierarchy. Why choose this
order? For the simple reason that it’s the easiest upgrade path. Using COM com-
ponents from Visual Basic .NET is simpler than using .NET components from
Visual Basic 6. The latter option requires changing both the .NET component
and the COM component at the same time. It also means dealing with GUID
and versioning issues that can make COM development a complicated process.
Starting with the client and moving up the dependency hierarchy keeps all of
the COM GUIDs the same and avoids COM versioning issues.
Keeping that rule in mind, you’ll find that the rest of the process is simple.
Break your application down into its core components, and work out the
dependencies. Starting from the client side, decide what components you will
upgrade and in what order, and the extent to which each of them will be
upgraded (none, partial, full, interop). Large applications will no doubt be han-
dled in stages, since attempting to pull off a full upgrade of all components
would be extremely disruptive and would lead to all sorts of testing problems
and new bugs. Try to go step by step, testing as you go, to ensure that you
maintain a high level of quality.
C0361587x.fm Page 59 Thursday, November 15, 2001 2:09 PM

Chapter 3 Upgrading Options 59

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.
C0361587x.fm Page 60 Thursday, November 15, 2001 2:09 PM
C0461587x.fm Page 61 Thursday, November 15, 2001 2:31 PM

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 straightfor-
ward 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 chap-
ter will help you prepare for your upgrade project by identifying common cod-
ing 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.

Why Change Anything?


The quality of your upgrade depends greatly on the quality of the application
you start with. It is therefore crucial to take the time to make your application
more compatible with the upgrade process. You’ll need to replace obsolete lan-
guage syntax and legacy (pre–Visual Basic 6) controls, modify your coding

61
C0461587x.fm Page 62 Thursday, November 15, 2001 2:31 PM

62 Part I Introduction to Upgrading

style, and watch for common traps. These steps are the best way to ensure that
your code maintains its current functionality throughout the upgrade. The fol-
lowing sections demonstrate what you can do to improve your application and
make it more suitable to an upgrade.

Cleaning Up Legacy Code


Visual Basic 6 is almost a wonder of compatibility; it maintains code compati-
bility 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.

VarPtr, DefInt, and Other No-Shows


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 distrib-
uted 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 possi-
ble. 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
C0461587x.fm Page 63 Thursday, November 15, 2001 2:31 PM

Chapter 4 Preparing Your Project for the Upgrade to Visual Basic .NET 63

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)
■ VarPtr, ObjPtr, StrPtr
■ Null and Empty nonzero-lower-bound arrays

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 Wiz-
ard just to see what it does, but that is a subject covered in a later chapter. (See
Chapter 5)

DAO and RDO Data Binding


Over the years, Microsoft has introduced several data access technologies. Start-
ing 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 inter-
face 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 technolo-
gies? 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.
C0461587x.fm Page 64 Thursday, November 15, 2001 2:31 PM

64 Part I Introduction to Upgrading

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 com-
patible with the Visual Basic Upgrade Wizard. Chapter 4 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 cor-
rect prior to the actual upgrade. The Upgrade Wizard can identify most of
these issues.

Variants and Variables


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 func-
tion 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?
C0461587x.fm Page 65 Thursday, November 15, 2001 2:31 PM

Chapter 4 Preparing Your Project for the Upgrade to Visual Basic .NET 65

Implicit vs. Explicit Variable Declaration


Look at the following code. Do you understand what is going on here?
Function MyFunction( var1, var2 )
MyFunction = var1 + var2
End Function

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:
Debug.Print MyFunction(“This and", “ that.”)
Debug.Print MyFunction(Now(), 1)
Debug.Print MyFunction(4, 5)

The following results show in the Immediate window:


This and that.
7/23/2001 8:04:59 PM
9
An alternative, and more proper, way to define the function would
look like this:
Function MyFunction( var1 As String, var2 As String ) As String
MyFunction = var1 & var2
End Function

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 pro-
grammer intends to happen. We also get the benefits of compile-time valida-
tion: 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.
C0461587x.fm Page 66 Thursday, November 15, 2001 2:31 PM

66 Part I Introduction to Upgrading

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 program-
ming 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.

Constants and Underlying Values


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.

Table 4-1 Mouse Pointer Constants


Constant Underlying Value Mouse Pointer
vbDefault 0 Default
vbArrow 1 Arrow
vbCrosshair 2 Cross
vbIbeam 3 I beam
vbIconPointer 4 Icon
vbSizePointer 5 Size
vbSizeNESW 6 Size NE, SW
vbSizeNS 7 Size N, S
vbSizeNWSE 8 Size NW, SE

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.
C0461587x.fm Page 67 Thursday, November 15, 2001 2:31 PM

Chapter 4 Preparing Your Project for the Upgrade to Visual Basic .NET 67

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 con-
stants are used properly, the Upgrade Wizard can upgrade code to the Visual
Basic .NET equivalent (if it exists) with no warnings.

Use the Proper Constants 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 func-
tion 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 offend-
ing 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 adCmdStored-


Proc 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.

Underlying Data Types


Another problematic coding style involves the use of underlying (or implemen-
tation) 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
C0461587x.fm Page 68 Thursday, November 15, 2001 2:31 PM

68 Part I Introduction to Upgrading

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.

How to Get a Double Date


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 represent-
ing 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.

Date 7/22/2001 6:38:29 PM

Double 37094 776724537

Decimal point

Try out the following example in Visual Basic 6:


F0km01

Dim dt As Date
Dim dbl As Double

dbl = 37094.776724537
dt = dbl

Debug.Print dt
Debug.Print dbl

The Immediate window displays the following:


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 Dec-
imal value less than 1.
C0461587x.fm Page 69 Thursday, November 15, 2001 2:31 PM

Chapter 4 Preparing Your Project for the Upgrade to Visual Basic .NET 69

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 imple-
mentation 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).

Early Binding vs. Late Binding vs. Soft Binding


Binding refers to the way that objects and methods are “bound” by the com-
piler. You can think of the different binding types in the context of variable dec-
laration and usage. Early binding is the most explicit form of variable
declaration and usage. Late binding is the least explicit form of variable decla-
ration. 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 gen-
erate errors when nonexistent object methods are called or when parameter
and return types aren’t compatible.

Example of Early Binding The following code shows an example of the use of
early binding. Notice that all of the function’s parameters, the return type, and
the local variables are explicitly typed. Nothing is explicitly or implicitly
declared as Variant.
Function GetADORs(connStr As String, statement As String) _
As ADODB.Recordset

Dim conn As New ADODB.Connection


conn.Open connStr
Set GetADORs = conn.Execute(statement)
conn.Close
End Function
C0461587x.fm Page 70 Thursday, November 15, 2001 2:31 PM

70 Part I Introduction to Upgrading

Late Binding
Late binding occurs when the Variant type is used (either implicitly or explic-
itly). 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 responsi-
bility 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:
Sub SetLabel( lbl, value )
lbl = value
End Sub

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 pro-
cessing overhead to a single line of code. While the performance consequences
are not obvious in a small application, large-scale enterprise or Web applica-
tions will notice the difference.

Late Binding and Upgrading 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:
Public Function Test(frm) As Integer
Dim lbl
Set lbl = frm.Label1
lbl.Caption = “This is a test"
frm.Label2 = “Another Test"
Test = True
End Function

This function implicitly takes a Variant parameter (intended to be a form) and


explicitly returns an Integer. From the standpoint of good programming prac-
tice 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:

Public Function Test(ByRef frm As Object) As Short


Dim lbl As Object
lbl = frm.Label1
C0461587x.fm Page 71 Thursday, November 15, 2001 2:31 PM

Chapter 4 Preparing Your Project for the Upgrade to Visual Basic .NET 71

lbl.Caption Label2 = “Another Test"


‘UPGRADE = “This is a test"
frm. _WARNING: Boolean True is being converted into a numeric.
Test = True
End Function

The following errors and warnings resulted:

■ lbl.Caption should be lbl.Text.


■ frm.Label2 should be frmLabel2.Text.
■ An upgrade warning occurred, indicating that the wizard was con-
verting 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:

■ Change Test to return Boolean.


■ Change the definition of lbl to a variable of type Label.
■ Explicitly type the frm parameter to be Form1.

The function looks like this after all of these changes:


Public Function Test(frm As Form1) As Boolean
Dim lbl As Label
Set lbl = frm.Label1
lbl.Caption = “This is a test"
frm.Label2 = “Another Test"
Test = True
End Function

The upgraded function now looks like this:

Public Function Test(ByRef frm As Form1) As Boolean


Dim lbl As System.Windows.Forms.Label
lbl = frm.Label1
lbl.Text = “This is a test"
frm.Label2.Text = “Another Test"
Test = True
End Function

The Test function has now upgraded cleanly with no errors.


C0461587x.fm Page 72 Thursday, November 15, 2001 2:31 PM

72 Part I Introduction to Upgrading

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. Imag-
ine a situation involving a form (MyForm) with a label (Label1). Consider the fol-
lowing example, in which you pass the form and new text for your control:
Sub Form_Load()
SetLabelText Me, “This is soft binding!"
End Sub

Sub SetLabelText( frm As Form, text As String )


frm.Label1.Caption = text
End Sub

Notice that everything is strongly typed. There is certainly no obvious late bind-
ing 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:
Sub SetLabelText1( frm As MyForm, text As String )
frm.Label1.Caption = text
End Sub

or this:
Sub SetLabelText2( frm As Form, text As String )
Dim f as MyForm
Set f = frm
frm.Label1.Caption = text
End Sub

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.
C0461587x.fm Page 73 Thursday, November 15, 2001 2:31 PM

Chapter 4 Preparing Your Project for the Upgrade to Visual Basic .NET 73

Watch Out for Null and Empty


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 imple-
ment 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:
Dim str As Variant
str = Null
str = Left(str, 4)

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:
Dim str As Variant
str = Null
str = Left$(str, 4)

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:
Dim str As Variant
str = Null
str = Left$(str & “", 4)

Concatenation with an empty string will cause Null to be coerced to an empty


string (but not a string that is Empty), and Left$ will not cause a run-time error.
Thus, you should use Left$ (and all of the associated functions—Right$, Mid$,
and so on) and ensure that your application behaves correctly before upgrad-
ing. If you do so, you should not have to worry about a difference in function-
ality because the behavior of functions such as Left$ is equivalent to their Visual
Basic .NET counterparts.
C0461587x.fm Page 74 Thursday, November 15, 2001 2:31 PM

74 Part I Introduction to Upgrading

Implicit Object Instantiation


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 decla-
ration. 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 instantiat-
ing 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 actu-
ally 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 cre-
ated in its place unless the application does so explicitly. In the second exam-
ple, 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
C0461587x.fm Page 75 Thursday, November 15, 2001 2:31 PM

Chapter 4 Preparing Your Project for the Upgrade to Visual Basic .NET 75

and lifetimes), but it could have consequences for your code. Example 3 dem-
onstrates 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:
Dim forms(0) As New Form
forms(0).Title = “Form 1"
ReDim Preserve forms(2)
forms(1).Title = “Form 2"
forms(2).Title = “Form 3”

This is where matters get tricky. Notice that we need to create this array of con-
trols 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 mer-
rily create the object for you and at the same time hide a bug in your appli-
cation. 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 (espe-
cially 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:
Dim forms(0) As Form
Set forms(0) = New Form
forms(0).Title = “Form 1"
ReDim Preserve forms(2)
For i = 1 To 2
Set forms(i) = New Form
forms(i).Title = “Form “ & i
Next

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
C0461587x.fm Page 76 Thursday, November 15, 2001 2:31 PM

76 Part I Introduction to Upgrading

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, cre-
ating a file, or whatever). In Visual Basic .NET, those kinds of assumptions can-
not 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 pro-
grammer, it increases the clarity of your code. No object will exist until you cre-
ate it, which makes for greater predictability all round.

Conclusion

As you have seen in this chapter, there are very definite and predictable steps
that you can take to improve the chances that your application will upgrade
smoothly. While some of these steps require work up front on your part, they
will save you time and effort over the course of a complete upgrade. Abandon-
ing deprecated features and improving code quality not only will ensure a
smooth upgrade but will reduce the barrier between your application and other
managed framework classes.
For additional information, you can consult the MSDN white paper “Pre-
paring a Visual Basic 6.0 Application for Upgrading.” You can find it by search-
ing the Help files included with Visual Studio .NET.
C0561587x.fm Page 77 Thursday, November 15, 2001 2:37 PM

Part II

Upgrading
Applications
5 Your First Upgrade 79
6 Common Tasks in Visual Basic .NET 101
7 Upgrading Wizard Ins and Outs 117
8 Errors, Warnings, and Issues 149
9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 175
C0561587x.fm Page 78 Thursday, November 15, 2001 2:37 PM
C0561587x.fm Page 79 Thursday, November 15, 2001 2:37 PM

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 sim-
ple 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.

Upgrade Walkthrough
Let’s start by creating a simple project in Visual Basic 6. We will assume that you
have Visual Basic 6 and Visual Basic .NET installed on the same machine. Hav-
ing both versions installed is actually a recommended configuration. Visual
Basic .NET installs to a different directory than Visual Basic 6. If Visual Basic 6
is already installed, it is left intact. Visual Basic .NET does not overwrite any
Visual Basic 6 files—the two versions can be installed and run side by side. The
order of installation doesn’t matter either; you can set up Visual Basic 6 after
installing Visual Basic .NET or vice versa. The benefits of installing both prod-
ucts on the same machine are twofold:

■ It allows you to ensure that your project actually runs. (If the project
doesn’t work in Visual Basic 6, it’s unlikely that it will work after
upgrading.)

79
C0561587x.fm Page 80 Thursday, November 15, 2001 2:37 PM

80 Part II Upgrading Applications

■ Visual Basic 6 Setup installs common ActiveX controls and libraries.


These are not distributed with Visual Basic .NET. The easiest way to
get them on the machine is to install Visual Basic 6.

We are going to create a simple Visual Basic 6 project, prjFirstUpgrade,


and upgrade it to Visual Basic .NET. (You can find a completed version of this
project on the companion CD that accompanies this book.) The project we are
about to develop generates a new random number between 0 and 10 every sec-
ond and shows the number in a TextBox. It continues to do this until you click
a CommandButton to stop it. Let’s get started. Launch Visual Basic 6, and follow
these steps to create the project:

1. Create a new Standard EXE project.


2. After Visual Basic 6 creates the project, you’ll see that Form1 is dis-
played in the integrated development environment (IDE). Add a
TextBox, a CommandButton, and a Timer control to Form1.
3. Select the Timer control. In the Properties grid, set the interval to
1000.
4. Double-click the Command1 CommandButton, and add this code to
the Command1_Click procedure:
Private Sub Command1_Click()
Me.Timer1.Interval = 0
End Sub

5. Now return to the Form window, and double-click the Timer control
to create the Timer1_Timer event. Add the following code to the
event:
Private Sub Timer1_Timer()
Dim i As Integer
i = Rnd * 10
Me.Text1 = i
End Sub

6. Save the project, using the default name for the form: Form1. We’ll
name the project something meaningful: prjFirstUpgrade. Make sure
you save the project into a directory on your local computer, rather
than to a network share. This step ensures that Visual Basic .NET has
enough security permissions to upgrade and run the project. The
form and code should look similar to Figure 5-1.
C0561587x.fm Page 81 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 81

F05km01

Figure 5-1 Random number generator in Visual Basic 6.

When you run the project, you’ll see that the TextBox updates every sec-
ond with a new random number. It stops when you click Command1.
Now that we’ve created a Visual Basic 6 project, let’s upgrade it to Visual
Basic .NET. You can leave Visual Basic 6 running, with the project still loaded—
doing so doesn’t interfere with the upgrade. Start Visual Basic .NET, and follow
these steps to upgrade your project:

1. In Visual Basic .NET, from the File menu, choose Open and then
Project to display the Open Project dialog box. Navigate to the loca-
tion where you saved your Visual Basic 6 project file, select prj-
FirstUpgrade.vbp, and click the Open button to upgrade
prjFirstUpgrade using the Upgrade Wizard.
2. The first page of the wizard contains welcome information, as shown
in Figure 5-2. After you’ve read the welcome information, click Next
to move to the second page.
C0561587x.fm Page 82 Thursday, November 15, 2001 2:37 PM

82 Part II Upgrading Applications

F05km02

Figure 5-2 Welcome screen of the Upgrade Wizard.

3. The second page, shown in Figure 5-3, is where you set options for
the upgrade. These options may be disabled depending on the
project type, and in most cases, you will leave them at their default
setting, but let’s examine them more closely to see when you would
use them. There are two options. The first determines what type of
Visual Basic .NET project to upgrade to. As you would expect, EXEs
upgrade to EXEs, and DLLs upgrade to DLLs; for standard EXE and
DLL projects, the upgrade type is determined automatically. With
EXE server projects, however, you have some flexibility. These
projects act like a combination of an EXE and a DLL. When upgrad-
ing these projects, you have the choice of converting them to a
Visual Basic .NET EXE or to a Visual Basic .NET DLL. Since our
project is a standard EXE, the first choice (EXE) is automatically
selected. The second choice, DLL/Custom Control Library, is dis-
abled, and for this reason the second option, Generate Default Inter-
faces For All Public Classes, is disabled also.
This option is available only for DLL and EXE server projects.
You should select this option only if other projects use the Imple-
ments keyword to implement classes from within your DLL or EXE
server. Selecting this option causes the wizard to create an interface
for every public class in your project.
Click Next to move to page 3.
C0561587x.fm Page 83 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 83

F05km03

Figure 5-3 Choosing a project type.

4. On the third page, shown in Figure 5-4, you specify the destination
directory for the upgraded project. The project is not moved to this
location. Instead, the Upgrade Wizard leaves your Visual Basic 6
project untouched and creates a brand new Visual Basic .NET project
in the directory you specify here. By default, the wizard suggests
placing the upgraded project in a new directory called <project-
name>.NET. You can use the text box and the Browse button to
change the location of this directory, but in most cases you should
simply accept the default name. You can always move the directory
later simply by copying it to a different location.

F05km04

Figure 5-4 Specifying a destination directory.


C0561587x.fm Page 84 Thursday, November 15, 2001 2:37 PM

84 Part II Upgrading Applications

If the destination directory already contains files, these will be


deleted to make room for the new project. With our soon-to-be-
upgraded Visual Basic 6 project, let’s accept the suggested name, prj-
FirstUpgrade.NET. Click Next to move to page 4.
5. Because the prjFirstUpgrade.NET folder doesn’t exist yet, a warning
message appears asking you if you want to create it. Click Yes.
6. On page 4, the wizard tells you that it is ready to perform the
upgrade. Click Next to generate the upgraded project.

Status text and a progress bar show you what the Upgrade Wizard is pro-
cessing and give you a visual indicator of the time left. The upgrade is not
instantaneous; it can take anywhere from a minute (for a small project such as
ours) to several hours (for a project with hundreds of forms and classes). After
the wizard has finished, your project opens in Visual Basic .NET. The result will
be similar to Figure 5-5, depending on what windows you already have open in
Visual Basic .NET.

F05km05

Figure 5-5 The upgraded project opens in Visual Basic .NET.


C0561587x.fm Page 85 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 85

What Just Happened?


Let’s look at the upgraded project to see what the wizard did with our Visual
Basic 6 project. You should see the Solution Explorer window in the top right
corner of the IDE. (If the window is not visible, press Ctrl+Alt+L to see it.)
Double-click Form1.vb to open the upgraded form. As you can see in Figure
5-6, the project looks exactly as it did in Visual Basic 6, with the exception that
the Timer is now in the component tray beneath the form. Why has it moved?
Nonvisual controls such as Timers, ToolTips, and Menus are now displayed in
the component tray instead of on the form itself.

F05km06

Figure 5-6 Upgraded form.

Now let’s try running the project. Press F5, just as you would in Visual
Basic 6. The first time you run an upgraded project in Visual Basic .NET, you are
prompted to save the solution file. A solution file is similar to a group file in
Visual Basic 6. A solution contains one or more projects, just as a group can
contain one or more projects. In Visual Basic .NET, since every project must be
part of a solution, one is created for you. Press Enter to save it with the default
name. The project then runs, and Form1 begins showing a new random num-
ber every second, as shown in Figure 5-7.
C0561587x.fm Page 86 Thursday, November 15, 2001 2:37 PM

86 Part II Upgrading Applications

F05km07

Figure 5-7 Generating random numbers in the upgraded project.

Click Command1 to stop the Timer. The Timer indeed stops, but not in the
way we intended. Instead, the program breaks with an exception: “‘0’ is not a
valid value for Interval. Interval must be greater than 0,” as shown in Figure 5-8.

F05km08

Figure 5-8 Exception generated by the upgraded project.

Click Continue to stop the program and return to the IDE. The exception
occurs at this line in the Command1_Click event:

Me.Timer1.Interval = 0

What happened? Let’s look at the upgraded code for the


Command1_Click event:

Private Sub Command1_Click(ByVal eventSender As System.Object, _


ByVal eventArgs As System.EventArgs) Handles Command1.Click
‘UPGRADE_WARNING: Timer property Timer1.Interval cannot have a value
‘of 0. Click for more:
‘ms-help://MS.VSCC/commoner/redir/redirect.htm?key-
‘word="vbup2020.htm"
Me.Timer1.Interval = 0
End Sub
C0561587x.fm Page 87 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 87

The Upgrade Wizard has inserted a war ning: “Timer Property


Timer1.Interval cannot have a value of 0.” Warnings alert you to run-time dif-
ferences between Visual Basic 6 and Visual Basic .NET. Click the underlined
part of the comment, and a Help topic opens in the IDE with the title “Timer
Interval Property Behavior Has Changed.” Figure 5-9 shows what the Help topic
looks like.

F05km09

Figure 5-9 Help topic for the Timer Interval property.

The Help topic says that Timer.Interval no longer disables the timer.
Instead you have to use the statement Timer1.Enabled = False. Let’s change our
code. Replace the line

Me.Timer1.Interval = 0

with

Me.Timer1.Enabled = False

Now press F5 to run the project. Notice that this time Command1 stops the
timer just as it did in Visual Basic 6.
C0561587x.fm Page 88 Thursday, November 15, 2001 2:37 PM

88 Part II Upgrading Applications

We’ve finished upgrading our project, but let’s not stop there. We’ll do
something with our upgraded project that you couldn’t do in Visual Basic 6—
make the form fade out. Add the following code to the Command1_Click event:
Dim i As Integer
For i = 99 To 0 Step -1
Me.Opacity = i / 100
Next
End

After you add this code, the event should look like this:
Private Sub Command1_Click(ByVal eventSender As System.Object, _
ByVal eventArgs As System.EventArgs) Handles Command1.Click
‘UPGRADE_WARNING: Timer property Timer1.Interval cannot have a value
‘of 0. Click for more: ms-help://MS.MSDNVS/vbcon/html/vbup2020.htm
Me.Timer1.Enabled = False
Dim i As Integer
For i = 99 To 0 Step -1
Me.Opacity = i / 100
Next
End
End Sub

Press F5 to run the project. Now when you click Command1, the form
becomes transparent and fades from view. As you can see, you may have to
make some modifications to get your project working, but after you’ve done
so, you can take advantage of the many features in Visual Basic .NET that
weren’t available in Visual Basic 6. Upgrading enables you to add more value
to your projects.

Language Changes
Now that we’ve got our project working, let’s take a few moments to review
where the Upgrade Wizard changed the code, stepping through the original
code line by line. Here is the original Visual Basic 6 code:
Option Explicit

Private Sub Command1_Click()


Me.Timer1.Interval = 0
End Sub
C0561587x.fm Page 89 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 89

Private Sub Timer1_Timer()


Dim i As Integer
i = Rnd * 10
Me.Text1 = i
End Sub

The first line, Option Explicit, is upgraded to

Option Strict Off


Option Explicit On

Notice that On was added to Option Explicit. This statement has the same
effect as the plain Option Explicit in Visual Basic 6—it means that all variables
must be explicitly declared using the Dim, Public, or Private statement. As you
might guess, you can also write Option Explicit Off, which lets you use variables
without explicitly declaring them. Option Strict is a new option. When it is set
to On, you will get compile errors if your code does either of the following:

■ Uses late binding


■ Performs a narrowing conversion—that is, converts a variable of one
type to another, where an overflow or type mismatch might occur
(like assigning a Date to a String or an Integer to a Long)

When your project is upgraded, Option Strict Off is put at the top of each
file to turn off strict type checking, since setting it to On would cause compile
errors in most Visual Basic 6 code.

The Different Option Statements


Visual Basic .NET has three types of Option statements. Along with Option
Strict and Option Explicit, there is Option Compare. As it did in Visual
Basic 6, Option Compare sets the string comparison mode. By default,
Strict is set to Off, Explicit is set to On, and Compare is set to Binary. You
can change the defaults for each option in the Build section of the
project’s properties. If you change the default for, say, Option Compare to
Text, all forms, modules, and classes will use Option Compare Text, unless
they have their own Option Compare statements that override the default
setting.
C0561587x.fm Page 90 Thursday, November 15, 2001 2:37 PM

90 Part II Upgrading Applications

The wizard also inserts a Class statement. In Visual Basic .NET, all source
files have a .vb file extension. Whether a file is a module, a class, or a form
depends on the code inside the file. Why does the wizard insert a Class state-
ment instead of a Form statement? Because all forms are actually classes that
inherit from the base class System.Windows.Forms.Form. Here is what the Class
statement looks like. (Event Code) indicates where your events are placed.

Friend Class Form1


Inherits System.Windows.Forms.Form
(Event Code)
End Class

If you want to, you can put several forms or classes into a single file, pro-
vided each has its own Class statement.
Just beneath the Class statement, you’ll see two collapsed regions: Win-
dows Form Designer Generated Code and Upgrade Support, as shown in
Figure 5-10.

F05km10

Figure 5-10 Collapsed code.

This is “system-generated code,” or code used by Windows Forms to store


the design layout of the form and by the Upgrade Wizard to store instancing
information. We’ll discuss these sections in Chapter 6 and in Chapter 12For
now, let’s move on and look at your upgraded event code. The
Command1_Click event code is upgraded from
Private Sub Command1_Click()
Me.Timer1.Interval = 0
End Sub

to

Private Sub Command1_Click(ByVal eventSender As System.Object, _


ByVal eventArgs As System.EventArgs) Handles Command1.Click
‘UPGRADE_WARNING: Timer property Timer1.Interval cannot have a value
‘of 0. Click for more: ms-help://MS.VSCC/commoner/redir/
‘redirect.htm?keyword="vbup2020.htm"
Me.Timer1.Interval = 0
End Sub

The signature of the Click procedure has changed from Visual Basic 6. In
Visual Basic .NET, most events are passed two parameters: eventSender, an
object that is set to the control (in this case CommandButton1); and eventArgs,
C0561587x.fm Page 91 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 91

an object that contains extra arguments (such as mouse coordinates). The con-
tents of eventArgs change depending on the type of event. For Click events, it
contains nothing of interest. At the end of the Sub statement, you’ll see the
Handles keyword. This keyword associates the event procedure with the con-
trol event. Each event procedure can be associated with multiple controls and
multiple events. For example, the following Handles clause would connect a
procedure to the Command1.Click, Command2.Click, and Command1.Text-
Changed events:

Handles Command1.Click, Command2.Click, Command1.TextChanged

The line after the Sub statement contains the following upgrade warning:

‘UPGRADE_WARNING: Timer property Timer1.Interval cannot have a value


‘of 0. Click for more: ms-help://MS.VSCC/commoner/redir/
‘redirect.htm?keyword="vbup2020.htm”

The Upgrade Wizard inserts warnings whenever it encounters a statement that


may not work the same in Visual Basic .NET. It inserts the warning on one line
before the statement with the problem. You can filter the Task List to show the
upgrade warnings. For information on using the Task List, see Chapter 6.
The final statement in the Command1_Click event is

Me.Timer1.Interval = 0

This statement is exactly the same as it was in Visual Basic 6.


Timer1_Tick has also undergone some changes during the upgrade. The
Visual Basic 6 code
Private Sub Timer1_Timer()
Dim i As Integer
i = Rnd * 10
Me.Text1 = i
End Sub

is upgraded to

Private Sub Timer1_Tick(ByVal eventSender As System.Object, _


ByVal eventArgs As System.EventArgs) Handles Timer1.Tick
Dim i As Short
i = Rnd() * 10
Me.Text1.Text = CStr(i)
End Sub

Just as we saw with Command1_Click, the Timer1_Tick procedure now


has a new signature. The line after the Sub statement has also changed. In
Visual Basic .NET, the Integer data type has grown in size from 16 bits to 32 bits,
C0561587x.fm Page 92 Thursday, November 15, 2001 2:37 PM

92 Part II Upgrading Applications

and a new data type, Short, has been added to the language for 16-bit numbers.
For this reason, the line
Dim i As Integer

is upgraded to

Dim i As Short

This ensures that the variable i is the same size as it was in Visual Basic 6.
Let’s move on to the next line:
i = Rnd * 10

is upgraded to

i = Rnd() * 10

In Visual Basic .NET, all calls to functions must have parentheses, so the
Upgrade Wizard has added them to the Rnd function. Now let’s look at the final
line of code in this event procedure.
Me.Text1 = i

is upgraded to

Me.Text1.Text = CStr(i)

This line has undergone two changes. Because Visual Basic .NET does not
support default properties, the Upgrade Wizard resolves the default property of
Me.Text1 and expands the statement to Me.Text1.Text. Because the Text prop-
erty is being assigned to a number, the wizard also explicitly converts i to a
string, using the CStr function.
Whew! The upgrade resulted in quite a number of changes, but looking
over the upgraded code you’ll see that it still looks familiar. The overarching
impression is that upgraded code is more explicit than the Visual Basic 6 ver-
sion. That, in a nutshell, is the real difference between Visual Basic 6 and Visual
Basic .NET. Visual Basic 6 concealed a lot of details, like forms being classes,
the design layout, and default properties. In Visual Basic .NET, these are all
exposed for you to see.

Other Files in Your Project


Now close the Code Editor and Windows Form Designer windows. Let’s turn
our attention to the Solution Explorer for a second. The Solution Explorer
shows all the files in your upgraded project. Notice that the Upgrade Wizard has
added two extra files to your project: _UpgradeReport.htm and Assembly-
C0561587x.fm Page 93 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 93

Info.vb. Go ahead and double-click AssemblyInfo.vb to open it in the Code Edi-


tor. This file contains attributes. Attributes don’t perform any action; they are
used to store additional information about a method, form, class, or project.
The attributes in this file allow you to change information about your project
such as the assembly title and assembly description. Generally, unless you have
a specific need, you can leave these values at their default settings. Close the
AssemblyInfo.vb Code Editor and we’ll look at the upgrade report.

More Info A discussion of the assembly attributes in Assembly-


Info.vb is beyond the scope of this book. These attributes can be used
to specify the identity of your Visual Basic .NET application. To learn
more about attributes, search the Visual Basic .NET Help for
“Attributes” and “Setting Assembly Attributes.”

The upgrade report is added to your project with the filename


_UpgradeReport.htm. It is an HTML file. Double-click the file in the Solution
Explorer to open it. Click the + sign next to Form1 to display all of the issues
with Form1, and you’ll see that the difference with Timer.Interval has been
recorded. The report should look similar to Figure 5-11.

F05km11

Figure 5-11 An upgrade report.


C0561587x.fm Page 94 Thursday, November 15, 2001 2:37 PM

94 Part II Upgrading Applications

The upgrade report captures in one place the issues found while upgrad-
ing the project to Visual Basic .NET. Like the comments in code, each issue is
also a hyperlink that navigates to a Help topic describing how to fix the prob-
lem. For more on upgrade issues, see Chapter 7
You now have your Visual Basic .NET project. The Upgrade Wizard keeps
the form layout the same, makes necessary syntax changes, and warns you
about any issues found during the upgrade. After you get your project working,
you can enhance it using some of the exciting new features of Visual Basic
.NET. We’ll put off a discussion of these features until later, because we still
need to look at how to upgrade project groups and snippets of code.

Upgrading Project Groups


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 com-
piled 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.
C0561587x.fm Page 95 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 95

2. Visual Basic 6 automatically adds a new class in Project1, Class1. Add


the following code to the class:
Sub sayHelloWorld()
MsgBox “Hello World"
End Sub

3. 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.
4. Now we will make prjFirstUpgrade reference Project1. Select prj-
FirstUpgrade 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.

F05km12

Figure 5-12 References dialog box.

5. 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:
Private Sub Command1_Click()
Dim c As New Class1
c.sayHelloWorld
End Sub
C0561587x.fm Page 96 Thursday, November 15, 2001 2:37 PM

96 Part II Upgrading Applications

After you’ve added this code, the project group will look like
Figure 5-13.

F05km13

Figure 5-13 Project group in Visual Basic 6.

6. 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.”
7. 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 refer-
ences 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 Walk-
through” earlier in this chapter.) You won’t need to make any modi-
fications to the upgraded code, since we replaced Timer.Interval = 0
with code that shows a message box.
C0561587x.fm Page 97 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 97

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 ref-
erence. 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 Refer-
ences, 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.

F05km07

Figure 5-14 Upgraded project group.

You now know the basic procedure for upgrading project groups. If your
Visual Basic 6 project group contains many projects, you simply repeat these
C0561587x.fm Page 98 Thursday, November 15, 2001 2:37 PM

98 Part II Upgrading Applications

steps for every project in the group. Start with the most downstream project,
and work your way up the dependency hierarchy.

Using the VB Snippet Upgrade Add-In


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.

F05km15

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 fol-
lowing 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:
C0561587x.fm Page 99 Thursday, November 15, 2001 2:37 PM

Chapter 5 Your First Upgrade 99

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 exam-
ine 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 refer-
ences 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.

Upgrading Using the Command Line


Those of you who prefer using command-line tools over graphical wizards will
be pleased to know that Visual Basic .NET ships with a command-line upgrade
tool: VBUpgrade.exe. If you accept the default file locations during installation,
you will find it installed to the following directory:
<drive>:\Program Files\Microsoft Visual Studio .NET\Vb7\VBUpgrade\
The command-line upgrade tool produces exactly the same result as the
Upgrade Wizard—in fact, they share the same underlying upgrade engine.
Let’s see it in action. Assuming that the tool resides on drive C, open a
Command Prompt window and change the current directory to the location
of the tool, using the CD command, as follows:

CD “C:\Program Files\Microsoft Visual Studio .NET\Vb7\VBUpgrade”


C0561587x.fm Page 100 Thursday, November 15, 2001 2:37 PM

100 Part II Upgrading Applications

Now type the following to see the command-line options:

vbupgrade /?

The upgrade tool shows you its available options:


Microsoft (R) Visual Basic.NET Upgrade Tool Version 7.00.9238.0
Copyright (C) Microsoft Corp 2000-2001.
Portions copyright ArtinSoft S.A.
All rights reserved.
Usage: VBUpgrade <filename> [/Out <directory>] [/NoLog | /LogFile
<filename>] [/Verbose] [/GenerateInterfaces]
/? Display this message
/Out Target directory (default is “.\OutDir”)
/Verbose Outputs status and results
/NoLog Don’t write a log file
/LogFile Log file name (default is
“<ProjectFileName>.log”)
/GenerateInterfaces Generates interfaces for public classes

To upgrade a project, you need to specify the project filename and the
destination directory for the upgraded project. If the destination directory
doesn’t exist already, the upgrade tool will create it for you. For example, the
following statement upgrades C:\Project1.vbp to the C:\Project1.NET directory:

vbupgrade c:\Project1.vbp /Out c:\Project1.NET

Because it is a command-line tool, the upgrade tool doesn’t show any wiz-
ard pages while upgrading. The one difference between the command-line tool
and the Upgrade Wizard is that the command-line tool does not delete any files
in the destination directory. If the destination directory already contains files,
the command-line tool stops with an error.

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.
C0661587x.fm Page 101 Thursday, November 15, 2001 3:08 PM

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 solv-
ing later in this chapter.

A Guide to Working in Visual Basic .NET


Microsoft Visual Studio .NET has a somewhat different development experience
than Visual Basic 6 developers are used to. In fact, the new IDE combines fea-
tures from Visual Basic 6, Microsoft Visual C++, and Microsoft Visual InterDev.
The single most powerful aspect of the new IDE is that it is shared across all
development languages. The Visual Basic editing experience is not all that differ-
ent from the C# or C++ editing experience. The menus change slightly between
project types, but the core interface, illustrated in Figure 6-1, remains the same.
In this section, you’ll develop a simple Windows application to get a feel
for the new IDE. The application will contain two buttons, a TextBox control
and a ListBox control. The first button will add text from the TextBox control to
the ListBox control. The second button will remove the selected item from the
ListBox control. Nice and simple. Let’s rock.

101
C0661587x.fm Page 102 Thursday, November 15, 2001 3:08 PM

102 Part II Upgrading Applications

F06km01

Figure 6-1 New Visual Basic .NET IDE.

Creating a Visual Basic .NET Project


Creating a Visual Basic .NET Windows Forms project is simple: under the File
menu, select New, and then select Project. In the New Project dialog box,
shown in Figure 6-2, select Visual Basic Projects as the project type and Win-
dows Application as the project template. Leave all settings at their defaults and
click OK to finish creating your project. Figure 6-3 shows the new project.

F06km02

Figure 6-2 New Project dialog box.


C0661587x.fm Page 103 Thursday, November 15, 2001 3:08 PM

Chapter 6 Common Tasks in Visual Basic .NET 103

Visual Basic .NET Project Types


Visual Basic .NET offers a sizable list of projects that you can create by
using the New Project dialog box. The notion of a multilanguage “solu-
tion” is evident to anyone who examines this list. The dialog box does not
change, whether you’re creating a new project or adding a new project to
an existing solution. Here are the Visual Basic .NET project types that are
supported out of the box:
■ Windows application
■ Class library
■ Windows control library
■ ASP.NET Web application
■ ASP.NET Web service
■ Web control library
■ Console application
■ Windows service
■ Empty project
■ Empty Web project
■ New project in existing folder

F06km03

Figure 6-3 Your new project.


C0661587x.fm Page 104 Thursday, November 15, 2001 3:08 PM

104 Part II Upgrading Applications

Getting to Know the Visual Studio .NET IDE


In Visual Basic .NET you still have projects, but instead of project groups (as
you had in Visual Basic 6) you now have solutions. Each solution can contain
multiple projects, even projects created in different programming languages
(for example, Visual C# and C++). Visual Basic .NET now supports additional
project types, including console applications, Web applications, Web services,
and Windows services, all of which can be included in your solutions. You use
the Solution Explorer to organize and manipulate your projects and build set-
tings. By default, you will find the Solution Explorer in the upper right corner
of the screen.
Figure 6-3 demonstrates several differences between Visual Basic .NET
and Visual Basic 6. In the Solution Explorer you can see that references are now
accessible from the project tree, and Visual Basic no longer uses file extensions
to identify forms, classes, and code modules. All Visual Basic files now have the
same extension, .vb.
On the left side of the IDE is the Toolbox. Moving your mouse over the
Toolbox tab causes the drawer to slide out from the side of the screen. The
Toolbox view defaults to the Windows Forms tab, which lists all the controls
available to your application.
Now we are going to walk through creating a simple Windows applica-
tion. To create a Windows Forms project, do the following:

1. Drag a TextBox control from the Toolbox and position it in the


upper left corner of the form.
2. Drag a Button control from the Toolbox and position it to the right of
the TextBox.
3. Drag another Button from the Toolbox and position it to the right of
the first button.
4. Drag a ListBox control to the form and position it below the TextBox
and buttons.
5. Position the controls so that the form looks roughly like Figure 6-4.

Now let’s set some properties for the controls we just added to the form.

1. Click the Button1 control. The property page should be visible on


the right. If it’s not, right-click the control and select Properties from
the shortcut menu.
C0661587x.fm Page 105 Thursday, November 15, 2001 3:08 PM

Chapter 6 Common Tasks in Visual Basic .NET 105

F06km04

Figure 6-4 Newly added controls.

2. In the property page set the Text property to &Add and the (Name)
property to AddButton.
3. Click the second button and set the Text property to &Remove and
the Name property to RemoveButton.
4. Click the ListBox1 control and find the Anchor property. (It is in the
group of properties that specify layout, toward the bottom of the
properties list.) Click the down arrow and make sure the top, left,
right, and bottom sides of the control are selected. Setting these
options will ensure the correct resizing behavior of the control.

You now have a Microsoft .NET application that doesn’t do much but look
pretty and resize nicely. Let’s add some functionality. Double-click the Add-
Button control. This takes you to the Code Editor in the AddButton_Click
method. Add the following line of code:
ListBox1.Items.Add(TextBox1.Text)

From the left drop-down menu directly above the editor, select Remove-
Button. In the drop-down menu on the right, select the Click event. This creates
a RemoveButton_Click event and positions the cursor inside the event. Now
you should add the following line of code:
ListBox1.Items.RemoveAt(ListBox1.SelectedIndex)

Not only have we implemented the desired functionality, but we’ve also
explored two ways of associating event handlers with controls in Visual Basic
.NET. Now that we have the features we want, let’s run the application.
C0661587x.fm Page 106 Thursday, November 15, 2001 3:08 PM

106 Part II Upgrading Applications

Running Your Project


Compiling in Visual Basic .NET is different from compiling in Visual Basic 6. In
Visual Basic 6, you could run your application with coding errors. The IDE
would break in and notify you of problems before it would execute problem-
atic code. In Visual Basic .NET, your entire application has to be compiled
before you can test anything at run time.
To run your application, press F5. This step will cause Visual Basic .NET to
compile the application before launching it under the debugger. At this point
we shouldn’t have any errors. Everything should work just fine and look some-
thing like Figure 6-5 (after the user has added a few listbox entries).

F06km05

Figure 6-5 Your new Visual Basic .NET application.

It turns out that everything is not working just fine. Try selecting an item
in the listbox (add one if necessary), and then click the Remove button twice.
You will get the error shown in Figure 6-6.

F06km06

Figure 6-6 The application is broken.

So it’s broken. Click Break and let’s dive into the debugger.
C0661587x.fm Page 107 Thursday, November 15, 2001 3:08 PM

Chapter 6 Common Tasks in Visual Basic .NET 107

A Quick Introduction to Debugging


Debugging in Visual Basic .NET is not exactly what traditional Visual Basic
developers are used to. Probably the most surprising missing feature is Edit And
Continue. However, Visual Basic .NET offers a host of new features that greatly
enhance the debugging experience, including service debugging, cross lan-
guage debugging, XCopy deployment, structured exception handling, and a
more sophisticated debugging API.

Where Is Edit And Continue?


The developers of the initial version of Visual Basic .NET decided not to
include Edit And Continue. Dropping a feature that Visual Basic develop-
ers have come to rely on was not done lightly, but it had to be done.
Visual Basic .NET and the .NET Framework are totally new platforms
developed from the ground up. The challenge was to provide all the fea-
tures of previous platforms that had come about during a decade of refine-
ments. Unfortunately, including Edit And Continue would have meant
delaying the initial release of Visual Basic .NET. Although it is a sadly
missed feature, don’t let this omission detract from the other powerful
debugging features available in Visual Basic .NET.

In the preceding section, we learned that the application is throwing an


exception on the following line of code, and we need to find out why. The
exception dialog box has already told us that the error was an ArgumentOutOf-
Range exception. Looking at the code, we can see that the problem is located
in the SelectedIndex property of the ListBox1 control:
ListBox1.Items.RemoveAt(ListBox1.SelectedIndex)

The Autos window in the lower left side of the IDE displays the value for
SelectedIndex. When no item is selected, the SelectedIndex property is –1. You
can also use the Command window to inspect the SelectedIndex property. Just
type ?ListBox1.SelectedIndex to print out the value in the Command window.
One possible solution is to add code that will check for this condition
before trying to remove an item from the ListBox control. You must end the
debugging session before you can make any changes to the source file. The
current version of Visual Studio .NET locks the source files during debug ses-
sions. Moving to the Code Editor, replace the body of the RemoveButton_Click
event with the following code:
C0661587x.fm Page 108 Thursday, November 15, 2001 3:08 PM

108 Part II Upgrading Applications

If ListBox1.SelectedIndex <> -1 Then


ListBox1.Items.RemoveAt(ListBox1.SelectedIndex)
End If

This code prevents the application from attempting to remove any items
from the ListBox unless an item is selected. It will avoid the exception previ-
ously encountered at this point. There is, however, another way to do this—
possibly a more proper way from a user-interface perspective. It makes sense
that the user should not be able to click the Remove button unless it will actu-
ally perform a valid action. To specify this behavior, do the following:

1. In the Form Designer, select the Remove button and change its
Enabled property to False.
2. In the Code Editor, select the ListBox1 control from the left drop-
down menu. Select the SelectedIndexChanged event from the right
drop-down menu. Add the following code to the new event handler:

If ListBox1.SelectedIndex = -1 Then
RemoveButton.Enabled = False
Else
RemoveButton.Enabled = True
End If

The end result is that by default, the Remove button is disabled at startup.
As soon as an item is selected in the listbox, the Remove button is enabled. If
the user clicks the button, the selected item is removed, and the button is again
disabled.

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.

Handling Build Errors


If your project contains compile errors, when you build it Visual Basic .NET
shows the dialog box seen in Figure 6-7.
C0661587x.fm Page 109 Thursday, November 15, 2001 3:08 PM

Chapter 6 Common Tasks in Visual Basic .NET 109

F06km07

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.

Using the Task List


The Task List has several different uses. First and foremost, it reports compila-
tion 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.

F06km08

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 Envi-
ronment 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
C0661587x.fm Page 110 Thursday, November 15, 2001 3:08 PM

110 Part II Upgrading Applications

of your application and prioritizing feature areas. For example, you can cre-
ate 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 Jan-
ice that indicate areas that individuals on your team need to address. This
approach is often useful when implementing large applications.

F06km9

Figure 6-9 Visual Studio .NET Options dialog box.

These comments would look like this in your code:

‘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.
C0661587x.fm Page 111 Thursday, November 15, 2001 3:08 PM

Chapter 6 Common Tasks in Visual Basic .NET 111

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.

F06km10

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.
C0661587x.fm Page 112 Thursday, November 15, 2001 3:08 PM

112 Part II Upgrading Applications

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 Ref-
erence 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.

F06km11

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.

Using the System.Diagnostics Library


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 diag-
nosing various application problems. Although their functionality is slightly dif-
ferent, 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
C0661587x.fm Page 113 Thursday, November 15, 2001 3:08 PM

Chapter 6 Common Tasks in Visual Basic .NET 113

(MSDN) documentation is that the Trace class can be used to instrument release
builds. Instrumentation allows you to monitor the health of your application run-
ning in real-life settings, isolate problems, and fix them without disturbing a run-
ning 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 insert-
ing 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.

Where Do I Get CorDbg?


CorDbg is located in the %SYSVOL%\Program Files\Microsoft Visual Stu-
dio .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.
C0661587x.fm Page 114 Thursday, November 15, 2001 3:08 PM

114 Part II Upgrading Applications

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 com-
mands is typical:

1. Type pro to get a list of managed processes and process IDs.


2. Type at [pid] to attach to the process.
3. Type ca e to catch all exceptions.
4. Type g to continue the program execution.

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 simi-
lar debugging experience to the Visual Basic .NET debugger.

Simplifying Complex Expressions


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 expres-
sions 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 follow-
ing Visual Basic .NET line is a complex expression because there are several
statements on one line:

MsgBox(Format(CDate(myString & “/2002”)))

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 stor-
ing the results of each statement in a temporary variable. The following sample
shows how to do this:

Dim tempString, formattedString As String, tempDate As Date


tempString = myString & “/2002"
tempDate = CDate(tempString)
formattedString = Format(tempDate, “d-mmm-yyyy”)
MsgBox(formattedString)
C0661587x.fm Page 115 Thursday, November 15, 2001 3:08 PM

Chapter 6 Common Tasks in Visual Basic .NET 115

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.

Conclusion

This chapter covered a lot of information, from the bare debugging essentials to
more sophisticated troubleshooting methods. We hope this chapter has pro-
vided you with a basic understanding of how to work with the IDE and how to
approach debugging applications in Visual Basic .NET. The Visual Basic .NET
debugging experience is significantly different from what you’re familiar with in
Visual Basic 6. This helps emphasize the importance of using the techniques
discussed in this chapter to ensure that you can work effectively with your
applications.
C0661587x.fm Page 116 Thursday, November 15, 2001 3:08 PM
C0761587x.fm Page 117 Thursday, November 15, 2001 2:43 PM

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 fol-
lowing statements:
“It’s your code.”
“Just make it work.”

It’s Your Code


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 rec-
ognize your code after the wizard has upgraded it. If the wizard transforms your

117
C0761587x.fm Page 118 Thursday, November 15, 2001 2:43 PM

118 Part II Upgrading Applications

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 ele-
ment 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 Frame-
work equivalent, Button.Text property. If no element in Visual Basic .NET rep-
resents 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.

Just Make It Work


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 List-
View ActiveX control in your application with the .NET Framework ListView
control, but it doesn’t. The wizard doesn’t make this upgrade because by reus-
ing 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 con-
tains language statements, controls, and objects that are compatible and most
familiar to you. Once you get the application running, you can choose to incre-
mentally replace code or components with .NET equivalents, and then test each
change as you go.
C0761587x.fm Page 119 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 119

Compatibility Library
To help your upgraded applications work, Visual Basic .NET includes a com-
patibility library. This library enables support for features such as control arrays,
ADO data binding, and loading resources from a .res file. You can also use
some of the functions in the compatibility library to convert between twips and
pixels or between System.Drawing.Image and IPicture. All objects and func-
tions contained in the compatibility library can be found under the namespace
Microsoft.VisualBasic.Compatibility.VB6. The Upgrade Wizard automatically
adds a reference to the compatibility library for all upgraded applications.

Upgrade Wizard Capabilities and Limitations


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 intrinsic controls to .NET Framework Windows Forms equiva-
lent controls
■ 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 Frame-
work (or in some cases the Visual Basic .NET compatibility library)
C0761587x.fm Page 120 Thursday, November 15, 2001 2:43 PM

120 Part II Upgrading Applications

Figure 7-1 illustrates how the elements of a form are upgraded. Figure 7-2 illus-
trates how code is upgraded.

ActiveX controls

User controls

Intrinsic controls

Compatibility class

F07km01

Figure 7-1 How a form is upgraded.

Most code is same


Syntax changes

Compatibility class

Behavior warning

F07km02

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.
C0761587x.fm Page 121 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 121

By reusing existing ActiveX components in your Visual Basic .NET appli-


cations 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 sup-
port for the following items:

■ ActiveX EXE projects


■ Designers such as Add-In Designer, DHTML Page Designer, Data-
Report, and DataEnvironment
■ Certain ActiveX controls: SSTab and UpDown
■ ActiveX document–based applications
■ Extensibility model–based applications
■ OLE container control
■ Drag and drop
■ Graphics statements and graphical controls
■ Dynamic Data Exchange

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. Equiva-
lent Visual Basic .NET project types exist for common project types such as
Standard EXE, ActiveX DLL, and UserControl.
C0761587x.fm Page 122 Thursday, November 15, 2001 2:43 PM

122 Part II Upgrading Applications

Note 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
Visual Basic 6 Project Type Visual Basic .NET Project Type
Standard EXE Windows application
ActiveX EXE No equivalent (Choose between upgrade to a
Windows application or a Class Library project.)
ActiveX DLL Class Library
ActiveX control Windows control library
WebClass-based project XML Web Forms

Project Attributes
A Visual Basic 6 project contains a number of attributes, including Name,
command-line arguments, conditional compilation constant settings, Help set-
tings, 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 con-
tinue 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.
C0761587x.fm Page 123 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 123

F07km03

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.

F07km04

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.
C0761587x.fm Page 124 Thursday, November 15, 2001 2:43 PM

124 Part II Upgrading Applications

F07km05

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 Prop-
erties from the Project menu. Within the Properties dialog box, select Build
under the Configuration Properties folder, as shown in Figure 7-6.

F07km06

Figure 7-6 Visual Basic .NET custom constants setting under


Configuration Properties, Build.
C0761587x.fm Page 125 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 125

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 Prop-
erties dialog box, as shown in Figure 7-7. You could also set a project-level
Help context ID.

F07km07

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 dif-
ferent. 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.

More Info 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 Applica-
tions; 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.
C0761587x.fm Page 126 Thursday, November 15, 2001 2:43 PM

126 Part II Upgrading Applications

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
■ Major Version Number
■ Minor Version Number
■ Revision

F07km08

Figure 7-8 Visual Basic 6 version settings in the Project


Properties dialog box.

The attributes are upgraded to a separate file in your project called


AssemblyInfo.vb, which contains project-level attributes for your project. For
C0761587x.fm Page 127 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 127

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(““)>

Note 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 num-
ber 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 com-
piler 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.

Individual item filenames 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
C0761587x.fm Page 128 Thursday, November 15, 2001 2:43 PM

128 Part II Upgrading Applications

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.

Forms and Intrinsic Controls


The Upgrade Wizard upgrades forms and intrinsic controls, such as Command-
Button, 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 pre-
served. 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.

Mapping Between Visual Basic 6 Intrinsic Controls and .NET


Table 7-2
Windows Forms Controls
Visual Basic 6 Visual Basic .NET
Upgrade Notes
Control Class Control Class
VB.CheckBox System.Windows.-
Forms.Checkbox
VB.ComboBox System.Windows.-
Forms.ComboBox
VB.CommandButton System.Windows.-
Forms.Button
VB.Data No equivalent Because the Data control
supports DAO data bind-
ing—which is not sup-
ported by Windows
Forms—the control
doesn’t upgrade. You can
use the ADO data control
in its place, update your
code to use ADO data
binding, and the ADO
control will upgrade.
VB.DirListBox DirListBox in
Microsoft.Visual-
Basic.Compatibility.VB6
VB.DriveListBox DriveListBox in
Microsoft.Visual-
Basic.Compatibility.VB6
C0761587x.fm Page 129 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 129

Mapping Between Visual Basic 6 Intrinsic Controls and .NET


Table 7-2
Windows Forms Controls (continued)
Visual Basic 6 Visual Basic .NET
Upgrade Notes
Control Class Control Class
VB.FileListBox FileListBox in
Microsoft.VisualBasic.-
Compatibility.VB6
VB.Frame System.Windows.- If the frame has a border,
Forms.GroupBox or Sys- the control upgrades to a
tem.Windows.Forms.- GroupBox; it upgrades to a
Panel Panel if there is no border.
VB.HScrollBar System.Windows.-
Forms.HScrollBar
VB.Image System.Windows.- The Visual Basic 6 image
Forms.Picture control is a graphical con-
trol, which can be trans-
parent. Windows Forms
doesn’t support transpar-
ent controls, so transpar-
ency will be lost after
upgrade.
VB.Label System.Windows.- Visual Basic 6 labels are
Forms.Label graphical and can be
transparent. Visual Basic
.NET labels have a win-
dow and cannot be trans-
parent. Transparency will
be lost after upgrade.
VB.Line No equivalent or For vertical and horizontal
System.Windows.- lines a label is used. The
Forms.Label label is set to the width of
the line. The back color of
the label is set to the line
color. Diagonal lines do
not map.
VB.ListBox System.Windows.-
Forms.ListBox
(continued)
C0761587x.fm Page 130 Thursday, November 15, 2001 2:43 PM

130 Part II Upgrading Applications

Mapping Between Visual Basic 6 Intrinsic Controls and .NET


Table 7-2
Windows Forms Controls (continued)
Visual Basic 6 Visual Basic .NET
Upgrade Notes
Control Class Control Class
VB.OLE No equivalent In some cases you can
replace the control with
the Microsoft Web
Browser control and use
the Navigate property.
VB.OptionButton System.Windows.-
Forms.OptionButton
VB.PictureBox System.Windows.- If the PictureBox has child
Forms.PictureBox or controls, it upgrades to a
System.Windows.- Panel; otherwise, it
Forms.Panel upgrades to a PictureBox.
VB.Shape No equivalent Shape is a graphical con-
trol; Windows Forms
doesn’t support graphical
controls. You can use the
.NET Framework graph-
ics commands in place of
the control.
VB.TextBox System.Windows.-
Forms.TextBox
VB.Timer System.Windows.- Setting the Interval prop-
Forms.Timer erty of a Visual Basic .NET
timer to 0 will throw an
exception. You need to
set the Enabled property
of a Timer to False to dis-
able this behavior.
VB.VScrollBar System.Windows.-
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.
C0761587x.fm Page 131 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 131

F07km09

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 gen-
erated 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.)

F07km10

Figure 7-10 Visual Basic 6 form with a button on it.


C0761587x.fm Page 132 Thursday, November 15, 2001 2:43 PM

132 Part II Upgrading Applications

The Visual Basic .frm file for the form will contain this description:
Begin VB.Form Form1
Caption = “Form1"
ClientHeight = 2085
ClientWidth = 3750
ClientLeft = 60
ClientTop = 345
Begin VB.CommandButton cmdMyButton
Caption = “MyButton"
TabIndex = 0
Default = -1 ‘True
Left = 1080
Top = 720
Width = 1455
Height = 495
End
End

If you upgrade the preceding Visual Basic .frm, the generated Visual Basic
.NET code will be

Private Sub InitializeComponent()


Me.cmdMyButton = New System.Windows.Forms.Button

Me.Text = “Form1"
Me.ClientSize = New System.Drawing.Size(250, 139)
Me.Location = New System.Drawing.Point(4, 23)

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

Note We’ve removed superfluous settings from the .frm and


InitializeComponent 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 map-
pings from (for example) Property X to Property X.
C0761587x.fm Page 133 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 133

Table 7-3 shows how the original .frm description and the generated
Visual Basic .NET code match up line for line.

Table 7-3Mapping Between Visual Basic 6 .frm Settings and Visual


Basic .NET Code
Visual Basic 6 .frm Visual Basic .NET
Upgrade Notes
Setting Code
Caption = "Form1" Me.Text = "Form1" Caption property is upgraded
to Text property in Windows.-
Forms. Generally you’ll see
Caption properties mapped to
Text properties in your code.
ClientHeight = 2085 Me.ClientSize = Form Height and Width set-
New System.- tings are mapped to the Visual
ClientWidth = 3750
Drawing.Size(250, Basic .NET form’s ClientSize
139) property. Sizes are automati-
cally converted from twips to
pixels.
ClientLeft = 60 Me.Location = New Left and Top settings are
System.Drawing.- mapped to the Location prop-
ClientTop = 345
Point(4, 23) erty. Positions are automati-
cally converted from twips to
pixels.
Begin VB.Command- Me.cmdMyButton = In Visual Basic 6, the run time
Button cmdMyButton New System. takes care of creating the form
Windows.Forms.- and controls spelled out in the
Button .frm file. In Visual Basic .NET,
declaration and allocation of
each control is explicit.
Caption = "MyButton" Me.cmdMyButton.- Caption is upgraded to Text.
Text =
"MyButton"
Default = -1 "True" Me.AcceptButton = Default and Cancel are no
Me.cmdMyButton longer properties of individual
buttons in Visual Basic .NET.
Instead, you set the AcceptBut-
ton and CancelButton proper-
ties of the form to point to the
respective Default and Cancel
buttons. In this case the
Default design-time property
setting is automatically
upgraded to the AcceptButton
setting on the current form.
C0761587x.fm Page 134 Thursday, November 15, 2001 2:43 PM

134 Part II Upgrading Applications

After the form is upgraded, it will look nearly identical to its Visual Basic 6
counterpart, as shown in Figure 7-11.

F07km11

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 MyBut-
ton 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 sec-
tion, 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 possi-
ble, it offers suggestions on how to work around or solve the issue.
C0761587x.fm Page 135 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 135

F07km12

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 con-
trol 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 chap-
ter 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. Con-
trol arrays and all associated code are upgraded as is, with minor syntactical
changes in some cases, as illustrated in the following example.
C0761587x.fm Page 136 Thursday, November 15, 2001 2:43 PM

136 Part II Upgrading Applications

Consider a Visual Basic 6 form that has two command buttons in a con-
trol array called Command1. The following Visual Basic 6 code loads
Command1(2) as a new control array element:
Private Sub Command1_Click(Index As Integer)
MsgBox “Command1 (“ & Index & “) clicked"
End Sub

Private Sub Form_Load()


Load Command1(2)
Command1(2).Left = Command1(1).Left + _
Command1(1).Width
Command1(2).Visible = True
End Sub

Here’s the Visual Basic .NET code after the Upgrade Wizard has upgraded it:

Private Sub Command1_Click(ByVal eventSender As System.Object, _


ByVal eventArgs As System.EventArgs) _
Handles Command1.Click
Dim Index As Short = Command1.GetIndex(eventSender)
MsgBox(“Command1 (“ & Index & “) clicked”)
End Sub

Private Sub Form1_Load(ByVal eventSender As System.Object, _


ByVal eventArgs As System.EventArgs) _
Handles MyBase.Load
Command1.Load(2)
Command1(2).Left = _
VB6.TwipsToPixelsX(VB6.PixelsToTwipsX(Command1(1).Left) + _
VB6.PixelsToTwipsX(Command1(1).Width))

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.
C0761587x.fm Page 137 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 137

ActiveX Controls and ActiveX References


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 sec-
tion focuses on some of the exceptions to the rule that ActiveX controls and refer-
ences are upgraded to use the same component. In some cases, an ActiveX control
or reference is upgraded to use a native .NET component replacement.

ADO Data Control


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 con-
trols. 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 communi-
cate the bound data to the Windows Forms TextBox control. The ADODC con-
trol 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 com-
patible 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 applica-
tions to continue working after you upgrade to Visual Basic .NET.

Microsoft Data Binding Collection


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 prop-
erty 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.
C0761587x.fm Page 138 Thursday, November 15, 2001 2:43 PM

138 Part II Upgrading Applications

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 Wiz-
ard 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 fol-
lowing code to hook up data binding:

Private ADOBind_Adodc1 As VB6.MBindingCollection

Public Sub VB6_AddADODataBinding()


ADOBind_Adodc1 = New VB6.MBindingCollection()
ADOBind_Adodc1.DataSource = CType(Adodc1, msdatasrc.DataSource)
ADOBind_Adodc1.Add(Text1, “Text", “CompanyName", Nothing, “Text1”)
ADOBind_Adodc1.UpdateMode = _
VB6.UpdateMode.vbUpdateWhenPropertyChanges
ADOBind_Adodc1.UpdateControls()
End Sub

Public Sub VB6_RemoveADODataBinding()


ADOBind_Adodc1.Clear()
ADOBind_Adodc1.Dispose()
ADOBind_Adodc1 = Nothing
End Sub

Note In Visual Basic 6, the binding collection is called BindingCol-


lection. In Visual Basic .NET, the binding collection is called MBinding-
Collection. The M stands for managed, meaning the managed code
version of the BindingCollection.
C0761587x.fm Page 139 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 139

VB6_AddADODataBinding is called from Sub New so that the data bind-


ings 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 applica-
tion 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 con-
trol, you know that it would be pretty useless if you couldn’t place other con-
trols inside it. The control reaches the height of uselessness when you place it
on a Windows form. Windows Forms doesn’t support the ISimpleFrame inter-
face, so you can’t place any controls on the tabs within the SSTab.
The Upgrade Wizard recognizes this situation and rectifies it by automati-
cally upgrading your instances of SSTab controls to instances of Windows
Forms TabControls. The Windows Forms TabControl is similar—but not identi-
cal—in behavior to the SSTab control. You’ll find that after upgrade, the Tab-
Control 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 Tab-
Control 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 appli-
cation. The UpDown control doesn’t update the buddy control.
C0761587x.fm Page 140 Thursday, November 15, 2001 2:43 PM

140 Part II Upgrading Applications

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.

Visual Basic Code


The Visual Basic .NET language is largely a superset of the Visual Basic 6 lan-
guage. 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.

General Types of Code Conversions


The Upgrade Wizard upgrades your Visual Basic 6 language statements using a
variety of means, which are explained in the following sections.

Mapping to native language statement with the same name 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.

Mapping to native language statement with a different name T h e f u n c t i o n s


IsEmpty, IsNothing, and IsObject fall into this category. Each of these func-
tions operates on Variants, which aren’t supported in Visual Basic .NET. These
functions are upgraded to functions that operate on Object types. The following
Visual Basic 6 code:
Dim b As Boolean
Dim v As Variant

b = IsEmpty(v)
b = IsObject(v)
b = IsMissing(v)
b = IsNull(v)

upgrades to the following Visual Basic .NET code:

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)
C0761587x.fm Page 141 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 141

‘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 exam-
ple of how conversion functions are added after upgrade, consider the follow-
ing Visual Basic 6 code:
Dim i As Integer
Dim TodaysDate As Date
Dim d As Double
i = “7"
TodaysDate = Now
d = TodaysDate

This code upgrades to the following Visual Basic .NET code:

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

Note If you need to convert between types in your Visual Basic .NET
code, look at the methods implemented by the .NET System.Convert
class.

Mapping to an equivalent .NET Framework function Math functions such as Abs,


Atn, Cos, and Sqrt fall into this category. Certain properties on the App object,
such as PrevInstance, upgrade to .NET equivalents as well. For example, take a
look at the following Visual Basic 6 code:
Dim i As Integer
i = Abs(-1)
C0761587x.fm Page 142 Thursday, November 15, 2001 2:43 PM

142 Part II Upgrading Applications

This code upgrades to the following Visual Basic .NET code, using the equiva-
lent 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:
Dim strPath As String
strPath = App.Path

upgrades to

Dim strPath As String


strPath = VB6.GetPath

No mapping available 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:
Dim iCharCode As Integer
Dim BinaryString() As Byte
BinaryString = StrConv(“This is my binary string", vbFromUnicode)
iCharCode = AscB(BinaryString)

upgrades to the following Visual Basic .NET code:

Dim iCharCode As Short


Dim BinaryString() As Byte
‘UPGRADE_ISSUE: Constant vbFromUnicode was not upgraded.
‘UPGRADE_TODO: Code was upgraded to use
‘System.Text.UnicodeEncoding.Unicode.GetBytes() which may not have the
‘same behavior.
BinaryString = System.Text.UnicodeEncoding.Unicode.GetBytes(StrConv( _
“This is my binary string", vbFromUnicode))
‘UPGRADE_ISSUE: AscB function is not supported.
iCharCode = AscB(BinaryString)

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.
C0761587x.fm Page 143 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 143

Option Explicit On Included Automatically


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 state-
ment (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 appro-
priate type. For example, the following Visual Basic 6 code:
DefStr A-Z ‘All unqualified variable declarations will be Strings
Sub Main
Dim s
s = “I’m a string"
End Sub

upgrades to the following Visual Basic .NET 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 perfor-
mance implications of using one size or another, but generally it makes no dif-
ference. 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
C0761587x.fm Page 144 Thursday, November 15, 2001 2:43 PM

144 Part II Upgrading Applications

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-4Mapping of Types Between Visual Basic 6 and Visual


Basic .NET
Visual Basic 6 Type Upgrades to Visual Basic .NET Type
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 param-
eters 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 con-
vention is ByRef, the Upgrade Wizard upgrades and qualifies all unqualified
parameters with the ByRef keyword.
For example, the following Visual Basic 6 code:
Sub Test(MyParam As Long)
MyParam = 7
End Sub

upgrades to the following Visual Basic .NET code:

Sub Test(ByRef MyParam As Integer)


MyParam = 7
End Sub

Code-Behind Forms
Code-behind forms refers to code that makes reference to a form, control, prop-
erty, 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
C0761587x.fm Page 145 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 145

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 upgraded code will appear as follows:

‘UPGRADE_ISSUE: CommandButton property cmdMyButton.UseMaskColor


‘was not_upgraded. Click for more:
‘‘ms-help://MS.VSCC/commoner/redir/redirect.htm?keyword="vbup2064"‘
cmdMyButton.UseMaskColor = True

The Upgrade Wizard leaves the code in as is—without omitting it or com-


menting 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 sur-
faced months later, you might end up spending a significant amount of time
tracking down the line of code that the Upgrade Wizard conveniently com-
mented 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 man-
ually upgrade the code related to the other global objects. For example, to
upgrade your Printer object–related code, use the .NET System.Drawing.Print-
ing.PrintDocument class. Chapter 12 contains more information on upgrading
global objects.

Class Modules and User Controls


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.
C0761587x.fm Page 146 Thursday, November 15, 2001 2:43 PM

146 Part II Upgrading Applications

Table 7-5 Class Attribute Mappings


Class Attribute Upgrades To
Private Friend
Public noncreatable Public
Single use Public
Global single use Public
Multiuse Public
Global multiuse Public

Persistable Classes: ReadProperties, WriteProperties


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 gen-
erating 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 prop-
erties to the PropertyBag or stream. When the server is a Visual Basic–authored
ActiveX component, the client will invoke the ReadProperties and WriteProper-
ties 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 Get-
DataMember 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.

Objects for Accessing Data


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
C0761587x.fm Page 147 Thursday, November 15, 2001 2:43 PM

Chapter 7 Upgrade Wizard Ins and Outs 147

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 origi-
nal Visual Basic 6 DataEnvironment. Any code that references the DataEnviron-
ment 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 sup-
ports reading reports stored in DataReport designer format.
C0761587x.fm Page 148 Thursday, November 15, 2001 2:43 PM

148 Part II Upgrading Applications

Conclusion

In terms of upgrade potential, is your application in or out? As discussed in this


chapter, the situation really depends on the type of Visual Basic application
you’ve written. If your application drives the Visual Basic 6 extensibility model,
uses an ActiveX designer such as the DHTML page designer, or relies on
Dynamic Data Exchange (DDE), it’s in the out category. You’ll need to do a sig-
nificant amount of rewriting to make your application work in the .NET envi-
ronment. In some cases, it might make more sense to leave your application
running in Visual Basic 6 and interoperate with it from Visual Basic .NET.
If, on the other hand, your application is a more typical three-tier applica-
tion with a presentation layer that communicates with a server component, it’s
most definitely in the in category. You’ll find that the Upgrade Wizard does a
good job of upgrading applications that fall into this category. The Upgrade
Wizard will strive to upgrade your application in the least intrusive way possi-
ble that will allow you to get it up and running quickly. Using the Upgrade Wiz-
ard, you will be able to get your Visual Basic 6 application up and running in
Visual Basic .NET more quickly than by porting your Visual Basic 6 application
by hand.
C0861587x.fm Page 149 Thursday, November 15, 2001 2:49 PM

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 under-
stand 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

149
C0861587x.fm Page 150 Thursday, November 15, 2001 2:49 PM

150 Part II Upgrading Applications

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

The second line is also upgraded automatically:

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:
o.Caption = “VB Rocks”

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:

‘UPGRADE_WARNING: Couldn’t resolve default property of object o.Cap-


tion. ‘Click for more: ms-help://MS.MSDNVS/vbcon/html/vbup1037.htm

o.Caption = “VB Rocks”

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.
C0861587x.fm Page 151 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 151

Declaring it as a label makes it strongly typed, and the wizard will have no trou-
ble 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”

After upgrading, the code becomes

Dim o As System.Windows.Forms.Label
o = Form1.DefInstance.Label1
o.Text = “VB Rocks”

This code works perfectly in Visual Basic .NET.


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 con-
verting 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 exam-
ples show the futility of this strategy. In the following example, even the origi-
nal 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:

‘UPGRADE_WARNING: Couldn’t resolve default property of object o.Cap-


tion. ‘Click for more: ms-help://MS.MSDNVS/vbcon/html/vbup1037.htm

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 com-
pilation or execution of your project.
C0861587x.fm Page 152 Thursday, November 15, 2001 2:49 PM

152 Part II Upgrading Applications

Identifying All the Problems


Does the Upgrade Wizard detect every problem during an upgrade? Is
every problem marked in your code with easy-to-follow guidance? Unfor-
tunately, 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 prob-
lems it finds, but as you’ll see later in this chapter, there are some that it
doesn’t warn about.

The Different Kinds of EWIs


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

‘UPGRADE_ISSUE: TextBox method Text1.OLEDrag was not upgraded


Text1.OLEDrag

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.
C0861587x.fm Page 153 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 153

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 fol-
lowing 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 key-
word 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 ini-
tializes 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 Initial-
ize method. The following code shows the modified Sub Main:

Public Sub Main()


Dim myVariable As myType
myVariable.Initialize()
End Sub
C0861587x.fm Page 154 Thursday, November 15, 2001 2:49 PM

154 Part II Upgrading Applications

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:
Dim myFilename As String
myFilename = Dir(“C:\Temp\*.*", vbDirectory)

the Dir function is marked with an upgrade warning:

Dim myFilename As String


‘UPGRADE_WARNING: Dir has a new behavior.
myFilename = Dir(“C:\Temp\*.*", FileAttribute.Directory)

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 prop-
erty to Manual. When you upgrade the project, the OLEDropMode property is
not upgraded, since there is no direct equivalent in Windows Forms. What hap-
pens 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 associ-
ated 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.
C0861587x.fm Page 155 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 155

Upgrade Notes and Global Warnings


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 sub-


stantially changed.
■ Global warnings are inserted into the upgrade report to alert you to
major issues, such as differences in data binding.

Understanding the Upgrade Report


The upgrade report summarizes any problems found during the upgrade. It is
added to every project during an upgrade, whether or not there were any prob-
lems. Suppose you upgrade a project containing a form with a text box that
implements drag and drop, and a module with a type that has a fixed-size array
in it. The upgrade report might look something like Figure 8-1.

Project name and time of upgrade

Upgrade settings Project items Troubleshooting link


F08km01

Figure 8-1 An upgrade report.


C0861587x.fm Page 156 Thursday, November 15, 2001 2:49 PM

156 Part II Upgrading Applications

Let’s look at the different parts of this report:

■ Project name and time of upgrade The first line of the report
gives the project name; the second line shows the date and time the
project was upgraded.
■ Project items The body of the report contains a table summarizing
the project items (forms, classes, modules, and designers) and the
issues found for each item during the upgrade. This table has seven
columns.
The first and second columns show the upgraded filename and
the original filename, respectively. The third column gives the type
of project item—form, class, module, and so on. The fourth column
summarizes what happened during upgrade: Upgraded (no issues
found), Upgraded with Issues, or Not Upgraded. If the project item
was not upgraded, the original Visual Basic 6 file will simply be
added to the project as a related document. The fifth and sixth col-
umns show the count of errors and warnings for each project item,
and the seventh column adds these two numbers to give the total
number of issues.
The table has a row for each project item, plus an extra row at
the top of the table labeled (Global Issues) that notifies you of any
project-level issues (such as general differences in data binding).
Each row has a plus symbol on the left. If you click the plus symbol
beside Form1, you’ll see the individual issues, as shown in Figure 8-2.

F08km02

Figure 8-2 Expanded view of Form1 issues.

Form1 has one design error. The OLEDragMode property of


TextBox1 was not upgraded. In the report, the description of each
error has a second purpose. The description is also a hyperlink that
navigates to Help for the EWI, just as the EWI comments in code do.
C0861587x.fm Page 157 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 157

■ Troubleshooting link This hyperlink navigates to general Help


on troubleshooting upgrading. The Help topic also links to pages on
the Internet offering the latest troubleshooting information.
■ Upgrade settings The final section of the report shows the
Upgrade Wizard settings, locations of the Visual Basic 6 and Visual
Basic .NET projects, and any options that were set.

Estimating Fix Time


How long will it take to get your upgraded project working in Visual Basic
.NET? Unfortunately, this chapter cannot give you a guaranteed length of time
that a given project will take. What we can give you is a mechanism for loading
the list of issues into Microsoft Excel and the reassurance that after doing a few
upgrades, you’ll be faster at solving issues and more accurate at knowing which
issues are quick to solve and which take time.
The upgrade report is formatted to be easy to read. For large projects, in
which you want to manage the distribution and status of issues within a group
of developers, loading the issues into a spreadsheet might make more sense.
Doing so allows you to add comments against each issue, such as who the issue
is assigned to and its status. Let’s look at how to do this.
The upgrade report is actually based on an underlying XML log file. The
Upgrade Wizard creates a log file for every upgraded project. The log—called
<projectname>.log—is placed in the same directory as the new Visual Basic
.NET project. This log contains all the information in the upgrade report. In fact,
the upgrade report is actually generated from the log file. The XML log file is
easy for software to read but awkward for humans. Using a simple Excel macro,
we can load the list of issues into a spreadsheet. Follow these steps:

1. Start Excel, and create a new workbook.


2. Add a new CommandButton to the worksheet. To do this, select
View, Toolbars, Control Toolbox. Draw a CommandButton onto the
spreadsheet.
3. Right-click the CommandButton, and choose Properties from the
context menu.
C0861587x.fm Page 158 Thursday, November 15, 2001 2:49 PM

158 Part II Upgrading Applications

4. In the Properties dialog box, change the name of the CommandBut-


ton to LoadUpgradeReport. Change the caption to read Load
Upgrade Report.
5. Double-click the button to open the VBA Macro Editor. Enter the
LoadUpgradeReport code.
6. Choose References from the Tools menu. In the References dialog
box, select Microsoft XML, v3.0, and click OK.
7. Return to the worksheet. Click the Exit Design Mode button, and
then click the Load Upgrade Report button. The macro prompts you
for the filename of the log file. Enter the path to the log file. The
macro populates the worksheet with the contents of the log file, as
shown in Figure 8-3.

F08km03

Figure 8-3 Loading the upgrade log into Excel.

Comments in the Excel code also show other columns that you can add to
the spreadsheet, such as object type, object name, and the underlying EWI
number.
C0861587x.fm Page 159 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 159

Working with EWIs


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 signifi-
cantly 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 filter-
ing 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:

‘TODO: Fix Paint Code later

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 modifi-
cations 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 mod-
ifications might take quite some time to fix, since you’re learning about upgrad-
ing 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.
C0861587x.fm Page 160 Thursday, November 15, 2001 2:49 PM

160 Part II Upgrading Applications

Performing a Two-Pass Upgrade


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 dis-
card the first upgraded project after examining the report. Using the
upgrade report, you fix some of the issues in Visual Basic 6 before upgrad-
ing 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 Different Upgrade EWIs


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 Cal-
endar 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
C0861587x.fm Page 161 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 161

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 <constant-
name> 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 cop-
ies 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 test-
ing for VbVarType.vbDataObject (value 13). Objects that don’t sup-
port 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-mode-
Constants.
C0861587x.fm Page 162 Thursday, November 15, 2001 2:49 PM

162 Part II Upgrading Applications

■ 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 Win-
dows 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.
C0861587x.fm Page 163 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 163

■ <controltype> Property <control>.<property> does not sup-


port 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 col-
lection 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 Ini-
tialize 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.
C0861587x.fm Page 164 Thursday, November 15, 2001 2:49 PM

164 Part II Upgrading Applications

Upgrade Warnings
■ As <variable type> was removed from ReDim <variable>
statement Arrays can be redimensioned using the ReDim state-
ment 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 marshal-
ling 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 initial-
ized before it can be used.
■ Array ‘xxx’ may need to have individual elements
initialized If an array of structures has been declared or redimen-
sioned 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 con-
trol 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.
C0861587x.fm Page 165 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 165

■ 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 func-
tions 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 dif-
ferent 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.Win-
dows.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.
C0861587x.fm Page 166 Thursday, November 15, 2001 2:49 PM

166 Part II Upgrading Applications

■ Event <object>.<event> may fire when form is initialized


Some 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 follow-
ing events:

OptionButton.Click upgraded to CheckChanged


Form.Resize upgraded to Resize
TextBox.Change upgraded to TextChanged
ComboBox.Change upgraded to TextChanged
CheckBox.Click upgraded to CheckStateChanged
ComboBox.Click upgraded to SelectedIndexChanged
DriveListBox.Click upgraded to SelectedIndexChanged
ListBox.Click upgraded to SelectedIndexChanged

■ Value <value> for <controltype> property <control>.<prop-


erty> could not be resolved Some properties and property enu-
merators have changed from Visual Basic 6 to Visual Basic .NET. This
EWI is generated if the Upgrade Wizard can’t map a property or
property enumerator. It usually occurs because the property is being
assigned to a variable or return value of another function that can’t
be resolved.
■ 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.
C0861587x.fm Page 167 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 167

■ <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 Key-
PressEventArgs.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 0
Timer.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 Win-
dows 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:
❑ Control.Keypress event
❑ ComboBox.Change event
C0861587x.fm Page 168 Thursday, November 15, 2001 2:49 PM

168 Part II Upgrading Applications

❑ Form.Activate event
❑ Form.Deactivate event
❑ Form.Picture property
❑ Form.QueryUnload event
❑ Form.Terminate event
❑ Form.Unload event
❑ Form.ZOrder property
❑ HScrollBar.Max property
❑ Image.Stretch property
❑ ListBox.Columns property
❑ ListBox.ItemCheck event
❑ OptionButton.Click event
❑ PictureBox.AutoSize property
❑ Screen.MousePointer property
❑ TextBox.TextLength property
❑ UserControl.Picture property
❑ 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 Item-
Data 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
C0861587x.fm Page 169 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 169

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 rec-
ommended 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 ref-
erences to examine the type libraries, create wrappers, and instanti-
ate 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 installa-
tion 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.
C0861587x.fm Page 170 Thursday, November 15, 2001 2:49 PM

170 Part II Upgrading Applications

■ 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 refer-
ences to the Microsoft Transaction Server Type Library or the COM+
Services Type Library, they are removed during upgrade. These ref-
erences 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 sec-
tion 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 con-
stant 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 con-
stants 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.
C0861587x.fm Page 171 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 171

■ 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 AutoShowChil-
dren = 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 procedure
Added 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 state-
ment 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.
C0861587x.fm Page 172 Thursday, November 15, 2001 2:49 PM

172 Part II Upgrading Applications

Which Problems Are Not Detected?


Perhaps you’re asking yourself, “Which errors doesn’t the Upgrade Wizard
catch?” The Upgrade Wizard doesn’t warn you about five important errors:

■ The wizard doesn’t warn about the New statement. The following
code has a different behavior in Visual Basic 6 than in Visual Basic
.NET (see Chapter 1for a full explanation):
Dim x As New y

■ The wizard doesn’t output an EWI for this because in 99.99 percent
of the cases you won’t notice the difference. If the wizard did create
a warning for every occurrence, some projects would be overcome
with EWIs.
■ The wizard doesn’t detect all coercion problems (in which a variable
of one type is assigned to another type). Some coercions are allowed
in Visual Basic 6 but not in Visual Basic .NET. Generally these errors
are easy to fix, since they almost always result in compile errors.
■ It is common to pass variables as parameters to functions. Often
these parameters are defined as ByRef, which means that if the func-
tion changes the value of the parameter, the underlying variable also
changes. For example, in the following code snippet, myVariable is
passed to a function as myParameter, and the function changes
myParameter to 5, which also changes myVariable to 5, since it is
passed as a ByRef parameter.
Sub Main()
Dim myVariable As Integer
myFunction myVariable
MsgBox myVariable
End Sub

Function myFunction(myParameter As Integer)


myParameter = 5
End Function

■ Visual Basic 6 has one exception to this behavior: if you pass a prop-
erty as a ByRef parameter, it will not be changed. For example, sup-
pose that our application has a form with a TextBox on it called
Text1. The following line of code passes the Text property of Text1
to the function myFunction.
myFunction Form1.Text1.Text
C0861587x.fm Page 173 Thursday, November 15, 2001 2:49 PM

Chapter 8 Errors, Warnings, and Issues 173

■ In Visual Basic 6, because the Text property is a property, it is not


changed when the function explicitly changes the value. In effect,
properties are always passed ByVal. Visual Basic .NET does not have
this limitation; properties passed as ByRef parameters are treated as
ByRef parameters. If the example just given was upgraded to Visual
Basic .NET, the Text property would be changed.
■ In some cases, this may lead to subtle differences in your project’s
run-time behavior. You can force Visual Basic .NET to pass proper-
ties as ByVal parameters by adding parentheses around the variable.
For example, this code passes the Text property ByRef:

myFunction(Me.Text1.Text)

whereas this code passes the Text property ByVal (just as Visual
Basic 6 did):

myFunction((Me.Text1.Text))

■ In Visual Basic 6, you can use ReDim to redimension a variable to


have a different number of indexes. For example, the following code
is valid in Visual Basic 6 but causes a compile error in Visual Basic
.NET:
Dim x()
ReDim x(5, 5)

■ The Upgrade Wizard doesn’t warn you about this, but the compile
error makes it easy to detect and easy to fix. In Visual Basic .NET,
each time a variable is redimensioned, it must always have the same
number of indexes. In Visual Basic .NET, if you change the Dim
statement to dimension the variable with two indexes (using a
comma), it works perfectly:

Dim x(,)
ReDim x(5, 5)

■ The wizard warns you about problems it detects with ActiveX con-
trols. However, there are subtle differences between hosting ActiveX
controls in Visual Basic 6 and hosting them in Visual Basic .NET. The
wizard doesn’t know about all these differences, and therefore it
can’t warn you. For a discussion of using ActiveX controls in Visual
Basic .NET, see Chapter 9 and Chapter 13.
C0861587x.fm Page 174 Thursday, November 15, 2001 2:49 PM

174 Part II Upgrading Applications

It’s important to be conscious of these issues. Learn the methods that will
allow you to handle each of them just as you handle issues that the Upgrade
Wizard detects.

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.
C0961587x.fm Page 175 Thursday, November 15, 2001 3:33 PM

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 compo-
nents. 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 keep-
ing 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)

175
C0961587x.fm Page 176 Thursday, November 15, 2001 3:33 PM

176 Part II Upgrading Applications

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 con-
trol in your application with the .NET version. Eventually your entire system
evolves to .NET, smoothly and without interruption.

Visual Studio .NET Is Built on COM Interop


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.
C0961587x.fm Page 177 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 177

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 applica-
tion 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.

Where COM Interop Comes into Play


There are three common situations in which you will encounter COM interop:
when using ActiveX controls, when calling a COM component from a .NET
application or component, and when calling a .NET component from a COM
application or component. Let’s take a look at each of these situations.

ActiveX Controls
Visual Basic .NET allows you to place either .NET or ActiveX controls on a Win-
dows form. Chapter 13covers ActiveX control hosting in more detail. Suffice it
to say that the way you use ActiveX controls in Visual Basic .NET is nearly iden-
tical to the way you use them in Visual Basic 6. You add the control to the Tool-
box, place the control on the form, set design-time property values, and then
write code behind it. No sweat.

Communication Between a .NET Client and a COM Server Component


The .NET Framework enables .NET clients to communicate with COM compo-
nents. It accomplishes this by wrapping the COM component in a runtime call-
able wrapper (RCW) to make it look like a .NET component, as illustrated in
Figure 9-1. Exposing a COM component for use with .NET requires no addi-
tional work. You simply add the COM component as a reference, and Visual
Studio .NET takes care of generating the RCW for you. Later in this chapter, we
demonstrate how to call a COM server from a .NET Web application.
C0961587x.fm Page 178 Thursday, November 15, 2001 3:33 PM

178 Part II Upgrading Applications

Runtime callable
wrapper
.NET component .NET look-alike
component

COM
component

F09km01

Figure 9-1 Runtime callable wrapper that enables a COM component


to look like a .NET component.

Communication Between a COM Client and a .NET Server Component


In addition to allowing .NET clients to communicate with COM components,
the .NET Framework allows COM applications to communicate with .NET com-
ponents by making a .NET component look like a COM component. It accom-
plishes this by creating a wrapper around the .NET component called a COM
callable wrapper (CCW), as illustrated in Figure 9-2. To expose a .NET compo-
nent to COM, you must register your .NET component as being COM callable.
Visual Studio .NET provides a way for .NET components to be registered auto-
matically for COM. The next section demonstrates how to upgrade a Visual
Basic 6 component to .NET and then expose that .NET component to COM.

COM callable
wrapper
COM component COM look-alike
component

.NET
component

F09km0

Figure 9-2 COM callable wrapper that enables a .NET component to


look like a COM component.

Upgrading a Visual Basic 6 Client/Server Application


Suppose you have a Visual Basic 6 ActiveX DLL that acts as a language transla-
tor, 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 compo-
C0961587x.fm Page 179 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 179

nent from a Visual Basic .NET client application. First, however, you need to
build the Visual Basic 6 server component by following these steps:

1. Run Visual Basic 6.


2. Open TranslatorServer.Vbp provided on the companion CD.
3. Open TranslatorServer.Cls, and you will find that it contains the fol-
lowing code:
Public Function Translate(ByVal SentenceFrom As String, _
ByVal LanguageFrom As String, _
ByVal LanguageTo As String) As String

‘ 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"
End Select
End If

End Function

4. From the File menu, choose Make TranslatorServer.dll and make the
.DLL file into a directory on your hard drive.

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 Transla-
torServer component.
Sub Main()

Dim Translation As String


Dim Ts As New TranslatorServer.Translator
Translation = Ts.Translate(“hello world", “English", “Spanish”)
MsgBox Translation

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.
C0961587x.fm Page 180 Thursday, November 15, 2001 3:33 PM

180 Part II Upgrading Applications

F09km03

Figure 9-3 “Hola mundo” translation successfully received from Visual


Basic 6 server.

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 compo-
nent 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.

Creating a .NET Client That Talks to a COM Server


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.

1. Run Microsoft Visual Studio .NET.


2. Choose New Project from the File menu.
3. Select Visual Basic ASP.NET Web Application, name the application
MyAmazingTranslator, and click OK.
C0961587x.fm Page 181 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 181

4. Right-click References on the Solution Explorer tab, and choose Add


Reference.
5. Select the COM tab.
6. Select TranslatorServer from the list, and click OK.
7. Open WebForm1.aspx.
8. Drag and drop the following controls from the Toolbox to
WebForm1: A Label, a TextBox, and a Button.
9. For the text of the label, enter My amazing English-to-Spanish
translator.
10. For the text of Button1, enter Translate.
11. Double-click the Translate button and insert the following code in
the Button1_Click event handler:

Dim ts As New TranslatorServer.Translator()


TextBox1.Text = ts.Translate(TextBox1.Text, “English", _
“Spanish”)

12. Choose Start from the Debug menu.


13. 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.

F09km04

Figure 9-4 My amazing English-to-Spanish translator at work on


the Web.
C0961587x.fm Page 182 Thursday, November 15, 2001 3:33 PM

182 Part II Upgrading Applications

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.
4. For best results, select No Optimization. See Figure 9-5.

F09km05

Figure 9-5 Recommended Visual Basic 6 options for debugging.

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.
C0961587x.fm Page 183 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 183

5. Click the Component tab.


6. Change the Version Compatibility setting from Project Compatibility
to Binary Compatibility, as shown in Figure 9-6.
7. Click OK to close the Project Properties dialog box.

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.

F09km06

Figure 9-6 Setting binary compatibility in Visual Basic 6.

Now you’re ready to debug.

1. Run Visual Studio .NET and open the MyAmazingTranslator Web


project you created earlier.
2. Select the MyAmazingTranslator project in Solution Explorer.
3. From the Project menu, choose Properties.
4. Expand the Configuration Properties folder and select Debugging.
5. Under Debuggers, click Unmanaged Code Debugging.
6. Click OK to close the dialog box.
7. Open WebForm1.aspx, and double-click the Translate button.
C0961587x.fm Page 184 Thursday, November 15, 2001 3:33 PM

184 Part II Upgrading Applications

8. Insert a breakpoint on the following line:

TextBox1.Text = ts.Translate(TextBox1.Text, “English", _


“Spanish”)

9. From the File menu, choose Open File, and open Transla-
torServer.cls, saved as part of the Visual Basic 6 TranslatorServer
project.
10. Right-click the first line of code in the Translate function, and select
Insert Breakpoint.
11. Choose Start from the Debug menu.
12. Click the Translate button. Execution should break on the following
line:

TextBox1.Text = ts.Translate(TextBox1.Text, “English", _


“Spanish”)

13. 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.

Note 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 privi-
leges. See Visual Studio .NET’s Help system for more details.

Exposing a Visual Basic .NET Component to Be Called


by a Visual Basic 6 Client
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 exam-
ple, 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 envi-
ronment such as Microsoft Transaction Server (MTS) in order to use object pool-
ing, for example.
C0961587x.fm Page 185 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 185

Let’s take the Visual Basic 6 TranslatorServer component located on the com-
panion CD and upgrade it to Visual Basic .NET by performing the following steps:

1. Run Visual Studio .NET.


2. From the File menu, choose Open Project and open Transla-
torServer.vbp.
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 con-
flict with the server name of its COM predecessor.
4. Select the TranslatorServer project in the Solution Explorer.
5. From the Project menu, choose Properties.
6. Change the Assembly Name from TranslatorServer to Transla-
torServer.net.
7. Click OK to close the Project Properties dialog box.
8. View the code for TranslatorServer.vb:
Option Strict Off
Option Explicit On
Public Class Translator

Public Function Translate(ByVal SentenceFrom As String, _


ByVal LanguageFrom As String, ByVal LanguageTo As String) _
As String

‘ 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"
End Select
End If

End Function
End Class

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
C0961587x.fm Page 186 Thursday, November 15, 2001 3:33 PM

186 Part II Upgrading Applications

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:

1. Select the TranslatorServer project in the Solution Explorer.


2. From the Project menu, choose Properties.
3. Under the Configuration Properties folder, select Build.
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 but-
ton to save the Solution file. This step creates the Visual Basic .NET
DLL and also registers it for COM.

F09km07

Figure 9-7 Registering the component for COM interop.

Note Registering a .NET component for COM interop does not nec-
essarily mean that only COM clients can use the component. Instead,
it opens that component up to the world of both COM and .NET. Expos-
ing 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.
C0961587x.fm Page 187 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 187

Now you are ready to call the Visual Basic .NET TranslatorServer compo-
nent 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.
3. Deselect TranslatorServer, the Visual Basic 6 server component.
4. Select TranslatorServer_net, the Visual Basic .NET server component.
5. Open TranslatorClient.bas.
6. Change the following declaration from
Dim Ts As New TranslatorServer.Translator

to

Dim Ts As New TranslatorServer_net.Translator

7. Run the application.

You should see the results shown in Figure 9-3.

Note If you want to add features to your upgraded Visual Basic .NET
component, you should add COM attributes to ensure binary compati-
bility. 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.

Debugging Between the Visual Basic 6 Client and .NET Server


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
C0961587x.fm Page 188 Thursday, November 15, 2001 3:33 PM

188 Part II Upgrading Applications

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 Trans-
late function on the following line:

If LCase(LanguageFrom) = “english” And _


LCase(LanguageTo) = “spanish” Then

3. Select the TranslatorServer project in Solution Explorer.


4. From the Project menu, choose Properties.
5. Under the Configuration Properties folder, select Debugging.
6. Select Start External Program, click the Browse (“…”) button, and
search for VB6.exe.
7. From the Debug menu, choose Start. Visual Basic 6 will launch.
8. From Visual Basic 6, open TranslatorClient.vbp.
9. Place a breakpoint on the following line:
Translation = Ts.Translate(“hello world", “English", _
“Spanish”)

10. Run the Visual Basic 6 client application.

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 debug-
ger. Pretty cool, eh?

Tying It All Together


As you have seen, COM interop enables you to upgrade your application one
piece at a time. You can choose to upgrade one part to .NET while keeping
other parts based on COM. If your goal is to move your application to the Web
or an intranet environment, plenty of options are available to you. For example,
you can create a Web front end that uses the same back-end logic currently in
use by your traditional Windows client applications. You can also make your
back-end functions—your business logic—available to remote clients on the
C0961587x.fm Page 189 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 189

Internet. To do so, you can expose your functions as Web services. In the case
of the Translator class, you can expose the Translate function over the Web by
adding a Web service class to the project, copying the contents of the Transla-
tor class to the Web service class, and marking the Translate function as a Web-
Method. We leave this task as an exercise for you to complete.

Replacing COM with .NET: Binary Compatibility


In creating Visual Basic versions 4, 5, and 6, the Microsoft Visual Basic devel-
opment team worked hard to allow you to create Visual Basic COM compo-
nents 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 ver-
sion 1 component, the version 2 component can include additional objects,
properties, and methods that improve upon the version 1 component. No mat-
ter 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:

A. Invest in fixing the version 2 component to behave identically to the


version 1 component and then invest further in testing the compo-
nent to ensure that no existing version 1 clients are broken by the
changes.
B. 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 com-
ponent and are not disturbed by the distribution of the version 2
component.
C. 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.
C0961587x.fm Page 190 Thursday, November 15, 2001 3:33 PM

190 Part II Upgrading Applications

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 replace-
ment 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:
Const Old_compiler = “VB6.EXE"
Const New_compiler = “VBC.EXE"
Const Old_runtime = “MSVBVM60.DLL"
Const New_runtime = “.NET Framework and Microsoft.VisualBasic.Dll"

If New_compiler <> Old_compiler And New_runtime <> Old_runtime Then


MsgBox “Minor behavioral differences that are expensive to “ & _
“find and fix"
End If

Indirect Replacement Model


Visual Basic .NET takes an indirect approach in creating Visual Basic .NET com-
ponents 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 ver-
sion 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 compo-
nent continue to run against it, unaffected by the changes in the version 2 com-
ponent. 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 wiz-
ard doesn’t give you a full, 100 percent binary-compatible replacement for your
C0961587x.fm Page 191 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 191

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 com-
ments 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.

Enabling Binary Compatibility in Visual Basic .NET Classes


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:

■ Declare all the public class properties and methods in an interface.


■ Declare all the public events in a separate event interface.
■ Add attributes to the class to declare ID attributes for the class, inter-
face, and event interface.
■ Change the class declaration so that it implements both the program-
mable 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 cli-
ent 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.

Option Strict Off


Option Explicit On

Public Class Translator

Public Event TranslationError(ByVal ErrorMessage As String)

Public Function Translate(ByVal SentenceFrom As String, _


ByVal LanguageFrom As String, _
ByVal LanguageTo As String) As String
(continued)
C0961587x.fm Page 192 Thursday, November 15, 2001 3:33 PM

192 Part II Upgrading Applications

Dim fSuccess As Boolean

‘ 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

If Not fSuccess Then


RaiseEvent TranslationError( _
“Translation not implemented for “ & SentenceFrom)
End If
End Function

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.
5. Change the Public Class declaration for ComClass1 to Translator as
follows:

Public Class Translator

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
C0961587x.fm Page 193 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 193

7. Replace the ComClass attribute in the Translator class with the fol-
lowing attributes:

<Guid(ClassId), ComVisible(True)> _
Public Class Translator

8. Define the programmable interface containing the Translate method.


Define the interface after the #End Region for “COM GUIDs” as follows:

<Guid(InterfaceId)> _
Interface ITranslate
<DispId(1)> _
Function Translate(ByVal SentenceFrom As String, _
ByVal LanguageFrom As String, _
ByVal LanguageTo As String) As String
End Interface

9. Immediately following the programmable interface, insert the decla-


ration for the event interface as follows:

<Guid(EventsId)> _
Interface ITranslateEvents
Event TranslationError(ByVal ErrorMessage As String)
End Interface

10. Add an Implements clause after the Public Class declaration to


implement the programmable and event interfaces you just
declared as follows:

Public Class Translator


Implements ITranslate, ITranslateEvents

11. Modify the Public Event declaration to implement the interface event
as follows:

Public Event TranslationError(ByVal ErrorMessage As String) _


Implements ITranslateEvents.TranslationError

12. Modify the public Translate method to implement the ITrans-


late.Translate interface method as follows:

Public Function Translate(ByVal SentenceFrom As String, _


ByVal LanguageFrom As String, _
ByVal LanguageTo As String) _
As String _
Implements ITranslate.Translate
C0961587x.fm Page 194 Thursday, November 15, 2001 3:33 PM

194 Part II Upgrading Applications

When completed, you should have the following class with all the neces-
sary COM attributes defined. You can rebuild the class without breaking com-
patibility 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 meth-
ods 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

#Region “COM GUIDs"


‘ These GUIDs provide the COM identity for this class
‘ and its COM interfaces. If you change them, existing
‘ clients will no longer be able to access the class.
Const ClassId As String = “4B33F612-69C0-4270-A7F3-1B460EBDE978"
Const InterfaceId As String = _
“D1C5A219-A6C9-46CA-939F-8CF2D22FE441"
Const EventsId As String = “86215D08-D4AD-4CA8-9BAC-01E8A592812D"
#End Region

<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

‘ A creatable COM class must have a Public Sub New()


‘ with no parameters; otherwise, the class will not be
‘ registered in the COM registry and cannot be created
‘ via CreateObject.
Public Sub New()
MyBase.New()
End Sub

Public Event TranslationError(ByVal ErrorMessage As String) _


Implements ITranslateEvents.TranslationError
C0961587x.fm Page 195 Thursday, November 15, 2001 3:33 PM

Chapter 9 Using Visual Basic 6 with Visual Basic .NET: COM Interop 195

Public Function Translate(ByVal SentenceFrom As String, _


ByVal LanguageFrom As String, _
ByVal LanguageTo As String) As String _
Implements ITranslate.Translate

Dim fSuccess As Boolean

‘ 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

If Not fSuccess Then


RaiseEvent TranslationError( _
“Translation not implemented for “ & SentenceFrom)
End If

End Function

End Class

Note 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.
C0961587x.fm Page 196 Thursday, November 15, 2001 3:33 PM

196 Part II Upgrading Applications

Conclusion

You can mix and match COM with .NET in any number of ways, depending on
the needs of your business. There are economic and practical reasons for
choosing to have one part of your application based on .NET and the other part
based on COM. The beauty of COM interop is that it gives you a choice. When
upgrading your applications, you don’t have to standardize on .NET immedi-
ately to take advantage of the new features. You can start using those new fea-
tures now and continue to use your existing Visual Basic 6 code and
components as needed to accomplish your goals more quickly and effectively.
C1061587x.fm Page 197 Friday, November 16, 2001 8:43 AM

Part III

Getting Your Project


Working
10 Ten Common Upgrade Problems 199
11 Resolving Issues with Language 223
12 Resolving Issues with Forms 265
13 Upgrading ActiveX Controls and Components 285
14 Resolving Data Access Issues 305
15 Problems That Require Redesign 323
16 Upgrading COM+ Components 347
17 Upgrading VB Application Wizard Projects 365
C1061587x.fm Page 198 Friday, November 16, 2001 8:43 AM
C1061587x.fm Page 199 Friday, November 16, 2001 8:43 AM

Ten Common Upgrade


Problems
Chapter 4 looked at common issues with Microsoft Visual Basic 6 code and dis-
cussed 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 appli-
cations on the companion CD that illustrate some of the situations discussed
here.

Default Properties
Default properties are no longer supported in the common language runtime. If
the Upgrade Wizard can determine a default property, it will properly modify
your code to state the property explicitly in Visual Basic .NET. If it cannot deter-
mine the property being referenced (in the case of late binding), the wizard will
leave your code as is and insert a warning highlighting the issue for you to
resolve. In those cases, the offending line of code will be preserved through the
upgrade and tagged to bring it to your attention. While it is possible (and often
more desirable) to go back to your original code and modify it, there are defi-
nitely cases in which it is not possible to make those changes anywhere but in
the upgraded application.

199
C1061587x.fm Page 200 Friday, November 16, 2001 8:43 AM

200 Part III Getting Your Project Working

The following code samples demonstrate the differences in upgrade


results when default properties are accessed using both late-bound and early-
bound objects. The code is contained in the DefaultProperties sample included
on the companion CD.
Private Sub CopyButton_Click()
EarlyBoundCopy Text1, Text2
LateBoundCopy Text1, Text3
End Sub

‘ This method’s parameters are explicitly defined as TextBoxes


Private Sub EarlyBoundCopy(sourceCtrl As TextBox, destCtrl As TextBox)
destCtrl.Text = sourceCtrl
End Sub

‘ This method’s parameters are variants (the default type)


Private Sub LateBoundCopy(sourceCtrl, destCtrl)
destCtrl.Text = sourceCtrl
End Sub

Although this code runs just fine in Visual Basic 6, the Upgrade Wizard
will insert two run-time warnings in your code. This type of warning means that
the code will compile after upgrading but will generate an exception at run
time. Specifically, the LateBoundCopy method will generate an InvalidCast-
Exception. The exception says the following: “Cast from type ‘TextBox’ to type
‘String’ is not valid.”
Here is the code the Upgrade Wizard generates:

Private Sub CopyButton_Click(ByVal eventSender As System.Object, _


ByVal eventArgs As System.EventArgs) Handles CopyButton.Click
EarlyBoundCopy(Text1, Text2)
LateBoundCopy(Text1, Text3)
End Sub

‘ This method’s parameters are explicitly defined as TextBoxes


Private Sub EarlyBoundCopy(ByRef sourceCtrl As _
System.Windows.Forms.TextBox, ByRef destCtrl As _
System.Windows.Forms.TextBox)
destCtrl.Text = sourceCtrl.Text
End Sub

‘ This method’s parameters are variants (the default type)


Private Sub LateBoundCopy(ByRef sourceCtrl As Object, _
ByRef destCtrl As Object)
‘UPGRADE_WARNING: Couldn’t resolve default property of object
‘destCtrl.Text.
‘UPGRADE_WARNING: Couldn’t resolve default property of object
‘sourceCtrl.
destCtrl.Text = sourceCtrl
End Sub
C1061587x.fm Page 201 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 201

When you encounter these kinds of problems, you will need to modify
your code. Two options are available. First, you can change the definition of
the variable to be strongly typed in Visual Basic 6 and run your application
through the Upgrade Wizard again. Your other option is to explicitly specify the
desired property in Visual Basic .NET. This last option results in the LateBound-
Copy method looking like this:

Private Sub LateBoundCopy(ByRef sourceCtrl As Object, _


ByRef destCtrl As Object)
destCtrl.Text = sourceCtrl.Text
End Sub

Note The second option involves modifying your code to explicitly


use the property you want to read or modify. Notice that this is exactly
what the Upgrade Wizard does for you when it knows what type it is
dealing with. This example illustrates the advantage of always using
early binding. The Upgrade Wizard will know what the default proper-
ties are and will take care of the work for you. Using late binding puts
you in the position of going through all of the upgrade warnings and
manually entering the desired properties.

The fact that you can upgrade an application that contains these kinds of
run-time problems should lead you to two conclusions. First, you need to pay
close attention to the issues raised by the Upgrade Wizard. Second, you must
test your upgraded application thoroughly to make sure that the original func-
tionality has been maintained. The next section examines how using default
properties can also lead to additional and more subtle run-time differences.

AddItem and ToString with COM Objects


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.
C1061587x.fm Page 202 Friday, November 16, 2001 8:43 AM

202 Part III Getting Your Project Working

Note 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 Text-


Box 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

‘ Button Click Event Handler


Private Sub AddButton_Click()
AddText Text1
End Sub

‘ Adding the control to the ListBox


Private Sub AddText(text)
‘ We are relying on the default property here
List1.AddItem text
End Sub

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
C1061587x.fm Page 203 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 203

As we mentioned previously, the use of a Variant for handling the Text-


Box 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:

‘ Button Click Event Handler


Private Sub AddButton_Click(ByVal eventSender As System.Object, _
ByVal eventArgs As System.EventArgs) Handles AddButton.Click
AddText(Text1)
End Sub

‘ Adding the control to the ListBox


‘UPGRADE_NOTE: Text was upgraded to text_Renamed.
Private Sub AddText(ByRef text_Renamed As Object)
‘ We are relying on the default property here
‘UPGRADE_WARNING: Couldn’t resolve default property of
‘object text_Renamed.
List1.Items.Add(text_Renamed)
End Sub

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.

Deterministic Finalization and Garbage Collection


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. Refer-
ence counting presents several problems within COM because of issues with
C1061587x.fm Page 204 Friday, November 16, 2001 8:43 AM

204 Part III Getting Your Project Working

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 guaran-
tees 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:

Dim conn As New ADODB.Connection


conn.Open( ... )
Dim rs = conn.Execute( ... )
conn = Nothing ‘ The object is not destroyed here and the
‘ connection is still open
C1061587x.fm Page 205 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 205

Note 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 phys-
ical 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 applica-
tions.

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:

Dim conn As New ADODB.Connection


conn.Open( ... )
Dim rs = conn.Execute( ... )
conn.Close()
conn = Nothing

The Close method ensures that the database connection is released immedi-
ately. 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.
C1061587x.fm Page 206 Friday, November 16, 2001 8:43 AM

206 Part III Getting Your Project Working

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 WaitForPending-
Finalizers, 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.

Generic Objects (Control/Form/Screen)


Generic objects are a form of soft binding (discussed in Chapter 4). A problem
arises when you use methods such as ActiveForm or ActiveControl that return
object types (Form or Control) that you use as though they were a more spe-
cific type. For example, suppose that you have created a form in your applica-
tion. If you add a TextBox to that form (call it Text1), you have a distinct class
derived from the Form object with a property (Text1) that does not exist in the
parent Form class.
When you call the method ActiveForm, it returns a Form object that does
not have a Text1 property. In Visual Basic 6, you can use that strongly typed
object in a late-bound fashion:
Screen.ActiveForm.Text1.Text = “This is a test”

Visual Basic 6 allows you to access the properties and methods of these objects.
Visual Basic .NET, on the other hand, has more strict type requirements. While
it allows both early and late binding, soft binding is no longer supported. When
you upgrade this kind of code, you need to cast the result from ActiveForm to
either an object (forcing late binding) or to the specific class that contains the
method or property you are attempting to invoke (forcing early binding). The
upgraded code can take one of the following forms:
C1061587x.fm Page 207 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 207

‘ Forcing late binding by casting to Object


CType(ActiveForm, Object).Text1.Text = “This is a test"

‘ Forcing early binding by casting to Form1


CType(ActiveForm, Form1).Text1.Text = “This is a test”

Although it is up to you to decide what form you prefer, we recommend that


you use early binding whenever possible. There is nothing intrinsically wrong
with using late binding, but early binding provides the developer with a level of
compile-time validation. Late binding can result in errors that are exposed only
at run time. It is also slower than early binding because the run-time environ-
ment has to bind the methods and properties to the object at run time (hence
the term late binding). In an informal test, early-bound property access was
more than five times faster than late-bound access. (See the Visual Basic .NET
project LateBinding Performance on the companion CD. As you run it, watch
the Output window in the debugger.) Why make life more difficult for yourself
when you can have the best of both worlds? Early binding is best, unless there
is no feasible alternative.

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 declara-
tion. Array declaration with As New is not supported at all. The following code
shows how the Upgrade Wizard handles both types of declarations.
Here are the declarations in Visual Basic 6:
Dim x As New Class1
Dim y(5) As New Class2

Here’s how the Upgrade Wizard handles the declarations:

Dim x As New Class1()


‘UPGRADE_WARNING: Arrays can’t be declared with New.
Dim y(5) As New Class2()

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.
C1061587x.fm Page 208 Friday, November 16, 2001 8:43 AM

208 Part III Getting Your Project Working

Dim y(5) As Class2


For i = 0 To 5
y(i) = New Class2()
Next

This solution does not address the other side of the As New behavior:
implicit instantiation. For example, the following code will fail in Visual Basic
.NET with a NullReferenceException at run time:

Dim x As New Object1()


x.AnyMethod()
x = Nothing
x.AnyMethod() ‘ This will cause a NullReferenceException

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 difficul-
ties. 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.

Dim x As New Object1()


x.AnyMethod()
x = Nothing
x = New Object1()
x.AnyMethod()

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.

Sub Main (or Default Form)


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.
C1061587x.fm Page 209 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 209

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.

Note 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. Other-
wise, when the specified form is closed, the application will terminate immedi-
ately. The following example demonstrates the use of the Application.Run
method in a fictional Sub Main.
‘ This is how to start your application from Sub Main
System.Windows.Forms.Application.Run(New Form1())

Font Disparities
In Visual Basic 6, it is possible to use two different types of fonts: raster and
TrueType. Although TrueType fonts will upgrade to Visual Basic .NET, raster
fonts are no longer supported. This change can present a problem for your
application. What happens to your carefully laid-out forms? Table 10-1 shows
how the Upgrade Wizard translates various Visual Basic 6 fonts during the
upgrade process.
C1061587x.fm Page 210 Friday, November 16, 2001 8:43 AM

210 Part III Getting Your Project Working

Table 10-1 Visual Basic Fonts and How They Are Upgraded
Font Name Type Converted Font Style Preserved?
Arial TrueType Arial Yes
Comic Sans MS TrueType Comic Sans MS Yes
Courier Raster Microsoft Sans Serif No: 8pt Plain
Courier New TrueType Courier New Yes
FixedSys Raster Microsoft Sans Serif No: 8pt Plain
Microsoft Sans Serif TrueType Microsoft Sans Serif Yes
Modern Raster Microsoft Sans Serif No: 8pt Plain
MS Sans Serif Raster Microsoft Sans Serif Yes
MS Serif Raster Microsoft Sans Serif Yes
Roman Raster Microsoft Sans Serif No: 8pt Plain
Script Raster Microsoft Sans Serif No: 8pt Plain
Small Fonts Raster Microsoft Sans Serif No: 8pt Plain
System Raster Microsoft Sans Serif No: 8pt Plain
Terminal Raster Microsoft Sans Serif No: 8pt Plain

If you used the default font MS Sans Serif in a Visual Basic 6 form, your
formatting will be preserved in Visual Basic .NET but the font will be changed
to the Microsoft Sans Serif TrueType font. You may encounter a problem with
layout disparities owing to the slight differences between the two fonts. Con-
trols that are laid out with very tight constraints on your forms might exhibit a
wrapping effect.
Non-TrueType fonts will lose their formatting when upgraded. The best
way to ensure that your application preserves its look is to modify your Visual
Basic 6 application to use TrueType fonts, thus ensuring that your upgraded
forms will require minimal control repositioning. Figures 10-1 and 10-2 demon-
strate what happens when raster fonts are upgraded. Figure 10-1 shows a sam-
ple application (FontTest, included on the companion CD) prepared in Visual
Basic 6. Notice how both raster and TrueType fonts are represented.
C1061587x.fm Page 211 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 211

F10km01.tif

Figure 10-1 Raster and TrueType fonts in Visual Basic 6.

As Figure 10-2 shows, the fonts can change dramatically after the applica-
tion is upgraded to Visual Basic .NET with the Upgrade Wizard. This example
demonstrates some of the challenges you will face in your form layouts.

F10km02.tif

Figure 10-2 Raster and TrueType fonts after upgrading.

Notice that all the raster fonts, with the exception of MS Sans Serif, lost
their formatting and ended up in plain 8-point Arial. As you can see, you have
no choice but to fix your fonts manually. Stick with MS Sans Serif, and you
should have few problems. (With luck, only spot fixes will be required.) The
other choice is to use TrueType fonts, which will always maintain formatting,
although they too may require some tweaking.
C1061587x.fm Page 212 Friday, November 16, 2001 8:43 AM

212 Part III Getting Your Project Working

Bad Constants
In Visual Basic 6, you can create a set of named constants, known as an enu-
meration. Each member of the enumeration is assigned an integer value; in
code, the member name evaluates to its assigned integer. Because enumera-
tions in Visual Basic 6 represent integer values instead of distinct types, the pro-
cess can verify only their underlying values. Unfortunately, this arrangement
allows developers to use constants intended for different purposes interchange-
ably 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 col-
lections 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:
‘ Correct MousePointer constant
Screen.MousePointer = vbDefault

‘ Incorrect constant, same underlying value


Screen.MousePointer = vbNormal

‘ The same underlying value


Screen.MousePointer = 0

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 Mouse-
Pointer 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 follow-
ing results from the Upgrade Wizard illustrate this scenario:

‘ Correct MousePointer constant


‘UPGRADE_WARNING: VB.Screen property Screen.MousePointer has a
‘new behavior.
System.Windows.Forms.Cursor.Current = _
System.Windows.Forms.Cursors.Default

‘ Incorrect constant, same underlying value


‘UPGRADE_ISSUE: Unable to determine which constant to upgrade
‘vbNormal to.
‘UPGRADE_ISSUE: VB.Screen property Screen.MousePointer does not
C1061587x.fm Page 213 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 213

‘support custom mousepointers.


‘UPGRADE_WARNING: VB.Screen property Screen.MousePointer has a
‘new behavior.
System.Windows.Forms.Cursor.Current = vbNormal

‘ The same underlying value


‘UPGRADE_WARNING: VB.Screen property Screen.MousePointer has a
‘new behavior.
System.Windows.Forms.Cursor.Current = _
System.Windows.Forms.Cursors.Default

If you have used an incorrect constant, you will need to fix it on your own. The
Upgrade Wizard cannot help you, other than highlighting the problem in the
upgrade report. Thankfully, the solution is very simple, thanks to IntelliSense.
Simply delete the offending constant, and you get an IntelliSense menu listing
the proper constant options. Pick the appropriate constant, and you are on your
way.

Drag and Drop


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.

Drag and Drop in Visual Basic 6


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 con-
trols and their support for manual and automatic drag-and-drop operations.
C1061587x.fm Page 214 Friday, November 16, 2001 8:43 AM

214 Part III Getting Your Project Working

Table 10-2 Visual Basic 6 Controls Grouped According to Their


Support for OLE Drag and Drop
Controls OLEDragMode OLEDropMode
TextBox, PictureBox, Image, VbManual, vbNone, vbManual,
RichTextBox, MaskedBox vbAutomatic vbAutomatic
ComboBox, ListBox, DirList- VbManual, vbNone, vbManual
Box, FileListBox, DBCombo, vbAutomatic
DBList, TreeView, ListView,
ImageCombo, DataList, Data-
Combo
Form, Label, Frame, Com- Not supported vbNone, vbManual
mandButton, DriveListBox,
Data, MSFlexGrid, SSTab,
TabStrip, Toolbar, StatusBar,
ProgressBar, Slider, Animation,
UpDown, MonthView,
DateTimePicker, CoolBar

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 OLE-
DragAndDrop 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.

F10km03.tif

Figure 10-3 OLEDragAndDrop application.


C1061587x.fm Page 215 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 215

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.

Sub Source_MouseDown(...)
The user clicks on the source control.
source.OLEDrag

Sub Source_OLEStartDrag(Data, Effects) The source control sets the available


Data.SetData [bytes] [,format] formats and the data to be transferred.

The user moves the mouse over the target


Sub Target_OLEStartDrag(Data, Effects)
Effects = vbDropEffectCopy control, which responds by setting one or
more drag-and-drop effects.

The source control provides


Sub Source_OLEGiveFeedback(Effect, DefaultCursors)
Screen.MousePointer = vbCustom feedback by changing the
cursor’s shape.

Sub Target_OLEDragDrop(Data, Effect, ...)


If Data.GetFormat([format]) Then The mouse button is released.
var = Data.GetData(...)

Sub Source_OLESetData(Data, Format) Sub Source_OLECompleteDrag(Effect)


Data.SetData var, [format]
The source control can optionally complete
The data is requested of the source control the operation by deleting source data.
if it was not specified in the OLEStartDrag
event method.
F10km04.tif

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.
C1061587x.fm Page 216 Friday, November 16, 2001 8:43 AM

216 Part III Getting Your Project Working

Sub source_MouseDown(...)

The user clicks on the source control.


Populate the data at this point.

Sub Source_DragEnter(...) Sub Source_DragOver(...)

The source evaluates This event is called repeatedly


the drag data and as the cursor is dragged around
defines it’s resonse. inside of the control boundaries.

Sub Source_DragLeave(...)

The source control provides


feedback by changing the
cursor’s shape.
Source Control Events

Target Control Events

Sub Target_DragEnter(...)

The target evaluates the drag


data and defines its response.

Sub Target_DragOver(...)

This event is called repeatedly


as the cursor is dragged around
inside of the control boundaries.

Sub Target_DragLeave(...) Sub Target_DragDrop(...)

The source control provides The mouse button is released


feedback by changing the inside of the target control.
cursor’s shape.

F10km05.tif

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:
Private Sub PictureDisplay_MouseDown(Button As Integer, _
Shift As Integer, X As Single, Y As Single)
PictureDisplay.OLEDrag
End Sub

Private Sub PictureDisplay_OLEDragDrop(Data As DataObject, _


Effect As Long, Button As Integer, Shift As Integer, X As Single, _
Y As Single)
C1061587x.fm Page 217 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 217

If Data.GetFormat(vbCFFiles) Then
Dim i As Integer
For i = 1 To Data.Files.Count
Dim file As String
file = Data.Files(i)

If Not EntryExists(file) Then


Select Case UCase(Right(file, 4))
Case “.tif", “.jpg", “.gif"
FileList.AddItem file
FileList.ListIndex = FileList.ListCount - 1

Set PictureDisplay.Picture = LoadPicture(file)


End Select
Else
SelectListItem file
End If
Next
End If
End Sub

Private Sub PictureDisplay_OLEStartDrag(Data As DataObject, _


AllowedEffects As Long)
Data.Files.Clear

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 pro-
gramming model, the Upgrade Wizard cannot automatically make the modifica-
tions 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 appli-
cation in Visual Basic .NET. (This code is also included on the companion CD.)

Private Sub Form1_Load(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
PictureDisplay.AllowDrop = True
End Sub

(continued)
C1061587x.fm Page 218 Friday, November 16, 2001 8:43 AM

218 Part III Getting Your Project Working

Private Sub PictureDisplay_DragDrop(ByVal sender As Object, _


ByVal e As System.Windows.Forms.DragEventArgs) _
Handles PictureDisplay.DragDrop
‘ Clear out the existing image and ensure that any resources
‘ are released
If Not PictureDisplay.Image Is Nothing Then
PictureDisplay.Image.Dispose()
PictureDisplay.Image = Nothing
End If

Dim files() As String = e.Data.GetData(DataFormats.FileDrop, True)

Dim i As Integer
For i = 0 To files.Length - 1
If Not EntryExists(files(i)) Then
FileList.SelectedIndex = FileList.Items.Add(files(i))
Else
SelectListItem(files(i))
End If
Next

‘ Set the picture to the last image added


PictureDisplay.Image = Image.FromFile(FileList.SelectedItem)
End Sub

Private Sub PictureDisplay_DragEnter(ByVal sender As Object, _


ByVal e As System.Windows.Forms.DragEventArgs) _
Handles PictureDisplay.DragEnter
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
Dim file() As String = e.Data.GetData(DataFormats.FileDrop, True)

Select Case UCase(Microsoft.VisualBasic.Right(file(0), 4))


Case “.tif", “.jpg", “.gif"
e.Effect = DragDropEffects.Copy
Case Else
e.Effect = DragDropEffects.None
End Select
Else
e.Effect = DragDropEffects.None
End If
End Sub

Private Sub PictureDisplay_MouseDown(ByVal sender As Object, _


ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles PictureDisplay.MouseDown
Dim file(0) As String
file(0) = FileList.SelectedItem
C1061587x.fm Page 219 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 219

Dim d As New DataObject(DataFormats.FileDrop, file)


PictureDisplay.DoDragDrop(d, DragDropEffects.Copy)
End Sub

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.

Collection Classes
Upgrading collections built with the Visual Basic Class Builder utility is fairly
straightforward. In the typical case, you will need to uncomment only a single
line of code to get the collection to work in Visual Basic .NET. Of course, the
ease of the upgrade depends a great deal on what modifications were made to
the collection and how closely it resembles the originally generated collection
class. In most cases, the Upgrade Wizard will insert a simple ToDo comment
that requires a minor modification to get your collection to work as expected.
The following example is a simple string collection class implemented in Visual
Basic 6 using the Class Wizard Utility:
‘Local variable to hold collection
Private mCol As Collection

Public Function Add(Key As String, Value As String) As String


‘Set the properties passed into the method
mCol.Add Value, Key
Add = Value
End Function

Public Property Get Item(vntIndexKey As Variant) As String


‘Used when referencing an element in the collection
‘vntIndexKey contains either the Index or Key to the collection,
‘which is why it is declared as a Variant
‘Syntax: Set foo = x.Item(xyz) or Set foo = x.Item(5)
Set Item = mCol(vntIndexKey)
End Property

Public Property Get Count() As Long


‘Used when retrieving the number of elements in the
‘collection. Syntax: Debug.Print x.Count
Count = mCol.Count
End Property

Public Sub Remove(vntIndexKey As Variant)


(continued)
C1061587x.fm Page 220 Friday, November 16, 2001 8:43 AM

220 Part III Getting Your Project Working

‘Used when removing an element from the collection


‘vntIndexKey contains either the Index or Key, which is why
‘it is declared as a Variant
‘Syntax: x.Remove(xyz)

mCol.Remove vntIndexKey
End Sub

Public Property Get NewEnum() As IUnknown


‘This property allows you to enumerate
‘this collection with the For...Each syntax
Set NewEnum = mCol.[_NewEnum]
End Property

Private Sub Class_Initialize()


‘Creates the collection when this class is created
Set mCol = New Collection
End Sub

Private Sub Class_Terminate()


‘Destroys collection when this class is terminated
Set mCol = Nothing
End Sub

Essentially, what will happen when you upgrade this code is that the New-
Enum property on the collection will be commented out and replaced with a
GetEnumerator method. The following example is the complete Visual Basic
.NET version of the previous collection class as created by the Upgrade Wizard.
Notice both the NewEnum property and the GetEnumerator method.

Friend Class StringCollection


Implements System.Collections.IEnumerable
‘Local variable to hold collection
Private mCol As Collection

Public Function Add(ByRef Key As String, _


ByRef Value As String) As String
‘Set the properties passed into the method
mCol.Add(Value, Key)
Add = Value
End Function

Default Public ReadOnly Property Item(ByVal vntIndexKey As Object) _


As String
Get
‘Used when referencing an element in the collection
‘vntIndexKey contains either the Index or Key to the
‘collection,
C1061587x.fm Page 221 Friday, November 16, 2001 8:43 AM

Chapter 10 Ten Common Upgrade Problems 221

‘which is why it is declared as a Variant


‘Syntax: Set foo = x.Item(xyz) or Set foo = x.Item(5)
Item = mCol.Item(vntIndexKey)
End Get
End Property

Public ReadOnly Property Count() As Integer


Get
‘Used when retrieving the number of elements in the
‘collection. Syntax: Debug.Print x.Count
Count = mCol.Count()
End Get
End Property

‘UPGRADE_NOTE: NewEnum property was commented out.


‘Public ReadOnly Property NewEnum() As stdole.IUnknown
‘Get
‘This property allows you to enumerate
‘this collection with the For...Each syntax
‘NewEnum = mCol._NewEnum
‘End Get
‘End Property

Public Function GetEnumerator() As System.Collections.IEnumerator _


Implements System.Collections.IEnumerable.GetEnumerator
‘UPGRADE_TODO: Uncomment and change the following line to return
‘the collection enumerator.
‘GetEnumerator = mCol.GetEnumerator
End Function

Public Sub Remove(ByRef vntIndexKey As Object)


‘Used when removing an element from the collection
‘vntIndexKey contains either the Index or Key, which is why
‘it is declared as a Variant
‘Syntax: x.Remove(xyz)

mCol.Remove(vntIndexKey)
End Sub

‘UPGRADE_NOTE: Class_Initialize was upgraded to


‘Class_Initialize_Renamed
Private Sub Class_Initialize_Renamed()
‘Creates the collection when this class is created
mCol = New Collection
End Sub

Public Sub New()


MyBase.New()
Class_Initialize_Renamed()
End Sub
(continued)
C1061587x.fm Page 222 Friday, November 16, 2001 8:43 AM

222 Part III Getting Your Project Working

‘UPGRADE_NOTE: Class_Terminate was upgraded to


‘Class_Terminate_Renamed.
Private Sub Class_Terminate_Renamed()
‘Destroys collection when this class is terminated
‘UPGRADE_NOTE: Object mCol may not be destroyed until it is
‘garbage collected.
mCol = Nothing
End Sub

Protected Overrides Sub Finalize()


Class_Terminate_Renamed()
MyBase.Finalize()
End Sub
End Class

To enable your collection class, you need to uncomment a single line of


code in the GetEnumerator method (the Visual Basic .NET equivalent of the
NewEnum property) and make any necessary additional modifications. The vast
majority of cases will not require additional modifications, but the Upgrade
Wizard does not take any chances. By requiring you to uncomment the line in
the GetEnumerator method, it forces you to evaluate whether you do in fact
need to make any additional changes. Otherwise, just returning the Enumerator
from the underlying collection class will be perfectly sufficient. To see this sam-
ple in action, check out the Visual Basic .NET version of the CollectionSample
project on the companion CD.

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 pro-
vided 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.
C1161587x.fm Page 223 Thursday, November 15, 2001 3:46 PM

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 Applica-
tions (VBA), to name just a few Microsoft PC-based varieties. Each product pro-
vides 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 lan-
guage 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 incre-
ment 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 ver-
sions. 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 sup-
port 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.

223
C1161587x.fm Page 224 Thursday, November 15, 2001 3:46 PM

224 Part III Getting Your Project Working

Language Elements
Let’s look first at language-related upgrade issues, setting aside for now issues
related to data structures and user-defined types. We’ll cover control-of-flow
statements such as While…Wend and GoSub…Return, precompiler statements
such as #If…#End If, and language statements such as Abs, Open, and Close.

#If…#End If Precompiler Statements


All conditional compilation statements are maintained after an upgrade. All
code within all paths of an #If…#End If block will be found in the upgraded
project, but only the code in the execution path—the path that evaluates to
True —is upgraded. This happens because the Upgrade Wizard cannot guaran-
tee that code in other execution paths is valid. For example, you can include
any text you want in an #If…#End If block that evaluates to False, such as
#Const COMMENT = False

#If COMMENT Then


This is my invalid code. There is no comment. This will only confuse
the Upgrade Wizard if it tries to upgrade me.
#End If

You should make sure that you have turned on all the conditional compi-
lation arguments in your Visual Basic 6 project for the code paths you want to
upgrade. Also be sure that your code compiles and runs in Visual Basic 6. The
wizard will not be able to upgrade invalid code contained in your project. This
includes code contained within an #If…#End If block that evaluates to True.

Constants and Constant Expressions


If you define a constant (in Visual Basic 6) to represent a property on a Win-
dows form or control, you may find that the code does not compile after you
upgrade the project. For example, suppose that your Visual Basic 6 project con-
tains the following code:
Const COLOR_RED = vbRed

The Upgrade Wizard will upgrade the code to the following:

‘UPGRADE_NOTE: COLOR_RED was changed from a Constant to a Variable.


Dim COLOR_RED As System.Drawing.Color = System.Drawing.Color.Red

Why was the Const declaration changed to a Dim? The reason is that Sys-
tem.Drawing.Color.Red, although it looks like a constant, is not; it is a noncon-
stant expression. This means that, rather than being a constant value, such as
C1161587x.fm Page 225 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 225

255 (the value of vbRed), it is a property that returns a System.Drawing.Color


object. To see the declaration for System.Drawing.Color.Red within the
upgraded Visual Basic .NET project, right-click Red in the Visual Basic .NET
code window and choose Go To Definition. The Object Browser will display
the definition of System.Drawing.Color.Red, as follows:

Public Shared ReadOnly Property Red As System.Drawing.Color

In some cases, you may encounter a compiler error in your upgraded


code. For example, suppose that in Visual Basic 6 you create an enumerator
containing values for red, green, and blue called RGBColors. You use the enu-
merator values in code to combine red and green to create a yellow back-
ground for your form, as follows:
Enum RGBColors
Red = vbRed
Green = vbGreen
Blue = vbBlue
End Enum

Private Sub Form_Load()


BackColor = RGBColors.Red + RGBColors.Green
End Sub

The resulting Visual Basic .NET code after upgrade will be as follows:

‘UPGRADE_ISSUE: Declaration type not supported: Enum member


‘of type Color.
Enum RGBColors
Red = System.Drawing.Color.Red
Green = System.Drawing.Color.Lime
Blue = System.Drawing.Color.Blue
End Enum

Private Sub Form1_Load(ByVal eventSender As System.Object, _


ByVal eventArgs As System.EventArgs) _
Handles MyBase.Load
BackColor = System.Drawing.ColorTranslator.FromOle(RGBColors.Red + _
RGBColors.Green)
End Sub

Because the declarations for the color objects Red, Lime, and Blue are not con-
stant, each line results in the compiler error “Constant expression is required.”
There are a couple of ways that you can fix up the code to work. You can
define your own constant values, or you can use nonconstant values directly, as
described in the sections that follow.
C1161587x.fm Page 226 Thursday, November 15, 2001 3:46 PM

226 Part III Getting Your Project Working

Define Your Own Constant Values


The easiest way to fix your code is to use constant values in place of the Red,
Lime, and Blue objects in the code just given. For example, you can declare
constants for vbRed, vbGreen, and vbBlue so that you can define the enumera-
tor members as these constant types, as was done in the Visual Basic 6 code.

Const vbRed As Integer = &HFF


Const vbGreen As Integer = &HFF00
Const vbBlue As Integer = &HFF0000

Enum RGBColors
Red = vbRed
Green = vbGreen
Blue = vbBlue
End Enum

Private Sub Form1_Load(ByVal eventSender As System.Object, _


ByVal eventArgs As System.EventArgs) _
Handles MyBase.Load
BackColor = System.Drawing.ColorTranslator.FromOle(RGBColors.Red + _
RGBColors.Green)
End Sub

Use Nonconstant Values Directly


Another way to solve the problem is to replace any instances in which a mem-
ber of RGBColors, such as RGBColors.Red, is used with the matching Sys-
tem.Drawing.Color, such as System.Drawing.Color.Red. When you make this
change, a new question arises: How do you manage calculations that involve
two objects? For example, in the example above, how do you add the values
of two color objects together to arrive at a new color value? You can’t directly
add two objects together to get a result. If you can use a method or a function
that will give you a meaningful numeric value for the object, you can use the
numeric values in your calculation. Using a numeric-to-object conversion func-
tion allows you to turn the result of the numeric calculation back into an object
representing the calculated value.
In the case of color objects, you can obtain a meaningful numeric (color)
value by using the ToOle method of the System.Drawing.ColorTranslator class.
The ToOle method takes a color object—such as System.Drawing.Color.Red—
and obtains an RGB color value for it. ToOle(System.Drawing.Color.Red), for
example, will return the RGB value 255, or FF in hexadecimal. Not by coinci-
dence, the vbRed constant in Visual Basic 6 has the same value. Once you have
calculated the new color, you can convert the result back to a color object by
using the ColorTranslator.FromOle method. The following is an example of
C1161587x.fm Page 227 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 227

how you can change the original upgraded Visual Basic .NET code to use the
System.Drawing.Color values directly:

‘Include the Imports statement at the top of the Form file


Imports System.Drawing

Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
BackColor = _
ColorTranslator.FromOle(ColorTransltor.ToOle(Color.Red) _
+ ColorTranslator.ToOle(Color.Lime))

End Sub

It’s up to you whether you want to define your own constants or use the
nonconstant values directly in your code. If, in your original code, you are
using the constant values in combination with other constant values—say, in a
numeric calculation—it would make sense to define your own constant values
and use the constants in your code. Otherwise, if you are making a simple
assignment of a constant value to a property, it would make more sense to use
the nonconstant value—in other words, the object—directly.

Control Flow
If you never knew that Visual Basic supported GoSub…Return, On…GoTo,
and On…GoSub, and you don’t use these statements in your Visual Basic 6
code, you’re ahead of the game. Visual Basic .NET drops support for these
three statements.

GoSub…Return
GoSub…Return is a holdover from earlier generations of the Basic language
that did not support subroutines, Sub and Function. Visual Basic .NET does
not support the GoSub statement. It does, however, support the Return state-
ment, but its meaning is different from that in Visual Basic 6. Return is used
to return from a subroutine. You can use the Return statement to return a
value in a function.
If you are using the GoSub statement in your code, we recommend copy-
ing the code associated with the GoSub label to its own subroutine. Any local
variables that the GoSub label handler uses should be passed as parameters to
the subroutine that you create. The following code uses GoSub in two places
within Sub Main to display a summary of the number of times an animal
shows up in a sorted list of animals. It is a good example of when you would
C1161587x.fm Page 228 Thursday, November 15, 2001 3:46 PM

228 Part III Getting Your Project Working

use GoSub in a subroutine to perform a suboperation that reuses local variable


values.
Sub Main()

Dim Animals(2) As String


Dim ctAnimal As Long
Dim PrevAnimal As String
Dim i As Long

Animals(0) = “Alligator"
Animals(1) = “Monkey"
Animals(2) = “Monkey"

For i = 0 To UBound(Animals)

‘ Detect break in sequence and show summary info


If PrevAnimal <> ““ And PrevAnimal <> Animals(i) Then
GoSub ShowDetail
End If

ctAnimal = ctAnimal + 1
PrevAnimal = Animals(i)

Next

‘ Show summary info for last animal in list


GoSub ShowDetail

Exit Sub

ShowDetail:
MsgBox PrevAnimal & “ “ & ctAnimal
ctAnimal = 0
Return
End Sub

The Upgrade Wizard upgrades the code as is, inserting UPGRADE_ISSUE com-
ments for each occurrence of GoSub and UPGRADE_WARNING comments for
each occurrence of Return. The comments tell you that GoSub is not supported
and that Return has a new behavior in Visual Basic .NET.
You can replace the GoSub statements with calls to a subroutine called
ShowDetail. You need to pass the local variables as parameters to the Show-
Detail subroutine. In the previous example, you would pass PrevAnimal and
ctAnimal to ShowDetail. You need to pass ctAnimal ByRef so that the value
gets reset to 0 when execution returns from ShowDetail. The following code
C1161587x.fm Page 229 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 229

demonstrates how you can use a function—ShowDetail—in place of a GoSub


label of the same name (found in the code shown previously):

Public Sub Main()

Dim Animals(2) As String


Dim ctAnimal As Integer
Dim PrevAnimal As String
Dim i As Integer

Animals(0) = “Alligator"
Animals(1) = “Monkey"
Animals(2) = “Monkey"

For i = 0 To UBound(Animals)
‘ Detect break in sequence and show summary info
If PrevAnimal <> ““ And PrevAnimal <> Animals(i) Then
ShowDetail(PrevAnimal, ctAnimal)
End If

ctAnimal = ctAnimal + 1
PrevAnimal = Animals(i)
Next

‘ Show summary info for last animal in list


ShowDetail(PrevAnimal, ctAnimal)
End Sub

Sub ShowDetail(ByVal Animal As String, ByRef ctAnimal As Integer)


MsgBox(Animal & “ “ & ctAnimal)
ctAnimal = 0
End Sub

On…GoTo
On…GoTo is an outdated version of Select Case. On…GoTo evaluates a given
numeric expression for a value between 1 and 255 and then jumps to the label
given by the nth parameter after GoTo.
The following Visual Basic 6 example demonstrates the use of On…GoTo.
Dim Mode As Integer

Mode = 2 ‘WriteMode
On Mode GoTo ReadMode, WriteMode

MsgBox “Unexpected mode"


Exit Sub
(continued)
C1161587x.fm Page 230 Thursday, November 15, 2001 3:46 PM

230 Part III Getting Your Project Working

ReadMode:
MsgBox “Read mode"
Exit Sub

WriteMode:
MsgBox “Write mode”

The Upgrade Wizard upgrades the code to use Select Case and GoTo as follows:

Dim Mode As Short

Mode = 2 ‘WriteMode
Select Case Mode
Case Is < 0
Error(5)
Case 1
GoTo ReadMode
Case 2
GoTo WriteMode
End Select

MsgBox(“Unexpected mode”)
Exit Sub

ReadMode:
MsgBox(“Read mode”)
Exit Sub

WriteMode:
MsgBox(“Write mode”)

End Sub

Although the wizard produces correct code, you will probably want to
move the code contained under each label to each Case statement. This step
will eliminate the need for GoTo and will also condense your Visual Basic .NET
code as follows:

Dim Mode As Short

Mode = 2 ‘WriteMode
Select Case Mode
Case 1
MsgBox(“Read mode”)
Case 2
MsgBox(“Write mode”)
Case Else
MsgBox(“Unexpected mode”)
End Select
C1161587x.fm Page 231 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 231

On…GoSub
On…GoSub works in much the same way as On…GoTo, except that when you
jump to the label, execution can return back to the next statement after the
On…GoSub statement when you call Return. The following example, in partic-
ular the UpdateAccountBalance subroutine, demonstrates the use of
On…GoSub to jump to a transaction—deposit or withdrawal—depending on
the value of the passed-in Transaction variable. The Transaction variable can
either be 1 for deposit or 2 for withdrawal. If, for example, the value is 1, the
On…GoSub statement jumps to the first label specified in the list—in this case
Deposit:
Option Explicit
Private m_AccountBalance As Currency

Private Sub Main()

Const Deposit = 1
Const Withdrawal = 2

m_AccountBalance = 500
UpdateAccountBalance Deposit, 100

End Sub

Private Sub UpdateAccountBalance(ByVal Transaction As Integer, _


ByVal Amount As Currency)

On Transaction GoSub Deposit, Withdrawal


MsgBox “New balance: “ & m_AccountBalance
Exit Sub

Deposit:
m_AccountBalance = m_AccountBalance + Amount
Return

Withdrawal:
m_AccountBalance = m_AccountBalance - Amount
Return

End Sub

As in the On…GoTo example given earlier, you can change your


On…GoSub code to use a Select Case statement.
C1161587x.fm Page 232 Thursday, November 15, 2001 3:46 PM

232 Part III Getting Your Project Working

Select Case Transaction


Case Deposit
m_AccountBalance = m_AccountBalance + Amount
Case Withdrawal
m_AccountBalance = m_AccountBalance - Amount
End Select
MsgBox “New balance: “ & m_AccountBalance

File Functions
Visual Basic 6 includes a number of language statements—such as Open, Close,
Put #, Get #, and Print #—that allow you to read and write text and binary files.
Visual Basic .NET provides a set of functions that are compatible with the Visual
Basic 6 file statements. The Upgrade Wizard will upgrade your Visual Basic 6
file statements to the equivalent Visual Basic .NET functions. Table 11-1 illus-
trates those relationships.

Table 11-1 File Function Mapping from Visual Basic 6 to


Visual Basic .NET
Visual Basic 6 Statement Visual Basic .NET Function
ChDir ChDir
ChDrive ChDrive
Close FileClose
FileCopy FileCopy
Get FileGet
Input # Input
Kill Kill
Line Input # LineInput
Lock Lock
MkDir MkDir
Open FileOpen
Print # Print, PrintLine
Put FilePut
Reset Reset
RmDir RmDir
SetAttr SetAttr
Unlock Unlock
Width # FileWidth
Write # Write, WriteLine
C1161587x.fm Page 233 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 233

File Format Compatibility


When you upgrade an application from Visual Basic 6 to Visual Basic .NET, not
only do you need to worry about getting your Visual Basic .NET application
working in a compatible way, but you also need to be concerned about being
able to read data that was written by a Visual Basic 6 application. Because
Visual Basic .NET offers you a full set of compatible file functions, you can read
files created by Visual Basic 6 applications. For example, you can read and
write each of the file types that Visual Basic 6 supports—Text, Binary, and Ran-
dom. In order to maintain compatibility, however, you need to be aware of
some subtleties in the way that you read and write files.

Print, PrintLine, Write, and WriteLine The Visual Basic .NET Print and Print-
Line functions are equivalent to the Visual Basic 6 Print # function. Which func-
tion you use depends on how Print # is used in your Visual Basic 6 application.
If the text you are printing is terminated by a semicolon, use Print. If the text
you are printing is not terminated by a semicolon, use PrintLine. The same sort
of mapping applies to the Visual Basic 6 Write # function. For example, the fol-
lowing Visual Basic 6 code:
Print #1, “This is a full line of text"
Print #1, “This is a partial line of text";
Write #2, “This is a full line using write"
Write #2, “This is a partial line using write";

is equivalent to the following Visual Basic .NET code:

PrintLine(1, “This is a full line of text”)


Print(1, “This is a partial line of text”)
WriteLine(2, “This is a full line using write”)
Write(2, “This is a partial line using write”)

The Upgrade Wizard automatically upgrades your Visual Basic 6 Print #


and Write # code to use the appropriate function, based on whether the text is
terminated by a semicolon. It is when you start editing your upgraded Visual
Basic .NET code or writing new code that you need to be aware that there are
two separate functions that give you full-line versus partial-line file printing.

When Reading or Writing Variables, Size Matters If you are reading or writing
information to a binary file, you need to pay attention to the variables you are
using. For example, the byte size of Integer and Long variables is different
between Visual Basic 6 and Visual Basic .NET. An Integer is 16 bits in Visual
Basic 6 and 32 bits in Visual Basic .NET; a Long is 32 bits in Visual Basic 6 and
64 bits in Visual Basic .NET. If, in Visual Basic .NET, you are reading data from
a binary file that was written as a Visual Basic 6 Integer, you will need to use a
variable of type Short (16 bits) to read the data.
C1161587x.fm Page 234 Thursday, November 15, 2001 3:46 PM

234 Part III Getting Your Project Working

If you use the Upgrade Wizard to upgrade your application, this change
will not be an issue because the wizard automatically maps all your variables
that are type Integer to Short and maps type Long to Integer. However, when
you edit the upgraded code or write new code, you need to be careful to use
the correct data type—based on size, not name—when reading and writing
binary files. This includes files that you open using Random mode.

Random-Access Files and Dynamic Arrays If you create a random-access binary


file in Visual Basic 6, additional header information relating to the format of
your data may be written to the file. This header information tells Visual Basic
how much data to read back in when it reads the file. The information is written
when you use a dynamic data type such as a dynamic array or a variable-length
string. For example, header information associated with a dynamic array
includes the count of the array dimensions, the size of the array, and the lower
bound. Header information associated with a variable-length string includes the
length of the string. If you use a fixed-length array or a fixed-length string, no
header information is written.

Dynamic Array Refresher


Not sure what a dynamic array is? You are probably not alone. The differ-
ence between a dynamic array and a fixed-length array involves a subtle
variation in syntax. If your declaration for an array does not specify the
size, it is considered to be a dynamic array. For example, the following
statement declares a dynamic array:
Dim MyDynamicArray() As Integer

Once the array is declared as a dynamic array, it is always considered


a dynamic array, even after you redimension it to a known size, as in the
following statement:
ReDim MyDynamicArray(100)

If you specify the size of the array as part of the declaration, it is con-
sidered to be a fixed-length array. For example,
Dim MyDynamicArray(100) As Integer

Once you have dimensioned a fixed-length array, you cannot use the
ReDim statement to change its size.
C1161587x.fm Page 235 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 235

The Upgrade Wizard does not handle situations that involve writing the
contents of a dynamic array to a random-access file. Consider the following
Visual Basic 6 code, which initializes the contents of a dynamic array, Output-
Data, with its own index values.
Dim OutputData() As Byte
ReDim OutputData(100)

Dim i As Integer

For i = 0 To UBound(OutputData)
OutputData(i) = i
Next

Open Environ(“TEMP”) & “\ArrayData.Dat” For Random As #1 Len = 120


Put #1, , OutputData
Close #1

If we use the following Visual Basic 6 code to read in the contents of the array,
everything works fine:
Dim OutputData() As Byte
Dim i As Integer

Open Environ(“TEMP”) & “\ArrayData.Dat” For Random As #1 Len = 120


Get #1, , OutputData
Close #1

For i = 0 To UBound(OutputData)
If i <> OutputData(i) Then
MsgBox “Error: Data element (“ & i & “) is incorrect. “ & _
“Value=“ & OutputData(i)
Exit For
End If
Next

If you upgrade this Visual Basic 6 code to Visual Basic .NET, the resulting code
is as follows:

Dim OutputData() As Byte


Dim i As Short

FileOpen(1, Environ(“TEMP”) & “\ArrayData.Dat", _


OpenMode.Random, , , 120)
‘UPGRADE_WARNING: Get was upgraded to FileGet and has a new behavior.
FileGet(1, OutputData)
FileClose(1)

For i = 0 To UBound(OutputData)
(continued)
C1161587x.fm Page 236 Thursday, November 15, 2001 3:46 PM

236 Part III Getting Your Project Working

If i <> OutputData(i) Then


MsgBox(“Error: Data element (“ & i & “) is incorrect. “ & _
“Value=“ & OutputData(i))
Exit For
End If
Next

If you run this upgraded code, the first problem you will encounter is a “Cannot
determine array type because it is Nothing” exception on the line FileGet(1,
OutputData). This exception occurs for exactly the reason that it indicates: the
OutputData array is uninitialized, so its value is Nothing. It is caused by a dif-
ference between Visual Basic 6 and Visual Basic .NET. In Visual Basic 6,
although the array is not initialized, the Visual Basic Get statement can still
determine the type of the array at run time. The type is needed so that the Get
statement can redimension the array with the correct type and fill it with the
data contained in the file.
To fix this problem, you need to redimension the array with at least one
element so that it is initialized to a value other than Nothing. Include the follow-
ing statement after the Dim OutputData() As Byte statement:

ReDim OutputData(0)

Run the code again, and you uncover another problem. A message box
pops up telling you “Error: Data element (0) is incorrect. Value=1.” This mes-
sage occurs because the Visual Basic .NET FileGet statement assumes that the
data it is reading was written from a fixed-length array, not a dynamic array.
The value of 1 that leads to the error is actually a value contained within the
header information for the dynamic array. It is the number of dimensions con-
tained in the dynamic array, not the dynamic array data itself.
To fix this problem, you need to tell the FileGet statement that the data it
is reading is coming from a dynamic array. You do this by specifying the
optional FileGet parameter called ArrayIsDynamic. Change the following line
in the Visual Basic .NET upgraded code:

FileGet(1, OutputData)

to

FileGet(1, OutputData, ArrayIsDynamic:=True)

Run the code again, and it will work as expected. The FileGet function will
expand the OutputData array to have an upper bound of 100 and will read in
the array contents, following the array header information, from the file.
C1161587x.fm Page 237 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 237

Random-Access Files and Fixed-Length Strings A problem similar to the dynamic


array problem discussed in the previous section will occur if you attempt to read
a fixed-length string from a random-access file. Consider the following upgraded
Visual Basic .NET code to read in a fixed-length string:

Dim OutputData As New VB6.FixedLengthString(9)


Dim i As Short

FileOpen(1, Environ(“TEMP”) & “\StringData.Dat", _


OpenMode.Random, , , 15)

‘UPGRADE_WARNING: Get was upgraded to FileGet and has a new behavior.


FileGet(1, OutputData.Value)
FileClose(1)

For i = 1 To Len(OutputData.Value)
If CStr(i) <> Mid(OutputData.Value, i, 1) Then
MsgBox(“Error: Character (“ & i & “) is incorrect. Value=“ & _
Mid(OutputData.Value, i, 1))
Exit For
End If
Next

This situation is different from the dynamic array case in that the FileGet func-
tion assumes that the data contained within the file is a variable-length string,
not a fixed-length string. This means that the FileGet function expects the file to
contain a string header giving the length of the string. If you execute the code,
you will encounter a “Bad record length” exception. The FileGet function is get-
ting tripped up because it thinks the first 4 bytes of data is the string length
when in fact it is the string data. The function attempts to use the first 4 bytes
of string data as the length, comes up with a large number—larger than the
record size—and throws an exception.
To address this problem, the FileGet function provides an optional param-
eter called StringIsFixedLength. Set the parameter to True, and the code will
work as expected. To fix the Visual Basic .NET code, change the following line:

FileGet(1, OutputData.Value)

to

FileGet(1, OutputData.Value, StringIsFixedLength:=True)


C1161587x.fm Page 238 Thursday, November 15, 2001 3:46 PM

238 Part III Getting Your Project Working

Types and Type Operations


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.

Object Replaces Variant


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:
Private m_Info As String
Private m_Picture As StdPicture

Public Sub SetData(Optional ByVal Info As String, _


Optional ByVal Picture As Variant)
C1161587x.fm Page 239 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 239

If Not IsMissing(Info) Then


m_Info = Info
End If

If Not IsMissing(Picture) Then


Set m_Picture = Picture
End If

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 clos-
est 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 automati-
cally 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 dis-
tinguish 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.

Private m_Info As String


Private m_Picture As System.Drawing.Image

Public Sub SetData(Optional ByVal Info As String = “", _


Optional ByVal Picture As Object = Nothing)

‘UPGRADE_NOTE: IsMissing() was changed to IsNothing().


If Not IsNothing(Info) Then
m_Info = Info
End If

‘UPGRADE_NOTE: IsMissing() was changed to IsNothing().


If Not IsNothing(Picture) Then
m_Picture = Picture
End If
End Sub
C1161587x.fm Page 240 Thursday, November 15, 2001 3:46 PM

240 Part III Getting Your Project Working

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 initializa-
tion values and fixing up the code to test for “<Missing>” yields the following:

Private m_Info As String


Private m_Picture As System.Drawing.Image

Public Sub SetData(Optional ByVal Info As String = “<Missing>", _


Optional ByVal Picture As Object = “<Missing>“)
If Info <> “<Missing>“ Then
m_Info = Info
End If

If Picture <> “<Missing>“ Then


m_Picture = Picture
End If
End Sub

Dealing with Null


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 rea-
son 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.
C1161587x.fm Page 241 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 241

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 lan-
guage 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 work-
ing 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 vCus-
tomerMiddleName is Null.
Dim vCustomerMiddleName As Variant
vCustomerMiddleName = Null
vCustomerMiddleName = LCase(vCustomerMiddleName)

Visual Basic .NET does not support null propagation. This means that
although the same Visual Basic 6 functions—Left, Mid, Chr, Hex, and LCase—
are available in Visual Basic .NET, the Visual Basic .NET functions do not accept
Null—System.DBNull.Value to be exact—as a legal value. The Upgrade Wizard
produces the following code from the previous Visual Basic 6 example:

Dim vCustomerMiddleName As Object


vCustomerMiddleName = System.DBNull.Value
vCustomerMiddleName = LCase(vCustomerMiddleName)

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:

Dim vCustomerMiddleName As Object


vCustomerMiddleName = System.DBNull.Value
If Not vCustomerMiddleName Is System.DBNull.Value Then
vCustomerMiddleName = LCase(vCustomerMiddleName)
End If
C1161587x.fm Page 242 Thursday, November 15, 2001 3:46 PM

242 Part III Getting Your Project Working

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 any-
where in your code—as follows:

Public Function LCase(ByVal obj As Object) As Object


If Not obj Is System.DBNull.Value Then
‘ Value is not null so call Visual Basic .NET function
obj = Microsoft.VisualBasic.LCase(obj)
End If
Return obj
End Function

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 Sys-
tem.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 decla-
ration 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
C1161587x.fm Page 243 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 243

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

upgrades to the following Visual Basic .NET code:

‘UPGRADE_WARNING: Lower bound of array OptionOneBasedArray


‘was changed from 1 to 0.
Dim OptionOneBasedArray(10) As Short

‘UPGRADE_WARNING: Lower bound of array PositiveLBoundArray was


‘changed from 10 to 0.
Dim PositiveLBoundArray(20) As Integer

‘UPGRADE_ISSUE: Declaration type not supported: Array with


‘lower bound less than zero.
Dim NegativeLBoundArray(-10 To 10) As Object

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 ele-
ments 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 com-
piler 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:

Dim NegativeLBoundArray(20) As Object


C1161587x.fm Page 244 Thursday, November 15, 2001 3:46 PM

244 Part III Getting Your Project Working

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 con-
stant, 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 Cre-
ateInstance, 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:
Dim OptionOneBasedArray(10) As Integer
Dim PositiveLBoundArray(10 To 20) As Long
Dim NegativeLBoundArray(-10 To 10) As Variant

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:

Dim OptionOneBasedArray As System.Array ‘ 1 to 10 As Short


Dim PositiveLBoundArray As System.Array ‘ 10 to 20 As Integer
C1161587x.fm Page 245 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 245

Dim NegativeLBoundArray As System.Array ‘ –10 to 10 As Object

‘ 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 Sys-


tem.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.

Dim StrongTypeArray() As Object


StrongTypeArray = NegativeLBoundArray

Structures
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 differ-
ence is that you cannot create instances of a structure, and all structure mem-
bers implicitly include the Shared attribute. This means that a structure can
C1161587x.fm Page 246 Thursday, November 15, 2001 3:46 PM

246 Part III Getting Your Project Working

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:
Private Type MyType
MyStringArray(10) As String
End Type

Sub Main()
Dim mt As MyType
MsgBox (mt.MyStringArray(0))
End Sub

The Upgrade Wizard upgrades this code to

Private Structure MyType


<VBFixedArray(10)> Dim MyStringArray() As String

‘UPGRADE_TODO: “Initialize” must be called to initialize


‘instances of this structure.
Public Sub Initialize()
ReDim MyStringArray(10)
End Sub
End Structure

Public Sub Main()


‘UPGRADE_WARNING: Arrays in structure mt may need to be
‘initialized before they can be used.
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
C1161587x.fm Page 247 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 247

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 NullReferenceExcep-
tion, “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 Ini-
tialize 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:

Private Structure MyType


<VBFixedArray(10)> Dim MyStringArray() As String

Public Sub Initialize()


ReDim MyStringArray(10)
End Sub
End Structure

Public Sub Main()


Dim mt As MyType
mt.Initialize()
MsgBox(mt.MyStringArray(0))
End Sub

LSet
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 mem-
ory 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 ver-
sion 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 inte-
ger to the user-defined type containing the version parts. The following Visual
Basic 6 code demonstrates how this can be done.
C1161587x.fm Page 248 Thursday, November 15, 2001 3:46 PM

248 Part III Getting Your Project Working

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.

‘UPGRADE_ISSUE: LSet cannot assign one type to another.

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:

Sub CopyVersion(ByVal vp As VersionPartsType, _


ByRef vt As VersionType)

‘ 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
C1161587x.fm Page 249 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 249

Note 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.

Making Your Code Thread-Safe


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 compo-
nent 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 environ-
ment such as Internet Explorer, Internet Information Services, or COM+. If you
are not sure whether your component will be run in a multithreaded environ-
ment, it is always best to err on the side of caution and ensure that your com-
ponent 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. Allow-
ing 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 incre-
mented 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 situ-
ation 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.
C1161587x.fm Page 250 Thursday, November 15, 2001 3:46 PM

250 Part III Getting Your Project Working

Public Class GreedyBank

Private m_Balance As Decimal

Public Sub New()


m_Balance = 20
End Sub

Public Function Withdraw(ByVal Amount As Decimal) As String

‘Protect against negative balance


If m_Balance >= Amount Then
m_Balance = m_Balance - Amount
Else
Return “The amount you requested will overdraw “ & _
“your account by $” & Amount – m_Balance
End If

End Function

End Class

This example is pretty straightforward. A client application calls With-


draw. 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 cus-
tomer 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:

m_Balance = m_Balance - Amount

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 state-
ment, a second thread comes along, requesting an amount of $20 and complet-
ing the following statement:

If m_Balance >= Amount Then

The second thread also sees that the bank balance, $20, is sufficient to cover the
withdrawal request and proceeds to the next line:

m_Balance = m_Balance - Amount


C1161587x.fm Page 251 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 251

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 legiti-
mate leads to unanticipated results—although the customer would need to sub-
mit two withdrawal requests simultaneously to encounter the problem.

Using a SyncLock Block


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)

‘Protect against negative balance


If m_Balance >= Amount Then
m_Balance = m_Balance - Amount
Else
Return “The amount you requested will overdraw “ & _
“your account by $” & Amount – m_Balance
End If

End SyncLock

If there is any chance that your Visual Basic .NET component or UserCon-
trol 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.

Windows API
The foundation upon which every Windows application is built is the Windows
API. Visual Basic applications are no exception. Visual Basic, through the lan-
guage and forms package, abstracts the Windows API to a set of easy-to-use
statements, components, and controls. In many cases, you can build a Visual
Basic application without needing to call a Windows API function directly.
C1161587x.fm Page 252 Thursday, November 15, 2001 3:46 PM

252 Part III Getting Your Project Working

However, because the Visual Basic language and forms package does not rep-
resent every Windows API function available, Visual Basic supports calling Win-
dows API functions directly. It has done so since version 1, enabling you to add
capabilities to your application that cannot be implemented in the Visual Basic
language. Visual Basic .NET carries this capability forward by enabling you to
declare and call Windows API functions in the same way as before.
This section focuses on the language changes that affect the way you cre-
ate Declare statements for API functions. It also shows how you can use classes
contained in the .NET Framework to complement or replace the Windows API
calls you make in your application.

Type Changes
Two changes that affect almost every Windows API function declaration involve
the numeric types Integer and Long. 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.
Visual Basic .NET adds a new type called Short, which, in terms of size, is the
replacement for the Visual Basic 6 Integer type.
In Visual Basic .NET, when you create a new Declare statement for a Win-
dows API function, you need to be mindful of this difference. Any parameter type
or user-defined type member that was formerly a Long needs to be declared as
Integer; any member formerly declared as Integer needs to be declared as Short.
Take, for example, the following Visual Basic 6 Declare statement, which
contains a mix of Integer and Long parameters:
Declare Function GlobalGetAtomName Lib “kernel32” _
Alias “GlobalGetAtomNameA” (ByVal nAtom As Integer, _
ByVal lpBuffer As String, ByVal nSize As Long) _
As Long

The equivalent Visual Basic .NET declaration is as follows:

Declare Function GlobalGetAtomName Lib “kernel32” _


Alias “GlobalGetAtomNameA” (ByVal nAtom As Short, _
ByVal lpBuffer As String, ByVal nSize As Integer) _
As Integer

The Upgrade Wizard will automatically change all the variable declara-
tions in your code to use the right size. You need to worry about the size of
a type only when you are creating new Declare statements or modifying exist-
ing statements.
C1161587x.fm Page 253 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 253

As Any No Longer Supported


Because in some cases you need to pass either a string or a numeric value to a
Windows API function, Visual Basic 6 allows you to declare parameter types As
Any. This declaration allows you to pass an argument of any type, and Visual
Basic will pass the correct information when the call is made. Although this
gives you greater flexibility, no type checking is performed on the argument
when you call the API function. If you pass an incompatible argument type, the
application may crash.
Visual Basic .NET does not support declaring Windows API parameters As
Any. Instead, it allows you to declare the same API function multiple times,
using different types for the same parameter.
The Windows API function SendMessage is an example of an instance in
which you would use As Any in the API function declaration. Depending on the
Windows message you are sending, the parameter types required for the mes-
sage will vary. The WM_SETTEXT message requires you to pass a string,
whereas the WM_GETTEXTLENGTH message requires you to pass 0, a numeric
value, for the last parameter. In order to handle both of these messages, you
create the Visual Basic 6 Declare statement for SendMessage as follows:
Private Declare Function SendMessage Lib “user32” _
Alias “SendMessageA” _
(ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Any) As Long

The last parameter has been declared As Any. The following code is an exam-
ple of setting the caption of the current form and then retrieving the length of
the caption set:
Dim TextLen As Long
Const WM_SETTEXT = &HC
Const WM_GETTEXTLENGTH = &HE

SendMessage Me.hwnd, WM_SETTEXT, 0, “My text"


TextLen = SendMessage(Me.hwnd, WM_GETTEXTLENGTH, 0, 0&)

To implement the equivalent functionality in Visual Basic .NET, you create


multiple Declare statements for the SendMessage function. In the case of the
SendMessage API, you create two Declare statements, using two different types
for the last parameter—String and Integer—as follows:
Declare Function SendMessage Lib “user32” Alias “SendMessageA” _
(ByVal hwnd As Integer, _
ByVal wMsg As Integer, _
(continued)
C1161587x.fm Page 254 Thursday, November 15, 2001 3:46 PM

254 Part III Getting Your Project Working

ByVal wParam As Integer, _


ByVal lParam As String) As Integer

Declare Function SendMessage Lib “user32” Alias “SendMessageA” _


(ByVal hwnd As Integer, _
ByVal wMsg As Integer, _
ByVal wParam As Integer, _
ByVal lParam As Integer) As Integer

Note You also need to change the other parameter types from Long
to Integer, as discussed in the previous section.

Although having to create multiple Declare statements for the same func-
tion is more work, you benefit by getting both the flexibility of using the same
function name and type checking when the call is made.

AddressOf Changes
Certain API functions, such as EnumWindows, require a pointer to a callback
function. When you call the API function, Windows calls the callback function
you provided. In the case of EnumWindows, Windows will call the function for
each top-level window contained on the screen.
Visual Basic 6 allows you to declare Windows API functions that take call-
back function pointers by declaring the function pointer parameter as Long, to
represent a 32-bit pointer. You call the API function passing the subroutine to
serve as the callback function, qualified by the AddressOf keyword.
Visual Basic .NET still supports the AddressOf keyword, but instead of
returning a 32-bit integer, it returns a delegate. A delegate is a new type in
Visual Basic .NET that allows you to declare pointers to functions or to class
members. This means that you can still create Declare statements for Windows
API functions that take a callback function pointer as a parameter. The differ-
ence is that instead of declaring the parameter type as a 32-bit integer, you need
to declare the function parameter as a delegate type.
The following Visual Basic 6 code demonstrates how to use AddressOf to
pass a pointer to a callback function:
Declare Function EnumWindows Lib “USER32” _
(ByVal EnumWindowsProc As Long, _
ByVal lParam As Long) As Boolean

Declare Function GetWindowText Lib “USER32” Alias “GetWindowTextA” _


C1161587x.fm Page 255 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 255

(ByVal hwnd As Long, ByVal TextBuffer As String, _


ByVal TextBufferLen As Long) As Long

Dim TopLevelWindows As New Collection

Sub Main()

Dim WindowCaption As Variant

‘ Enumerate all top-level windows, EnumWindowsCallback will


‘ be called
EnumWindows AddressOf EnumWindowsCallback, 0

‘ Echo list of captions to Immediate window


For Each WindowCaption In TopLevelWindows
Debug.Print WindowCaption
Next

End Sub

Function EnumWindowsCallback(ByVal hwnd As Long, _


ByVal lParam As Long) _
As Boolean
Dim TextBuffer As String
Dim ActualLen As Long

‘ Get window caption


TextBuffer = Space(255)
ActualLen = GetWindowText(hwnd, TextBuffer, Len(TextBuffer))
TextBuffer = Left(TextBuffer, ActualLen)

‘ Put window caption into list


If TextBuffer <> ““ Then
TopLevelWindows.Add TextBuffer
End If

‘ Return True to keep enumerating


EnumWindowsCallback = True

End Function

When you use the Visual Basic .NET Upgrade Wizard to upgrade the above
code, you get the following result:

Declare Function EnumWindows Lib “USER32” _


(continued)
C1161587x.fm Page 256 Thursday, November 15, 2001 3:46 PM

256 Part III Getting Your Project Working

(ByVal EnumWindowsProc As Integer, ByVal lParam As Integer) _


As Boolean

Declare Function GetWindowText Lib “USER32” Alias “GetWindowTextA” _


(ByVal hwnd As Integer, ByVal TextBuffer As String, _
ByVal TextBufferLen As Integer) As Integer

Dim TopLevelWindows As New Collection

Public Sub Main()

Dim WindowCaption As Object

‘UPGRADE_WARNING: Add a delegate for AddressOf EnumWindowsCallback


EnumWindows(AddressOf EnumWindowsCallback, 0)

For Each WindowCaption In TopLevelWindows


System.Diagnostics.Debug.WriteLine(WindowCaption)
Next WindowCaption

End Sub

Function EnumWindowsCallback(ByVal hwnd As Integer, _


ByVal lParam As Integer) As Boolean

Dim TextBuffer As String


Dim ActualLen As Integer

TextBuffer = Space(255)
ActualLen = GetWindowText(hwnd, TextBuffer, Len(TextBuffer))
TextBuffer = Left(TextBuffer, ActualLen)

If TextBuffer <> ““ Then


TopLevelWindows.Add(TextBuffer)
End If

‘ Return True to keep enumerating


EnumWindowsCallback = True

End Function

The upgraded code contains a single compiler error: “‘AddressOf’ expres-


sion cannot be converted to ‘Integer’ because ‘Integer’ is not a delegate type.”
The error occurs on the following statement:

EnumWindows(AddressOf EnumWindowsCallback, 0)
C1161587x.fm Page 257 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 257

To fix the error, you need to change the Declare declaration for EnumWindows
to accept a delegate type for the EnumWindowsProc parameter instead of an
Integer type. The easiest way to create a delegate declaration for the EnumWin-
dowsCallback function is to perform the following steps:

1. Copy and paste the subroutine declaration in your code that is to


serve as the callback function.
2. Insert the Delegate keyword at the beginning of the declaration.
3. Change the function name by appending the word Delegate to it.
The delegate name must be unique.

In the previous example, we can create the delegate declaration by copy-


ing and pasting the following function signature to the section of code contain-
ing the Declare statements:

Function EnumWindowsCallback(ByVal hwnd As Integer, _


ByVal lParam As Integer) As Boolean

Insert the keyword Delegate at the beginning of the function declaration


given previously, and change the name by appending Delegate to the original
function name—EnumWindowsCallback becomes EnumWindowsCallback-
Delegate. You end up with a delegate declaration for a function with the form—
name and parameters—of EnumWindowsCallback:

Delegate Function EnumWindowsCallbackDelegate(ByVal hwnd As Integer, _


ByVal lParam As Integer) _
As Boolean

Now that the delegate declaration for the callback function is in place, you
need to change the parameter type for the EnumWindows Declare statement
from Integer to the delegate type EnumWindowsCallbackDelegate, as follows:

Declare Function EnumWindows Lib “USER32” _


(ByVal EnumWindowsProc As EnumWindowsCallbackDelegate, _
ByVal lParam As Integer) As Boolean

The code will compile and run without error, producing the same results as the
original Visual Basic 6 code.
C1161587x.fm Page 258 Thursday, November 15, 2001 3:46 PM

258 Part III Getting Your Project Working

Passing User-Defined Types to API Functions


When you pass a Visual Basic 6 user-defined type to an API function, Visual
Basic passes a pointer to the memory containing the user-defined type; the API
function sees the members of the user-defined type in the same order as they
were declared in Visual Basic. This is not the case for Visual Basic .NET, how-
ever. If you declare a user-defined type, the order of the members is not guar-
anteed to be the order in which they appear in the code. The .NET runtime may
reorganize the members of a user-defined type in a way that is most efficient for
the user-defined type to be passed to a function. To guarantee that the mem-
bers are passed exactly as declared in the code, you need to use marshalling
attributes.

Marshaling Attributes
A marshaling attribute is an attribute that you attach to a structure mem-
ber or subroutine parameter declaration to specify how the member or
parameter should be stored in memory and passed to the function. For
example, if you are calling an API function named MyFunction, written in
C or C++, that takes a VARIANT_BOOL—a 2-byte Boolean type—parame-
ter, you can use the MarshalAs attribute to specify the parameter type that
the API function expects. Normally, a Boolean parameter is passed using
4 bytes, but if you include UnmanagedType.VariantBool as a parameter to
the MarshalAs attribute, the parameter is marshaled—in other words,
passed—as a 2-byte VARIANT_BOOL argument. The following code dem-
onstrates how to apply the MarshalAs attribute to your API function dec-
larations:

Declare Sub MyFunction Lib “MyLibrary.dll” _


(<MarshalAs(UnmanagedType.VariantBool)> ByVal MyBool As Boolean)

The MarshalAs attribute is contained in the System.Run-


time.InteropServices namespace. In order to call MarshalAs without qual-
ification, you need to add an Imports statement at the top of the module
for System.Runtime.InteropServices, as follows:

Imports System.Runtime.InteropServices
C1161587x.fm Page 259 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 259

For structures passed to API functions, declare the structure using the
StructLayout attribute to ensure compatibility with Visual Basic 6. The Struct-
Layout attribute takes a number of parameters, but the two most significant for
ensuring compatibility are LayoutKind and CharSet. To specify that the struc-
ture members should be passed in the order in which they are declared, set
LayoutKind to LayoutKind.Sequential. To ensure that string parameters are
marshaled as ANSI strings, set the CharSet member to CharSet.Ansi.
Suppose you are calling an API function named MyFunction in a DLL
named MyLibrary, which takes as a parameter a structure called MyStructure.
MyStructure contains a string and a fixed-length string type, declared in Visual
Basic 6 as follows:
Type MyStructure
MyStringMember As String
MyFixedLenStringMember As String * 32
End Type

An approximately equivalent Visual Basic .NET declaration is

Structure MyStructure
Dim MyStringMember As String
Dim MyFixedLenStringMember As String
End Structure

The above declaration is approximate because it is missing attributes to make it


fully equivalent with the Visual Basic 6 declaration. It is missing attributes on
the structure to specify layout and is also missing an attribute on MyFixedLen-
StringMember to mark it as a fixed-length string.
Because you cannot guarantee that the .NET runtime will pass the string
members in the order declared—for example, the .NET runtime may reorganize
the structure in memory so that MyFixedLenStringMember comes first—you
need to add marshalling attributes to the structure as follows:

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Structure MyStructure
Dim MyStringMember As String
Dim MyFixedLenStringMember As String
End Structure

Visual Basic .NET does not natively support fixed-length strings. However,
when you pass a structure containing a string to an API function, you can
change the string member to a character array and include a MarshalAs
C1161587x.fm Page 260 Thursday, November 15, 2001 3:46 PM

260 Part III Getting Your Project Working

attribute to tell the .NET runtime to pass a string of a fixed size. To do this,
declare the structure member—MyFixedLenStringMember—as a Char array
instead of a String. Include the UnmanagedType.ByValArray and SizeConst
arguments to the MarshalAs attribute. If the structure is going to be used with
any of the Visual Basic file functions—for example, if the structure serves as a
record in a random access file—you need to add the VBFixedString attribute.
You include or omit the MarshalAs and VBFixedString attributes depending on
how the structure is used in your code. In this case, since we are passing the
structure to a Windows API function, you need to add the MarshalAs attribute
to the MyFixedLenStringMember structure member but omit the VBFixedString
attribute, as follows:

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Structure MyStructure
Dim MyStringMember As String
‘Note: Change MyFixedLenStringMember to Char array. You must
‘ pad this member to 32 characters before calling the API
‘ function.

MarshalAs(UnmanagedType.ByValArray, SizeConst:=32) _
Dim MyFixedLenStringMember() As Char
End Structure

In addition to the ByValArray and SizeConst attributes, the code contains


a comment to warn you that the ByValArray attribute requires you to pass a
string padded out to exactly the size specified by SizeConst. This means that if
you want to pass 3 characters you must append 29 spaces to the end of the
string to create a buffer 32 characters in length. You can use the FixedLength-
String class provided in the compatibility library to create strings that are auto-
matically padded out to the desired size. For example, if you want to set the
MyFixedLenStringMember to a 32-character fixed-length string containing the
characters “123,” here’s how you would do it:

Dim ms As MyStructure
ms.MyFixedLenStringMember = New VB6.FixedLengthString(32, “123”)

With both the StructLayout and MarshalAs attributes in place, the structure
will be passed in memory to an API function in the same way as the equivalent
Visual Basic 6 Type…End Type structure is passed.
C1161587x.fm Page 261 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 261

Note Note The Upgrade Wizard incorrectly upgrades fixed-length


strings contained in structures. Instead of declaring the string as a
Char array, the wizard declares the upgraded fixed-length string as a
String. The wizard applies the ByValTStr attribute instead of the ByV-
alArray attribute. The upgraded code will compile and run. However,
there is a subtle problem that you need to be aware of: the last charac-
ter in the fixed-length string buffer is set to Null when passed to an API
function. If, for example, you are passing a 5-character fixed-length
string (as part of a structure to an API function) containing “12345”,
only “1234” will be passed. The last character, “5”, is replaced with a
Null character. To fix this problem, you need to change the member
type from String to Char array and change the MarshalAs attribute
from ByValTStr to ByValArray. Review your code to make sure you are
using the FixedLengthString class to create strings that you assign to
the member variable. Keep in mind that if the structure is not passed to
an API function you can remove the MarshalAs attribute, and you don’t
need to review your code in this case.

ObjPtr and StrPtr Not Supported


Visual Basic 6 includes helper functions that return a 32-bit address for an
object (ObjPtr) or a string (StrPtr). Visual Basic .NET does not support these
functions. Moreover, it does not allow you to obtain the memory address for
any type.
In cases where you must have control over underlying memory, the .NET
Framework provides a pointer type known as System.IntPtr. The System.Run-
time.InteropServices.Marshal class contains a number of functions that you can
use to obtain a pointer type for a given type. For example, the functions AllocH-
Global, GetComInterfaceForObject, and OffSetOf all return pointers to memory.
You can also use the GCHandle structure to obtain a handle to an element con-
tained in Garbage Collector–managed memory. In addition, you can obtain a
pointer to the memory by having the Garbage Collector pin the memory to a
single memory location and not move it.
The following Visual Basic 6 example calls CopyMemory to copy a source
string to a destination string. It uses StrPtr to obtain the memory addresses of
the source and destination strings.
C1161587x.fm Page 262 Thursday, November 15, 2001 3:46 PM

262 Part III Getting Your Project Working

Declare Sub CopyMemory Lib “kernel32” Alias “RtlMoveMemory” _


(ByVal lpDest As Long, ByVal lpSource As Long, _
ByVal cbCopy As Long)

Sub Main()
Dim sourceString As String, destString As String

sourceString = “Hello"
destString = “ World"

CopyMemory ByVal StrPtr(destString), _


ByVal StrPtr(sourceString), 10

MsgBox destString
End Sub

Because there is no direct equivalent for StrPtr in Visual Basic .NET, you
must use a different approach. To obtain a memory address to a string in Visual
Basic .NET, you call GCHandle.Alloc to associate a handle with the string. Call-
ing AddrOfPinnedObject on the handle allows you to obtain an IntPtr to the
string buffer. Finally, you can obtain the memory address by calling
IntPtr.ToInt32 to get the address value. It’s more work than using StrPtr, but it
achieves the same result. Here is the equivalent Visual Basic .NET code for the
Visual Basic 6 code just given:

Declare Sub CopyMemory Lib “kernel32” Alias “RtlMoveMemory” _


(ByVal lpDest As Integer, ByVal lpSource As Integer, _
ByVal cbCopy As Integer)

Public Sub Main()


Dim sourceString As String, destString As String

sourceString = “Hello"
destString = “ World"

Dim sourceGCHandle As GCHandle = _


GCHandle.Alloc(sourceString, GCHandleType.Pinned)
Dim sourceAddress As Integer = _
sourceGCHandle.AddrOfPinnedObject.ToInt32

Dim destGCHandle As GCHandle = _


GCHandle.Alloc(destString, GCHandleType.Pinned)
C1161587x.fm Page 263 Thursday, November 15, 2001 3:46 PM

Chapter 11 Resolving Issues with Language 263

Dim destAddress As Integer = _


destGCHandle.AddrOfPinnedObject.ToInt32

CopyMemory(destAddress, sourceAddress, 10)


sourceGCHandle.Free()
destGCHandle.Free()

MsgBox(destString)
End Sub

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 func-
tionality, or by using the .NET Framework to achieve equivalent behavior. Ulti-
mately, 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 lan-
guage and the .NET Framework.
C1161587x.fm Page 264 Thursday, November 15, 2001 3:46 PM
C1261587x.fm Page 265 Thursday, November 15, 2001 3:51 PM

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 prin-
ciple 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 com-
patible 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.

Similarities in Form Structure


When you create a new project in Visual Basic .NET, you will find yourself at
home in the environment. The way you create and design forms is the same in
Visual Basic .NET as it is in Visual Basic 6. Although the names of some of the
properties, methods, and events may have changed, you should be able to

265
C1261587x.fm Page 266 Thursday, November 15, 2001 3:51 PM

266 Part 3 Getting Your Project Working

quickly find the one you need to use. In fact, you will find that you can create
the exact same form in both Visual Basic 6 and Visual Basic .NET, using largely
the same actions, the same controls, and the same property settings.
The Upgrade Wizard can re-create your Visual Basic 6 forms in Visual
Basic .NET. This is possible because the essential pieces of Visual Basic .NET
are, for the most part, similar to those in Visual Basic 6: equivalent controls and
equivalent properties, methods, and events. Figures 12-1 and 12-2 demonstrate
a Visual Basic 6 form before and after being upgraded to Visual Basic .NET.

F12km01

Figure 12-1 Visual Basic 6 form in design view.

F12km02

Figure 12-2 Upgraded Visual Basic .NET form in design view.


C1261587x.fm Page 267 Thursday, November 15, 2001 3:51 PM

Chapter 12 Resolving Issues with Forms 267

As for the differences between forms in Visual Basic 6 and Visual Basic
.NET, most of them are subtle, many related to the renaming or restructuring of
components. It is these subtle differences, however, that can give you the most
grief. We will spend the remainder of the chapter talking about them.

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 funda-
mental differences should give you a good base to start from when you are try-
ing to understand issues you encounter involving a specific form or control.

Differences in Properties, Methods, and Events


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.

Some Properties, Methods, and Events Have Been Renamed


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, prop-
erties, 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 Lost-
Focus 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.

Multiple Properties Can Be Mapped into a Single Object Property


In some cases, the Upgrade Wizard will map more than one property to con-
structor 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
C1261587x.fm Page 268 Thursday, November 15, 2001 3:51 PM

268 Part 3 Getting Your Project Working

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 com-
ponent. For example, you can replace the following Visual Basic 6 code:
Command1.Left = 10
Command1.Top = 20

with the following Visual Basic .NET code:

Button1.Location = New System.Drawing.Point(10, 20)

In a similar manner, the Visual Basic 6 Width and Height properties can be rep-
resented by the Size property in Visual Basic .NET.

Note 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 proper-
ties to set the position and size of controls on the form. The Upgrade
Wizard will automatically map properties such as Left and Top to con-
structor parameters on an object such as Point.

Some Properties, Methods, and Events Are Not Supported


In some cases you will find that there is no equivalent Visual Basic .NET coun-
terpart 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.

Support Available in a Different Manner You won’t find the Visual Basic 6 prop-
erties 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 Sys-
tem.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 accom-
plish 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.
C1261587x.fm Page 269 Thursday, November 15, 2001 3:51 PM

Chapter 12 Resolving Issues with Forms 269

Table 12-1User Interface Components and Their


Visual Basic .NET Equivalents
Visual Basic 6 Visual Basic .NET
Graphics statements System.Drawing.Graphics class
(such as Line and Circle)
OLE drag and drop Windows Forms drag-and-drop properties, methods,
and events to implement manual drag and drop
Clipboard object System.Windows.Forms.Clipboard class
Printer object System.Drawing.Printing.PrintDocument class

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 Frame-
work supports these features. The Upgrade Wizard will preserve any Visual
Basic 6 code you have written related to out-of-date objects, properties, meth-
ods, 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 Win-
dows 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
C1261587x.fm Page 270 Thursday, November 15, 2001 3:51 PM

270 Part 3 Getting Your Project Working

technologies has accumulated over the years. Visual Basic 6 supports all tech-
nologies, 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 sec-
tion 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 Sys-
tem.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 con-
tinue 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.
C1261587x.fm Page 271 Thursday, November 15, 2001 3:51 PM

Chapter 12 Resolving Issues with Forms 271

.NET Framework Equivalents for Visual Basic 6


Table 12-2
Graphics Statements
Visual Basic 6 Visual Basic .NET
Circle System.Drawing.Graphics.DrawEllipse method
Cls System.Drawing.Graphics.Clear (color) method
CurrentX X parameter to various graphics methods, such as
System.Drawing.Graphics.DrawString
CurrentY Y parameter to various graphics methods, such as
System.Drawing.Graphics.DrawString
DrawMode Attributes of System.Drawing.Pen passed to a Draw
method on System.Drawing.Graphics
DrawStyle Attributes of System.Drawing.Pen passed to a Draw
method on System.Drawing.Graphics
DrawWidth Width property of System.Drawing.Pen passed to a Draw
method on System.Drawing.Graphics
FillColor One of the colors contained in System.Drawing.Brushes
passed to either System.Drawing.Graphics method Fill-
Ellipse or FillRectangle
FillStyle HatchBrush class and HatchStyle enumeration in the
System.Drawing.Drawing2D namespace
HDC System.Drawing.Graphics.GetHdc method
Image System.Drawing.Image class
Line System.Drawing.Graphics.DrawLine method
PaintPicture System.Drawing.Graphics.DrawImage method
Point System.Drawing.Bitmap.GetPixel method
Print System.Drawing.Graphics.DrawString method
PSet System.Drawing.Bitmap.SetPixel method
TextHeight System.Drawing.Graphics.MeasureString method
TextWidth System.Drawing.Graphics.MeasureString method

Dynamic Data Exchange


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 sup-
port 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
C1261587x.fm Page 272 Thursday, November 15, 2001 3:51 PM

272 Part 3 Getting Your Project Working

expose public data and methods do so via COM, DDE is generally not the pri-
mary way that you retrieve public data and invoke public methods on a Win-
dows application. You use COM as a means of communicating with most
Windows applications.
After upgrading your application to Visual Basic .NET, it is highly recom-
mended 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 imple-
menting 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 exam-
ple, 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:

1. Create a Visual Basic 6 ActiveX EXE project.


2. Add a public method to Class1 called LinkExecute that takes two
parameters: ServerTopic and ExecCommand.
3. Add a form—called Form1—to the ActiveX EXE project.
4. Add a TextBox—called Text1—to the form.
5. Add the following code to the LinkExecute method in Class1:

Dim f As New Form1

f.Text1.LinkMode = 0 ‘None
f.Text1.LinkTopic = ServerTopic
f.Text1.LinkMode = 2 ‘Manual
f.Text1.LinkExecute ExecCommand

6. Build the ActiveX EXE server as Project1.dll.

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”)
C1261587x.fm Page 273 Thursday, November 15, 2001 3:51 PM

Chapter 12 Resolving Issues with Forms 273

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 mem-
ory 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


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 Com-
mandButton 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 inter-
face, 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 pro-
vides 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 drag-
ging. 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,
C1261587x.fm Page 274 Thursday, November 15, 2001 3:51 PM

274 Part 3 Getting Your Project Working

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 opera-
tion. 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 pre-
serves 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.

Issues Involving Forms


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.

Event Firing Differences


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
C1261587x.fm Page 275 Thursday, November 15, 2001 3:51 PM

Chapter 12 Resolving Issues with Forms 275

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 element 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 System.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 initialized


.
Private Sub Form1_Resize(ByVal eventSender As System.Object, _
ByVal eventArgs As System.EventArgs) _
Handles MyBase.Resize
‘ Position control array element 1 below control array element 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 ele-
ment 1 doesn’t exist.” It occurs because the Resize event takes place before the
Load event.
C1261587x.fm Page 276 Thursday, November 15, 2001 3:51 PM

276 Part 3 Getting Your Project Working

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 element 0
Command1(1).Top = _
VB6.TwipsToPixelsY(VB6.PixelsToTwipsY(Command1(0).Top) + _
VB6.PixelsToTwipsY(Command1(0).Height) + 3)
End If

In the Form1_Load event procedure, set m_Loaded to True as follows:

m_Loaded = True

‘ 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.
C1261587x.fm Page 277 Thursday, November 15, 2001 3:51 PM

Chapter 12 Resolving Issues with Forms 277

The Default Form: DefInstance


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. Hav-
ing 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
C1261587x.fm Page 278 Thursday, November 15, 2001 3:51 PM

278 Part 3 Getting Your Project Working

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.”

Application Lifetime and Forms


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, terminates 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.

Note 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 vari-
ous settings under a number of categories. Select the General cate-
gory 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.
C1261587x.fm Page 279 Thursday, November 15, 2001 3:51 PM

Chapter 12 Resolving Issues with Forms 279

F12km03

Figure 12-3. Startup Object list in the Visual Basic .NET project set-
tings 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 appli-
cation 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 ter-
minates 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
C1261587x.fm Page 280 Thursday, November 15, 2001 3:51 PM

280 Part 3 Getting Your Project Working

If you upgrade the project to Visual Basic .NET, the application will termi-
nate 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 pro-
gram. 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 decre-
mented and reaches 0, meaning that no more forms are loaded, call Applica-
tion.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
C1261587x.fm Page 281 Thursday, November 15, 2001 3:51 PM

Chapter 12 Resolving Issues with Forms 281

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 proce-
dure 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 System.EventArgs) _
Handles MyBase.Load
AddForm()
Form2.DefInstance.Show()
End Sub

Private Sub Form1_Closed(ByVal sender As Object, _


ByVal e As System.EventArgs) _
Handles MyBase.Closed
RemoveForm()
End Sub

Here is the code you need to add to Form2:

Private Sub Form2_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) _
Handles MyBase.Load
AddForm()
End Sub

Private Sub Form2_Closed(ByVal sender As Object, _


ByVal e As System.EventArgs) _
Handles MyBase.Closed
RemoveForm()
End Sub
C1261587x.fm Page 282 Thursday, November 15, 2001 3:51 PM

282 Part 3 Getting Your Project Working

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 appli-
cation 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 dis-
plays 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.

Lifetime of MDI Form Applications


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 applica-
tions, 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 dis-
played 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.
C1261587x.fm Page 283 Thursday, November 15, 2001 3:51 PM

Chapter 12 Resolving Issues with Forms 283

Conclusion

This chapter has covered the general types of differences between Visual Basic
6 and Visual Basic .NET that you will encounter. For example, it looked at how
property model differences and differences in technology play out in your
upgraded Visual Basic .NET application. In addition, it looked at how form-
related issues such as event ordering and lifetime can lead to subtle differences
in the execution of your upgraded application. We have offered solutions to
help you move your Visual Basic 6 code to Visual Basic .NET. Solutions are
available even when the technology is no longer supported or the components
and language statements that provide the support have changed drastically.
The characteristics that make up a Visual Basic form have remained
largely constant since the inception of Visual Basic in 1988. The form is based
on a Windows window in which a form designer allows you to drag and drop
controls and set properties. The way you create a form and the behavior you
expect from your form in a compiled application have also remained
unchanged over the years. The Visual Basic .NET form is no exception. You cre-
ate and interact with Visual Basic .NET forms in pretty much the same way as
you do Visual Basic 6 forms. Differences become noticeable when you start
using Visual Basic .NET forms on a more granular level. For example, proper-
ties do not always jive between Visual Basic 6 and Visual Basic .NET.
Figuring out the mapping between Visual Basic 6 and Visual Basic .NET
properties is a matter of learning such details as that Caption always maps to
Text or that OLEStartDrag maps to DoDragDrop. Most of these mappings can
be looked up or memorized from a table. Appendix A of this book provides
such a table. It should serve as your technical reference for mapping Visual
Basic 6 objects and statements to their Visual Basic .NET equivalents.
C1261587x.fm Page 284 Thursday, November 15, 2001 3:51 PM
C1361587x.fm Page 285 Friday, November 16, 2001 8:16 AM

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 compo-
nents with Visual Basic .NET.

ActiveX Controls Are Still Supported—Yes!


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

285
C1361587x.fm Page 286 Friday, November 16, 2001 8:16 AM

286 Part III Getting Your Project Working

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 com-
ponent 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. Further-
more, Microsoft is not introducing any of the new .NET controls and compo-
nents as direct replacements of similarly featured ActiveX controls. For
example, even though there is a .NET TreeView control, it is not fully compat-
ible 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.

ActiveX Upgrade Strategy


Since one of the overriding goals of the Upgrade Wizard is to ensure compati-
ble 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 gen-
eral 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.

Limitations of ActiveX Control Hosting


If you have ever used an ActiveX control in an environment outside Visual
Basic, such as Microsoft Internet Explorer or Microsoft Office, you might have
encountered differences or limitations in behavior for that control. Likewise, a
.NET Windows form is a unique control host environment with its own capabil-
ities and limitations. Since a .NET Windows form is primarily the host container
for .NET components, some interesting magic is cast on an ActiveX control to
make it act as if it were a .NET component. Unfortunately, this magic has lim-
ited power and cannot be applied to all types of ActiveX controls. Therefore the
Windows Forms environment offers no support for the following types of
ActiveX controls and components:
C1361587x.fm Page 287 Friday, November 16, 2001 8:16 AM

Chapter 13 ActiveX Controls and Components 287

■ Container controls
■ Windowless controls
■ DAO-based data-bound controls
■ Controls that make use of internal Visual Basic 6 interfaces
■ Components that hook into the Visual Basic 6 extensibility model
■ ActiveX designers

We discuss each of these in turn in the sections that follow.

Container Controls
The SSTab ActiveX control is an example of a container control. Although you
can place an SSTab control on a Windows form, you cannot place other con-
trols within the SSTab. Part of the problem is that the SSTab control needs to
communicate with the controls placed inside it. The communication happens
using ActiveX interfaces that are not supported by a .NET Windows form. The
end result is that the SSTab cannot find the child controls. Moreover, it cannot
associate each child control with the tab to which it belongs. The ability to dis-
play contained controls on separate tabs—an essential feature of the SSTab con-
trol—is lost.

Note The Visual Basic .NET Upgrade Wizard automatically replaces


each instance of the SSTab control with a .NET Windows TabControl.
This is one of the few cases in which the Upgrade Wizard replaces an
ActiveX control with an equivalent .NET control. Since the SSTab con-
trol is dead weight on a Windows form, the Visual Basic team decided
to give you a head start. You will find that all of the child controls on
each SSTab tab are moved to the appropriate TabControl tab. You are
left to focus on changing top-level properties of the TabControl and
tweaking some code. Otherwise, you would be spending hours metic-
ulously re-creating and placing each child control on each TabControl
tab exactly as it appeared in the SSTab control.

Windowless Controls
The Windows Forms environment provides support only for controls that dis-
play within a standard window. To support windowless controls, a host needs
to support painting the control directly within the form window. One nice fea-
ture is that windowless controls can be made transparent, since the host man-
ages the painting for the control and the form on the same window surface. A
C1361587x.fm Page 288 Friday, November 16, 2001 8:16 AM

288 Part III Getting Your Project Working

side effect of Windows Forms not supporting windowless controls is that you
cannot create transparent labels, as you could in Visual Basic 6.
In most cases the lack of support for windowless controls will not prohibit
you from placing a windowless control on a Windows form. Most windowless
controls have a split personality of being either windowed or windowless,
depending on the capabilities of the host. If the host supports windowless con-
trols, the control will take on its preferred windowless form. If the host does not
support windowless controls, the control will take on its less desirable, but work-
able, windowed appearance. In the case of Windows Forms, all windowless con-
trols will be asked to create a window and render as a windowed control.

DAO-Based Data-Bound Controls


DAO-based data-bound controls include the Visual Basic 5 DBGrid and RDO
Data control. Visual Basic 6 links these controls to their data source using an
internal data binding manager object. Visual Basic .NET contains no such inter-
nal data binding manager object and so provides no such support for DAO-
based controls. Therefore, you will find that you cannot bind a DBGrid control
to an RDO Data control in Visual Basic .NET.
ADO data binding works just fine. The data binding manager object for
ADO is contained in an ActiveX component called MSBind. This component
enables you to bind ADO components such as the Visual Basic 6 DataGrid to
ADO data sources such as an upgraded DataEnvironment class.

Controls That Make Use of Internal Visual Basic 6 Interfaces


A limitation of the ActiveX architecture is that it does not provide a standard
mechanism permitting controls to find one another on the same form or docu-
ment. It is left up to the host to define interfaces that allow controls to hook up
with one another. Visual Basic 6 defines interfaces such as IVBGetControl that
allow ActiveX controls to find their parent, children, or siblings. Visual Basic
.NET does not provide full support for these interfaces. As a result, controls that
depend on other controls will not work properly. For example, the UpDown
ActiveX control becomes more or less a paperweight in the Visual Basic .NET
environment. You can attempt to buddy the control to another control, but you
will find that the values for the buddy control will not update when you click
the UpDown control. In this case, it is recommended that you use the .NET
Framework UpDown control instead.

Components That Hook into the Visual Basic 6 Extensibility Model


Don’t expect to be able to use a Visual Basic 6 add-in component in the Visual
Basic .NET environment. Add-in components such as wizards generally interact
with the development environment to create controls, change code, and update
project settings. The component interacts with the environment through a set of
C1361587x.fm Page 289 Friday, November 16, 2001 8:16 AM

Chapter 13 ActiveX Controls and Components 289

extensibility interfaces. The problem is that Visual Basic .NET supports a com-
pletely different set of extensibility interfaces than Visual Basic 6 does. Visual
Basic 6 add-in components simply will not work in the new environment.

ActiveX Designers
ActiveX designers such as the DataEnvironment, WebClass, DHTML, and
DataReport Writer are not supported in Visual Basic .NET. The Microsoft Visual
Studio .NET environment supports a completely different approach to designers
known as packages. None of these designers are provided as Visual Studio
.NET packages. In the case of the DataEnvironment and WebClass designers,
the Upgrade Wizard will upgrade your Visual Basic 6 DataEnvironment and
WebClass projects to take advantage of run-time support classes for these
designers. This means that your projects based on DataEnvironment and Web-
Class can be made to work at run time, but you will not get a fancy designer
that you can use to change settings at design time. All settings must be changed
by writing code.

ActiveX .NET Controls: Best of Both Worlds


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.

ActiveX Interop Ax Wrapper: The Windows Forms Wrapper


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 con-
trol. Figure 13-1 illustrates how an ActiveX control is extended to include prop-
erties 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 wrap-
per 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.
C1361587x.fm Page 290 Friday, November 16, 2001 8:16 AM

290 Part III Getting Your Project Working

Windows Forms
Ax wrapper

Location
Tag
ActiveX control Visible
BackColor
Caption
Font

F13km01

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 gen-
erated 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.

Note 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 con-
trol 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.

Property and Parameter Type Mappings


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 prop-
erty is based on the StdPicture object, which implements the IPicture and IPic-
tureDisp interfaces. In Visual Basic .NET, however, the Picture property for
.NET components is type System.Drawing.Image. The problem is that you can-
not directly assign a property of type StdPicture to a System.Drawing.Image
C1361587x.fm Page 291 Friday, November 16, 2001 8:16 AM

Chapter 13 ActiveX Controls and Components 291

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.

Ax wrapper
ActiveX control Translation layer

Picture
System.Drawing.Image

F13km02

Figure 13-2 Picture property translated to System.Drawing.Image.

In a similar manner, the Windows Forms Ax wrapper exposes other com-


mon 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.

Table 16-1 Ax Wrapper Mapping of ActiveX Types to .NET Types


ActiveX Control Property Type Maps to Ax Wrapper Property Type
OLE_COLOR System.Drawing.Color
Font System.Drawing.Font
Picture (bitmap) System.Drawing.Image

Take, for example, the following Visual Basic 6 code, which assigns a Pic-
ture 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

The code is upgraded to the following Visual Basic .NET code:

ListView1.BackColor = System.Drawing.Color.Red
ListView1.Picture = _
System.Drawing.Image.FromFile(Environ(“WINDIR”) & _
“\Prairie Wind.bmp”)
Picture1.Image = ListView1.Picture
C1361587x.fm Page 292 Friday, November 16, 2001 8:16 AM

292 Part III Getting Your Project Working

Variant and Object Types: Mapping—Not!


As you can see, you can assign native .NET types such as System.Draw-
ing.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 hap-
pens 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 param-
eters. 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.Draw-
ing.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, how-
ever, the Picture parameter is mapped to Object and requires the underlying
C1361587x.fm Page 293 Friday, November 16, 2001 8:16 AM

Chapter 13 ActiveX Controls and Components 293

ActiveX type IPictureDisp. The compatibility library function ImageToIPicture-


Disp is required to convert the Image object to an IPictureDisp—the underlying
interface type for Picture—and the resulting IPictureDisp is passed as the Pic-
ture 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 conver-
sion 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 Tree-
View’s top-level properties, methods, and events. Properties such as Appear-
ance, 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.Draw-
ing.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.
C1361587x.fm Page 294 Friday, November 16, 2001 8:16 AM

294 Part III Getting Your Project Working

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

After upgrade, the Visual Basic .NET code is as follows:

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.Draw-
ing.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 Sys-
tem.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 con-
vert 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.
C1361587x.fm Page 295 Friday, November 16, 2001 8:16 AM

Chapter 13 ActiveX Controls and Components 295

Table 16-2 Useful Conversion Functions for Common


ActiveX Object Types
Conversion Function(s) Description
System.Convert methods Allow you to convert from virtually
any .NET type to another .NET type.
Use 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
methods color, Windows color, and .NET color
types.
FontToIFont, IFontToFont Located in the Microsoft.Visual-
Basic.Compatibility.VB6.Support
class. You use these functions to con-
vert between .NET and ActiveX Font
types. They are used primarily when
you are trying to get or set an ActiveX
component property that is exposed
as Object, IFont, IFontDisp, or as a
component-specific class that imple-
ments IFont.
CursorToIPicture, IconToIPicture Allow you to convert a System.Win-
dows.Forms.Cursor or System.Draw-
ing.Icon type directly to an ActiveX
IPicture.
ImageToIPicture, ImageToIPictureDisp, Allow you to convert between the
IPictureToImage, IPictureDispToImage .NET System.Drawing.Image and
ActiveX IPicture types. These func-
tions are intended for situations in
which you are trying to get or set an
ActiveX component Picture property
that is defined as Object, IPicture,
IPictureDisp, or as a component-
specific class declaration that imple-
ments IPicture.

Common Exceptions That Require Type Conversions


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.
C1361587x.fm Page 296 Friday, November 16, 2001 8:16 AM

296 Part III Getting Your Project Working

The problem is that an ActiveX MousePointer property is usually a MousePoint-


erConstants 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 excep-
tion 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 com-
ponent 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 con-
trol 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 follow-
ing 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 underly-
ing 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()
C1361587x.fm Page 297 Friday, November 16, 2001 8:16 AM

Chapter 13 ActiveX Controls and Components 297

Name Collisions
Name collisions happen when the Ax wrapper class includes a property,
method, or event that has the same name as the ActiveX control. Take, for
example, an ActiveX control that has a property called Size. The ActiveX con-
trol’s Size property will be in conflict with the Size property that the Ax wrapper
adds to the control. To manage this type of conflict, the Ax wrapper will rename
the ActiveX control property to CtlSize. If you need to set the ActiveX control’s
size property, you should set CtlSize. To set the size property managed by Win-
dows Forms on behalf of the ActiveX control, you would set the Size property.
Fortunately, name conflicts will not occur for most ActiveX controls. When the
Upgrade Wizard detects a name conflict, it will upgrade your code to use the
correct property or method even if the property or method has been renamed.
Naming conflicts are not new to Visual Basic .NET. Visual Basic 6 also
manages name conflicts for you. As we mentioned earlier in this chapter, Visual
Basic 6 creates a wrapper class for ActiveX controls. If there is a conflict with a
property or method name, the wrapper hides the ActiveX control property or
method in favor of the wrapper’s property or method. To access the underlying
ActiveX property, you need to use the ActiveX control’s Object property. For
example, the following Visual Basic 6 code:
MyFavoriteControl.Object.Tag = “My control’s internal tag"
MyFavoriteControl.Tag = “The Tag property given to me by Visual Basic”

upgrades to the following Visual Basic .NET code:

MyFavoriteControl.CtlTag = “My control’s internal tag"


MyFavoriteControl.Tag = “The Tag property given to me by Visual Basic”

Event Name Collisions


If an event name conflicts with another property or base class event name, the
Upgrade Wizard renames the event. It does so by appending the word Event to
the end of the event name. One ActiveX control that exhibits this behavior is
the Microsoft WinSock control. If you place a WinSock control on a Windows
form and view its events, you will see that the Close and Connect events have
been renamed CloseEvent and ConnectEvent, respectively. Since most controls
contain unique event names, you will seldom encounter this issue. Even if you
do, you probably will not notice. If a name conflict does occur, the renamed
event is easy to find, since the event name is mostly preserved. The Upgrade
Wizard automatically upgrades your code to use the renamed event name, so you
should not encounter any issues after upgrading your Visual Basic 6 application.
C1361587x.fm Page 298 Friday, November 16, 2001 8:16 AM

298 Part III Getting Your Project Working

Using ActiveX Components from .NET


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.

When ByRef Bites


The Visual Basic .NET compiler introduces a new semantic when passing prop-
erties 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 System.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.
C1361587x.fm Page 299 Friday, November 16, 2001 8:16 AM

Chapter 13 ActiveX Controls and Components 299

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.List-
Items.Count:

‘ Pass AxListView1.ListItems.Count ByVal to avoid Set being called


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
C1361587x.fm Page 300 Friday, November 16, 2001 8:16 AM

300 Part III Getting Your Project Working

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 InvalidCastEx-


ception 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.
C1361587x.fm Page 301 Friday, November 16, 2001 8:16 AM

Chapter 13 ActiveX Controls and Components 301

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 Sys-
tem.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.

Alias Types Are Not Supported


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
C1361587x.fm Page 302 Friday, November 16, 2001 8:16 AM

302 Part III Getting Your Project Working

lost. Instead you need to use the base type. For example, you need to use the
.NET System.UInt32 type instead of OLE_COLOR.

Note 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.

Module Methods Are Not Supported


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 con-
tains 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 mod-
ule 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
C1361587x.fm Page 303 Friday, November 16, 2001 8:16 AM

Chapter 13 ActiveX Controls and Components 303

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 follow-
ing 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
C1361587x.fm Page 304 Friday, November 16, 2001 8:16 AM

304 Part III Getting Your Project Working

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 abil-
ity enables you to get up and running in .NET quickly.
C1461587x.fm Page 305 Friday, November 16, 2001 9:10 AM

Resolving Data Access


Issues
Most business applications have some sort of data access. This fact is not sur-
prising when you consider that conceptually, most businesses are themselves
based around data: customers and orders, inventory and purchase informa-
tion—all of this business information needs to be stored in such a way that it
can be easily retrieved, edited, and stored again. Countless Microsoft Visual
Basic applications have been developed to provide a friendly face for informa-
tion in databases. Each new version of Visual Basic has delivered enhancements
to data access over previous versions. Visual Basic .NET is no exception. With
this version, we see continued support for ActiveX Data Objects (ADO) code
and data binding. Visual Basic .NET also supports Data Access Objects (DAO)
and Remote Data Objects (RDO) code, but it does not support data binding for
DAO and RDO. Finally, it introduces a new disconnected type of data access
called ADO.NET. This chapter looks at upgrading the three major data access
technologies, concentrating on ADO.
The examples in this chapter are all included on the companion CD. They
use the Northwind Microsoft Access database. This database is distributed with
Microsoft Access, Microsoft Office XP Professional, Visual Basic 6, and various
other Microsoft products. The examples in this chapter assume that this database
is in the location C:\NWind.mdb. If it is somewhere else, you will need to edit
the projects to point to the correct location or move the database to this location.

305
C1461587x.fm Page 306 Friday, November 16, 2001 9:10 AM

306 Part III Getting Your Project Working

Data Access in Visual Basic


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 ele-
ment of each of the three components. Let’s look quickly at the three compo-
nents 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

This code upgrades to the following:


Dim cn As ADODB.Connection = New ADODB.Connection()
Dim rs As ADODB.Recordset
cn.Open(“Provider=Microsoft.Jet.OLEDB.3.51;Data
Source=c:\temp\nwind.mdb”)
rs = cn.Execute(“Select * From Employees”)
MsgBox(rs.Fields(“FirstName”).Value & “ “ & rs.Fields(“Last-
Name”).Value)
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 upgrad-
ing, 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!Field-
Name. 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:
C1461587x.fm Page 307 Friday, November 16, 2001 9:10 AM

Chapter 14 Resolving Data Access Issues 307

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, sup-
ports 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 implemen-
tation allowed a seamless integration, but it also tied the data binding technol-
ogy 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 Win-
dows 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 con-
trol and the ADO data environment.

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 environ-
ment, the Upgrade Wizard upgrades the connections, commands, and Record-
sets to a class that has the same run-time behavior as the Visual Basic 6 data
environment.
C1461587x.fm Page 308 Friday, November 16, 2001 9:10 AM

308 Part III Getting Your Project Working

Components That Don’t Upgrade


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.

Visual Basic 6 Visual Basic .NET

Code Code
DAO
Data control

Code Code

RDO Data control

Data Connection Designer

Code Code

Data control Data control


ADO
Data environment Data environment class

Data source class

F14km01

Figure 14-1 How data access technologies upgrade.


C1461587x.fm Page 309 Friday, November 16, 2001 9:10 AM

Chapter 14 Resolving Data Access Issues 309

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.

General Issues with Data Access Code


This section looks at some of the changes that are made to your data access
when it is upgraded from Visual Basic 6 to Visual Basic .NET. It also discusses
solutions to problems you may encounter when upgrading data access code.

DAO and RDO Module Methods


Both DAO and RDO expose module methods. As we discussed in Chapter 13,
module methods are properties and functions of the COM library itself that are
made globally available. They can be used without creating an instance of any
class. For example, the following DAO code opens a database using the Open-
Database module method:
Dim db As Database
Set db = OpenDatabase(“c:\temp\NWind.mdb”)

When Visual Basic 6 executes this code, it recognizes that OpenDatabase is a


method of the DAO COM library rather than of a particular class within the
C1461587x.fm Page 310 Friday, November 16, 2001 9:10 AM

310 Part III Getting Your Project Working

library. Visual Basic .NET does not support module methods; every method
must be qualified with a class.
Because module methods are actually methods of the COM library’s
underlying AppID class, the Upgrade Wizard locates the AppID class within the
COM component’s type library. It then creates a global variable of this class and
places it in a module called UpgradeSupport.vb. If this module doesn’t already
exist, the wizard creates it for you. The name of the variable it creates is
DAODBEngine_definst for DAO, RDOrdoEngine_definst for RDO. When the
previous example is upgraded, the wizard creates an UpgradeSupport.vb mod-
ule with the following code:
Module UpgradeSupport
Friend DAODBEngine_definst As New DAO.DBEngine
End Module

The code that uses the OpenDatabase method is upgraded to be a method of


this default instance:
Dim db As DAO.Database
db = DAODBEngine_definst.OpenDatabase(“c:\temp\NWind.mdb”)

Default instances are a good solution for module methods. If you’re con-
tinuing to write DAO and RDO code, you should be aware of this difference
and use the global variable to access the module methods. For example, you
could add the following code to the upgraded project that uses the DAO Begin-
Trans and CommitTrans methods:
DAODBEngine_definst.BeginTrans()
DAODBEngine_definst.CommitTrans()

Similarly, you can add the following DAO methods and properties:

BeginTrans CommitTrans CompactDatabase


CreateDatabase CreateWorkspace Idle
OpenConnection Properties RegisterDatabase
RepairDatabase Rollback SetOption
SystemDB Version Workspaces

You can also add the following RDO methods and properties:

rdoCreateEnvironment rdoRegisterDataSource

If you are writing a new Visual Basic .NET project that uses module meth-
ods, you will need to add a global default instance variable to your application
to access these methods.
C1461587x.fm Page 311 Friday, November 16, 2001 9:10 AM

Chapter 14 Resolving Data Access Issues 311

ADO Version 2.7


ADO comes in a number of versions: 2.1, 2.2, 2.5, 2.6, and now version 2.7. You
can use any of these versions in your application. The different versions are
merely different type libraries that point to the same DLL: Msado15.dll. Each
time you install a new version of Microsoft Data Access Components (MDAC),
it updates the underlying Msado15.dll and installs a new type library for the lat-
est version. ADO 2.7 is installed with Visual Basic .NET. When you upgrade
your project, the Upgrade Wizard automatically changes any references to ear-
lier versions of ADO to ADO 2.7. These changes don’t cause any problems with
upgraded code, since ADO 2.7 is backward compatible with previous versions.

Errors in Events
Most data access applications use events in one way or another. For example,
if your project uses the ADO Data control, you may have an event handler for
the ADODC_MoveComplete event. Another common usage is to declare an
ADO.Connection variable using the WithEvents keyword and to define one or
more event handlers for the variable. In Visual Basic .NET, if a COM object
raises an event, and an untrapped error occurs in the event, the error will be
silently ignored. Let’s look at an example—the Visual Basic 6 project prjADO-
Event (included on the companion CD). This project has a TextBox bound to an
ADO Data control that accesses the Employees table in the Northwind data-
base. When you run this project from the Visual Basic 6 IDE and click the Raise
Error button, an error occurs in the ADODC1_MoveComplete event, as shown in
Figure 14-2.

F14km02

Figure 14-2 Error occurring in the Visual Basic 6 project prjADOEvent.

Try upgrading the application to Visual Basic .NET. Now when you click
the Raise Error button, no error occurs. Did Visual Basic .NET somehow fix the
C1461587x.fm Page 312 Friday, November 16, 2001 9:10 AM

312 Part III Getting Your Project Working

error? Unfortunately not. It is simply ignoring the error, as it does all errors
occurring in events raised by COM objects. The solution is to put error trapping
code in the event. In this example, the following Visual Basic 6 code for the
MoveComplete event:
Private Sub Adodc1_MoveComplete(ByVal adReason As ADODB.EventReasonE-
num, _
ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, _
ByVal pRecordset As ADODB.Recordset)
Dim x As Integer, y As Integer
If m_RaiseError Then
x = x / y
End If
End Sub

is upgraded to

Private Sub Adodc1_MoveComplete(ByVal adReason As _


ADODB.EventReasonEnum, _
ByVal pError As ADODB.Error, ByRef adStatus As _
ADODB.EventStatusEnum, _
ByVal pRecordset As ADODB.Recordset) Handles Adodc1.MoveComplete
Dim x, y As Short
If m_RaiseError Then
x = x / y
End If
End Sub

Let’s change this event to include Try...Catch error trapping:

Private Sub Adodc1_MoveComplete(ByVal adReason As _


ADODB.EventReasonEnum, _
ByVal pError As ADODB.Error, ByRef adStatus As _
ADODB.EventStatusEnum, _
ByVal pRecordset As ADODB.Recordset) Handles Adodc1.MoveComplete
Try
Dim x, y As Short
If m_RaiseError Then
x = x / y
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub

Now Visual Basic .NET catches and displays the error, as shown in Figure 14-3.
You should add error trapping code to all DAO, RDO, and ADO events that
may raise errors.
C1461587x.fm Page 313 Friday, November 16, 2001 9:10 AM

Chapter 14 Resolving Data Access Issues 313

F14km03

Figure 14-3 Error caught and displayed by Visual Basic .NET.

RDO Connection
A common technique when programming with RDO in Visual Basic 6 is to let
the user configure the database connection at run time, rather than requiring it
to be set up at design time. To do this, you write code that opens an rdoCon-
nection with a blank Connect argument, as in the following code:
Dim cn As rdoConnection
Dim en As rdoEnvironment
Set en = rdoEnvironments(0)
Set cn = en.OpenConnection(““)

When this code runs, it brings up the Select Data Source dialog box, as shown
in Figure 14-4.

F14km04

Figure 14-4 Select Data Source dialog box.


C1461587x.fm Page 314 Friday, November 16, 2001 9:10 AM

314 Part III Getting Your Project Working

The code upgrades to the following:


Dim cn As RDO.rdoConnection
Dim en As RDO.rdoEnvironment
en = RDOrdoEngine_definst.rdoEnvironments(0)
cn = en.OpenConnection(““)

Whether this upgraded code works or not in Visual Basic .NET depends on
where it is located. If it is located in a Button_Click event, it will work. How-
ever, if it is in Form_Load, it will fail with the error shown in Figure 14-5.

F14km05

Figure 14-5 RDO connection error in Visual Basic .NET.

The reason that it fails in Form_Load is that the dialog box tries to retrieve
a handle for the parent window. In Visual Basic 6 that handle exists, but in
Visual Basic .NET it hasn’t been created yet. The solution is to move the code
from Form_Load to another event, such as a Button_Click event.

Null, vbNullString, and vbNullChar


As we discussed in Chapter 11, the Null value works differently in Visual Basic
.NET than it does in Visual Basic 6. The use of Null can sometimes lead to sub-
tle, hard-to-find errors. You may find one such error with vbNullString. It is a
common practice in Visual Basic 6 to use vbNullString to reset a Recordset filter,
as in the following code:
Dim rs As New ADODB.Recordset
rs.Filter = vbNullString

This code sets the Recordset filter to an empty string. After upgrading, it
becomes the following:
Dim rs As New ADODB.Recordset()
rs.Filter = vbNullString

When run, this code causes a COM exception in Visual Basic .NET because
vbNullString has the value Nothing in Visual Basic .NET, whereas in Visual
Basic 6 it has the empty string value " ". The Recordset filter is expecting a string
C1461587x.fm Page 315 Friday, November 16, 2001 9:10 AM

Chapter 14 Resolving Data Access Issues 315

value rather than a Null, and hence the error. The solution is to change the code
to use an empty string:
Dim rs As New ADODB.Recordset()
rs.Filter = ““

This code works perfectly.

ADO Data Environment


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, com-
mands, 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

After upgrading, this code looks like the following:


Dim rs As ADODB.Recordset
rs = myDataEnvironment.rsmyCommand

With a few exceptions that we will discuss now, the upgraded data environ-
ment 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 exam-
ple, 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.
C1461587x.fm Page 316 Friday, November 16, 2001 9:10 AM

316 Part III Getting Your Project Working

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.

Cursor Location with Microsoft Access Databases


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 cur-
sor 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 con-
nections created in code.

ADO Data Binding


With ADO data binding, you can bind controls on forms to fields in a data-
base. In Visual Basic 6, you can bind a control to one of three ADO objects:

■ ADO data environment


■ ADO Data control
■ ADO data source classes

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 Record-
C1461587x.fm Page 317 Friday, November 16, 2001 9:10 AM

Chapter 14 Resolving Data Access Issues 317

set 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.

Table 14-1 Properties Controlling Simple Binding in Visual Basic 6


Property Description
DataSource The name of the ADO Data control or ADO data environment to
bind to
DataMember For ADO data environments only, the name of the command to
bind to
DataField The field of the Recordset to bind to
DataFormat The format in which the data is to be displayed

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 con-
trol. When it upgrades the form, the Upgrade Wizard does the following:

■ Inserts a form variable that manages the control binding. The vari-
able 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 auto-
matically. Let’s look at them now.
C1461587x.fm Page 318 Friday, November 16, 2001 9:10 AM

318 Part III Getting Your Project Working

Control Arrays of ADO Data Controls


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.

Setting Data Binding Properties at Run Time


Many projects that use data binding have code that adjusts data binding prop-
erties 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 North-
wind 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 Text-
Box. When it is run, the form looks like Figure 14-6.

F16km06

Figure 14-6 Setting up data binding dynamically at run time.


C1461587x.fm Page 319 Friday, November 16, 2001 9:10 AM

Chapter 14 Resolving Data Access Issues 319

Here is the relevant code in Form_Load that changes the data binding:
‘Set up connection string and recordsource for the ADODC
Me.dcEmployees.ConnectionString = _
“Provider=Microsoft.Jet.OLEDB.4.0;Data Source=“ & _
m_NorthwindDatabase
Me.dcEmployees.CommandType = adCmdTable
Me.dcEmployees.RecordSource = “Employees"
‘Set up binding for the textbox
Set Me.txtLastname.DataSource = Me.dcEmployees
Me.txtLastname.DataField = “Lastname”

After upgrading, this section of code becomes the following:


‘Set up connection string and recordsource for the ADODC
Me.dcEmployees.ConnectionString = _
“Provider=Microsoft.Jet.OLEDB.4.0;Data Source=“ & _
m_NorthwindDatabase
Me.dcEmployees.CommandType = ADODB.CommandTypeEnum.adCmdTable
Me.dcEmployees.RecordSource = “Employees”
‘Set up binding for the textbox
‘UPGRADE_ISSUE: TextBox property txtLastname.DataSource was not
‘upgraded.
Me.txtLastname.DataSource = Me.dcEmployees
‘UPGRADE_ISSUE: TextBox property txtLastname.DataField was not
‘upgraded.
Me.txtLastname.DataField = “Lastname”

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
‘UPGRADE_ISSUE: TextBox property txtLastname.DataSource was not
‘upgraded.
Me.txtLastname.DataSource = Me.dcEmployees
‘UPGRADE_ISSUE: TextBox property txtLastname.DataField was not
‘upgraded.
Me.txtLastname.DataField = “Lastname”

to the following:

ADOBind_dcEmployees.Remove(“txtLastname”)
ADOBind_dcEmployees.Add(txtLastname, “Text", “Lastname", _
Nothing, “txtLastname”)
C1461587x.fm Page 320 Friday, November 16, 2001 9:10 AM

320 Part III Getting Your Project Working

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:

Private Sub frmMain_Load(ByVal eventSender As System.Object, _


ByVal eventArgs As System.EventArgs) Handles MyBase.Load
If DoesDatabaseExist(m_NorthwindDatabase) Then
‘Set up connection string and recordsource for the ADODC
Me.dcEmployees.ConnectionString = _
“Provider=Microsoft.Jet.OLEDB.4.0;Data Source=“ & _
m_NorthwindDatabase
Me.dcEmployees.CommandType = ADODB.CommandTypeEnum.adCmdTable
Me.dcEmployees.RecordSource = “Employees”
VB6_AddADODataBinding()
Else
MsgBox(“Northwind database can’t be found at “ & _
m_NorthwindDatabase, MsgBoxStyle.Critical)
End
End If
End Sub

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.
C1461587x.fm Page 321 Friday, November 16, 2001 9:10 AM

Chapter 14 Resolving Data Access Issues 321

Conclusion

This chapter has looked at how to upgrade data access to Visual Basic .NET.
DAO, RDO, and ADO code upgrades easily. ADO data binding also upgrades
well, but Visual Basic .NET does not support DAO and RDO data binding. If
your application relies heavily on data binding, it may be useful to reimplement
the data binding in ADO.NET, since Visual Basic .NET has a rich design-time
experience for ADO.NET data binding. For more information on moving ADO
to ADO.NET, see Chapter 20.
C1461587x.fm Page 322 Friday, November 16, 2001 9:10 AM
C1561587x.fm Page 323 Friday, November 16, 2001 8:26 AM

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 mech-
anisms 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.

Replacing the OLE Container Control


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 Con-
tainer control is commonly used in three ways.

323
C1561587x.fm Page 324 Friday, November 16, 2001 8:26 AM

324 Part III Getting Your Project Working

■ 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 sub-
sequently reload it using the ReadFromFile method.
■ To create a control that contains a linked object, such as a Word doc-
ument. 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 docu-
ment, any changes you make are immediately reflected in the docu-
ment 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 hap-
pens 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!!”

F15km01

Figure 15-1 OLE Container control with linked object in Visual Basic 6.
C1561587x.fm Page 325 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 325

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:
‘UPGRADE_ISSUE: Ole method OLE1.CreateLink was not upgraded.

F15km02

Figure 15-2 OLE Container control after upgrade.

Let’s work on replacing the OLE control and getting our upgraded appli-
cation 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.
C1561587x.fm Page 326 Friday, November 16, 2001 8:26 AM

326 Part III Getting Your Project Working

F15km03

Figure 15-3 Word document displayed using the WebBrowser control.

Here are the steps required to replace an OLE control with the Web-
Browser control:

1. Remove the red label from the form.


2. Add a WebBrowser control to the form. Finding this control can be a
little confusing. For starters, it is contained in the obscurely named
Shdocvw.dll. In the Customize Toolbox component picker, you need
to select Microsoft WebBrowser Control on the COM Components
tab. In the Toolbox it is called an Explorer control, and when you
add it to your form it is called a 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 loca-
tion 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.
C1561587x.fm Page 327 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 327

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 Con-
tainer 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 loca-
tion in the following source. Here are the steps:

1. Create a new Visual Basic .NET Windows application.


2. Add a reference to ADODB.
3. Add the adoHelper helper class to the application. For the source
code of the helper class, see the Bind OLE Picture application on the
companion CD.
4. Add a PictureBox to the form.
5. In the Form1_Load event, add this code:
Dim cn As New ADODB.Connection()
Dim rs As New ADODB.Recordset()
Dim c As New adoHelper()
cn.Open(“Provider=Microsoft.Jet.OLEDB.4.0;” & _
“Data Source=C:\NWIND.MDB”)
rs.Open(“Employees", cn)
Me.PictureBox1.Image = c.rsFieldToBitmap(rs.Fields(“photo”))

That’s it! Press F5 and notice that the PictureBox retrieves the photo from
the database, as shown in Figure 15-4.
C1561587x.fm Page 328 Friday, November 16, 2001 8:26 AM

328 Part III Getting Your Project Working

F15km04

Figure 15-4 Binding a PictureBox to an OLE image in a database.

Replacing Painting Functions


Remember how, in Visual Basic 6, you could draw a circle on a form using the
Form.Circle method? Remember also how you could make the line stay on the
form by setting the Form.AutoDraw property to True? Well, things have
changed a little in Visual Basic .NET. The graphics methods Line, Circle, Print,
PaintPicture, and Cls have been removed from Form and PictureBox. Instead,
Windows Forms has access to the .NET GDI+ object library, which has a much
richer set of graphics functions. While all this is very exciting, you’ve probably
guessed that the graphics methods you’re used to are not supported in Win-
dows Forms.
For example, suppose the Click event of a Button contains the following
Visual Basic 6 code, which draws a line and a circle on the current form:
Me.Line (0, 200)-(2000, 200)
Me.Circle (300, 300), 100

The two methods cannot automatically be upgraded to Visual Basic .NET, so


after upgrading, these statements become the following:
‘UPGRADE_ISSUE: Form method Form1.Line was not upgraded
Me.Line(0, 200) To (2000, 200)
‘UPGRADE_ISSUE: Form method Form1.Circle was not upgraded
Me.Circle(300, 300, 100)
C1561587x.fm Page 329 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 329

The line and circle methods cause compile errors in Visual Basic .NET
because they are not available as methods of the Form object. Where have the
graphics methods gone? you may ask. They have been moved to the GDI+
graphics object. To use a GDI+ graphics object, you create a graphics object,
draw onto the graphics object, and then dispose of the graphics object. For
example, the following code paints a line and a circle on the form. Try it out by
putting it in a Button_Click event in a Visual Basic .NET project.
Dim g As Graphics = Me.CreateGraphics
g.DrawLine(Pens.Black, 0, 20, 100, 20)
g.DrawEllipse(Pens.Black, 24, 24, 30, 30)g.Dispose()

Figure 15-5 shows the effect of running this code.

F15km05

Figure 15-5 Drawing a line and a circle in Visual Basic .NET.

This code draws a line of length 100 and a circle with a diameter of 24.
Notice that graphics coordinates are in pixels. In Visual Basic 6, the graphics
unit was twips by default. Also notice that, as in Visual Basic 6, the origin is the
top left corner. In this example, we put the graphics code in a Click event to
ensure that the form is visible when the code is run. There is no AutoRedraw
property in Visual Basic .NET. If the window is hidden, minimized, or moved
off the screen, the line and circle you worked so hard to draw are erased. Luck-
ily, Windows Forms gives you a way to redraw your objects every time the form
is repainted. You do this using the Form.Paint event. This event is called every
time a portion of the form’s background has to be repainted. The following
code shows how to paint a line and a circle using the Paint event. Notice that
the system passes a graphics object to the event, so you don’t need to create
one yourself.
C1561587x.fm Page 330 Friday, November 16, 2001 8:26 AM

330 Part III Getting Your Project Working

Private Sub frmLineAndCircle_Paint(ByVal sender As Object, _


ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
e.Graphics.DrawLine(Pens.Black, 0, 20, 100, 20)
e.Graphics.DrawEllipse(Pens.Black, 24, 24, 30, 30)End Sub

In most applications, Form.Paint is the best place to put graphics code, since
the graphics are redrawn each time the form is repainted.
Now let’s look at something else you can do in the Paint event using
GDI+. The following Paint event makes the form background fade from black
to white:
Private Sub frmGradient_Paint(ByVal sender As Object, _ByVal e As Sys-
tem.Windows.Forms.PaintEventArgs) Handles MyBase.Paint Dim rectForm
As New Rectangle(New Point(0, 0), Me.ClientSize) Dim l As New
Drawing2D.LinearGradientBrush(rectForm, Color.Black, _
Color.White, Drawing.Drawing2D.LinearGradientMode.Horizontal)
e.Graphics.FillRectangle(l, rectForm)End Sub

Figure 15-6 shows the form with the gradient background. Notice that this form
also has a TextBox and a PictureBox on it, and that these controls haven’t been
painted with the gradient. This brings us to an important point: the graphics
object passed to the Form.Paint event paints only the form background. Each
control on the form also has a Paint event, which you can use to draw on that
particular control.

F15km06

Figure 15-6 Black to white background on a form.


C1561587x.fm Page 331 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 331

In addition to circles, lines, and gradient backgrounds, the graphics object


also has all the methods that were available in Visual Basic 6. To erase the back-
ground, use the Clear method; to paint a picture, use the PaintImage method;
and to print text onto a form, use the DrawString method. These methods are
incredibly flexible. For example, the following code draws the string “VB
Rocks” onto the background of a form, using the contents of a PictureBox as a
brush:
Dim bitmapBrush As New Drawing.TextureBrush(PictureBox1.Image)
Dim f As New Font(“Arial", 60, FontStyle.Bold, GraphicsUnit.Pixel)
e.Graphics.DrawString(“VB Rocks", f, bitmapBrush, 20, 20)

As you can see, the GDI+ methods are quite powerful.

Learn More About GDI+


What we’ve shown here just scratches the surface. GDI+ is a fully featured
set of graphics classes. You can draw paths, rectangles, icons, and pictures.
You can rotate and transform these objects, and you can use alpha blending
to fade one color into another. You will find GDI+ in the System.Drawing
namespace. To learn more, search for “Introduction to GDI+” and “Using
GDI+ Managed Classes” in the Visual Basic .NET Help system.

Rewriting Clipboard Code


Visual Basic 6 has a Clipboard object that allows you to store and retrieve text
and pictures to and from the Windows Clipboard. In Visual Basic .NET, you
manipulate the Clipboard using the System.Windows.Forms.Clipboard
namespace. The new Clipboard classes are more flexible than those in Visual
Basic 6, in that they allow you to set and retrieve data in a particular format and
query the contents of the Clipboard to see what formats it supports. Unfortu-
nately, Clipboard code cannot be upgraded automatically. However, you’ll find
it straightforward to implement the same functionality in Visual Basic .NET.
C1561587x.fm Page 332 Friday, November 16, 2001 8:26 AM

332 Part III Getting Your Project Working

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:
‘Set and retrieve text
Dim s As String
Clipboard.SetText “VB Rocks"
s = Clipboard.GetText
‘Set and retrieve a picture
Clipboard.SetData Me.sourcePictureBox.Picture
Me.destinationPictureBox.Picture = Clipboard.GetData

When this code is upgraded, the Clipboard code is left as is and is marked
with upgrade warnings:
‘UPGRADE_ISSUE: Clipboard method Clipboard.SetText was not
upgraded.Clipboard.SetText(“VB Rocks”)

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:
‘Set and retrieve text
Dim s As String
Dim stringData As IDataObject
Dim getString As New DataObject(DataFormats.StringFormat)
Clipboard.SetDataObject(“VB Rocks", True)
stringData = Clipboard.GetDataObject()
If stringData.GetDataPresent(DataFormats.Text) Then
s = stringData.GetData(DataFormats.Text, True)
End If
‘Set and retrieve a picture
Dim pictureData As IDataObject
Clipboard.SetDataObject(Me.sourcePictureBox.Image)
pictureData = Clipboard.GetDataObject
If pictureData.GetDataPresent(DataFormats.Bitmap) Then
Me.destinationPictureBox.Image = _
pictureData.GetData(DataFormats.Bitmap, True)
End If
C1561587x.fm Page 333 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 333

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.

Using the Controls Collection


To add and remove controls at run time in Visual Basic 6, you can use the con-
trols collection. For example, the following code adds a TextBox dynamically at
run time:
Dim myControl As Control
Set myControl = Me.Controls.Add(“VB.TextBox", “myControl”)
myControl.Visible = True

This code adds a TextBox to the form and makes it visible. Figure 15-7 shows
the result.

F15km07

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 fol-


lowing 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 con-
trols collection has no IntelliSense, so you have to remember the methods,
parameters, and ProgID of the control to add. There is no automatic upgrade
C1561587x.fm Page 334 Friday, November 16, 2001 8:26 AM

334 Part III Getting Your Project Working

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 con-
trol 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):
Dim c As New AxMSComctlLib.AxTreeView()
c.Name = “myTreeView”
Me.Controls.Add(c)

The control can be removed dynamically in a manner similar to removing an


intrinsic control:
C1561587x.fm Page 335 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 335

Dim c As Control
For Each c In Me.Controls
If c.Name = “myTreeView” Then
Me.Controls.Remove(c)
Exit For
End If
Next

Using the Forms Collection


The forms collection in Visual Basic 6 is a collection of all open forms in the
project. The most common uses of the forms collection are to determine
whether a form is open, to iterate through the forms collection, and to close a
form by name. The following Visual Basic 6 example shows how to iterate
through the forms collection, determine whether a form called Form1 is open,
and then unload Form1 if it is open:
Dim f As Form
For Each f In Forms
If f.Name = “Form1” Then
Unload f
MsgBox “Form unloaded"
End If
Next

The forms collection cannot be upgraded automatically, so if the previous


code is upgraded, the collection is marked with an upgrade issue:
‘UPGRADE_ISSUE: Forms collection was not upgraded.
For Each f In Forms

The only way to achieve the same effect in Visual Basic .NET is to imple-
ment your own forms collection. Luckily, this is easier than it sounds. First cre-
ate a new Visual Basic .NET Windows application. Add a new module, and in
the module put the following code (you can find this code on the companion
CD in the file FormCollectionClass.vb):
Module FormsCollection
Public Forms As New FormsCollectionClass()
End Module
(continued)
C1561587x.fm Page 336 Friday, November 16, 2001 8:26 AM

336 Part III Getting Your Project Working

Class FormsCollectionClass : Implements IEnumerable


Private c As New Collection()
Sub Add(ByVal f As Form)
c.Add(f)
End Sub
Sub Remove(ByVal f As Form)
Dim itemCount As Integer
For itemCount = 1 To c.Count
If f Is c.Item(itemCount) Then
c.Remove(itemCount)
Exit For
End If
Next
End Sub
ReadOnly Property Item(ByVal index) As Form
Get
Return c.Item(index)
End Get
End Property
Overridable Function GetEnumerator() As _
IEnumerator Implements IEnumerable.GetEnumerator
Return c.GetEnumerator
End Function
End Class

This is the collection that will maintain your collection of forms. You need
to make sure that each form is added to the collection when it is created and
removed from the collection when it is disposed of. In the New event for
Form1, insert the following line:
Forms.Add(Me)

In the Disposed event, insert this line:


Forms.Remove(Me)

That’s all you need to do to implement your forms collection. If your applica-
tion has many forms, you’ll have to put the New and Dispose code in every one.
C1561587x.fm Page 337 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 337

Let’s see how to use this collection in code. The following Visual Basic
.NET example does exactly what the first Visual Basic 6 example in this chapter
did: loop through the forms collection, determine whether a form called Form1
is open, and unload it if it is open.
Dim f As Form
For Each f In Forms
If f.Name = “Form1” Then
f.Close()
End If
Next

You can use this forms collection to unload forms, loop through the collection
of open forms, or simply check whether a particular form is open.

Do You Really Need a Forms Collection?


Before adding a forms collection to your project, you may want to ask your-
self whether you really need it. One drawback is that you need to remem-
ber to add the code to every form in your application. If you forget to add
it to a single form, that form will not be added to the collection. If you do
use the forms collection, and you use it a lot, consider adding the New and
Dispose code to the template from which every new form is created.
Some applications created with the Visual Basic Application Wizard
contain code that loops through the forms collection and closes every
form in the application. In many cases you can safely remove this code
from the project and avoid having to create your own forms collection.

Upgrading PrintForm Code


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 qual-
ity is not always picture-perfect. But for many people, it’s good enough. Unfor-
tunately, Visual Basic .NET has no direct equivalent of PrintForm, and thus the
C1561587x.fm Page 338 Friday, November 16, 2001 8:26 AM

338 Part III Getting Your Project Working

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:

1. Create a new Visual Basic .NET Windows application.


2. Add the PrintForm helper class.
3. Add a Button to the default form, and in the Button_Click event
insert the following code:
Dim c As New PrintForm()
c.Print(Me)

4. 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, espe-
cially 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:

1. Add a second form, Form2, to your application.


2. Add a second Button to Form1, and in the Button2_Click event insert
the following code:
Dim c As New PrintForm()
Dim i As Image
i = c.getImageFromForm(Me)
Dim f2 As New Form2()
f2.Show()
f2.CreateGraphics.DrawImage(i, New Point(0, 0))
C1561587x.fm Page 339 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 339

3. 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.

F15km08

Figure 15-8 Which is the real Form1?

Where Are the Print Functions?


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 pro-
grammatically from your application, search for the topic “Printing with
the PrintDocument Component” in the Visual Basic .NET Help system.
C1561587x.fm Page 340 Friday, November 16, 2001 8:26 AM

340 Part III Getting Your Project Working

Replacing Property Pages


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:
Dim m_bitmapCollection() As Bitmap
Property BitmapCollection() As Bitmap()
Get
Return m_bitmapCollection
End Get
Set(ByVal Value As Bitmap())
m_bitmapCollection = Value.Clone
End Set
End Property

If you add the control to a form, you will notice that it has a new property, bit-
mapCollection, and that you can edit this property using a collection editor. Fig-
ure 15-9 shows this editor. It’s that’s simple.
C1561587x.fm Page 341 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 341

F15km09

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:
Private m_targetDate As Date
Public Property targetDate() As Date
Get
Return m_targetDate
End Get
Set(ByVal Value As Date)
m_targetDate = Value
End Set
End Property

The Property Browser automatically uses the date editor for the property, as
shown in Figure 15-10.
C1561587x.fm Page 342 Friday, November 16, 2001 8:26 AM

342 Part III Getting Your Project Working

F15km10

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.

Writing Your Own Property Editors


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.”
C1561587x.fm Page 343 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 343

Eliminating ObjPtr, VarPtr, and StrPtr


Visual Basic 6 supports the undocumented functions ObjPtr, StrPtr, and VarPtr.
These functions return the memory address of the location where the corre-
sponding object, variable, or string is stored. This memory address, also known
as a pointer, can then be passed to Windows APIs that accept memory
addresses as parameters. Not surprisingly, these functions are rarely used, and
also not surprisingly, programmers can get into real trouble using them, since
they bypass the memory management and safety features of Visual Basic 6.
There is no automatic upgrade for ObjPtr, VarPtr, and StrPtr, but there is
a way to achieve the same functionality in Visual Basic .NET. Before continuing,
we must warn you that manipulating memory is not a good practice. You
shouldn’t need to do it; there is almost always a more appropriate workaround
that uses safer techniques.
Okay, okay, if you really want to know how to get the memory address in
Visual Basic .NET, we’ll show you. But remember, this stuff is dangerous! Let’s
look at some examples. Create a new Visual Basic .NET console application.
Our examples will use the default module. We will be using the System.Run-
time.InteropServices namespace, so at the top of the module, add the following
line of code:
Imports System.Runtime.InteropServices

Memory pointer functions are commonly used with the CopyMemory API,
so let’s add a Declare statement for the API in the declarations section of our
module:
Declare Sub CopyMemory Lib “kernel32” Alias “RtlMoveMemory” _
(ByVal lpDest As Integer, ByVal lpSource As Integer, _
ByVal cbCopy As Integer)

After adding this statement, your module should look like the following:
Imports System.Runtime.InteropServices
Module Module1
Declare Sub CopyMemory Lib “kernel32” Alias “RtlMoveMemory” _
(ByVal lpDest As Integer, ByVal lpSource As Integer, _
ByVal cbCopy As Integer)

Sub Main()

End SubEnd Module


C1561587x.fm Page 344 Friday, November 16, 2001 8:26 AM

344 Part III Getting Your Project Working

We will be working with our examples inside the Sub Main method. In
Visual Basic .NET, variables are garbage-collected and can in theory be moved
around in memory arbitrarily by the Garbage Collector. As a result, getting the
memory address of a variable poses a problem, since if the Garbage Collector
decides to move the variable in memory, the memory address we just retrieved
becomes invalid. Luckily, the Garbage Collector provides a method to “pin” the
object in memory so that it won’t be moved around. The GCHandle.Alloc
method gets a handle to the variable and pins it in memory. The handle’s
AddrOfPinnedObject method then returns the memory address. Let’s see this in
action. The following code creates an integer, pins it in memory, displays the
memory address of the variable, and then unpins the handle. (You should
always unpin the handle when you’re finished playing around with the memory
address.)
Sub Main()
Dim myInteger As Integer
Dim handle As GCHandle = _
GCHandle.Alloc(myInteger, GCHandleType.Pinned)
Dim address As Integer = handle.AddrOfPinnedObject.ToInt32
MsgBox(“The memory address of myInteger is “ & address)
handle.Free()End Sub

Let’s look at another example. This code creates two strings, gets the
memory addresses of each, and then uses the CopyMemory API to copy one
string into another:
Sub Main()
Dim src, dest As String
src = “VB Rocks”
dest = “ “
Dim srcHandle As GCHandle = _
GCHandle.Alloc(src, GCHandleType.Pinned)
Dim srcAddr As Integer = srcHandle.AddrOfPinnedObject.ToInt32
Dim destHandle As GCHandle = _
GCHandle.Alloc(dest, GCHandleType.Pinned)
Dim destAddr As Integer = destHandle.AddrOfPinnedObject.ToInt32
CopyMemory(destAddr, srcAddr, Len(src) * 2)
srcHandle.Free()
destHandle.Free()
MsgBox(dest)
End Sub
C1561587x.fm Page 345 Friday, November 16, 2001 8:26 AM

Chapter 15 Problems That Require Redesign 345

If you run this code, you’ll see that the source string is copied into the destina-
tion string, which is then shown in a message box. Something to be aware of is
that variables are stored differently in Visual Basic .NET than in Visual Basic 6.
Notice that when you use the CopyMemory API, the third parameter is the num-
ber of bytes to copy, which is double the length of the string. In Visual Basic
.NET, strings are stored as Unicode, so each character is stored in 2 bytes. Thus,
when copying a string using APIs, you have to specify a number of bytes that
is double the string length.

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 rede-
sign 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.
C1561587x.fm Page 346 Friday, November 16, 2001 8:26 AM
C1661587x.fm Page 347 Friday, November 16, 2001 8:30 AM

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 informa-
tion here applies to the upgrading of all kinds of COM+ components. From this
chapter you should take away a basic understanding of how COM+ compo-
nents are implemented in Visual Basic .NET, and you should have an idea of the
effort required to upgrade your existing components.

Note 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.

COM+ Application Types


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,

347
C1661587x.fm Page 348 Friday, November 16, 2001 8:30 AM

348 Part III Getting Your Project Working

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 pro-


cess. 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 informa-
tion 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.

Note 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 compo-
nents 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.

Using COM+ in Visual Basic .NET


Before getting into the how-to of upgrading, it’s important to take a step back
and investigate how COM+ services are implemented in .NET. For those of you
who are curious as to what has happened to COM+ in .NET, fear not. COM+ is still
the same sprightly beast that it was before you installed Visual Studio .NET. The
C1661587x.fm Page 349 Friday, November 16, 2001 8:30 AM

Chapter 16 Upgrading COM+ Components 349

.NET Framework exposes all of the existing COM+ services to Visual Basic .NET
applications. Table 16-1 lists the COM+ services available to .NET developers.

Table 16-1 COM+ Services Supported in Visual Basic .NET


Service Description
Automatic transaction processing Applies declarative transaction process-
ing features.
BYOT (bring your own transaction) Allows a form of transaction inheritance.
COM Transaction Integrator Encapsulates Customer Information Con-
(COMTI) trol System (CICS) and Internet Mail Ser-
vice (IMS) applications in Automation
objects.
Compensating Resource Manager Applies atomicity and durability proper-
ties to nontransactional resources.
Just-in-time activation Activates an object on a method call and
deactivates it when the call returns.
Loosely coupled events Manages object-based events.
Object construction Passes a persistent string value to a class
instance on construction of the instance.
Object pooling Provides a pool of ready-made objects.
Private components Protects components from out-of-process
calls.
Queued components Provides asynchronous message queuing.
Role-based security Applies security permission based on
role.
SOAP services Publishes components as XML Web
services.
Synchronization Manages concurrency.
XA interoperability Supports the X/Open transaction process-
ing model.

Despite implementation differences, underneath the .NET Framework the


stalwart heart of COM+ still beats. The .NET Framework COM+ services are built on
top of COM+, rather than being a substitute for it. These are the same services that
you are already familiar with; they are just implemented differently.
The fact that COM+ is a set of native application services (it does not run
on the common language runtime with Visual Basic .NET) raises interesting
questions: Is this COM interop, and is there a performance penalty? The short
and simple answer is no. COM+ services are able to call directly into the run-
time environment and vice versa without COM interop wrappers or variable
C1661587x.fm Page 350 Friday, November 16, 2001 8:30 AM

350 Part III Getting Your Project Working

marshaling overhead. The technical details as to why are too involved to go


into here (this is not to say that the authors don’t need to be reminded of them
periodically), but rest assured—you will not pay a performance penalty for
choosing to use COM+ services through the .NET Framework instead of
through Visual Basic 6. In fact, thanks to the new power of Visual Basic .NET,
more COM+ services are available to you than ever before, and you have more
leverage to use them. In addition, Visual Basic .NET makes implementing
COM+ applications far easier than in Visual Basic 6 and gives the developer a
significant amount of control over component deployment issues.

Note At this point, you might be wondering why COM interop doesn’t
come into play with COM+. Isn’t COM+ the same as COM? Yes and
no. COM+ was designed as an application infrastructure for develop-
ing COM components for the enterprise. While there is a high level of
integration between the two technologies, COM+ is not really COM.
This difference enables COM+ to call directly into the runtime (and
vice versa) without the overhead and marshaling associated with a
COM interop method call.

What about the differences between COM+ in Visual Basic 6 and COM+ in
.NET? While they do exist, COM+ is COM+ no matter how you look at it. On the
other hand, an ActiveX DLL is by no means a managed assembly (a Visual Basic
.NET DLL), and this has specific benefits (see the discussion of attributes later in
this chapter). Visual Basic .NET is designed to provide more integrated support
for COM+, above and beyond that found in Visual Basic 6. To help illustrate the
differences, let’s take a look at how COM+ applications are built in Visual Basic
.NET.

COM+ Requirements in Visual Basic .NET


By now you should be familiar with the namespace structure, if not the content,
of the .NET Framework. Support for COM+ services is implemented in the Sys-
tem.EnterpriseServices namespace. Any component wishing to take advantage
of COM+ (aside from adding a reference to the System.EnterpriseServices.dll
assembly) must meet all of the following requirements:

■ It must inherit from the ServicedComponent class or from another


class derived from ServicedComponent.
■ It must apply attributes specifying the COM+ services supported by
the class.
C1661587x.fm Page 351 Friday, November 16, 2001 8:30 AM

Chapter 16 Upgrading COM+ Components 351

■ The assembly containing the COM+ component must have a strong


name.

The sections that follow discuss each of these requirements.

Inheriting from the ServicedComponent Class


We have mentioned that there are differences between the implementation of
COM+ in Visual Basic 6 and its implementation in Visual Basic .NET. One of
these differences is that in Visual Basic 6 any class can implement a COM+ ser-
vice by implementing specific interfaces from the COM+ services type library.
The downside to this approach is that you have to implement a great deal of
boilerplate code yourself. In .NET, the situation is quite different. All compo-
nents that want to leverage COM+ services need to inherit their objects from the
ServicedComponent class. This class provides a framework for your custom
component that enables context sharing between COM+ and .NET Framework
classes. In addition, it provides a host of features that enable self-registration
and support for new .NET features like remoting and XML Web services.
This change means that your COM+ classes take on the following struc-
ture. (Note that this is by no means a complete implementation.)
Imports System.EnterpriseServices

‘ A trivial COM+ Class


Public Class COMPlusClass
Inherits ServicedComponent ‘ Required to support COM+

Public Sub New()


MyBase.New()
End Sub
End Class

Instead of implementing external interfaces to handle events such as


Construct, Activate, and Deactivate, you use member functions of the Serviced-
Component class. To implement specific features that you need, you override
the base class functionality. This makes your class look like the following:
Imports System.EnterpriseServices

‘ A trivial COM+ Class


Public Class COMPlusClass
Inherits ServicedComponent ‘ Required to support COM+

Public Sub New()


MyBase.New()
End Sub
(continued)
C1661587x.fm Page 352 Friday, November 16, 2001 8:30 AM

352 Part III Getting Your Project Working

Protected Overrides Sub Construct(ByVal ConstructionString _


As String)
End Sub

Protected Overrides Sub Activate()


End Sub

Protected Overrides Sub Deactivate()


End Sub
End Class

Starting to look familiar yet? It’s still not even close to being complete,
however. We have created the base framework for our COM+ class, but we
need to provide more information about what we intend it to do and what ser-
vices it supports. That is where attributes come into play.

Working with Attributes


Attributes are a new feature in Visual Basic .NET. Attributes are specialized
classes that are applied to code elements, allowing you to provide descriptive
metadata about your component. At compile time, attributes are emitted into
metadata that is available to the common language runtime or to custom tools
and applications through the System.Reflection namespace. You can explicitly
mark objects and methods with attributes that affect their behavior.
You attach an attribute to a component by preceding the component with
a reference to the attribute and providing any relevant parameters or flags. This
call to the constructor is placed within angle brackets (<>) in Visual Basic.
Whenever you create a new application in Visual Basic .NET, a file called
AssemblyInfo.vb is created. This file contains a set of common attributes for
your project. The file typically looks like this:
Imports System.Reflection
Imports System.Runtime.InteropServices

‘ General Information about an assembly is controlled through


‘ the following set of attributes. Change these attribute values to
‘ modify the information associated with an assembly.

‘ Review the values of the assembly attributes

<Assembly: AssemblyTitle(““)>
<Assembly: AssemblyDescription(““)>
<Assembly: AssemblyCompany(““)>
<Assembly: AssemblyProduct(““)>
<Assembly: AssemblyCopyright(““)>
C1661587x.fm Page 353 Friday, November 16, 2001 8:30 AM

Chapter 16 Upgrading COM+ Components 353

<Assembly: AssemblyTrademark(““)>
<Assembly: CLSCompliant(True)>

‘ The following GUID is for the ID of the typelib if this project


‘ is exposed to COM

<Assembly: Guid(“17465E40-15E9-4F52-8954-CF931AC54747”)>

‘ Version information for an assembly consists of the following


‘ four values:

‘ Major Version
‘ Minor Version
‘ Build Number
‘ Revision

‘ You can specify all the values or you can default the Build and
‘ Revision Numbers by using the ‘*’ as shown below:

<Assembly: AssemblyVersion(“1.0.*”)>

In addition to this standard set of attributes, the System.EnterpriseServices


namespace defines a host of attributes specific to COM+. These attributes pro-
vide a combination of services. Some specify feature support; others specify
your application’s default COM+ settings. Table 16-2 contains a list of COM+
specific attributes.

Table 16-2 COM+ Specific Attributes


Unconfigured
Attribute Scope Configured Default Value
Default Value
ApplicationAccessControl Assembly False True
ApplicationActivation Assembly Library No default
ApplicationID Assembly Generated GUID No default
ApplicationName Assembly Assembly name No default
ApplicationQueuing Assembly No default No default
AutoComplete Method False True
ComponentAccessControl Class False True
COMTIIntrinsics Class False True
ConstructionEnabled Class False True
Description Assembly No default No default
Class
Method
Interface
C1661587x.fm Page 354 Friday, November 16, 2001 8:30 AM

354 Part III Getting Your Project Working

Table 16-2 COM+ Specific Attributes (continued)

Unconfigured
Attribute Scope Configured Default Value
Default Value

EventClass Class No default FireInParallel = False


AllowInprocSubscribers =
True
PublisherFilter = Null
EventTrackingEnabled Class False True
ExceptionClass Class No default No default
InterfaceQueuing Class False True
Interface
JustInTimeActivation Class False True
LoadBalancingSupported Class False True
MustRunInClientContext Class False True
ObjectPooling Class False True
PrivateComponent Class No default Private
SecureMethod Assembly No default No default
Class
Method
SecurityRole Assembly No default No default
Class
Interface
Synchronization Class False Synchronization-
Option.Required
Transaction Class False Transaction-
Option.Required
TransactionIsolation-
Level.Serializable
Timeout = infinite

Let’s see how our sample COM+ class looks with some of these important
attributes:
Imports System.EnterpriseServices

<Assembly: ApplicationName(“Sample COMPlus Application”)>


<Assembly: ApplicationActivation(ActivationOption.Library)>

‘ A trivial COM+ Class with attributes


C1661587x.fm Page 355 Friday, November 16, 2001 8:30 AM

Chapter 16 Upgrading COM+ Components 355

<Transaction(TransactionOption.Required), _
ConstructionEnabled([default]:=“ This is a test”), _
ObjectPooling()> _

Public Class COMPlusClass

Inherits ServicedComponent ‘ Required to support COM+

Public Sub New()


MyBase.New()

End Sub

Protected Overrides Sub Construct(ByVal ConstructionString _


As String)
Trace.WriteLine(“Construct() : “"“ & ConstructionString & “""“)
End Sub

Protected Overrides Sub Activate()


Trace.WriteLine(“Activate()”)
End Sub

Protected Overrides Sub Deactivate()


Trace.WriteLine(“Activate()”)
End Sub

<AutoComplete()> Public Sub DoTasks()

End Sub
End Class

This example demonstrates the use of all three scopes (method, class, and
assembly) of attributes and shows how you can specify support for various
COM+ features. Some of these attributes are fairly obvious and are not too big
a leap from Visual Basic 6 (the Transaction attribute, for example). Others high-
light the ability of attributes to better control deployment. An example of the
latter is the ConstructionEnabled attribute. This particular attribute allows the
developer to specify a default construction string that will be visible (and mod-
ifiable) in the Component Services Microsoft Management Console (MMC).
C1661587x.fm Page 356 Friday, November 16, 2001 8:30 AM

356 Part III Getting Your Project Working

While other COM+ attributes provide only default settings for use during
self-registration, the AutoComplete attribute provides a fundamentally new twist
on programming with transactions. Marking a method with AutoComplete
causes SetCommit to be called automatically if the method terminates normally.
This saves the developer a fair bit of work. If an unexpected error occurs (in the
form of an unhandled exception), SetAbort is called automatically. You can still
call SetAbort on your own based on a logical violation, but it is no longer nec-
essary for you to explicitly commit or abort the transaction in every circum-
stance. This attribute reduces a great deal of repetition by requiring code only
for the extraordinary cases. The following example shows how simple imple-
menting transactions now becomes with the AutoComplete attribute:
Imports System.EnterpriseServices
<Transaction(TransactionOption.Required)>
Public Class Sample COMPlusClass

Inherits ServicedComponent

<AutoComplete()>Public Function DoTransactionTask() As Boolean

‘ Do Your stuff.
‘ Calls set complete automatically if no exception is generated

End Function
End Class

All COM+ settings can be set using attributes on a class or method, instead
of using the property pages found in Visual Basic 6. This approach makes set-
tings more explicit and less prone to accidental change.
We’ve only been able to scratch the surface of the topic of attributes here,
but hopefully this discussion has given you a decent idea of how to use them.
Attributes are used extensively throughout Visual Basic .NET to implement all
sorts of functionality—not only COM+ features but also newer features like XML
Web services.

Creating a Strong Name for Your Assembly


If you are registering a component as part of a COM+ application, you must
provide a strong name for the assembly. A strong name consists of an assem-
bly’s identity—its simple text name, version number, and culture information (if
provided)—strengthened by a public key and a digital signature generated
over the assembly. Assemblies with the same strong name are expected to be
identical.
C1661587x.fm Page 357 Friday, November 16, 2001 8:30 AM

Chapter 16 Upgrading COM+ Components 357

Generating Key-Pair Files


To sign an assembly with a strong name, you must have a public/private key
pair. This public and private cryptographic key pair is used during compilation
to create a strong-named assembly. You can create a key pair using the Strong
Name tool (Sn.exe). Key-pair files usually have an .snk extension.

Note The Sn.exe tool is provided with the .NET Framework SDK and
Visual Basic .NET, but it is not normally available from the standard
command-line prompt. To use the tool, on the Programs menu, point
to Microsoft Visual Studio .NET and then to Visual Studio .NET Tools,
and choose Visual Studio .NET Command Prompt. This command-
line tool has all of the necessary environment variables set to run all of
the Framework SDK and Visual Studio .NET tools.

To create a key pair, type the following at the command prompt:


sn –k <filename>
In this command, filename is the name of the output file containing the key
pair. The following example creates a key pair called COMPlusClass.snk.
sn –k COMPlusClass.snk
Once you create the key pair, you must put the file where Visual Basic can
find it. When signing an assembly with a strong name, Visual Basic looks for the
key file in the directory containing the Visual Studio solution. Put the key file in
the appropriate project directory, and add the following assembly attribute near
the top of the COMPlusClass.vb file:
<Assembly: AssemblyKeyFile(“COMPlusClass.snk”)>

Note Although we chose to name the key file after the class it was
created for, the key file itself has nothing specifically to do with the class.
The key file is applied at the assembly level to ensure that the applica-
tion has a unique signature. That is, after all, why the strong name fea-
ture exists. It allows for the definition of unique applications regardless
of any possible naming conflicts. This uniqueness becomes very
important when dealing with applications that have machine scope.
C1661587x.fm Page 358 Friday, November 16, 2001 8:30 AM

358 Part III Getting Your Project Working

Registering COM+ Applications


Now that you know all of the requirements for COM+ components, it’s time to
talk about how to register the components with COM+. There are two ways to
register .NET COM+ applications. The first is easier: dynamic registration. This
method requires the least amount of work on your part and will be done for
you as long as you specify the appropriate attributes in your assembly manifest.
The second option is manual registration. It requires you to do a little more
work but is not substantially more difficult than dynamic registration.

Dynamic Registration
Dynamic registration is a process whereby COM+ components are self-registered.
Other than specifying a set of assembly attributes, you need only make sure that
your COM+ application (whether it is in a separate assembly or not) resides in
your application’s bin directory. Fusion will find the assembly and automatically
register the COM+ application, using all of the attributes you have specified.

What Is Fusion?
Fusion is the mechanism that enables the common language runtime to
handle run-time assembly binding. It is responsible for finding an appro-
priate assembly that contains the objects required by your application.
This is the essential tool that enables side-by-side application deployment
and eliminates the need for registering assemblies and type libraries.
While fusion is a complex concept, all you really need to know is that it
is responsible for binding your application’s assemblies at run time and
makes it possible to eliminate DLL conflicts from Visual Basic .NET.

As an example, we have provided the COMPlusClass project on the com-


panion CD. This project contains one form and one COM+ component. Run-
ning the project brings up the main form, shown in Figure 16-1.

F16km02

Figure 16-1 COMPlusClass sample application.


C1661587x.fm Page 359 Friday, November 16, 2001 8:30 AM

Chapter 16 Upgrading COM+ Components 359

Clicking the Go button causes the COMPlusClass to be instantiated. When this


happens, the component is automatically registered in COM+. You can see this in
the Component Services MMC, which should look something like Figure 16-2.

F16km01

Figure 16-2 Installed COMPlusClass application in COM+.

It can’t get much easier than that, can it? If you check the properties of the
COM+ application in the MMC, you will see the effects of the attributes you
specified in the application itself. Replacing the application defined in COM+ is
as simple as deleting it from the Component Services console and running the
application again.

Manual Registration
Manually registering an assembly can be done for both library and server appli-
cations. You register an application manually by running the RegSvcs.exe utility
on your application’s assembly. Here is the general syntax of this command:
regsvcs [assemblyname]
This utility does the following:

1. Loads the assembly.


2. Registers the assembly.
3. Generates a type library.
4. Registers the type library.
C1661587x.fm Page 360 Friday, November 16, 2001 8:30 AM

360 Part III Getting Your Project Working

5. Installs the type library into the request application.


6. Configures the class.

You can use the RegSvcs.exe utility to customize the registration process and
also as a general maintenance utility that you use to remove COM+ applications.

Upgrading COM+ Components


Unfortunately, because a Visual Basic .NET COM+ services application has to
be implemented in quite a different manner than a Visual Basic 6 COM+ ser-
vices 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, Simple-
Class, that is marked as RequiresNewTransaction. The SimpleClass.cls code
looks like this:
Implements ObjectControl
Implements IObjectConstruct

Dim ctxt As ObjectContext


Dim connStr As String
Const m_module As String = “SimpleTransaction.SimpleClass"

Private Sub IObjectConstruct_Construct(ByVal pCtorObj As Object)


connStr = pCtorObj.ConstructString
End Sub

Private Sub ObjectControl_Activate()


Set ctxt = GetObjectContext(m_module)
End Sub

Private Function ObjectControl_CanBePooled() As Boolean


ObjectControl_CanBePooled = True
End Function

Private Sub ObjectControl_Deactivate()


End Sub
C1661587x.fm Page 361 Friday, November 16, 2001 8:30 AM

Chapter 16 Upgrading COM+ Components 361

Public Sub DoTasks()


On Error GoTo HandleError
Dim conn As Connection
Set conn = New Connection

Dim sqlCmd As String


sqlCmd = “This is a sample query"

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:
Option Strict Off
Option Explicit On
Public Class SimpleClass
Implements COMSVCSLib.ObjectControl
Implements COMSVCSLib.IObjectConstruct

Dim ctxt As COMSVCSLib.ObjectContext


Dim connStr As String

Const m_module As String = “SimpleTransaction.SimpleClass”

Private Sub IObjectConstruct_Construct(ByVal pCtorObj As Object) _


Implements COMSVCSLib.IObjectConstruct.Construct
‘ UPGRADE_WARNING: Couldn’t resolve default property of object
‘ pCtorObj.ConstructString...
connStr = pCtorObj.ConstructString
End Sub

Private Sub ObjectControl_Activate() Implements _


COMSVCSLib.ObjectControl.Activate
ctxt = COMSVCSLibAppServer_definst.GetObjectContext(m_module)
End Sub

Private Function ObjectControl_CanBePooled() As Boolean _


Implements COMSVCSLib.ObjectControl.CanBePooled
ObjectControl_CanBePooled = True
End Function

(continued)
C1661587x.fm Page 362 Friday, November 16, 2001 8:30 AM

362 Part III Getting Your Project Working

Private Sub ObjectControl_Deactivate() _


Implements COMSVCSLib.ObjectControl.Deactivate
End Sub

Public Sub DoTasks()


On Error GoTo HandleError
Dim conn As ADODB.Connection
conn = New ADODB.Connection

Dim sqlCmd As String


sqlCmd = “This is a sample query”

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 unex-
pected and potentially undesirable behavior.
Using the previous example, let’s walk through the steps necessary to get
the class working:

1. Add a reference to System.EnterpriseServices.


2. Change the class to inherit from EnterpriseServices.ServicedCompo-
nent, and remove all COMSVCS Implements statements.
3. Add the necessary COM+ attributes to the class (Transaction, Con-
strutionEnabled, ObjectPooling, and so on).
4. Change the COM+ interface methods Activate, Construct, and
Deactivate to be member methods that override base methods on
ServicedComponent.
5. Add the AutoComplete attribute to the DoTasks method. Remove the
explicit commit and abort code.
C1661587x.fm Page 363 Friday, November 16, 2001 8:30 AM

Chapter 16 Upgrading COM+ Components 363

After you’ve completed all of these steps, the class looks like this:
Option Strict Off
Option Explicit On

Imports System
Imports System.EnterpriseServices

<Transaction(TransactionOption.RequiresNew), ConstructionEnabled(),
ObjectPooling()> Class SimpleClass
Inherits ServicedComponent

Dim connStr As String


Const m_module As String = “SimpleTransaction.SimpleClass”

Protected Overrides Sub Construct(ByVal constructionString _


As String)
connStr = constructionString
End Sub

<AutoComplete()> Public Sub DoTasks(ByVal fail As Boolean)


Dim conn As ADODB.Connection
conn = New ADODB.Connection()

Dim sqlCmd As String

sqlCmd = “This is a sample query”

conn.Open(connStr)
conn.Execute(sqlCmd)

If fail Then ContextUtil.SetAbort()


End Sub
End Class

Notice that the DoTasks method does not explicitly commit or abort the trans-
action 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.
C1661587x.fm Page 364 Friday, November 16, 2001 8:30 AM

364 Part III Getting Your Project Working

Making .NET and COM Components Work Together


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 applica-
tion has a ServicedComponent class that calls into the imported COM+ applica-
tion, 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:

■ Avoid using parameterized constructors.


■ Avoid using static methods.
■ Define event-source interfaces in managed code.
■ Include HRESULTs in user-defined exceptions.
■ Supply GUIDs for types that require them.
■ Expect inheritance differences.

Conclusion

Upgrading your COM+ application to Visual Basic .NET is not a totally auto-
matic process. The Upgrade Wizard does the hard work, upgrading your appli-
cation structure and business logic, but legwork on your part is necessary to
add the ServicedComponent Inherits statement, the COM+ attributes, and calls
to the SetComplete and SetAbort methods.
We’ve covered how to upgrade COM+ (and Microsoft Transaction Server)
transactional components; however, there is a lot more to COM+ services than
simply transactions. So in a way, we are just scratching the surface of what is
possible. To learn more about using COM+ services, see the topics “Serviced
Component Overview,” “Writing Serviced Components,” and “Summary of Ser-
vices” in the Visual Basic .NET Help.
C1761587x.fm Page 365 Friday, November 16, 2001 9:22 AM

Upgrading VB Application
Wizard Projects
This chapter looks specifically at upgrading projects created with the VB Appli-
cation 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.

365
C1761587x.fm Page 366 Friday, November 16, 2001 9:22 AM

366 Part III Getting Your Project Working

F17km01

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


Project Item Description
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.
frmMain form Each project created with the VB Application Wizard has a
form called frmMain. This is the primary form of the appli-
cation.
frmSplash form frmSplash is a splash screen that shows after the frmLogin
form but before frmMain opens.
frmBrowser An application for which you specify Internet connectivity
has a frmBrowser form. This form contains a WebBrowser
ActiveX control.
C1761587x.fm Page 367 Friday, November 16, 2001 9:22 AM

Chapter 17 Upgrading VB Application Wizard Projects 367

Table 17-1 Project Items Created by the VB Application Wizard


Project Item Description

Data forms Data forms are added to display and edit information from
a database.
Module1 module Each project created with the VB Application Wizard has a
module called Module1. This module has a Sub Main
method, the startup object that opens the frmMain form.
frmDocument form Each MDI interface project has a form called frmDocu-
ment; this is the MDI child form.
frmOptions form frmOptions is a tabbed dialog form for setting application
options. Because this form upgrades without 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 work-
ing 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
C1761587x.fm Page 368 Friday, November 16, 2001 9:22 AM

368 Part III Getting Your Project Working

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

lblVersion.Text = “Version “ & _


System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMajorPart _
& “.” & System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMinorPart & “.” & App.Revision

There are two ways to fix the compile error. The first and easiest is simply
to remove the App.Revision part of the line:

& “.” & App.Revision

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:

lblVersion.Text = “Version “ & System.Diagnostics.FileVersionInfo.Get-


VersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMajorPart _
& “.” & System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMinorPart

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:

lblVersion.Text = “Version “ & Application.ProductVersion

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.
C1761587x.fm Page 369 Friday, November 16, 2001 9:22 AM

Chapter 17 Upgrading VB Application Wizard Projects 369

What’s Up with Versioning in Visual Basic .NET?


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 proper-
ties. 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.

frmAbout Form
The VB Application Wizard adds the frmAbout form to your project when you
choose to add an About box standard form to the application. The About box
opens when the user chooses About from the Help menu. It shows the version
information for the application and has a command button that opens the Sys-
tem Information application. There is one compile error that you need to fix
after upgrading. It is in the Form_Load event, and it occurs because the wizard
uses the App.Revision property to generate the version information shown in
the box. As we discussed in the previous section, you should replace the code

lblVersion.Text = “Version “ & _


System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMajorPart _
& “.” & System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMinorPart & “.” & App.Revision

with the following:

lblVersion.Text = “Version “ & Application.ProductVersion

In the StartSysInfo procedure, you’ll also find a warning that Dir has a
new behavior. This behavior difference applies to the Dir method when it is
used to return a list of directories. In this case, since Dir is being used to deter-
mine whether the file Msinfo32.exe exists, the warning can be ignored, since it
applies only to directories.
C1761587x.fm Page 370 Friday, November 16, 2001 9:22 AM

370 Part III Getting Your Project Working

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.

API Declare Statements


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:
Private Declare Function SendMessage Lib “user32” _
Alias “SendMessageA” _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
ByVal lParam As Any) As Long

Private Declare Function OSWinHelp% Lib “user32” Alias “WinHelpA” _


(ByVal hwnd&, ByVal HelpFile$, ByVal wCommand%, dwData As Any)

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:

Private Declare Function SendMessage Lib “user32” Alias _


“SendMessageA"(ByVal hwnd As Integer, ByVal wMsg As Integer, _
ByVal wParam As Integer, ByVal lParam As Any) As Integer

Private Declare Function OSWinHelp Lib “user32” Alias “WinHelpA"( _


ByVal hwnd As Integer, ByVal HelpFile As String, _
ByVal wCommand As Short, ByRef dwData As Any) As Short

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 OSWin-
Help API, the declare statement for the API looks like this:

Private Declare Function OSWinHelp Lib “user32” Alias “WinHelpA” ( _


ByVal hwnd As Integer, ByVal HelpFile As String, _
ByVal wCommand As Short, ByRef dwData As String) As Short
C1761587x.fm Page 371 Friday, November 16, 2001 9:22 AM

Chapter 17 Upgrading VB Application Wizard Projects 371

Note Although not discussed here, the correct Visual Basic .NET
declaration for SendMessage is:
Private Declare Function SendMessage Lib “user32” Alias _
“SendMessageA” (ByVal hwnd As Integer, _
ByVal wMsg As Integer, _
ByVal wParam As Integer, ByVal lParam As Integer) _
As Integer

mnuHelpAbout_Click Event Procedure


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 gen-
erate the application version. It upgrades to

MsgBox(“Version “ & _
System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMajorPart _
& “.” & System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMinorPart & “.” & App.Revision)

As we discussed in the section on App.Revision, you should replace this line


with the following:

MsgBox(“Version “ & Application.ProductVersion)

App.HelpFile
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.
C1761587x.fm Page 372 Friday, November 16, 2001 9:22 AM

372 Part III Getting Your Project Working

For example, in the mnuHelpSearchForHelpOn_Click event handler, the


line
nRet = OSWinHelp(Me.hwnd, App.HelpFile, 261, 0)

is upgraded to

nRet = OSWinHelp(Me.Handle.ToInt32, App.HelpFile, 261, 0)

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:

nRet = OSWinHelp(Me.Handle.ToInt32, “c:\MyProject.hlp", 261, 0)

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:

nRet = OSWinHelp(Me.Handle.ToInt32, “", 261, 0)

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.

More Info 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 con-
trols 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
C1761587x.fm Page 373 Friday, November 16, 2001 9:22 AM

Chapter 17 Upgrading VB Application Wizard Projects 373

form is determined only at run time; therefore, it is common to use the Active-
Form 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:
Select Case Button.Key
Case “Align Right"
ActiveForm.rtfText.SelAlignment = rtfRight

For MDI applications, the Visual Basic .NET equivalent of Visual Basic 6’s
ActiveForm is ActiveMdiForm. The code just shown upgrades to the following:

Select Case eventArgs.button.Key


Case “Align Right”
‘UPGRADE_WARNING: Control rtfText could not be resolved because
‘it was within the generic namespace ActiveMdiChild.
ActiveMdiChild.rtfText.SelAlignment = _
RichTextLib.SelAlignmentConstants.rtfRight

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.
First of all, add a property called GetActiveMdiChild to the form:

ReadOnly Property GetActiveMdiChild() As frmDocument


Get
Return Me.ActiveMdiChild
End Get
End Property

Then change all occurrences of ActiveMdiChild to use this property:

Select Case eventArgs.button.Key


Case “Align Right”
GetActiveMdiChild.rtfText.SelAlignment = _
RichTextLib.SelAlignmentConstants.rtfRight
C1761587x.fm Page 374 Friday, November 16, 2001 9:22 AM

374 Part III Getting Your Project Working

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 “GetActiveMdi-
Child”. 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:

ReadOnly Property GetActiveMdiChild() As Object


Get
Return Me.ActiveMdiChild
End Get
End Property

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.

Forms Collection in frmMain_Closed


In the Form_Unload event of SDI and Explorer applications, the VB Application
Wizard generates code that unloads each open form:
‘Close all sub forms
For i = Forms.Count - 1 To 1 Step -1
Unload Forms(i)
Next

This code upgrades to the following. Not shown here is that Form_Unload is
renamed frmMain_Closed during the upgrade.

‘Close all sub forms


‘UPGRADE_ISSUE: Forms collection was not upgraded.
‘UPGRADE_WARNING: Couldn’t resolve default property of object
Forms.Count.
For i = Forms.Count - 1 To 1 Step -1
‘UPGRADE_ISSUE: Forms collection was not upgraded.
‘UPGRADE_ISSUE: Unload Forms(i) was not upgraded.
Unload(Forms(i))
Next
C1761587x.fm Page 375 Friday, November 16, 2001 9:22 AM

Chapter 17 Upgrading VB Application Wizard Projects 375

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.

Clipboard in MDI Projects


MDI applications have three event procedures for the Clipboard object: Cut,
Copy, and Paste. They are created as follows:
Private Sub mnuEditCut_Click()
On Error Resume Next
Clipboard.SetText ActiveForm.rtfText.SelRTF
ActiveForm.rtfText.SelText = vbNullString
End Sub

Private Sub mnuEditCopy_Click()


On Error Resume Next
Clipboard.SetText ActiveForm.rtfText.SelRTF
End Sub

Private Sub mnuEditPaste_Click()


On Error Resume Next
ActiveForm.rtfText.SelRTF = Clipboard.GetText
End Sub

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).

‘UPGRADE_ISSUE: Clipboard method Clipboard.SetText was not upgraded.


Clipboard.SetText(ActiveMdiChild.rtfText.SelRTF)
‘UPGRADE_ISSUE: Clipboard method Clipboard.GetText was not upgraded.
ActiveMdiChild.rtfText.SelRTF = Clipboard.GetText

You can fix the problem by adding two helper methods to Module1.
These helper methods get and set the Clipboard text:

Function ClipboardSetText(ByVal newText As String)


Try
Clipboard.SetDataObject(newText, True)
Catch ex As Exception
MsgBox(“Exception setting clipboard text: “ & ex.Message)
End Try
End Function
(continued)
C1761587x.fm Page 376 Friday, November 16, 2001 9:22 AM

376 Part III Getting Your Project Working

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.

frmSplash Form
The form frmSplash is a splash screen. It is added to the project if you specify
a splash screen at application startup in the VB Application Wizard. After
being upgraded, this form has two problems. First, as the frmAbout form
does, frmSplash inserts the application version into a label. As we discussed
in the earlier section on App.Revision, you should replace the version code in
Form_Load. The following Visual Basic 6 code
lblVersion.Caption = “Version “ & App.Major & “.” & App.Minor & “.” _
& App.Revision

upgrades to

lblVersion.Text = “Version “ & _


System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMajorPart _
& “.” & System.Diagnostics.FileVersionInfo.GetVersionInfo( _
System.Reflection.Assembly.GetExecutingAssembly.Location _
).FileMinorPart & “.” & App.Revision
C1761587x.fm Page 377 Friday, November 16, 2001 9:22 AM

Chapter 17 Upgrading VB Application Wizard Projects 377

Replace this code with the following:

lblVersion.Text = “Version “ & Application.ProductVersion

The second problem is something new. Applications with a splash screen


perform a series of steps in Sub Main to

1. Show the frmSplash form.


2. Refresh the frmSplash form (forcing the form to be fully painted).
3. Load the frmMain form.
4. Unload the frmSplash form.
5. And finally, show the frmMain form.

Here is the code that performs this series of steps:


Sub Main()
frmSplash.Show
frmSplash.Refresh
Set fMainForm = New frmMain
Load fMainForm
Unload frmSplash
fMainForm.Show
End Sub

After upgrading, this code looks like the following:

Public Sub Main()


frmSplash.DefInstance.Show()
frmSplash.DefInstance.Refresh()
fMainForm.DefInstance = New frmMain
‘UPGRADE_ISSUE: Load statement is not supported.
Load(fMainForm)
frmSplash.DefInstance.Close()
System.Windows.Forms.Application.Run(fMainForm.DefInstance)
End Sub

This code causes a compile error because the Load statement is not supported
in Visual Basic .NET. To fix the error, remove the Load(fMainForm) statement;
the application will work normally. The updated Sub Main event will look like
this:

Public Sub Main()


frmSplash.DefInstance.Show()
frmSplash.DefInstance.Refresh()
fMainForm.DefInstance = New frmMain
frmSplash.DefInstance.Close()
System.Windows.Forms.Application.Run(fMainForm.DefInstance)

End Sub
C1761587x.fm Page 378 Friday, November 16, 2001 9:22 AM

378 Part III Getting Your Project Working

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:
Private Sub mnuViewWebBrowser_Click()
Dim frmB As New frmBrowser
frmB.StartingAddress = “http://www.microsoft.com"
frmB.Show
End Sub

This code upgrades to the following:

Public Sub mnuViewWebBrowser_Click(ByVal eventSender As _


System.Object, _
ByVal eventArgs As System.EventArgs) _
Handles mnuViewWeb Browser.Click
Dim frmB As frmBrowser = New frmBrowser
frmB.StartingAddress = “http://www.microsoft.com”
frmB.Show()
End Sub

If this is an MDI project, you need to adjust the behavior of the frm-
Browser 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 prop-
erty. Data binding by using a data class cannot be upgraded for two reasons:
C1761587x.fm Page 379 Friday, November 16, 2001 9:22 AM

Chapter 17 Upgrading VB Application Wizard Projects 379

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.

Module1 Module
The VB Application Wizard adds a module called Module1 to your project.
This module contains the Sub Main startup method and will also contain the
LoadResStrings method if you chose to store the project’s strings in a resource file.

LoadResStrings Method
If you choose Yes when the VB Application Wizard asks if you would like to
use a resource file for the strings in your application, the wizard stores strings
(such as text for menu items) in a resource file. The wizard adds a method
named LoadResStrings to the application to load the strings at run time. This
procedure loads the strings for menu captions, ToolTips, control captions, and
fonts. After the upgrade, this procedure causes 19 compile errors, mainly
because it uses soft binding. The Upgrade Wizard adds an EWI to the method,
advising you to replace the function with the following code supplied in Help:

Sub LoadResStrings(ByRef frm As System.Windows.Forms.Form)


On Error Resume Next
Dim ctl As System.Windows.Forms.Control
Dim obj As Object
Dim fnt As System.Drawing.Font
Dim sCtlType As String
Dim nVal As Short

‘ Set the form’s caption.


frm.Text = VB6.LoadResString(CShort(frm.Tag))

‘ Set the font.


Dim FontName As String, FontSize As Double
FontName = VB6.LoadResString(20)
FontSize = CShort(VB6.LoadResString(21))
frm.Font = New System.Drawing.Font(FontName, FontSize)

‘ Set the controls’ captions using the Caption


‘ property for menu items and the Tag property
‘ for all other controls.
For Each ctl In frm.Controls
(continued)
C1761587x.fm Page 380 Friday, November 16, 2001 9:22 AM

380 Part III Getting Your Project Working

ctl.Font = fnt
sCtlType = TypeName(ctl)
If sCtlType = “Label” Then
ctl.Text = VB6.LoadResString(CShort(ctl.Tag))
ElseIf sCtlType = “AxTabStrip” Then
For Each obj In CObj(ctl).Tabs
obj.Caption = VB6.LoadResString(CShort(obj.Tag))
obj.ToolTipText = VB6.LoadResString _
(CShort(obj.ToolTipText))
Next obj
ElseIf sCtlType = “AxToolbar” Then
For Each obj In CObj(ctl).Buttons
obj.ToolTipText = VB6.LoadResString _
(CShort(obj.ToolTipText))
Next obj
ElseIf sCtlType = “AxListView” Then
For Each obj In CObj(ctl).ColumnHeaders
obj.Text = VB6.LoadResString(CShort(obj.Tag))
Next obj
Else
nVal = 0
nVal = Val(ctl.Tag)
If nVal > 0 Then ctl.Text = VB6.LoadResString(nVal)
nVal = 0
nVal = Val(CObj(frm).ToolTip1.GetToolTip(ctl))
If nVal > 0 Then
CObj(frm).ToolTip1.SetToolTip(ctl, VB6.Load-
ResString(nVal))
End If
End If
Next ctl

Dim mnu As System.Windows.Forms.MainMenu = CObj(frm).MainMenu1


If Not mnu Is Nothing Then
LoadMenuResStrings(mnu)
End If
End Sub

Public Sub LoadMenuResStrings(ByVal mnu As System.Windows.Forms.Menu)


Dim mnuItem As System.Windows.Forms.MenuItem
For Each mnuItem In mnu.MenuItems
On Error Resume Next
mnuItem.Text = VB6.LoadResString(CInt(mnuItem.Text))
On Error Goto 0
If mnuItem.MenuItems.Count > 0 Then
LoadMenuResStrings(mnuItem)
End If
Next
End Sub
C1761587x.fm Page 381 Friday, November 16, 2001 9:22 AM

Chapter 17 Upgrading VB Application Wizard Projects 381

This code is a replacement for the standard LoadResStrings method generated


by the VB Application Wizard. If you haven’t modified the method, simply
copy and paste this procedure into your application, replacing the existing
LoadResStrings method.

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.
C1761587x.fm Page 382 Friday, November 16, 2001 9:22 AM
C1861587x.fm Page 383 Friday, November 16, 2001 8:59 AM

Part IV

Techniques for Adding


Value
18 Adding Value to Your Applications 385
19 Replacing ActiveX Controls with Windows Forms Controls 403
20 Moving from ADO to ADO.NET 417
21 Upgrading Distributed Applications 435
C1861587x.fm Page 384 Friday, November 16, 2001 8:59 AM
C1861587x.fm Page 385 Friday, November 16, 2001 8:59 AM

Adding Value to Your


Applications
Parts I, II, and III of this book discussed how to prepare applications for
upgrading, introduced the Microsoft Visual Basic .NET upgrade technologies,
and looked at how to fix problems in your upgraded application. The final four
chapters of this book depart from the subject of upgrading and instead look at
how you can continue developing your application in Visual Basic .NET and
how you can add value by incorporating new features of Visual Basic .NET into
your application.
In this chapter, we’ll demonstrate a number of new features that you may
want to add to your upgraded applications. You’ll see how to load objects
dynamically from other DLLs, create rich graphics that were out of the reach of
Visual Basic 6 programming, access the registry with less effort than ever
before, and work with the powerful new Microsoft .NET file classes.
Note that the features we discuss in this chapter are all optional additions
to your projects. We introduce some new ways of doing familiar things, plus
some techniques that were not possible in Visual Basic 6. This chapter does not
provide an exhaustive list of new capabilities in Visual Basic .NET; instead, it
simply describes some cool features you can add to upgraded projects.
Throughout this chapter we’ll refer to the sample application Dynamic-
App.sln, located on the companion CD.

385
C1861587x.fm Page 386 Friday, November 16, 2001 8:59 AM

386 Part IV Techniques for Adding Value

Overview of the Sample Application


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.

F18km01

Figure 18-1 Dashboard opens the registry editing form and the snappy
graphics form.
C1861587x.fm Page 387 Friday, November 16, 2001 8:59 AM

Chapter 18 Adding Value to Your Applications 387

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 Win-
dows 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.

New File Functions


As we mentioned before, the Dashboard form handles three tasks of interest:

■ It obtains a list of DLLs in a directory.


■ It examines each of the DLLs for Windows forms.
■ It dynamically loads and shows the form.

Let’s see how it performs each of these actions.

Reading the Contents of a Directory


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 directo-
ries 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:

Dim filePathArray() As String


filePathArray = System.IO.Directory.GetFiles(myDirectory(), “*.dll”)

After this code runs, the filePathArray variable is filled with an array of file
paths, such as

C:\DynamicShell\bin\GraphicsFeatures.dll
C1861587x.fm Page 388 Friday, November 16, 2001 8:59 AM

388 Part IV Techniques for Adding Value

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 Sys-


tem.IO.Path.GetFileName method returns the filename of a file path. For exam-
ple, the code

MsgBox(System.IO.Path.GetFileName( _
“C:\DynamicShell\bin\GraphicsFeatures.dll”))

shows GraphicsFeatures.dll in the message box. Another useful function is


System.Reflection.Assembly.GetExecutingAssembly. This last method returns
the file path of the application. DynamicApp passes the file path to
System.IO.Path.GetDirectoryName to get the application’s directory, which is
how it knows where to look for DLLs.

Finding All the Forms in a 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 Dash-
board.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:

Private Sub Form1_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
Dim asm As Reflection.Assembly
Dim typeArray() As Type
Dim typeCounter As Integer
asm = asm.LoadFrom( _
System.Reflection.Assembly.GetExecutingAssembly.Location)
typeArray = asm.GetTypes()
For typeCounter = 0 To typeArray.Length - 1
MsgBox(typeArray(typeCounter).Name)
Next
End Sub
C1861587x.fm Page 389 Friday, November 16, 2001 8:59 AM

Chapter 18 Adding Value to Your Applications 389

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.

Loading Forms Dynamically


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.

Dim asm As Reflection.Assembly


Dim f As Form
asm = asm.LoadFrom(“MyDll.dll”)
f = CType(asm.CreateInstance(“MyDll.Form1”), Form)
f.Show()

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.

Reading and Writing to Files


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
C1861587x.fm Page 390 Friday, November 16, 2001 8:59 AM

390 Part IV Techniques for Adding Value

message box. This sample is on the companion CD, and it is called FileReadAnd-
Write.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()

‘* Open a file and read from it


Dim inFile As System.IO.FileStream
inFile = New System.IO.FileStream(“C:\tempFile.txt", _
IO.FileMode.Open, IO.FileAccess.Read)
Dim fileReader As New System.IO.StreamReader(inFile)
While fileReader.Peek > -1
MsgBox(fileReader.ReadLine)
End While
fileReader.Close()
inFile.Close()
End Sub

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 meth-
ods. 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.

Using Dynamic Properties


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.
C1861587x.fm Page 391 Friday, November 16, 2001 8:59 AM

Chapter 18 Adding Value to Your Applications 391

F18km02

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:

<add key="Dashboard.Text” value="DynamicApp” />

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 Note-
pad and change the setting to something like

<add key="Dashboard.Text” value="Hello World” />

“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 appli-
cation 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 partic-
ular property dynamic, you use the (Dynamic Properties) property in the Prop-
erty Browser; you use the (Advanced) builder to select the properties that you
want to make dynamic, as shown in Figure 18-3.
C1861587x.fm Page 392 Friday, November 16, 2001 8:59 AM

392 Part IV Techniques for Adding Value

F18km03

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.

New Windows Capabilities


Now let’s look at what each of the subforms in the sample application does.

Accessing the Registry


Accessing the registry is awkward in Visual Basic 6. You have to use APIs like
RegCreateKey and RegDeleteKey, which are cumbersome. The .NET Frame-
work provides a much easier mechanism for accessing the registry. The registry
editing form in the RegistryFeatures project (included on the companion CD)
shows how to use the .NET Framework classes to create and delete keys and
values. Figure 18-4 shows the registry editing form.
Clicking the Create button in the MyRegistryKey Key group box creates
the registry key HKEY_CURRENT_USER\Software\MyRegistryKey. Clicking the
Delete button deletes this key. Clicking the Create button in the MyRegis-
tryKey\MyValue Value box adds a new string value to the key called
MyValue. Clicking the Delete button deletes the value. The contents of the
HKEY_CURRENT_USER\Software key are shown in the tree view on the right
of the form.
C1861587x.fm Page 393 Friday, November 16, 2001 8:59 AM

Chapter 18 Adding Value to Your Applications 393

F18km04

Figure 18-4 Registry editing form.

The following code shows how the form adds the registry key. First it tries
to open the registry key, and if the registry key doesn’t already exist, the code
creates it.

Dim rk As Microsoft.Win32.RegistryKey
rk = Registry.CurrentUser.OpenSubKey(“Software\MyRegistryKey", True)
If rk Is Nothing Then
Registry.CurrentUser.CreateSubKey(“Software\MyRegistryKey”)
Else
rk.Close()
End If

To create a registry key, we first create a Microsoft.Win32.RegistryKey vari-


able called rk. We then use rk to attempt to open a registry key within Current-
User. The CurrentUser property maps to the HKEY_CURRENT_USER registry
hive. If the registry key doesn’t exist, it is created using the CreateSubKey
method.
Deleting a key is even simpler. It involves only one line of code.

Registry.CurrentUser.DeleteSubKeyTree(“Software\MyRegistryKey”)

This line deletes the MyRegistryKey registry key and any registry subkeys and
values it may have.
To add a value, we open the registry key and use the SetValue method. In
the following example, we add a string value “MyValue” with the contents of
the newValue.Text TextBox.
C1861587x.fm Page 394 Friday, November 16, 2001 8:59 AM

394 Part IV Techniques for Adding Value

Dim rk As Microsoft.Win32.RegistryKey
rk = Registry.CurrentUser.OpenSubKey(“Software\MyRegistryKey", True)
rk.SetValue(“MyValue", CStr(Me.newValue.Text))
rk.Close()

If the value already exists, it is overwritten with this new value. To delete
the value, use the DeleteValue method, as in the following example:

Dim rk As Microsoft.Win32.RegistryKey
rk = Registry.CurrentUser.OpenSubKey(“Software\MyRegistryKey", True)
rk.DeleteValue(“MyValue”)
rk.Close()

Notice in all these examples that each time we open a registry key, we
also close it using the Close method. The registry editing form acts upon the
HKEY_CURRENT_USER hive. In addition, you can access the
HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT, HKEY_USERS, and
HKEY_CURRENT_CONFIG registry hives using the Registry object’s LocalMa-
chine, ClassesRoot, Users, and CurrentConfig properties. This is certainly much
easier than working with the Visual Basic 6 registry APIs.

Control Anchoring
The registry editing form also demonstrates automatic resizing using control
anchoring. When you resize the form, the tree view also resizes—without your
writing a single line of resize code! Using the Anchor property of the TreeView
control, you can bind the edges of the control to the edges of the form. The
TreeView control is anchored to each side of the form so that when the form
resizes, the control also resizes, keeping the top, bottom, left, and right edges
the same distance from the edges of the form. This method is much easier than
in Visual Basic 6, where you have to write lines and lines of code in the
Form_Resize event to adjust the size of the control.

Graphics Features
If you enjoy adding some “sharpness” to your applications with graphics, you’ll
be pleased to know that the .NET Framework has a set of dedicated graphics
classes called GDI+, making it easier to add graphics than in any previous ver-
sion of Visual Basic. We discussed creating gradient backgrounds and drawing
shapes in Chapter 15; now we’ll look at three other features: opacity, regions,
and alpha blending. These features are all demonstrated in the Graphics Effects
form in the Graphics Features project on the companion CD. This form has
three check boxes and a button. Each one applies a different graphics effect.
The first check box is Form Opacity. Setting this option makes the form
semitransparent, as shown in Figure 18-5.
C1861587x.fm Page 395 Friday, November 16, 2001 8:59 AM

Chapter 18 Adding Value to Your Applications 395

F18km05

Figure 18-5 Semitransparent form.

The opacity property is a measure of opaqueness. It can take any value


between 0 and 1. Setting the property to 1 makes the form solid, setting it to 0
makes the form invisible, and setting it to 0.5 makes the form semitransparent.
A fun use of this property is to fade a form from invisible to fully solid. Click the
Fade In button on the Graphics Effects Form to see a demonstration of this.
Here is the code that fades the form from invisible to solid:

Private Sub FadeInButton_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles FadeInButton.Click
Dim d As Double
For d = 0 To 1 Step 0.01
Me.Opacity = d
Me.Refresh()
System.Threading.Thread.CurrentThread.Sleep(5)
Next
Me.HasOpacity.Checked = False
End Sub

The For…Next loop increments the variable d from 0 to 1. The code then
sets the opacity to the value of d: 0.00, 0.01, 0.02, 0.03, and so on. The Refresh
method forces the form to completely repaint, and the System.Thread-
ing.Thread.CurrentThread.Sleep method waits 5 milliseconds before continu-
ing to fade the form in. While opacity may not be instrumental in solving any
real-world business problems, it’s a fun effect to add.
C1861587x.fm Page 396 Friday, November 16, 2001 8:59 AM

396 Part IV Techniques for Adding Value

The second graphics feature this form demonstrates is regions. Setting the
Donut Shape option changes the form from rectangular to a doughnut shape, as
shown in Figure 18-6.

F18km06

Figure 18-6 Form displayed using the Donut Shape option.

To change the region of a form, create a region from a set of graphics


paths and apply this to the form’s Region property. The following code shows
how to do this:

Dim myPath As New Drawing2D.GraphicsPath()


Dim bigDiameter As Integer = Me.ClientSize.Height
Dim donutWidth As Integer = 80
myPath.AddEllipse(0, 0, bigDiameter, bigDiameter)
myPath.AddEllipse(donutWidth, donutWidth, _
bigDiameter - (donutWidth * 2), _
bigDiameter - (donutWidth * 2))
Me.Region = New Region(myPath)

This code creates a GraphicsPath variable myPath. This variable is used to store
one or more vector shapes. The sample above adds two ellipses to the variable.
The first ellipsis is the outer ring of the doughnut. The second is the inner ring,
the hole of the doughnut. Finally, the Region property of the form is set to the
newly created path. This shape is a true doughnut—if you click in the middle
of the hole, the mouse clicks are sent to the window behind the doughnut form.
This technique is very flexible. The number of different graphics paths and differ-
ent shapes you can create are limited only by your imagination. For example, the
following code changes the form’s shape to the string “VB.NET Rocks.”

Dim myPath As New Drawing2D.GraphicsPath()


myPath.AddString(“VB.NET Rocks", Me.Font.FontFamily, Font.Bold, _
80, New Point(0, 0), StringFormat.GenericDefault)
Me.Region = New Region(myPath)
C1861587x.fm Page 397 Friday, November 16, 2001 8:59 AM

Chapter 18 Adding Value to Your Applications 397

The third graphics feature this form demonstrates is alpha blending. This
technique allows you to paint text or pictures semitransparently. Figure 18-7
shows the effect of setting the Alpha Blending option on the Graphics Effects
form.

F18km07

Figure 18-7 Alpha blending.

Setting the check box enables a timer on the form. In the Timer.Tick
event, two strings are painted onto the form: a blue VB.NET is painted first, and
a red Rocks!! is painted over the top. Each is painted with a different alpha
value that sets the transparency. The alpha value cycles from 0 to 256 and back
to 0 each time the Timer.Tick event fires. The net effect is that the text on the
form fades from VB.NET to Rocks!! to VB.NET again. Here is the code from the
Timer.Tick event that does the alpha blending:
Static alpha As Integer = 0, upDown As Integer = 2
Dim g As Graphics
Dim f As New Font(“Arial Black", 60, FontStyle.Regular, _
GraphicsUnit.Pixel, 0)
Dim tempBitmap As New Bitmap(300, 100)
alpha += upDown
If alpha <= 0 Then
upDown = -upDown
alpha = 0
ElseIf alpha >= 255 Then
upDown = -upDown
alpha = 255
End If
g = Graphics.FromImage(tempBitmap)
g.Clear(Me.BackColor)
g.DrawString(“VB.NET", f, New SolidBrush(Color.FromArgb(255 - alpha, _
Color.DarkBlue)), New RectangleF(0, 0, 300, 100))
(continued)
C1861587x.fm Page 398 Friday, November 16, 2001 8:59 AM

398 Part IV Techniques for Adding Value

g.DrawString(“Rocks!!", f, New SolidBrush(Color.FromArgb(alpha, _


Color.Red)), 0, 0)
g.Dispose()
g = Me.CreateGraphics
g.DrawImage(tempBitmap, 200, 10)
g.Dispose()
tempBitmap.Dispose()
f.Dispose()

The alpha and upDown variables are static—they retain their old value
each time the event is called. Each time the event is called, alpha increases by
2 until it reaches 256, and then it decreases by 2 until it reaches 0 and starts all
over again. A temporary bitmap called tempBitmap is created for drawing the
string. The reason for drawing to a temporary bitmap first is to avoid screen
flicker. The two strings are drawn with the opposite alpha values—when
VB.NET is drawn as solid, Rocks!! is drawn as transparent. Valid alpha values
range from 0 (transparent) to 255 (opaque). Finally, the bitmap is drawn onto
the form.
Notice that to draw onto the bitmap or form, we first have to create a
graphics object:

g = Me.CreateGraphics
g.DrawImage(tempBitmap, 200, 10)

This graphics object is the GDI+ graphics drawing surface that supports all of
the advanced graphics methods. A new graphics object has to be created for
each bitmap or form you want to draw onto. When you finish using the graph-
ics object, it is important to destroy it using the Dispose method:

g.Dispose()

Windows XP–Style Controls


The Windows XP user interface has a new look. The Windows common con-
trols, such as buttons, group boxes, and tabbed dialog boxes, are softer in
appearance—they have curved edges and are highlighted with a pleasant
orange outline when the mouse moves over them.
Although you can’t add or edit Windows XP–style controls using the
Visual Basic .NET Windows Forms designer, you can configure your applica-
tion to use Windows XP controls when it runs. In effect, these are normal
controls that simply display differently at run time. The XPTheme sample on
C1861587x.fm Page 399 Friday, November 16, 2001 8:59 AM

Chapter 18 Adding Value to Your Applications 399

the companion CD shows how to use Windows XP–themed controls, and the
following steps describe how to add the Windows XP theme to a new application:

1. Create a new Windows application. Add some controls.


2. For every control that has a FlatStyle property, set the FlatStyle prop-
erty to System, and Windows XP will use the default system style for
the control.
3. Compile your application as usual.
4. Create a manifest file in the same directory as the application’s
executable (usually the bin directory). A manifest file is an XML
file that contains run-time information about the application. Using
Notepad, create a text file with the following constants:

<?xml version="1.0” encoding="UTF-8” standalone="yes”?>


<assembly xmlns="urn:schemas-microsoft-com:asm.v1”
manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32”
name="Microsoft.Windows.Common-Controls”
version="6.0.0.0”
processorArchitecture="X86”
publicKeyToken="6595b64144ccf1df”
language="*”
/>
</dependentAssembly>
</dependency>
</assembly>

5. Save the file with the name <exename>.manifest. For example, if


your application name is MyApp.exe, this file should be called
MyApp.exe.manifest. The manifest works on an application-by-
application basis rather than a file-by-file basis. Thus, you need to
create only one manifest for the main EXE of the application, even if
it contains many DLLs. When your application runs, Windows XP
looks at the application manifest and uses the information it finds
there to alter the application’s execution. In this case, the manifest
instructs Windows XP to use the latest version of the common con-
trols library—the version with the Windows XP–style controls in it.
C1861587x.fm Page 400 Friday, November 16, 2001 8:59 AM

400 Part IV Techniques for Adding Value

Figure 18-8 shows a form with standard controls on the left and the same
forms with Windows XP controls on the right.

Default controls

Windows XP themed controls

F18km08

Figure 18-8 Normal controls (above) and Windows XP controls (below).

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 (refer-
ring 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
C1861587x.fm Page 401 Friday, November 16, 2001 8:59 AM

Chapter 18 Adding Value to Your Applications 401

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 appli-
cations, 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 instal-
lation doesn’t overwrite other files (unless you explicitly decide to overwrite an
existing file), and other installations don’t affect your application’s files. How-
ever, 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 compo-
nents, and second, if one of these COM components is overwritten, your appli-
cation 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 con-
trols with Windows Forms controls.

Conclusion

This chapter has looked at several ways you can extend your upgraded appli-
cations. 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 applica-
tions. 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 distrib-
uted applications.
C1861587x.fm Page 402 Friday, November 16, 2001 8:59 AM
C1961587x.fm Page 403 Friday, November 16, 2001 9:02 AM

Replacing ActiveX Controls


with Windows Forms
Controls
If you’ve been using Microsoft Visual Basic long enough—since before Visual
Basic 4, to be exact—you will have fond memories of upgrading your controls
from one control model to another: from VBX to OCX. When Visual Basic pole-
vaulted to 32 bits, VBX—short for Visual Basic extension—controls didn’t make
it over the bar and were left behind. Visual Basic 4 32-Bit Edition offered
OCX—short for OLE control extension—replacements for most of the VBX con-
trols delivered with Visual Basic 4 16-Bit Edition. In order to move your Visual
Basic application to 32 bits, you were forced to upgrade your VBX controls to
OCX controls. Visual Basic 4 provided a feature that would automatically
replace the VBX controls in your project with equivalent OCX controls—com-
monly referred to as ActiveX controls today.
Visual Basic .NET offers a new control model called Windows Forms con-
trols. Although there are many Windows Forms controls that are equivalent to
the ActiveX controls you find in Visual Basic 6, you are not forced to replace all
of your ActiveX controls with Windows Forms equivalents; Visual Basic .NET
supports ActiveX controls as is. In fact, as we discussed in Chapter 13, the
Upgrade Wizard—except in limited situations—does not replace your ActiveX
controls with equivalent Windows Forms controls, even when equivalent con-
trols exist.
This chapter discusses how you can manually replace your ActiveX con-
trols with equivalent Windows Forms controls. In addition, it discusses the ben-
efits of making this transition.

403
C1961587x.fm Page 404 Friday, November 16, 2001 9:02 AM

404 Part IV Techniques for Adding Value

Benefits of Upgrading Controls


Upgrading from ActiveX controls to Windows Forms controls has certain bene-
fits. As you’ll see in this section, these benefits include 100 percent Microsoft
.NET compatibility, improved versioning, and simpler deployment.

100 Percent .NET Compatibility


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 con-
trol 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 Win-
dows 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 appli-
cations in which the control is used will share the control from the same loca-
tion. 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 appli-
cation 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
C1961587x.fm Page 405 Friday, November 16, 2001 9:02 AM

Chapter 19 Replacing ActiveX Controls with Windows Forms Controls 405

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 Win-
dows 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 appli-
cation 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 Rich-
Text, 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.

Process of Replacing Controls


Because the Visual Basic .NET Upgrade Wizard does not automatically replace
your ActiveX controls with Windows Forms equivalents, the process of replac-
ing the ActiveX controls in your upgraded project with equivalent controls pro-
vided in the Windows Forms package is a manual one. This process involves
the following general steps:

1. Copy the design-time property settings for the ActiveX control.


2. Delete the ActiveX control from the form.
3. Place the equivalent Windows Forms control on the form.
C1961587x.fm Page 406 Friday, November 16, 2001 9:02 AM

406 Part IV Techniques for Adding Value

4. Rename the Windows Forms control to match the name of the


ActiveX control.
5. Paste the design-time settings for the control.
6. Fix up the design-time settings.
7. Resolve errors in the code.
8. Remove the ActiveX control library reference.

The example in this section demonstrates each of these steps in turn.

Manually Upgrading a Control


Let’s take a look at a simple example of replacing the ActiveX ProgressBar con-
trol 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 demon-
strates how to manually upgrade the ActiveX ProgressBar control to the Win-
dows 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

For i = ProgressBar1.Min To ProgressBar1.Max

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:
Private Sub ProgressBar1_Click()
MsgBox “You clicked my progress bar!"
End Sub
C1961587x.fm Page 407 Friday, November 16, 2001 9:02 AM

Chapter 19 Replacing ActiveX Controls with Windows Forms Controls 407

First load the Visual Basic 6 sample PBar application and try it out. Then
choose Start from the Run menu, and click the Show Progress button. You
should see the progress automatically increment from minimum to maximum.
Figure 19-1 shows an example of the application running.

F19km01

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, fol-
lowing the steps outlined earlier.

Copy the Design-Time Property Settings for the ActiveX Control


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.

Copy the extended property settings Let’s start by copying the upgraded
extended property settings found in the upgraded form file. To see these prop-
erty 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:

Me.ProgressBar1.Location = New System.Drawing.Point(8, 64)


Me.ProgressBar1.Name = “ProgressBar1”
(continued)
C1961587x.fm Page 408 Friday, November 16, 2001 9:02 AM

408 Part IV Techniques for Adding Value

Me.ProgressBar1.OcxState = _
CType(resources.GetObject(“ProgressBar1.OcxState”), _
System.Windows.Forms.AxHost.State)
Me.ProgressBar1.TabIndex = 0
Me.ProgressBar1.Size = New System.Drawing.Size(297, 57)

Select all property settings and copy the code to the Clipboard. Run Note-
pad 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 repre-
sents 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 infor-
mation 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:
Begin MSComctlLib.ProgressBar ProgressBar1
Height = 855
Left = 120
TabIndex = 0
Top = 960
Width = 4455
_ExtentX = 7858
_ExtentY = 1508
_Version = 393216
Appearance = 1
Min = 1
Max = 200
End

Let’s copy the ActiveX control-specific property settings. In this case, the
control-specific property settings are Appearance, Min, and Max. Custom prop-
erty 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 set-
tings 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
C1961587x.fm Page 409 Friday, November 16, 2001 9:02 AM

Chapter 19 Replacing ActiveX Controls with Windows Forms Controls 409

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.

_ExtentY DataFormat Left


_ExtentX DataMember Name
_Version DataSource TabIndex
Extender properties Default TabStop
Align DragIcon Tag
Cancel DragMode TooltipText
CausesValidation Enabled Top
Container Height Visible
DataBindings HelpContextID WhatsThisHelpID
DataChanged Index Width
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:

Me.ProgressBar1.Location = New System.Drawing.Point(8, 64)


Me.ProgressBar1.Name = “ProgressBar1”
Me.ProgressBar1.TabIndex = 0
Me.ProgressBar1.Size = New System.Drawing.Size(297, 57)
Appearance = 1
Min = 1
Max = 200

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:

Me.ProgressBar1.Location = New System.Drawing.Point(8, 64)


Me.ProgressBar1.Name = “ProgressBar1”
Me.ProgressBar1.TabIndex = 0
Me.ProgressBar1.Size = New System.Drawing.Size(297, 57)

(continued)
C1961587x.fm Page 410 Friday, November 16, 2001 9:02 AM

410 Part IV Techniques for Adding Value

Me.ProgressBar1.Appearance = 1
Me.ProgressBar1.Min = 1
Me.ProgressBar1.Max = 200

Delete the ActiveX Control from the Form


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.

Place the Equivalent Windows Forms Control on the Form


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 plac-
ing the control in exactly the same position as the previous control. The posi-
tion 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.

Paste the Design-Time Settings for the 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.”

Fix Up the Design-Time Settings


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 Win-
dows 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
C1961587x.fm Page 411 Friday, November 16, 2001 9:02 AM

Chapter 19 Replacing ActiveX Controls with Windows Forms Controls 411

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 Pro-
gressBar 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 exam-
ple, code is written in response to the ProgressBar Click event. When the Pro-
gressBar ActiveX control is deleted, the following subroutine exists but is not
associated with the Windows Forms ProgressBar control:

Private Sub ProgressBar1_ClickEvent( _


ByVal eventSender As System.Object, _
ByVal eventArgs As System.EventArgs)
MsgBox(“You clicked my progress bar!”)
End Sub

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:

Private Sub ProgressBar1_Click(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles ProgressBar1.Click
End Sub
C1961587x.fm Page 412 Friday, November 16, 2001 9:02 AM

412 Part IV Techniques for Adding Value

Cut and paste the body of the ProgressBar1_ClickEvent subroutine to the


Pr ogr essBar1_Click event subroutine, and then delete the original
ProgressBar1_Click event subroutine, leaving the following code:

Private Sub ProgressBar1_Click(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles ProgressBar1.Click
MsgBox(“You clicked my progress bar!”)
End Sub

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 Win-
dows Forms control.

Remove the ActiveX Control Library Reference


The final step is optional and should be performed only if you have replaced all
ActiveX controls for a given library with Windows Forms equivalents. In the
case of the example demonstrated here, all controls contained in the Windows
common controls library—the ProgressBar control—have been replaced with
Windows Forms equivalents. Therefore, you can safely remove the reference to
the ActiveX control library. To remove the reference, expand the References list
for the project in the Solution Explorer. You will need to remove two references
for each ActiveX control. Right-click AxMSComctlLib, and choose Remove from
the shortcut menu. Then right-click MSComctlLib and choose Remove. Remov-
ing an ActiveX control library reference in this manner means that fewer files
need to be deployed with the application.
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.

Mappings for Visual Basic 6 ActiveX Controls


Visual Basic 6 provides a number of ActiveX controls, such as the RichText,
Masked Edit, and Common Dialog controls, as well as the Windows common
controls—ProgressBar, Slider, StatusBar, Toolbar, TreeView, and so on. Table
19-1 lists the Windows Forms controls that make suitable replacements for
Visual Basic 6 ActiveX controls.
C1961587x.fm Page 413 Friday, November 16, 2001 9:02 AM

Chapter 19 Replacing ActiveX Controls with Windows Forms Controls 413

Table 19-1 Mapping of Visual Basic 6 ActiveX Controls to


Visual Basic .NET Windows Forms Controls
Visual Basic 6 ActiveX Visual Basic .NET
ActiveX Control Control Library Windows Forms Control
ADO Data control* MSAdodc.ocx ADODC control provided by the
Visual Basic compatibility library
Common Dialog ComDlg32.ocx OpenFileDialog, SaveFileDialog, Font-
Dialog, ColorDialog, and PrintDialog
DataGrid MSDatGrd.ocx No equivalent control; Windows
Forms DataGrid does not bind to
ADO data, just to ADO.NET data
Rich TextBox RichTx32.ocx RichTextBox
*
SSTab TabCtl32.ocx TabControl
ListView MSComCtl.ocx ListView
TreeView MSComCtl.ocx TreeView
ImageList MSComCtl.ocx ImageList
TabStrip MSComCtl.ocx TabControl
Toolbar MSComCtl.ocx ToolBar
StatusBar MSComCtl.ocx StatusBar
ProgressBar MSComCtl.ocx ProgressBar
Slider MSComCtl.ocx TrackBar
UpDown MSComCt2.ocx NumericUpDown
MonthView MSComCt2.ocx MonthCalendar
DTPicker MSComCt2.ocx DateTimePicker
* Denotes an ActiveX control that is automatically upgraded to the equivalent Windows Forms control.

ActiveX Controls vs. Windows Forms Controls


For the most part, the Windows Forms controls listed in Table 19-1 offer the
same functionality as their ActiveX control counterparts. One of the main differ-
ences—as demonstrated by the ProgressBar upgrade example given earlier—is
that features of a Windows Forms control are not exposed in the same way. For
example, the same type of properties may exist but have different names. In the
case of the ProgressBar control, the Windows Forms control’s Maximum and
Minimum properties equate to the Max and Min properties of the ActiveX con-
trol. For properties such as these, a simple one-to-one mapping exists, and it is
C1961587x.fm Page 414 Friday, November 16, 2001 9:02 AM

414 Part IV Techniques for Adding Value

easy to find the equivalent property, method, or event when replacing an


ActiveX control with a Windows Forms control.
We refer to controls that do not contain any subobjects, such as a collec-
tion of items or nodes, as having a flat object model. This means that all the
properties and methods for the control exist directly on the control. You need
to use only one dot (or period) to access any one of the properties or methods
when writing code. Replacing a flat-model ActiveX control—such as a Progress-
Bar or Slider—with its Windows Forms equivalent is a relatively straightforward
task. With both controls—the ActiveX version and the Windows Forms ver-
sion—on a form side by side, you can use IntelliSense or the Object Browser to
take a quick inventory of each control and compare how you map the proper-
ties, methods, and events from one control to the other.
We refer to controls that have properties that return objects such as collec-
tions or other complex structures as having a deep or rich object model. Con-
trols such as TreeView and ListView fall into this category. The TreeView
control, for example, has a Nodes property representing a collection of Node
objects. The Nodes collection is itself an object having its own set of properties
and methods. One of these methods—the Item method—returns a Node object
that in turn has its own set of properties and methods. You can navigate from
the control to a child object by using a dot (or period) to separate each succes-
sive object in the object hierarchy.
Replacing a rich-model ActiveX control with the equivalent Windows
Forms control presents a unique challenge. The challenge stems from differ-
ences in how the object models allow you to interact with subobjects of ActiveX
and Windows Forms controls. ActiveX controls take a more property-centered
approach to adding or navigating a control’s subobjects. Windows Forms con-
trols take a more object-oriented approach. This difference affects the way you
write code to add or find a subobject for a control. The challenge is to figure
out the code that you need to write to achieve the same results in a Windows
Forms control as are achieved in its ActiveX counterpart.
Let’s take a look at adding child nodes to a TreeView control as an exam-
ple. This example highlights the general differences between ActiveX and Win-
dows Forms controls having rich object hierarchies, such as the ListView,
ToolBar, StatusBar, and tabbed dialog controls.
The following is an example of code written to add a single parent and
child node to a TreeView control. The code is associated with a Visual Basic 6
form containing a TreeView control and an ImageList control named TreeView1
and ImageList1, respectively. Assume that the ImageList control contains at least
one image added at design time.
Set TreeView1.ImageList = ImageList1
TreeView1.Nodes.Add , , “Parent", “Parent node", 1
TreeView1.Nodes.Add 1, tvwChild, “Child", “Child node", 1
C1961587x.fm Page 415 Friday, November 16, 2001 9:02 AM

Chapter 19 Replacing ActiveX Controls with Windows Forms Controls 415

The ActiveX controls provided with Visual Basic 6 do not allow you to cre-
ate a subobject directly. For example, you cannot dimension a variable of type
Node using the New qualifier. Instead, you create a node indirectly by calling
the Add method on the Nodes collection and passing in a number of arguments
representing property settings for the newly created node. Because this type of
object model directs you to specify the properties of the Node object rather than
the Node object itself, it is a property-centered model for creating nodes.
To achieve the same result using the Windows Forms TreeView control,
you must think in a more object-oriented fashion. In the object model used in
Windows Forms, it is preferable to create Node objects and then add them to
the Nodes collection. We should note that the Windows Forms TreeView con-
trol, and other Windows Forms controls for that matter, do allow you to create
collection member objects in a property-centered way. For example, you can
create a Node object by calling the Add method on the Nodes collection and
passing the Text value for the node. However, the Add method on collections
generally does not support a large number of arguments—unlike collections
related to ActiveX controls, where the arguments represent properties for the
new item. You must get back into the object-oriented mindset and set the prop-
erty values on the object after it is created.
The following code is the equivalent of the previous example; it adds a
single parent and child node to a Windows Forms TreeView control:

Dim nodeChild As TreeNode = New TreeNode(“Child node", 0, 0)


Dim childNodes() As TreeNode = {nodeChild}
Dim nodeParent As TreeNode = New TreeNode(“Parent node", _
0, 0, childNodes)

TreeView1.ImageList = ImageList1
TreeView1.Nodes.Add(nodeParent)

Compared to the ActiveX TreeView control, the Windows Forms TreeView


control requires you to take a more structured, bottom-up approach to creating
the nodes within the TreeView. In the Windows Forms version of TreeView,
you cannot add individual nodes relative to other nodes in an ad hoc fashion.
Instead, you must add the child nodes for a parent node at the time you add the
parent node. You do this by passing in an array of TreeNode child objects as an
argument to the TreeNode constructor for the parent node. As this code demon-
strates, you add nodes in a bottom-up manner by first allocating the child nodes
and then creating the parent node and passing it an array of its children. The
code has a more object-oriented feel to it, as you construct the Node objects and
then pass the objects as arguments to other methods—such as Add—to be
added to the Nodes collection.
C1961587x.fm Page 416 Friday, November 16, 2001 9:02 AM

416 Part IV Techniques for Adding Value

We intentionally used the ImageList control in this example to point out


another difference between ActiveX controls and Windows Forms controls that
use the rich object model. The ActiveX controls provided in Visual Basic 6
assume the value of 1 as the index for the first item in a collection. Windows
Forms controls, on the other hand, always assume an index value of 0 for all
collections. That is why the code for the ActiveX TreeView control passes a
value of 1 as the index for the first image in the ImageList ListImages collection,
whereas the Windows Forms code example uses 0.

Conclusion

Although the Upgrade Wizard makes life easier by having your upgraded Visual
Basic 6 applications use the same ActiveX controls, keeping the ActiveX con-
trols 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.
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.

ADO.NET for the ADO Programmer


It is obvious from the get-go that ADO.NET isn’t the traditional ADO. The data
object model provided by ADO.NET is substantially different from that of ADO
and requires a different approach when designing and implementing solutions.

417
418 Part IV Techniques for Adding Value

While some of the concepts remain familiar to ADO developers (such as using
Connection and Command objects to manipulate a data source), others will
require some adjustment. This discussion gives only a short overview, so we
can quickly move on to the more interesting stuff.

Overview of ADO.NET
While ADO.NET presents a new data object model, parts of it are quite similar
to the ADO we all know and love. Think of ADO.NET as the result of the inte-
gration of both classic ADO and the Microsoft XML technologies in a single,
coherent framework. Unlike ADO, ADO.NET was designed from the ground up
to support a more general data architecture that is abstracted from an underly-
ing database. In other words, ADO.NET was designed to work with a myriad of
data sources and can function as a data store in and of itself. This framework
provides a series of data container objects that support functions similar to
those used in databases (indexing, sorting, and views) without requiring an
actual physical database behind the scenes.
ADO.NET also provides an interface that is consistent with other .NET
stream-based data classes. This is a huge step forward and a wonderful design
win. In Visual Basic 6, ADO uses a totally different object model compared to
the FileSystemObject or the Microsoft XML libraries. ADO.NET, on the other
hand, is designed to be consistent with other .NET Framework classes. This
new compatibility enables developers to move between vastly different kinds
of classes with relative ease. Figure 20-1 provides an overview of the ADO.NET
class structure.

System.Data Data Classes

DataView DataSet DataTable

Connection Command DataAdapter DataReader


Provider-specific data classes (OleDb or SqlClient)

F20km01

Figure 20-1 Overview of ADO.NET classes.

While the Connection and Command objects are still required for most
data access, the distinction between the two objects has been made literal, as
the Connection object now solely represents a database connection, while the
Command object handles all queries.
Chapter 20 Moving from ADO to ADO.NET 419

The classic ADO Recordset functionality has been split into smaller and
more specialized objects: the DataSet and DataReader classes. Forward-only
“firehose” Recordsets map directly to DataReaders, while disconnected Record-
sets map best to DataSets. DataSet is a powerful new class designed solely to
address the disconnected data model problem. It was designed specifically to
fill the gaps in the way ADO handles disconnected data, which was never very
satisfactory. Given the new kinds of applications that Visual Basic .NET targets,
improving and enhancing data access was a primary goal. ADO.NET goes still
further by providing strong integration with XML, for which ADO had only min-
imal support.

DataSets
DataSets are probably the most foreign of the new objects in the ADO.NET
arsenal and thus require a bit more of an introduction than the rest. A DataSet
is an in-memory, named cache of data retrieved from a generic data container.
What this means is that the original data source is not important and does not
have to be a database. DataSets are extremely flexible for that reason. They can
be populated from various sources, including data adapters, local data, or XML.
In essence, a DataSet contains collections of DataTables and DataRela-
tions. The DataTable class conceptually corresponds closely to a database
table, while the DataRelation class is used to specify relations between the
DataTables. DataSets can also contain user-specific information with the
ExtendedProperties collection. Contained data and the current schema (or
both) can be directly extracted as XML streams, making the information highly
mobile. Figure 20-2 illustrates the structure of the DataSet class and its relation-
ship to the data-specific ADO.NET classes.

Important Any relationships between DataTables need to be speci-


fied by the programmer. The current version of ADO.NET does not
support propagating preexisting relationships from the data source to
the DataSet. Therefore, any relationships that are defined in your SQL
database (or other relational databases) need to be redefined in your
DataSet.
420 Part IV Techniques for Adding Value

DataSet

Tables

Table

Columns

Column

Constraints

Constraint

Rows

Row
Relations

Relation

F20km02

Figure 20-2 DataSet class hierarchy.

Using a DataAdapter in conjunction with a DataSet enables you to explic-


itly manage changes made to all of the tables contained within the DataSet and
propagate those changes back to the originating data source. The DataAdapter
is a new component in ADO.NET. It is designed to operate as an intermediary
between disconnected forms of data (DataSets, DataTables) and the underlying
data store. The simplest use of a DataAdapter is to fill a DataSet or DataTable
with the results of a database query, but it is able to go much further than that.
The DataAdapter is an intermediary in the truest sense of the word, in that it
supports bidirectional data transfers. Using the DataAdapter, you can define
how changes to a DataSet or DataTable are merged back into the source data-
base and vice versa. In its most advanced use, the DataAdapter can specify how
to handle conflict resolution when merging, including insertions and updates.
The .NET Framework ships with two types of DataAdapters: the OleDb-
DataAdapter and the SqlDataAdapter. Although these DataAdapters target dif-
ferent data sources, they can be used interchangeably, allowing you to move
between data sources with minimal disruption. As you will see later in this
chapter, the OleDbDataAdapter can also be used as a bridge between ADO and
ADO.NET.
Chapter 20 Moving from ADO to ADO.NET 421

A Quick Note on Providers


You will see managed providers mentioned throughout this chapter.
Two kinds of providers ship with Visual Basic .NET: the OleDb providers
and the SqlClient providers, which live in the System.Data.OleDb and Sys-
tem.Data.SqlClient namespaces, respectively. The majority of the exam-
ples in this chapter use the OleDb managed provider because it maps the
most directly to ADO. The OleDbConnection object can use the same con-
nection string that ADO uses. The SqlClient managed provider is designed
to talk directly to a Microsoft SQL server and uses a slightly different con-
nection string. (There is no need to specify a provider in a connection
string for the SqlConnection object.)

Integrating Your ADO Code into a Visual


Basic .NET Application
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.

ADO with ADO.NET: Is It the Best Architecture?


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.
422 Part IV Techniques for Adding Value

Binding Your ADO Recordset to .NET Controls


.NET provides a wide range of controls for both Web applications and Microsoft
Windows–based form applications. Many of these controls support data bind-
ing 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 sim-
ple: 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:

‘Here is the familiar ADO stuff


Dim cn As New ADODB.Connection()
Dim rs As New ADODB.Recordset()
cn.Open(“Provider=SQLOLEDB.1;Integrated Security=SSPI;” & _
“Persist Security Info=False;” & _
“Initial Catalog=Northwind;Data Source=bart”)
rs.Open(“Select * From Employees", cn)

‘This is where things start getting interesting. Here we declare not


‘only the DataAdapter, but also the DataTable the data is destined for.
Dim olead As New Data.OleDb.OleDbDataAdapter()
Dim dt As New Data.DataTable()

‘We use the DataAdapter to fill a DataTable


olead.Fill(dt, rs)

‘Now we bind the DataTable to the DataGrid


Me.DataGrid1.DataSource = dt

‘This is cleanup. Always close your objects as


‘soon as you are done with them.
rs.Close()
cn.Close()

In this simple example, an ADO Recordset is bound to a DataGrid control on a


Windows form. Figure 20-3 shows the form with the DataGrid control before
and after data binding. The OleDbDataAdapter.Fill method is capable of popu-
lating a DataTable object with data from an ADODB Recordset.
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).
Chapter 20 Moving from ADO to ADO.NET 423

Before Data Binding After Data Binding

F20km03

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 signif-
icant advantages over technologies such as Remote Data Service (RDS) and Dis-
tributed 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, con-
sult 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
424 Part IV Techniques for Adding Value

code is used mainly for presentation or data binding and can readily
be replaced with managed code.

ADO and XML Web Services Option 1 ADO and XML Web Services Option 2

Recordset Recordset

XML
Logic Logic DataTable

XML Web Service

Button click Button click


XML Recordset

DataTable DataTable

Control DataSet Control DataSet

F20km04

Figure 20-4 Options for implementing XML Web services with Recordsets.

Serializing Your Recordset to XML


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:

Function RsToString(ByVal rs As ADODB.Recordset) As String


Dim stm As New ADODB.Stream()
rs.Save(stm, ADODB.PersistFormatEnum.adPersistXML)
Return stm.ReadText()
End Function

Function StringToRs(ByVal s As String) As ADODB.Recordset


Dim stm As New ADODB.Stream()
Dim rs As New ADODB.Recordset()
stm.Open()
stm.WriteText(s)
stm.Position = 0
rs.Open(stm)
Return rs
End Function
Chapter 20 Moving from ADO to ADO.NET 425

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 Chap-
ter 21 for details on how to implement the XML Web service itself.

Mapping ADO Objects to ADO.NET


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 imple-
mentation 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, how-
ever, 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?

Connection and Command Objects


The Connection and Command objects still exist in ADO.NET. There are, how-
ever, 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
426 Part IV Techniques for Adding Value

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 connec-
tion 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 compara-
ble to the ADO Command class in that it can be used to execute run-time que-
ries 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:

■ ExecuteReader Execute a query and return a DataReader object.


■ ExecuteScalar Execute a query and return a single value.
■ ExecuteXmlReader Execute an XML query and return an Xml-
Reader object.
■ ExecuteNonQuery Execute a query with no results.

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:
‘ Visual Basic 6 Method Using ADO
Sub ExecuteSql( connStr As String, statement As String )
Dim conn as New ADODB.Connection
Dim rs as ADODB.RecordSet
conn.Open connStr
Set rs = conn.Execute( sqlStatement )
End Sub
Chapter 20 Moving from ADO to ADO.NET 427

‘ Visual Basic .NET Equivalent Method Using ADO.NET


Sub ExecuteSql( connStr As String, statement As String )
Dim conn as New OleDbConnection( connStr )
Dim cmd as New OleDbCommand( statement, conn )
conn.Open()
Dim dr As OleDbDataReader = cmd.ExecuteReader()
End Sub

Recordsets
Not all Recordsets are created equal. Depending on the settings you use, they
can take several different forms, leading to various consequences for applica-
tion 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 (discon-
nected, forward-only “firehose,” and so on) and so that the classes can be opti-
mized 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 imple-
mentation 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 fire-
hose Recordsets and the ADO.NET DataReader (note the differences in the loop
structures):
‘ Visual Basic 6 Forward-Only Recordset Access
Sub VB6RecordSetExample(connStr As String, statement As String)
Dim conn As New ADODB.Connection
Dim rs As ADODB.Recordset
conn.Open (connStr)
(continued)
428 Part IV Techniques for Adding Value

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 creat-
ing different kinds of Recordsets, ADO.NET has specific methods for creating
different forms of data repositories.
‘ This is a Visual Basic 6 Dynamic Cursor Recordset Example
Function VB6DynamicRSExample(connStr As String, _
statement As String) As Recordset
Dim rs As New ADODB.Recordset
rs.Open statement, connStr, adOpenDynamic

Set rs = conn.Execute(statement)
Set VB6DynamicRSExample = rs
Set rs = Nothing
End Function
Chapter 20 Moving from ADO to ADO.NET 429

‘ 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 Record-
sets 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 row As DataRow


For Each row In ds.Tables(0).Rows
‘ Do stuff
Next

Dim i As Integer
For i = 0 To ds.Tables(0).Rows.Count - 1
row = ds.Tables(0).Rows(i)
‘ Do other stuff

Next

Using DataViews
A DataView is an object that allows you to create multiple views of your data
and that can be used for data binding. The concept is fairly simple, but its
importance cannot be overstated. The flexibility it introduces is impressive
because a DataView does not actually change the DataTable from which it is
generated. Instead, it offers a filtered window to the data. The following code
shows how you can use the DataView control to filter rows based on a column
value (in this case, the FirstName and LastName columns):

Function DataViewTest()
Dim adapter As New OleDbDataAdapter(“Select * From Customers", _
connStr)
Dim ds As New DataSet()

adapter.Fill(ds)
(continued)
430 Part IV Techniques for Adding Value

Dim view1 As DataView = ds.Tables(“Customers”).DefaultView


Dim view2 As DataView = ds.Tables(“Customers”).DefaultView

view1.RowFilter = “‘LastName’ Like ‘O%’”


view2.RowFilter = “‘FirstName’ Like ‘E%’”

Dim i As Integer

Debug.WriteLine(“All LastNames starting with ‘O’”)


For i = 0 To view1.Count - 1
Debug.WriteLine(view1(i)(“LastName”))
Next

Debug.WriteLine(“All FirstNames starting with ‘E’”)


For i = 0 To view2.Count - 1
Debug.WriteLine(view1(i)(“FirstName”))
Next

End Function

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.

Binding to Windows Forms Controls


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:

Dim adapter As New OleDbDataAdapter(“Select * From Customers", _


connStr)
Dim dt As New DataTable()

‘ Import the query results into the DataTable


adapter.Fill(dt)

‘ Bind the DataTable to the DataGrid


DataGrid1.DataSource = dt
Chapter 20 Moving from ADO to ADO.NET 431

The DataSet works in almost exactly the same way:

Dim adapter As New OleDbDataAdapter(“Select * From Customers",


connStr)
Dim ds As New DataSet()

‘ Import the query results into the DataSet


adapter.Fill(ds)

‘ Bind a DataTable from the DataSet to the DataGrid


DataGrid1.DataSource = ds.Tables(“Customers”)

Notice in this example that instead of passing the entire DataSet to the Data-
Grid, we’ve chosen to bind only a single table. This is often necessary with con-
trols 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.

Binding with Web Forms Controls


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.Win-
dows.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:

Dim adapter As New OleDbDataAdapter(“Select * From Customers", _


connStr)
Dim ds As New DataSet()

‘ Import the query results into the DataSet


adapter.Fill(ds)

‘ 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
DataBind on the Web Form DataGrid forces the binding to occur—a nec-
essary step, without which the control would not be bound to the speci-
fied data.
432 Part IV Techniques for Adding Value

A Note About Performance


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:

■ Open connections as late as possible.


■ Close them as soon as possible.

These two guidelines seem simple, yet their importance cannot be over-
stated. Connections to databases represent real physical resources on your
machine, and hogging those resources will inevitably result in poor perfor-
mance. 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 auto-
matically 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 solu-
tion). 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).
Chapter 20 Moving from ADO to ADO.NET 433

Using Open and Close Effectively


If you need to do a lot of database work (such as making multiple sequen-
tial 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)

‘ Open the connection and execute the command


conn.Open()
Dim dr As SqlDataReader = cmd.ExecuteReader()

While dr.Read()
‘ Do some processing
End While

‘ Close the reader and the connection


dr.Close()
conn.Close()

‘ Get ready to do your next query


cmd.CommandText = “Select * From Employees”

‘ Open the connection and execute the next command


conn.Open()
dr = cmd.ExecuteReader()

While dr.Read()
‘ Do some other processing
End While

‘ Close the reader and the connection


dr.Close()
conn.Close()
End Function
434 Part IV Techniques for Adding Value

Conclusion

We covered a lot of ground in this chapter, from using ADO within your Visual
Basic .NET application, to data binding and getting Recordsets to work with
XML Web services, to moving to ADO.NET. Remember that you have many
choices regarding how you implement your application. If your investment in
ADO is too heavy at this time for you to consider upgrading to ADO.NET, you
can still continue to improve and develop your application with an eye to
switching to ADO.NET in the future. On the other hand, when you make the
plunge into ADO.NET and the various managed providers, you will reap signif-
icant performance gains over pure ADO.
C2161587x.fm Page 435 Friday, November 16, 2001 9:08 AM

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 corner-
stone of the entire Microsoft .NET initiative. The .NET Framework is designed
from the ground up to support new first-class distributed technologies. Know-
ing 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.

Note Some of the examples in this chapter use the Northwind data-
base Northwind.mdb (included on the companion CD) as a basis for
demonstrating various forms of data access.

435
C2161587x.fm Page 436 Friday, November 16, 2001 9:08 AM

436 Part IV Techniques for Adding Value

Important Concepts for Distributed Applications


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 appli-
cation. To this end, this section looks at three important concepts in large-scale
distributed application development:

■ Loosely coupled and tightly coupled applications


■ Overhead involved in method calls
■ Componentization and logical organization

Loosely Coupled vs. Tightly Coupled Applications


Imagine using a standard platform-independent communication mechanism
between remote (network-separated) application segments (think SOAP). What
this does is enable you to develop applications that have no knowledge or
understanding of either participating system. In this scenario, you agree only on
the communications mechanism. The platforms can vary wildly. In .NET, two
systems are considered loosely coupled if the only mandate imposed on them
both is to understand self-describing, text-based messages.
Tightly coupled systems, on the other hand, impose a significant amount
of customized overhead to enable communication and require a greater under-
standing 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 com-
munication 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 respon-
sibility 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.
C2161587x.fm Page 437 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 437

Overhead in Method Invocation


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 typi-
cally 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 net-
work 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 pro-
cess-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.

In-process

Out-of-process

Remote process

Time
F21km01

Figure 21-1 Overhead of method calls in different contexts.

So while we can call into local methods without any concern for over-
head, and even out-of-process overhead isn’t too bad, we need to be extra care-
ful 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 sin-
gle method to set all the properties at once.

‘ Approach 1 – Don’t do this for a remote or out-of-process object


Obj.Property1 = val1
Obj.Property2 = val2
Obj.Property3 = val3
Obj.Property4 = val4
Obj.Property5 = val5

‘ Approach 2 – Better for a remote or out-of-process object


Obj.SetValues(val1, val2, val3, val4, val5)
C2161587x.fm Page 438 Friday, November 16, 2001 9:08 AM

438 Part IV Techniques for Adding Value

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 prop-
erty access involves a separate network operation, whereas approach 2 is
“chunky” because it accomplishes its task with a single network operation.

Network overhead time


Application execution time
Time

Setting one property Approach 2 Approach 1


(Chunky) (Chatty)
F21km02

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 appli-
cations. The more efficient your design, the better your application will perform
and the better it will scale up in an enterprise environment.
C2161587x.fm Page 439 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 439

Componentization and Logical Organization


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 com-
ponents. This separation has several benefits. Imagine a customer management
application. You might divide the server-side components into database, busi-
ness logic, and general utility components, as shown in Figure 21-3. This struc-
ture 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.

Controls

Client UI Business logic

Utility classes

Business logic

Middle tier Database logic

Utility classes

SQL

F21km03

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.
C2161587x.fm Page 440 Friday, November 16, 2001 9:08 AM

440 Part IV Techniques for Adding Value

Distributed Technologies in .NET


Visual Basic .NET uses three main distributed technologies (ignoring the
ADO.NET data access technologies). The first is XML Web services. This is an
open standard that enables developers to expose a set of software services over
the Internet and to do so simply. The second is another new technology,
unique to the .NET Framework, called .NET remoting. You can think of remot-
ing as the .NET equivalent of DCOM. It is a method of communication between
different operating system processes, regardless of whether they are on the
same computer. The .NET remoting system is an architecture designed to sim-
plify communication between objects living in different application domains,
whether they’re on the same computer or not, and between different contexts,
whether they’re in the same application domain or not. The last of the distrib-
uted technologies is COM+ application proxies. If you have worked with COM+
in a distributed environment, you are probably already familiar with application
proxies. Let’s look at each of these technologies in turn.

XML Web Services


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 program-
ming 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 inter-
face 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 funda-
mentally 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 automat-
ically by the ASP.NET runtime when a request to the service is made. (Subse-
quent requests are serviced by a cached precompiled object.)
C2161587x.fm Page 441 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 441

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 ser-
vices 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 advan-
tage 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 alter-
native 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 devel-
oper, 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 imple-
ment 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 major-
ity of applications, however, the built-in serialization capabilities of the .NET
Framework are more than sufficient.

Creating a Simple XML Web Service


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:

1. Create a new ASP.NET Web service project.


2. Add a new XML Web service file to the project.
3. Add a method to the XML Web service class, and mark the method
with the WebMethod attribute.
C2161587x.fm Page 442 Friday, November 16, 2001 9:08 AM

442 Part IV Techniques for Adding Value

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 meth-
ods 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 appli-
cation 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 fol-
lowing excerpt from the SimpleService.asmx.vb file (part of the SimpleWebSer-
vice sample project) demonstrates the WebMethod attribute in action:

<WebMethod()> Public Function HelloWorld() As String


HelloWorld = “Hello World. The current time is “ & Now()
End Function

You can do much more with the WebMethod attribute, such as manipulate
additional settings, but we will use it in its simplest, most basic form, as dem-
onstrated here. The MSDN documentation gives a more thorough treatment of
the use of this attribute, above and beyond its default behavior.
Now is a good time to talk about restrictions on parameters and return val-
ues 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 cus-
tom classes you have two options. You can implement the ISerializable inter-
face 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.
C2161587x.fm Page 443 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 443

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.

F21km04

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.
C2161587x.fm Page 444 Friday, November 16, 2001 9:08 AM

444 Part IV Techniques for Adding Value

F21km05

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:

<?xml version="1.0” encoding="utf-8” ?>


<string xmlns="http://tempuri.org/">Hello World. The current time is
10/13/2001 11:13:21 AM</string>

Pretty neat, huh? Now we need to look at how you can use this method in an
application.

Consuming a Simple Web Service


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.
C2161587x.fm Page 445 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 445

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 usu-
ally 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:

1. Create the XML Web service proxy object.


2. Call the HelloWorld method, and display a message box with the
result.

The code itself is simple:

Private Sub CallButton_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles CallButton.Click
Dim ws As New localhost.SimpleService()
MsgBox(ws.HelloWorld())
End Sub

You can see the results in Figure 21-6. This sample project helps demon-
strate how XML Web services simplify the development of distributed applica-
tions. XML Web Services are a powerful tool that enable the development of
fundamentally different kinds of applications, due to its platform- and lan-
guage-independent nature.
C2161587x.fm Page 446 Friday, November 16, 2001 9:08 AM

446 Part IV Techniques for Adding Value

F21km06

Figure 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
C2161587x.fm Page 447 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 447

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 seri-
alization. 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 modifica-
tions 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.

Using Recordset with XML Web Services


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 per-
sisted to XML on the server and is returned to the client. The client then recon-
structs the original Recordset. Notice that the reconstructed Recordset is not
exactly the same as the original. The new Recordset does not contain any con-
nections to a database, nor does it understand where it came from. For this rea-
son, we say that this Recordset is disconnected.
C2161587x.fm Page 448 Friday, November 16, 2001 9:08 AM

448 Part IV Techniques for Adding Value

XML Web Service

COM
object ADO Recordset

Recordset
in XML

WebMethod WebMethod
call return

XML Web Service Client

Recordset
Button Click
in XML

ADO Recordset

Control DataTable DataSet

F21km07

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 Win-
dows 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 represen-
tation 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.
C2161587x.fm Page 449 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 449

Imports ADODB
Imports System.Web.Services

<WebService(Namespace:="http://tempuri.org/”)> _
Public Class NorthwindDatabase
Inherits System.Web.Services.WebService

#Region “ Web Services Designer Generated Code “

<WebMethod()> Public Function Query(ByVal queryString As String) _


As String
Dim conn As New Connection()

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

Dim str As New StreamClass()


rs.Save(str, PersistFormatEnum.adPersistXML)
Query = str.ReadText()

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 seri-
alization 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.
C2161587x.fm Page 450 Friday, November 16, 2001 9:08 AM

450 Part IV Techniques for Adding Value

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 accept-
able 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

Public Class Form1


Inherits System.Windows.Forms.Form

#Region “ Windows Form Designer generated code “

Dim nwdb As localhost.NorthwindDatabase


Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
nwdb = New localhost.NorthwindDatabase()

‘ Add the query strings for the database


QueryComboBox.Items.Add(“Select * From Categories”)
QueryComboBox.Items.Add(“Select * From Customers”)
QueryComboBox.Items.Add(“Select * From Employees”)
QueryComboBox.Items.Add(“Select * From [Order Details]”)
QueryComboBox.Items.Add(“Select * From Orders”)
QueryComboBox.Items.Add(“Select * From Products”)
QueryComboBox.Items.Add(“Select * From Shippers”)
QueryComboBox.Items.Add(“Select * From Suppliers”)
End Sub

Private Sub ExecuteButton_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles ExecuteButton.Click
Dim xml As String

xml = nwdb.Query(QueryComboBox.Text)
If xml Is Nothing Then
QueryDataGrid.DataSource = Nothing
MsgBox(“The query failed!”)
Exit Sub
End If
C2161587x.fm Page 451 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 451

Dim str As New Stream()


str.Open()
str.WriteText(xml)
str.Position = 0

Dim rs As New Recordset()


rs.Open(str)

Dim da As New OleDbDataAdapter()


Dim table As New DataTable()

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 but-
ton. Another neat feature of this application is that the ComboBox is editable at
run time, so you can further customize the query.

F21km08

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.
C2161587x.fm Page 452 Friday, November 16, 2001 9:08 AM

452 Part IV Techniques for Adding Value

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
■ Stateless and stateful objects
■ Channels and serialization
■ Lease-based lifetime
■ Hosting objects in IIS

The most basic remoting scenario requires a client application and a min-
imal host process. The host process must register specific object types with the
runtime and specify the desired configuration details. Remoting itself can be
thought of as an abstraction layer over a transport protocol that enables appli-
cations to instantiate objects that may reside on remote machines and work
with them as if they were local. It is an interesting technology because it is
removed from the underlying transport protocol. What this means is that, unlike
DCOM, you have choices as to how your client and server processes talk to
each other.
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
C2161587x.fm Page 453 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 453

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.

A Simple Remoting 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:

Public Class SimpleClass


Inherits MarshalByRefObject

Public Function GetDateTime() As Date


Return Now()
End Function

Public Function Hello() As String


Return “Hello World!"
End Function
End Class

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:

Dim channel As New TcpChannel(9999)


ChannelServices.RegisterChannel(channel)
RemotingConfiguration.ApplicationName = “SimpleRemotingServer"
Dim t As Type = Type.GetType(“Server.SimpleClass”)
RemotingConfiguration.RegisterWellKnownServiceType(t, “SimpleClass", _
WellKnownObjectMode.SingleCall)
C2161587x.fm Page 454 Friday, November 16, 2001 9:08 AM

454 Part IV Techniques for Adding Value

It is important to note that the type is available through remoting only


when the host process is active. If the host process terminates at any time, all
types registered by that process are immediately unregistered and cease to
respond to any incoming requests. This includes objects that have already been
instantiated. Thus, if the process terminates while a client is still working with
the proxy, the client will experience a network failure on the next call through
the proxy object. Figure 21-9 illustrates the lifetime of the Simple Remoting
example. It emphasizes that once the SimpleClass type has been registered, it is
available only during the lifetime of the server process.

Server Process Host Runtime Client Process

Process Process
starts starts

Create channel
and register
SimpleClass type
Channel opened
SimpleClass
registered

SimpleClass Request
instantiated SimpleClass proxy
Loop
Call GetDateTime
SimpleClass

method
proxy

Call Hello method

Terminate proxy
Process
ends Channel closed
SimpleClass
unregistered
Process
ends

F21km09

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.
C2161587x.fm Page 455 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 455

Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Tcp

Public Class Form1


Inherits System.Windows.Forms.Form

#Region “ Windows Form Designer generated code “

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

Private Sub HelloButton_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles HelloButton.Click
If Not Initialize() Then Return

MsgBox(sc.Hello(), MsgBoxStyle.DefaultButton1, “Hello()”)


End Sub

Public Function Initialize() As Boolean


If sc Is Nothing Then
Dim chan As New TcpChannel()
ChannelServices.RegisterChannel(chan)
Dim t As Type = Type.GetType(“Server.SimpleClass”)
sc = Activator.GetObject(t, _
“tcp://localhost:9999/SimpleClass”)

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
C2161587x.fm Page 456 Friday, November 16, 2001 9:08 AM

456 Part IV Techniques for Adding Value

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.

F21km10

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 applica-
tion. This observation leads us to a discussion of the best architecture for an
application that uses remoting.

Architecture for 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. Pro-
gramming 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.
C2161587x.fm Page 457 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 457

Interfaces assembly

INorthwind

Project reference

Host runtime Client application

INorthwind INorthwind

Database Proxy

F21km11

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 pro-
gram 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 inter-
face. 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:

Public Interface INorthwindDatabase


ReadOnly Property Categories() As DataTable
ReadOnly Property Customers() As DataTable
ReadOnly Property Employees() As DataTable
ReadOnly Property OrderDetails() As DataTable
ReadOnly Property Orders() As DataTable
ReadOnly Property Products() As DataTable
ReadOnly Property Shippers() As DataTable
ReadOnly Property Suppliers() As DataTable
End Interface

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
C2161587x.fm Page 458 Friday, November 16, 2001 9:08 AM

458 Part IV Techniques for Adding Value

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 defini-
tion, 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 database class containing the implementation for the


‘ INorthwindDatabase interface
Public Class Database
Inherits ServicedComponent
Implements MyInterfaces.INorthwindDatabase

Dim conn As OleDbConnection


Dim cmd As OleDbCommand
Dim da As OleDbDataAdapter

Public Sub New()


conn = New OleDbConnection( _
“Provider=Microsoft.Jet.OLEDB.4.0;” &
“Data Source=C:\Northwind.mdb”)
cmd = New OleDbCommand(“", conn)
da = New OleDbDataAdapter(cmd)
End Sub

Private Function ExecuteQuery(ByVal query As String) As DataTable


Dim t As New DataTable()
conn.Open()
cmd.CommandText = query
da.Fill(t)
conn.Close()
Return t
End Function

Public ReadOnly Property Categories() As DataTable _


Implements INorthwindDatabase.Categories
Get
Return ExecuteQuery(“Select * From Categories”)
End Get
End Property
C2161587x.fm Page 459 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 459

Public ReadOnly Property Customers() As DataTable _


Implements INorthwindDatabase.Customers
Get
Return ExecuteQuery(“Select * From Customers”)
End Get
End Property

Public ReadOnly Property Employees() As DataTable _


Implements INorthwindDatabase.Employees
Get
Return ExecuteQuery(“Select * From Employees”)
End Get
End Property

Public ReadOnly Property OrderDetails() As DataTable _


Implements INorthwindDatabase.OrderDetails
Get
Return ExecuteQuery(“Select * From [Order Details]”)
End Get
End Property

Public ReadOnly Property Orders() As DataTable _


Implements INorthwindDatabase.Orders
Get
Return ExecuteQuery(“Select * From Orders”)
End Get
End Property

Public ReadOnly Property Products() As DataTable _


Implements INorthwindDatabase.Products
Get
Return ExecuteQuery(“Select * From Products”)
End Get
End Property

Public ReadOnly Property Shippers() As DataTable _


Implements INorthwindDatabase.Shippers
Get
Return ExecuteQuery(“Select * From Shippers”)
End Get
End Property

Public ReadOnly Property Suppliers() As DataTable _


Implements INorthwindDatabase.Suppliers
Get
Return ExecuteQuery(“Select * From Suppliers”)
End Get
End Property
End Class
C2161587x.fm Page 460 Friday, November 16, 2001 9:08 AM

460 Part IV Techniques for Adding Value

The server process is not very interesting. It registers the ServerPro-


cess.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 remot-
ing 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

Public Class Form1


Inherits System.Windows.Forms.Form

#Region “ Windows Form Designer generated code “

Dim northwindDB As MyInterfaces.INorthwindDatabase


Public Function Initialize() As Boolean
If northwindDB Is Nothing Then
Dim chan As New TcpChannel()
ChannelServices.RegisterChannel(chan)
Dim t As Type = Type.GetType( _
“MyInterfaces.INorthwindDatabase,MyInterfaces”)
northwindDB = Activator.GetObject(t, _
“tcp://localhost:8086/NorthwindDB”)

If northwindDB Is Nothing Then


MsgBox(“Could not initialize the remoting client.” & _
“Check your configuration.”)
Return False
End If
End If

Return True
End Function

Private Sub CustomersButton_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles CustomersButton.Click
If Not Initialize() Then Return
QueryDataGrid.DataSource = northwindDB.Customers
End Sub

Private Sub EmployeesButton_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles EmployeesButton.Click
If Not Initialize() Then Return
QueryDataGrid.DataSource = northwindDB.Employees
End Sub
C2161587x.fm Page 461 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 461

Private Sub CategoriesButton_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles CategoriesButton.Click
If Not Initialize() Then Return
QueryDataGrid.DataSource = northwindDB.Categories
End Sub

Private Sub OrderDetailsButton_Click _


(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles OrderDetailsButton.Click
If Not Initialize() Then Return
QueryDataGrid.DataSource = northwindDB.OrderDetails
End Sub

End Class

Distributed COM+ Applications


Probably one of the most important uses for COM+ is in distributed applica-
tions. Creating application proxies was always one of the easiest ways to imple-
ment a distributed application. The number of choices in .NET might seem
somewhat daunting by comparison. Not too worry, though. The increased
choices are all designed to address certain deficiencies of the WinDNA platform
and technologies such as DCOM and RDS and to make it easier to create dis-
tributed COM+ applications.

COM+ and Remoting


The process of creating server applications that run through remoting is fairly
straightforward, if not obvious. Remember that remoting requires a host process
to handle registration and configuration. This is still true for COM+ applications.
Granted, you are already halfway there because any component derived from
ServicedComponent has MarshalByRefObject as a base class—a prerequisite for
all objects to be made available through remoting. The Architecting for Remot-
ing example demonstrates how to register a ServicedComponent with the com-
mon language runtime. Support for the COM+ context and other services is
implicit through remoting, and there really isn’t anything extra that you need to
do for it to work.

Using SOAP Services


Publishing a ServicedComponent through Remoting is powerful, but it requires
you to do your own work to register the individual components that are
intended to be available. There is an alternative: SOAP. The self-registration
process in .NET for ServicedComponents provides the ability to mark your
C2161587x.fm Page 462 Friday, November 16, 2001 9:08 AM

462 Part IV Techniques for Adding Value

entire COM+ application as publishable through SOAP Services (essentially an


XML Web service interface). Your COM+ application is then published through
a virtual root of the IIS server the application is registered on. This technique is
powerful, but it has greater implicit calling overhead than a binary format such
as Remoting. On the other hand, using SOAP Services allows you to publish
your COM+ application in a simple and straightforward manner. Recall from
Chapter 16 how important attributes are to COM+ applications. The SOAP Ser-
vice is made accessible through the ApplicationActivation assembly attribute.
There is a default property that you can set in this attribute that instructs the
self-registration process to create a SOAP proxy for your application (and any
ServicedComponent contained within the Assembly). This property allows these
ServicedComponents to be activated through a Web Service.

Limitations of SOAP Services


While using SOAP Services is a painless way to publish your COM+ appli-
cation as an XML Web service, it does lose some of its abilities. For exam-
ple, the COM+ transaction context ends at the XML Web service boundary.
This means that you cannot have a transactional client call the XML Web
service and have the transaction context propagate from the client to the
server. COM+ essentially begins and ends at the XML Web service bound-
ary. Although your COM+ application can take advantage of all of the
COM+ Services available, those services are not available to any calling
clients. As far as any client is concerned, there is nothing COM+ about
the XML Web service.

Included on the CD is the Soap Services example. It contains a simple


class, SoapServicesTest, that defines the COM+ application.

Imports System.EnterpriseServices
Imports System.Reflection

<Assembly: AssemblyKeyFile(“SoapServicesTest.snk”)>
<Assembly: ApplicationName(“COM+ SOAP Services Test”)>
<Assembly: ApplicationActivation(ActivationOption.Server,
SoapVRoot:="SOAPServicesTest”)>

Public Class SoapServicesTest


Inherits ServicedComponent
C2161587x.fm Page 463 Friday, November 16, 2001 9:08 AM

Chapter 21 Upgrading Distributed Applications 463

Public Sub New()


MyBase.New()
Debug.WriteLine(“SOAPServicesTest::New()”)
End Sub

Public Function HelloWorld() As String


Return “Hello World”
End Function
End Class

When this class is registered (by using RegSvcs.exe or loading it via


another process), the COM+ self-registration process will create the IIS virtual
directory and build all the necessary support files for an XML Web service,
including the inspection page (accessible through a Web browser).

Note Windows XP will require Service Pack 1 before the included


sample application will function as expected. At the time of this writing,
the SOAP Services attribute is not supported for Windows 2000. Sup-
port exists only for Windows XP and the Windows .NET server product
family.

COM+ Application Proxies in .NET


Using COM+ application proxies has always been an easy way to implement
DCOM in your applications. Such proxies enable you to create remote compo-
nents that you can deploy and program against as though the components
resided on the local machine. COM+ allows you to register your component on
a server in a COM+ application and generate an export package that can be reg-
istered on virtually any client (domain security issues aside). As a result, you
can avoid the whole issue of creating COM proxy stub packages and imple-
menting custom object marshaling—COM+ does it all for you.
You will be relieved to discover that nothing has changed in Visual Basic
.NET. After a ServicedComponent is registered on the server, it is possible to cre-
ate an export package, deploy it to the client, and use it as you always have.
You will need the assembly containing the class and interface definitions on the
client (unless you want to everything to be late bound), but COM+ will still mar-
shal the objects through DCOM for you. There really isn’t a lot more that needs
to be said here. It just works. Create your export package, register it on the cli-
ent, and you’re done.
C2161587x.fm Page 464 Friday, November 16, 2001 9:08 AM

464 Part IV Techniques for Adding Value

Note There is a caveat to the “just works” mantra for COM+ proxy
applications. It turns out that there are some platform requirements for
this to work correctly. Because of problems in Windows 2000, support
for .NET-based application proxies will not be available until Service
Pack 3 has been released. At the time of this writing, Service Pack 2 is
the most recent available. Application proxies will work just fine on Win-
dows XP and the Windows .NET server product family.

Conclusion

At this point you should have at least a basic understanding of how to imple-
ment the core distributed technologies and where the relative strengths of
remoting and XML Web services lie. Traditionally, developing distributed appli-
cations 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 proto-
col 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.
Z01A61587x.fm Page 465 Friday, November 16, 2001 9:38 AM

Part V

Appendixes
A Object Mapping Reference 467
B Function Mapping Reference 515
Z01A61587x.fm Page 466 Friday, November 16, 2001 9:38 AM
Z01A61587x.fm Page 467 Friday, November 16, 2001 9:38 AM

Appendix A

Object Mapping Reference


This appendix shows the property, method, and event mappings from
Microsoft Visual Basic 6 to Visual Basic .NET for common objects. The Upgrade
Wizard uses these mappings during the upgrade.
Each section covers one Visual Basic 6 object and begins by briefly stating
what object it maps to in Visual Basic .NET. The table in each section then gives
the individual mappings for each property, method, and event. If the Visual
Basic .NET column indicates “No mapping,” it means that there is no one-to-
one mapping, and therefore the Upgrade Wizard will be unable to move the
property, method, or event to Visual Basic .NET.
For most objects, the type of each element remains the same after
upgrade, and any exceptions are indicated within the table. With the App
object, however, many element types have not been preserved. To make the
distinction clear, the table for the App object has an additional column indicat-
ing the new type.

App
No direct equivalent to upgrade to. Individual properties and methods are
upgraded as shown in the table.

Visual Basic 6 Type Visual Basic .NET Type


Comments Property System.Diagnostics.FileVersion- Method
Info.GetVersionInfo(System.Reflec-
tion.Assembly.GetExecutingAssembly.-
Location).Comments
CompanyName Property System.Diagnostics.FileVersion- Method
Info.GetVersionInfo(System.Reflec-
tion.Assembly.GetExecutingAssembly.L
ocation).CompanyName
EXEName Property VB6.GetEXEName Method
(continued)

467
Z01A61587x.fm Page 468 Friday, November 16, 2001 9:38 AM

468 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET Type


FileDescription Property System.Diagnostics.FileVersion- Method
Info.GetVersionInfo(System.Reflec-
tion.Assembly.GetExecutingAssembly.-
Location).FileDescription
HelpFile Property No mapping
HInstance Property VB6.GetHInstance Method
LegalCopyright Property System.Diagnostics.FileVersion- Method
Info.GetVersionInfo(System.Reflec-
tion.Assembly.GetExecutingAssembly.-
Location).LegalCopyright
LegalTrademarks Property System.Diagnostics.FileVersion- Method
Info.GetVersionInfo(System.Reflec-
tion.Assembly.GetExecutingAssembly.-
Location).LegalTrademarks
LogEvent Method No mapping
LogMode Property No mapping
LogPath Property No mapping
Major Property System.Diagnostics.FileVersion- Method
Info.GetVersionInfo(System.Reflec-
tion.Assembly.GetExecutingAssembly.-
Location).FileMajorPart
Minor Property System.Diagnostics.FileVersion- Method
Info.GetVersionInfo(System.Reflec-
tion.Assembly.GetExecutingAssembly.-
Location).FileMinorPart
NonModalAllowed Property No mapping
OleRequestPendingMsgText Property No mapping
OleRequestPendingMsgTitle Property No mapping
OleRequestPendingTimeout Property No mapping
OleServerBusyMsgText Property No mapping
OleServerBusyMsgTitle Property No mapping
OleServerBusyRaiseError Property No mapping
OleServerBusyTimeout Property No mapping
Path Property VB6.GetPath Method
PrevInstance (Boolean) Property UBound(Diagnostics.Process.GetPro- Expres-
cessesByName(Diagnostics.Process.- sion
GetCurrentProcess.ProcessName)) > 0
Z01A61587x.fm Page 469 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 469

Visual Basic 6 Type Visual Basic .NET Type


ProductName Property System.Diagnostics.FileVersion- Method
Info.GetVersionInfo(System.Reflec-
tion.Assembly.GetExecutingAssembly.-
Location).ProductName
RetainedProject Property No mapping
Revision Property No mapping
StartLogging Method No mapping
StartMode Property No mapping
TaskVisible Property No mapping
ThreadID Property No mapping
Title Property System.Reflection.Assembly.GetExecut- Method
ingAssembly.GetName.Name
No mapping for Set
UnattendedApp Property No mapping

CheckBox
Upgrades to System.Windows.Form.CheckBox.

Visual Basic 6 Type Visual Basic .NET


Alignment Property CheckAlign
Appearance Property FlatStyle
BackColor Property BackColor
Caption Property Text
CausesValidation Property CausesValidation
Click Event CheckStateChanged
Container Property Parent
DataChanged Property No mapping
DataField Property For ADO, used in VB6_AddADODatabinding
method
DataFormat Property No mapping
DataMember Property For ADO, used in VB6_AddADODatabinding
method
DataSource Property For ADO, used in VB6_AddADODatabinding
method
(continued)
Z01A61587x.fm Page 470 Friday, November 16, 2001 9:38 AM

470 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


DisabledPicture Property No mapping
DownPicture Property No mapping
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
GotFocus Event Enter
Height Property Height
HelpContextID Property No mapping
HWnd Property Handle
Index Property Index property of ControlArray object
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyUp Event KeyUp
Left Property Left
LostFocus Event Leave
MaskColor Property No mapping
MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Z01A61587x.fm Page 471 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 471

Visual Basic 6 Type Visual Basic .NET


Move Method SetBounds
Name Property Name
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Picture Property Image
Refresh Method Refresh
RightToLeft Property RightToLeft
SetFocus Method Focus
ShowWhatsThis Method No mapping
Style Property Appearance
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
UseMaskColor Property No mapping
Validate Event Validating
Value Property CheckState
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 472 Friday, November 16, 2001 9:38 AM

472 Part V Appendixes

ComboBox
Upgrades to System.Windows.Forms.ComboBox.

Visual Basic 6 Type Visual Basic .NET


AddItem Method Items.Add, or Items.Insert (if Index parameter exists)
Appearance Property No mapping
BackColor Property BackColor
CausesValidation Property CausesValidation
Change Event TextChanged
Clear Method Items.Clear
Click Event SelectedIndexChanged
Container Property Parent
DataChanged Property No mapping
DataField Property For ADO, used in VB6_AddADODatabinding
method
DataFormat Property No mapping
DataMember Property For ADO, used in VB6_AddADODatabinding
method
DataSource Property For ADO, used in VB6_AddADODatabinding
method
DblClick Event No mapping
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
DropDown Event DropDown
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
Z01A61587x.fm Page 473 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 473

Visual Basic 6 Type Visual Basic .NET


ForeColor Property ForeColor
GotFocus Event Enter
Height Property Height
HelpContextID Property No mapping
HWnd Property Handle
IMEMode Property ImeMode
Index Property Index property of ControlArray object
IntegralHeight Property IntegralHeight
ItemData Property VB6.GetItemData, VB6.SetItemData methods
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyUp Event KeyUp
Left Property Left
List Property VB6.GetItemString, VB6.SetItemString methods
ListCount Property Items.Count
ListIndex Property SelectedIndex
Locked Property No mapping
LostFocus Event Leave
MouseIcon Property No mapping
MousePointer Property Cursor
Move Method SetBounds
Name Property Name
NewIndex Property Return result of Items.Add (if preceding line is
Items.Add)
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragMode Property No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
(continued)
Z01A61587x.fm Page 474 Friday, November 16, 2001 9:38 AM

474 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


Refresh Method Refresh
RemoveItem Method Items.RemoveAt
RightToLeft Property RightToLeft
Scroll Event No mapping
SelLength Property SelectionLength
SelStart Property SelectionStart
SelText Property SelectedText
SetFocus Method Focus
ShowWhatsThis Method No mapping
Sorted Property Sorted
Style Property DropDownStyle
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
Text Property Text
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
TopIndex Property No mapping
Validate Event Validating
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 475 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 475

CommandButton
Upgrades to System.Windows.Forms.Button.

Visual Basic 6 Type Visual Basic .NET


Appearance Property No mapping
BackColor Property BackColor
Cancel Property At design time, Form.CancelButton property
At run time,VB6.GetCancel, VB6.SetCancel methods
Caption Property Text
CausesValidation Property CausesValidation
Click Event Click
Container Property Parent
Default Property At design time, Form.AcceptButton
At run time, VB6.GetDefault, VB6.SetDefault methods
DisabledPicture Property No mapping
DownPicture Property No mapping
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
GotFocus Event Enter
Height Property Height
HelpContextID Property No mapping
hWnd Property Handle
Index Property Index property of ControlArray object
KeyDown Event KeyDown
(continued)
Z01A61587x.fm Page 476 Friday, November 16, 2001 9:38 AM

476 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


KeyPress Event KeyPress
KeyUp Event KeyUp
Left Property Left
LostFocus Event Leave
MaskColor Property No mapping
MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
Name Property Name
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Picture Property Image
Refresh Method Refresh
RightToLeft Property RightToLeft
SetFocus Method Focus
ShowWhatsThis Method No mapping
Style Property No mapping
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
UseMaskColor Property No mapping
Z01A61587x.fm Page 477 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 477

Visual Basic 6 Type Visual Basic .NET


Value Property No mapping for Get
PerformClick method if value is nonzero for Set
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1

DirListBox
Upgrades to Microsoft.VisualBasic.Compatibility.VB6.DirListBox.

Visual Basic 6 Type Visual Basic .NET


Appearance Property Used to determine BorderStyle
BackColor Property BackColor
CausesValidation Property CausesValidation
Change Event Change
Click Event SelectedIndexChanged
Container Property Parent
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
GotFocus Event Enter
(continued)
Z01A61587x.fm Page 478 Friday, November 16, 2001 9:38 AM

478 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


Height Property Height
HelpContextID Property No mapping
HWnd Property Handle
IMEMode Property ImeMode
Index Property Index property of ControlArray object
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyUp Event KeyUp
Left Property Left
List Property DirList
ListCount Property DirListCount
ListIndex Property DirListIndex
LostFocus Event Leave
MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
Name Property Name
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragMode Property No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Path Property Path
Refresh Method Refresh
Scroll Event No mapping
SetFocus Method Focus
Z01A61587x.fm Page 479 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 479

Visual Basic 6 Type Visual Basic .NET


ShowWhatsThis Method No mapping
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
TopIndex Property TopIndex
Validate Event Validating
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1

DriveListBox
Upgrades to Microsoft.VisualBasic.Compatibility.VB6.DriveListBox.

Visual Basic 6 Type Visual Basic .NET


Appearance Property No mapping
BackColor Property BackColor
CausesValidation Property CausesValidation
Change Event SelectedIndexChanged
Container Property Parent
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Drive Property Drive
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
(continued)
Z01A61587x.fm Page 480 Friday, November 16, 2001 9:38 AM

480 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
GotFocus Event Enter
Height Property Height
HelpContextID Property No mapping
HWnd Property Handle
Index Property Index property of ControlArray object
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyUp Event KeyUp
Left Property Left
List Property Items
ListCount Property Items.Count
ListIndex Property SelectedIndex
LostFocus Event Leave
MouseIcon Property No mapping
MousePointer Property Cursor
Move Method SetBounds
Name Property Name
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Refresh Method Refresh
Scroll Event No mapping
Z01A61587x.fm Page 481 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 481

Visual Basic 6 Type Visual Basic .NET


SetFocus Method Focus
ShowWhatsThis Method No mapping
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
TopIndex Property No mapping
Validate Event Validating
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1

FileListBox
Upgrades to Microsoft.VisualBasic.Compatibility.VB6.FileListBox.

Visual Basic 6 Type Visual Basic .NET


Appearance Property Used to determine BorderStyle
Archive Property Archive
BackColor Property BackColor
CausesValidation Property CausesValidation
Click Event SelectedIndexChanged
Container Property Parent
DblClick Event DoubleClick
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
(continued)
Z01A61587x.fm Page 482 Friday, November 16, 2001 9:38 AM

482 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


FileName Property FileName
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
GotFocus Event Enter
Height Property Height
HelpContextID Property No mapping
Hidden Property Hidden
hWnd Property Handle
IMEMode Property ImeMode
Index Property Index property of ControlArray object
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyUp Event KeyUp
Left Property Left
List Property Items
ListCount Property Items.Count
ListIndex Property SelectedIndex
LostFocus Event Leave
MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
MultiSelect Property SelectionMode
Name Property Name
Normal Property Normal
OLECompleteDrag Event No mapping
Z01A61587x.fm Page 483 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 483

Visual Basic 6 Type Visual Basic .NET


OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragMode Property No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Path Property Path
PathChange Event PathChange
Pattern Property Pattern
PatternChange Event PatternChange
ReadOnly Property ReadOnly
Refresh Method Refresh
Scroll Event No mapping
Selected Property GetSelected, SetSelected
SetFocus Method Focus
ShowWhatsThis Method No mapping
System Property System
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
TopIndex Property TopIndex
Validate Event Validating
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 484 Friday, November 16, 2001 9:38 AM

484 Part V Appendixes

Form
Upgrades to System.Windows.Forms.Form.

Visual Basic 6 Type Visual Basic .NET


Activate Event Activated
ActiveControl Property ActiveControl
Appearance Property No mapping
AutoRedraw Property No mapping
BackColor Property BackColor
BorderStyle Property FormBorderStyle
Caption Property Text
Circle Method No mapping
Click Event Click
ClipControls Property No mapping
Cls Method No mapping
ControlBox Property ControlBox
Controls Property Controls
Count Property Controls.Count
CurrentX Property No mapping
CurrentY Property No mapping
DblClick Event DoubleClick
Deactivate Event Deactivate
DragDrop Event No mapping
DragOver Event No mapping
DrawMode Property No mapping
DrawStyle Property No mapping
DrawWidth Property No mapping
Enabled Property Enabled
FillColor Property No mapping
FillStyle Property No mapping
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
Z01A61587x.fm Page 485 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 485

Visual Basic 6 Type Visual Basic .NET


FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontTransparent Property No mapping
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
GotFocus Event GotFocus
HasDC Property No mapping
HDC Property No mapping
Height Property Height
HelpContextID Property No mapping
Hide Method Hide
HWnd Property Handle
Icon Property Icon
Image Property No mapping
Initialize Event Initialize method called from New event
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyPreview Property KeyPreview
KeyUp Event KeyUp
Left Property Left
Line Method No mapping
LinkClose Event No mapping
LinkError Event No mapping
LinkExecute Event No mapping
LinkMode Property No mapping
LinkOpen Event No mapping
LinkTopic Property No mapping
Load Event Load
LostFocus Event LostFocus
MaxButton Property MaximizeBox
MDIChild Property At design time, if value is True, then MDIParent
property is set in the New event
At run time, maps to expression Not f.MDIParent Is
Nothing
MinButton Property MinimizeBox
MouseDown Event MouseDown
(continued)
Z01A61587x.fm Page 486 Friday, November 16, 2001 9:38 AM

486 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
Moveable Property No mapping
Name Property Name
NegotiateMenus Property No mapping
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Paint Event Paint
PaintPicture Method No mapping
Palette Property No mapping
PaletteMode Property No mapping
Picture Property BackgroundImage
Point Method No mapping
PopupMenu Method No mapping
PrintForm Method Not mapped (See Chapter 15 for how to implement
PrintForm.)
PSet Method No mapping
QueryUnload Event Closing
Refresh Method Refresh
Resize Event Resize
RightToLeft Property RightToLeft
Scale Method No mapping
Z01A61587x.fm Page 487 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 487

Visual Basic 6 Type Visual Basic .NET


ScaleHeight Property At run time, x.ClientRectangle.Height method for
Get
ScaleLeft Property No mapping
ScaleMode Property No mapping
ScaleTop Property No mapping
ScaleWidth Property At run time, x.ClientRectangle.Width method for
Get
ScaleX Method No mapping
ScaleY Method No mapping
SetFocus Method Activate
Show Method Show, or
ShowDialog if Modal parameter is vbModal,
VB6.ShowForm method if Modal parameter is a
variable
ShowInTaskbar Property ShowInTaskBar
StartUpPosition Property StartPosition
Tag Property Tag
Terminate Event Terminate method, called from Dispose event
TextHeight Method No mapping
TextWidth Method No mapping
Top Property Top
Unload Event Closed
ValidateControls Method VB6.ValidateControls method
Visible Property Visible
WhatsThisButton Property HelpButton
WhatsThisHelp Property No mapping
WhatsThisMode Method VB6.WhatsThisMode method
Width Property Width
WindowState Property WindowState
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 488 Friday, November 16, 2001 9:38 AM

488 Part V Appendixes

Frame
Upgrades to (1) System.Windows.Forms.Panel, or
(2) System.Windows.Forms.GroupBox if control has a border.

Visual Basic 6 Type Visual Basic .NET


Appearance Property No mapping
BackColor Property BackColor
BorderStyle Property (2) BorderStyle
Caption Property (1) Text
Click Event (1) Click
ClipControls Property No mapping
Container Property Parent
DblClick Event (1) DoubleClick
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
Height Property Height
HelpContextID Property No mapping
hWnd Property Handle
Index Property Index property of ControlArray object
Left Property Left
Z01A61587x.fm Page 489 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 489

Visual Basic 6 Type Visual Basic .NET


MouseDown Event (1) MouseDown
MouseIcon Property No mapping
MouseMove Event (1) MouseMove
MousePointer Property (1) Cursor
MouseUp Event (1) MouseUp
Move Method SetBounds
Name Property Name
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Refresh Method Refresh
RightToLeft Property RightToLeft
ShowWhatsThis Method No mapping
TabIndex Property TabIndex
Tag Property Tag
ToolTipText Property SetToolTip method on ToolTip
component
Top Property Top
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 490 Friday, November 16, 2001 9:38 AM

490 Part V Appendixes

HScrollBar
Upgrades to System.Windows.Forms.HScrollBar.

Visual Basic 6 Type Visual Basic .NET


CausesValidation Property CausesValidation
Change Event Change method called from Scroll event
Container Property Parent
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
GotFocus Event Enter
Height Property Height
HelpContextID Property No mapping
HWnd Property Handle
Index Property Index property of ControlArray object
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyUp Event KeyUp
LargeChange Property At design time, LargeChange
At run time, three lines of code to calculate new
value
Left Property Left
LostFocus Event Leave
Max Property At design time, Maximum
At run time, value calculated from LargeChange
Min Property Minimum
MouseIcon Property No mapping
MousePointer Property Cursor
Move Method SetBounds
Name Property Name
Parent Property FindForm
Refresh Method Refresh
RightToLeft Property RightToLeft
Z01A61587x.fm Page 491 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 491

Visual Basic 6 Type Visual Basic .NET


Scroll Event Scroll event, or
Scroll_renamed method called from Scroll event if
Change event exists
SetFocus Method Focus
ShowWhatsThis Method No mapping
SmallChange Property SmallChange
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
Top Property Top
Validate Event Validating
Value Property Value
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1

Image
Upgrades to System.Windows.Forms.PictureBox.

Visual Basic 6 Type Visual Basic .NET


Appearance Property Used to determine BorderStyle
BorderStyle Property BorderStyle
Click Event Click
Container Property Parent
DataChanged Property No mapping
DataField Property For ADO, used in VB6_AddADODatabinding
method
DataFormat Property No mapping
DataMember Property For ADO, used in VB6_AddADODatabinding
method
DataSource Property For ADO, used in VB6_AddADODatabinding
method
(continued)
Z01A61587x.fm Page 492 Friday, November 16, 2001 9:38 AM

492 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


DblClick Event DoubleClick
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
Height Property Height
Index Property Index property of ControlArray object
Left Property Left
MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
Name Property Name
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragMode Property No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Picture Property Image
Refresh Method Refresh
ShowWhatsThis Method No mapping
Stretch Property SizeMode
Tag Property Tag
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
Z01A61587x.fm Page 493 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 493

Visual Basic 6 Type Visual Basic .NET


Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1

Label
Upgrades to System.Windows.Forms.Label.

Visual Basic 6 Type Visual Basic .NET


Alignment Property TextAlign
Appearance Property Used to determine BorderStyle
AutoSize Property AutoSize
BackColor Property BackColor
BackStyle Property No mapping
BorderStyle Property Used to determine BorderStyle
Caption Property Text
Change Event No mapping
Click Event Click
Container Property Parent
DataChanged Property No mapping
DataField Property For ADO, used in VB6_AddADODatabinding
method
DataFormat Property No mapping
DataMember Property For ADO, used in VB6_AddADODatabinding
method
DataSource Property For ADO, used in VB6_AddADODatabinding
method
DblClick Event DoubleClick
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
(continued)
Z01A61587x.fm Page 494 Friday, November 16, 2001 9:38 AM

494 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


DragOver Event No mapping
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
Height Property Height
Index Property Index property of ControlArray object
Left Property Left
LinkClose Event No mapping
LinkError Event No mapping
LinkExecute Method No mapping
LinkItem Property No mapping
LinkMode Property No mapping
LinkNotify Event No mapping
LinkOpen Event No mapping
LinkPoke Method No mapping
LinkRequest Method No mapping
LinkSend Method No mapping
LinkTimeout Property No mapping
LinkTopic Property No mapping
MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
Name Property Name
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
Z01A61587x.fm Page 495 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 495

Visual Basic 6 Type Visual Basic .NET


OLEDragDrop Event No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Refresh Method Refresh
RightToLeft Property RightToLeft
ShowWhatsThis Method No mapping
TabIndex Property TabIndex
Tag Property Tag
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
UseMnemonic Property UseMnemonic
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
WordWrap Property No mapping
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1

Line
Upgrades to System.Windows.Forms.Label if horizontal or vertical line. Diago-
nal lines are not upgraded.

Visual Basic 6 Type Visual Basic .NET


BorderColor Property BackColor
BorderStyle Property No mapping
BorderWidth Property Width for horizontal lines, or
Height for vertical lines
Container Property Parent
DrawMode Property No mapping
(continued)
Z01A61587x.fm Page 496 Friday, November 16, 2001 9:38 AM

496 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


Index Property Index property of ControlArray object
Name Property Name
Parent Property FindForm
Refresh Method Refresh
Tag Property Tag
Visible Property Visible
X1 Property Used for Location and Size at design time
X2 Property Used for Location and Size at design time
Y1 Property Used for Location and Size at design time
Y2 Property Used for Location and Size at design time
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1

ListBox
Upgrades to (1) System.Windows.Forms.ListBox, or
(2) System.Windows.Forms.CheckedListBox if Style =1.

Visual Basic 6 Type Visual Basic .NET


AddItem Method Items.Add
Items.Insert (if Index parameter exists)
Appearance Property Used to determine BorderStyle
BackColor Property BackColor
CausesValidation Property CausesValidation
Clear Method Items.Clear
Click Event SelectedIndexChanged
Columns Property Used to determine MultiColumn, ColumnWidth
properties
Container Property Parent
DataChanged Property No mapping
DataField Property For ADO, used in VB6_AddADODatabinding
method
DataFormat Property No mapping
DataMember Property For ADO, used in VB6_AddADODatabinding
method
Z01A61587x.fm Page 497 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 497

Visual Basic 6 Type Visual Basic .NET


DataSource Property For ADO, used in VB6_AddADODatabinding
method
DblClick Event DoubleClick
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
GotFocus Event Enter
Height Property Height
HelpContextID Property No mapping
hWnd Property Handle
IMEMode Property ImeMode
Index Property Index property of ControlArray object
IntegralHeight Property IntegralHeight
ItemCheck Event (2) ItemCheck
ItemData Property VB6.GetItemData, VB6.SetItemData methods
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyUp Event KeyUp
Left Property Left
List Property VB6.GetItemString, VB6.SetItemString methods
ListCount Property Items.Count
ListIndex Property SelectedIndex
LostFocus Event Leave
(continued)
Z01A61587x.fm Page 498 Friday, November 16, 2001 9:38 AM

498 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
MultiSelect Property SelectionMode
Name Property Name
NewIndex Property Return result of Items.Add (if preceding line is
Items.Add)
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragMode Property No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Refresh Method Refresh
RemoveItem Method Items.RemoveAt
RightToLeft Property RightToLeft
Scroll Event No mapping
SelCount Property (1) SelectedItems.Count
(2) CheckedIndices.Count
Selected Property (1) GetSelected, SetSelected
(2) GetItemChecked, SetItemChecked
SetFocus Method Focus
ShowWhatsThis Method No mapping
Sorted Property Sorted
Style Property Used to determine control type to upgrade to
TabIndex Property TabIndex
TabStop Property TabStop
Z01A61587x.fm Page 499 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 499

Visual Basic 6 Type Visual Basic .NET


Tag Property Tag
Text Property Text
ToolTipText Property SetToolTip method on ToolTip
component
Top Property Top
TopIndex Property TopIndex
Validate Event Validating
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1

MDIForm
Upgrades to System.Windows.Forms.Form, with IsMDIContainer property set to
True.

Visual Basic 6 Type Visual Basic .NET


Activate Event Activated
ActiveControl Property ActiveControl
ActiveForm Property ActiveMDIChild
Appearance Property No mapping
Arrange Method LayoutMDI
AutoShowChildren Property If True, code is added to child forms in New event
BackColor Property No mapping
Caption Property Text
Click Event No mapping
Controls Property Controls
Count Property Controls.Count
DblClick Event DoubleClick
Deactivate Event Deactivate
(continued)
Z01A61587x.fm Page 500 Friday, November 16, 2001 9:38 AM

500 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


DragDrop Event No mapping
DragOver Event No mapping
Enabled Property Enabled
Height Property Height
HelpContextID Property No mapping
Hide Method Hide
HWnd Property Handle
Icon Property Icon
Initialize Event Initialize method called from New event
Left Property Left
LinkClose Event No mapping
LinkError Event No mapping
LinkExecute Event No mapping
LinkMode Property No mapping
LinkOpen Event No mapping
LinkTopic Property No mapping
Load Event Load
MouseDown Event No mapping
MouseIcon Property No mapping
MouseMove Event No mapping
MousePointer Property Cursor
MouseUp Event No mapping
Move Method SetBounds
Moveable Property No mapping
Name Property Name
NegotiateToolbars Property No mapping
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
Z01A61587x.fm Page 501 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 501

Visual Basic 6 Type Visual Basic .NET


OLESetData Event No mapping
OLEStartDrag Event No mapping
Picture Property No mapping
PopupMenu Method No mapping
QueryUnload Event Closing
Resize Event Resize
RightToLeft Property RightToLeft
ScaleHeight Property At run time, x.ClientRectangle.Height for Get
ScaleWidth Property At run time, x.ClientRectangle.Width for Get
ScrollBars Property No mapping
SetFocus Method Activate
Show Method Show, or
ShowDialog if Modal parameter is vbModal,
VB6.ShowForm method if Modal parameter is a
variable
StartUpPosition Property StartPosition
Tag Property Tag
Terminate Event Terminate method, called from Dispose event
Top Property Top
Unload Event Closed
ValidateControls Method VB6.ValidateControls method
Visible Property Visible
WhatsThisButton Property HelpButton
WhatsThisHelp Property No mapping
WhatsThisMode Method VB6.WhatsThisMode method
Width Property Width
WindowState Property WindowState
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 502 Friday, November 16, 2001 9:38 AM

502 Part V Appendixes

Menu
Upgrades to System.Windows.Forms.MenuItem. A MainMenu control is also
automatically added to the form.

Visual Basic 6 Type Visual Basic .NET


Caption Property Text
Checked Property Checked
Click Event Click
Enabled Property Enabled
HelpContextID Property No mapping
Index Property Index property of ControlArray object
Name Property Name
NegotiatePosition Property No mapping
Parent Property GetMainMenu.GetForm
Shortcut Property Shortcut
Tag Property No mapping
Visible Property Visible
WindowList Property MDIList

OptionButton
Upgrades to System.Windows.Forms.RadioButton.

Visual Basic 6 Type Visual Basic .NET


Alignment Property CheckAlign
Appearance Property FlatStyle
BackColor Property BackColor
Caption Property Text
CausesValidation Property CausesValidation
Click Event CheckedChanged
Container Property Parent
DblClick Event No mapping
DisabledPicture Property No mapping
DownPicture Property No mapping
Z01A61587x.fm Page 503 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 503

Visual Basic 6 Type Visual Basic .NET


Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
GotFocus Event Enter
Height Property Height
HelpContextID Property No mapping
hWnd Property Handle
Index Property Index property of ControlArray object
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyUp Event KeyUp
Left Property Left
LostFocus Event Leave
MaskColor Property No mapping
MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
Name Property Name
OLECompleteDrag Event No mapping
(continued)
Z01A61587x.fm Page 504 Friday, November 16, 2001 9:38 AM

504 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
Picture Property Image
Refresh Method Refresh
RightToLeft Property RightToLeft
SetFocus Method Focus
ShowWhatsThis Method No mapping
Style Property Appearance
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
UseMaskColor Property No mapping
Validate Event Validating
Value Property Checked
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 505 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 505

PictureBox
Upgrades to (1) System.Windows.Forms.PictureBox, or
(2) System.Windows.Forms.Panel if the control has child controls.

Visual Basic 6 Type Visual Basic .NET


Align Property Dock
Appearance Property Used to determine BorderStyle
AutoRedraw Property No mapping
AutoSize Property (1) SizeMode
BackColor Property BackColor
BorderStyle Property Used to determine BorderStyle
CausesValidation Property No mapping
Change Event No mapping
Circle Method No mapping
Click Event Click
ClipControls Property No mapping
Cls Method No mapping
Container Property Parent
CurrentX Property No mapping
CurrentY Property No mapping
DataChanged Property No mapping
DataField Property For ADO, used in VB6_AddADODatabinding
method
DataFormat Property No mapping
DataMember Property For ADO, used in VB6_AddADODatabinding
method
DataSource Property For ADO, used in VB6_AddADODatabinding
method
DblClick Event DoubleClick
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
(continued)
Z01A61587x.fm Page 506 Friday, November 16, 2001 9:38 AM

506 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


DrawMode Property No mapping
DrawStyle Property No mapping
DrawWidth Property No mapping
Enabled Property Enabled
FillColor Property No mapping
FillStyle Property No mapping
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontTransparent Property No mapping
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
GotFocus Event No mapping
HasDC Property No mapping
HDC Property No mapping
Height Property Height
HelpContextID Property No mapping
HWnd Property Handle
Image Property No mapping
IMEMode Property No mapping
Index Property Index property of ControlArray object
KeyDown Event No mapping
KeyPress Event No mapping
KeyUp Event No mapping
Left Property Left
Line Method No mapping
LinkClose Event No mapping
LinkError Event No mapping
LinkExecute Method No mapping
Z01A61587x.fm Page 507 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 507

Visual Basic 6 Type Visual Basic .NET


LinkItem Property No mapping
LinkMode Property No mapping
LinkNotify Event No mapping
LinkOpen Event No mapping
LinkPoke Method No mapping
LinkRequest Method No mapping
LinkSend Method No mapping
LinkTimeout Property No mapping
LinkTopic Property No mapping
LostFocus Event No mapping
MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
Name Property Name
OLECompleteDrag Event No mapping
OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragMode Property No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Paint Event Paint
PaintPicture Method No mapping
Parent Property FindForm
Picture Property (1) Image
(2) BackgroundImage
Point Method No mapping
PSet Method No mapping
(continued)
Z01A61587x.fm Page 508 Friday, November 16, 2001 9:38 AM

508 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


Refresh Method Refresh
Resize Event Resize
RightToLeft Property RightToLeft
Scale Method No mapping
ScaleHeight Property At run time, x.ClientRectangle.Height for Get
ScaleLeft Property No mapping
ScaleMode Property No mapping
ScaleTop Property No mapping
ScaleWidth Property At run time, x.ClientRectangle.Width for Get
ScaleX Method No mapping
ScaleY Method No mapping
SetFocus Method Focus
ShowWhatsThis Method No mapping
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
TextHeight Method No mapping
TextWidth Method No mapping
ToolTipText Property SetToolTip method on ToolTip
component
Top Property Top
Validate Event Validating
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 509 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 509

Shape
Upgrades to System.Windows.Forms.Label if shape is a rectangle or square.

Visual Basic 6 Type Visual Basic .NET


BackColor Property BackColor
BackStyle Property No mapping
BorderColor Property No mapping
BorderStyle Property BorderStyle if value is solid or transparent
BorderWidth Property No mapping
Container Property Parent
DrawMode Property No mapping
FillColor Property BackColor, if FillStyle is Solid
FillStyle Property Used to determine FillColor and BackColor
Height Property Height
Index Property Index property of ControlArray object
Left Property Left
Move Method SetBounds
Name Property Name
Parent Property FindForm
Refresh Method Refresh
Shape Property Used to determine whether control upgrades or not
Tag Property Tag
Top Property Top
Visible Property Visible
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 510 Friday, November 16, 2001 9:38 AM

510 Part V Appendixes

TextBox
Upgrades to System.Windows.Forms.TextBox.

Visual Basic 6 Type Visual Basic .NET


Alignment Property TextAlign
Appearance Property Used to determine BorderStyle
BackColor Property BackColor
BorderStyle Property Used to determine BorderStyle
CausesValidation Property CausesValidation
Change Event TextChanged
Click Event No mapping
Container Property Parent
DataChanged Property No mapping
DataField Property For ADO, used in VB6_AddADODatabinding
method
DataFormat Property No mapping
DataMember Property For ADO, used in VB6_AddADODatabinding
method
DataSource Property For ADO, used in VB6_AddADODatabinding
method
DblClick Event DoubleClick
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
Font Property Font
FontBold Property Font.Bold, VB6.FontChangeBold
FontItalic Property Font.Italic, VB6.FontChangeItalic
FontName Property Font.Name, VB6.FontChangeName
FontSize Property Font.SizeInPoints, VB6.FontChangeSize
FontStrikethru Property Font.StrikeOut, VB6.FontChangeStrikeOut
FontUnderline Property Font.Underline, VB6.FontChangeUnderline
ForeColor Property ForeColor
GotFocus Event Enter
Z01A61587x.fm Page 511 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 511

Visual Basic 6 Type Visual Basic .NET


Height Property Height
HelpContextID Property No mapping
HideSelection Property HideSelection
HWnd Property Handle
IMEMode Property IMEMode
Index Property Index property of ControlArray object
KeyDown Event KeyDown
KeyPress Event KeyPress
KeyUp Event KeyUp
Left Property Left
LinkClose Event No mapping
LinkError Event No mapping
LinkExecute Method No mapping
LinkItem Property No mapping
LinkMode Property No mapping
LinkNotify Event No mapping
LinkOpen Event No mapping
LinkPoke Method No mapping
LinkRequest Method No mapping
LinkSend Method No mapping
LinkTimeout Property No mapping
LinkTopic Property No mapping
Locked Property ReadOnly
LostFocus Event Leave
MaxLength Property MaxLength
MouseDown Event MouseDown
MouseIcon Property No mapping
MouseMove Event MouseMove
MousePointer Property Cursor
MouseUp Event MouseUp
Move Method SetBounds
MultiLine Property MultilLine
Name Property Name
OLECompleteDrag Event No mapping
(continued)
Z01A61587x.fm Page 512 Friday, November 16, 2001 9:38 AM

512 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


OLEDrag Method No mapping
OLEDragDrop Event No mapping
OLEDragMode Property No mapping
OLEDragOver Event No mapping
OLEDropMode Property No mapping
OLEGiveFeedback Event No mapping
OLESetData Event No mapping
OLEStartDrag Event No mapping
Parent Property FindForm
PasswordChar Property PasswordChar
Refresh Method Refresh
RightToLeft Property RightToLeft
ScrollBars Property ScrollBars
SelLength Property SelectionLength
SelStart Property SelectionStart
SelText Property SelectedText
SetFocus Method Focus
ShowWhatsThis Method No mapping
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
Text Property Text
ToolTipText Property SetToolTip method on ToolTip component
Top Property Top
Validate Event Validating
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z01A61587x.fm Page 513 Friday, November 16, 2001 9:38 AM

Appendix A Object Mapping Reference 513

Timer
Upgrades to System.Windows.Forms.Timer.

Visual Basic 6 Type Visual Basic .NET


Enabled Property Enabled
Index Property Index property of ControlArray object
Interval Property Interval
Name Property Name
Parent Property No mapping
Tag Property No mapping
Timer Event Tick

VScrollBar
Upgrades to System.Windows.Forms.VScrollBar

Visual Basic 6 Type Visual Basic .NET


CausesValidation Property CausesValidation
Change Event Change method called from Scroll event
Container Property Parent
Drag Method No mapping
DragDrop Event No mapping
DragIcon Property No mapping
DragMode Property No mapping
DragOver Event No mapping
Enabled Property Enabled
GotFocus Event Enter
Height Property Height
HelpContextID Property No mapping
hWnd Property Handle
Index Property Index property of ControlArray object
KeyDown Event KeyDown
(continued)
Z01A61587x.fm Page 514 Friday, November 16, 2001 9:38 AM

514 Part V Appendixes

Visual Basic 6 Type Visual Basic .NET


KeyPress Event KeyPress
KeyUp Event KeyUp
LargeChange Property LargeChange at design time; three lines of code at
run time to calculate new value
Left Property Left
LostFocus Event Leave
Max Property At design time,Maximum
At run time, value calculated from LargeChange
Min Property Minimum
MouseIcon Property No mapping
MousePointer Property Cursor
Move Method SetBounds
Name Property Name
Parent Property FindForm
Refresh Method Refresh
RightToLeft Property RightToLeft
Scroll Event Scroll event, or Scroll_renamed method called from
Scroll event if Change event exists
SetFocus Method Focus
ShowWhatsThis Method No mapping
SmallChange Property SmallChange
TabIndex Property TabIndex
TabStop Property TabStop
Tag Property Tag
Top Property Top
Validate Event Validating
Value Property Value
Visible Property Visible
WhatsThisHelpID Property No mapping
Width Property Width
ZOrder Method VB6.ZOrder method, or
BringToFront if Position parameter is 0,
SendToBack if Position parameter is 1
Z02B61587x.fm Page 515 Friday, November 16, 2001 9:43 AM

Appendix B

Function Mapping Reference


The following table shows the Microsoft Visual Basic 6 set functions and state-
ments with the equivalent Visual Basic .NET functions. This is the table of map-
pings that the Upgrade Wizard uses when upgrading functions and stantements
to Visual Basic. NET.

Visual Basic 6 Member Type Visual Basic .NET


Abs Function System.Math.Abs
AppActivate Statement AppActivate
Asc Function Asc
AscB Function No mapping
AscW Function AscW
Atn Function System.Math.Atan
Beep Statement Beep
Calendar Property No mapping
CallByName Function CallByName
CBool Function CBool
CByte Function CByte
CCur Function CDec
CDate Function CDate
CDbl Function CDbl
CDec Function CDec
ChDir Method ChDir
ChDrive Method ChDrive
Choose Function Choose
Chr Function Chr
ChrB Function No mapping
ChrW Function ChrW
CInt Function CShort
(continued)

515
Z02B61587x.fm Page 516 Friday, November 16, 2001 9:43 AM

516 Part V Appendixes

Visual Basic 6 Member Type Visual Basic .NET


CLng Function CInt
Close Statement FileClose
Command Function Command
Cos Function System.Math.Cos
CreateObject Function CreateObject
CSng Function CSng
CurDir Function CurDir
CVar Function CObj
CVDate Function CDate
CVErr Function No mapping
Date Property Today
Date$ Property DateString
DateAdd Function DateAdd
DateDiff Function DateDiff
DatePart Function DatePart
DateSerial Function DateSerial
DateValue Function DateValue
Day Function Day
DDB Function DDB
DeleteSetting Statement DeleteSetting
Dir Method Dir
DoEvents Statement System.Windows.Forms.Application.DoEvents
Environ Function Environ
EOF Function EOF
Eqv Operator VB6.Eqv
Err Function Err
Error$ Function ErrorToString
Exp Function System.Math.Exp
FileAttr Function FileAttr
FileCopy Method FileCopy
FileDateTime Function FileDateTime
FileLen Function FileLen
Filter Function Filter
Fix Function Fix
Z02B61587x.fm Page 517 Friday, November 16, 2001 9:43 AM

Appendix B Function Mapping Reference 517

Visual Basic 6 Member Type Visual Basic .NET


Format Function Format
FormatCurrency Function FormatCurrency
FormatDate- Function FormatDateTime
Time
FormatNumber Function FormatNumber
FormatPercent Function FormatPercent
FreeFile Function FreeFile
FV Function FV
Get Statement FileGet
GetAllSettings Function GetAllSettings
GetAttr Function GetAttr
GetObject Function GetObject
GetSetting Function GetSetting
Hex Function Hex
Hour Function Hour
IIf Function IIf
IMEStatus Function No mapping
Imp Operator VB6.Imp
Input # statement Statement Input
Input function Function InputString
InputBox Function InputBox
InStr Function InStr
InStrB Function No mapping
InStrRev Function InStrRev
Int Function Int
IPmt Function IPmt
IRR Function IRR
IsArray Function IsArray
IsDate Function IsDate
IsEmpty Function IsNothing
IsError Function IsError
IsMissing Function IsNothing
IsNothing Function IsNothing
IsNull Function IsDbNull
(continued)
Z02B61587x.fm Page 518 Friday, November 16, 2001 9:43 AM

518 Part V Appendixes

Visual Basic 6 Member Type Visual Basic .NET


IsNumeric Function IsNumeric
IsObject Function IsReference
Join Function Join
Kill Method Kill
LBound Function LBound
LCase Function LCase
Left Function Left
LeftB Function No mapping
Len Function Len
LenB Function No mapping
LineInput Statement LineInput
Loc Function Loc
Lock Statement Lock
LOF Function LOF
Log Function System.Math.Log
LSet Statement LSet (only for left-justifying strings within
strings)
LTrim Function LTrim
Mid Function Mid
Mid Statement Mid
MidB Function No mapping
Minute Function Minute
MIRR Function MIRR
MkDir Function MkDir
Month Function Month
MonthName Function MonthName
MsgBox Function MsgBox
Name Statement Rename
Now Function Now
NPer Function NPer
NPV Function NPV
Oct Function Oct
Open Statement FileOpen
Partition Function Partition
Z02B61587x.fm Page 519 Friday, November 16, 2001 9:43 AM

Appendix B Function Mapping Reference 519

Visual Basic 6 Member Type Visual Basic .NET


Pmt Function Pmt
PPmt Function PPmt
Print Statement Print, PrintLine
Put Statement FilePut
PV Function PV
QBColor Function QBColor
Randomize Function Randomize
Rate Function Rate
Replace Function Replace
Reset Method Reset
RGB Function RGB
Right Function Right
RightB Function No mapping
RmDir Method RmDir
Rnd Function Rnd
Round Function System.Math.Round
RSet Statement RSet
RTrim Function RTrim
SaveSetting Function SaveSetting
Second Function Second
Seek Function Seek
SendKeys Statement System.Windows.Forms.SendKeys.Send
SetAttr Method SetAttr
Sgn Function System.Math.Sign
Shell Function Shell
Sin Function System.Math.Sin
SLN Function SLN
Space Function Space
Spc Function Spc
Split Function Split
Sqr Function System.Math.Sqrt
Str Function Str
StrComp Function StrComp
StrConv Function StrConv
(continued)
Z02B61587x.fm Page 520 Friday, November 16, 2001 9:43 AM

520 Part V Appendixes

Visual Basic 6 Member Type Visual Basic .NET


String Function New String constructor
StrReverse Function StrReverse
Switch Function Switch
SYD Function SYD
Tab Function Tab
Tan Function System.Math.Tan
Time Property TimeOfDay
Time$ Property TimeString
Timer Function Timer
TimeSerial Function TimeSerial
TimeValue Function TimeValue
Trim Function Trim
TypeName Function TypeName
UBound Function UBound
UCase Function UCase
Unlock Statement Unlock
Val Function Val
VarType Function VarType
Weekday Function Weekday
WeekdayName Function WeekdayName
Width Statement FileWidth
Write Statement Write
Year Function Year
Z03I61587x.fm Page 521 Tuesday, November 20, 2001 3:16 PM

521

Index
Symbols and Numbers installing with Visual Basic 6 Setup, 80
<> (angle brackets), 352 mappings for, 412–13
+= (assignment shortcut), 223 no direct .NET replacements for, 286
-= (assignment shortcut), 223 outside Visual Basic environments, 286
. and .. directories, 154 placing on a Visual Basic .NET form, 32
= (operator, testing for Null and Empty), 73 removing dynamically at run time, 334–35
( ) (parentheses) replacing with Windows Forms controls, 403, 405–6
with function calls, 92 subobjects in, 293
indicating parameters not to be set, 299 support for, 285–86
required on subroutine calls, 37 Toolbox references to, 31–32
95 percent automated upgrade, 9–10 types not supported in the Windows Forms
100 percent .NET compatibility, 404 environment, 286–87
100 percent upgrade, 9 upgrade strategy for, 286
upgrading, 137–40
using from .NET, 298–303
A in Visual Basic .NET, 177
About box form, 369 ActiveX Data Objects. See ADO
About menu item, 371 ActiveX designers, 289
Abs function, 141–42 ActiveX DHTML page projects, 48
abstract base class, 36 ActiveX DLL project types, 122, 365, 366
abstraction, 66–69 ActiveX DLLs, 249
Activator class, 455 ActiveX document projects, 48
active form, 373 ActiveX documents, 170
ActiveConnection properties, 425–26 ActiveX EXE project, 272
ActiveForm object, 372–73 ActiveX EXE project types, 122
ActiveMDIChild, 373–74 ActiveX libraries, installing, 80
ActiveX ADO Data controls. See ADO Data control ActiveX MousePointer property, 295–96
ActiveX components ActiveX MSBind object, 137
designing and testing for target environments, 21 ActiveX .NET control, 289
reusing in Visual Basic .NET applications, 121 ActiveX object types, conversion functions for common,
threading of, 29 295
types not supported in the Windows Forms ActiveX portion of an ActiveX .NET control, 289
environment, 286–87 ActiveX ProgressBar control, 406
using from .NET, 298–303 ActiveX types, unique identifiers associated with, 28
ActiveX control library, 412 ActiveX UpDown control. See UpDown control
ActiveX control project type, 122 ActiveX vs. .NET framework, 19–29
ActiveX controls ActiveX wrapper. See Ax wrapper
adding dynamically at run time, 334 “Add a delegate for AddressOf” upgrade issue, 163
compared to Windows Forms controls, 413–16 add-in components for Visual Basic 6, 288–89
copying design-time property settings for, 407–10 Add method
creating in Visual Basic 6, 20 calling for a ComboBox or ListBox, 201
deleting from forms, 410 calling the ToString method, 202
exposing property types to .NET types, 291 Add Project from File menu in Visual Basic 6, 94
extended property settings supported by Visual Basic Add Reference dialog box, 111
6, 408–9 Add Web Reference dialog box, 112
extra DLLs generated for, 405 AddForm function, 280–81
global use of, 404 AddHandler keyword, 36
hosting, 173, 286–89

521
Z03I61587x.fm Page 522 Tuesday, November 20, 2001 3:16 PM

522 AddItem method with COM objects

AddItem method with COM objects, 201–3 App object


“‘AddressOf’ expression cannot be converted” compiler concatenating properties in Visual Basic 6, 367
error, 256–57 upgrading properties and methods of, 467–69
AddressOf keyword, 254–57 App.HelpFile property, 371–72
AddrOfPinnedObject method, 344 AppID class, 310
ADO (Active Data Objects), 25–26, 63, 417 application architecture, 55–57
versions of, 311 application form count, decrementing, 280–81
with XML Web services, 423–25 application lifetime, 278
ADO-based applications, 146–47 application manifest, 399
ADO classes, closing out, 449 application proxies. See COM+ application proxies
ADO code application proxy file, 348
integrating into Visual Basic .NET applications, “Application will terminate when Sub Main() finishes”
421–25 upgrade warning, 165
manipulating data objects, 306 Application Wizard projects, 209
ADO data binding, 63, 288, 316–17 ApplicationAccessControl attribute, 353
compared to ADO.NET, 309 ApplicationActivation attribute, 353, 462
upgrade note about, 170 Application.Exit function, 280, 282
ADO Data control (ADODC), 137, 413 ApplicationID attribute, 353
control array of, 318 ApplicationName attribute, 353
in place of the Data control, 128 ApplicationQueuing attribute, 353
ADO data environment, 307, 315–16 Application.Run function, 280
ADO objects, mapping to ADO.NET, 425–29 Application.Run method, 209
ADO Recordset. See also Recordsets applications
splitting into DataSet and DataReader classes, 419 adding value to upgraded, 385–401
substituting SqlDataReader for, 53 analyzing with the Upgrade Wizard, 49
ADODB Stream class, 449 configuring to run Windows XP-style controls,
ADODB Stream object, 447 398–400
ADODC-based data-bound application, 139 deployment with Windows Forms controls, 405
ADODC controls. See ADO Data control finishing in Visual Basic 6 and Visual Basic .NET, 165
ADODC1_MoveComplete event, 311 identifying the main form, 279
adoHelper helper class, 327 integrating ADO code into, 421–25
ADO.NET, 309 leaving in Visual Basic 6, 45–46
concepts of, 417–21 lifetime of, 208
consistency with other .NET Framework classes, 418 limitations of upgraded, 400–401
data access object model, 53, 63 managing the lifetime of, 279–80
data binding, 63 preparing for the upgrade process, 61–76
mapping ADO objects to, 425–29 registering manually, 359
alias types, 301–2 stand-alone, 57
AlocHGlobal function, 261 startup scenarios for, 209
alpha blending, 397–98 supporting Web services in existing, 446–51
Alphabetical view in Property Browser, 32 termination of, 278
ambiguous types, 27 testing after Upgrade Wizard modification, 49
Anchor property upgrade options for, 45–47
setting for a ListBox control, 105 upgrade potential of, 148
of the TreeView control, 394 upgrading distributed, 435–64
AndAlso keyword, 36 upgrading multitiered, 175
angle brackets (<>), 352 upgrading one piece at a time, 188
Ansi keyword, 36 upgrading partially to Visual Basic .NET, 46–47
ANSI strings, marshaling string parameters as, 259 App.Major object property, 367, 369
apartment threading, 29 App.Minor object property, 367, 369
API Declare statements, 370–71 App.Revision object property, 367, 368, 369
API functions. See Windows API functions architecture for remoting, 456–61
Z03I61587x.fm Page 523 Tuesday, November 20, 2001 3:16 PM

ArgumentOutOfRange exception 523

ArgumentOutOfRange exception, 107 attributes, 12, 352–56


Arial font, 210 adding to declare ID attributes, 191, 193
arithmetic operators, shortcuts for, 12 in AssemblyInfo.vb, 93
array bound checks, 127 attaching to components, 352
array declarations, upgrading Visual Basic 6, 243 scopes of, 355
“Array may need to have individual elements upgrading Visual Basic 6, 122–27
initialized” upgrade warning, 164 Auto keyword, 36
array types in Visual Basic .NET, 242 AutoComplete attribute, 353, 356, 363
ArrayIsDynamic parameter with FileGet, 236 automated upgrade, plan for, 9–10
arrays automatic coercions between types, 17–18
declaring with As New, 207 automatic formatting, 18
defining in compatibility with Visual Basic .NET, 75 automatic transaction processing, 349
initializing, 163, 207–8, 247 AutoRedraw property, 329
listing forms in, 389 Ax wrapper, 290
lower bounds of, 164 applying to the TreeView control, 293
nonzero-bound, 301 exposing ActiveX properties, 291
ReDim statement with, 164, 236 renaming ActiveX properties, 297
zero-bound lower dimension, 39–40 Ax wrapper class, 290
“Arrays can’t be declared with New” upgrade warning,
164, 207
“Arrays in structure may need to be initialized” upgrade B
back-end legacy systems, 57
warning, 164
BackColor property, 20
As Any declarations, 253–54
background
As Any parameter, 161
compilation, 18
As New syntax
compiler, 16
upgrade issues with, 207–8
erasing in GDI+, 331
in Visual Basic 6, 74, 75
Task List, 16
in Visual Basic .NET, 74–76
bad constants, 212–13
“As <variable type> was removed from ReDim
“Bad record length”exception, 237
statement” upgrade warning, 164
base class instance, 36
AscB function, 142, 160
base filenames, upgrading two files with the same, 127
.asmx file extension, 440
Basic programming language, 223
ASP pages, 56
behavior differences between Visual Basic 6 and Visual
ASP Web applications, 56
Basic .NET, 154
ASP.NET
bidirectional data transfers, 420
debugging applications on a local machine, 184
binary compatibility
pages, 53
controlling for upgraded components, 195
programming model, 440
enabling in Visual Basic .NET classes, 191–95
Aspnet_wp.exe, 184
ensuring, 187
.aspx files, 440
turning on in a Visual Basic 6 project, 182–83
assemblies, 389
in Visual Basic 6, 189
creating for public interfaces, 456–57
binary files, 233–34
creating strong names for, 356, 357
binary string functions, 142
types contained in, 28
binding. See data binding
Assembly Name, 122
binding collection, 138, 317
Assembly.CreateInstance method, 389
bitmap collection editor, 340–41
AssemblyInfo.vb file, 126–27, 352–53
bitmapCollection property, 340
assembly attributes in, 93
Break method, 113
AssemblyVersion attribute, 369
break mode, 42–43
“Assignment not supported: KeyAscii to a non-zero
breaking compatibility, 6–8, 9
value” upgrade warning, 167
breakpoints, 110
asynchronous call, 206
BuddyControl property, 139
Atn function, 141–42
build errors, handling, 108–9
attached debugger, 113
Z03I61587x.fm Page 524 Tuesday, November 20, 2001 3:16 PM

524 Build property in Windows

Build property in Windows, 369 Clear method in GDI+, 331


Button controls, adding to a Windows Forms project, Click event
104, 105 editing, 95–96
BYOT (bring your own transaction), 349 reassociating with the ProgressBar control, 411–12
ByRef Click procedure, signature of, 90–91
calling convention, 38 Click statement, 271
keyword, 144 client application, required for remoting, 452
parameters, 172–73, 298–99 client for the Architecting for Remoting sample, 460–61
ByteArray, assigning to a string, 163 client process in the Simple Remoting example, 454–56
ByVal client projects, 58
calling convention, 38 client/server applications
keyword, 144 forms-based, 56–57
parameters, 173 upgrading from Visual Basic 6, 178–81
ByValArray attribute, 260, 261 client-side cursor location, 316
ByValTStr attribute, 261 client-side technologies, 5
ClientSize property, 133
Clipboard class in the .NET Framework, 268
C Clipboard code, rewriting, 331–33
C# language, 16–18
“Clipboard method was not upgraded” upgrade issue,
C++ .NET, 46
332, 375
Calendar statement, 160
Clipboard object, 268, 331, 375
callback function, 254
Clipboard text, helper methods for getting and setting,
Cancel property for Unload, 162
375–76
CancelButton property setting, 133
Close method
“Cannot determine array type because it is Nothing”
closing registry keys, 394
exception, 236
releasing physical resources, 205
“Can’t resolve the name of control” upgrade warning,
Close statement, 232
164
closed environments, 21
Caption property, upgrading to Text property, 133, 267
Cls method, 328
Catch keyword, 37
Cls statement, 271
Category view in Property Browser, 32
CObj keyword, 36
CChar keyword, 36
code
CCW (COM callable wrapper), 178
behind forms, 144–45
CD with this book. See companion CD
changes in upgraded, 37–41
Char array, 260
manipulating data objects, 306–7
Char type, 223
preserving during the upgrade process, 118
Charset parameter with StructLayout, 259
size and scope as upgrade factors for an application,
ChDir statement, 232
58
ChDrive statement, 232
thread-safe, 249–51
CheckBox control, 128
types of conversions, 140–42
CheckBox object, 469–71
upgrading, 120
child nodes, adding to a TreeView control, 414
upgrading Visual Basic 6, 140–45
Chr function, 241
Code Editor in the AddButton_Click method, 105
ChrB function, 160
“Code was upgraded to use <function> which may not
Circle method, 328
have the same behavior” upgrade issue, 163
class attributes, 145–46
coding practices, common problematic, 64–76
“Class instancing was changed” upgrade warning, 165
coercion
Class keyword, 36
functions, 36
class-level Boolean variable, 276
mapping with, 141
class modules, 145–46
problems, 172
Class statement, 90
collapsed code regions, 90
class structure of ADO.NET, 418
Collect method, 25, 206
classes, implementing, 82
collection classes, 170, 219–22
ClassesRoot property of the Registry object, 394
collection enumerator, 163
Z03I61587x.fm Page 525 Tuesday, November 20, 2001 3:16 PM

collections 525

collections, 162, 299–300 ComboBox


color objects, 226 for the Northwind Database Viewer application, 451
colors, declaring constants as, 170 upgrading, 128
ColorTranslator.FromOle method, 226 upgrading properties, methods, and events of, 472–74
COM, 271–72, 350 “ComboBox property was not upgraded” design error,
COM attributes 168
adding to ensure binary compatibility, 187 “ComboBox property was not upgraded” upgrade
specifying, 195 issue, 162
COM-based systems, 423 COMException, 296, 299
COM callable wrapper (CCW), 178 Comic Sans MS font, 210
COM clients, communicating with .NET server command-line arguments, 122–23
components, 178 command-line debugger, 113–14
COM components command-line upgrade tool, 99–100
applications with a dependency on, 47 Command object, 418, 425
creating in Visual Basic 6, 178–79, 189 CommandButton, 128, 475–77
.NET clients communicating with, 177 comments
with no .NET equivalent, 176 embedding in C#, 17
reference counting in, 203–4 preserving, 118
removing prior to XCopy deployment, 401 Common Dialog ActiveX control, 413
serializing to an intrinsic data type, 446–47 common framework for all .NET languages, 6
from Visual Basic .NET, 58 common language runtime, 21
“COM expression not supported: Module methods of code running under, 46
COM objects” upgrade issue, 161, 303 default properties no longer supported, 199
COM GUIDs, 194 companion CD
COM interop, 121, 175 Architecting for Remoting sample, 457
compared to COM+ services, 349–50 Bind OLE Picture application, 327
registering a component for, 186 COMPlusClass project, 358
when to use, 177–78 DefaultProperties sample, 200
upgrading applications one piece at a time, 188 DynamicApp.sln sample application, 385–401
in Visual Studio .NET, 176 examples in Chapter 14, 305
COM interop layer, 46, 47 FileReadAndWrite.sln, 390
COM objects FontTest sample application, 210
errors ignored during upgrade in events raised by, FormCollectionClass.vb file, 335
312 Graphics Features project, 394
lifetimes in Visual Basic 6, 204 LateBinding Performance project, 207
wrapping an XML service around existing, 446–47 ListBoxExample sample, 202
COM references, 111 MyButton.vbp project, 131
COM server, creating a .NET client to talk to, 180–81 Northwind.mdb, 435
COM Transaction Integrator (COMTI), 349 OLEDragAndDrop sample application, 214, 217
COM+ PBar sample Visual Basic 6 application, 406–7
attributes specific to, 353–54 PrintForm application, 338
compared to COM, 350 prjADOEvent, 311
in distributed applications, 461 prjADORuntime, 318
requirements in Visual Basic .NET, 350–51 prjFirstUpgrade project, 80
settings, 356 Registry Features project, 392
COM+ application proxies, 440, 461–64 sample applications on, 199
COM+ applications, 347–48 Simple Remoting example, 453
registering, 358–59 Simple Web Service sample, 441
remoting and, 461 SimpleTransaction, 360–61
removing, 359 SOAP Services example, 462
COM+ classes TranslatorClient, 178, 179
sample with attributes, 354–55 TranslatorServer, 178, 179
structure of, 351 Web Services and ADODB sample, 448
COM+ components, 360–63 XPTheme sample, 398
COM+ services, 348–50
Z03I61587x.fm Page 526 Tuesday, November 20, 2001 3:16 PM

526 compatibility

compatibility constants
with COM clients, 194 declaring as colors, 170
reasons for breaking, 6–8 defining to represent a property, 224
compatibility library, 119 proper usage of, 67
functions provided by, 142 upgrading, 161
MSBind .NET class in, 137 in Visual Basic 6, 66
support for control arrays, 135 construction
Compensating Resource Manager, 349 not supported by COM objects in Visual Basic 6, 76
compilation, background, 18 in Visual Basic .NET, 75–76
compilation errors ConstructionEnabled attribute, 353, 355
flagging in real time, 16 constructors
reporting of, 109 calling, 76
compile errors, upgrade issues warning of, 152 defining for user-defined types, 246
Compile tab of a Project Properties dialog box, 182 container controls, 287
compile-time validation Context parameter of MsgBox and InputBox, 167
lost by reliance on the Variant type, 64, 65 context-sensitive Help, reimplementing in Visual Basic
provided by early binding, 207 .NET, 371, 372
compiler context sharing, enabling between COM+ and .NET
background, 16 Framework classes, 351
binding of objects and methods, 69 control anchoring, 14, 394
errors compared to run-time errors, 296 control array classes, 135
upgrading Visual Basic 6 settings, 127 “Control array element 1 doesn’t exist” error, 275
compiling Visual Basic .NET compared to Visual Basic control arrays, 135–36, 318
6, 106 control flow, 227–32
complete upgrade to Visual Basic .NET, 47 “Control was not upgraded” design error, 167
complex binding, 316 controls
complex expressions, simplifying, 114–15 adding dynamically at run time, 333
COMPlusClass project, 358 adding to a Windows Forms project, 104–5
Component Services MMC, 359 adjusting the size of, 394
component tray, 85 benefits of upgrading ActiveX to Windows Forms,
ComponentAccessControl attribute, 353 404–5
componentization of an application, 439 binding to a database image, 327–28
components binding to ADO objects, 316
attaching attributes to, 352–53 displaying all associated properties and methods,
no direct replacement of Visual Basic 6 by .NET, 190 410–11
sharing in closed environments, 21 explicit declaration and allocation of, 133
upgrading one at a time, 175–76 global use of, 404
COMTIIntrinsics attribute, 353 indexing, 334
conditional compilation constant settings, 123–24 with no subobjects, 414
conditional compilation statements normal compared to Windows XP, 400
maintaining after an upgrade, 224 removing by name in Windows Forms, 334
upgrading, 171 removing dynamically at run time, 333
Configuration Properties folder, 124 replacing ActiveX with Windows Forms, 405–6
conn reference, setting to Nothing in Visual Basic 6 vs. resolving the name of, 164
.NET, 205 returning complex structures, 414
Connection object, 418, 425–26 setting properties for, 104–5
connection pool support for OLE drag and drop in Visual Basic 6, 214
getting connections from, 432 upgrading manually, 406–12
increasing churn in, 433 controls collection, 333–35
connections, 432–33. See also database connection Controls.Add, 32
“Constant expression is required” compiler error, 225 conversion, performing a narrowing, 89
constant values, defining, 226, 227 conversion functions
“Constant was not upgraded” upgrade issue, 161 adding after an upgrade, 141
for common ActiveX object types, 295
Z03I61587x.fm Page 527 Tuesday, November 20, 2001 3:16 PM

conversion helper functions, listing of 527

conversion helper functions, listing of, 294–95 data binding, 69, 307, 430–31. See also ADO data
Cooper, Alan, 265 binding; DAO data binding; early binding; late
copy function, replacing an LSet statement, 248 binding; RDO data binding
CopyMemory Windows API function, 261–63 adding and removing, 138–39
CopyTo method, 161 with ADO.NET data sources, 422
CorDbg, 113–14 changing DAO and RDO to ADO, 147
Cos function, 141–42 with code, 378
“Could not find file” global warning, 169 to a control, 430
“Could not load referenced component” global with a data class, 378–79
warning, 169 forcing, 431
“Couldn’t resolve default property of object” upgrade with Web Forms controls, 431
warning, 166 data binding properties, setting at run time, 318–20
Courier font, 210 Data control, 128, 134
Courier New font, 210 data environments, 315
Create Symbolic Debug Info check box, 182 data forms, 367
CreateInstance method, 244–45, 389 data object model of ADO.NET, 417
CreateSubKey method, 393 data persistence, .NET Framework vs. ActiveX, 146
cross-language interoperability, 15–16 data repositories, creating different forms of, 428–29
cryptographic key pair, 357 data types, 238–49
CrystalReports package, 147 standardizing across languages, 6
CShort keyword, 36 upgrading based on size, 234
CtlRefresh method, 411 upgrading to fit, 143–44
CType keyword, 36 DataAdapter, 420, 428
Currency data type, 8, 223 database class in the Architecting for Remoting sample,
current class instance, 36 457–59
CurrentConfig property of the Registry object, 394 database connection, 313–14. See also connections
CurrentUser property, 393 database images, binding controls to, 327–28
CurrentX statement, 271 DataBind, calling on the Web Form DataGrid, 431
cursor, 316 DataEnvironment class, 147
Cursor object, assigning to a numeric type, 296 DataEnvironment designer, 147, 289
CursorToIPicture conversion function, 295 DataField property
custom mouse pointers, 163 controlling simple binding, 317
custom property editors, 342 of a TextBox control, 137, 138
custom tokens, 109–10 DataFormat property, controlling simple binding, 317
Customize Toolbox component picker, 326 DataGrid control
CVErr function, 160 binding an ADO Recordset to, 422, 423
mapping to Visual Basic .NET, 413
DataMember property, controlling simple binding, 317
D DataReader class, 419
DAO (Data Access Objects), 25–26, 63, 310
DataReader object, 427, 428
DAO data binding, 63, 307, 308
DataRelation class, 419
DAO Data control, 308
DataReport designer, 147
DAO-related elements, 147
DataReport Writer, 289
DAODBEngine_definst variable, 310–11
DataSet class, 419, 420
dashboard in DynamicApp, 386
DataSets, 419, 428, 429, 431
data
DataSource interface, 146
creating multiple views of, 429–30
DataSource property
synchronizing access to, 251
controlling simple binding, 317
data access
not supported by Visual Basic .NET controls, 378, 379
code, 310–15
of the QueryDataGrid, 450
forms, 378–79
of a TextBox control, 137, 138
models, 25
DataSourceBehavior property, 146
technologies, 63, 305–21
DataTable class, 419
in Visual Basic, 306–8
Data Access Objects. See DAO
Z03I61587x.fm Page 528 Tuesday, November 20, 2001 3:16 PM

528 DataTables

DataTables, 309 DefaultProperties sample, 200


converting Recordsets to, 422 defaults for Option statements, 89
data binding with, 430 DefInstance property, 277, 278
specifying relationships between, 419 DefInstance shared method, 278
DataViews, 429–30 DefInt, 62
Date data type, 67 DefType statement, 143
date editor, 342 delegate declaration, 257
Date object, 68 Delegate keyword, 36, 257
DbgCLR, 114 delegate type, 254
DBGrid control, 288 DeleteSubKeyTree method, 393
DBNull.Value, 240 DeleteValue method, 394
DDE (Dynamic Data Exchange), 271–73 dependency hierarchy, 58, 94
DDE communication, replacing with COM, 272 deployment with Windows Forms controls, 405
DDE helper class, 272 deprecated features, removing, 62–63
DDE-related API functions, 273 Description attribute, 353
debugger, changes in Visual Basic .NET, 42–43 design errors
Debugger Users group, 184 listing in an upgrade report, 156
DebuggerHidden attribute, 12 listing of, 167–69
debugging design issues, 154
problem-solving techniques, 112–15 design-time licenses, 169
between a Visual Basic 6 client and .NET server, design-time property settings, 407–10
187–88 design-time settings, 134
in Visual Basic .NET, 107–8 design view, forms in, 266
between Visual Basic .NET clients and Visual Basic designers, 147
server, 182–84 destination directory, specifying in Upgrade Wizard,
Debugging property, 123 83–84
Decimal data type, 8, 223 deterministic finalization, 204
“Declaration type not supported” upgrade issue, 161, deterministic lifespan of a variable, 22
243 deterministic memory-management scheme, 205
Declare statements development environment
creating for SendMessage in Visual Basic 6, 253 improvements in, 15–16
creating Windows API functions, 252 Visual Basic 6 vs. Visual Basic .NET, 4, 29–34
declaring multiple for the SendMessage function, Devenv.exe, 29
253–54 DHTML designer, 289
upgrading Integer and Long parameters, 252 DHTML pages, 5
“Declaring a parameter As Any is not supported” DHTMLReport WebClass designer, 147
upgrade issue, 161 Dim...As New statements, 207–8
deep object model, 414 Dir function
“Def statement was removed” upgrade note, 170 . and .. directories not returned by, 165
Default design-time property setting, 133 marking with an upgrade warning, 154
default font in Visual Basic 6, 210 Dir$ function, calling in Visual Basic 6, 387
default form, 277 Dir method, warning about, 369
default form instance, 277–78 DirectCast keyword, 36
default instances as a solution for module methods, 310 directories, reading the contents of, 387–88
Default keyword, 36 directory name, returning from a file path, 388
default properties DirListBox, 128, 477–79
eliminating the use of, 203 disconnected Recordsets, 447
no longer supported in the common language mapping to DataSets, 419
runtime, 199–201 retrieving, 428–29
resolving, 92 Dispose event, 316
run-time differences from, 201–3 Dispose method, 24
stating explicitly, 199–201 calling, 24, 315
support of implicit, 202 destroying graphics objects, 398
Visual Basic 6 compared to .NET, 7
Z03I61587x.fm Page 529 Tuesday, November 20, 2001 3:16 PM

distributed applications 529

distributed applications empty strings


definition of, 436 replacing all instances of App.HelpFile with, 372
upgrading, 435–64 substituting for Null values, 314
distributed technologies, 440 Enabled property, enabling the timer, 167
DLL/Custom Control Library option, 82 English-to-Spanish translator example, 180–81
DLL-exported functions, 302 Enter event, 267
DLL projects enterprise architecture with legacy or distributed
adding in Visual Basic 6, 94 systems, 57
upgrading, 82 enumerations, 212
DLLs enumerator members, defining as constant types, 226
conflicts during installation of Visual Basic 6 EnumWindows API function, 254
applications, 401 Eqv, 62
eliminating conflicts, 358 Erase statement, 171
finding all forms in, 388–89 error-handling block, 11
loading dynamically, 389 error trapping, adding to events, 313
“DoEvents does not return a value” upgrade issue, errors
160–61 in events, 311–13
Donut Shape check box, 396 obtuse, 114
DoTasks method, 363 reducing, 13
Double value, Date data type implemented as, 67–68 undetected by Upgrade Wizard, 172–74
drag-and-drop operations. See also OLE drag and drop; errors, warnings, and issues. See EWIs
Visual Basic drag and drop event code
in Visual Basic 6, 213–15 updating in a Windows Forms control, 411
in Visual Basic .NET, 215–19 upgraded, 90–92
DragDrop event for a form or control, 273 event firing differences, 274–76
drawing, 268 event handlers, 36
drawing code, 270 “Event may fire when form is initialized” upgrade
DrawMode statement, 271 warning, 166, 275, 276
DrawString method, 331 event name collisions, 297
DrawStyle statement, 271 “Event parameter was not upgraded” upgrade issue,
DrawWidth statement, 271 162
DriveListBox, 128, 479–81 event parameters, 39
DTPicker ActiveX control, 413 “Event was not upgraded” upgrade warning, 165
dummy form, creating for ActiveX controls, 334 EventArgs object, 39
dynamic arrays, 234, 235 eventArgs parameter, 90–91
dynamic cursors, 428 EventClass attribute, 354
Dynamic Data Exchange. See DDE events
dynamic data types, 234 associated by name in Visual Basic 6, 38
dynamic properties, 390–92 changing to procedures, 171
dynamic registration, 358–59 declaring public in a separate event interface, 191,
DynamicApp sample application, 385, 386–87 192, 193
design error for new behavior, 167–68
errors in, 311–13
E firing as components are initialized, 166
early binding, 69
mapping the upgrade of individual by object,
advantage of always using, 201
467–515
compared to late, 207
not supported for forms, 268–69
forcing, 206–7
passing in Visual Basic .NET, 90–91
Edit And Continue, not yet available in Visual Basic
renamed for forms, 267
.NET, 107
Visual Basic 6 vs. Visual Basic .NET, 38
embedded objects, 324
eventSender parameter, 90
Empty keyword, 63, 73
EventTrackingEnabled attribute, 354
Z03I61587x.fm Page 530 Tuesday, November 20, 2001 3:16 PM

530 EWIs

EWIs, 151 file path, 388


listing of, 160–71 file printing, full-line vs. partial-line, 233
types of, 152–55 file statements, 232
working with, 159 FileClose function, 232
Excel, loading upgrade report issues into, 157–58 FileCopy statement, 232
exception handling, 11 FileGet function, 232, 237
ExceptionClass attribute, 354 FileGet statement, 236
exceptions FileListBox control, 129
generated by an upgraded project, 86 FileListBox object, 481–83
requiring type conversions, 295–96 filenames
EXE projects, 82 returning an array of, 387
EXE Server projects, 82 returning from file paths, 388
Execute method of the ADO Command object, 426 FileOpen function, 232
ExecuteNonQuery method, 426 FilePut function, 232
ExecuteReader method, 426 FileReadAndWrite sample application, 389–90
ExecuteScalar method, 426 files
ExecuteXmlReader method, 426 not finding during an upgrade, 169
execution path, upgrading the code in, 224 opening for writing, 390
explicit code, 92 reading and writing Visual Basic 6 in .NET, 233
explicit conversions, 141 reading from and writing to, 389–90
explicit declaration and allocation of controls, 133 renaming with internal names, 127–28
explicit dereferencing, 204 FileWidth function, 232
explicit types, Upgrade Wizard and, 64 FillColor statement, 271
explicit variable declaration, 65, 76 FillStyle statement, 271
export package, 463–64 Finalize event, 316
expression shortcuts, 12 Finally keyword, 37
expressions, simplifying complex, 114–15 firehose Recordsets, 419, 427–28
extended design-time settings for ActiveX controls, 407 fix time, estimating, 157–58
extended PMEs, 289 fixed-length array declarations, 246
extended property settings, 407–9 fixed-length arrays, 234
extensibility interfaces, 289 fixed-length strings
not supported in Visual Basic .NET, 40, 223
reading from random-access files, 237
F supporting, 259–60
Fade In button, 395
upgrading in structures, 261
fade out feature for forms, 88
fixed-size arrays
fat-client systems, 56–57
declaring variables of a type containing, 153
features
defining structures with, 164
added in Visual Basic .NET, 6, 223
FixedLengthString class, 260, 261
graphics-related, 394–98
FixedSys font, 210
handled by the Upgrade Wizard but not
flat-model ActiveX controls, 414
recommended for use, 62
flat object model, 414
in the IDE, 15–16
FlatStyle property, 399
not handled by the Upgrade Wizard, 63
floodgate effect, 8
out-of-date, 269
Focus event, 267
remoting, 452
Font events, 165
unique in Visual Basic .NET, 17–18
Font property, 291
file format compatibility of Visual Basic and .NET files,
Font types, converting, 295
233
fonts
file functions, 232–37
supported in Windows Forms, 168–69
mapping from Visual Basic 6 to Visual Basic .NET,
types of, 209
232
upgrading from Visual Basic 6, 210
new in Visual Basic .NET, 387–92
FontTest sample application, 210, 211
file handles, closing, 23
Z03I61587x.fm Page 531 Tuesday, November 20, 2001 3:16 PM

FontToIFont conversion function 531

FontToIFont conversion function, 295 frmBrowser form, 366, 378


ForeColor property, 294 frmDocument form, 367
form background, fading from black to white, 330 frmLogin form, 366, 370
form designer in Ruby, 265 frmMain form, 366, 371–72
form fade out, 88 frmOptions form, 367
“Form method was not upgraded” upgrade issue, 328 frmSplash form, 366, 376–77
Form object, 484–87 fromMain_Closed, renaming Form_Unload to, 374
Form Opacity check box, 395 function calls, processing overhead of, 437
“Form property <formname>ScaleMode is not “function is not supported” upgrade issue, 160
supported” upgrade issue, 162 function parameters, declaring as delegate types, 254
Form.BorderStyle, 171 functional tiers of an application, 439
FormCollectionClass.vb file, 335–36 functions
Form.Load event, 274 commenting out noncritical, 159
Form.Paint event, 329 with different behaviors in Visual Basic .NET, 165
Form_Resize event, 394 exposing as Web services, 189
forms, 274 mapping to equivalent, 141–42, 515–20
adding WebBrowser controls to, 326 null-propagating, 241
in any type of .NET project, 34 overloading, 12
changing the regions of, 396–97 parentheses required with, 92
changing the shape of, 396–97 fusion, 358
code behind, 144–45
creating instances of, 389
differences between Visual Basic 6 and Visual Basic G
garbage-collected variables and objects, 4
.NET, 274–83
garbage collection, 203–5
fading in, 395
forcing, 25, 205–6
finding all in a DLL, 388–89
freeing the developer from memory-management
higher number of issues with, 159
issues, 205
identifying differences in the design of, 154
Garbage Collector (GC), 22
listing in an array, 389
Garbage Collector-managed memory, 261
loading dynamically, 389
Garbage Collector method, 344
making semitransparent, 14
GCHandle structure, 261
opening from Sub Main, 209
GCHandle.Alloc, 262
printing, 337–40
GCHandle.Alloc method, 344
returning the number of open, 160–61
GDI+, 331
as startup objects, 209
graphics drawing surface, 398
upgrading, 120, 128–30, 266
graphics object, 329
forms-based client/server applications, 56–57
object library, 328, 331
forms collection
in Windows Forms, 14
considering the need for, 337
Generate Default Interfaces For All Public Classes
in frmMain_Closed, 374–75
option, 82
implementing in Visual Basic .NET, 335–37
generic objects, 206–7
upgrade issue generated by, 161
generic System.Array type, 301
upgrading, 335–37
Get statement, 232
“Forms collection was not upgraded” upgrade issue,
GetActiveMdiChild property, 373
335
GetComInterfaceForObject function, 261
forms development systems, 14
GetEnumerator method, 220, 221, 222
forms packages, 34–35, 265
getImageFromForm method, 338
Forms3, 35
GetOcx, 296
forward-only Recordsets, 427–28, 449
GetType keyword, 36
Frame control, 129
gizmos, 265
Frame object, 488–89
global binding variable, 320
Framework SDK, debuggers included with, 113, 114
global default instance variable, 310
.frm file, 130–31, 132, 133
global error handler, 11
frmAbout form, 366, 369
Z03I61587x.fm Page 532 Tuesday, November 20, 2001 3:16 PM

532 global form count

global form count, 280–81 HScrollBar, 129, 490–91


Global Issues in the upgrade report, 156 HTTP protocol, 452
global multiuse class attribute, 146 human-readable language, 17
global objects, 145, 268 human-readable names, associated with type
global project setting, 123 identifiers, 28
global single use class attribute, 146
global use of controls, 404
global warnings, 155, 169–70 I
IconToIPicture conversion function, 295
GoSub statement
ID attributes, 191, 193
fixing, 160
IDE (Integrated Development Environment), 29
not supported in Visual Basic .NET, 227
new features in, 15–16
performing a suboperation reusing local variable
redesign of, 4
values, 228
for Visual Basic .NET, 101–2
replacing with ShowDetail, 228–29
IDispatch interface, testing for, 161
upgrading, 228
IDisposable interface, 24
GoSub...Return statement, 63, 223, 227–29
“#If #EndIf block was not upgraded” upgrade note, 171
GotFocus event, 267
#If...#End If precompiler statements, 224
GoTo, 62
IFontToFont conversion function, 295
graphics
#If...Then conditional statements, 123
new features, 394–98
#If...Then directives, 118
Visual Basic 6 compared to Visual Basic .NET, 270–71
Image control, 129
Visual Basic 6 properties and methods related to, 268
Image object
Graphics Effects form in the Graphics Features project,
converting to an IPictureDisp, 293
394
upgrading properties, methods, and events of, 491–93
graphics methods
Image statement, 271
on forms in Visual Basic 6, 270
ImageList ActiveX control, mapping, 413
upgrading, 328–31
images, binding controls to, 327–28
graphics objects, 398
ImageToIPicture conversion function, 295
group file, 85
ImageToIPictureDisp compatibility library function, 293
GUI debuggers, 114
ImageToIPictureDisp conversion function, 295
GUIDs, 194
IMEStatus function, 160
Imp, 62
H implementation data types, 67–68
handle, obtaining, 261 implementation inheritance, 10
Handles clause, 38 Implements keyword, 82
Handles keyword, 36, 91 implicit default properties, 202
HatchBrush class, 271 implicit instantiation, 208
HatchStyle enumeration, 271 implicit object instantiation, 74–76
HDC statement, 271 implicit variable declaration, 65
header information in random-access binary files, 234 Imports keyword, 36
Height property, 268 Imports statement, 27
help in category for upgrading, 148
creating for Visual Basic .NET applications, 125 in-process method invocation, 437
displaying with Windows forms, 371–72 indeterministic lifespan of a variable, 22
help context ID, 125 index values, 416
help settings, 125 indexed property declarations, attribute for, 36
Help topic for the Timer Interval property, 87 indirect approach for creating Visual Basic .NET
HelpFile parameter of MsgBox and InputBox, 167 components, 190
Hex function, null propagation and, 241 indirect replacement model, 190–91
HKEY_CURRENT_USER registry hive, 393 inheritance, 10–11, 52, 223
host process Inherits keyword, 10–11, 36
required for remoting, 452 Inherits statement, 223
in the Simple Remoting example, 453, 454
Z03I61587x.fm Page 533 Tuesday, November 20, 2001 3:16 PM

Initialize event 533

Initialize event, 316 IsNothing function


Initialize method replacing IsMissing, 171
calling, 153–54, 163 upgrading, 140–41
inserting within a structure, 246–47 upgrading IsMissing to, 239
“‘Initialize’ must be called to initialize instances of this IsNull function, 73, 165
structure” upgrade issue, 163 IsObject function, 140–41, 165
InitializeComponent subroutine IsReference function, 165
copying property settings to, 410 issues, loading into a spreadsheet, 157–58
mapping Visual Basic 6 design-time settings to, 131, ItemData property, 168
132, 133 iteration in disconnected Recordsets vs. DataSets, 429
of an upgraded form file, 408–9 IVBGetControl interface, 288
INorthwindDatabase interface in the Architecting for
Remoting sample, 457
Input # statement, mapping to Input function, 232 J
just-in-time activation, 349
Insert Breakpoint from the shortcut menu, 110
JustInTimeActivation attribute, 354
instantiation by remote applications, 348
instrumentation of release builds, 113
Int32 value, converting to a UInt32 value, 294 K
Integer data type, 91 key file, 357
Integer numeric type, 252 key pair files, 357
Integer type, changing to the delegate type, 257 KeyAscii keypress parameter, 167
integer values, representing enumerations, 212 keywords, added to Visual Basic .NET, 35–37
Integer variable Kill statement, mapping, 232
storage size for, 143
upgrading to Short, 143, 144
Visual Basic 6 vs. Visual Basic .NET, 233 L
integrated development environment. See IDE Label control, 129
interface inheritance, 10, 11 Label object, 493–95
Interface keyword, 11, 36 Language property of a form, 15
InterfaceQueuing attribute, 354 language-related upgrade issues, 224–37
interfaces, 456, 457 language statements, 232
interfaces assembly, 456–57 languages
internal data binding manager objects, 288 common environment shared by, 21–22
internal name, renaming files with, 127–28 in the Visual Studio .NET IDE, 29–30
internationalization, 15 large-scale distributed applications, core concepts
interoperability upgrade option, 47 behind, 435–39
interoperation among COM components, 177 late binding, 17, 69, 70–72
Interval property, setting to zero, 167 compared to early, 207
IntPtr, 262 disadvantage of, 201
intrinsic controls, mapping Visual Basic 6 to Windows forcing, 206–7
Forms, 128–30 prohibiting, 13
invalid file format, 169 late-bound variables
“Invalid picture” error, 293 setting to a value, 166
InvalidCastException, 200, 296 upgrading properties of, 149–50
IPictureDisp ActiveX type, 293 LateBinding Performance project, 207
IPictureDispToImage conversion function, 295 LateBoundCopy method, 200, 201
IPictureToImage conversion function, 295 layout and design-time property settings, 130–34
IsDBNull, upgrading IsNull to, 165 LayoutKind parameter, 259
IsEmpty function, 73, 140–41 LBound, 242
ISerializable interface, 442 LCase function, 241–42
ISimpleFrame interface, 139 Leave event, 267
IsMdiContainer property, 282 Left function, null propagation and, 241
IsMissing function, upgrading to IsNothing, 238–40 Left property, setting with Location, 267–68
“IsMissing was changed to IsNothing” upgrade note, Left$ function with Null, 73
171 LeftB function, 142
Z03I61587x.fm Page 534 Tuesday, November 20, 2001 3:16 PM

534 legacy applications, upgrade scenario

legacy applications, upgrade scenario, 47 Long variable


legacy code, cleaning up, 62–63 storage size for, 143
legacy systems, upgrading back-end, 57 upgrading to Integer, 143, 144
library applications, 348 Visual Basic 6 vs. Visual Basic .NET, 233
life cycle loosely coupled applications, 423, 436
of applications, 208 loosely coupled events, 349
managing for applications, 279–80 LostFocus event, 267
of MDI form applications, 282 “Lower bound of array was changed to 0” upgrade
of OLE drag-and-drop operations, 215–16 warning, 164, 242
of the Simple Remoting example, 454 lower bounds, specifying for arrays, 242
of the startup form, 279 “LSet cannot assign one type to another” upgrade issue,
Line control, 129 161, 248
“line couldn’t be parsed” global warning, 170 LSet statement, 63, 247–49
Line Input # statement, 232
Line method, 328
Line object, 495–96 M
“magic” properties, 317
Line statement, 271
main form in an application, 279
lines, drawing in Visual Basic .NET, 329
MainMenu components, dragging onto a form, 30
linked objects, 324
Make Project Group, 96
ListBox
Make tab for Project Properties
adding the contents of a TextBox control to, 202–3
setting command-line arguments under, 122–23
adding to a Windows Forms project, 104, 105
setting conditional compilation constants, 123–24
upgrading, 129
managed code, 46
upgrading properties, methods, and events of, 496–99
managed executables, 46
“ListBox|ComboBox property <control>.NewIndex was
managed providers, 421
not upgraded” upgrade issue, 162
manifest file, 399
“ListBox|ComboBox was not upgraded” design error,
manual drag-and-drop application, 214
168
manual registration of COM+ components, 358, 359
ListBoxExample sample, 202
MarshalAs attribute, 258, 259–60
ListView ActiveX control, 413
MarshalByRefObject class, 453
ListView Columns property, 33
marshaling overhead for a loosely coupled application,
ListView ListItems subobject, 299
436
Load event
marshaling attributes, 164, 258, 259
upgrade events firing before, 276
Masked Edit ActiveX control, 32
Visual Basic .NET events that can occur before, 274
math functions, mapping, 141–42
Load <formname> statement, 160
MBindingCollection, 138
“Load statement is not supported” upgrade issue, 377
MDAC, 311
LoadBalancingSupported attribute, 354
MDI child forms
LoadResStrings function, 163
accessing controls on, 372–73
LoadResStrings method, 379–81
specifying, 282
“LoadResStrings method may need to be replaced”
MDI form applications, lifetime of, 282
upgrade warning, 163
MDI projects, Clipboard in, 375–76
LoadUpgradeReport code, 158
MDIChild property, 282
Localizable property of a form, 15
MDIForm Object
LocalMachine property of the Registry object, 394
adding in Visual Basic 6, 282
Location property
upgrading properties, methods, and events of, 499–501
mapping Left and Top settings to, 133
MDIParent property, 282
setting, 267–68
member functions of the ServicedComponent class,
Lock statement, mapping, 232
351–52
Log method, 113
memory address
logical separation of code, 439
obtaining to a string, 262
login dialog box, adding to an application, 370
returning, 343–45
Long numeric type, 252
memory leaks, 205
memory management, 22–25
Z03I61587x.fm Page 535 Tuesday, November 20, 2001 3:16 PM

Menu Editor 535

Menu Editor, 30–31 MSBind.dll COM library, 307


Menu object, upgrading properties and events of, 502 MSIL, 22
menus, inserting, 30 “MTS/COM+ objects were not upgraded” global
metadata, 352 warning, 170
method invocation, 437–38 MTS pooling, 29
methods multiple document interface (MDI) child forms, 171
declaring public in an interface, 191, 192, 193 multithreaded components, 29
design error for new behavior, 167–68 multithreaded environments, 249
mapping the upgrade of individual by object, multithreading, 13, 223
467–514 multitiered applications, 175
mapping Visual Basic 6 to equivalent .NET functions, multiuse class attribute, 146
515–20 MustInherit keyword, 36
not supported for forms, 268–69 MustOverride keyword, 36
renamed for forms, 267 MustRunInClientContext attribute, 354
Microsoft Access databases, cursor location with, 316 MyBase keyword, 36
Microsoft Data Access Components (MDAC), 311 MyButton.vbp project, 131
Microsoft Intermediate Language (MSIL), 22 MyClass keyword, 36
Microsoft Sans Serif font, 210
Microsoft Visual Studio .NET. See Visual Studio .NET
Microsoft Web Browser control, 130 N
name collisions, 297
Microsoft Windows XP user interface, controls in,
Name property of a Windows Forms control, 410
398–400
named constants
Microsoft.VisualBasic.Compatibility.Data library, 307
creating a set of, 212
Microsoft.VisualBasic.Compatibility.VB6.DirListBox, 477
readability of, 66
Microsoft.VisualBasic.Compatibility.VB6.DriveListBox,
names, associated with type identifiers, 28
479
Namespace keyword, 36
Microsoft.VisualBasic.Compatibility.VB6.FileListBox,
namespaces, 27
481
importing, 27
Mid function, null propagation and, 241
remoting requirement for the client and server class
Mid$ function with Null, 73
to reside in the same, 455–56
MidB function, 142, 160
narrowing conversion, 89
middle tier, replacing with XML Web services, 423
native code, 46
“<Missing>“ parameter value, 240
native language statements, mapping to, 140–41
missing value, 238
Navigate method with WebBrowser ActiveX controls,
MkDir statement, mapping, 232
325
mnuHelpAbout_Click event, 371
negative lower bounds in array declarations, 242–43,
mnuHelpSearchForHelpOn_Click event handler, 372
243–44
mnuViewWebBrowser_Click event in frmMain, 378
nested scopes, 41
Modern font, 210
.NET clients
Module keyword, 36
communicating with COM components, 177
module methods, 309
creating to talk to COM servers, 180–81
code using, 161
.NET collections, 299–300
not supported in Visual Basic .NET, 302–3
.NET components, 20
Module1 module, 367, 379
adding features to upgraded, 187
MonthView ActiveX control, 413
COM applications communicating with, 178
mouse pointer constants, 66
exposing to a Visual Basic 6 client, 184–87
mouse pointers, 163
registering for COM, 178
MousePointer property of an ActiveX control, 295–96
from Visual Basic 6, 58
MS Sans Serif font, 210
.NET Framework, 13, 21–22. See also Visual Basic .NET
MS Serif font, 210
accessing the registry, 392
Msado15.dll, 311
vs. ActiveX, 19–29
MSBind ActiveX component, 288
COM+ services, 349
MSBind collection, 138
elements of, 22–29
MSBind .NET binding collection, 138
file methods, 389
MSBind .NET class, 137
Z03I61587x.fm Page 536 Tuesday, November 20, 2001 3:16 PM

536 .NET Framework

.NET Framework (continued) Nothing state, 238


printing model in, 339 NotInheritable keyword, 36
types of DataAdapters, 420 NotOverridable keyword, 36
UpDown control, 288 Null, 63, 165, 240
wrapping the COM component in a runtime callable Null keyword, 73
wrapper (RCW), 177 Null propagation, 165, 241
.NET GDI+ object library, 328, 331 Null values
.NET object type, 292 avoiding upgrade errors associated with, 314
.NET part of an ActiveX .NET control, 289 coercing to an empty string, 73
.NET platform, 4, 6, 8–9 propagation of, 73
.NET references, 111 NullReferenceException at run time, 208
.NET remoting system. See remoting numeric key indexes for collections, 162
.NET runtime numeric-to-object conversion function, 226
instructions, 22 numeric values, passing to Windows API functions, 253
memory management by, 22 NumericUpDown controls, 140
pointers and, 62
.NET server, exposing to COM, 186
.NET serviced components, working with COM clients, O
object construction, 349
364
“<object> could not be resolved because it was within
.NET types
the generic namespace” upgrade issue, 162
assigning to ActiveX types, 296
object instantiation, 74
converting to another .NET type, 295
“Object may not be destroyed until it is garbage
converting to the equivalent ActiveX type, 292–93
collected” upgrade note, 171
.NET version of the ADODC control, 137
object-oriented approach, to adding or navigating a
.NET Windows form as a control host environment, 286
control’s subobjects, 414
.NET Windows Tab control, 287
object pooling, 349
New, declaring arrays of classes with, 164
object properties, direct editing of, 33
New event, moving the ADO Initialize procedure into,
Object property of an ActiveX control, 297
316
“Object reference not set to an instance of an object”
New Project dialog box, 102, 103, 365–66
NullReferenceException, 247
New statement
Object type
different behavior in Visual Basic .NET, 172
mapping to a .NET object type, 292
in Visual Basic 6, 7
in the .NET Framework, 40
NewEnum property, commenting out, 220, 221, 222
upgrading Variant to, 143–44
“NewEnum property was commented out” upgrade
Variant-like behavior of, 238
note, 170
“object was not upgraded” upgrade issue, 161
NewIndex property of a ComboBox or Listbox control,
ObjectPooling attribute, 354
162
objects
no-drop icon, 273, 274
assigning in Visual Basic 6, 7
nodes, adding in a bottom-up manner, 415
ensuring the destruction of, 205–6
non-TrueType fonts, 210
explicitly declaring, 76
nonconstant expressions, 224–25
forcing to release resources, 24
nonconstant values, 226–27
generic, 206–7
nondeterministic memory-management scheme, 205
in the .NET Framework, 268–69
nonvisual controls, displaying in the component tray,
mapping references from Visual Basic 6 to Visual
85
Basic .NET, 467–514
nonzero-based arrays, 223
preventing the unintentional use of, 456
nonzero-bound arrays, 301
redrawing, 329
nonzero lower bounds, 242
setting to Nothing, 171
normal controls, compared to Windows XP controls,
“Objects of type vbDataObject are not supported”
400
upgrade issue, 161
Northwind Microsoft Access database, 305, 435
ObjPtr function, 63, 261
Northwind Viewer client application, 448, 450–51
eliminating, 343–45
NorthwindDatabase XML Web service, 448–49
not supported in Visual Basic .NET, 261–63
Nothing, setting objects to, 171
upgrade issue generated by, 160
Z03I61587x.fm Page 537 Tuesday, November 20, 2001 3:16 PM

obsolete features, removing from Visual Basic 6 applications 537

obsolete features, removing from Visual Basic 6 overhead


applications, 62–63 for a loosely coupled application, 436
obtuse errors, 114 in method invocation, 437–38
OCX controls, 403. See also ActiveX controls overloaded functions, 12
OcxState setting, deleting from an ActiveX control, 408 Overloads keyword, 36, 223
OffSetOf function, 261 Overridable keyword, 36
OLE Container control Overrides keyword, 37
with linked object, 324
replacing, 323–28
OLE control, 130 P
packages, 289
OLE control extension. See OCX controls
paint code, debugging in Visual Basic .NET, 43
OLE drag and drop, 213, 273
Paint event
OLE objects, binding controls to, 324
in GDI+, 330
OLE_ types, 301–2
in Visual Basic .NET, 270
OLE_COLOR property, 291
PaintImage method, 331
OleDb namespace, 425
painting, not allowed in break mode, 42–43
OleDb providers, 421, 432
painting functions, replacing, 328–31
OleDbCommand class, 426
PaintPicture method, 328
OleDbConnection object, 421
PaintPicture statement, 271
OleDbDataAdapter method, 420, 422, 450
Panels, upgrading PictureBoxes to, 162
OleDbDataAdapter.Fill method, 422
“ParamArray was changed from ByRef to ByVal”
OLEDrag event, 165
upgrade warning, 164
OLEDrag method, 152
parameter initialization values, setting to “<Missing>”,
OLEDragAndDrop sample application, 214–19
240
OLEDragOver event, 165
“parameter is not supported” upgrade warning, 167
OLEDropMode property, 154
parameter types, declaring As Any, 253–54
On...GoSub statement, 160, 231–32
parameterized constructors, 76
On...GoTo statement, 229–30
parameters
opacity property, 395
passing by value in XML Web services, 442
open forms
returning the number of, 160–61 upgrading and qualifying unqualified, 144
parentheses
unloading in SDI and Explorer applications, 374–75
Open Project dialog box, 81 with function calls, 92
indicating parameters not to be set, 299
Open statement, 232
OpenDatabase module method, 309 required on subroutine calls, 37
partial upgrade options, 46–47
operators, mapping Visual Basic 6 to equivalent .NET
functions, 516, 517 PBar sample Visual Basic 6 application, 406–7
Peek method, 390
Option Compare statement, 89
performance
Option Explicit statement, 89, 143
gains with upgraded applications in real-world
Option statements, 89
systems, 55
Option Strict On, 13, 18
Option Strict statement, 36, 89 guidelines for data access, 432–33
as a reason to upgrade, 52–55
optional parameters, 17, 239, 292
OptionButton object, 130, 502–4 persistable classes, 146
physical factors, determining upgrade suitability, 55–58
options, setting in Upgrade Wizard, 82
Options dialog box in Visual Studio .NET, 110 physical resources, releasing, 205
Picture (bitmap) property, exposing as System.Draw-
OrElse keyword, 36
OsWinHelp API, 370–71 ing.Image, 290–91
“Picture will be tiled” design error, 168
out category for upgrading, 120, 148
PictureBox controls, 130, 270
out-of-date features, 269. See also features
PictureBoxes, 505–8
out-of-date technologies, moving to Visual Basic .NET,
binding to fields in a table, 327
269–74
upgrading to Panels, 162
out-of-process method invocation, 437
upgrading with child controls, 168
Z03I61587x.fm Page 538 Tuesday, November 20, 2001 3:16 PM

538 pictures

pictures project names, upgrading Visual Basic 6, 122


painting in GDI+, 331 Project Properties dialog box, 123, 124, 125
printing semitransparently, 397 project references, 111
pixels, graphics coordinates as, 329 project types
platform requirements for COM+ proxy applications, not supported in Visual Basic .NET, 48
464 upgrading, 121–22
PME models, 267 Visual Basic 6 and equivalent Visual Basic .NET, 122
PMEs of controls, 289–90 in Visual Basic .NET, 103
Point statement, 271 projects, 121
pointers, 62 building with debugging symbols turned on, 182–83
in C#, 17 compiling referenced, 170
declaring to functions or class members, 254 creating empty, 365
passing to callback functions in Visual Basic 6, preparing for the upgrade, 61–76
254–55 running, 106
returning to memory, 261 running as quickly as possible, 159
positive lower bounds in array declarations, 242, 243 selecting for upgrading, 50–58
predictability of reference counting in COM, 204 selecting the type of, 82
Print function, 233 properties
Print method, 328 controlling simple binding in Visual Basic 6, 317
Print statement, 271 declaring public in an interface, 191, 192, 193
Print # statement, mapping, 232, 233 default, 199–201
PrintDocument class, 268 design error for new behavior, 167–68
Printer object, 268 making dynamic, 391–92
Printers collection, 161 mapping multiple into a single object property,
PrintForm helper class, 338 267–68
PrintForm method, 337–40 mapping the upgrade of individual by object,
printing functions in .NET, 339 467–514
PrintLine function, 233 mapping Visual Basic 6 to equivalent .NET functions,
private class attribute, 146 515–20
private components, 349 not supported for forms, 268–69
PrivateComponent attribute, 354 passing ByRef to a function in .NET, 298–99
prjADOEvent, 311 renamed for forms, 267
prjADORuntime, 318 resolving default, 92
prjFirstUpgrade project setting for controls, 104–5
creating in Visual Basic 6, 80–81 showing in the Property Browser, 342
upgrading to Visual Basic .NET, 81–84 specifying explicitly, 201
programmable Web sites, 440 summarizing those not upgraded, 134
programming errors, 13 properties, methods, and events. See PMEs of controls
programming practices. See coding practices, common property, method, and event models, 267
problematic Property Browser
ProgressBar control limitations in Visual Basic 6, 340
mapping to Visual Basic .NET, 413 Visual Basic 6 vs. Visual Basic .NET, 32–33
replacing ActiveX with Windows Forms, 406–12 in Visual Basic .NET, 340
project attributes, 122–27 in Visual Studio .NET, 176
project filenames, 127–28 property-centered approach to adding or navigating a
project groups, 94–98 control’s subobjects, 414
project items property-centered model for creating nodes, 415
created by the VB Application Wizard, 366–67 property editors, writing custom, 342
with invalid file formats, 169 “property has a new behavior” design error, 167–68
in the upgrade report, 155, 156 “property is not supported at runtime” upgrade issue,
project-level issues. See Global Issues in the upgrade 162
report “Property is read-only.” COMException, 299
project name and time of upgrade in the upgrade property page editors for every .NET class, 341
report, 155, 156
Z03I61587x.fm Page 539 Tuesday, November 20, 2001 3:16 PM

property pages 539

property pages, 170, 340–42 Recordsets. See also cursor disconnected Recordsets;
property settings, copying from the Visual Basic 6 form firehose Recordsets; forward-only Recordsets
file, 408–10 accessing fields of, 306
“property was not upgraded” design error, 167 binding to .NET controls, 422–23
“Property was not upgraded” upgrade issue, 162 converting to DataTables or DataSets, 423–24
Protected Friend keyword, 37 deserializing from XML, 450
Protected keyword, 37 forms of, 427–29
protocols, supported by remoting, 452 passing between tiers, 425
providers, 421 serializing to and from XML, 423, 424–25
proxy class, 445 with XML Web services, 447–51
PSet statement, 271 red labels
public class, creating an interface for every, 82 on forms, 134
public noncreatable class attribute, 146 placeholder replacing controls, 167
public PMEs, 289 replacing an OLE control during an upgrade, 325
public/private key pair, 357 redesign problems, solutions for common, 323–45
Put statement, mapping, 232 ReDim statement, 164, 173
reference counting in COM, 203–4
referenced projects, compiling, 170
Q references, 111
Query method in NorthwindDatabase, 448, 449
to ActiveX controls in a Toolbox, 31–32
QueryUnload event, 162
not finding during an upgrade, 169
queued components, 349
removing to ActiveX control libraries, 412
References dialog box, 95
R References list, viewing in Visual Basic 6, 302
random-access binary files, header information in, 234 reflection, 388
random-access files, reading fixed-length strings from, Refresh method for the Windows Forms ProgressBar
237 control, 411
random number generator, 81 RegCreateKey API, 392
raster fonts RegDeleteKey API, 392
no longer supported, 168, 209 regions of forms, 396
upgrading, 210, 211 Register For COM Interop attribute, 186
RCW (runtime callable wrapper), 177, 178 registration
RDO (Remote Data Objects), accessing module for COM+ applications, 358–59
methods, 310 of a .NET component for COM interop, 186
RDO connection error, 314 registry, accessing, 392–94
RDO data binding, 307 registry editing form
not supported in Visual Basic .NET, 63 in DynamicApp, 386
upgrading, 308 in the Registry Features project, 392–94
RDO DataControl, 288 registry file in Visual Basic .NET installation, 169
RDO-related elements, 147 registry key, 393
rdoConnection, opening, 313 RegistryFeatures project, 392
RDOrdoEngine_definst variable, 310 RegSvcs.exe utility, 359
Read method, 427 Remote Data Objects. See RDO
ReadFromFile method, 324 remote objects, calling, 456
reading the contents of a file, 390 remote process method invocation, calling overhead,
ReadOnly keyword, 37 437
ReadProperties event, 146 remoting, 440, 452–61
“real” inheritance, 10, 11 architecture for, 456–61
reconstructed Recordsets, 447 COM+ applications and, 461
Recordset filter, setting to an empty string, 314 example of, 453–56
Recordset object, 309 features of, 452
Recordset type Remove Breakpoint from the shortcut menu, 110
changing in Visual Basic 6, 26 Remove button, disabling if no item is selected, 108
RemoveButton_Click event, 105
Z03I61587x.fm Page 540 Tuesday, November 20, 2001 3:16 PM

540 RemoveForm function

RemoveForm function, 280–82 semitransparent form, 395


RemoveHandle keyword, 36 SendMessage Windows API function, 253, 370–71
repainting, not allowed in break mode, 42–43 Serializable attribute, 442
Reset statement, mapping, 232 serialization
resizing, automatic, 394 implementing in XML Web services, 442
resource file, storing a project’s strings in, 379 supported explicitly for Recordset objects, 447
Return keyword, 37 to and from XML, 446
Return statement, 227, 228 Serialize class attribute, 442
revision number formats, Visual Basic 6 vs. Visual Basic server applications, 348
.NET, 127 server class, creating persistable, 146
Revision property in Windows, 369 server process in the Architecting for Remoting sample,
rich-model ActiveX controls, 414 460
rich object model, 414 server project in the Simple Remoting example, 453
Rich TextBox ActiveX control, 413 server-side cursor location, 316
Right$ function with Null, 73 ServicedComponent class, 453
RightB function, 142 calling into COM+ applications, 364
RmDir statement, 232 inheriting from, 350, 351–52
role-based security, 349 SetAbort, calling automatically, 356
Roman font, 210 SetAttr statement, mapping, 232
Root Namespace name, 122 SetCommit, calling automatically, 356
RsToString function, 424 SetFocus event, 267
Ruby, 265–67 SetValue method, 393–94
run time, setting data binding properties at, 318–20 Shadows keyword, 37
run-time assembly binding, 358 Shape control, 130
run-time behavior of Visual Basic .NET vs. Visual Basic Shape object, 509
6, 4 Shared attribute, 245–46
run-time errors, compared to compiler errors, 296 shared method, 277
run-time warnings, 154, 200 Shdocvw.dll, 326
runtime callable wrapper (RCW), 177 Short data type, 233, 234, 252
runtime common language, 21 for 16-bit numbers, 92
upgrading Integer to, 143, 144
Short keyword, 37
S shortcuts for arithmetic operators, 12
Save method on the Recordset object, 447
Show method, 389
Save Project Group from the File menu, 95
ShowDetail subroutine, 228–29
SaveToFile method, 324
simple binding, 317
scalability as a reason to upgrade, 52
simple component wrappers, 293
scale mode, setting, 162
Simple Remoting example, 453
Scale-mode-Constants, 161
Simple Web Service sample, 441
scopes of attributes, 355
SimpleClass type in the Simple Remoting example, 453,
screen flicker, avoiding, 398
454
Screen.MousePointer property, 212
SimpleService.asmx.vb file, 442
Script font, 210
SimpleTransaction, 360–61
SecureMethod attribute, 354
single threading, 29
SecurityRole attribute, 354
single-use class attribute, 146
Select Case statement
single-use classes, changing to public, 165
changing On...GoSub to, 231–32
Size property, 268
upgrading On...GoTo, 230
SizeConst attribute with Marshal As, 260
Select Data Source dialog box, 313
Slider ActiveX control, mapping, 413
selection process for upgrading applications, 50–58
Small Fonts font, 210
self-contained objects, 442
snappy graphics form in DynamicApp, 386
self-documenting comments in C#, 17
Sn.exe tool, 357
self-registration of COM+ components, 358
snippets, 98–99
semitransparency in a Windows application, 14
Z03I61587x.fm Page 541 Tuesday, November 20, 2001 3:16 PM

SOAP 541

SOAP, 423, 441 stepping through Visual Basic 6 code, 184


SOAP applications, XML Web services as a framework stream objects, 390
for developing, 441 strict data types, 70–71
SOAP Services, 349 strict type coercion, 18
attribute, 463 strict type conversion, 36
example, 462 “A string cannot be used to index the <variablename>
limitations of, 462 control collection” upgrade issue, 162
publishing COM+ applications through, 461–63 string comparison mode, 89
SOAP Toolkit, XML Web services as an alternative to, string concatenation, 53–54
441 string concatenation operator, 73
SoapServicesText class in the SOAP Services example, string parameters, marshaling as ANSI strings, 259
462–63 StringBuilder class, 53–54
soft binding, 69, 72, 206, 372–73 StringIsFixedLength parameter, 237
Solution Explorer, 85, 92, 104 strings
solution file, 85 with an odd-number of bytes, 163
solutions, 94, 104 passing to Windows API functions, 253
“Specified cast is not valid” exception, 245 storage of, 345
splash screen, added by VB Application Wizard, 376 StringToRs function, 424
spreadsheet, loading upgrade report issues into, 157–58 strong name, creating for an assembly, 356, 357
SQL Server databases, fast access to, 53 Strong Name tool, 357
SqlClient namespace, 425, 432 strongly typed arrays, 242, 245
SqlClient providers, 421 strongly typed variables, 69, 72
SqlConnection object, 421 StrPtr function, 63, 261–62
SqlDataAdapter, 420 eliminating, 343–45
SqlDataReader class, 53 not supported in Visual Basic .NET, 261–63
Sqrt function, 141–42 upgrade issue generated by, 160
SSTab ActiveX control, 139 StructLayout attribute, 259
mapping to Visual Basic .NET, 413 Structure keyword, 36, 153
upgrading with a .NET Windows TabControl, 287 structure member, declaring as a Char array, 260
on a Windows form, 287 structured error handling, 37
stand-alone applications, 57 structured exception handling, 11, 223
standard drag and drop. See drag-and-drop operations Structure...End Structure, 223, 245
Standard EXE project type, 122, 365, 366 structures, 245–49
standard form layouts, 14 Sub Main, ending applications, 165
standard references, 111 Sub Main event, 377
standardization as a justification for upgrading, 52 Sub Main method as a startup object, 209
startup form “Sub Main won’t be called at component startup”
for MDI forms, 282 upgrade warning, 165
in Visual Basic .NET, 278 Sub New constructor, 277
startup object, ending applications, 165 subobjects, 293
Startup Object list, 279 subroutines, 36–38
startup scenarios for Visual Basic applications, 209 subroutine calls, 37, 38
state of an application, 324 subscopes, 41
statement attributes, 36 synchronization, 349
statement “is not supported” upgrade issue, 160 Synchronization attribute, 354
statements synchronization problems with threads, 249–51
the .NET Framework, 268–69 SyncLock blocks, 251
mapping Visual Basic 6 to equivalent .NET functions, SyncLock keyword, 37, 223, 251
515–20 syntax of Visual Basic .NET vs. Visual Basic 6, 4
new in Visual Basic .NET, 223 System font, 210
static subroutines, 223 System types, 21
StatusBar ActiveX control, 413 System.Array class, 242, 244
StdOle2.tlb, 301–2 System.Array type, 301
Z03I61587x.fm Page 542 Tuesday, November 20, 2001 3:16 PM

542 System.Convert class

System.Convert class, 141, 294 System.Windows.Forms.RadioButton, upgrading


System.Convert methods, 295 OptionButton to, 502
System.DBNull.Value object, 165, 240 System.Windows.Forms.TextBox, 510
System.Diagnostics namespace, 112–13 System.Windows.Forms.Timer, 513
System.Drawing.Color .NET type, 291 System.Windows.Forms.VScrollBar, 513
System.Drawing.Color values, 227
System.Drawing.Color.Red object
assigning to the ForeColor property, 294 T
tab index number, 33, 34
displaying the definition of, 225
Tab Layout Editor, 33–34
System.Drawing.ColorTranslator methods, 295
tab ordering, 33–34
System.Drawing.Font .NET type, 291
TabControl in Windows Forms, 139
System.Drawing.Graphics class, 268
TabStrip ActiveX control, 413
System.Drawing.Graphics object, 270
Task List, 109–10
System.Drawing.Image .NET type, 290–91
background, 16
System.Drawing.Point object, 268
customizing what is displayed in, 109–10
System.Drawing.Printing namespace, 339
filtering for upgrade warnings, 91
System.EnterpriseServices namespace, 350
TCP/IP, 452
System.IntPtr pointer type, 261
technologies as a factor in upgrading, 57–58
System.IO namespace, 387–88
template class, 192
System.IO.Directory.GetFiles method, 387
Terminal font, 210
System.IO.Path.GetDirectoryName method, 388
test page, creating for an XML Web service, 443–44
System.IO.Path.GetFileName method, 388
text
System.Object, ToString method defined by, 202
changing alignment for selected text inside a control,
System.Reflection.Assemble.GetExecutingAssembly
373
method, 388
printing into forms in GDI+, 331
System.Runtime.InteropServices namespace, 258
printing semitransparently, 397
System.Threading.Thread.CurrentThread.Sleep method,
Text property
395
passing, 173
System.UInt32 type, 293, 302
replacing Caption, 267
System.Web.UI namespace, 431
TextBox
System.Windows.Forms.Application.Run method, 209
adding dynamically at run time, 333, 334
System.Windows.Forms.Button, 475
adding to a ListBox control, 202–3
System.Windows.Forms.Checkbox, 469
adding to a Windows Forms project, 104, 105
System.Windows.Forms.CheckedListBox, 496
upgrading, 130
System.Windows.Forms.Clipboard namespace, 331–33
upgrading properties, methods, and events of, 510–12
System.Windows.Forms.ComboBox, 472
TextHeight statement, 271
System.Windows.Forms.Form
TextWidth statement, 271
upgrading Form to, 484
thread-safe code, 249–51
upgrading MDIForm to, 499
thread synchronization block, 37
System.Windows.Forms.GroupBox, upgrading Frame
threading model, 29
to, 488
Throw keyword, 37
System.Windows.Forms.HScrollBar, 490
tightly coupled applications, 436
System.Windows.Forms.Label
time, estimated for fixing upgraded projects, 157–58
upgrading Label to, 493
time of upgrade in the upgrade report, 155, 156
upgrading Line to, 495
timer, enabling on a form, 397
upgrading Shape to, 509
Timer control, 130
System.Windows.Forms.ListBox, 496
Timer Interval property, Help topic for, 87
System.Windows.Forms.MenuItem, upgrading Menu to,
Timer object, upgrading properties and events of, 513
502
“Timer property .Interval cannot have a value of 0”
System.Windows.Forms.Panel
upgrade warning, 167
upgrading Frame to, 488
ToDo comments, 16, 154. See also Upgrade ToDos
upgrading PictureBox to, 505
ToDo EWIs. See Upgrade ToDos
System.Windows.Forms.PictureBox
Toolbar ActiveX control, 413
upgrading Image to, 491
upgrading PictureBox to, 505
Z03I61587x.fm Page 543 Tuesday, November 20, 2001 3:16 PM

Toolbox 543

Toolbox U
Visual Basic 6 vs. Visual Basic .NET, 31–32 UInt32 color value, 294
in the Visual Basic .NET IDE, 103, 104 UInt32 type, 293
ToOle function, 294 “Unable to determine which constant to upgrade to”
ToOle method, 226 upgrade issue, 161
top level scope, moving variable declarations to, 41 “Uncomment and change the following line to return
Top property, setting with Location, 267–68 the collection enumerator”, 163
ToString method, 201–3 underlying data types, 67–68
Trace class, 112–13 underlying integer values for constants, 66–67
Transaction attribute, 354, 355, 360–63 undetected errors, 172–74
transactions, explicitly committing or aborting, 363 undocumented functions, supported in Visual Basic 6,
TranslatorClient sample application, 178, 179 343
TranslatorServer project Unicode keyword, 36
calling the .NET version, 187 unified debugger, 16
creating and running, 178–80 unified framework, 6
upgrading to Visual Basic .NET, 185–86 universal forms package, 35
transparent controls, not supported by Windows Forms, Unload event, 162
129 “Unload <object> was not upgraded” upgrade issue,
transparent labels, 288 162
TreeView control UnloadMode for QueryUnload, 162
adding a single parent and child node to, 414–15 Unlock statement, mapping, 232
Anchor property of, 394 unmanaged code, 46
applying the Ax wrapper to, 293 Unmanaged Code Debugging option, 183
mapping to Visual Basic .NET, 413 unqualified parameters, upgrading and qualifying, 144
structure of, 414 unqualified variable declarations, 143
subobjects exposed by, 293 Update Controls option, 50
troubleshooting link in the upgrade report, 155, 157 UpDown control, 139–40
TrueType fonts, 209, 210, 211 mapping to Visual Basic .NET, 413
Try keyword, 37 upgrading to .NET Framework UpDown control, 288
Try...Catch error trapping, 312–13 upgrade, performing a two-pass, 160
Try...Catch statement, 223 UPGRADE_ comments for code-behind forms, 145
Try...Catch...End Try exception-handling block, 11 upgrade form file, 407
twips, 329 upgrade issues, 152
two-pass upgrade, 160 across forms, controls, and components, 267–74
type aliasing, 301–2 generated by the Upgrade Wizard, 142
type-casting function, 40 listing of, 160–63
type checking, 13 upgrade log, loading into Excel, 157–58
Type class, returning, 36 upgrade notes, 155, 170–71
type conversion functions, 41 upgrade options, 45–47
type conversions, exceptions requiring, 295–96 upgrade plan, developing, 58
type declaration, 36, 74 upgrade problems, 199–222
type identity, 25–28 upgrade process, 9, 10, 79–84
Type keyword, 153 upgrade report, 93–94, 134–35, 155–57
type mismatch assignments in upgraded code, 295–96 generating for problem identification, 160
type names, fully qualifying, 26 parts of, 155, 156–57
Type...End Type, 223, 245 produced by the Upgrade Wizard, 48
types, 238–49, 388 upgrade settings in, 155, 157
converting between in Visual Basic .NET code, 141 Upgrade ToDos, 153–54, 163
listing all in the current application, 388–89 upgrade warnings, 71, 164–67
qualifying ambiguous, 27 upgrade with interoperability option, 47
Z03I61587x.fm Page 544 Tuesday, November 20, 2001 3:16 PM

544 Upgrade Wizard

Upgrade Wizard fixed-length arrays not supported in Visual Basic


approach to upgrading, 117 .NET, 246
choosing a project type and setting options, 82–83 passing to API functions, 258–60
code changes by, 88–92 UserControl project, 122
COM interop and, 176 UserControls, threading issues with upgrading, 249
detecting name conflicts, 297 Users property of the Registry object, 394
dynamic arrays and, 235–36
errors not detected by, 172–74
errors, warnings, and issues generated by, 149–74 V
“Value for property could not be resolved” upgrade
features handled but not recommended, 62
warning, 166
features not handled by, 63
variable declarations
font translation during the upgrade process, 209–11
moving to the top-level scope, 41
goals for, 117–18
not supported in Visual Basic .NET, 161
items not upgraded or only partially supported, 121
variable-length strings, header information associated
methodology of, 119–28
with, 234
role of, 48–49
variable types
running, 119
determining for unqualified variable declarations, 143
specifying a destination directory, 83–84
not explicitly declaring, 64
underlying constant values and, 67
variables
upgrading Visual Basic 6 components, 190–91
declaring explicitly, 89, 143
warnings inserted by, 87, 91
declaring with As New, 207
welcome information, 81–82
deterministic lifespan of, 22
working with an underlying constant, 212–13
indeterministic lifespan of, 22
upgraded applications, testing thoroughly, 49
passing as parameters to functions, 172–73
upgraded code, compared to original Visual Basic 6
redimensioning, 173
code, 88–92
specifying explicitly, 69
upgraded components, controlling the binary
storage of, 345
compatibility of, 195
visibility of those declared in nested scopes, 41
upgraded event code, 90–92
Variant data type, 64, 238
upgraded projects
eliminated in Visual Basic .NET, 40
running, 85
in late binding, 70
specifying destination directories for, 83–84
mapping to a .NET object type, 292
UPGRADE_ISSUE comments, 118, 303
not used in early binding, 69
UPGRADE_ISSUE messages, 142, 152
replaced by Object, 238
_UpgradeReport.htm file, 93, 134
upgrading properties of, 149–51
UpgradeSupport.vb module, 310
upgrading to Object, 143–44
UPGRADE_TODO messages, 142, 153
VARIANT_BOOL parameter, 258
UPGRADE_WARNING message, 150, 151, 154. See also
variants. See Variant data type
EWIs
VarPtr function, 62, 63
upgrading
eliminating, 343–45
evaluating the benefits of, 51–55
upgrade issue generated by, 160
overview of options, 45–59
VB Application Wizard
philosophy of, 117–18
replacing the LoadResStrings method generated by,
physical factors determining, 55–58
379–81
preparing projects for, 61–76
starting from the New Project dialog box, 365–66
reasons for, 10–16
upgrading projects created with, 365–81
selecting projects for, 50–58
.vb file extension, 90, 104, 127
steps involved in, 10
VB Snippet Upgrade add-in, 98–99
using the command line, 99–100
VB6_AddADODataBinding method, 317
“Use of Null/IsNull detected” upgrade warning, 165
VB6.exe, 29
user controls, 145–46, 340
VB6_RemoveADODataBinding method, 317
user-creatable COM+ applications, 347–48
VBFixedArray attribute, 246
user-defined types, 238, 245
VBFixedLengthString class, 40
assigning the contents of one to another, 247
Z03I61587x.fm Page 545 Tuesday, November 20, 2001 3:16 PM

VBFixedString attribute 545

VBFixedString attribute, 260 managing name conflicts, 297


vbNullString, 314 Menu Editor, 30–31
.vbproj files, 127 options for debugging using Visual Studio .NET,
VBUpgrade.exe, 99–100 182–83
VbVarType.vbDataObject, 161 runtime, 21
VBX components, migrating to ActiveX, 285 server, 184–87
VBX controls, 403 snippets, 98
version 1 server component, 189 sourced projects, 9
version 2 component, 189 stepping through code, 184
version number string concatenation in, 53
in Visual Basic 6, 126–27, 367 thread safety in, 249
in Visual Basic .NET, 127, 368 Toolbox, 31–32
versioning upgrading code, 140–45
improving by upgrading ActiveX controls, 404–5 upgrading projects to Visual Basic .NET, 3
properties, 369 Visual Basic 7, 19
visibility of variables declared in nested scopes, 41 Visual Basic Class Builder utility, 219
Visual Basic Visual Basic drag and drop, 269, 273–74
additions to, in Visual Basic .NET, 35–41 Visual Basic extension. See VBX controls
fixing, 7 Visual Basic forms, replaced by Windows Forms, 14
history of, 4–6, 265 Visual Basic .NET, 3. See also .NET Framework
improvements to, 7–8 adding and removing intrinsic controls, 334
language features not recommended for use, 62–63 ADO data environment not supported, 315
modernizing, 8 attributes, 352–56
upgrading from earlier versions of, 49–50 backward compatibility of, 3
Visual Basic 1.0, 4, 5 case sensitivity of, 17
Visual Basic 2 and 3, 4 changes in, 8–10, 17–18
Visual Basic 3, 285 changes in upgraded code, 37–41
Visual Basic 4, 4–5 circles, drawing in, 329
Visual Basic 5, 49–50, 169 COM+ requirements in, 350–51
Visual Basic 5 and 6, 5 COM+ services in, 348–50
Visual Basic 6 compared to C#, 16–18
accessing the registry, 392 compared to Visual Basic 6, 3–6, 8–9, 19–43
add-in components, 288–89 construction in, 75–76
associating events by name, 38 control arrays in, 135
automatic dereferencing, 204 creating a project, 102, 103
changing code for the Visual Studio .NET debugger, debugger, 42
182–84 debugging in, 107–8
as a closed environment, 21 development of, 19
coding practices, good, 64–76 distributed technologies in, 440
collections, 299–300 drag-and-drop operations in, 215–19
compared to Visual Basic .NET, 19–43 enabling binary compatibility in classes, 191–95
components, 190–91 explicit declaration of objects, 76
controls, 213–14 forms package in, 265
data binding in, 316–17 functions, 515–20
debugger, 188 guide to working in, 101–12
differences from Visual Basic .NET, 4 IDE, 101–2, 108–12
drag-and-drop operations, 213–15 installing on the same machine with Visual Basic 6,
features, 57–58 79
form file, 407 lack of support for certain Visual Basic 6 features, 120
graphics model, 57, 58 manipulating the Clipboard, 331–33
installing on the same machine with Visual Basic memory management through garbage collection,
.NET, 79 204
interfaces for ActiveX controls, 288 module methods not supported, 310
leaving applications in, 45–46 new language features, 10–13
Z03I61587x.fm Page 546 Tuesday, November 20, 2001 3:16 PM

546 Visual Basic .NET

Visual Basic .NET (continued) Web Service Tester project, 445–46


no synchronization of threads for components, 249 Web services, 15
null propagation not supported, 241 enabling applications to consume, 112
OLE Container controls not supported, 324 exposing functions as, 189
project types, 103 Web Services and ADODB sample, 448
reasons to upgrade to, 51–55 WebBrowser ActiveX control, 325–26
server, 185–86 WebClass-based projects, 122
setting the startup form in, 278 WebClass designer, 289
subroutines associating with events by including the WebClass project, 122
Handles clause, 38 WebClasses, 5
support for the latest technologies, 270 WebMethod attribute, 441–42
Upgrade Wizard approach to upgrading, 117 WFW (Windows Forms Wrapper), 290
using the Visual Basic 6 Collection object, 300 When keyword, 37
versioning in, 369 While...Wend, 223
Visual Basic .NET compatibility library. See compatibil- Width property, 268
ity library Width # statement, 232
Visual Basic OLE drag and drop. See OLE drag and Win32 declared functions, 57–58
drop Window RAD, 4
Visual Basic Web site, 99, 122 windowless controls, 287–88
Visual C++, 21 Windows API, calling functions from, 251–63
visual inheritance, 10, 14 Windows API functions
Visual Studio 6, 29 calling DDE-related, 273
Visual Studio .NET, 15–16 calling directly, 252
built on COM interop, 176 passing user-defined types to, 258–60
common elements of, 29 Windows application, creating, 104–5
creating a .NET client, 180 Windows application architectures, 56–57
debugger, 42, 182–84, 188 Windows Clipboard, 331–33
IDE, 29 Windows Common controls, licenses for, 169
locking source files during debug sessions, 107 Windows Common Controls TreeView control, 334
Options dialog box, 110 Windows Form Designer Generated Code hidden
Property Browser, 32 block, 131
Tab Layout Editor, 33–34 Windows Forms, 13, 14–15, 34–35
Toolbox, 31–32 DAO and RDO data binding not supported, 307
VScrollBar control, 130 default font, 169
VScrollBar object, 513–14 development features in, 14
equivalents for Visual Basic 6 intrinsic controls, 128
fonts supported in, 168–69
W indexing of controls, 334
Wait parameter of AppActivate, 167
list of ActiveX controls and components not
WaitForPendingFinalizers method, 25, 206
supported, 286–87
walkthrough of the upgrade process, 79–84
NumericUpDown controls, 140
warnings inserted by the Upgrade Wizard, 87, 91
similarities with Ruby, 265–67
weak named assemblies, 28
transparent controls not supported by, 129
Web applications, separate forms package for, 35
windowless controls not supported in, 288
Web Browser control, 130
Windows Forms controls, 403
Web Browser form, created by VB Application Wizard,
binding data to, 137
378
compared to ActiveX controls, 413–16
Web development capabilities in Visual Basic 6, 5
data binding to, 430–31
Web development enhancements in Visual Basic .NET,
inherent advantages in the .NET environment, 404
15
renaming, 410
Web Forms controls, data binding with, 431
replacing ActiveX controls with, 405–6
Web Forms package, 34
table of replacements for ActiveX controls, 412–13
Web Forms presentation layer, 15
updating, 404–5
Web references, 111, 112, 445
Windows Forms project, 104
Z03I61587x.fm Page 547 Tuesday, November 20, 2001 3:16 PM

Windows Forms TabControl 547

Windows Forms TabControl, 139 XML


Windows Forms Wrapper (WFW), 290 over HTTP, 453
Windows versioning, 369 serializing objects to and from, 424
Windows XP-style controls, 398–400 storing values in editable files, 391
WinSock control, 297 support for, 15
wizard. See Upgrade Wizard support for ADO Recordset objects, 447–48
WM_GETTEXTLENGTH message, 253 XML log file, upgrade report based on, 157
WM_SETTEXT message, 253 XML Web service client, 444–46
Word document XML Web service proxy, 450
displayed in an OLE Container control, 324 XML Web services, 440–51
displayed with a WebBrowser control, 326 ADO with, 423–25
wrapper, implementing Web services as a, 446 implementing, 441–46
wrapper class, writing code for controls against, 289 improving legacy systems, 57
Write function, 232 restrictions on parameters and return values for, 442
Write # statement, mapping, 232, 233 supporting in existing applications, 446–51
WriteLine function, 232 testing, 443–44
WriteOnly keyword, 37 XPTheme sample, 398
WriteProperties event, 146

X Z
zero-bound lower dimension for arrays, 39–40
XA interoperability, 349
XCopy deployment, 400–401
Z03I61587x.fm Page 548 Tuesday, November 20, 2001 3:16 PM
IMPORTANT-READ CAREFULLY: This End-User License Agreement (“EULA”) is a legal
agreement between you (either an individual or a single entity) and Microsoft Corporation for the
Microsoft software that accompanies this EULA, which includes computer software and may
include associated media, printed materials, “online” or electronic documentation, and Internet-
based services (“Software”). An amendment or addendum to this EULA may accompany the
Software. YOU AGREE TO BE BOUND BY THE TERMS OF THIS EULA BY INSTALLING,
COPYING, OR OTHERWISE USING THE SOFTWARE. IF YOU DO NOT AGREE, DO NOT
INSTALL, COPY, OR USE THE SOFTWARE.

1. GRANT OF LICENSE. Microsoft grants you the following rights provided that you
comply with all terms and conditions of this EULA:
1.1 Microsoft grants you a personal, nonexclusive, royalty-free license to install and
use the Software for design, development, and testing purposes. You may install and use the
Software on an unlimited number of computers so long as you are the only individual using the
Software.
1.2 Solely with respect to the sample source code contained in the Software (“MS
Samples”), Microsoft also grants you a limited, nonexclusive, royalty-free license to: (a) use and
modify the source code version of those portions of the MS Samples for the sole purposes of
designing, developing, and testing your software product(s), and (b) to reproduce and distribute
the MS Samples, along with any modifications thereof, in object and/or source code form. For
applicable redistribution requirements for MS Samples, see Section 2 below.
2. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS
2.1 If you choose to redistribute the MS Samples, you agree: (i) to distribute the MS
Samples only as a part of a software application product developed by you (“Licensee
Software”); (ii) not to use Microsoft’s name, logo, or trademarks to market the Licensee Software;
(iii) to display your own valid copyright notice which shall be sufficient to protect Microsoft’s
copyright in the MS Samples; (iv) to indemnify, hold harmless, and defend Microsoft from and
against any claims or lawsuits, including attorney’s fees, that arise or result from the use or
distribution of the Licensee Software; (v) not to permit further distribution of the MS Samples by
your end users; and (vi) that Microsoft reserves all rights not expressly granted.
2.2 If you use the MS Samples, then the following also applies. Your license rights to
the MS Samples are conditioned upon your (i) not incorporating Identified Software into, or
combining Identified Software with, the MS Samples or a derivative work thereof; and (ii) not
distributing Identified Software in conjunction with the MS Samples or a derivative work thereof.
“Identified Software” means software which is licensed pursuant to terms that directly or
indirectly (A) create, or purport to create, obligations for Microsoft with respect to the MS
Samples or derivative work thereof or (B) grant, or purport to grant, to any third party any rights
or immunities under Microsoft’s intellectual property or proprietary rights in the MS Samples or
derivative work thereof. Identified Software includes, without limitation, any software that
requires as a condition of its use, modification and/or distribution, that any other software
incorporated into, derived from or distributed with such software must also be (1) disclosed or
distributed in source code form; (2) licensed for the purpose of making derivative works; or (3)
redistributable at no charge.
3. RESERVATION OF RIGHTS AND OWNERSHIP. Microsoft reserves all rights not
expressly granted to you in this EULA. The Software is protected by copyright and other
intellectual property laws and treaties. Microsoft or its suppliers own the title, copyright, and
other intellectual property rights in the Software. The Software is licensed, not sold.
4. LIMITATIONS ON REVERSE ENGINEERING, DECOMPILATION, AND
DISASSEMBLY. You may not reverse engineer, decompile, or disassemble the Software, except
and only to the extent that such activity is expressly permitted by applicable law notwithstanding
this limitation.
5. SUPPORT SERVICES. No technical support will be provided for the Software.
6. LINKS TO THIRD PARTY SITES. You may link to third party sites through the use of
the Software. The third party sites are not under the control of Microsoft, and Microsoft is not
responsible for the contents of any third party sites, any links contained in third party sites, or
any changes or updates to third party sites. Microsoft is not responsible for webcasting or any
other form of transmission received from any third party sites. Microsoft is providing these links
to third party sites to you only as a convenience, and the inclusion of any link does not imply an
endorsement by Microsoft of the third party site.
7. ADDITIONAL SOFTWARE/SERVICES. This EULA applies to updates, supplements,
add-on components, or Internet-based services components, of the Software that Microsoft may
provide to you or make available to you after the date you obtain your initial copy of the
Software, unless we provide other terms along with the update, supplement, add-on component,
or Internet-based services component. Microsoft reserves the right to discontinue any Internet-
based services provided to you or made available to you through the use of the Software.
8. U.S. GOVERNMENT LICENSE RIGHTS. All Software provided to the U.S.
Government pursuant to solicitations issued on or after December 1, 1995 is provided with the
commercial license rights and restrictions described elsewhere herein. All Software provided to
the U.S. Government pursuant to solicitations issued prior to December 1, 1995 is provided with
“Restricted Rights” as provided for in FAR, 48 CFR 52.227-14 (JUNE 1987) or DFAR, 48 CFR
252.227-7013 (OCT 1988), as applicable.
9. EXPORT RESTRICTIONS. You acknowledge that the Software is subject to U.S. export
jurisdiction. You agree to comply with all applicable international and national laws that apply
to the Software, including the U.S. Export Administration Regulations, as well as end-user, end-
use, and destination restrictions issued by U.S. and other governments. For additional
information see <http://www.microsoft.com/exporting/>.
10. SOFTWARE TRANSFER. The initial user of the Software may make a one-time
permanent transfer of this EULA and Software to another end user, provided the initial user
retains no copies of the Software. The transfer may not be an indirect transfer, such as a
consignment. Prior to the transfer, the end user receiving the Software must agree to all the
EULA terms.
11. TERMINATION. Without prejudice to any other rights, Microsoft may terminate this
EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must
destroy all copies of the Software and all of its component parts.
12. DISCLAIMER OF WARRANTIES. TO THE MAXIMUM EXTENT PERMITTED BY
APPLICABLE LAW, MICROSOFT AND ITS SUPPLIERS PROVIDE TO YOU THE
SOFTWARE, AND SUPPORT SERVICES (IF ANY) AS IS AND WITH ALL FAULTS; AND
MICROSOFT AND ITS SUPPLIERS HEREBY DISCLAIM ALL OTHER WARRANTIES AND
CONDITIONS, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT
LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES, DUTIES OR CONDITIONS OF
MERCHANTABILITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF RELIABILITY OR
AVAILABILITY, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF
WORKMANLIKE EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE, ALL
WITH REGARD TO THE SOFTWARE, AND THE PROVISION OF OR FAILURE TO
PROVIDE SUPPORT OR OTHER SERVICES, INFORMATION, SOFTWARE, AND
RELATED CONTENT THROUGH THE SOFTWARE OR OTHERWISE ARISING OUT OF
THE USE OF THE SOFTWARE. ALSO, THERE IS NO WARRANTY OR CONDITION OF
TITLE, QUIET ENJOYMENT, QUIET POSSESSION, CORRESPONDENCE TO
DESCRIPTION OR NON-INFRINGEMENT WITH REGARD TO THE SOFTWARE.
13. EXCLUSION OF INCIDENTAL, CONSEQUENTIAL AND CERTAIN OTHER
DAMAGES. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO
EVENT SHALL MICROSOFT OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL,
INCIDENTAL, PUNITIVE, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER
(INCLUDING, BUT NOT LIMITED TO, DAMAGES FOR LOSS OF PROFITS OR
CONFIDENTIAL OR OTHER INFORMATION, FOR BUSINESS INTERRUPTION, FOR
PERSONAL INJURY, FOR LOSS OF PRIVACY, FOR FAILURE TO MEET ANY DUTY
INCLUDING OF GOOD FAITH OR OF REASONABLE CARE, FOR NEGLIGENCE, AND
FOR ANY OTHER PECUNIARY OR OTHER LOSS WHATSOEVER) ARISING OUT OF OR
IN ANY WAY RELATED TO THE USE OF OR INABILITY TO USE THE PRODUCT, THE
PROVISION OF OR FAILURE TO PROVIDE SUPPORT OR OTHER SERVICES,
INFORMATON, SOFTWARE, AND RELATED CONTENT THROUGH THE PRODUCT OR
OTHERWISE ARISING OUT OF THE USE OF THE PRODUCT, OR OTHERWISE UNDER
OR IN CONNECTION WITH ANY PROVISION OF THIS EULA, EVEN IN THE EVENT OF
THE FAULT, TORT (INCLUDING NEGLIGENCE), MISREPRESENTATION, STRICT
LIABILITY, BREACH OF CONTRACT OR BREACH OF WARRANTY OF MICROSOFT OR
ANY SUPPLIER, AND EVEN IF MICROSOFT OR ANY SUPPLIER HAS BEEN ADVISED OF
THE POSSIBILITY OF SUCH DAMAGES.
14. LIMITATION OF LIABILITY AND REMEDIES. NOTWITHSTANDING ANY
DAMAGES THAT YOU MIGHT INCUR FOR ANY REASON WHATSOEVER (INCLUDING,
WITHOUT LIMITATION, ALL DAMAGES REFERENCED ABOVE AND ALL DIRECT OR
GENERAL DAMAGES IN CONTRACT OR ANYTHING ELSE), THE ENTIRE LIABILITY OF
MICROSOFT AND ANY OF ITS SUPPLIERS UNDER ANY PROVISION OF THIS
SUPPLEMENTAL EULA AND YOUR EXCLUSIVE REMEDY FOR ALL OF THE
FOREGOING SHALL BE LIMITED TO THE GREATER OF THE ACTUAL DAMAGES YOU
INCUR IN REASONABLE RELIANCE ON THE SOFTWARE UP TO THE AMOUNT
ACTUALLY PAID BY YOU FOR THE SOFTWARE OR U.S.$5.00. THE FOREGOING
LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SHALL APPLY TO THE MAXIMUM
EXTENT PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS ITS
ESSENTIAL PURPOSE.
15. APPLICABLE LAW. If you acquired this Software in the United States, this EULA is
governed by the laws of the State of Washington. If you acquired this Software in Canada, unless
expressly prohibited by local law, this EULA is governed by the laws in force in the Province of
Ontario, Canada; and, in respect of any dispute which may arise hereunder, you consent to the
jurisdiction of the federal and provincial courts sitting in Toronto, Ontario. If you acquired this
Software in the Eurpean Union, Iceland, Norway, or Switzerland, then local law applies. If you
acquired this Software in any other country, then local law may apply.
16. ENTIRE AGREEMENT; SEVERABILITY. This EULA (including any addendum or
amendment to this EULA which is included with the Software) are the entire agreement between
you and Microsoft relating to the Software and the support services (if any) and they supersede
all prior or contemporaneous oral or written communications, proposals and representations
with respect to the Software or any other subject matter covered by this EULA. To the extent the
terms of any Microsoft policies or programs for support services conflict with the terms of this
EULA, the terms of this EULA shall control. If any provision of this EULA is held to be void,
invalid, unenforceable or illegal, the other provisions shall continue in full force and effect.

Si vous avez acquis votre logiciel Microsoft au CANADA :


DÉNI DE GARANTIES. Dans la mesure maximale permise par les lois applicables, le Logiciel
et les services de soutien technique (le cas échéant) sont fournis TELS QUELS ET AVEC TOUS
LES DÉFAUTS par Microsoft et ses fournisseurs, lesquels par les présentes dénient toutes
autres garanties et conditions expresses, implicites ou en vertu de la loi, notamment, mais sans
limitation, (le cas échéant) les garanties, devoirs ou conditions implicites de qualité
marchande, d’adaptation à une fin usage particulièere, de fiabilité ou de disponibilité,
d’exactitude ou d’exhaustivité des réponses, des résultats, des efforts déployés selon les règles
de l’art, d’absence de virus et d’absence de négligence, le tout à l’égard du Logiciel et de la
prestation des services de soutien technique ou de l’omission de la ’une telle prestation des
services de soutien technique ou à l’égard de la fourniture ou de l’omission de la fourniture de
tous autres services, renseignements, logiciels, et contenu qui s’y rapporte grâce au Logiciel ou
provenant autrement de l’utilisation du Logiciel . PAR AILLEURS, IL N’Y A AUCUNE
GARANTIE OU CONDITION QUANT AU TITRE DE PROPRIÉTÉ, À LA JOUISSANCE OU
LA POSSESSION PAISIBLE, À LA CONCORDANCE À UNE DESCRIPTION NI QUANT À
UNE ABSENCE DE CONTREFAÇON CONCERNANT LE LOGICIEL.

EXCLUSION DES DOMMAGES ACCESSOIRES, INDIRECTS ET DE CERTAINS AUTRES


DOMMAGES. DANS LA MESURE MAXIMALE PERMISE PAR LES LOIS APPLICABLES,
EN AUCUN CAS MICROSOFT OU SES FOURNISSEURS NE SERONT RESPONSABLES
DES DOMMAGES SPÉCIAUX, CONSÉCUTIFS, ACCESSOIRES OU INDIRECTS DE
QUELQUE NATURE QUE CE SOIT (NOTAMMENT, LES DOMMAGES À L’ÉGARD DU
MANQUE À GAGNER OU DE LA DIVULGATION DE RENSEIGNEMENTS
CONFIDENTIELS OU AUTRES, DE LA PERTE D’EXPLOITATION, DE BLESSURES
CORPORELLES, DE LA VIOLATION DE LA VIE PRIVÉE, DE L’OMISSION DE REMPLIR
TOUT DEVOIR, Y COMPRIS D’AGIR DE BONNE FOI OU D’EXERCER UN SOIN
RAISONNABLE, DE LA NÉGLIGENCE ET DE TOUTE AUTRE PERTE PÉCUNIAIRE OU
AUTRE PERTE DE QUELQUE NATURE QUE CE SOIT) SE RAPPORTANT DE QUELQUE
MANIÈRE QUE CE SOIT À L’UTILISATION DU LOGICIEL OU À L’INCAPACITÉ DE S’EN
SERVIR, À LA PRESTATION OU À L’OMISSION DE LA ’UNE TELLE PRESTATION DE
SERVICES DE SOUTIEN TECHNIQUE OU À LA FOURNITURE OU À L’OMISSION DE LA
FOURNITURE DE TOUS AUTRES SERVICES, RENSEIGNEMENTS, LOGICIELS, ET
CONTENU QUI S’Y RAPPORTE GRÂCE AU LOGICIEL OU PROVENANT AUTREMENT
DE L’UTILISATION DU LOGICIEL OU AUTREMENT AUX TERMES DE TOUTE
DISPOSITION DE LA U PRÉSENTE CONVENTION EULA OU RELATIVEMENT À UNE
TELLE DISPOSITION, MÊME EN CAS DE FAUTE, DE DÉLIT CIVIL (Y COMPRIS LA
NÉGLIGENCE), DE RESPONSABILITÉ STRICTE, DE VIOLATION DE CONTRAT OU DE
VIOLATION DE GARANTIE DE MICROSOFT OU DE TOUT FOURNISSEUR ET MÊME SI
MICROSOFT OU TOUT FOURNISSEUR A ÉTÉ AVISÉ DE LA POSSIBILITÉ DE TELS
DOMMAGES.

LIMITATION DE RESPONSABILITÉ ET RECOURS. MALGRÉ LES DOMMAGES QUE


VOUS PUISSIEZ SUBIR POUR QUELQUE MOTIF QUE CE SOIT (NOTAMMENT, MAIS
SANS LIMITATION, TOUS LES DOMMAGES SUSMENTIONNÉS ET TOUS LES
DOMMAGES DIRECTS OU GÉNÉRAUX OU AUTRES), LA SEULE RESPONSABILITÉ
’OBLIGATION INTÉGRALE DE MICROSOFT ET DE L’UN OU L’AUTRE DE SES
FOURNISSEURS AUX TERMES DE TOUTE DISPOSITION DEU LA PRÉSENTE
CONVENTION EULA ET VOTRE RECOURS EXCLUSIF À L’ÉGARD DE TOUT CE QUI
PRÉCÈDE SE LIMITE AU PLUS ÉLEVÉ ENTRE LES MONTANTS SUIVANTS : LE
MONTANT QUE VOUS AVEZ RÉELLEMENT PAYÉ POUR LE LOGICIEL OU 5,00 $US. LES
LIMITES, EXCLUSIONS ET DÉNIS QUI PRÉCÈDENT (Y COMPRIS LES CLAUSES CI-
DESSUS), S’APPLIQUENT DANS LA MESURE MAXIMALE PERMISE PAR LES LOIS
APPLICABLES, MÊME SI TOUT RECOURS N’ATTEINT PAS SON BUT ESSENTIEL.
À moins que cela ne soit prohibé par le droit local applicable, la présente Convention est régie
par les lois de la province d’Ontario, Canada. Vous consentez Chacune des parties à la présente
reconnaît irrévocablement à la compétence des tribunaux fédéraux et provinciaux siégeant à
Toronto, dans de la province d’Ontario et consent à instituer tout litige qui pourrait découler de
la présente auprès des tribunaux situés dans le district judiciaire de York, province d’Ontario.

Au cas où vous auriez des questions concernant cette licence ou que vous désiriez vous mettre en
rapport avec Microsoft pour quelque raison que ce soit, veuillez utiliser l’information contenue
dans le Logiciel pour contacter la filiale de succursale Microsoft desservant votre pays, dont
l’adresse est fournie dans ce produit, ou visitez écrivez à : Microsoft sur le World Wide Web à
<http://www.microsoft.com>Sales Information Center, One Microsoft Way, Redmond,
Washington 98052-6399.

You might also like