Patterns for maintaining VB6 code and strategies for migrating to VB.NET with COM interop.
VB6 to VB.NET Migration
Key Differences
' VB6 - Variant typesDim data As Variant
data = 123
data = "Hello"' VB.NET - Strong typing with Option Strict OnOptionStrictOnDim data AsObject' Still avoid when possibleDim number AsInteger = 123DimtextAsString = "Hello"' VB6 - Default properties
Text1.Text = "Hello"
Text1 = "Hello"' Uses default Text property' VB.NET - Explicit properties required
TextBox1.Text = "Hello"' Must be explicit' VB6 - ByRef defaultSub ProcessData(data AsString) ' ByRef by default' VB.NET - ByVal defaultSub ProcessData(data AsString) ' ByVal by defaultSub ProcessData(ByRef data AsString) ' Explicit ByRef' VB6 - On ErrorOn
ErrorHandler
ex Exception
Error
Resume
Next
On
Error
GoTo
' VB.NET - Try-Catch
Try
' Code
Catch
As
' Handle error
End
Try
Common Migration Issues
' VB6 - Fixed-length stringsDim name AsString * 50' VB.NET - Use regular string and PadRightDim name AsString = "John".PadRight(50)
' VB6 - Currency typeDim amount As Currency
' VB.NET - Use DecimalDim amount AsDecimal' VB6 - Control arraysDim TextBox(5) As TextBox
' VB.NET - Use collectionDim textBoxes AsNew List(Of TextBox)()
' VB6 - Let/Set keywordsLet x = 5Set obj = NewMyClass' VB.NET - Assignment without keywordsDim x AsInteger = 5Dim obj AsNewMyClass()
COM Interop from VB.NET
Early Binding (Type Library Reference)
' Add COM reference in project' Tools -> Add Reference -> COM -> Excel Object LibraryImports Excel = Microsoft.Office.Interop.Excel
PublicSub ExportToExcel(data As DataTable)
Dim excelApp As Excel.Application = NothingDim workbook As Excel.Workbook = NothingDim worksheet As Excel.Worksheet = NothingTry
excelApp = New Excel.Application()
workbook = excelApp.Workbooks.Add()
worksheet = CType(workbook.Worksheets(1), Excel.Worksheet)
' Write headersFor col = 0To data.Columns.Count - 1
worksheet.Cells(1, col + 1) = data.Columns(col).ColumnName
Next' Write dataFor row = 0To data.Rows.Count - 1For col = 0To data.Columns.Count - 1
worksheet.Cells(row + 2, col + 1) = data.Rows(row)(col)
NextNext
excelApp.Visible = TrueCatch ex As Exception
MessageBox.Show($"Excel export failed: {ex.Message}")
Finally' Release COM objectsIf worksheet IsNotNothingThen
System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet)
EndIfIf workbook IsNotNothingThen
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook)
EndIfIf excelApp IsNotNothingThen
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp)
EndIfEndTryEndSub
Late Binding (No Type Library)
Imports System.Reflection
PublicSub CreateExcelLateBound()
Dim excelType As Type = Type.GetTypeFromProgID("Excel.Application")
If excelType IsNothingThen
MessageBox.Show("Excel not installed")
ReturnEndIfDim excelApp AsObject = NothingTry' Create COM object
excelApp = Activator.CreateInstance(excelType)
' Call methods via reflection
excelType.InvokeMember("Visible",
BindingFlags.SetProperty,
Nothing,
excelApp,
NewObject() {True})
Dim workbooks AsObject = excelType.InvokeMember("Workbooks",
BindingFlags.GetProperty,
Nothing,
excelApp,
Nothing)
' Add workbook
workbooks.GetType().InvokeMember("Add",
BindingFlags.InvokeMethod,
Nothing,
workbooks,
Nothing)
FinallyIf excelApp IsNotNothingThen
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp)
EndIfEndTryEndSub
COM Object Release Pattern
' ✅ Good: Proper COM object cleanupPublicSub UseComObject()
Dim excelApp As Excel.Application = NothingDim workbook As Excel.Workbook = NothingTry
excelApp = New Excel.Application()
workbook = excelApp.Workbooks.Add()
' Use objectsFinally' Release in reverse order of creationIf workbook IsNotNothingThen
workbook.Close(False)
Marshal.ReleaseComObject(workbook)
workbook = NothingEndIfIf excelApp IsNotNothingThen
excelApp.Quit()
Marshal.ReleaseComObject(excelApp)
excelApp = NothingEndIf
GC.Collect()
GC.WaitForPendingFinalizers()
EndTryEndSub' ❌ Bad: Not releasing COM objects (memory leak)Dim excelApp = New Excel.Application()
excelApp.Workbooks.Add()
' No cleanup - Excel process remains in memory!
Creating COM-Visible .NET Components
COM-Visible Class
Imports System.Runtime.InteropServices
<ComVisible(True)>
<Guid("12345678-1234-1234-1234-123456789012")>
<ClassInterface(ClassInterfaceType.None)>
<ProgId("MyCompany.Calculator")>
PublicClass Calculator
Implements ICalculator
PublicFunction Add(a AsInteger, b AsInteger) AsIntegerImplements ICalculator.Add
Return a + b
EndFunctionPublicFunction Subtract(a AsInteger, b AsInteger) AsIntegerImplements ICalculator.Subtract
Return a - b
EndFunctionEndClass' Interface for COM
<ComVisible(True)>
<Guid("87654321-4321-4321-4321-210987654321")>
<InterfaceType(ComInterfaceType.InterfaceIsDual)>
PublicInterface ICalculator
Function Add(a AsInteger, b AsInteger) AsIntegerFunction Subtract(a AsInteger, b AsInteger) AsIntegerEndInterface
Register for COM
# Register assembly for COM
regasm MyAssembly.dll /tlb /codebase
# Unregister
regasm MyAssembly.dll /u
# Generate type library
tlbexp MyAssembly.dll
Legacy VB6 Patterns to Modernize
Error Handling
' VB6 style (avoid in new code)PublicFunction GetCustomer(id AsInteger) As Customer
OnErrorGoTo ErrorHandler
' Code hereExitFunctionErrorHandler:
MsgBox "Error: " & Err.Description
ResumeNextEndFunction' VB.NET modern stylePublicFunction GetCustomer(id AsInteger) As Customer
Try' Code hereCatch ex As ArgumentException
MessageBox.Show($"Invalid argument: {ex.Message}")
ReturnNothingCatch ex As Exception
MessageBox.Show($"Error: {ex.Message}")
ThrowEndTryEndFunction
' VB6 Collection (avoid in new code)Dim customers AsNew Collection()
customers.Add(customer, "key1")
Dim item = customers("key1")
' VB.NET generic collectionsDim customers = New Dictionary(OfString, Customer)()
customers.Add("key1", customer)
Dim item = customers("key1")
' Or List(Of T)Dim customerList = New List(Of Customer)()
customerList.Add(customer)
Migration Strategy
Incremental Migration
' 1. Start with COM interop wrapper' Wrap VB6 COM component in VB.NETPublicClass VB6Wrapper
Private vb6Component AsObjectPublicSubNew()
vb6Component = CreateObject("VB6Project.Component")
EndSubPublicFunction ProcessData(data AsString) AsStringReturn vb6Component.ProcessData(data).ToString()
EndFunctionEndClass' 2. Gradually replace with native VB.NETPublicClass ModernComponent
PublicFunction ProcessData(data AsString) AsString' New VB.NET implementationReturn data.ToUpper()
EndFunctionEndClass
Best Practices
✅ DO
' Use modern VB.NET features in new codeOptionStrictOnOptionExplicitOn' Release COM objects explicitly
Marshal.ReleaseComObject(comObject)
GC.Collect()
' Use Try-Catch instead of On ErrorTry' CodeCatch ex As Exception
' HandleEndTry' Use generics instead of CollectionsDim items = New List(Of Customer)()
' Use async for I/OAwait File.WriteAllTextAsync("file.txt", content)
❌ DON'T
' Don't use VB6 compatibility moduleImports Microsoft.VisualBasic.Compatibility.VB6 ' Legacy only!' Don't use Option Strict OffOptionStrictOff' Avoid!' Don't use On Error in new codeOnErrorResumeNext' VB6 style' Don't forget to release COM objectsDim excelApp = New Excel.Application()
' ... (no cleanup - memory leak!)' Don't use late binding when early binding availableDim obj = CreateObject("Excel.Application") ' Use typed reference instead