Skip to main content

PowerShell Integration with TextPipe COM

Automate data transformations from PowerShell using the TextPipe COM API. These examples cover COM object instantiation, filter execution, output verification, batch folder processing, and proper error handling with resource cleanup.

Basic Usage

The simplest TextPipe COM automation: instantiate the COM object, load a filter, add input files, execute the transformation, and release the COM object.

# Basic TextPipe COM automation from PowerShell
# Requires: TextPipe Pro installed and registered

# Create the COM object
$tp = New-Object -ComObject TextPipe.Application

try {
    # Configure for unattended operation
    $tp.Silent = $true
    $tp.Visible = $false
    $tp.OverwriteOutput = $true

    # Load a pre-configured filter list
    $filterLoaded = $tp.LoadFilter("C:\Filters\cleanup_csv.fll")
    if (-not $filterLoaded) {
        throw "Failed to load filter: $($tp.GetLastError())"
    }

    # Add input file and set output destination
    $tp.AddInputFile("C:\Data\raw_export.csv")
    $tp.OutputFile = "C:\Data\cleaned_export.csv"

    # Execute the transformation
    $filesProcessed = $tp.Go()

    Write-Host "Successfully processed $filesProcessed file(s)."
}
catch {
    Write-Error "TextPipe automation failed: $_"
    exit 1
}
finally {
    # Always release the COM object to prevent memory leaks
    $tp.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($tp) | Out-Null
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
}

Output Verification

After executing a transformation, verify that the output file exists, has content, and optionally compare file sizes or check for expected patterns.

# TextPipe COM automation with output verification
param(
    [Parameter(Mandatory=$true)]
    [string]$FilterPath,

    [Parameter(Mandatory=$true)]
    [string]$InputFile,

    [Parameter(Mandatory=$true)]
    [string]$OutputFile
)

$tp = New-Object -ComObject TextPipe.Application

try {
    $tp.Silent = $true
    $tp.Visible = $false
    $tp.OverwriteOutput = $true

    # Load filter
    if (-not $tp.LoadFilter($FilterPath)) {
        throw "Filter load failed: $($tp.GetLastError())"
    }

    # Configure input/output
    $tp.AddInputFile($InputFile)
    $tp.OutputFile = $OutputFile

    # Record start time for performance tracking
    $startTime = Get-Date

    # Execute transformation
    $filesProcessed = $tp.Go()
    $duration = (Get-Date) - $startTime

    # --- Output Verification ---

    # Check 1: File exists
    if (-not (Test-Path $OutputFile)) {
        throw "Output file was not created: $OutputFile"
    }

    # Check 2: File is not empty
    $outputInfo = Get-Item $OutputFile
    if ($outputInfo.Length -eq 0) {
        throw "Output file is empty (0 bytes): $OutputFile"
    }

    # Check 3: Output has reasonable size relative to input
    $inputInfo = Get-Item $InputFile
    $sizeRatio = $outputInfo.Length / $inputInfo.Length
    if ($sizeRatio -lt 0.1) {
        Write-Warning "Output is less than 10% of input size. Verify transformation is correct."
    }

    # Check 4: Verify expected content pattern (optional)
    $firstLine = Get-Content $OutputFile -TotalCount 1
    if ([string]::IsNullOrWhiteSpace($firstLine)) {
        Write-Warning "First line of output is blank."
    }

    # Summary
    Write-Host "Transformation complete:" -ForegroundColor Green
    Write-Host "  Files processed: $filesProcessed"
    Write-Host "  Input size:      $($inputInfo.Length) bytes"
    Write-Host "  Output size:     $($outputInfo.Length) bytes"
    Write-Host "  Duration:        $($duration.TotalSeconds.ToString('F2')) seconds"
}
catch {
    Write-Error "Verification failed: $_"
    exit 1
}
finally {
    $tp.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($tp) | Out-Null
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
}

Batch Folder Processing

Process all files in one or more folders, with logging and per-folder summaries. Useful for nightly batch jobs or scheduled data pipeline stages.

# Batch folder processing with TextPipe COM
# Process multiple source folders and log results

$filterPath = "C:\Filters\data_standardization.fll"
$logFile = "C:\Logs\textpipe_batch_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"

