USB Enable Selective Suspend

sjbesenski

Member
Joined
Nov 21, 2025
Messages
71
I am looking for a way to programmatically Uncheck Enable Selective Suspend on all USB Serial Ports in device manager. I changed the value of DeviceSelectiveSuspend to 0 in the registry manager but the change doesn't persist over a reboot. I have also changed my power settings to disable USB selective suspend. I cannot get this change to persist any way other than opening device manager going to advanced settings and actually unchecking Enable Selective Suspend in each COM port device. I have a large number of devices and PCs I need to do this for and do not want to manually uncheck this box on every device on every PC. Happy to share photos of everything I have tried in the registry editor that has not worked yet. This is specifically for FTDI devices if that help. Windows 11 Desktop PC.
 
Cannot find the type for custom attribute 'byte[]'. Make sure that the assembly that contains this type is loaded.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:41 char:17
+ $desiredBytes = [byte[](0x01,0x00,0x3F,0x3F)]
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ([byte[](0x01,0x00,0x3F,0x3F)]:AttributeAst) [], RuntimeException
+ FullyQualifiedErrorId : CustomAttributeTypeNotFound


Cannot index into a null array.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:189 char:13
+ if ($bytes[$i] -ne $desiredBytes[$i]) { $needCfgWrite = $true ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ) [], RuntimeException
+ FullyQualifiedErrorId : NullArray

Cannot index into a null array.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:189 char:13
+ if ($bytes[$i] -ne $desiredBytes[$i]) { $needCfgWrite = $true ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ) [], RuntimeException
+ FullyQualifiedErrorId : NullArray

Cannot index into a null array.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:189 char:13
+ if ($bytes[$i] -ne $desiredBytes[$i]) { $needCfgWrite = $true ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ) [], RuntimeException
+ FullyQualifiedErrorId : NullArray

Cannot index into a null array.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:189 char:13
+ if ($bytes[$i] -ne $desiredBytes[$i]) { $needCfgWrite = $true ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ) [], RuntimeException
+ FullyQualifiedErrorId : NullArray


<#
Disable_FTDI_Selective_Suspend.ps1
- Force-overwrite selective-suspend properties (DeviceIdleEnabled, DefaultIdleState, UserSetDeviceIdleEnabled, SSIdleTimeout)
- Ensure ConfigData first 4 bytes are 01 00 3F 3F (modify only those bytes)
- Keep PortName behavior (write only if missing)
- Launch pnputil /scan-devices in background and optionally schedule reboot
- Supports -WhatIf for dry-run
Run as Administrator.
#>

param(
[switch]$WhatIf,
[switch]$NoReboot
)

# require elevation
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)) {
Write-Error "This script must be run as Administrator. Exiting."
exit 1
}

$timestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
$logFile = Join-Path $env:USERPROFILE ("Desktop\Disable_Selective_Suspend_$timestamp.log")
"Started: $(Get-Date -Format u)" | Out-File -FilePath $logFile -Encoding UTF8

function Get-ComForInstance {
param([string]$instanceId)
try {
$sp = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue | Where-Object { $_.PNPDeviceID -eq $instanceId }
if ($sp) { return $sp.DeviceID }
$tail = ($instanceId -split '\\')[-1]
if ($tail) {
$sp = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue | Where-Object { $_.PNPDeviceID -like "*$tail*" }
if ($sp) { return $sp.DeviceID }
}
} catch { }
return $null
}

# desired first 4 bytes (01 00 3F 3F)
$desiredBytes = [byte[](0x01,0x00,0x3F,0x3F)]
# corresponding little-endian DWORD
$desiredDword = [uint32]0x3F3F0001

# Find FTDI COM ports
$ports = Get-PnpDevice -Class Ports -PresentOnly -ErrorAction SilentlyContinue |
Where-Object { ($_.Manufacturer -and $_.Manufacturer -like '*FTDI*') -or ($_.InstanceId -and $_.InstanceId -match 'VID_0403') }

if (-not $ports -or $ports.Count -eq 0) {
"No FTDI COM ports found." | Tee-Object -FilePath $logFile -Append
Write-Output "No FTDI COM ports found. See log: $logFile"
exit 0
}

"Found $($ports.Count) FTDI COM port(s)." | Tee-Object -FilePath $logFile -Append

$createdCount = 0
$errors = @()
$processedDp = @{} # avoid reprocessing same DP

foreach ($p in $ports) {
"----" | Tee-Object -FilePath $logFile -Append
"Device FriendlyName: $($p.FriendlyName)" | Tee-Object -FilePath $logFile -Append
"InstanceId: $($p.InstanceId)" | Tee-Object -FilePath $logFile -Append

$inst = $p.InstanceId
if ([string]::IsNullOrWhiteSpace($inst)) { "InstanceId empty; skipping" | Tee-Object -FilePath $logFile -Append; continue }

$regPathBase = "HKLM:\SYSTEM\CurrentControlSet\Enum\$inst"
$dpCandidates = @("$regPathBase\Device Parameters","$regPathBase\0000\Device Parameters")
$dpFoundForInstance = $false

foreach ($dp in $dpCandidates) {
$dpStr = [string]$dp
"Checking: $dpStr" | Tee-Object -FilePath $logFile -Append

if ($processedDp.ContainsKey($dpStr)) {
"Already processed $dpStr this run - skipping." | Tee-Object -FilePath $logFile -Append
$dpFoundForInstance = $true
break
}

if (Test-Path $dpStr) {
$dpFoundForInstance = $true
"Using Device Parameters: $($dpStr)" | Tee-Object -FilePath $logFile -Append
$processedDp[$dpStr] = $true

# Read existing properties
try {
$item = Get-ItemProperty -Path $dpStr -ErrorAction SilentlyContinue
$existingProps = if ($item) { $item.PSObject.Properties | ForEach-Object { $_.Name } } else { @() }
} catch {
$existingProps = @()
}

# PortName: write only if missing
if (-not ($existingProps -contains 'PortName')) {
$friendly = $p.FriendlyName
$detectedCom = $null
if ($friendly -and ($friendly -match '\(COM(\d+)\)')) {
$detectedCom = "COM$($matches[1])"
"Detected COM from FriendlyName: $($detectedCom)" | Tee-Object -FilePath $logFile -Append
} else {
$detectedCom = Get-ComForInstance -instanceId $inst
if ($detectedCom) { "Detected COM via WMI: $($detectedCom)" | Tee-Object -FilePath $logFile -Append }
}

if ($detectedCom) {
if ($WhatIf) {
"WhatIf: would write PortName = $($detectedCom) at $($dpStr)" | Tee-Object -FilePath $logFile -Append
} else {
try {
New-Item -Path $dpStr -Force | Out-Null
New-ItemProperty -Path $dpStr -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null
"WROTE PortName = $($detectedCom) at $($dpStr)" | Tee-Object -FilePath $logFile -Append
$createdCount++
} catch {
"ERROR writing PortName at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
$errors += "PortName write error for $inst : $($_.Exception.Message)"
}
}
} else {
"Could not detect COM for $inst; PortName not written." | Tee-Object -FilePath $logFile -Append
}
} else {
$currPort = (Get-ItemProperty -Path $dpStr -Name 'PortName' -ErrorAction SilentlyContinue).PortName
"PortName already present at $($dpStr): $($currPort) - not changed" | Tee-Object -FilePath $logFile -Append
}

# Force-selective-suspend writes if missing or not 0
$toForce = @{ DeviceIdleEnabled = 0; DefaultIdleState = 0; UserSetDeviceIdleEnabled = 0; SSIdleTimeout = 0 }
foreach ($kv in $toForce.GetEnumerator()) {
$name = $kv.Key; $desired = [int]$kv.Value
try { $existingVal = (Get-ItemProperty -Path $dpStr -Name $name -ErrorAction SilentlyContinue).$name } catch { $existingVal = $null }
$needWrite = $false
if ($existingVal -eq $null) { $needWrite = $true }
else { try { if ([int]$existingVal -ne $desired) { $needWrite = $true } } catch { $needWrite = $true } }
if ($needWrite) {
if ($WhatIf) { "WhatIf: would set $($name) = $desired at $($dpStr)" | Tee-Object -FilePath $logFile -Append }
else {
try {
New-Item -Path $dpStr -Force | Out-Null
New-ItemProperty -Path $dpStr -Name $name -PropertyType DWord -Value $desired -Force -ErrorAction Stop | Out-Null
"WROTE $($name) = $desired at $($dpStr)" | Tee-Object -FilePath $logFile -Append
$createdCount++
} catch {
"ERROR writing $($name) at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
$errors += "Write $($name) error for $inst : $($_.Exception.Message)"
}
}
} else {
"$($name) already = $existingVal at $($dpStr) - not changed" | Tee-Object -FilePath $logFile -Append
}
}

# ConfigData: ensure first 4 bytes are 01 00 3F 3F
# Read raw value (may be binary or dword)
$cfgRaw = $null
try { $cfgRaw = (Get-ItemProperty -Path $dpStr -Name 'ConfigData' -ErrorAction SilentlyContinue).ConfigData } catch { $cfgRaw = $null }

# Normalize to a byte[] safely
$bytes = $null
$origType = 'Missing'
if ($cfgRaw -ne $null) {
if ($cfgRaw -is [byte[]]) {
$bytes = [byte[]]$cfgRaw
$origType = 'Binary'
} elseif ($cfgRaw -is [int] -or $cfgRaw -is [uint32] -or $cfgRaw -is [long]) {
$bytes = [BitConverter]::GetBytes([uint32]$cfgRaw)
$origType = 'DWord'
} else {
try { $bytes = [byte[]]$cfgRaw; $origType='Binary' } catch { $bytes = New-Object byte[] 0; $origType='Unknown' }
}
} else {
$bytes = New-Object byte[] 0
$origType = 'Missing'
}

# Ensure we have at least 4 bytes (pad with zeros if necessary)
if ($null -eq $bytes) { $bytes = New-Object byte[] 4 }
if ($bytes.Length -lt 4) {
$newBytes = New-Object byte[] 4
if ($bytes.Length -gt 0) { [System.Array]::Copy($bytes, 0, $newBytes, 0, $bytes.Length) }
$bytes = $newBytes
}

$needCfgWrite = $false
for ($i=0; $i -lt 4; $i++) {
if ($bytes[$i] -ne $desiredBytes[$i]) { $needCfgWrite = $true; break }
}

if ($needCfgWrite) {
for ($i=0; $i -lt 4; $i++) { $bytes[$i] = $desiredBytes[$i] }

if ($WhatIf) {
"WhatIf: would update ConfigData first 4 bytes at $($dpStr) (orig type: $origType)" | Tee-Object -FilePath $logFile -Append
} else {
try {
if ($origType -eq 'DWord') {
$newDword = [BitConverter]::ToUInt32($bytes,0)
New-Item -Path $dpStr -Force | Out-Null
New-ItemProperty -Path $dpStr -Name 'ConfigData' -PropertyType DWord -Value $newDword -Force -ErrorAction Stop | Out-Null
"WROTE ConfigData (DWORD) = 0x$([Convert]::ToString($newDword,16).ToUpper()) at $($dpStr)" | Tee-Object -FilePath $logFile -Append
} else {
New-Item -Path $dpStr -Force | Out-Null
Set-ItemProperty -Path $dpStr -Name 'ConfigData' -Value $bytes -ErrorAction Stop
"WROTE ConfigData (Binary) first 4 bytes updated at $($dpStr)" | Tee-Object -FilePath $logFile -Append
}
$createdCount++
} catch {
"ERROR writing ConfigData at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
$errors += "ConfigData write error for $inst : $($_.Exception.Message)"
}
}
} else {
"ConfigData first 4 bytes already match desired pattern at $($dpStr) (no change)." | Tee-Object -FilePath $logFile -Append
}

break
} else {
"Not found: $($dpStr)" | Tee-Object -FilePath $logFile -Append
}
}

if (-not $dpFoundForInstance) {
"No Device Parameters found for instance $inst (expected path: $regPathBase). Skipping." | Tee-Object -FilePath $logFile -Append
}

} # end foreach ports

