# 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 {}