$sourceFolders = @(
    @{ Path = "C:\Data\Sales";     Mask = "*.csv"; Recurse = $true },
    @{ Path = "C:\Data\Marketing"; Mask = "*.tsv"; Recurse = $false },
    @{ Path = "C:\Data\Finance";   Mask = "*.txt"; Recurse = $true }
)

$tp = New-Object -ComObject TextPipe.Application

try {
    $tp.Silent = $true
    $tp.OverwriteOutput = $true
    $tp.LogFile = $logFile

    # Load the transformation filter once
    if (-not $tp.LoadFilter($filterPath)) {
        throw "Failed to load filter: $($tp.GetLastError())"
    }

    $totalProcessed = 0

    foreach ($folder in $sourceFolders) {
        # Clear previous input files
        $tp.ClearInputFiles()

        # Add folder contents
        $tp.AddInputFolder($folder.Path, $folder.Mask, $folder.Recurse)

        # Create output folder alongside source
        $outputPath = Join-Path $folder.Path "Processed"
        if (-not (Test-Path $outputPath)) {
            New-Item -ItemType Directory -Path $outputPath -Force | Out-Null
        }
        $tp.OutputFolder = $outputPath

        # Execute
        $count = $tp.Go()
        $totalProcessed += $count

        Write-Host "Folder: $($folder.Path) - Processed $count files -> $outputPath"
    }

    Write-Host "`nBatch complete. Total files processed: $totalProcessed" -ForegroundColor Green
    Write-Host "Log file: $logFile"
}
catch {
    Write-Error "Batch processing failed: $_"
    exit 1
}
finally {
    $tp.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($tp) | Out-Null
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
}

Error Handling Patterns

Robust error handling is essential for production automation. Use structured try/catch/finally to handle COM errors, validate inputs, and guarantee cleanup.

# Robust error handling pattern for TextPipe COM automation
function Invoke-TextPipeTransform {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateScript({ Test-Path $_ -PathType Leaf })]
        [string]$FilterPath,

        [Parameter(Mandatory)]
        [ValidateScript({ Test-Path $_ -PathType Leaf })]
        [string]$InputFile,

        [Parameter(Mandatory)]
        [string]$OutputFile,

        [int]$TimeoutSeconds = 300
    )

    # Validate output directory exists
    $outputDir = Split-Path $OutputFile -Parent
    if (-not (Test-Path $outputDir)) {
        New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
    }

    $tp = $null

    try {
        # Attempt COM instantiation
        $tp = New-Object -ComObject TextPipe.Application -ErrorAction Stop
    }
    catch [System.Runtime.InteropServices.COMException] {
        throw "TextPipe COM object not registered. Run: regsvr32 TextPipe.dll (as Administrator)"
    }
    catch {
        throw "Cannot create TextPipe COM object: $_"
    }

    try {
        $tp.Silent = $true
        $tp.Visible = $false
        $tp.OverwriteOutput = $true

        # Load filter with validation
        if (-not $tp.LoadFilter($FilterPath)) {
            $err = $tp.GetLastError()
            throw "Filter load failed for '$FilterPath': $err"
        }

        # Add input and configure output
        if (-not $tp.AddInputFile($InputFile)) {
            throw "Cannot add input file '$InputFile': $($tp.GetLastError())"
        }
        $tp.OutputFile = $OutputFile

        # Execute with timeout monitoring
        $job = Start-Job -ScriptBlock {
            param($tpRef) $tpRef.Go()
        } -ArgumentList $tp

        $completed = $job | Wait-Job -Timeout $TimeoutSeconds
        if (-not $completed) {
            $job | Stop-Job
            throw "Transformation timed out after $TimeoutSeconds seconds"
        }

        $filesProcessed = $job | Receive-Job
        $job | Remove-Job

        if ($filesProcessed -eq 0) {
            $err = $tp.GetLastError()
            throw "No files processed. Error: $err"
        }

        # Return result object
        [PSCustomObject]@{
            Success        = $true
            FilesProcessed = $filesProcessed
            OutputFile     = $OutputFile
            OutputSize     = (Get-Item $OutputFile).Length
        }
    }
    catch {
        [PSCustomObject]@{
            Success = $false
            Error   = $_.Exception.Message
        }
    }
    finally {
        if ($tp) {
            $tp.Quit()
            [System.Runtime.InteropServices.Marshal]::ReleaseComObject($tp) | Out-Null
            [System.GC]::Collect()
            [System.GC]::WaitForPendingFinalizers()
        }
    }
}

