Created
June 2, 2026 19:58
-
-
Save wqweto/4532ab026aeecd9c43fc487d2daa3fa5 to your computer and use it in GitHub Desktop.
Finds VB6 procedures that have `On Error GoTo 0` but do NOT have a real error handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Find-DanglingOnErrorGoto0.ps1 | |
| param( | |
| [string]$Path | |
| ) | |
| # Finds VB6 procedures that have `On Error GoTo 0` but do NOT have a real | |
| # error handler (neither `On Error GoTo <label>` nor `On Error Resume Next`). | |
| if (-not $Path) { | |
| @" | |
| Find-DanglingOnErrorGoto0.ps1 - Find VB6 procedures with a dangling 'On Error GoTo 0'. | |
| Reports procedures that contain 'On Error GoTo 0' but never install a real error | |
| handler ('On Error GoTo <label>' or 'On Error Resume Next') before it. | |
| Usage: | |
| .\Find-DanglingOnErrorGoto0.ps1 -Path <folder-or-file> | |
| Parameters: | |
| -Path Folder (searched recursively) or file to scan. Scans *.bas, *.cls, | |
| *.frm and *.ctl files. | |
| Example: | |
| .\Find-DanglingOnErrorGoto0.ps1 -Path C:\Projects\MyVB6App | |
| "@ | Write-Output | |
| return | |
| } | |
| $reProcStart = '^\s*(?:(?:Public|Private|Friend|Static)\s+)*(?:Sub|Function|Property\s+(?:Get|Let|Set))\s+(\w+)' | |
| $reProcEnd = '^\s*End\s+(?:Sub|Function|Property)\b' | |
| $reGoto0 = '^\s*On\s+Error\s+GoTo\s+0\s*$' | |
| $reResume = '^\s*On\s+Error\s+Resume\s+Next\b' | |
| $reGotoLabel = '^\s*On\s+Error\s+GoTo\s+(?!0\s*$)\S+' | |
| $files = Get-ChildItem -Path $Path -Recurse -Include *.bas, *.cls, *.frm, *.ctl -File | |
| $results = foreach ($f in $files) { | |
| $lines = Get-Content -LiteralPath $f.FullName | |
| $inProc = $false | |
| $procName = '' | |
| $procStartLine = 0 | |
| $handlerInstalled = $false # is a handler active AT THIS POINT | |
| $danglingLines = @() # On Error GoTo 0 with no active handler before it | |
| for ($i = 0; $i -lt $lines.Count; $i++) { | |
| $line = $lines[$i] | |
| $lineNo = $i + 1 | |
| if (-not $inProc) { | |
| $m = [regex]::Match($line, $reProcStart, 'IgnoreCase') | |
| if ($m.Success) { | |
| $inProc = $true | |
| $procName = $m.Groups[1].Value | |
| $procStartLine = $lineNo | |
| $handlerInstalled = $false | |
| $danglingLines = @() | |
| } | |
| continue | |
| } | |
| if ($line -imatch $reProcEnd) { | |
| if ($danglingLines.Count -gt 0) { | |
| [pscustomobject]@{ | |
| File = $f.FullName | |
| Procedure = $procName | |
| ProcLine = $procStartLine | |
| Goto0At = ($danglingLines -join ',') | |
| } | |
| } | |
| $inProc = $false | |
| continue | |
| } | |
| if ($line -imatch $reResume -or $line -imatch $reGotoLabel) { | |
| $handlerInstalled = $true # installs a handler for the rest of the procedure | |
| } elseif ($line -imatch $reGoto0) { | |
| # flag only if NO handler was installed anywhere earlier in the procedure; | |
| # sticky flag, because due to branching (each GoTo 0 sits in a separate branch, | |
| # followed by Err.Raise/Exit) the top-level handler is actually active on the path | |
| if (-not $handlerInstalled) { $danglingLines += $lineNo } | |
| } | |
| } | |
| } | |
| $results = @($results) | |
| if ($results.Count -gt 0) { | |
| $results | Sort-Object File, ProcLine | Format-Table -AutoSize | |
| "Total: $($results.Count) procedure(s) with dangling 'On Error GoTo 0'." | Write-Output | |
| } else { | |
| "None found." | Write-Output | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment