- shopfloor-setup/Keyence/09-Setup-Keyence.ps1: populate placeholder with
MSI install via msiexec and driver install via pnputil. Idempotent on
ProductCode {058E7194-...} and DriverStore entry. Logs to C:\Logs\Keyence\.
- shopfloor-setup/Keyence/installers/VR-6000 Series Software.msi: main
product (1.7 MB; pulled from Keyence6000.exe Inno wrapper's Windows
Installer cache, built with InstallShield 2019).
- shopfloor-setup/Keyence/drivers/: KEYENCE VR Series USB driver
package (.inf + .cat + Wdf/WinUsb co-installers). 2.7 MB, pulled from
DriverStore\FileRepository\keyence_vr_series.inf_amd64_b5e5eb0924d7b4ce.
- preinstall.json: add VC++ 2013 x64 Min + Add entries (PCTypes: ["*"])
as prereqs for VR-6000. GUIDs {A749D8E6-B613-...} and {929FBD26-9020-...}.
Staging footprint for non-Keyence PCs is unchanged (the 4.4 MB Keyence
payload lives under shopfloor-setup/Keyence/ which startnet.cmd only
xcopies for PCTYPE=Keyence). Rollout still requires dropping the two
VC++ 2013 x64 MSIs into \$PXE_IMAGES_DIR/dependencies/vcredist/2013-x64-{min,add}/
on the workstation running sync-preinstall.sh.
Rationale for bundling the MSI + driver locally rather than running
Keyence6000.exe: the Inno wrapper calls an InstallShield child (Setup.exe)
without silent flags, which hangs indefinitely in session 0 during
automated imaging. msiexec + pnputil from the extracted bundle runs
fully non-interactive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
141 lines
6.0 KiB
PowerShell
141 lines
6.0 KiB
PowerShell
# 09-Setup-Keyence.ps1 - Keyence type setup (runs during shopfloor-setup phase).
|
|
#
|
|
# Installs the VR-6000 Series Software MSI and the KEYENCE VR Series USB driver
|
|
# package. Both payloads ship inside this directory (shopfloor-setup\Keyence\)
|
|
# and are staged by startnet.cmd during WinPE, so they exist locally on disk
|
|
# when this script runs - no share mapping required.
|
|
#
|
|
# Layout (sibling to this .ps1):
|
|
# installers\VR-6000 Series Software.msi
|
|
# drivers\keyence_vr_series.inf
|
|
# drivers\KEYENCE_VR_SERIES.cat
|
|
# drivers\amd64\WdfCoInstaller01009.dll
|
|
# drivers\amd64\WinUsbCoinstaller2.dll
|
|
#
|
|
# Prereqs (installed earlier by 00-PreInstall-MachineApps.ps1 via preinstall.json):
|
|
# - Microsoft Visual C++ 2010 x86 + x64
|
|
# - Microsoft Visual C++ 2013 x86 + x64 (Min + Add)
|
|
# - Microsoft Visual C++ 2017/2022 x86 (UCRT covers 2015-2022)
|
|
#
|
|
# Idempotent: skips the MSI if the product GUID is already registered, skips
|
|
# the driver if the package is already in the DriverStore.
|
|
#
|
|
# Log: C:\Logs\Keyence\09-Setup-Keyence.log
|
|
|
|
$ErrorActionPreference = 'Continue'
|
|
|
|
$logDir = 'C:\Logs\Keyence'
|
|
$transcriptLog = Join-Path $logDir '09-Setup-Keyence.log'
|
|
|
|
$installerDir = Join-Path $PSScriptRoot 'installers'
|
|
$msiPath = Join-Path $installerDir 'VR-6000 Series Software.msi'
|
|
$msiLog = Join-Path $logDir 'VR-6000-msiexec.log'
|
|
$driverDir = Join-Path $PSScriptRoot 'drivers'
|
|
$driverInf = Join-Path $driverDir 'keyence_vr_series.inf'
|
|
|
|
# Product registration - ProductCode read from the MSI via msiinfo: must match
|
|
# the value baked in, so idempotency works after a successful install.
|
|
$productCode = '{058E7194-BDF8-4FA2-9D69-978BB0F25214}'
|
|
$expectedVersion = '4.3.7'
|
|
$uninstallRegPaths = @(
|
|
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$productCode",
|
|
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$productCode"
|
|
)
|
|
|
|
if (-not (Test-Path $logDir)) {
|
|
New-Item -Path $logDir -ItemType Directory -Force | Out-Null
|
|
}
|
|
|
|
try { Start-Transcript -Path $transcriptLog -Append -Force | Out-Null } catch {}
|
|
|
|
function Write-KeyenceLog {
|
|
param([string]$Message, [string]$Level = 'INFO')
|
|
$stamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
Write-Host "[$stamp] [$Level] $Message"
|
|
}
|
|
|
|
Write-KeyenceLog "================================================================"
|
|
Write-KeyenceLog "=== Keyence Setup session start (PID $PID) ==="
|
|
Write-KeyenceLog "Running as: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)"
|
|
Write-KeyenceLog "Script root: $PSScriptRoot"
|
|
Write-KeyenceLog "================================================================"
|
|
|
|
# --- Detection: skip MSI if already installed at expected version ---
|
|
$msiAlreadyInstalled = $false
|
|
foreach ($regPath in $uninstallRegPaths) {
|
|
if (Test-Path $regPath) {
|
|
$installed = (Get-ItemProperty $regPath -ErrorAction SilentlyContinue).DisplayVersion
|
|
if ($installed -eq $expectedVersion) {
|
|
Write-KeyenceLog "MSI already installed (DisplayVersion=$installed at $regPath) - skipping"
|
|
$msiAlreadyInstalled = $true
|
|
break
|
|
} else {
|
|
Write-KeyenceLog "MSI registered but at version '$installed' (expected $expectedVersion) - will reinstall" "WARN"
|
|
}
|
|
}
|
|
}
|
|
|
|
# --- Install MSI ---
|
|
if (-not $msiAlreadyInstalled) {
|
|
if (-not (Test-Path $msiPath)) {
|
|
Write-KeyenceLog "MSI not found at $msiPath - aborting MSI install" "ERROR"
|
|
} else {
|
|
$msiSize = [math]::Round((Get-Item $msiPath).Length / 1MB, 1)
|
|
Write-KeyenceLog "Installing VR-6000 Series Software MSI ($msiSize MB)"
|
|
$msiArgs = @(
|
|
'/i', "`"$msiPath`"",
|
|
'/qn', '/norestart',
|
|
'ALLUSERS=1', 'REBOOT=ReallySuppress',
|
|
'/L*v', "`"$msiLog`""
|
|
)
|
|
$sw = [Diagnostics.Stopwatch]::StartNew()
|
|
$proc = Start-Process -FilePath 'msiexec.exe' -ArgumentList $msiArgs -Wait -PassThru -NoNewWindow
|
|
$sw.Stop()
|
|
# msiexec exit codes we tolerate: 0 = success, 3010 = success w/ reboot recommended
|
|
if ($proc.ExitCode -eq 0 -or $proc.ExitCode -eq 3010) {
|
|
Write-KeyenceLog "MSI install succeeded (exit=$($proc.ExitCode), elapsed=$([int]$sw.Elapsed.TotalSeconds)s)"
|
|
} else {
|
|
Write-KeyenceLog "MSI install FAILED (exit=$($proc.ExitCode)). See $msiLog" "ERROR"
|
|
}
|
|
}
|
|
}
|
|
|
|
# --- Detection: skip driver if already in DriverStore ---
|
|
$driverAlreadyStaged = $false
|
|
try {
|
|
$pnpOut = & pnputil.exe /enum-drivers 2>&1 | Out-String
|
|
if ($pnpOut -match 'keyence_vr_series\.inf') {
|
|
Write-KeyenceLog "Keyence USB driver already in DriverStore - skipping"
|
|
$driverAlreadyStaged = $true
|
|
}
|
|
} catch {
|
|
Write-KeyenceLog "pnputil /enum-drivers failed ($($_.Exception.Message)); attempting install anyway" "WARN"
|
|
}
|
|
|
|
# --- Install driver ---
|
|
if (-not $driverAlreadyStaged) {
|
|
if (-not (Test-Path $driverInf)) {
|
|
Write-KeyenceLog "Driver INF not found at $driverInf - aborting driver install" "ERROR"
|
|
} else {
|
|
Write-KeyenceLog "Adding KEYENCE VR Series USB driver to DriverStore via pnputil"
|
|
$pnp = Start-Process -FilePath 'pnputil.exe' `
|
|
-ArgumentList '/add-driver', "`"$driverInf`"", '/install' `
|
|
-Wait -PassThru -NoNewWindow `
|
|
-RedirectStandardOutput (Join-Path $logDir 'pnputil-stdout.log') `
|
|
-RedirectStandardError (Join-Path $logDir 'pnputil-stderr.log')
|
|
# pnputil exit codes: 0 = ok, 3010 = ok (reboot recommended),
|
|
# 259 = ERROR_NO_MORE_ITEMS (no INFs matched)
|
|
if ($pnp.ExitCode -eq 0 -or $pnp.ExitCode -eq 3010) {
|
|
Write-KeyenceLog "Driver install succeeded (exit=$($pnp.ExitCode))"
|
|
} else {
|
|
Write-KeyenceLog "Driver install FAILED (exit=$($pnp.ExitCode))" "ERROR"
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-KeyenceLog "================================================================"
|
|
Write-KeyenceLog "=== Keyence Setup session end ==="
|
|
Write-KeyenceLog "================================================================"
|
|
|
|
try { Stop-Transcript | Out-Null } catch {}
|