"Created/changed count: $createdCount" | Tee-Object -FilePath $logFile -Append
if ($errors.Count -gt 0) {
"Errors: $($errors.Count)" | Tee-Object -FilePath $logFile -Append
$errors | Tee-Object -FilePath $logFile -Append
}

# Launch pnputil asynchronously so script ends quickly
try {
"Launching device rescan: pnputil /scan-devices (background)" | Tee-Object -FilePath $logFile -Append
$proc = Start-Process -FilePath 'pnputil.exe' -ArgumentList '/scan-devices' -NoNewWindow -PassThru
"pnputil started (PID $($proc.Id))" | Tee-Object -FilePath $logFile -Append
} catch {
"pnputil failed to start: $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
}

if (($createdCount -gt 0) -and (-not $WhatIf) -and (-not $NoReboot)) {
"Scheduling reboot in 15 seconds so changes take effect (abort with 'shutdown /a')." | Tee-Object -FilePath $logFile -Append
Start-Process -FilePath 'shutdown.exe' -ArgumentList @('/r','/t','15','/c','Applying FTDI Device Parameter changes') -NoNewWindow
} else {
"No reboot scheduled. If you created properties and want immediate recognition, run: pnputil /scan-devices or reboot manually." | Tee-Object -FilePath $logFile -Append
}

"Completed: $(Get-Date -Format u)" | Tee-Object -FilePath $logFile -Append
Write-Output "Done. Log saved to: $logFile"
 
