Keyence VR-6000 Series Software + USB driver deployment

- 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>
This commit is contained in:
cproudlock
2026-04-18 10:02:34 -04:00
parent 719a550be8
commit 22c59b889e
7 changed files with 251 additions and 9 deletions

View File

@@ -71,6 +71,26 @@
"DetectionPath": "HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F8CFEB22-A2E7-3971-9EDA-4B11EDEFC185}", "DetectionPath": "HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F8CFEB22-A2E7-3971-9EDA-4B11EDEFC185}",
"PCTypes": ["*"] "PCTypes": ["*"]
}, },
{
"_comment": "VC++ 2013 x64 Minimum Runtime - required by Keyence VR-6000 Series Software. Extracted from the Keyence installer's Windows Installer cache. Same REBOOT=ReallySuppress pattern as the x86 variants.",
"Name": "VC++ Redistributable 2013 x64 (Minimum)",
"Installer": "vcredist/2013-x64-min/installer.msi",
"Type": "MSI",
"InstallArgs": "/qn /norestart REBOOT=ReallySuppress NOVSUI=1 USING_EXUIH_SILENT=1",
"DetectionMethod": "Registry",
"DetectionPath": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{A749D8E6-B613-3BE3-8F5F-045C84EBA29B}",
"PCTypes": ["*"]
},
{
"_comment": "VC++ 2013 x64 Additional Runtime - required by Keyence VR-6000 Series Software. Pairs with the Minimum Runtime above.",
"Name": "VC++ Redistributable 2013 x64 (Additional)",
"Installer": "vcredist/2013-x64-add/installer.msi",
"Type": "MSI",
"InstallArgs": "/qn /norestart REBOOT=ReallySuppress NOVSUI=1 USING_EXUIH_SILENT=1",
"DetectionMethod": "Registry",
"DetectionPath": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{929FBD26-9020-399B-9A7A-751D61F0B942}",
"PCTypes": ["*"]
},
{ {
"_comment": "VC++ 2010 x64 - required by PC-DMIS 2016/2019 R2 on CMM PCs. PCDLRN.exe links against msvcr100.dll and the VS 2010 MFC DLLs which are only provided by this redistributable. Extracted from the PC-DMIS 2016 bundle's attached container (a1 payload). Silent install: /q /norestart. Detection: Uninstall key under the native x64 hive with fixed product GUID.", "_comment": "VC++ 2010 x64 - required by PC-DMIS 2016/2019 R2 on CMM PCs. PCDLRN.exe links against msvcr100.dll and the VS 2010 MFC DLLs which are only provided by this redistributable. Extracted from the PC-DMIS 2016 bundle's attached container (a1 payload). Silent install: /q /norestart. Detection: Uninstall key under the native x64 hive with fixed product GUID.",
"Name": "VC++ Redistributable 2010 x64", "Name": "VC++ Redistributable 2010 x64",

View File

@@ -1,12 +1,140 @@
# 09-Setup-Keyence.ps1 - Keyence-specific setup (runs after Shopfloor baseline) # 09-Setup-Keyence.ps1 - Keyence type setup (runs during shopfloor-setup phase).
# #
# PLACEHOLDER: add type-specific app installs when details are finalized. # Installs the VR-6000 Series Software MSI and the KEYENCE VR Series USB driver
# This script will be called by Run-ShopfloorSetup.ps1 as part of the # package. Both payloads ship inside this directory (shopfloor-setup\Keyence\)
# type-specific phase, after all baseline scripts have completed. # and are staged by startnet.cmd during WinPE, so they exist locally on disk
# when this script runs - no share mapping required.
# #
# For share-based installs, copy the pattern from CMM/09-Setup-CMM.ps1 # Layout (sibling to this .ps1):
# (credential lookup + share mount + install from share). # 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
Write-Host "=== Keyence Setup ===" $ErrorActionPreference = 'Continue'
Write-Host " (no type-specific apps configured yet)"
Write-Host "=== Keyence Setup Complete ===" $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 {}

View File

@@ -0,0 +1,94 @@
[Version]
Signature = "$Windows NT$"
DriverPackageDisplayName=%DESC%
Class = KeyenceUSB
ClassGuid={EE304F37-F588-45FC-91FE-B76873981AD1}
Provider = %ProviderName%
CatalogFile=KEYENCE_VR_SERIES.cat
DriverVer=03/26/2020,1.0.0.0
; ================== Class section ==================
[ClassInstall32]
Addreg=KeyenceClassReg
[KeyenceClassReg]
HKR,,,0,%ClassName%
HKR,,Icon,,-5
; ========== Manufacturer/Models sections ===========
[Manufacturer]
%ProviderName% = KEYENCE,NTamd64
[KEYENCE.NTamd64]
%USB\KeyenceDevice.DeviceDesc% =USB_Install,USB\VID_0720&PID_102C
; =================== Installation ===================
;[1]
[USB_Install]
Include=winusb.inf
Needs=WINUSB.NT
;[2]
[USB_Install.Services]
Include=winusb.inf
AddService=WinUSB,0x00000002,WinUSB_ServiceInstall
;[3]
[WinUSB_ServiceInstall]
DisplayName = %WinUSB_SvcDesc%
ServiceType = 1
StartType = 3
ErrorControl = 1
ServiceBinary = %12%\WinUSB.sys
;[4]
[USB_Install.Wdf]
KmdfService=WINUSB, WinUsb_Install
[WinUSB_Install]
KmdfLibraryVersion=1.9
;[5]
[USB_Install.HW]
AddReg=Dev_AddReg
[Dev_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,"{B708995F-18C9-4D52-8D29-41FF2BFBF215}"
;[6]
[USB_Install.CoInstallers]
AddReg=CoInstallers_AddReg
CopyFiles=CoInstallers_CopyFiles
[CoInstallers_AddReg]
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01009.dll,WdfCoInstaller","WinUsbCoinstaller2.dll"
[CoInstallers_CopyFiles]
WdfCoInstaller01009.dll
WinUsbCoinstaller2.dll
[DestinationDirs]
CoInstallers_CopyFiles=11
; ================= Source Media Section =====================
;[7]
[SourceDisksNames]
1 = %DISK_NAME%,,,\amd64
[SourceDisksFiles.amd64]
WinUSBCoInstaller2.dll=1
WdfCoInstaller01009.dll=1
; =================== Strings ===================
[Strings]
ProviderName="KEYENCE"
DESC="VR Series USB-Driver"
WinUSB_SvcDesc="WinUSB.sys : KEYENCE USB Device"
DISK_NAME="KEYENCE USB Driver Installation Disk"
ClassName="KEYENCE USB Device Class"
USB\KeyenceDevice.DeviceDesc="VR Series"