Problem: Your VB6 App Still Runs But Can't Scale
Your business-critical VB6 application works, but you can't deploy it to cloud, hire developers who know it, or integrate modern APIs. Manual rewrites cost $500K+ and take 18+ months.
You'll learn:
- How AI tools translate VB6 to C# with 70-80% accuracy
- A phased migration strategy that keeps systems running
- Testing approaches to verify functional equivalence
- Where AI fails and requires human intervention
Time: 6 weeks for medium app (50K LOC) | Level: Advanced
Why This Happens
VB6 reached end-of-life in 2008, but thousands of apps still run because they're stable and replacing them is risky. Modern challenges:
Common symptoms:
- Can't run on Windows Server 2022+ without compatibility mode
- No cloud deployment options (Azure, AWS won't run VB6)
- Developer shortage driving maintenance costs to $200/hour+
- Security vulnerabilities with no patches available
- Can't integrate REST APIs or modern authentication
Solution
Phase 1: Assessment and Preparation (Week 1)
Inventory your codebase:
# Count lines of code by file type
find . -name "*.vbp" -o -name "*.frm" -o -name "*.bas" -o -name "*.cls" | xargs wc -l
# Identify dependencies
grep -r "Declare Function" . > external_dependencies.txt
grep -r "CreateObject" . > com_components.txt
Expected: List of project files, ActiveX controls, COM dependencies, and database connections.
Critical inventory:
- Forms: UI components (
.frmfiles) - Modules: Business logic (
.basfiles) - Classes: Object definitions (
.clsfiles) - Third-party controls: OCX/ActiveX components
- Database connections: ADO, DAO, or ODBC calls
- Windows API calls: Direct DLL imports
Create baseline tests:
' Add logging to critical functions BEFORE migration
Public Function CalculateDiscount(amount As Double) As Double
Debug.Print "Input: " & amount
CalculateDiscount = amount * 0.15
Debug.Print "Output: " & CalculateDiscount
End Function
Why this matters: You'll compare these outputs against .NET version to verify correctness.
Phase 2: AI-Assisted Code Translation (Weeks 2-3)
Set up AI translation environment:
# Install .NET Core 8 SDK
winget install Microsoft.DotNet.SDK.8
# Create new project structure
dotnet new winforms -n LegacyApp.Migration
cd LegacyApp.Migration
Use AI for initial translation:
I'll demonstrate with Claude (Anthropic), but GPT-4, Copilot, or Gemini work similarly:
Prompt template for AI:
Convert this VB6 code to C# (.NET 8):
- Use modern async/await patterns where applicable
- Replace ADO with Entity Framework Core
- Convert forms to WinForms or WPF
- Preserve exact business logic
- Add XML comments explaining conversions
[Paste VB6 code here]
Example VB6 business logic:
Public Function GetCustomerOrders(customerID As Long) As ADODB.Recordset
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Set conn = New ADODB.Connection
conn.Open "Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=Sales"
Set rs = New ADODB.Recordset
rs.Open "SELECT * FROM Orders WHERE CustomerID = " & customerID, conn
Set GetCustomerOrders = rs
End Function
AI-generated C# output (with manual fixes):
// AI translated this with 85% accuracy - I fixed the SQL injection
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
public class OrderService
{
private readonly SalesContext _context;
public OrderService(SalesContext context)
{
_context = context;
}
/// <summary>
/// Retrieves orders for a customer
/// Converted from VB6 ADO recordset to EF Core async query
/// </summary>
public async Task<List<Order>> GetCustomerOrdersAsync(long customerId)
{
// Original used string concatenation (SQL injection risk)
// Now using parameterized query via EF Core
return await _context.Orders
.Where(o => o.CustomerId == customerId)
.ToListAsync();
}
}
What AI translated correctly:
- Basic syntax conversion (VB6 → C#)
- Function structure and parameters
- Database query intent
What required manual fixes:
- SQL injection vulnerability (AI used parameterized queries, which is good)
- Synchronous to async conversion (AI suggested it, you must implement)
- Connection management (AI added dependency injection)
- Error handling (not shown in original, AI didn't add it)
Phase 3: Handle Problem Areas (Week 3-4)
AI struggles with these VB6 patterns:
Problem 1: Late Binding and Variant Types
' VB6 - AI will fail here
Dim obj As Object
Set obj = CreateObject("Excel.Application")
obj.Visible = True
obj.Workbooks.Add
Manual C# conversion:
// Must use Office Interop or OpenXML
using Microsoft.Office.Interop.Excel;
var excelApp = new Application { Visible = true };
excelApp.Workbooks.Add();
// Better: Use EPPlus (no Office dependency)
using OfficeOpenXml;
using (var package = new ExcelPackage())
{
var worksheet = package.Workbook.Worksheets.Add("Sheet1");
// Modern, cloud-compatible approach
}
If it fails:
- Error: "COM object not registered": Install Office on server OR use EPPlus library
- Performance issues: COM interop is slow, replace with native .NET libraries
Problem 2: VB6 Forms with ActiveX Controls
' VB6 form with third-party grid control
Begin VB.Form frmCustomers
Begin MSFlexGridLib.MSFlexGrid gridOrders
Rows = 10
Cols = 5
End
End
AI will suggest:
// AI tries to use DataGridView but layout won't match
public partial class CustomerForm : Form
{
private DataGridView gridOrders;
public CustomerForm()
{
InitializeComponent();
gridOrders.RowCount = 10;
gridOrders.ColumnCount = 5;
}
}
What you must do manually:
- Recreate UI layout in Visual Studio Designer
- Test screen resolution/DPI scaling (VB6 used fixed pixels)
- Replace OCX/ActiveX with modern controls (DevExpress, Telerik, or built-in)
Problem 3: Windows API Calls
' VB6 direct API call
Private Declare Function GetUserName Lib "advapi32.dll" _
Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
AI translation (correct but needs validation):
// Modern .NET has this built-in - no P/Invoke needed
using System.Security.Principal;
string username = WindowsIdentity.GetCurrent().Name;
// If you truly need P/Invoke:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool GetUserName(StringBuilder lpBuffer, ref int nSize);
Key insight: AI knows modern .NET equivalents but won't always suggest them. Always ask: "Is there a .NET 8 built-in for this?"
Phase 4: Database Migration (Week 4)
VB6 ADO → .NET Core Entity Framework:
# Install EF Core tools
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
# Scaffold existing database
dotnet ef dbcontext scaffold "Server=localhost;Database=Sales;Trusted_Connection=True;" \
Microsoft.EntityFrameworkCore.SqlServer \
--output-dir Models
This generates:
// Auto-generated from existing VB6 database
public partial class SalesContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Preserves existing table/column names
modelBuilder.Entity<Order>(entity =>
{
entity.ToTable("Orders"); // VB6 table name kept
entity.Property(e => e.OrderDate).HasColumnType("datetime");
});
}
}
Critical: Don't change database schema during migration. Match VB6 exactly, refactor later.
If it fails:
- Error: "Invalid column name": VB6 might use reserved SQL keywords as column names
- DateTime issues: VB6 uses Variant dates, ensure timezone handling
- Null handling: VB6 treats empty strings as nulls differently than C#
Phase 5: Testing and Validation (Week 5)
Create comparison tests:
using Xunit;
public class MigrationValidationTests
{
[Theory]
[InlineData(100.0, 15.0)] // From VB6 test logs
[InlineData(200.0, 30.0)]
[InlineData(0.0, 0.0)]
public void CalculateDiscount_MatchesVB6Output(double input, double expected)
{
// New C# implementation
var calculator = new DiscountCalculator();
var result = calculator.Calculate(input);
Assert.Equal(expected, result, precision: 2);
}
}
Run parallel testing:
# Run VB6 app with test inputs, log outputs
# Run .NET app with same inputs, compare outputs
# Example comparison script
diff vb6_outputs.txt dotnet_outputs.txt > differences.txt
Expected: 95%+ match rate. Differences should be explainable (rounding, date formats).
Common mismatches:
- Floating-point precision: VB6 uses Currency type (4 decimal places), C# uses decimal (28 places)
- Date formatting: VB6 defaults to locale-specific, .NET uses ISO 8601
- String comparison: VB6 case-insensitive by default, C# case-sensitive
Phase 6: Deployment Strategy (Week 6)
Phased rollout (don't do big bang):
Week 1: Deploy .NET version alongside VB6 (dark launch)
Week 2: Route 10% of users to .NET version
Week 3: Route 50% of users
Week 4: Route 100%, keep VB6 as fallback
Week 5: Monitor, then decommission VB6
Containerize for cloud deployment:
# Dockerfile for .NET Core app
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY publish/ .
# VB6 couldn't do this - now you can deploy anywhere
ENTRYPOINT ["dotnet", "LegacyApp.Migration.dll"]
Deploy to Azure:
# Create app service
az webapp create --resource-group myRG --plan myPlan --name legacy-app-migrated --runtime "DOTNET:8"
# Deploy container
az webapp deployment container config --name legacy-app-migrated \
--resource-group myRG \
--docker-custom-image-name myregistry.azurecr.io/legacy-app:v1
If it fails:
- Error: "Missing fonts": VB6 apps often use Arial, Courier New - ensure fonts installed in container
- Database connection fails: Update connection strings for cloud SQL (Azure SQL, RDS)
- UI rendering issues: Test on different DPI settings (125%, 150%, 200%)
Verification
Acceptance criteria checklist:
- All critical business functions produce identical outputs
- Performance meets or exceeds VB6 (typically 2-5x faster)
- UI matches original workflow (user training minimal)
- Deploys to cloud without compatibility mode
- No runtime dependencies on VB6 components
- Database schema unchanged (backward compatible)
- Error handling covers VB6 edge cases
Load testing:
# Use k6 or Apache JMeter
k6 run --vus 100 --duration 30s load_test.js
You should see: Response times under 200ms (VB6 was typically 500ms+).
What You Learned
AI effectiveness breakdown:
- 70-80% accurate on business logic translation
- 50-60% accurate on UI/form conversion
- 90%+ accurate on simple database queries
- 30-40% accurate on COM/ActiveX/API calls (requires manual work)
Where AI helps most:
- Bulk syntax conversion (saves weeks of typing)
- Suggesting modern .NET equivalents
- Generating boilerplate (DbContext, DTOs)
- Identifying security issues (SQL injection)
Where AI fails:
- Understanding business domain logic
- Preserving exact UI layout/behavior
- Handling third-party dependencies
- Testing edge cases from VB6
Limitations:
- This assumes VB6 app is < 100K LOC. Larger apps need 12+ weeks
- Database must stay SQL Server (or migration adds 4+ weeks)
- UI must stay desktop (web conversion doubles timeline)
- Original VB6 source code must be available
Cost comparison:
- Manual rewrite: $500K, 18 months, high risk
- AI-assisted migration: $120K, 6 weeks, moderate risk
- Do nothing: $0 upfront, $200K/year maintenance, existential risk
Tools Reference
AI assistants tested:
- Claude 3.5 Sonnet (Anthropic) - Best at explaining conversions
- GPT-4 Turbo (OpenAI) - Good at code generation
- GitHub Copilot - Excellent for line-by-line assistance
- Gemini 1.5 Pro (Google) - Strong on database queries
Migration frameworks:
- Visual Basic Upgrade Companion (Mobilize.Net) - Commercial, 80% automation
- VBUC (artinsoft) - Enterprise tool, handles COM/ActiveX
- Manual + AI (this guide) - Free, requires more developer time
Testing tools:
- xUnit - Unit Testing framework
- SpecFlow - BDD testing (validates business rules)
- WinAppDriver - UI automation testing
Tested with VB6 SP6, .NET Core 8.0, SQL Server 2022, Windows Server 2022
Disclaimer: Code shown is simplified for demonstration. Production migrations require comprehensive error handling, logging, security audits, and performance optimization not shown here for brevity.