KnowDotNet Visual Organizer

Convert VB6 to VB.NET

Macros Generating Code - Part 2

by Les Smith
Print this Article Discuss in Forums

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.
  

Writing Add-Ins for Visual Studio .NET
Writing Add-ins for Visual Studio .NET
by Les Smith
Apress Publishing