Files
pxe-server/playbook/shopfloor-setup/gea-shopfloor-cmm/Install-PCDMISPDFConverter.ps1
cproudlock f49fa0f940 CMM: install PC-DMIS PDF converter (Amyuni) the standalone-MSI bypassed
Our patched standalone PC-DMIS MSI never installs the Amyuni Document
Converter 500 (system printer "PC-DMIS 50 Converter"). INSTALLPDFCONVERTER
is a Burn-bundle property the main MSI never reads (0 of 153 custom actions
reference it; not in the Property table), and the patched-MSI strategy
bypasses the bundle that would have chained the Amyuni install. The MSI only
lays the installer on disk at <installdir>\PDFDriverInstallFiles\
BatFileInstallPDF50.zip and nothing runs it.

Install-PCDMISPDFConverter.ps1 runs it: scans Program Files\Hexagon (and
Wai) for the laid-down zip, extracts it, parses the InstallPDF50.exe
invocation from the shipped bat (printer name + Wilcox licensee + license
code, read not hardcoded), and runs the exe directly from the extract dir
so sibling DLLs resolve. The shipped bat ends in `pause` (hangs under /qn)
so we never run it. InstallPDF50.exe creates the printer then hangs (same
trait as the bundle), so we poll for the printer and kill the stuck exe
once it appears. Idempotent: printer already present -> exit 0.

Wired as a PS1 manifest entry placed after the PC-DMIS MSIs (files must
exist on disk first), no _CmmVersion (one shared printer covers every
version), MarkerFile detection for one-shot at imaging.

Smoke tested on the win11 VM as SYSTEM: fresh install 7.2s (printer +
driver created), idempotent re-run 0.6s, both exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 14:10:54 -04:00

142 lines
6.7 KiB
PowerShell

