Language Elements

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 guarantee 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 compilation 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 Windows 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 contains 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 System.Drawing.Color.Red, although it looks like a constant, is not; it is a nonconstant expression. This means that, rather than being a constant value, such as 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 enumerator values in code to combine red and green to create a yellow background 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 constant, 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.

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 enumerator 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 member of RGBColors,such as RGBColors.Red, is used with the matching System.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 function 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 coincidence, 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 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 statement, 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 copying 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 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 comments 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 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     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

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

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

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 Random. 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 PrintLine functions are equivalent to the Visual Basic 6 Print # function. Which function 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 following 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.

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 difference 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 considered 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.

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, OutputData, 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)              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 difference 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 following 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 message 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 contained 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.

Random-Access Files and Fixed-Length Strings

A problem similar to the dy namic 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 function 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 getting 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 parameter 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)



Upgrading Microsoft Visual Basic 6.0to Microsoft Visual Basic  .NET
Upgrading Microsoft Visual Basic 6.0 to Microsoft Visual Basic .NET w/accompanying CD-ROM
ISBN: 073561587X
EAN: 2147483647
Year: 2001
Pages: 179

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net