|
|
Convert VB6 to VB.NETMacros Generating Code - Part 2 | | Macros, in combination with Regular Expressions form a powerful tool set for generating code. This article will generate a Input Line Object from a VB6 Type Object. This a second in a series of articles on Code Generation utilizing Macros and Regular Expressions. To see the first article, click here.
In VB6, we often used the Type object (Structure in VB.NET) for defining the format of input files. Additionally, in VB6, a String could be defined as a fixed length; no longer supported in VB.NET. In .NET, using a little Macro wizardry, we can use the the old Type structure just long enough to assist us in creating an object-oriented approach to handling input records from a text file. The macro that I have written and demonstrate in this article will actually generate a whole class, including properties for each field in the original Type object. Additionally, the class will populate the properties without calling any methods in the Class.
Take the simple Type object shown below from a theoretical VB6 Application. We will assume that this object has not been processed by the Migration Wizard, otherwise the Type object would have been converted to a Structure and the string lengths would have been commented. If this had been done, then a simple modification to the Regular Expression, used in the Macro, would still use the string lengths to create the LineInputObject Class.
Public Type Applicant
Dim LastName As String * 25
Dim FirstName as String * 15
Dim Address As String * 25
Dim City as String * 15
Dim State As String * 2
Dim SSN As String * 11
Dim DOB as String * 10
End Type
|
Next, I will show the code for the Macro. It will create a new class and instert it into a Text window in the IDE. With just a little more code the Macro could create a Class object and add it to the project, but that code has been shown in other articles on automation and will not be done here.
Public Sub CreateInputObjectMethod()
Dim ts As TextSelection = DTE.ActiveDocument.Selection
Dim s As String = ts.Text
Dim mc As MatchCollection = _
Regex.Matches(s, "^\s*(?<field>\w+)\s+As\s+String\s+\*\s+(?<nbr>\d+)", _
RegexOptions.Multiline)
Dim cnt As Integer = 0
Dim stPtr As Integer = 1
Dim sb As New Text.StringBuilder(5000)
Dim sbpriv As New Text.StringBuilder(5000)
Dim sbprop As New Text.StringBuilder(5000)
Dim sbClass As New Text.StringBuilder(5000)
Const gf1 As String = "Private Function GetField(ByVal dataLine As String, " & _
"ByVal stChar As Integer, ByVal endChar As Integer, " & _
"ByVal len As Integer) As String"
Const gf2 As String = " If stChar >= 1 AndAlso _"
Const gf3 As String = " endChar >= stChar AndAlso _"
Const gf4 As String = " dataLine.Length >= endChar AndAlso _"
Const gf5 As String = " (endChar - stChar + 1) = Len _"
Const gf6 As String = " Then"
Const gf7 As String = _
" Return dataLine.Substring(stChar - 1, endChar - stChar + 1).Trim"
Const gf8 As String = " Else"
Const gf9 As String = _
" Throw New Exception(""GetField() bad input parameters"")"
Const gf10 As String = "End If"
Const gf11 As String = "End Function"
Dim name As String = _
InputBox("Enter name for new Input Object", "Enter Object Name", "")
If Name.Length > 0 Then
sbClass.Append("Public Class " & name & vbCrLf)
sb.Append(" Private Sub Parse(Byval line As String)" & vbCrLf)
For Each m As Match In mc
Dim nbr As Integer = CType(m.Groups("nbr").Value, Integer)
cnt += nbr
' create the private var for each match
sbpriv.Append(" Private _" & m.Groups("field").Value & _
" As String = String.Empty" & vbCrLf)
' create the matching property
sbprop.Append(" Public Property " & m.Groups("field").Value & _
"() As String" & vbCrLf)
sbprop.Append(" Get" & vbCrLf)
sbprop.Append(" Return _" & m.Groups("field").Value & vbCrLf)
sbprop.Append(" End Get" & vbCrLf)
sbprop.Append(" End Property" & vbCrLf)
sb.Append(m.Groups("field").Value & " = GetField(line, " & _
stPtr.ToString & ", " & cnt & ", " & _
m.Groups("nbr").Value & ")" & vbCrLf)
stPtr += nbr
Next
sb.Append(" End Sub" & vbCrLf)
sb.Append(" Public Sub New(Byval line As String)" & vbCrLf)
sb.Append(" Parse(line)" & vbCrLf)
sb.Append(" End Sub" & vbCrLf)
sb.Append(gf1 & vbCrLf)
sb.Append(gf2 & vbCrLf)
sb.Append(gf3 & vbCrLf)
sb.Append(gf4 & vbCrLf)
sb.Append(gf5 & vbCrLf)
sb.Append(gf6 & vbCrLf)
sb.Append(gf7 & vbCrLf)
sb.Append(gf8 & vbCrLf)
sb.Append(gf9 & vbCrLf)
sb.Append(gf10 & vbCrLf)
sb.Append(gf11 & vbCrLf)
Debug.WriteLine(sb.ToString)
DTE.ItemOperations.NewFile("General\Text File")
sbClass.Append(sbpriv.ToString() & vbCrLf)
sbClass.Append(sbprop.ToString() & vbCrLf)
sbClass.Append(sb.ToString() & vbCrLf)
sbClass.Append("End Class" & vbCrLf)
DTE.ActiveDocument.Object("TextDocument").Selection.Insert(sbClass.ToString)
End If
End Sub
|
Four StringBuilders are being constructed by the Macro and then they are merged at the end of the Macro to form the complet class. You will note that the Macro displays an InputBox prompting the developer for a name for the class. You could hard-code the name and remove the Inputbox.
The code shown below was generated by the macro. To use the macro, I need to select the inside lines of the Type object shown previously and listed below. Haviing selected the following lines, I double-click on the name of the macro in the Macro Explorer to invoke the macro.
Dim LastName As String * 25
Dim FirstName as String * 15
Dim Address As String * 25
Dim City as String * 15
Dim State As String * 2
Dim SSN As String * 11
Dim DOB as String * 10
|
From examining the seven lines above, the macro will generate seven Private variable, seven Public Properties, along with the code to populate the properties. The Parse function of the class is used to extract the data from the text line that is passed to the constructor of the class. Once the class is instantiated and control is returned from the constructor to the instantiater of the class, the properties will be populated with the data from the input line. The Class, shown below was generated by the macro. Since there is no reason to change the properties from outside the class, the properties are generated as ReadOnly.
Public Class InputLineObject
Private _LastName As String = String.Empty
Private _FirstName As String = String.Empty
Private _Address As String = String.Empty
Private _City As String = String.Empty
Private _State As String = String.Empty
Private _SSN As String = String.Empty
Public ReadOnly Property LastName() As String
Get
Return _LastName
End Get
End Property
Public ReadOnly Property FirstName() As String
Get
Return _FirstName
End Get
End Property
Public ReadOnly Property Address() As String
Get
Return _Address
End Get
End Property
Public ReadOnly Property City() As String
Get
Return _City
End Get
End Property
Public ReadOnly Property State() As String
Get
Return _State
End Get
End Property
Public ReadOnly Property SSN() As String
Get
Return _SSN
End Get
End Property
Private Sub Parse(ByVal line As String)
LastName = GetField(line, 1, 25, 25)
FirstName = GetField(line, 26, 40, 15)
Address = GetField(line, 41, 65, 25)
City = GetField(line, 66, 80, 15)
State = GetField(line, 81, 82, 2)
SSN = GetField(line, 83, 93, 11)
End Sub
Public Sub New(ByVal line As String)
Parse(line)
End Sub
Private Function GetField(ByVal dataLine As String, _
ByVal stChar As Integer, ByVal endChar As Integer, _
ByVal len As Integer) As String
If dataLine.Length < 205 Then dataLine += Space(205 - dataLine.Length)
If stChar >= 1 AndAlso _
endChar <= 205 AndAlso _
endChar >= stChar AndAlso _
dataLine.Length >= endChar AndAlso _
(endChar - stChar + 1) = Len _
Then
Return dataLine.Substring(stChar - 1, endChar - stChar + 1).Trim
Else
Throw New Exception("GetField() bad input parameters")
End If
End Function
End Class
|
A simple use of the new class from the application would be as follows.
Dim sr As New IO.StreamReader(fileName)
Dim line As String = String.Empty
Do
line = sr.ReadLine
If line IsNot Nothing AndAlso line.Length > 0 Then
Dim ilo As New InputLineObject(line)
With ilo
Dim firstName As String = .FirstName
Dim lastName As String = .LastName
ProcessApplicantName(firstName, lastName)
.....
End With
End If
Loop
|
Use your imagination to finish the processing. Well, we have used another fairly simple macro that we can use many times in the future if you have the task of converting a number of VB6 applications to VB.NET.
| Ask a Question, or give your feedback on my articles or products by going to the KnowDotNet Forum or by clicking on My Blog. |  |
|
|