<#
Install-PCDMISPDFConverter.ps1
Installs the PC-DMIS PDF converter (Amyuni Document Converter 500, printer name
"PC-DMIS 50 Converter").
Why this exists: our CMM image installs PC-DMIS from a patched STANDALONE MSI,
bypassing Hexagon's Burn bundle. The Amyuni PDF converter is NOT a custom action
in the main MSI (INSTALLPDFCONVERTER is a bundle property the MSI never reads).
The bundle would have run the Amyuni install as a separate chained step - which
we skip. The MSI does lay the installer down on disk at:
C:\Program Files\Hexagon\PC-DMIS <ver> 64-bit\PDFDriverInstallFiles\BatFileInstallPDF50.zip
but nothing ever executes it. This script does.
The zip ships InstallPDF50.exe + the Amyuni driver (amyuni.inf, acfpdf*.dll,
cdintf*.dll, atpdf500.cat) + InstallPDF50.bat. We do NOT run the .bat (it ends in
`pause` and hangs under /qn) - we parse its InstallPDF50.exe invocation (printer
name + Wilcox licensee + license code) and run that directly from the extracted
folder so the sibling DLLs resolve.
The converter is ONE system printer shared by every PC-DMIS version, so we install
from the first PDFDriverInstallFiles we find and stop once the printer exists.
Idempotent: if the "PC-DMIS 50 Converter" printer already exists, exits 0 without
reinstalling. Run as administrator / SYSTEM (driver install needs it).
Exit: 0 = printer present (installed or already there), 1 = failed.
#>
param(
[string]$PrinterName = 'PC-DMIS 50 Converter',
[string]$OutDir = 'C:\Logs\CMM'
)
$ErrorActionPreference = 'Continue'
New-Item -ItemType Directory -Path $OutDir -Force -ErrorAction SilentlyContinue | Out-Null
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
$log = Join-Path $OutDir "pdfconverter-$ts.log"
function Log($m){ $line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $m"; Write-Host $line; $line | Out-File -FilePath $log -Append -Encoding ascii }
function Test-ConverterPresent {
# Get-Printer is the authoritative check. Fall back to the printer-name key
# in the registry for old hosts where the Printing cmdlets are absent.
try { if (Get-Printer -Name $PrinterName -ErrorAction SilentlyContinue) { return $true } } catch {}
$k = 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Printers\' + $PrinterName
return (Test-Path $k)
}
Log "==== PC-DMIS PDF converter install on $env:COMPUTERNAME ===="
if (Test-ConverterPresent) {
Log "Printer '$PrinterName' already present - nothing to do."
exit 0
}
# Find the Amyuni installer the MSI laid down. PC-DMIS 2016 (vendor Wai) and
# 2019/2026 (vendor Hexagon) all install under Program Files\Hexagon, but scan
# both Hexagon and Wai trees to be safe.
$zips = @()
foreach ($root in @("$env:ProgramFiles\Hexagon","$env:ProgramFiles\Wai","${env:ProgramFiles(x86)}\Hexagon","${env:ProgramFiles(x86)}\Wai")) {
if (-not (Test-Path $root)) { continue }
$zips += Get-ChildItem -Path $root -Recurse -Filter 'BatFileInstallPDF50.zip' -ErrorAction SilentlyContinue
}
$zips = $zips | Sort-Object FullName -Unique
if (-not $zips) {
Log "ERROR: no BatFileInstallPDF50.zip found under any PC-DMIS install dir."
Log " PC-DMIS may not be installed yet, or PDFDriverInstallFiles is missing."
exit 1
}
Log ("Found {0} Amyuni installer zip(s):" -f $zips.Count)
$zips | ForEach-Object { Log " $($_.FullName)" }
Add-Type -AssemblyName System.IO.Compression.FileSystem
foreach ($zip in $zips) {
$stage = Join-Path $env:TEMP ("amyuni-pdf-" + $ts + "-" + [Guid]::NewGuid().ToString('N').Substring(0,6))
try {
New-Item -ItemType Directory -Path $stage -Force | Out-Null
[System.IO.Compression.ZipFile]::ExtractToDirectory($zip.FullName, $stage)
Log "Extracted $($zip.Name) -> $stage"
$exe = Join-Path $stage 'InstallPDF50.exe'
$bat = Join-Path $stage 'InstallPDF50.bat'
if (-not (Test-Path $exe)) { Log " no InstallPDF50.exe in zip - skipping"; continue }
# Parse InstallPDF50.bat for the exact InstallPDF50.exe arguments (printer
# name + Wilcox licensee + license code). License code can differ per
# version, so read it rather than hardcode. Strip the leading exe token.
$args = $null
if (Test-Path $bat) {
$cmd = (Get-Content $bat | Where-Object { $_ -match 'InstallPDF50\.exe' } | Select-Object -First 1)
if ($cmd) { $args = ($cmd -replace '(?i)^\s*[^"]*InstallPDF50\.exe\s*','').Trim() }
}
if (-not $args) {
# Fallback to the known-good invocation if the bat is missing/odd.
$args = '"' + $PrinterName + '" -n "Wilcox Associates, Inc."'
Log " WARN: could not parse args from bat - using fallback (no license code): $args"
} else {
Log " parsed install args from bat"
}
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = $exe
$psi.Arguments = $args
$psi.WorkingDirectory = $stage # sibling DLLs (cdintf64.dll, acfpdf*, amyuni.inf, atpdf500.cat) must resolve
$psi.UseShellExecute = $false
$psi.CreateNoWindow = $true
$psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
Log " running: InstallPDF50.exe $args (cwd=$stage)"
$proc = [System.Diagnostics.Process]::Start($psi)
# InstallPDF50.exe creates the printer in a few seconds but then hangs
# (does not self-exit, same as Hexagon's Burn bundle). Poll for the
# printer instead of blocking; once it appears, kill the hung exe and
# move on. Hard cap at 90s as a backstop for a genuinely stuck install.
$deadline = (Get-Date).AddSeconds(90)
$appeared = $false
while ((Get-Date) -lt $deadline) {
if ($proc.HasExited) { Log " InstallPDF50.exe exited on its own (code $($proc.ExitCode))"; break }
if (Test-ConverterPresent) { $appeared = $true; break }
Start-Sleep -Seconds 2
}
if (-not $proc.HasExited) {
Log (" printer {0} - killing InstallPDF50.exe (it does not self-exit)" -f $(if ($appeared) { 'present' } else { 'NOT present after 90s' }))
try { $proc.Kill() } catch {}
}
Start-Sleep -Seconds 2
if (Test-ConverterPresent) {
Log "SUCCESS: printer '$PrinterName' is now present."
Remove-Item $stage -Recurse -Force -ErrorAction SilentlyContinue
exit 0
}
Log " printer not present after this attempt - trying next zip if any."
} catch {
Log " ERROR during install from $($zip.Name): $($_.Exception.Message)"
} finally {
Remove-Item $stage -Recurse -Force -ErrorAction SilentlyContinue
}
}
if (Test-ConverterPresent) { Log "Printer '$PrinterName' present."; exit 0 }
Log "ERROR: printer '$PrinterName' still not present after all attempts."
exit 1