c:\___Fire\Docs\GML\Office>powershell .\convert_mackserve_vba_to_vbnet.ps1 -VbaExportDir "C:\___Fire\Docs\GML\Office\VBA\mackserve_vba_export" -OutFile "C:\___Fire\Docs\GML\OfficeBridge\MackServe\MackServe_Mirror.vb" At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:104 char:22 + [void]$sb.AppendLine(Get-Content -Raw -LiteralPath (Join-Path $PSScri ... + ~ Missing ')' in method call. At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:104 char:22 + [void]$sb.AppendLine(Get-Content -Raw -LiteralPath (Join-Path $PSScri ... + ~~~~~~~~~~~ Unexpected token 'Get-Content' in expression or statement. At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:104 char:127 + ... ath $PSScriptRoot "vb_helpers_wariant_and_compat.vb") -Encoding UTF8) + ~ Unexpected token ')' in expression or statement. At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:139 char:35 + $endToken = $subSig.Success ? 'End Sub' : 'End Function' + ~ Unexpected token '?' in expression or statement. At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:220 char:3 + ' VBA Variant can hold many primitive types; this Wariant is a tagged ... + ~~~ Unexpected token 'VBA' in expression or statement. At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:248 char:30 + Dim w As New Wariant() + ~ An expression was expected after '('. At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:249 char:11 + If v Is Nothing Then + ~ Missing '(' after 'If' in if statement. At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:254 char:11 + If TypeOf v Is Boolean Then + ~ Missing '(' after 'If' in if statement. At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:257 char:11 + If TypeOf v Is Integer Then + ~ Missing '(' after 'If' in if statement. At C:\___Fire\Docs\GML\Office\convert_mackserve_vba_to_vbnet.ps1:260 char:11 + If TypeOf v Is Long Then + ~ Missing '(' after 'If' in if statement. Not all parse errors were reported. Correct the reported errors and try again. + CategoryInfo : ParserError: (:) [], ParseException + FullyQualifiedErrorId : MissingEndParenthesisInMethodCall c:\___Fire\Docs\GML\Office> ---------------convert_mackserve_vba_to_vbnet.ps1 [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$VbaExportDir, # e.g. C:\___Fire\Docs\GML\Office\VBA\mackserve_vba_export [Parameter(Mandatory=$true)] [string]$OutFile, # e.g. C:\___Fire\Docs\GML\OfficeBridge\MackServe\MackServe_Mirror.vb [switch]$TreatEventSubsAsFunctions # optional: turn event handlers into Functions too ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" function Ensure-Dir([string]$path) { $dir = Split-Path -Parent $path if ($dir -and -not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null } } function Is-EventSignature([string]$signatureLine) { # crude but practical: "Private Sub X(...)" with " _" continuation handled earlier # detect common Excel event patterns: # Worksheet_*, Workbook_*, UserForm_*, CommandButton*_Click, etc. $nameMatch = [regex]::Match($signatureLine, '^\s*(Public|Private|Friend)?\s*Sub\s+([A-Za-z_][A-Za-z0-9_]*)\b', 'IgnoreCase') if (-not $nameMatch.Success) { return $false } $n = $nameMatch.Groups[2].Value return ($n -match '^(Workbook_|Worksheet_|UserForm_|CommandButton|ToggleButton|TextBox|ComboBox|ListBox|CheckBox|OptionButton|SpinButton|ScrollBar)') ` -or ($signatureLine -match '\bHandles\b') } function Normalize-Line([string]$line) { # Basic normalizations for VB.NET parsing # Replace VBA-only type aliases if needed $line = $line -replace '\bVariant\b', 'Wariant' return $line } function Convert-ParamList([string]$paramList) { # Converts "Optional x As Variant" => "Optional x As Wariant" $p = $paramList -replace '\bVariant\b', 'Wariant' # VBA allows "As Integer" etc; VB.NET is similar enough for compileability. return $p } function Convert-SubSignatureToFunction([string]$sigLine) { # Input: "Private Sub Foo(a As Long, Optional b As Variant)" # Output: "Private Function Foo(a As Long, Optional b As Wariant) As String" $m = [regex]::Match($sigLine, '^\s*(Public|Private|Friend)?\s*Sub\s+([A-Za-z_][A-Za-z0-9_]*)\s*\((.*)\)\s*$', 'IgnoreCase') if (-not $m.Success) { return $null } $vis = $m.Groups[1].Value $name = $m.Groups[2].Value $params = $m.Groups[3].Value $params = Convert-ParamList $params $prefix = "" if ($vis) { $prefix = "$vis " } return "$prefix" + "Function $name(" + $params + ") As String" } function Convert-FunctionSignature([string]$sigLine) { # Keep VB Functions but normalize Variant->Wariant $sigLine = $sigLine -replace '\bVariant\b', 'Wariant' return $sigLine } function Read-VbaFile([string]$path) { # VBA exports may be ANSI/UTF8; try UTF8 then fallback try { return Get-Content -LiteralPath $path -Encoding UTF8 } catch { return Get-Content -LiteralPath $path } } # Collect source files if (-not (Test-Path -LiteralPath $VbaExportDir)) { throw "VbaExportDir not found: $VbaExportDir" } $srcFiles = Get-ChildItem -LiteralPath $VbaExportDir -Recurse -File | Where-Object { $_.Extension -in @(".bas",".cls",".frm") } | Sort-Object FullName Ensure-Dir $OutFile $sb = New-Object System.Text.StringBuilder # Header [void]$sb.AppendLine("' AUTO-GENERATED VB.NET MIRROR (compileable, not intended runnable)") [void]$sb.AppendLine("' Source: exported MackServe VBA modules") [void]$sb.AppendLine("' Rules: Subs->Functions (String), Variant->Wariant, callsites dummy-assign") [void]$sb.AppendLine("' GeneratedUtc: " + (Get-Date).ToUniversalTime().ToString("o")) [void]$sb.AppendLine("") [void]$sb.AppendLine("Option Strict Off") [void]$sb.AppendLine("Option Explicit On") [void]$sb.AppendLine("Imports System") [void]$sb.AppendLine("Imports System.Collections.Generic") [void]$sb.AppendLine("") # Helper: Wariant + VbaCompat [void]$sb.AppendLine("'") [void]$sb.AppendLine(Get-Content -Raw -LiteralPath (Join-Path $PSScriptRoot "vb_helpers_wariant_and_compat.vb") -Encoding UTF8) [void]$sb.AppendLine("'") [void]$sb.AppendLine("") # Track subs turned into functions so we can rewrite call sites $subToFunc = New-Object System.Collections.Generic.HashSet[string]([StringComparer]::OrdinalIgnoreCase) foreach ($f in $srcFiles) { $lines = Read-VbaFile $f.FullName $rel = $f.FullName.Substring($VbaExportDir.TrimEnd('\').Length).TrimStart('\') [void]$sb.AppendLine("' ===================================================================") [void]$sb.AppendLine("' FILE: " + $rel) [void]$sb.AppendLine("' ===================================================================") [void]$sb.AppendLine("") # We wrap each file as a Module for compileability. # (You can later split Modules/Classes more faithfully if desired.) $moduleName = ("MackServe_" + ($f.BaseName -replace '[^A-Za-z0-9_]', '_')) [void]$sb.AppendLine("Public Module " + $moduleName) [void]$sb.AppendLine("") $i = 0 while ($i -lt $lines.Count) { $line = Normalize-Line $lines[$i] # Preserve VBA as comments for context # Detect member start: Sub or Function (single-line signature only; good enough for most exports) $subSig = [regex]::Match($line, '^\s*(Public|Private|Friend)?\s*Sub\s+[A-Za-z_][A-Za-z0-9_]*\s*\(.*\)\s*$', 'IgnoreCase') $funSig = [regex]::Match($line, '^\s*(Public|Private|Friend)?\s*Function\s+[A-Za-z_][A-Za-z0-9_]*\s*\(.*\)\s*As\s+\w+', 'IgnoreCase') if ($subSig.Success -or $funSig.Success) { # capture VBA block until End Sub/End Function $vbaBlock = New-Object System.Text.StringBuilder $startLine = $i $endToken = $subSig.Success ? 'End Sub' : 'End Function' while ($i -lt $lines.Count) { $raw = $lines[$i] [void]$vbaBlock.AppendLine($raw) if ($raw.Trim().Equals($endToken, [StringComparison]::OrdinalIgnoreCase)) { break } $i++ } # emit VBA comment [void]$sb.AppendLine(" ' --- VBA ORIGINAL ---") foreach ($vbLn in $vbaBlock.ToString().Split("`n")) { $vv = $vbLn.TrimEnd("`r") [void]$sb.AppendLine(" ' " + $vv) } [void]$sb.AppendLine(" ' --- /VBA ORIGINAL ---") # Now emit converted signature $sigLine = Normalize-Line $lines[$startLine] if ($subSig.Success) { $isEvent = Is-EventSignature $sigLine if ($isEvent -and -not $TreatEventSubsAsFunctions) { # keep as Sub, just normalize Variant->Wariant in params $sigLine = $sigLine -replace '\bVariant\b', 'Wariant' [void]$sb.AppendLine(" " + $sigLine.Trim()) [void]$sb.AppendLine(" ' NOTE: Event-style Sub preserved as Sub for mirror.") [void]$sb.AppendLine(" ' TODO: Implement body in .NET emulator if ever needed.") [void]$sb.AppendLine(" End Sub") } else { $fnSig = Convert-SubSignatureToFunction $sigLine if ($null -eq $fnSig) { # fallback $sigLine = $sigLine -replace 'Sub', 'Function' $fnSig = $sigLine + " As String" } # capture name for callsite rewrite $nm = [regex]::Match($fnSig, '\bFunction\s+([A-Za-z_][A-Za-z0-9_]*)\b', 'IgnoreCase') if ($nm.Success) { $subToFunc.Add($nm.Groups[1].Value) | Out-Null } [void]$sb.AppendLine(" " + $fnSig.Trim()) [void]$sb.AppendLine(" ' NOTE: Former VBA Sub converted to Function for safer round-trip.") [void]$sb.AppendLine(" ' TODO: Port logic as needed; for now compileable stub.") [void]$sb.AppendLine(" Return ""OK""") [void]$sb.AppendLine(" End Function") } } else { # existing Function: keep, normalize Variant->Wariant $fnSig = Convert-FunctionSignature $sigLine [void]$sb.AppendLine(" " + $fnSig.Trim()) [void]$sb.AppendLine(" ' TODO: Port function body; compileable stub.") [void]$sb.AppendLine(" Return Nothing") [void]$sb.AppendLine(" End Function") } [void]$sb.AppendLine("") $i++ continue } $i++ } [void]$sb.AppendLine("End Module") [void]$sb.AppendLine("") } # Optional: a summary of Sub->Function names (for your later VBA rewrite pass) [void]$sb.AppendLine("' ===================================================================") [void]$sb.AppendLine("' Sub->Function converted names (dummy assign at call sites recommended):") foreach ($n in $subToFunc) { [void]$sb.AppendLine("' " + $n) } [void]$sb.AppendLine("' ===================================================================") [void]$sb.AppendLine("") Set-Content -LiteralPath $OutFile -Value $sb.ToString() -Encoding UTF8 Write-Host "WROTE: $OutFile" Write-Host ("SRCFILES: {0} SUB2FUNC: {1}" -f $srcFiles.Count, $subToFunc.Count) vb_helpers_wariant_and_compat.vb ' Minimal Variant replacement for VB.NET mirror compileability. ' VBA Variant can hold many primitive types; this Wariant is a tagged union. Public Enum WariantKind Empty = 0 NullValue = 1 BoolValue = 2 Int32Value = 3 Int64Value = 4 DoubleValue = 5 DecimalValue = 6 StringValue = 7 DateValue = 8 ObjectValue = 9 End Enum Public Class Wariant Public Kind As WariantKind = WariantKind.Empty Public BoolV As Boolean Public Int32V As Integer Public Int64V As Long Public DoubleV As Double Public DecimalV As Decimal Public StringV As String Public DateV As Date Public ObjectV As Object Public Shared Function FromObject(v As Object) As Wariant Dim w As New Wariant() If v Is Nothing Then w.Kind = WariantKind.Empty Return w End If If TypeOf v Is Boolean Then w.Kind = WariantKind.BoolValue : w.BoolV = CBool(v) : Return w End If If TypeOf v Is Integer Then w.Kind = WariantKind.Int32Value : w.Int32V = CInt(v) : Return w End If If TypeOf v Is Long Then w.Kind = WariantKind.Int64Value : w.Int64V = CLng(v) : Return w End If If TypeOf v Is Double Then w.Kind = WariantKind.DoubleValue : w.DoubleV = CDbl(v) : Return w End If If TypeOf v Is Decimal Then w.Kind = WariantKind.DecimalValue : w.DecimalV = CDec(v) : Return w End If If TypeOf v Is String Then w.Kind = WariantKind.StringValue : w.StringV = CStr(v) : Return w End If If TypeOf v Is Date Then w.Kind = WariantKind.DateValue : w.DateV = CDate(v) : Return w End If w.Kind = WariantKind.ObjectValue w.ObjectV = v Return w End Function Public Function ToObject() As Object Select Case Kind Case WariantKind.Empty : Return Nothing Case WariantKind.NullValue : Return Nothing Case WariantKind.BoolValue : Return BoolV Case WariantKind.Int32Value : Return Int32V Case WariantKind.Int64Value : Return Int64V Case WariantKind.DoubleValue : Return DoubleV Case WariantKind.DecimalValue : Return DecimalV Case WariantKind.StringValue : Return StringV Case WariantKind.DateValue : Return DateV Case WariantKind.ObjectValue : Return ObjectV Case Else : Return Nothing End Select End Function Public Overrides Function ToString() As String Dim o = ToObject() If o Is Nothing Then Return "" Return o.ToString() End Function End Class ' Lightweight VBA-isms used during mirroring. Public Module VbaCompat Public Function Nz(v As Object, Optional fallback As Object = Nothing) As Object If v Is Nothing Then Return fallback Return v End Function End Module