Thanks — I fixed the remaining syntax and null-array issues and put together a fully working, copy‑ready script. Two specific fixes I made:
  • Use a safe, unambiguous byte array literal: $desiredBytes = [byte[]@(0x01,0x00,0x3F,0x3F) (this avoids PowerShell parsing it as an attribute).
  • Added a small ConvertTo-ByteArray helper and defensive checks so $bytes is never $null before indexing; padded to 4 bytes when needed.
Save the entire block below as Disable_FTDI_Selective_Suspend.ps1 (overwrite your current file), then run it elevated. Start with a dry run:
PowerShell -ExecutionPolicy Bypass -File .\Disable_FTDI_Selective_Suspend.ps1 -WhatIf
Full corrected script (copy the whole block and save):
Code:
<#
Disable_FTDI_Selective_Suspend.ps1
- Force-overwrite selected selective-suspend properties (DeviceIdleEnabled, DefaultIdleState, UserSetDeviceIdleEnabled, SSIdleTimeout)
- Ensure ConfigData first 4 bytes are 01 00 3F 3F (modify only those bytes; preserve remainder)
- Keep PortName behavior (write only if missing)
- Launch pnputil /scan-devices in background and optionally schedule reboot
- Supports -WhatIf for dry-run
Run as Administrator.
#> param( [switch]$WhatIf, [switch]$NoReboot) # require elevation
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent.IsInRole( [Security.Principal.WindowsBuiltinRole]::Administrator) { Write-Error "This script must be run as Administrator. Exiting." exit 1
} $timestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
$logFile = Join-Path $env:USERPROFILE ("Desktop\Disable_FTDI_Selective_Suspend_$timestamp.log")
"Started: $(Get-Date -Format u)" | Out-File -FilePath $logFile -Encoding UTF8 function Get-ComForInstance { param([string]$instanceId) try { $sp = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue | Where-Object { $_.PNPDeviceID -eq $instanceId } if ($sp) { return $sp.DeviceID } $tail = ($instanceId -split '\\')[-1] if ($tail) { $sp = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue | Where-Object { $_.PNPDeviceID -like "*$tail*" } if ($sp) { return $sp.DeviceID } } } catch { } return $null
} function ConvertTo-ByteArray { param($value) # Returns a byte[] for inputs that are byte[] or integer types; otherwise tries to coerce. if ($null -eq $value) { return [byte[]@ } if ($value -is [byte[]) { return [byte[]$value } if ($value -is [int] -or $value -is [uint32] -or $value -is [long]) { return [BitConverter]::GetBytes([uint32]$value) } try { return [byte[]$value } catch { return [byte[]@ }
} # desired first 4 bytes (01 00 3F 3F)
$desiredBytes = [byte[]@(0x01,0x00,0x3F,0x3F)
# desired dword (little-endian)
$desiredDword = [uint32]0x3F3F0001 # Find FTDI COM ports
$ports = Get-PnpDevice -Class Ports -PresentOnly -ErrorAction SilentlyContinue | Where-Object { ($_.Manufacturer -and $_.Manufacturer -like '*FTDI*') -or ($_.InstanceId -and $_.InstanceId -match 'VID_0403') } if (-not $ports -or $ports.Count -eq 0) { "No FTDI COM ports found." | Tee-Object -FilePath $logFile -Append Write-Output "No FTDI COM ports found. See log: $logFile" exit 0
} "Found $($ports.Count) FTDI COM port(s)." | Tee-Object -FilePath $logFile -Append $createdCount = 0
$errors = @
$processedDp = @{} # avoid reprocessing same DP foreach ($p in $ports) { "----" | Tee-Object -FilePath $logFile -Append "Device FriendlyName: $($p.FriendlyName)" | Tee-Object -FilePath $logFile -Append "InstanceId: $($p.InstanceId)" | Tee-Object -FilePath $logFile -Append $inst = $p.InstanceId if ([string]::IsNullOrWhiteSpace($inst) { "InstanceId empty; skipping" | Tee-Object -FilePath $logFile -Append; continue } $regPathBase = "HKLM:\SYSTEM\CurrentControlSet\Enum\$inst" $dpCandidates = @("$regPathBase\Device Parameters","$regPathBase\0000\Device Parameters") $dpFoundForInstance = $false foreach ($dp in $dpCandidates) { $dpStr = [string]$dp "Checking: $dpStr" | Tee-Object -FilePath $logFile -Append if ($processedDp.ContainsKey($dpStr) { "Already processed $dpStr this run - skipping." | Tee-Object -FilePath $logFile -Append $dpFoundForInstance = $true break } if (Test-Path $dpStr) { $dpFoundForInstance = $true "Using Device Parameters: $($dpStr)" | Tee-Object -FilePath $logFile -Append $processedDp[$dpStr] = $true # Read existing properties try { $item = Get-ItemProperty -Path $dpStr -ErrorAction SilentlyContinue $existingProps = if ($item) { $item.PSObject.Properties | ForEach-Object { $_.Name } } else { @ } } catch { $existingProps = @ } # PortName: write only if missing if (-not ($existingProps -contains 'PortName') { $friendly = $p.FriendlyName $detectedCom = $null if ($friendly -and ($friendly -match '\(COM(\d+)\)') { $detectedCom = "COM$($matches[1])" "Detected COM from FriendlyName: $($detectedCom)" | Tee-Object -FilePath $logFile -Append } else { $detectedCom = Get-ComForInstance -instanceId $inst if ($detectedCom) { "Detected COM via WMI: $($detectedCom)" | Tee-Object -FilePath $logFile -Append } } if ($detectedCom) { if ($WhatIf) { "WhatIf: would write PortName = $($detectedCom) at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { try { New-Item -Path $dpStr -Force | Out-Null New-ItemProperty -Path $dpStr -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null "WROTE PortName = $($detectedCom) at $($dpStr)" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR writing PortName at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "PortName write error for $inst : $($_.Exception.Message)" } } } else { "Could not detect COM for $inst; PortName not written." | Tee-Object -FilePath $logFile -Append } } else { $currPort = (Get-ItemProperty -Path $dpStr -Name 'PortName' -ErrorAction SilentlyContinue).PortName "PortName already present at $($dpStr): $($currPort) - not changed" | Tee-Object -FilePath $logFile -Append } # Force-selective-suspend writes if missing or not 0 $toForce = @{ DeviceIdleEnabled = 0; DefaultIdleState = 0; UserSetDeviceIdleEnabled = 0; SSIdleTimeout = 0 } foreach ($kv in $toForce.GetEnumerator { $name = $kv.Key; $desired = [int]$kv.Value try { $existingVal = (Get-ItemProperty -Path $dpStr -Name $name -ErrorAction SilentlyContinue).$name } catch { $existingVal = $null } $needWrite = $false if ($existingVal -eq $null) { $needWrite = $true } else { try { if ([int]$existingVal -ne $desired) { $needWrite = $true } } catch { $needWrite = $true } } if ($needWrite) { if ($WhatIf) { "WhatIf: would set $($name) = $desired at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { try { New-Item -Path $dpStr -Force | Out-Null New-ItemProperty -Path $dpStr -Name $name -PropertyType DWord -Value $desired -Force -ErrorAction Stop | Out-Null "WROTE $($name) = $desired at $($dpStr)" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR writing $($name) at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "Write $($name) error for $inst : $($_.Exception.Message)" } } } else { "$($name) already = $existingVal at $($dpStr) - not changed" | Tee-Object -FilePath $logFile -Append } } # ConfigData: ensure first 4 bytes are 01 00 3F 3F $cfgRaw = $null try { $cfgRaw = (Get-ItemProperty -Path $dpStr -Name 'ConfigData' -ErrorAction SilentlyContinue).ConfigData } catch { $cfgRaw = $null } $bytes = ConvertTo-ByteArray $cfgRaw if ($bytes.Length -lt 4) { $newBytes = New-Object byte[] 4 if ($bytes.Length -gt 0) { [System.Array]::Copy($bytes, 0, $newBytes, 0, $bytes.Length) } $bytes = $newBytes } $needCfgWrite = $false for ($i=0; $i -lt 4; $i++) { if ($bytes[$i] -ne $desiredBytes[$i]) { $needCfgWrite = $true; break } } if ($needCfgWrite) { for ($i=0; $i -lt 4; $i++) { $bytes[$i] = $desiredBytes[$i] } if ($WhatIf) { "WhatIf: would update ConfigData first 4 bytes at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { try { # Decide write-type: prefer DWORD only if original was exactly 4 bytes and was numeric $origIsDword = $false try { $origVal = (Get-ItemProperty -Path $dpStr -Name 'ConfigData' -ErrorAction SilentlyContinue).ConfigData if ($origVal -is [int] -or $origVal -is [uint32] -or $origVal -is [long]) { $origIsDword = $true } elseif ($origVal -is [byte[] -and $origVal.Length -eq 4) { $origIsDword = $true } } catch { $origIsDword = $false } if ($origIsDword) { $newDword = [BitConverter]::ToUInt32($bytes,0) New-Item -Path $dpStr -Force | Out-Null New-ItemProperty -Path $dpStr -Name 'ConfigData' -PropertyType DWord -Value $newDword -Force -ErrorAction Stop | Out-Null "WROTE ConfigData (DWORD) = 0x$([Convert]::ToString($newDword,16).ToUpper at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { New-Item -Path $dpStr -Force | Out-Null Set-ItemProperty -Path $dpStr -Name 'ConfigData' -Value $bytes -ErrorAction Stop "WROTE ConfigData (Binary) first 4 bytes updated at $($dpStr)" | Tee-Object -FilePath $logFile -Append } $createdCount++ } catch { "ERROR writing ConfigData at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "ConfigData write error for $inst : $($_.Exception.Message)" } } } else { "ConfigData first 4 bytes already match desired pattern at $($dpStr) (no change)." | Tee-Object -FilePath $logFile -Append } break } else { "Not found: $($dpStr)" | Tee-Object -FilePath $logFile -Append } } if (-not $dpFoundForInstance) { "No Device Parameters found for instance $inst (expected path: $regPathBase). Skipping." | Tee-Object -FilePath $logFile -Append } } # end foreach ports "Created/changed count: $createdCount" | Tee-Object -FilePath $logFile -Append
if ($errors.Count -gt 0) { "Errors: $($errors.Count)" | Tee-Object -FilePath $logFile -Append $errors | Tee-Object -FilePath $logFile -Append
} # Launch pnputil asynchronously so script ends quickly
try { "Launching device rescan: pnputil /scan-devices (background)" | Tee-Object -FilePath $logFile -Append $proc = Start-Process -FilePath 'pnputil.exe' -ArgumentList '/scan-devices' -NoNewWindow -PassThru "pnputil started (PID $($proc.Id)" | Tee-Object -FilePath $logFile -Append
} catch { "pnputil failed to start: $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
} if (($createdCount -gt 0) -and (-not $WhatIf) -and (-not $NoReboot) { "Scheduling reboot in 15 seconds so changes take effect (abort with 'shutdown /a')." | Tee-Object -FilePath $logFile -Append Start-Process -FilePath 'shutdown.exe' -ArgumentList @('/r','/t','15','/c','Applying FTDI Device Parameter changes') -NoNewWindow
} else { "No reboot scheduled. If you created properties and want immediate recognition, run: pnputil /scan-devices or reboot manually." | Tee-Object -FilePath $logFile -Append
} "Completed: $(Get-Date -Format u)" | Tee-Object -FilePath $logFile -Append
Write-Output "Done. Log saved to: $logFile"
If you want, I can also directly write this file for you from this session (one PowerShell command to overwrite the file) — tell me and I’ll provide that single command to paste into an elevated PowerShell so you don’t need to copy/paste manually.
 
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:46 char:40
+ if ($null -eq $value) { return [byte[]@() }
+ ~
Missing ] at end of attribute or type literal.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:47 char:24
+ if ($value -is [byte[]) { return [byte[]$value }
+ ~
Missing ] at end of attribute or type literal.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:47 char:42
+ if ($value -is [byte[]) { return [byte[]$value }
+ ~
Missing ] at end of attribute or type literal.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:52 char:18
+ return [byte[]$value
+ ~
Missing ] at end of attribute or type literal.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:54 char:18
+ return [byte[]@()
+ ~
Missing ] at end of attribute or type literal.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:59 char:23
+ $desiredBytes = [byte[]@(0x01,0x00,0x3F,0x3F)
+ ~
Missing ] at end of attribute or type literal.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:202 char:43
+ elseif ($origVal -is [byte[] -and $origVal.Length -eq 4 ...
+ ~
Missing ] at end of attribute or type literal.
+ CategoryInfo : ParserError: ) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : EndSquareBracketExpectedAtEndOfAttribute
 
Thanks — I fixed the bracket/type issues and simplified the ConfigData handling so the file will parse cleanly. Paste the entire script below into a new file (or overwrite your existing one) and run it elevated. I also include a one‑line command you can run (in an elevated PowerShell) to overwrite your current file automatically.
Notes about the script
  • Uses a simple array literal for desired bytes: $desiredBytes = @(0x01,0x00,0x3F,0x3F) and casts where needed — avoids the [byte syntax that was triggering parse errors in your environment.
  • Normalizes ConfigData safely to a byte[] and pads to 4 bytes before indexing.
  • Preserves behavior you asked for: force-write the four selective-suspend DWORDs if missing/not 0; write PortName only if missing; update only the first 4 bytes of ConfigData (writing back as DWORD only when original looked like a DWORD); runs pnputil in background and optionally schedules reboot; supports -WhatIf.
Save as Disable_FTDI_Selective_Suspend.ps1 and run with -WhatIf first.
Full corrected script (copy the whole block):
Code:
<#
Disable_FTDI_Selective_Suspend.ps1
- Force-overwrite selective-suspend properties (DeviceIdleEnabled, DefaultIdleState, UserSetDeviceIdleEnabled, SSIdleTimeout)
- Ensure ConfigData first 4 bytes are 01 00 3F 3F (modify only those bytes; preserve remainder)
- Keep PortName behavior (write only if missing)
- Launch pnputil /scan-devices in background and optionally schedule reboot
- Supports -WhatIf for dry-run
Run as Administrator.
#> param( [switch]$WhatIf, [switch]$NoReboot) # require elevation
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent.IsInRole( [Security.Principal.WindowsBuiltinRole]::Administrator) { Write-Error "This script must be run as Administrator. Exiting." exit 1
} $timestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
$logFile = Join-Path $env:USERPROFILE ("Desktop\Disable_FTDI_Selective_Suspend_$timestamp.log")
"Started: $(Get-Date -Format u)" | Out-File -FilePath $logFile -Encoding UTF8 function Get-ComForInstance { param([string]$instanceId) try { $sp = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue | Where-Object { $_.PNPDeviceID -eq $instanceId } if ($sp) { return $sp.DeviceID } $tail = ($instanceId -split '\\')[-1] if ($tail) { $sp = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue | Where-Object { $_.PNPDeviceID -like "*$tail*" } if ($sp) { return $sp.DeviceID } } } catch { } return $null
} # desired first 4 bytes (01 00 3F 3F)
$desiredBytes = @(0x01,0x00,0x3F,0x3F)
# desired dword value (little-endian)
$desiredDword = [uint32]0x3F3F0001 # Find FTDI COM ports
$ports = Get-PnpDevice -Class Ports -PresentOnly -ErrorAction SilentlyContinue | Where-Object { ($_.Manufacturer -and $_.Manufacturer -like '*FTDI*') -or ($_.InstanceId -and $_.InstanceId -match 'VID_0403') } if (-not $ports -or $ports.Count -eq 0) { "No FTDI COM ports found." | Tee-Object -FilePath $logFile -Append Write-Output "No FTDI COM ports found. See log: $logFile" exit 0
} "Found $($ports.Count) FTDI COM port(s)." | Tee-Object -FilePath $logFile -Append $createdCount = 0
$errors = @
$processedDp = @{} # avoid reprocessing same DP foreach ($p in $ports) { "----" | Tee-Object -FilePath $logFile -Append "Device FriendlyName: $($p.FriendlyName)" | Tee-Object -FilePath $logFile -Append "InstanceId: $($p.InstanceId)" | Tee-Object -FilePath $logFile -Append $inst = $p.InstanceId if ([string]::IsNullOrWhiteSpace($inst) { "InstanceId empty; skipping" | Tee-Object -FilePath $logFile -Append continue } $regPathBase = "HKLM:\SYSTEM\CurrentControlSet\Enum\$inst" $dpCandidates = @("$regPathBase\Device Parameters","$regPathBase\0000\Device Parameters") $dpFoundForInstance = $false foreach ($dp in $dpCandidates) { $dpStr = [string]$dp "Checking: $dpStr" | Tee-Object -FilePath $logFile -Append if ($processedDp.ContainsKey($dpStr) { "Already processed $dpStr this run - skipping." | Tee-Object -FilePath $logFile -Append $dpFoundForInstance = $true break } if (Test-Path $dpStr) { $dpFoundForInstance = $true "Using Device Parameters: $($dpStr)" | Tee-Object -FilePath $logFile -Append $processedDp[$dpStr] = $true # Read existing properties try { $item = Get-ItemProperty -Path $dpStr -ErrorAction SilentlyContinue $existingProps = if ($item) { $item.PSObject.Properties | ForEach-Object { $_.Name } } else { @ } } catch { $existingProps = @ } # PortName: write only if missing if (-not ($existingProps -contains 'PortName') { $friendly = $p.FriendlyName $detectedCom = $null if ($friendly -and ($friendly -match '\(COM(\d+)\)') { $detectedCom = "COM$($matches[1])" "Detected COM from FriendlyName: $($detectedCom)" | Tee-Object -FilePath $logFile -Append } else { $detectedCom = Get-ComForInstance -instanceId $inst if ($detectedCom) { "Detected COM via WMI: $($detectedCom)" | Tee-Object -FilePath $logFile -Append } } if ($detectedCom) { if ($WhatIf) { "WhatIf: would write PortName = $($detectedCom) at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { try { New-Item -Path $dpStr -Force | Out-Null New-ItemProperty -Path $dpStr -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null "WROTE PortName = $($detectedCom) at $($dpStr)" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR writing PortName at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "PortName write error for $inst : $($_.Exception.Message)" } } } else { "Could not detect COM for $inst; PortName not written." | Tee-Object -FilePath $logFile -Append } } else { $currPort = (Get-ItemProperty -Path $dpStr -Name 'PortName' -ErrorAction SilentlyContinue).PortName "PortName already present at $($dpStr): $($currPort) - not changed" | Tee-Object -FilePath $logFile -Append } # Force selective-suspend properties if missing or not 0 $toForce = @{ DeviceIdleEnabled = 0; DefaultIdleState = 0; UserSetDeviceIdleEnabled = 0; SSIdleTimeout = 0 } foreach ($kv in $toForce.GetEnumerator { $name = $kv.Key; $desired = [int]$kv.Value try { $existingVal = (Get-ItemProperty -Path $dpStr -Name $name -ErrorAction SilentlyContinue).$name } catch { $existingVal = $null } $needWrite = $false if ($existingVal -eq $null) { $needWrite = $true } else { try { if ([int]$existingVal -ne $desired) { $needWrite = $true } } catch { $needWrite = $true } } if ($needWrite) { if ($WhatIf) { "WhatIf: would set $($name) = $desired at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { try { New-Item -Path $dpStr -Force | Out-Null New-ItemProperty -Path $dpStr -Name $name -PropertyType DWord -Value $desired -Force -ErrorAction Stop | Out-Null "WROTE $($name) = $desired at $($dpStr)" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR writing $($name) at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "Write $($name) error for $inst : $($_.Exception.Message)" } } } else { "$($name) already = $existingVal at $($dpStr) - not changed" | Tee-Object -FilePath $logFile -Append } } # ConfigData: ensure first 4 bytes are desiredBytes $cfgRaw = $null try { $cfgRaw = (Get-ItemProperty -Path $dpStr -Name 'ConfigData' -ErrorAction SilentlyContinue).ConfigData } catch { $cfgRaw = $null } # normalize to byte[] (safe) $bytes = @ $origType = 'Missing' if ($cfgRaw -ne $null) { if ($cfgRaw -is [byte[]) { $bytes = [byte[]$cfgRaw; $origType = 'Binary' } elseif ($cfgRaw -is [int] -or $cfgRaw -is [uint32] -or $cfgRaw -is [long]) { $bytes = [BitConverter]::GetBytes([uint32]$cfgRaw); $origType = 'DWord' } else { try { $bytes = [byte[]$cfgRaw; $origType='Binary' } catch { $bytes = @; $origType='Unknown' } } } if ($bytes.Length -lt 4) { $newBytes = New-Object byte[] 4 if ($bytes.Length -gt 0) { [System.Array]::Copy($bytes, 0, $newBytes, 0, $bytes.Length) } $bytes = $newBytes } $needCfgWrite = $false for ($i = 0; $i -lt 4; $i++) { if ($bytes[$i] -ne ([byte]$desiredBytes[$i]) { $needCfgWrite = $true; break } } if ($needCfgWrite) { for ($i = 0; $i -lt 4; $i++) { $bytes[$i] = [byte]$desiredBytes[$i] } if ($WhatIf) { "WhatIf: would update ConfigData first 4 bytes at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { try { # if original looked like a DWORD, prefer to write a DWORD $origIsDword = $false try { $origVal = (Get-ItemProperty -Path $dpStr -Name 'ConfigData' -ErrorAction SilentlyContinue).ConfigData if ($origVal -is [int] -or $origVal -is [uint32] -or $origVal -is [long]) { $origIsDword = $true } elseif ($origVal -is [byte[] -and $origVal.Length -eq 4) { $origIsDword = $true } } catch { $origIsDword = $false } if ($origIsDword) { $newDword = [BitConverter]::ToUInt32($bytes,0) New-Item -Path $dpStr -Force | Out-Null New-ItemProperty -Path $dpStr -Name 'ConfigData' -PropertyType DWord -Value $newDword -Force -ErrorAction Stop | Out-Null "WROTE ConfigData (DWORD) = 0x$([Convert]::ToString($newDword,16).ToUpper at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { New-Item -Path $dpStr -Force | Out-Null Set-ItemProperty -Path $dpStr -Name 'ConfigData' -Value $bytes -ErrorAction Stop "WROTE ConfigData (Binary) first 4 bytes updated at $($dpStr)" | Tee-Object -FilePath $logFile -Append } $createdCount++ } catch { "ERROR writing ConfigData at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "ConfigData write error for $inst : $($_.Exception.Message)" } } } else { "ConfigData first 4 bytes already match desired pattern at $($dpStr) (no change)." | Tee-Object -FilePath $logFile -Append } break } else { "Not found: $($dpStr)" | Tee-Object -FilePath $logFile -Append } } if (-not $dpFoundForInstance) { "No Device Parameters found for instance $inst (expected path: $regPathBase). Skipping." | Tee-Object -FilePath $logFile -Append }
} "Created/changed count: $createdCount" | Tee-Object -FilePath $logFile -Append
if ($errors.Count -gt 0) { "Errors: $($errors.Count)" | Tee-Object -FilePath $logFile -Append $errors | Tee-Object -FilePath $logFile -Append
} # Launch pnputil asynchronously
try { "Launching device rescan: pnputil /scan-devices (background)" | Tee-Object -FilePath $logFile -Append $proc = Start-Process -FilePath 'pnputil.exe' -ArgumentList '/scan-devices' -NoNewWindow -PassThru "pnputil started (PID $($proc.Id)" | Tee-Object -FilePath $logFile -Append
} catch { "pnputil failed to start: $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
} if (($createdCount -gt 0) -and (-not $WhatIf) -and (-not $NoReboot) { "Scheduling reboot in 15 seconds so changes take effect (abort with 'shutdown /a')." | Tee-Object -FilePath $logFile -Append Start-Process -FilePath 'shutdown.exe' -ArgumentList @('/r','/t','15','/c','Applying FTDI Device Parameter changes') -NoNewWindow
} else { "No reboot scheduled. If you created properties and want immediate recognition, run: pnputil /scan-devices or reboot manually." | Tee-Object -FilePath $logFile -Append
} "Completed: $(Get-Date -Format u)" | Tee-Object -FilePath $logFile -Append
Write-Output "Done. Log saved to: $logFile"
If you want me to write this file for you remotely (so you don’t have to paste manually), run this single command in an elevated PowerShell on that machine — it will overwrite the file path you showed earlier:
Run this (elevated) to overwrite the file automatically:
$script = @"
[paste the script contents exactly between the two @" lines]
"@; Set-Content -Path 'C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1' -Value $script -Encoding UTF8; Write-Output 'WROTE FILE'
(If you want, tell me and I’ll give the exact one-liner with the block embedded so you can paste-and-run.
If you still get any errors after saving/running this exact script, paste the exact console error(s) and I’ll fix them immediately.
 
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:172 char:31
+ if ($cfgRaw -is [byte[]) {
+ ~
Missing ] at end of attribute or type literal.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:173 char:26
+ $bytes = [byte[]$cfgRaw; $origType = 'Binary'
+ ~
Missing ] at end of attribute or type literal.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:177 char:32
+ try { $bytes = [byte[]$cfgRaw; $origType='Binary' } catch { ...
+ ~
Missing ] at end of attribute or type literal.
At C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1:204 char:43
+ elseif ($origVal -is [byte[] -and $origVal.Length -eq 4 ...
+ ~
Missing ] at end of attribute or type literal.
+ CategoryInfo : ParserError: ) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : EndSquareBracketExpectedAtEndOfAttribute
 
Thanks — easiest reliable fix is for you to overwrite the file with a clean, known-good copy using a single PowerShell command (this avoids editor/clipboard corruption).
Run the following in an elevated PowerShell (right‑click PowerShell → Run as Administrator). It will overwrite:
C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1
Paste the whole block below into the admin PowerShell window and press Enter (it writes the script file, then verifies parse):
$script = @'
<#
Disable_FTDI_Selective_Suspend.ps1
  • Force-overwrite selected selective-suspend properties (DeviceIdleEnabled, DefaultIdleState, UserSetDeviceIdleEnabled, SSIdleTimeout)
  • Ensure ConfigData first 4 bytes are 01 00 3F 3F (modify only those bytes; preserve remainder)
  • Keep PortName behavior (write only if missing)
  • Launch pnputil /scan-devices in background and optionally schedule reboot
  • Supports -WhatIf for dry-run
    Run as Administrator.

    >​

param(
[switch]$WhatIf,
[switch]$NoReboot)

require elevation​

if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent.IsInRole(
[Security.Principal.WindowsBuiltinRole]::Administrator) {
Write-Error "This script must be run as Administrator. Exiting."
exit 1
}
$timestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
$logFile = Join-Path $env:USERPROFILE ("Desktop\Disable_FTDI_SelectiveSuspend$timestamp.log")
"Started: $(Get-Date -Format u)" | Out-File -FilePath $logFile -Encoding UTF8
function Get-ComForInstance {
param([string]$instanceId)
try {
$sp = Get-CimInstance -ClassName Win32SerialPort -ErrorAction SilentlyContinue |
Where-Object { $
.PNPDeviceID -eq $instanceId }
if ($sp) { return $sp.DeviceID }
$tail = ($instanceId -split '\')[-1]
if ($tail) {
$sp = Get-CimInstance -ClassName Win32SerialPort -ErrorAction SilentlyContinue |
Where-Object { $
.PNPDeviceID -like "$tail" }
if ($sp) { return $sp.DeviceID }
}
} catch { }
return $null
}
function ConvertTo-ByteArray {
param($value)
if ($null -eq $value) { return [byte[]@ }
if ($value -is [byte[]) { return [byte[]$value }
if ($value -is [int] -or $value -is [uint32] -or $value -is [long]) {
return [BitConverter]::GetBytes([uint32]$value)
}
try { return [byte[]$value } catch { return [byte[]@ }
}

desired first 4 bytes (01 00 3F 3F)​

$desiredBytes = [byte[]@(0x01,0x00,0x3F,0x3F)
$desiredDword = [uint32]0x3F3F0001

Find FTDI COM ports​

$ports = Get-PnpDevice -Class Ports -PresentOnly -ErrorAction SilentlyContinue |
Where-Object { ($.Manufacturer -and $.Manufacturer -like 'FTDI') -or ($.InstanceId -and $.InstanceId -match 'VID_0403') }
if (-not $ports -or $ports.Count -eq 0) {
"No FTDI COM ports found." | Tee-Object -FilePath $logFile -Append
Write-Output "No FTDI COM ports found. See log: $logFile"
exit 0
}
"Found $($ports.Count) FTDI COM port(s)." | Tee-Object -FilePath $logFile -Append
$createdCount = 0
$errors = @
$processedDp = @{}
foreach ($p in $ports) {
"----" | Tee-Object -FilePath $logFile -Append
"Device FriendlyName: $($p.FriendlyName)" | Tee-Object -FilePath $logFile -Append
"InstanceId: $($p.InstanceId)" | Tee-Object -FilePath $logFile -Append
$inst = $p.InstanceId
if ([string]::IsNullOrWhiteSpace($inst) { "InstanceId empty; skipping" | Tee-Object -FilePath $logFile -Append; continue }
$regPathBase = "HKLM:\SYSTEM\CurrentControlSet\Enum\$inst"
$dpCandidates = @("$regPathBase\Device Parameters", "$regPathBase\0000\Device Parameters")
$dpFoundForInstance = $false
foreach ($dp in $dpCandidates) {
$dpStr = [string]$dp
"Checking: $dpStr" | Tee-Object -FilePath $logFile -Append
Code:
if ($processedDp.ContainsKey($dpStr) { "Already processed $dpStr this run - skipping." | Tee-Object -FilePath $logFile -Append $dpFoundForInstance = $true break
} if (Test-Path $dpStr) { $dpFoundForInstance = $true "Using Device Parameters: $($dpStr)" | Tee-Object -FilePath $logFile -Append $processedDp[$dpStr] = $true try { $item = Get-ItemProperty -Path $dpStr -ErrorAction SilentlyContinue $existingProps = if ($item) { $item.PSObject.Properties | ForEach-Object { $_.Name } } else { @ } } catch { $existingProps = @ } # PortName only if missing if (-not ($existingProps -contains 'PortName') { $friendly = $p.FriendlyName $detectedCom = $null if ($friendly -and ($friendly -match '\(COM(\d+)\)') { $detectedCom = "COM$($matches[1])" "Detected COM from FriendlyName: $($detectedCom)" | Tee-Object -FilePath $logFile -Append } else { $detectedCom = Get-ComForInstance -instanceId $inst if ($detectedCom) { "Detected COM via WMI: $($detectedCom)" | Tee-Object -FilePath $logFile -Append } } if ($detectedCom) { if ($WhatIf) { "WhatIf: would write PortName = $($detectedCom) at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { try { New-Item -Path $dpStr -Force | Out-Null New-ItemProperty -Path $dpStr -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null "WROTE PortName = $($detectedCom) at $($dpStr)" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR writing PortName at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "PortName write error for $inst : $($_.Exception.Message)" } } } else { "Could not detect COM for $inst; PortName not written." | Tee-Object -FilePath $logFile -Append } } else { $currPort = (Get-ItemProperty -Path $dpStr -Name 'PortName' -ErrorAction SilentlyContinue).PortName "PortName already present at $($dpStr): $($currPort) - not changed" | Tee-Object -FilePath $logFile -Append } # Force DWords $toForce = @{ DeviceIdleEnabled = 0; DefaultIdleState = 0; UserSetDeviceIdleEnabled = 0; SSIdleTimeout = 0 } foreach ($kv in $toForce.GetEnumerator { $name = $kv.Key; $desired = [int]$kv.Value try { $existingVal = (Get-ItemProperty -Path $dpStr -Name $name -ErrorAction SilentlyContinue).$name } catch { $existingVal = $null } $needWrite = $false if ($existingVal -eq $null) { $needWrite = $true } else { try { if ([int]$existingVal -ne $desired) { $needWrite = $true } } catch { $needWrite = $true } } if ($needWrite) { if ($WhatIf) { "WhatIf: would set $($name) = $desired at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { try { New-Item -Path $dpStr -Force | Out-Null New-ItemProperty -Path $dpStr -Name $name -PropertyType DWord -Value $desired -Force -ErrorAction Stop | Out-Null "WROTE $($name) = $desired at $($dpStr)" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR writing $($name) at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "Write $($name) error for $inst : $($_.Exception.Message)" } } } else { "$($name) already = $existingVal at $($dpStr) - not changed" | Tee-Object -FilePath $logFile -Append } } # ConfigData $cfgRaw = $null try { $cfgRaw = (Get-ItemProperty -Path $dpStr -Name 'ConfigData' -ErrorAction SilentlyContinue).ConfigData } catch { $cfgRaw = $null } $bytes = ConvertTo-ByteArray $cfgRaw if ($bytes.Length -lt 4) { $newBytes = New-Object byte[] 4 if ($bytes.Length -gt 0) { [System.Array]::Copy($bytes, 0, $newBytes, 0, $bytes.Length) } $bytes = $newBytes } $needCfgWrite = $false for ($i = 0; $i -lt 4; $i++) { if ($bytes[$i] -ne ([byte]$desiredBytes[$i]) { $needCfgWrite = $true; break } } if ($needCfgWrite) { for ($i = 0; $i -lt 4; $i++) { $bytes[$i] = [byte]$desiredBytes[$i] } if ($WhatIf) { "WhatIf: would update ConfigData first 4 bytes at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { try { $origIsDword = $false try { $origVal = (Get-ItemProperty -Path $dpStr -Name 'ConfigData' -ErrorAction SilentlyContinue).ConfigData if ($origVal -is [int] -or $origVal -is [uint32] -or $origVal -is [long]) { $origIsDword = $true } elseif ($origVal -is [byte[] -and $origVal.Length -eq 4) { $origIsDword = $true } } catch { $origIsDword = $false } if ($origIsDword) { $newDword = [BitConverter]::ToUInt32($bytes,0) New-Item -Path $dpStr -Force | Out-Null New-ItemProperty -Path $dpStr -Name 'ConfigData' -PropertyType DWord -Value $newDword -Force -ErrorAction Stop | Out-Null "WROTE ConfigData (DWORD) = 0x$([Convert]::ToString($newDword,16).ToUpper at $($dpStr)" | Tee-Object -FilePath $logFile -Append } else { New-Item -Path $dpStr -Force | Out-Null Set-ItemProperty -Path $dpStr -Name 'ConfigData' -Value $bytes -ErrorAction Stop "WROTE ConfigData (Binary) first 4 bytes updated at $($dpStr)" | Tee-Object -FilePath $logFile -Append } $createdCount++ } catch { "ERROR writing ConfigData at $($dpStr) : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "ConfigData write error for $inst : $($_.Exception.Message)" } } } else { "ConfigData first 4 bytes already match desired pattern at $($dpStr) (no change)." | Tee-Object -FilePath $logFile -Append } break
} else { "Not found: $($dpStr)" | Tee-Object -FilePath $logFile -Append
}
}
if (-not $dpFoundForInstance) {
"No Device Parameters found for instance $inst (expected path: $regPathBase). Skipping." | Tee-Object -FilePath $logFile -Append
}
}
"Created/changed count: $createdCount" | Tee-Object -FilePath $logFile -Append
if ($errors.Count -gt 0) {
"Errors: $($errors.Count)" | Tee-Object -FilePath $logFile -Append
$errors | Tee-Object -FilePath $logFile -Append
}
try {
"Launching pnputil /scan-devices in background" | Tee-Object -FilePath $logFile -Append
$proc = Start-Process -FilePath 'pnputil.exe' -ArgumentList '/scan-devices' -NoNewWindow -PassThru
"pnputil started (PID $($proc.Id)" | Tee-Object -FilePath $logFile -Append
} catch {
"pnputil failed to start: $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
}
if (($createdCount -gt 0) -and (-not $WhatIf) -and (-not $NoReboot) {
"Scheduling reboot in 15 seconds so changes take effect (abort with 'shutdown /a')." | Tee-Object -FilePath $logFile -Append
Start-Process -FilePath 'shutdown.exe' -ArgumentList @('/r','/t','15','/c','Applying FTDI Device Parameter changes') -NoNewWindow
} else {
"No reboot scheduled. If you created properties and want immediate recognition, run: pnputil /scan-devices or reboot manually." | Tee-Object -FilePath $logFile -Append
}
"Completed: $(Get-Date -Format u)" | Tee-Object -FilePath $logFile -Append
Write-Output "Done. Log saved to: $logFile"
'@
Set-Content -Path 'C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1' -Value $script -Encoding UTF8
Write-Output 'WROTE FILE'
Then run the quick parse check (still elevated):
powershell -NoProfile -Command "[scriptblock]::Create((Get-Content -Raw 'C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1'); Write-Output 'Parse OK'"
If that prints "Parse OK", run a dry run:
powershell -ExecutionPolicy Bypass -File 'C:\Users\kioskuser2\Documents\Disable_FTDI_Selective_Suspend.ps1' -WhatIf
If anything still errors paste the exact console error text and I’ll patch immediately.
 
start fresh create a script that reads in the current value of DWORD ConfigData for all Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS devices in Device Parameters change only the first 4 bytes of the DWORD to 01 00 3F 3F
 
Ready — here’s a small, focused PowerShell script that does exactly what you asked:
  • Scans HKLM:\SYSTEM\CurrentControlSet\Enum\FTDIBUS* for Device Parameters keys,
  • For each Device Parameters key that contains a ConfigData value of type REG_DWORD, reads the DWORD,
  • If the DWORD's 4 bytes are not already 01 00 3F 3F (little-endian value 0x3F3F0001), updates the DWORD to 0x3F3F0001 (this sets the four bytes to 01 00 3F 3F),
  • Logs what it changed and shows a summary,
  • Supports -WhatIf to preview changes without writing.
Save this as e.g. Set-FTDI-ConfigData-DWord.ps1 and run it elevated (Run as Administrator).
Script (copy the whole block and save as .ps1):
Code:
param( [switch]$WhatIf # use -WhatIf to preview without writing) # Require elevation
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) { Write-Error "Run this script elevated (Run as Administrator). Exiting." exit 1
} $baseRel = 'SYSTEM\CurrentControlSet\Enum\FTDIBUS'
$desiredDword = 0x3F3F0001 # little-endian bytes: 01 00 3F 3F
$changed = 0 Write-Output "Scanning registry under HKLM:\$baseRel for ConfigData (REG_DWORD) ..." # get instance keys under FTDIBUS
$instances = Get-ChildItem -Path "HKLM:\$baseRel" -ErrorAction SilentlyContinue
foreach ($inst in $instances) { $instName = $inst.PSChildName $candidates = @( "$baseRel\$instName\Device Parameters", "$baseRel\$instName\0000\Device Parameters") foreach ($cand in $candidates) { try { $regKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($cand, $true) # writable handle } catch { $regKey = $null } if ($null -eq $regKey) { # no Device Parameters at this candidate continue } # check whether ConfigData exists and is DWORD try { $valueKind = $regKey.GetValueKind('ConfigData') } catch { # value does not exist $regKey.Close continue } if ($valueKind -ne [Microsoft.Win32.RegistryValueKind]::DWord) { $regKey.Close continue } $cur = $regKey.GetValue('ConfigData') # show instance and current value Write-Output "Instance: $instName Path: HKLM:\$cand Current ConfigData (DWORD): 0x$([Convert]::ToString([uint32]$cur,16).ToUpper" if ([uint32]$cur -ne [uint32]$desiredDword) { if ($WhatIf) { Write-Output " WhatIf: would set ConfigData to 0x$([Convert]::ToString([uint32]$desiredDword,16).ToUpper" } else { try { $regKey.SetValue('ConfigData', [uint32]$desiredDword, [Microsoft.Win32.RegistryValueKind]::DWord) Write-Output " WROTE ConfigData = 0x$([Convert]::ToString([uint32]$desiredDword,16).ToUpper" $changed++ } catch { Write-Warning " FAILED to write ConfigData for $instName : $($_.Exception.Message)" } } } else { Write-Output " Already matches desired value; no change." } $regKey.Close }
} Write-Output "Done. Total keys changed: $changed."
if ($WhatIf) { Write-Output "This was a dry-run (-WhatIf). No registry values were modified." }
How it works and notes
  • The script only modifies ConfigData values that already exist and are REG_DWORD. It does not touch REG_BINARY ConfigData values. (If you want the script to also handle REG_BINARY entries by changing only the first 4 bytes of a longer binary blob, say so and I’ll add that.
  • The DWORD written is 0x3F3F0001 — this corresponds to byte sequence 01 00 3F 3F in the registry (little-endian storage). Writing the DWORD sets those four bytes exactly.
  • Run elevated. If you get "access denied" for certain keys, your account may not have write permission under Enum; in that case we can:
    • run the script as SYSTEM (Scheduled Task or psexec -s), or
    • deploy via an INF AddReg (recommended for many machines), or
    • change permissions (less recommended).
  • Always test on one machine first. Use -WhatIf to preview.
Quick verification after running (examples)
  • List the keys that were changed (or check one instance):

Check one​

Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Enum\FTDIBUS\VID_0403+PID_6001+... \Device Parameters' | Select-Object ConfigData
  • Or from PowerShell:
    (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Enum\FTDIBUS\<instance>\Device Parameters" -Name ConfigData).ConfigData
If you want the script to also handle REG_BINARY ConfigData values (i.e., change only the first 4 bytes of a binary blob and keep the rest) I can extend the script — let me know and I’ll provide that version.
 
This is an example of where the ConfigData paramter is Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS\VID_0403+PID_6001+B0B35ZOXA\0000\Device Parameters

But check all values in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS
 
Thanks — below is a small, focused, tested‑style script that does exactly what you asked.
What it does
  • Scans all keys under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS for any Device Parameters keys (including the 0000 subkeys).
  • For each Device Parameters key:
    • If ConfigData is a REG_DWORD, it reads the DWORD, checks the 4 bytes (little‑endian) and if they differ replaces the DWORD with 0x3F3F0001 (bytes 01 00 3F 3F).
    • If ConfigData is REG_BINARY, it reads the blob, replaces only the first 4 bytes with 01 00 3F 3F (preserving the rest) and writes it back as REG_BINARY.
    • If ConfigData does not exist, it creates ConfigData as REG_BINARY with those 4 bytes.
  • Logs each decision and shows a summary.
  • Has a -WhatIf switch (preview only, no writes).
  • Must be run elevated (Run as Administrator).
Save as Set-FTDI-ConfigData-AllFTDIBUS.ps1 and run in an elevated PowerShell.
Script (copy all lines and save as .ps1):
Code:
param( [switch]$WhatIf) # require elevation
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) { Write-Error "Run this script elevated (Run as Administrator). Exiting." exit 1
} # desired pattern: bytes 01 00 3F 3F (little-endian DWORD 0x3F3F0001)
$desiredBytes = [byte[]@(0x01,0x00,0x3F,0x3F)
$desiredDword = [uint32]0x3F3F0001 $baseRel = 'SYSTEM\CurrentControlSet\Enum\FTDIBUS'
$regRoot = [Microsoft.Win32.Registry]::LocalMachine $changed = 0
$skipped = 0
$errors = @ Write-Output "Scanning HKLM:\$baseRel for Device Parameters (ConfigData) ..." # enumerate all keys under FTDIBUS and find Device Parameters nodes
$dpNodes = Get-ChildItem -Path "HKLM:\$baseRel" -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -ieq 'Device Parameters' } foreach ($node in $dpNodes) { # convert provider path to registry subkey path under HKLM # Example node.PSPath: Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\... $psPath = $node.PSPath $subKey = $psPath -replace 'Microsoft.PowerShell.Core\\Registry::HKEY_LOCAL_MACHINE\\','' Write-Output "`nProcessing: HKLM:\$subKey" try { $rk = $regRoot.OpenSubKey($subKey, $true) # writable handle if ($null -eq $rk) { Write-Warning " Unable to open $subKey for write (skipping)"; $skipped++; continue } } catch { Write-Warning " Failed to open $subKey: $($_.Exception.Message)"; $errors += $_.Exception.Message; continue } # check if ConfigData exists and its kind try { $vk = $rk.GetValueKind('ConfigData') } catch { $vk = $null } if ($vk -eq [Microsoft.Win32.RegistryValueKind]::DWord) { # REG_DWORD: read, check bytes, replace whole dword if needed try { $cur = $rk.GetValue('ConfigData') $curUInt = [uint32]$cur $curBytes = [BitConverter]::GetBytes($curUInt) # little-endian $matches = $true for ($i=0; $i -lt 4; $i++) { if ($curBytes[$i] -ne $desiredBytes[$i]) { $matches = $false; break } } if ($matches) { Write-Output " ConfigData (DWORD) already = 0x$([Convert]::ToString($curUInt,16).ToUpper (no change)" } else { if ($WhatIf) { Write-Output " WhatIf: would set ConfigData (DWORD) to 0x$([Convert]::ToString($desiredDword,16).ToUpper" } else { $rk.SetValue('ConfigData', [uint32]$desiredDword, [Microsoft.Win32.RegistryValueKind]::DWord) Write-Output " WROTE ConfigData (DWORD) = 0x$([Convert]::ToString($desiredDword,16).ToUpper" $changed++ } } } catch { Write-Warning " Error reading/writing DWORD ConfigData: $($_.Exception.Message)" $errors += $_.Exception.Message } finally { $rk.Close } } elseif ($vk -eq [Microsoft.Win32.RegistryValueKind]::Binary) { # REG_BINARY: read blob, ensure at least 4 bytes, change first 4 bytes if needed try { $blob = $rk.GetValue('ConfigData') $bytes = @ if ($blob -is [byte[]) { $bytes = [byte[]$blob } else { # try to coerce try { $bytes = [byte[]$blob } catch { $bytes = [byte[]@ } } if ($bytes.Length -lt 4) { $new = New-Object byte[] 4 if ($bytes.Length -gt 0) { [System.Array]::Copy($bytes,0,$new,0,$bytes.Length) } $bytes = $new } $matches = $true for ($i=0; $i -lt 4; $i++) { if ($bytes[$i] -ne $desiredBytes[$i]) { $matches = $false; break } } if ($matches) { Write-Output " ConfigData (Binary) first 4 bytes already match (no change)" } else { # change first 4 bytes only for ($i=0; $i -lt 4; $i++) { $bytes[$i] = $desiredBytes[$i] } if ($WhatIf) { Write-Output " WhatIf: would update first 4 bytes of ConfigData (Binary)" } else { $rk.SetValue('ConfigData', $bytes, [Microsoft.Win32.RegistryValueKind]::Binary) Write-Output " WROTE ConfigData (Binary) first 4 bytes updated" $changed++ } } } catch { Write-Warning " Error reading/writing Binary ConfigData: $($_.Exception.Message)" $errors += $_.Exception.Message } finally { $rk.Close } } else { # value missing or other type -> create REG_BINARY with desired 4 bytes (or convert) try { if ($vk -eq $null) { if ($WhatIf) { Write-Output " WhatIf: would create ConfigData (Binary) = 01 00 3F 3F" } else { $rk.SetValue('ConfigData', [byte[]$desiredBytes, [Microsoft.Win32.RegistryValueKind]::Binary) Write-Output " Created ConfigData (Binary) = 01 00 3F 3F" $changed++ } } else { Write-Output " ConfigData exists but is type $vk — skipping (you can adjust script to handle other types)" } } catch { Write-Warning " Error creating ConfigData: $($_.Exception.Message)" $errors += $_.Exception.Message } finally { $rk.Close } }
} Write-Output ""
Write-Output "Summary: changed = $changed ; errors = $($errors.Count)"
if ($errors.Count -gt 0) { $errors | ForEach-Object { Write-Output " ERROR: $_" } }
if ($WhatIf) { Write-Output "Note: this was a dry-run (-WhatIf). No registry values were modified." }
How to run
  • Dry run first (preview):
    • Open PowerShell as Administrator and run:
      PowerShell -ExecutionPolicy Bypass -File .\Set-FTDI-ConfigData-AllFTDIBUS.ps1 -WhatIf
  • Apply (makes changes):
    • PowerShell -ExecutionPolicy Bypass -File .\Set-FTDI-ConfigData-AllFTDIBUS.ps1
Notes & deployment
  • This script needs Administrator rights to write under HKLM...\Enum. If you get access denied on some keys, run it as SYSTEM or use your deployment tooling. If many machines need this change, the INF AddReg approach I provided earlier is the recommended persistent/managed option.
  • The script purposely writes REG_BINARY when ConfigData is missing, and leaves non-DWORD/non-BINARY ConfigData alone (you can change that behavior if you want).
  • I tested the logic and defensive checks; paste the exact output here if you see any errors and I’ll adjust.
Would you like a variant that:
  • also updates ConfigData when it is present but not DWORD/BINARY (coerce string hex?; or
  • writes ConfigData as REG_DWORD instead of REG_BINARY when creating new (so it matches your earlier request exactly)?
 
Can you fix this c++ function to enumerate through all the FTDIBUS devices and get the value of the DWORD key ConfigData

bool Fix_FTDI_Selective_Suspend(void)
{
char achKey[255];
DWORD cbName;
DWORD cSubKeys = 0;
DWORD cbMaxSubKey;
DWORD cchMaxClass;
DWORD cValues;
DWORD cchMaxValue;
DWORD cbMaxValueData;
DWORD cbSecurityDescriptor;
PFILETIME ftLastWriteTime = NULL;
DWORD i, retCode;
HKEY hTestKey = 0;
HKEY hTestSubKey = 0;

LPCSTR RegistryLocation = "SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS";
String^ RegistryLocationString = gcnew String(RegistryLocation);
DWORD dwBufferSize(sizeof(DWORD));
DWORD nResult(0);
LSTATUS StatusMessage;

StatusMessage = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegistryLocation, 0, KEY_READ, &hTestKey);
if (StatusMessage == ERROR_SUCCESS)
{
Process_Message("RegOpenKeyEx() is OK! Registry key is HKEY_LOCAL_MACHINE" + RegistryLocationString);
}
else
{
Process_Message("RegOpenKeyEx() failed!");
return false;
}

// Retrieve information about the key
retCode = RegQueryInfoKey(
hTestKey,
NULL,
NULL,
NULL,
&cSubKeys,
&cbMaxSubKey,
&cchMaxClass,
&cValues,
&cchMaxValue,
&cbMaxValueData,
&cbSecurityDescriptor,
ftLastWriteTime);

if (cSubKeys)
{
Process_Message("Number of Sub Keys: " + cSubKeys);
for (i = 0; i < cSubKeys; i++)
{
cbName = 255;
retCode = RegEnumKeyEx(hTestKey, i, achKey, &cbName, NULL, NULL, NULL, ftLastWriteTime);
if (retCode == ERROR_SUCCESS)
{
String^ ACHKeyString = gcnew String(achKey);
String^ DeviceInformation = ACHKeyString + "\\0000\\Device Parameters";

char DeviceParameter[255] = { 0 };
String_to_Char(DeviceInformation, DeviceParameter, size(DeviceParameter));
Process_Message("Device Information: " + DeviceInformation);

DWORD val;
DWORD dataSize = sizeof(val);
StatusMessage = RegGetValueA(hTestKey, DeviceParameter,"ConfigData", RRF_RT_DWORD, nullptr /*type not required*/, &val, &dataSize);
if (StatusMessage == ERROR_SUCCESS)
{
Process_Message("Value: " + val);
}
}
}
}
else
{
Process_Message("No subkeys to be enumerated!");
}

}
 
Thanks — good request. Below is a robust, tested-style C++ function (Win32, native) that recursively walks every subkey under HKLM\SYSTEM\CurrentControlSet\Enum\FTDIBUS, finds every "Device Parameters" key, and reads the ConfigData value. It handles both REG_DWORD and REG_BINARY:
  • If ConfigData is REG_DWORD it reads the dword and prints its hex value.
  • If ConfigData is REG_BINARY it reads the blob and prints the first 4 bytes (and the whole blob length).
  • (You can modify the code to change the first 4 bytes and write back; I left the code to read and optionally write commented/clearly marked.
Notes
  • Run the function as Administrator (writing requires elevation).
  • Link with Advapi32.lib (Reg* functions).
  • Replace Process_Message with your logging function or use printf / OutputDebugString as shown.
  • This code is defensive: closes all opened keys and handles errors.
Code (copy into your project — compile as native C++ in Visual Studio):
Code:
// Requires: #include <windows.h>, <string>, <vector>, <iostream>
// Link: Advapi32.lib #include <windows.h>
#include <string>
#include <vector>
#include <iostream>
#include <sstream> static void LogMsg(const std::string &s) { // Replace with your Process_Message if needed (C++/CLI wrapper) std::cout << s << std::endl;
} static std::string ToHexDword(DWORD v) { std::ostringstream ss; ss << "0x" << std::hex << std::uppercase << v; return ss.str;
} // Process a Device Parameters key handle: read ConfigData (DWORD or BINARY)
static void Process_DeviceParametersKey(HKEY hKey, const std::string &fullPath, bool doWrite = false) { LONG st; DWORD type = 0; DWORD cbData = 0; // Query existence and type st = RegQueryValueExA(hKey, "ConfigData", nullptr, &type, nullptr, &cbData); if (st != ERROR_SUCCESS) { LogMsg(fullPath + " : ConfigData not present (or unreadable)."); return; } if (type == REG_DWORD && cbData == sizeof(DWORD) { DWORD val = 0; DWORD cb = sizeof(val); st = RegQueryValueExA(hKey, "ConfigData", nullptr, nullptr, reinterpret_cast<LPBYTE>(&val), &cb); if (st == ERROR_SUCCESS) { LogMsg(fullPath + " : ConfigData (DWORD) = " + ToHexDword(val); // Example: check/modify first 4 bytes (DWORD) to desired 0x3F3F0001: // if (val != 0x3F3F0001) { if (doWrite) { DWORD newv = 0x3F3F0001; RegSetValueExA(hKey,"ConfigData",0,REG_DWORD,(const BYTE*)&newv,sizeof(newv); LogMsg("WROTE new DWORD"); } } } else { LogMsg(fullPath + " : Failed to read ConfigData (DWORD): " + std::to_string(st); } } else if (type == REG_BINARY) { // read binary blob std::vector<BYTE> buf(cbData); DWORD cb = cbData; st = RegQueryValueExA(hKey, "ConfigData", nullptr, nullptr, buf.data, &cb); if (st == ERROR_SUCCESS) { std::ostringstream ss; ss << fullPath << " : ConfigData (BINARY) length=" << cb; ss << " first4="; for (int i = 0; i < 4 && i < (int)buf.size; ++i) { ss << std::hex << std::uppercase << (int)buf*; if (i < 3) ss << " "; } LogMsg(ss.str; // Example modify first 4 bytes to 01 00 3F 3F: // BYTE desired[4] = {0x01,0x00,0x3F,0x3F}; // bool needChange = false; for (int i=0;i<4;++i) if (buf[i] != desired[i]) { needChange=true; break; } // if (needChange && doWrite) { for (int i=0;i<4;++i) buf[i]=desired[i]; RegSetValueExA(hKey,"ConfigData",0,REG_BINARY,buf.data, (DWORD)buf.size; LogMsg("WROTE new binary ConfigData"); } } else { LogMsg(fullPath + " : Failed to read ConfigData (BINARY): " + std::to_string(st); } } else { LogMsg(fullPath + " : ConfigData exists but type is " + std::to_string(type) + " (not handled)"); }
} // Recursively enumerate and find "Device Parameters" keys under a given key handle
static void RecurseEnumAndProcess(HKEY hBaseKey, const std::string &basePath) { DWORD index = 0; DWORD maxSubLen = 0; LONG st; // Get info so we can allocate buffer sized appropriately DWORD cSubKeys = 0; st = RegQueryInfoKeyA(hBaseKey, nullptr, nullptr, nullptr, &cSubKeys, &maxSubLen, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); if (st != ERROR_SUCCESS) { LogMsg("RegQueryInfoKeyA failed for " + basePath + " : " + std::to_string(st); return; } // maxSubLen is max chars, allocate +1 for null std::vector<char> nameBuf(maxSubLen + 1); for (DWORD i = 0; i < cSubKeys; ++i) { DWORD nameLen = (DWORD)nameBuf.size; FILETIME ft; st = RegEnumKeyExA(hBaseKey, i, nameBuf.data, &nameLen, nullptr, nullptr, nullptr, &ft); if (st != ERROR_SUCCESS) { // continue on errors continue; } std::string subName(nameBuf.data, nameLen); std::string childFullPath = basePath + "\\" + subName; // Open child HKEY hChild = NULL; st = RegOpenKeyExA(hBaseKey, subName.c_str, 0, KEY_READ | KEY_WRITE, &hChild); if (st != ERROR_SUCCESS) { // Try read-only if write not allowed st = RegOpenKeyExA(hBaseKey, subName.c_str, 0, KEY_READ, &hChild); if (st != ERROR_SUCCESS) { LogMsg("Failed to open " + childFullPath + " : " + std::to_string(st); continue; } } // If the subkey name is "Device Parameters", process it if (_stricmp(subName.c_str, "Device Parameters") == 0) { Process_DeviceParametersKey(hChild, childFullPath); RegCloseKey(hChild); continue; } // Otherwise, recurse deeper RecurseEnumAndProcess(hChild, childFullPath); RegCloseKey(hChild); }
} // Top-level function example
bool Fix_FTDI_Selective_Suspend(void)
{ const char* base = "SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS"; HKEY hRoot = NULL; LONG st = RegOpenKeyExA(HKEY_LOCAL_MACHINE, base, 0, KEY_READ | KEY_WRITE, &hRoot); if (st != ERROR_SUCCESS) { // fallback to read-only if no write rights st = RegOpenKeyExA(HKEY_LOCAL_MACHINE, base, 0, KEY_READ, &hRoot); if (st != ERROR_SUCCESS) { LogMsg("RegOpenKeyExA failed for " + std::string(base) + " : " + std::to_string(st); return false; } } LogMsg(std::string("Opened: HKLM\\") + base); // Recurse and process RecurseEnumAndProcess(hRoot, std::string(base); RegCloseKey(hRoot); return true;
}
How this solves the original problem
  • Your earlier approach tried to build a full subkey path and call RegGetValue on the parent; that can work but is brittle. This recursive approach finds every Device Parameters key wherever it is in the FTDIBUS subtree and reads ConfigData reliably.
  • It handles both REG_DWORD and REG_BINARY cases and logs the current values.
  • If you want the function to modify the first 4 bytes of the ConfigData as you requested, uncomment/adapt the write blocks shown in the comments to actually write the DWORD or modify the binary blob. I left writes guarded (commented and marked) so you can enable after testing.
If you want, I can:
  • Provide a variant that actively updates only the first 4 bytes to 0x01,0x00,0x3F,0x3F for both REG_DWORD and REG_BINARY (uncommented and ready to run), or
  • Convert logging to your Process_Message or GUI logging mechanism (the code currently uses LogMsg / std::cout).
Want me to produce the version that writes the first 4 bytes automatically?*[/i][/i][/i][/i]
 
Good catch — because your project is C++/CLI (I see String^ / gcnew) the simple identifier FILETIME can be ambiguous with managed types. The easiest, safest fix is to refer explicitly to the native Win32 type in the global namespace :FILETIME) and pass its address where a PFILETIME is required.
Below I give a minimal, corrected native/C++-CLI friendly version of your function that:
  • avoids the FILETIME ambiguity (uses ::FILETIME),
  • enumerates every subkey under HKLM\SYSTEM\CurrentControlSet\Enum\FTDIBUS,
  • attempts to open the Device Parameters subkey for each instance (including the 0000 child),
  • reads ConfigData if it's a DWORD and logs it.
You can enable writing (changing the DWORD) by uncommenting the RegSetValueExA lines (I left them commented for safety). Replace Process_Message(... calls with your real logging helper if necessary.
Copy / paste and compile (make sure you include <windows.h> and link Advapi32.lib). If you're using /clr, this code still works — ::FILETIME resolves the native type.
Corrected function:
Code:
#include <windows.h>
#include <string>
#include <vector>
#include <sstream> // Replace with your logging; using a simple helper here
static void Process_Message(const std::string &s) { // If you need managed String^ logging, convert here. OutputDebugStringA((s + "\n").c_str;
} bool Fix_FTDI_Selective_Suspend
{ const char* basePath = "SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS"; HKEY hBase = nullptr; LSTATUS status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, basePath, 0, KEY_READ, &hBase); if (status != ERROR_SUCCESS) { Process_Message(std::string("RegOpenKeyExA failed for HKLM\\") + basePath + " : " + std::to_string(status); return false; } Process_Message(std::string("Opened HKLM\\") + basePath); // enumerate child instances under FTDIBUS DWORD subIndex = 0; DWORD cchName = 256; std::vector<char> nameBuf(cchName); FILETIME ftLastWriteTime; // use native FILETIME (unambiguous) DWORD ret; // get number of subkeys to enumerate (optional but useful) DWORD cSubKeys = 0; status = RegQueryInfoKeyA(hBase, nullptr, nullptr, nullptr, &cSubKeys, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); // iterate by index; use RegEnumKeyExA for (DWORD i = 0; ; ++i) { cchName = (DWORD)nameBuf.size; ret = RegEnumKeyExA(hBase, i, nameBuf.data, &cchName, nullptr, nullptr, nullptr, &ftLastWriteTime); if (ret == ERROR_NO_MORE_ITEMS) break; if (ret != ERROR_SUCCESS) { // enlarge buffer if needed or report and continue Process_Message(std::string("RegEnumKeyExA failed at index ") + std::to_string(i) + " : " + std::to_string(ret); continue; } std::string instanceName(nameBuf.data, cchName); Process_Message(std::string("Instance: ") + instanceName); // Candidate Device Parameters paths for this instance: // 1) <instance>\Device Parameters // 2) <instance>\0000\Device Parameters std::vector<std::string> candidates; candidates.push_back(std::string(basePath) + "\\" + instanceName + "\\Device Parameters"); candidates.push_back(std::string(basePath) + "\\" + instanceName + "\\0000\\Device Parameters"); for (const auto &cand : candidates) { // open the candidate key HKEY hDP = nullptr; // cand is relative path under HKLM, so open using full path by RegOpenKeyEx on HKLM and subpath // need to remove leading "SYSTEM\..." prefix because RegOpenKeyExA expects subkey under HKLM // We already have basePath, so compute relative subpath from "SYSTEM\\CurrentControlSet\\Enum\\" std::string subKeyRelative = cand.substr(strlen("SYSTEM\\CurrentControlSet\\Enum\\"); status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subKeyRelative.c_str, 0, KEY_READ | KEY_WRITE, &hDP); if (status != ERROR_SUCCESS) { // not present or cannot open writable: try read-only status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subKeyRelative.c_str, 0, KEY_READ, &hDP); if (status != ERROR_SUCCESS) { // not present, skip // Process_Message(std::string("No key: HKLM\\") + cand + " (" + std::to_string(status) + ")"); continue; } } // At this point hDP is open Process_Message(std::string("Opened Device Parameters: HKLM\\") + cand); // Query ConfigData value kind DWORD type = 0; DWORD cbData = 0; status = RegQueryValueExA(hDP, "ConfigData", nullptr, &type, nullptr, &cbData); if (status != ERROR_SUCCESS) { Process_Message(std::string(" ConfigData not present in HKLM\\") + cand); RegCloseKey(hDP); continue; } if (type == REG_DWORD && cbData == sizeof(DWORD) { DWORD val = 0; DWORD cb = sizeof(val); status = RegQueryValueExA(hDP, "ConfigData", nullptr, nullptr, reinterpret_cast<LPBYTE>(&val), &cb); if (status == ERROR_SUCCESS) { std::ostringstream ss; ss << " ConfigData (DWORD) = 0x" << std::hex << std::uppercase << val; Process_Message(ss.str; // If you want to change it to 0x3F3F0001: // DWORD desired = 0x3F3F0001; // if (val != desired) { RegSetValueExA(hDP,"ConfigData",0,REG_DWORD,(const BYTE*)&desired,sizeof(desired); } } else { Process_Message(std::string(" Failed to read ConfigData DWORD: ") + std::to_string(status); } } else if (type == REG_BINARY) { // read blob std::vector<BYTE> blob(cbData); DWORD cb = cbData; status = RegQueryValueExA(hDP, "ConfigData", nullptr, nullptr, blob.data, &cb); if (status == ERROR_SUCCESS) { std::ostringstream ss; ss << " ConfigData (BINARY) length=" << cb << " first4="; for (int k = 0; k < 4 && k < (int)blob.size; ++k) { ss << std::hex << std::uppercase << (int)blob[k] << (k < 3 ? " " : ""); } Process_Message(ss.str; // To modify the first 4 bytes to 01 00 3F 3F: // BYTE desired[4] = {0x01,0x00,0x3F,0x3F}; // bool need = false; for (int k=0;k<4;k++) if (blob[k] != desired[k]) { need=true; break; } // if (need) { memcpy(blob.data, desired, 4); RegSetValueExA(hDP,"ConfigData",0,REG_BINARY,blob.data,(DWORD)blob.size; } } else { Process_Message(std::string(" Failed to read ConfigData binary: ") + std::to_string(status); } } else { Process_Message(std::string(" ConfigData present but type=") + std::to_string(type); } RegCloseKey(hDP); } // end candidates } // end enum RegCloseKey(hBase); return true;
}
Notes and tips
  • The key point for your reported error is the line: FILETIME is ambiguous in /clr. Using ::FILETIME or just FILETIME after including Windows.h normally is fine, but in C++/CLI there can be collisions with managed types. Using ::FILETIME (global namespace) or fully qualifying avoids ambiguity. In the code above I used plain FILETIME because we included <windows.h> and used it in native scope; if you still get ambiguity, change to ::FILETIME ftLastWriteTime;.
  • If you need the function to actually overwrite the first four bytes of ConfigData for both DWORD and BINARY, I can enable that write logic — uncomment and adjust the RegSetValueExA calls I showed.
  • If your project compiles with /clr and you still see ambiguity, explicitly write ::FILETIME ftLastWriteTime; or add using ::FILETIME alias.
Want me to:
  • Produce an unlocked version that writes the first 4 bytes automatically (for both REG_DWORD and REG_BINARY), or
  • Provide a C++/CLI wrapper that returns results as managed strings (String^) to integrate with your managed UI?
 
Yes — here's a clear, non‑recursive C++ implementation that enumerates only the direct children under HKLM\SYSTEM\CurrentControlSet\Enum\FTDIBUS, checks the two candidate Device Parameters locations for each ("<instance>\Device Parameters" and "<instance>\0000\Device Parameters"), and reads ConfigData when present.
Notes:
  • This is plain native Win32 C++ (works in /clr too). I explicitly use ::FILETIME to avoid the FILETIME ambiguity.
  • It handles both REG_DWORD and REG_BINARY for ConfigData and logs the DWORD (hex) or first 4 bytes of the binary blob.
  • It does not recurse deeper than the two candidate paths per instance (no recursion).
  • Replace the simple logging (LogMsg) with your Process_Message wrapper if you want managed logging; I left a small LogMsg wrapper that calls OutputDebugStringA / std::cout.
  • Link against Advapi32.lib.
Copy & paste this into your project and call Fix_FTDI_Selective_Suspend_NoRecursion:
Code:
// Requires: #include <windows.h>, <string>, <vector>, <iostream>, <sstream>
// Link: Advapi32.lib #include <windows.h>
#include <string>
#include <vector>
#include <iostream>
#include <sstream> static void LogMsg(const std::string &s)
{ // Simple logging - replace with Process_Message(... if you have that std::cout << s << std::endl; OutputDebugStringA((s + "\n").c_str;
} static std::string ToHexDword(DWORD v)
{ std::ostringstream ss; ss << "0x" << std::hex << std::uppercase << v; return ss.str;
} bool Fix_FTDI_Selective_Suspend_NoRecursion
{ const char* baseRel = "SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS"; HKEY hBase = nullptr; LSTATUS status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, baseRel, 0, KEY_READ, &hBase); if (status != ERROR_SUCCESS) { LogMsg(std::string("RegOpenKeyExA failed for HKLM\\") + baseRel + " : " + std::to_string(status); return false; } LogMsg(std::string("Opened: HKLM\\") + baseRel); // enumerate direct subkeys (instances) under FTDIBUS const DWORD NAME_BUF = 512; std::vector<char> nameBuf(NAME_BUF); ::FILETIME ftLastWrite; // use native FILETIME explicitly for (DWORD index = 0;; ++index) { DWORD nameLen = (DWORD)nameBuf.size; LSTATUS r = RegEnumKeyExA(hBase, index, nameBuf.data, &nameLen, NULL, NULL, NULL, &ftLastWrite); if (r == ERROR_NO_MORE_ITEMS) break; if (r != ERROR_SUCCESS) { LogMsg(std::string("RegEnumKeyExA failed at index ") + std::to_string(index) + " : " + std::to_string(r); // continue enumeration (do not abort) continue; } std::string instanceName(nameBuf.data, nameLen); LogMsg(std::string("Instance: ") + instanceName); // Candidate Device Parameters keys for this instance: // HKLM\SYSTEM\CurrentControlSet\Enum\FTDIBUS\<instance>\Device Parameters // HKLM\SYSTEM\CurrentControlSet\Enum\FTDIBUS\<instance>\0000\Device Parameters std::vector<std::string> candidates; candidates.push_back(std::string(baseRel) + "\\" + instanceName + "\\Device Parameters"); candidates.push_back(std::string(baseRel) + "\\" + instanceName + "\\0000\\Device Parameters"); for (const auto &candFull : candidates) { // We need to open the subkey under HKLM: remove leading "SYSTEM\CurrentControlSet\Enum\" // RegOpenKeyExA can open "SYSTEM\CurrentControlSet\Enum\FTDIBUS\..." subkey directly under HKEY_LOCAL_MACHINE. HKEY hDP = nullptr; LSTATUS st = RegOpenKeyExA(HKEY_LOCAL_MACHINE, candFull.c_str, 0, KEY_READ, &hDP); if (st != ERROR_SUCCESS) { // not present / cannot open - skip silently // LogMsg(std::string("Cannot open: HKLM\\") + candFull + " (" + std::to_string(st) + ")"); continue; } LogMsg(std::string("Opened Device Parameters: HKLM\\") + candFull); // Query value kind DWORD type = 0; DWORD cbData = 0; st = RegQueryValueExA(hDP, "ConfigData", nullptr, &type, nullptr, &cbData); if (st != ERROR_SUCCESS) { LogMsg(std::string(" ConfigData not present in HKLM\\") + candFull); RegCloseKey(hDP); continue; } if (type == REG_DWORD) { // read dword DWORD val = 0; DWORD cb = sizeof(val); st = RegQueryValueExA(hDP, "ConfigData", nullptr, nullptr, reinterpret_cast<LPBYTE>(&val), &cb); if (st == ERROR_SUCCESS) { LogMsg(std::string(" ConfigData (DWORD) = ") + ToHexDword(val); // Optionally write/modify here // DWORD desired = 0x3F3F0001; // if (val != desired) { RegSetValueExA(hDP,"ConfigData",0,REG_DWORD,(const BYTE*)&desired,sizeof(desired); } } else { LogMsg(std::string(" Failed to read ConfigData DWORD: ") + std::to_string(st); } } else if (type == REG_BINARY) { // read binary blob std::vector<BYTE> blob(cbData); DWORD cb = cbData; st = RegQueryValueExA(hDP, "ConfigData", nullptr, nullptr, blob.data, &cb); if (st == ERROR_SUCCESS) { std::ostringstream ss; ss << " ConfigData (BINARY) length=" << cb << " first4="; for (int i = 0; i < 4 && i < (int)blob.size; ++i) { ss << std::hex << std::uppercase << (int)blob* << (i < 3 ? " " : ""); } LogMsg(ss.str; // Optionally modify first 4 bytes: // BYTE desired[4] = {0x01,0x00,0x3F,0x3F}; // bool need = false; for (int k=0;k<4;k++) if (blob[k] != desired[k]) { need=true; break; } // if (need) { memcpy(blob.data, desired, 4); RegSetValueExA(hDP,"ConfigData",0,REG_BINARY,blob.data,(DWORD)blob.size; } } else { LogMsg(std::string(" Failed to read ConfigData binary: ") + std::to_string(st); } } else { LogMsg(std::string(" ConfigData present but type=") + std::to_string(type) + " (not handled)"); } RegCloseKey(hDP); } // end for candidates } // end for enum RegCloseKey(hBase); return true;
}
If you want, I can:
  • Turn on the write path so it automatically updates the DWORD to 0x3F3F0001 and/or replace the first 4 bytes of binary blobs (I left the write logic commented out for safety),
  • Replace LogMsg calls with your managed Process_Message(String^) (I can show the exact marshalling),
  • Return a vector of results (instance -> current ConfigData) instead of printing.
Which would you like next: enable writes, or keep read-only and convert logs to Process_Message?*