diff --git a/playbook/shopfloor-setup/gea-shopfloor-cmm/Install-PCDMISPDFConverter.ps1 b/playbook/shopfloor-setup/gea-shopfloor-cmm/Install-PCDMISPDFConverter.ps1 new file mode 100644 index 0000000..e4d92d1 --- /dev/null +++ b/playbook/shopfloor-setup/gea-shopfloor-cmm/Install-PCDMISPDFConverter.ps1 @@ -0,0 +1,141 @@ +<# +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 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 diff --git a/playbook/shopfloor-setup/gea-shopfloor-cmm/cmm-manifest.json b/playbook/shopfloor-setup/gea-shopfloor-cmm/cmm-manifest.json index 314a79a..c393ea3 100644 --- a/playbook/shopfloor-setup/gea-shopfloor-cmm/cmm-manifest.json +++ b/playbook/shopfloor-setup/gea-shopfloor-cmm/cmm-manifest.json @@ -29,6 +29,14 @@ "DetectionMethod": "Registry", "DetectionPath": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{81BACE1B-FB08-4DCF-8100-79911AD3EC1E}" }, + { + "_comment": "PC-DMIS PDF converter (Amyuni Document Converter 500; system printer 'PC-DMIS 50 Converter'). NOT installed by the PC-DMIS MSI: INSTALLPDFCONVERTER is a Burn-bundle property the standalone MSI never reads (0 of 153 custom actions reference it), and our patched-MSI strategy bypasses the bundle that would have chained the Amyuni install. The MSI only lays the installer down at \\PDFDriverInstallFiles\\BatFileInstallPDF50.zip; this entry runs it (InstallPDF50.exe directly - the shipped .bat ends in `pause` and hangs under /qn). MUST stay AFTER the PC-DMIS entries so the files exist on disk. One shared printer covers every installed version; script is idempotent and scans Program Files\\Hexagon (and Wai). MarkerFile keeps it one-shot at imaging.", + "Name": "PC-DMIS PDF Converter", + "Type": "PS1", + "Script": "Install-PCDMISPDFConverter.ps1", + "DetectionMethod": "MarkerFile", + "DetectionPath": "C:\\Logs\\CMM\\pdfconverter.installed" + }, { "_comment": "Protect Viewer - companion tool. Install for all versions.", "Name": "Protect Viewer",