# Usage example
$result = Invoke-TextPipeTransform `
    -FilterPath "C:\Filters\ebcdic_to_ascii.fll" `
    -InputFile "C:\Data\mainframe_extract.dat" `
    -OutputFile "C:\Data\converted_output.csv" `
    -TimeoutSeconds 600

if ($result.Success) {
    Write-Host "Success! Output: $($result.OutputFile) ($($result.OutputSize) bytes)"
} else {
    Write-Error "Failed: $($result.Error)"
    exit 1
}

Advanced: Reusable Function Module

Wrap TextPipe COM automation in a PowerShell module for reuse across scripts and scheduled tasks. This pattern ensures consistent cleanup and makes it easy to integrate into larger automation pipelines.

# TextPipeModule.psm1 - Reusable PowerShell module for TextPipe automation

function New-TextPipeSession {
    <#
    .SYNOPSIS
        Creates a new TextPipe COM automation session.
    .DESCRIPTION
        Instantiates the TextPipe.Application COM object configured for
        silent, unattended operation. Returns a session object that must
        be closed with Close-TextPipeSession when finished.
    #>
    [CmdletBinding()]
    param(
        [string]$LogFile
    )

    $tp = New-Object -ComObject TextPipe.Application
    $tp.Silent = $true
    $tp.Visible = $false
    $tp.OverwriteOutput = $true

    if ($LogFile) {
        $tp.LogFile = $LogFile
    }

    return $tp
}

function Close-TextPipeSession {
    <#
    .SYNOPSIS
        Cleanly releases a TextPipe COM session.
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object]$Session
    )

    $Session.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($Session) | Out-Null
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
}

function Invoke-TextPipeFilter {
    <#
    .SYNOPSIS
        Executes a TextPipe filter against input files.
    .EXAMPLE
        $session = New-TextPipeSession
        Invoke-TextPipeFilter -Session $session -Filter "C:\f.fll" -Input "C:\in.csv" -Output "C:\out.csv"
        Close-TextPipeSession -Session $session
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][object]$Session,
        [Parameter(Mandatory)][string]$Filter,
        [Parameter(Mandatory)][string]$Input,
        [Parameter(Mandatory)][string]$Output
    )

    $Session.ClearInputFiles()

    if (-not $Session.LoadFilter($Filter)) {
        throw "Load filter failed: $($Session.GetLastError())"
    }

    if (Test-Path $Input -PathType Container) {
        $Session.AddInputFolder($Input, "*.*", $true)
        $Session.OutputFolder = $Output
    } else {
        $Session.AddInputFile($Input)
        $Session.OutputFile = $Output
    }

    $count = $Session.Go()
    if ($count -eq 0 -and $Session.GetLastError()) {
        throw "Execution failed: $($Session.GetLastError())"
    }

    return $count
}

Export-ModuleMember -Function New-TextPipeSession, Close-TextPipeSession, Invoke-TextPipeFilter

Using the Module

# Import and use the TextPipe module
Import-Module .\TextPipeModule.psm1

$session = New-TextPipeSession -LogFile "C:\Logs\transform.log"

try {
    # Process multiple transformations in one session
    $count1 = Invoke-TextPipeFilter -Session $session `
        -Filter "C:\Filters\step1_normalize.fll" `
        -Input "C:\Data\raw.csv" `
        -Output "C:\Data\normalized.csv"

    $count2 = Invoke-TextPipeFilter -Session $session `
        -Filter "C:\Filters\step2_validate.fll" `
        -Input "C:\Data\normalized.csv" `
        -Output "C:\Data\validated.csv"

    Write-Host "Pipeline complete: Step 1 ($count1 files), Step 2 ($count2 files)"
}
finally {
    Close-TextPipeSession -Session $session
}

Next Steps