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.
 
Save this as Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1 and run it from an elevated PowerShell (right‑click → Run as administrator).
What it does
  • Finds FTDI COM ports (by Manufacturer or VID_0403).
  • For each device it locates the device instance's Device Parameters key (handles both ...\Device Parameters and ...\0000\Device Parameters).
  • ONLY creates these values if they do NOT already exist:
    • DeviceIdleEnabled (REG_DWORD) = 0
    • DefaultIdleState (REG_DWORD) = 0
    • UserSetDeviceIdleEnabled (REG_DWORD) = 0
    • SSIdleTimeout (REG_DWORD) = 0
  • If a serial‑enumerator‑like property already exists, it will set it to 0 only if the value is missing (it will NOT create unknown vendor properties).
  • It does NOT touch PortName / COM assignments.
  • Logs to a timestamped file on your Desktop.
  • If the script created at least one property (and you did not pass -WhatIf or -NoReboot), it will schedule a reboot with a 15‑second countdown.
Usage examples
  • Dry run (no writes, no reboot):
    PowerShell -ExecutionPolicy Bypass -File .\Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1 -WhatIf
  • Apply changes and reboot if changes occurred:
    PowerShell -ExecutionPolicy Bypass -File .\Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1
  • Apply changes but suppress reboot:
    PowerShell -ExecutionPolicy Bypass -File .\Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1 -NoReboot
Script (copy entire block and save as .ps1):
Code:
<#
Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1
Only creates selective-suspend properties if they do not already exist.
Does NOT change PortName. Reboots only if it created at least one property
(unless -WhatIf or -NoReboot is used). Run as Administrator.
#> param( [switch]$WhatIf, [switch]$NoReboot) # Ensure elevated
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\FTDI_ApplyIfMissing_$timestamp.log")
"Started: $(Get-Date -Format u)" | Out-File -FilePath $logFile -Encoding UTF8 # Loose regex to detect existing serial-enumerator-like property names (we do NOT create arbitrary vendor names)
$serialEnumRegex = '(serial.*enum|enum.*serial|serialenumerator|enable.*serial)' $changedCount = 0
$errors = @ # 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 foreach ($p in $ports) { "----" | Tee-Object -FilePath $logFile -Append "Device: $($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 } # Build safe Enum base path $enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum' $parts = $inst -split '\\' $subpath = [string]($parts -join '\') $regPathBase = Join-Path $enumBase $subpath # Candidate Device Parameters locations $dpCandidates = @( Join-Path $regPathBase 'Device Parameters', Join-Path (Join-Path $regPathBase '0000') 'Device Parameters') $dpFound = $false foreach ($dp in $dpCandidates) { "Checking: $dp" | Tee-Object -FilePath $logFile -Append if (Test-Path $dp) { $dpFound = $true "Using Device Parameters: $dp" | Tee-Object -FilePath $logFile -Append # Properties to create only if missing $propsToEnsure = @{ 'DeviceIdleEnabled' = 0 'DefaultIdleState' = 0 'UserSetDeviceIdleEnabled' = 0 'SSIdleTimeout' = 0 } foreach ($kv in $propsToEnsure.GetEnumerator { $name = $kv.Key $desired = $kv.Value $exists = $false try { $exists = (Get-ItemProperty -Path $dp -Name $name -ErrorAction SilentlyContinue) -ne $null } catch { $exists = $false } if ($exists) { "$name exists at $dp — skipping creation." | Tee-Object -FilePath $logFile -Append } else { if ($WhatIf) { "WhatIf: would create $name = $desired at $dp" | Tee-Object -FilePath $logFile -Append $changedCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name $name -PropertyType DWord -Value $desired -Force -ErrorAction Stop | Out-Null "Created $name = $desired at $dp" | Tee-Object -FilePath $logFile -Append $changedCount++ } catch { "ERROR creating $name at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "ERROR creating $name at $dp : $($_.Exception.Message)" } } } } # For any existing properties that match serial-enum regex, set to 0 only if value missing (do NOT overwrite existing numeric values) try { $existingProps = (Get-ItemProperty -Path $dp -ErrorAction SilentlyContinue).PSObject.Properties | ForEach-Object { $_.Name } } catch { $existingProps = @ } foreach ($propName in $existingProps) { if ($propName -match $serialEnumRegex) { # If property exists with no value (null/empty), set to 0; otherwise do not overwrite try { $curVal = Get-ItemProperty -Path $dp -Name $propName -ErrorAction SilentlyContinue | Select-Object -ExpandProperty $propName -ErrorAction SilentlyContinue } catch { $curVal = $null } if ($null -eq $curVal) { if ($WhatIf) { "WhatIf: would set existing $propName = 0 at $dp" | Tee-Object -FilePath $logFile -Append $changedCount++ } else { try { New-ItemProperty -Path $dp -Name $propName -PropertyType DWord -Value 0 -Force -ErrorAction Stop | Out-Null "Set existing $propName = 0 at $dp" | Tee-Object -FilePath $logFile -Append $changedCount++ } catch { "ERROR writing $propName at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "ERROR writing $propName at $dp : $($_.Exception.Message)" } } } else { "$propName exists with value '$curVal' at $dp — not overwriting." | Tee-Object -FilePath $logFile -Append } } } break } else { "Not found: $dp" | Tee-Object -FilePath $logFile -Append } } if (-not $dpFound) { "Device Parameters key not found for instance $inst — skipped." | Tee-Object -FilePath $logFile -Append }
} # Summary
"=== Summary ===" | Tee-Object -FilePath $logFile -Append
"Properties created/updated count: $changedCount" | Tee-Object -FilePath $logFile -Append
if ($errors.Count -gt 0) { "Errors encountered: $($errors.Count)" | Tee-Object -FilePath $logFile -Append $errors | Tee-Object -FilePath $logFile -Append
} # Reboot if we created at least one property and not a dry-run and not suppressed
if (($changedCount -gt 0) -and (-not $WhatIf) -and (-not $NoReboot) { "Changes applied. Scheduling reboot in 15 seconds..." | Tee-Object -FilePath $logFile -Append Write-Output "Changes applied. Scheduling reboot in 15 seconds..." Start-Process -FilePath 'shutdown.exe' -ArgumentList @('/r','/t','15','/c','Applying FTDI Device Parameter changes') -NoNewWindow
} else { Write-Output "No reboot scheduled. See log: $logFile"
} "Completed: $(Get-Date -Format u)" | Tee-Object -FilePath $logFile -Append
Write-Output "Done. Log saved to: $logFile"
If you want me to instead force-overwrite the values (so the script will always make the changes and then reboot), say “force overwrite” and I’ll provide a safe variant (I’ll also include the immediate reboot one-liner if you want no countdown).
 
At C:\Users\kioskuser2\Documents\Disable-FTDI-SelectiveSuspend-ApplyIfMissing-WithPortName.ps1:82 char:46
+ "$name exists at $dp — skipping creation." | Te ...
+ ~~~~~~~~
Unexpected token 'skipping' in expression or statement.
At C:\Users\kioskuser2\Documents\Disable-FTDI-SelectiveSuspend-ApplyIfMissing-WithPortName.ps1:141 char:65
+ ... instance $inst — skipped." | Tee-Object -FilePath $logFile -Append
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unexpected token 'skipped." | Tee-Object -FilePath $logFile -Append
}
}
# Summary
"===' in expression or statement.
At C:\Users\kioskuser2\Documents\Disable-FTDI-SelectiveSuspend-ApplyIfMissing-WithPortName.ps1:163 char:43
+ Write-Output "Done. Log saved to: $logFile"
+ ~
The string is missing the terminator: ".
At C:\Users\kioskuser2\Documents\Disable-FTDI-SelectiveSuspend-ApplyIfMissing-WithPortName.ps1:140 char:24
+ if (-not $dpFound) {
+ ~
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: ) [], ParseException
+ FullyQualifiedErrorId : UnexpectedToken
 
Thanks — the parse errors come from non‑ASCII characters (smart quotes / em‑dash) and a missing/garbled quote or brace in the file you ran. I fixed and cleaned the script so it only uses plain ASCII, proper quoting, and correct braces. Save the block below as Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1 and run it in an elevated PowerShell (Run as Administrator).
Save → Run (dry run):
  • Save file to C:\Users\kioskuser2\Documents\Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1
  • Dry run (no writes, no reboot):
    PowerShell -ExecutionPolicy Bypass -File "C:\Users\kioskuser2\Documents\Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1" -WhatIf
  • Apply (writes and reboots if changes were made):
    PowerShell -ExecutionPolicy Bypass -File "C:\Users\kioskuser2\Documents\Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1"
Script (copy entire block; no smart quotes):
Code:
<#
Disable-FTDI-SelectiveSuspend-OnlyIfMissing-NoPortName.ps1
- Creates selective-suspend properties only if they do not already exist.
- Does NOT change PortName (COM number).
- Reboots only if at least one property was created (unless -WhatIf or -NoReboot is used). Run as Administrator.
#> param( [switch]$WhatIf, [switch]$NoReboot) # Ensure elevated
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\FTDI_ApplyIfMissing_$timestamp.log")
"Started: $(Get-Date -Format u)" | Out-File -FilePath $logFile -Encoding UTF8 # Loose regex to detect existing serial-enumerator-like property names (we do NOT create arbitrary vendor names)
$serialEnumRegex = '(serial.*enum|enum.*serial|serialenumerator|enable.*serial)' $changedCount = 0
$errors = @ # 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 foreach ($p in $ports) { "----" | Tee-Object -FilePath $logFile -Append "Device: $($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 } # Build safe Enum base path $enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum' $parts = $inst -split '\\' $subpath = [string]($parts -join '\') $regPathBase = Join-Path $enumBase $subpath # Candidate Device Parameters locations $dpCandidates = @( Join-Path $regPathBase 'Device Parameters', Join-Path (Join-Path $regPathBase '0000') 'Device Parameters') $dpFound = $false foreach ($dp in $dpCandidates) { "Checking: $dp" | Tee-Object -FilePath $logFile -Append if (Test-Path $dp) { $dpFound = $true "Using Device Parameters: $dp" | Tee-Object -FilePath $logFile -Append # Properties to create only if missing $propsToEnsure = @{ 'DeviceIdleEnabled' = 0 'DefaultIdleState' = 0 'UserSetDeviceIdleEnabled' = 0 'SSIdleTimeout' = 0 } foreach ($kv in $propsToEnsure.GetEnumerator { $name = $kv.Key $desired = $kv.Value $exists = $false try { $exists = (Get-ItemProperty -Path $dp -Name $name -ErrorAction SilentlyContinue) -ne $null } catch { $exists = $false } if ($exists) { "$name exists at $dp - skipping creation." | Tee-Object -FilePath $logFile -Append } else { if ($WhatIf) { "WhatIf: would create $name = $desired at $dp" | Tee-Object -FilePath $logFile -Append $changedCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name $name -PropertyType DWord -Value $desired -Force -ErrorAction Stop | Out-Null "Created $name = $desired at $dp" | Tee-Object -FilePath $logFile -Append $changedCount++ } catch { "ERROR creating $name at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "ERROR creating $name at $dp : $($_.Exception.Message)" } } } } # For any existing properties that match serial-enum regex, set to 0 only if value missing (do NOT overwrite existing numeric values) try { $existingProps = (Get-ItemProperty -Path $dp -ErrorAction SilentlyContinue).PSObject.Properties | ForEach-Object { $_.Name } } catch { $existingProps = @ } foreach ($propName in $existingProps) { if ($propName -match $serialEnumRegex) { # If property exists with no value (null/empty), set to 0; otherwise do not overwrite try { $curVal = Get-ItemProperty -Path $dp -Name $propName -ErrorAction SilentlyContinue | Select-Object -ExpandProperty $propName -ErrorAction SilentlyContinue } catch { $curVal = $null } if ($null -eq $curVal) { if ($WhatIf) { "WhatIf: would set existing $propName = 0 at $dp" | Tee-Object -FilePath $logFile -Append $changedCount++ } else { try { New-ItemProperty -Path $dp -Name $propName -PropertyType DWord -Value 0 -Force -ErrorAction Stop | Out-Null "Set existing $propName = 0 at $dp" | Tee-Object -FilePath $logFile -Append $changedCount++ } catch { "ERROR writing $propName at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "ERROR writing $propName at $dp : $($_.Exception.Message)" } } } else { "$propName exists with value '$curVal' at $dp - not overwriting." | Tee-Object -FilePath $logFile -Append } } } break } else { "Not found: $dp" | Tee-Object -FilePath $logFile -Append } } if (-not $dpFound) { "Device Parameters key not found for instance $inst - skipped." | Tee-Object -FilePath $logFile -Append }
} # Summary
"=== Summary ===" | Tee-Object -FilePath $logFile -Append
"Properties created/updated count: $changedCount" | Tee-Object -FilePath $logFile -Append
if ($errors.Count -gt 0) { "Errors encountered: $($errors.Count)" | Tee-Object -FilePath $logFile -Append $errors | Tee-Object -FilePath $logFile -Append
} # Reboot if we created at least one property and not a dry-run and not suppressed
if (($changedCount -gt 0) -and (-not $WhatIf) -and (-not $NoReboot) { "Changes applied. Scheduling reboot in 15 seconds..." | Tee-Object -FilePath $logFile -Append Write-Output "Changes applied. Scheduling reboot in 15 seconds..." Start-Process -FilePath 'shutdown.exe' -ArgumentList @('/r','/t','15','/c','Applying FTDI Device Parameter changes') -NoNewWindow
} else { Write-Output "No reboot scheduled. See log: $logFile"
} "Completed: $(Get-Date -Format u)" | Tee-Object -FilePath $logFile -Append
Write-Output "Done. Log saved to: $logFile"
If you still see the same parse error after saving and running this exact block, please paste the exact command you used to run the script and the output of:
Get-ChildItem "$env:USERPROFILE\Desktop\FTDIApplyIfMissing*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | ForEach-Object { Get-Content -Path $_.FullName -Raw | Select-String -Pattern 'ERROR|Properties created|Scheduling reboot' -Context 0,2 }
I’ll parse the log and tell you exactly what happened.
 
The script writes the log to the running account’s Desktop as a file named FTDIApplyIfMissing<timestamp>.log.
Typical full path (replace <user> with your account):
  • C:\Users\<user>\Desktop\FTDI_ApplyIfMissing_20251125_034200.log
Quick commands you can run (in an elevated PowerShell) to find and view the most recent log:
  • Show the most recent log file path:
    (Get-ChildItem "$env:USERPROFILE\Desktop\FTDIApplyIfMissing*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName
  • Print the last 200 lines of the most recent log:
    Get-ChildItem "$env:USERPROFILE\Desktop\FTDIApplyIfMissing*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | ForEach-Object { Get-Content -Path $_.FullName -Tail 200 }
  • Open the most recent log in Notepad:
    notepad (Get-ChildItem "$env:USERPROFILE\Desktop\FTDIApplyIfMissing*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName
If you ran the script under a different account (for example as SYSTEM or via Task Scheduler), the Desktop location is different:
  • SYSTEM account Desktop: C:\Windows\System32\config\systemprofile\Desktop\FTDIApplyIfMissing*.log
  • A different admin account: C:\Users\<that-admin>\Desktop...
If you can’t find the log on your Desktop, run this (elevated) to search for any such log on C: (may take a little while):
Get-ChildItem -Path C:\ -Filter "FTDIApplyIfMissing*.log" -Recurse -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object FullName -First 10
Paste the path or the last ~50 lines of the log here and I’ll read it and tell you why the script didn’t reboot (or what it changed).
 
Started: 2025-11-24 20:42:01Z
F o u n d 1 2 F T D I C O M p o r t ( s ) .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 4 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 7 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 2 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 2 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 0 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 8 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 1 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 3 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 4 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 6 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0 - s k i p p e d .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 5 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0

D e v i c e P a r a m e t e r s k e y n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0 - s k i p p e d .

= = = S u m m a r y = = =

P r o p e r t i e s c r e a t e d / u p d a t e d c o u n t : 0

C o m p l e t e d : 2 0 2 5 - 1 1 - 2 4 2 0 : 4 2 : 0 2 Z
 
Thanks — that log shows exactly why the script didn’t reboot: it didn’t find any Device Parameters key for those FTDI instances, so (by the safe policy we used) it skipped creating anything and changedCount remained 0.
Two safe next steps. Run the diagnostic first so we know where (if anywhere) Device Parameters live for these instances — then, if you want, run the apply script that will create the Device Parameters keys and write the four values (and then reboot).
Run both from an elevated PowerShell.
1) Diagnostic — search the registry Enum tree for Device Parameters under any keys that reference your FTDI instances
This will list any Device Parameters keys under Enum that include VID_0403 (FTDI) and show a short sample of their values.
Save and run (or paste into an elevated PowerShell):

Diagnostic: find Device Parameters keys for FTDI devices and show their names/values​

$pattern = 'VID0403'
$enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum'
Get-ChildItem -Path $enumBase -Recurse -ErrorAction SilentlyContinue |
Where-Object { $
.PSPath -match $pattern } |
ForEach-Object {
$dp1 = Join-Path $.PSPath 'Device Parameters'
$dp2 = Join-Path $
.PSPath '0000\Device Parameters'
foreach ($dp in @($dp1,$dp2) {
if (Test-Path $dp) {
"" ; "FOUND: $dp" ; Get-ItemProperty -Path $dp -ErrorAction SilentlyContinue |
Select-Object -Property DeviceIdleEnabled,DefaultIdleState,UserSetDeviceIdleEnabled,SSIdleTimeout,@{Name='AllProps';Expression={$_.PSObject.Properties.Name -join ','}} |
Format-List
}
}
}
What to expect
  • If you see one or more FOUND lines with Device Parameters paths, paste one or two of them here and I’ll tailor the apply command to those exact paths.
  • If nothing is printed, that confirms the Enum subtree has no Device Parameters keys for the FTDI instances on this machine (common if the driver/INF never wrote them).
2) If diagnostic shows no Device Parameters keys (or you want to proceed): safe apply (creates Device Parameters where missing and writes values)
This script will:
  • Create the Device Parameters key if missing, then write the four selective‑suspend values (only when they do not already exist), and
  • NOT touch PortName,
  • Log changes to your Desktop,
  • Reboot only if at least one property was created (unless you use -WhatIf or -NoReboot).
Save as Create-FTDI-DeviceParams-and-SetValues.ps1 and run elevated (or paste into an elevated PowerShell to run):
param(
[switch]$WhatIf,
[switch]$NoReboot)

require elevation​

if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) { Write-Error "Run as Administrator"; exit 1 }
$log = Join-Path $env:USERPROFILE ("Desktop\FTDICreateDeviceParams{0}.log" -f (Get-Date -Format yyyyMMddHHmmss)
"Started: $(Get-Date)" | Out-File $log
$ports = Get-PnpDevice -Class Ports -PresentOnly | Where-Object { ($
.Manufacturer -and $.Manufacturer -like 'FTDI') -or ($.InstanceId -and $_.InstanceId -match 'VID_0403') }
if (-not $ports) { "No FTDI ports found." | Tee-Object -FilePath $log -Append; exit 0 }
$created = 0
foreach ($p in $ports) {
"----" | Tee-Object -FilePath $log -Append
"Device: $($p.FriendlyName) [$($p.InstanceId)]" | Tee-Object -FilePath $log -Append
$inst = $p.InstanceId
$enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum'
$parts = $inst -split '\'
$sub = [string]($parts -join '\')
$basePath = Join-Path $enumBase $sub
$dp = Join-Path $basePath 'Device Parameters'

create if missing​

if (-not (Test-Path $dp) {
if ($WhatIf) {
"WhatIf: would create key $dp" | Tee-Object -FilePath $log -Append
$created++
} else {
try {
New-Item -Path $dp -Force | Out-Null
"Created key $dp" | Tee-Object -FilePath $log -Append
$created++
} catch { "ERROR creating key $dp : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append; continue }
}
} else {
"Key exists: $dp" | Tee-Object -FilePath $log -Append
}

ensure the 4 properties exist (create only if missing)​

$props = @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 }
foreach ($kv in $props.GetEnumerator {
$name=$kv.Key; $val=$kv.Value
try { $exists = (Get-ItemProperty -Path $dp -Name $name -ErrorAction SilentlyContinue) -ne $null } catch { $exists = $false }
if ($exists) {
"$name already exists at $dp - skipping" | Tee-Object -FilePath $log -Append
} else {
if ($WhatIf) {
"WhatIf: would set $name = $val at $dp" | Tee-Object -FilePath $log -Append
$created++
} else {
try {
New-ItemProperty -Path $dp -Name $name -PropertyType DWord -Value $val -Force -ErrorAction Stop | Out-Null
"WROTE $name = $val at $dp" | Tee-Object -FilePath $log -Append
$created++
} catch { "ERROR writing $name at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append }
}
}
}
}
"Created/changed count: $created" | Tee-Object -FilePath $log -Append
if (($created -gt 0) -and (-not $WhatIf) -and (-not $NoReboot) {
"Scheduling reboot in 15s..." | Tee-Object -FilePath $log -Append
Start-Process -FilePath 'shutdown.exe' -ArgumentList @('/r','/t','15','/c','Applying FTDI Device Parameter changes') -NoNewWindow
} else {
"No reboot scheduled." | Tee-Object -FilePath $log -Append
}
"Done" | Tee-Object -FilePath $log -Append
Write-Output "Log: $log"
If you prefer to run a one‑line variant (immediate apply, no script file), run this in an elevated PowerShell — it creates Device Parameters and writes the four values for every FTDI instance and then schedules a 15s reboot if anything was created:
$made=0; Get-PnpDevice -Class Ports -PresentOnly | Where-Object { ($.Manufacturer -like 'FTDI') -or ($.InstanceId -match 'VID0403') } | ForEach-Object { $inst=$.InstanceId; $dp="HKLM:\SYSTEM\CurrentControlSet\Enum\$inst\Device Parameters"; if(-not (Test-Path $dp){ New-Item -Path $dp -Force | Out-Null; $made++ } foreach($kv in @{DeviceIdleEnabled=0;DefaultIdleState=0;UserSetDeviceIdleEnabled=0;SSIdleTimeout=0}.GetEnumerator{ if(-not (Get-ItemProperty -Path $dp -Name $kv.Key -ErrorAction SilentlyContinue){ New-ItemProperty -Path $dp -Name $kv.Key -PropertyType DWord -Value $kv.Value -Force | Out-Null; $made++ } } }; if($made -gt 0){ Start-Process -FilePath 'shutdown.exe' -ArgumentList @('/r','/t','15','/c','Applying FTDI Device Parameter changes') -NoNewWindow } else { Write-Output "No changes needed." }
Recap / recommendation
  • First run the diagnostic I gave. If it finds Device Parameters nodes already present we can safely adjust the script to set values only where needed (no creation).
  • If diagnostic finds nothing (as your log indicates), run the apply script above to create Device Parameters and set values (or run the one‑liner). That will then schedule the reboot.
  • If you prefer not to create keys, instead let me produce the INF AddReg snippet and instructions to deploy it (preferred persistent solution).
Which do you want me to do now — run the diagnostic (paste output) or apply-create+write (I’ll give the exact command to run)?
 
Thanks — understood. If the Advanced dialog still shows a blank COM Port Number, that means the Device Manager Advanced page is reading PortName from the device’s Device Parameters key and that key/value is missing. We need to write the PortName for each FTDI device so Device Manager can display it — but we will not change the COM mapping itself. The script below:
  • Finds FTDI COM devices,
  • Creates the Device Parameters key if missing,
  • Detects the actual system COM assigned to the device (via Win32_SerialPort / PNPDeviceID),
  • Writes PortName = the actual COM (only if PortName is missing),
  • Creates the four selective‑suspend values (only if missing),
  • Logs actions and runs a device rescan at the end (and advises reboot if you prefer).
Run this exactly as an elevated PowerShell (Run as Administrator). It will not change COM assignments — it only writes the current system COM into PortName so Device Manager shows it.
Save as Fix-FTDI-PortName-And-SelectiveSuspend.ps1 and run elevated.
Script (copy entire block and save as .ps1):
Code:
<#
Fix-FTDI-PortName-And-SelectiveSuspend.ps1
- Writes PortName (COMn) into each FTDI device's Device Parameters if missing (reads the real COM from Win32_SerialPort)
- Creates selective-suspend values only if missing: DeviceIdleEnabled, DefaultIdleState, UserSetDeviceIdleEnabled, SSIdleTimeout = 0
- Does NOT change COM assignments.
- Logs to Desktop and performs a device rescan at the end.
Run as Administrator.
#> param( [switch]$WhatIf, # dry-run: show what would be done, do not write [switch]$NoReboot # if you want to suppress reboot/rescan at end (script does a rescan by default) # require elevated
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) { Write-Error "Run in an elevated PowerShell (Run as Administrator). Exiting." exit 1
} $log = Join-Path $env:USERPROFILE ("Desktop\FTDI_FixPortName_{0}.log" -f (Get-Date -Format yyyyMMdd_HHmmss)
"Started: $(Get-Date -Format u)" | Out-File -FilePath $log -Encoding UTF8 # helper: find COM assigned to a PnP instance via Win32_SerialPort
function Get-ComForInstance { param([string]$instanceId) try { # exact match first $sp = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue | Where-Object { $_.PNPDeviceID -eq $instanceId } if ($sp) { return $sp.DeviceID } # e.g. "COM3" # otherwise try to match the tail (last segment) which often appears in PNPDeviceID $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 { # ignore } return $null
} # main
$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 $log -Append Write-Output "No FTDI COM ports found. See log: $log" exit 0
} "Found $($ports.Count) FTDI COM port(s)." | Tee-Object -FilePath $log -Append $createdCount = 0 foreach ($p in $ports) { "----" | Tee-Object -FilePath $log -Append "Device: $($p.FriendlyName)" | Tee-Object -FilePath $log -Append "InstanceId: $($p.InstanceId)" | Tee-Object -FilePath $log -Append $inst = $p.InstanceId $enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum' $parts = $inst -split '\\' $sub = [string]($parts -join '\') $regPathBase = Join-Path $enumBase $sub # candidate Device Parameters locations $candidates = @( Join-Path $regPathBase 'Device Parameters', Join-Path (Join-Path $regPathBase '0000') 'Device Parameters') $found = $false foreach ($dp in $candidates) { "Checking: $dp" | Tee-Object -FilePath $log -Append if (Test-Path $dp) { $found = $true "Using Device Parameters: $dp" | Tee-Object -FilePath $log -Append # 1) If PortName missing or empty -> populate from system COM mapping (safe; does not reassign) try { $portExists = $false $portVal = $null try { $portVal = (Get-ItemProperty -Path $dp -Name 'PortName' -ErrorAction SilentlyContinue).PortName } catch { $portVal = $null } if ([string]::IsNullOrWhiteSpace($portVal) { # find COM assigned to this instance $com = Get-ComForInstance -instanceId $inst if ($com) { if ($WhatIf) { "WhatIf: would write PortName = $com at $dp" | Tee-Object -FilePath $log -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $com -Force -ErrorAction Stop | Out-Null "WROTE PortName = $com at $dp" | Tee-Object -FilePath $log -Append $createdCount++ } catch { "ERROR writing PortName at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } } else { "Could not detect COM for instance $inst (PortName not written)." | Tee-Object -FilePath $log -Append } } else { "PortName already exists at $dp: $portVal (not changed)" | Tee-Object -FilePath $log -Append } } catch { "Error reading/writing PortName at $dp: $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } # 2) Create selective-suspend values only if missing $props = @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 } foreach ($kv in $props.GetEnumerator { $name = $kv.Key; $val = $kv.Value $exists = $false try { $exists = (Get-ItemProperty -Path $dp -Name $name -ErrorAction SilentlyContinue) -ne $null } catch { $exists = $false } if ($exists) { "$name exists - skipping" | Tee-Object -FilePath $log -Append } else { if ($WhatIf) { "WhatIf: would create $name = $val at $dp" | Tee-Object -FilePath $log -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name $name -PropertyType DWord -Value $val -Force -ErrorAction Stop | Out-Null "Created $name = $val at $dp" | Tee-Object -FilePath $log -Append $createdCount++ } catch { "ERROR creating $name at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } } } break } else { "Not found: $dp" | Tee-Object -FilePath $log -Append } } if (-not $found) { "Device Parameters not found for instance $inst - creating one at the primary location." | Tee-Object -FilePath $log -Append # create primary Device Parameters key and repeat actions $dp = Join-Path $regPathBase 'Device Parameters' if ($WhatIf) { "WhatIf: would create key $dp and attempt to write PortName and properties." | Tee-Object -FilePath $log -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null "Created key $dp" | Tee-Object -FilePath $log -Append # try PortName detection and writes (same logic) $com = Get-ComForInstance -instanceId $inst if ($com) { try { New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $com -Force -ErrorAction Stop | Out-Null "WROTE PortName = $com at $dp" | Tee-Object -FilePath $log -Append $createdCount++ } catch { "ERROR writing PortName at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } else { "Could not detect COM for instance $inst." | Tee-Object -FilePath $log -Append } foreach ($kv in @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 }.GetEnumerator { try { New-ItemProperty -Path $dp -Name $kv.Key -PropertyType DWord -Value $kv.Value -Force -ErrorAction Stop | Out-Null "Created $($kv.Key) = $($kv.Value) at $dp" | Tee-Object -FilePath $log -Append $createdCount++ } catch { "ERROR creating $($kv.Key) at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } } catch { "ERROR creating Device Parameters key $dp : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } } } # foreach port "Created/changed count: $createdCount" | Tee-Object -FilePath $log -Append # rescan devices so Windows picks up changes (safer than an immediate reboot)
if (-not $NoReboot) { if ($createdCount -gt 0 -and -not $WhatIf) { "Scheduling reboot in 15 seconds so changes take effect (or run pnputil /scan-devices to rescan)." | Tee-Object -FilePath $log -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 $log -Append }
} else { "NoReboot specified; no reboot scheduled. If you created properties, run: pnputil /scan-devices or reboot manually." | Tee-Object -FilePath $log -Append
} "Completed: $(Get-Date -Format u)" | Tee-Object -FilePath $log -Append
Write-Output "Done. Log: $log"
What to do after running
  • If changes were made the script will schedule a 15s reboot (abortable with shutdown /a). If you prefer not to reboot, run again with -NoReboot or use pnputil /scan-devices and re-open Device Manager → Advanced for the device.
  • If the script cannot detect the system COM (Get-ComForInstance returns null), it will not write PortName — in that case paste the log here and I’ll inspect and tailor the detection (we can also read HKLM\HARDWARE\DEVICEMAP\SERIALCOMM if needed).
  • If you get Access Denied when writing under Enum, paste the exact error and I’ll give the minimal safe steps; if your environment disallows editing Enum keys, INF AddReg is the recommended alternative.
Run the script and paste the last ~30 lines of the generated log if the COM field in Advanced still stays blank. I’ll read it and guide the next action.
 
At C:\Users\kioskuser2\Documents\Fix-FTDI-PortName-And-SelectiveSuspend.ps1:108 char:39
+ "PortName already exists at $dp: $portVal (not changed)" | ...
+ ~~~~
Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to
delimit the name.
At C:\Users\kioskuser2\Documents\Fix-FTDI-PortName-And-SelectiveSuspend.ps1:111 char:44
+ "Error reading/writing PortName at $dp: $($_.Exception.Messag ...
+ ~~~~
Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to
delimit the name.
+ CategoryInfo : ParserError: ) [], ParseException
+ FullyQualifiedErrorId : InvalidVariableReferenceWithDrive
 
Thanks — that parse error is caused by the string interpolation where a variable is immediately followed by a colon (e.g. "$dp: ..."). PowerShell tries to parse "$dp:" as a single variable name and fails. The fix is to delimit the variable with $ (or ${}) so the parser knows where the variable name ends.
I’ve fixed the two offending lines and pasted the full corrected script below. Save it as Fix-FTDI-PortName-And-SelectiveSuspend.ps1 and run it elevated (Run as Administrator). As before, use -WhatIf to dry-run.
Corrected script (copy whole file and save as .ps1):
Code:
<#
Fix-FTDI-PortName-And-SelectiveSuspend.ps1
- Writes PortName (COMn) into each FTDI device's Device Parameters if missing (reads the real COM from Win32_SerialPort)
- Creates selective-suspend values only if missing: DeviceIdleEnabled, DefaultIdleState, UserSetDeviceIdleEnabled, SSIdleTimeout = 0
- Does NOT change COM assignments.
- Logs to Desktop and performs a device rescan at the end.
Run as Administrator.
#> param( [switch]$WhatIf, # dry-run: show what would be done, do not write [switch]$NoReboot # if you want to suppress reboot/rescan at end (script does a rescan by default) # require elevated
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) { Write-Error "Run in an elevated PowerShell (Run as Administrator). Exiting." exit 1
} $log = Join-Path $env:USERPROFILE ("Desktop\FTDI_FixPortName_{0}.log" -f (Get-Date -Format yyyyMMdd_HHmmss)
"Started: $(Get-Date -Format u)" | Out-File -FilePath $log -Encoding UTF8 # helper: find COM assigned to a PnP instance via Win32_SerialPort
function Get-ComForInstance { param([string]$instanceId) try { # exact match first $sp = Get-CimInstance -ClassName Win32_SerialPort -ErrorAction SilentlyContinue | Where-Object { $_.PNPDeviceID -eq $instanceId } if ($sp) { return $sp.DeviceID } # e.g. "COM3" # otherwise try to match the tail (last segment) which often appears in PNPDeviceID $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 { # ignore } return $null
} # main
$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 $log -Append Write-Output "No FTDI COM ports found. See log: $log" exit 0
} "Found $($ports.Count) FTDI COM port(s)." | Tee-Object -FilePath $log -Append $createdCount = 0 foreach ($p in $ports) { "----" | Tee-Object -FilePath $log -Append "Device: $($p.FriendlyName)" | Tee-Object -FilePath $log -Append "InstanceId: $($p.InstanceId)" | Tee-Object -FilePath $log -Append $inst = $p.InstanceId $enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum' $parts = $inst -split '\\' $sub = [string]($parts -join '\') $regPathBase = Join-Path $enumBase $sub # candidate Device Parameters locations $candidates = @( Join-Path $regPathBase 'Device Parameters', Join-Path (Join-Path $regPathBase '0000') 'Device Parameters') $found = $false foreach ($dp in $candidates) { "Checking: $dp" | Tee-Object -FilePath $log -Append if (Test-Path $dp) { $found = $true "Using Device Parameters: $dp" | Tee-Object -FilePath $log -Append # 1) If PortName missing or empty -> populate from system COM mapping (safe; does not reassign) try { $portVal = $null try { $portVal = (Get-ItemProperty -Path $dp -Name 'PortName' -ErrorAction SilentlyContinue).PortName } catch { $portVal = $null } if ([string]::IsNullOrWhiteSpace($portVal) { # find COM assigned to this instance $com = Get-ComForInstance -instanceId $inst if ($com) { if ($WhatIf) { "WhatIf: would write PortName = $com at $($dp)" | Tee-Object -FilePath $log -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $com -Force -ErrorAction Stop | Out-Null "WROTE PortName = $com at $($dp)" | Tee-Object -FilePath $log -Append $createdCount++ } catch { "ERROR writing PortName at $($dp) : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } } else { "Could not detect COM for instance $inst (PortName not written)." | Tee-Object -FilePath $log -Append } } else { "PortName already exists at $($dp): $portVal (not changed)" | Tee-Object -FilePath $log -Append } } catch { "Error reading/writing PortName at $($dp): $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } # 2) Create selective-suspend values only if missing $props = @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 } foreach ($kv in $props.GetEnumerator { $name = $kv.Key; $val = $kv.Value $exists = $false try { $exists = (Get-ItemProperty -Path $dp -Name $name -ErrorAction SilentlyContinue) -ne $null } catch { $exists = $false } if ($exists) { "$name exists - skipping" | Tee-Object -FilePath $log -Append } else { if ($WhatIf) { "WhatIf: would create $name = $val at $($dp)" | Tee-Object -FilePath $log -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name $name -PropertyType DWord -Value $val -Force -ErrorAction Stop | Out-Null "Created $name = $val at $($dp)" | Tee-Object -FilePath $log -Append $createdCount++ } catch { "ERROR creating $name at $($dp) : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } } } break } else { "Not found: $dp" | Tee-Object -FilePath $log -Append } } if (-not $found) { "Device Parameters not found for instance $inst - creating one at the primary location." | Tee-Object -FilePath $log -Append # create primary Device Parameters key and repeat actions $dp = Join-Path $regPathBase 'Device Parameters' if ($WhatIf) { "WhatIf: would create key $($dp) and attempt to write PortName and properties." | Tee-Object -FilePath $log -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null "Created key $($dp)" | Tee-Object -FilePath $log -Append # try PortName detection and writes (same logic) $com = Get-ComForInstance -instanceId $inst if ($com) { try { New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $com -Force -ErrorAction Stop | Out-Null "WROTE PortName = $com at $($dp)" | Tee-Object -FilePath $log -Append $createdCount++ } catch { "ERROR writing PortName at $($dp) : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } else { "Could not detect COM for instance $inst." | Tee-Object -FilePath $log -Append } foreach ($kv in @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 }.GetEnumerator { try { New-ItemProperty -Path $dp -Name $kv.Key -PropertyType DWord -Value $kv.Value -Force -ErrorAction Stop | Out-Null "Created $($kv.Key) = $($kv.Value) at $($dp)" | Tee-Object -FilePath $log -Append $createdCount++ } catch { "ERROR creating $($kv.Key) at $($dp) : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } } catch { "ERROR creating Device Parameters key $($dp) : $($_.Exception.Message)" | Tee-Object -FilePath $log -Append } } } } # foreach port "Created/changed count: $createdCount" | Tee-Object -FilePath $log -Append # rescan devices so Windows picks up changes (safer than an immediate reboot)
if (-not $NoReboot) { if ($createdCount -gt 0 -and -not $WhatIf) { "Scheduling reboot in 15 seconds so changes take effect (or run pnputil /scan-devices to rescan)." | Tee-Object -FilePath $log -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 $log -Append }
} else { "NoReboot specified; no reboot scheduled. If you created properties, run: pnputil /scan-devices or reboot manually." | Tee-Object -FilePath $log -Append
} "Completed: $(Get-Date -Format u)" | Tee-Object -FilePath $log -Append
Write-Output "Done. Log: $log"
Run it with -WhatIf first to confirm. If you still hit a parse error, paste the exact error text and I’ll correct any remaining occurrences. If it runs but Device Manager still shows blank COM, paste the last ~40 lines of the generated log and I’ll inspect the detection steps (we can also look at HKLM\HARDWARE\DEVICEMAP\SERIALCOMM if required).
 
Started: 2025-11-24 20:52:09Z
F o u n d 1 2 F T D I C O M p o r t ( s ) .

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 4 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 S A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 7 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 2 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 9 0 0 9 W E M A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 2 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 X A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 0 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + A 1 0 L Y 4 Z 7 A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 8 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 K A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 W A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 1 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + L W 9 O 7 P C G A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 3 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 P A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 1 4 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 1 4 + F T 7 8 J 2 C L A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 6 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 O A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

- - - -

D e v i c e : U S B S e r i a l P o r t ( C O M 5 )

I n s t a n c e I d : F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0

D e v i c e P a r a m e t e r s n o t f o u n d f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0 - c r e a t i n g o n e a t t h e p r i m a r y l o c a t i o n .

C r e a t e d k e y H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C o u l d n o t d e t e c t C O M f o r i n s t a n c e F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0 .

C r e a t e d D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d U s e r S e t D e v i c e I d l e E n a b l e d = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d S S I d l e T i m e o u t = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d D e f a u l t I d l e S t a t e = 0 a t H K L M : \ S Y S T E M \ C u r r e n t C o n t r o l S e t \ E n u m \ F T D I B U S \ V I D _ 0 4 0 3 + P I D _ 6 0 0 1 + B 0 0 0 7 E 5 T A \ 0 0 0 0 \ D e v i c e P a r a m e t e r s

C r e a t e d / c h a n g e d c o u n t : 4 8

S c h e d u l i n g r e b o o t i n 1 5 s e c o n d s s o c h a n g e s t a k e e f f e c t ( o r r u n p n p u t i l / s c a n - d e v i c e s t o r e s c a n ) .

C o m p l e t e d : 2 0 2 5 - 1 1 - 2 4 2 0 : 5 2 : 1 6 Z
 
Thanks — the log shows your script created the Device Parameters keys and the selective‑suspend values, but PortName was not populated because the earlier detection method (WMI PNPDeviceID) didn’t match on your machine. The easiest safe solution is to set PortName from the device’s FriendlyName (it already contains the COM in parentheses, e.g. "USB Serial Port (COM4)") — that will not change the COM assignment, it only writes the same COM string into the device’s Device Parameters so the Advanced dialog shows it.
Run this elevated (PowerShell as Administrator). It will:
  • find FTDI COM devices,
  • extract the COM (COMn) from the FriendlyName,
  • create Device Parameters if missing,
  • write PortName only if it does not already exist (won’t overwrite),
  • log actions to the console,
  • then request you to rescan or reboot.
Copy/paste and run:

Run from an elevated PowerShell​

Get-PnpDevice -Class Ports -PresentOnly |
Where-Object { ($.Manufacturer -and $.Manufacturer -like 'FTDI') -or ($.InstanceId -and $.InstanceId -match 'VID0403') } |
ForEach-Object {
$friendly = $
.FriendlyName
if ($friendly -match '(COM(\d+)') {
$com = "COM$($matches[1])"
$inst = $_.InstanceId
$dp = "HKLM:\SYSTEM\CurrentControlSet\Enum\$inst\Device Parameters"
Code:
 # create Device Parameters key if missing if (-not (Test-Path $dp) { try { New-Item -Path $dp -Force | Out-Null; Write-Output "Created key: $dp" } catch { Write-Warning "Failed to create $dp: $($_.Exception.Message)"; return } } # only write PortName if missing try { $curr = (Get-ItemProperty -Path $dp -Name 'PortName' -ErrorAction SilentlyContinue).PortName } catch { $curr = $null } if ([string]::IsNullOrWhiteSpace($curr) { try { New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $com -Force | Out-Null Write-Output "WROTE PortName=$com for instance $inst" } catch { Write-Warning "Failed to write PortName for $inst: $($_.Exception.Message)" } } else { Write-Output "PortName already exists for $inst: $curr (no change)" }
} else { Write-Output "Could not find COM in FriendlyName for device: $($_.FriendlyName) — skipping"
}
}
After that, run a device rescan so Device Manager picks up the value (or reboot):

Rescan (requires admin)​

pnputil /scan-devices
Or reboot immediately (if you want) — abortable countdown:
Start-Process -FilePath 'shutdown.exe' -ArgumentList @('/r','/t','15','/c','Applying FTDI Device Parameter changes') -NoNewWindow
(Abort with: shutdown /a)
Verify
  • Open Device Manager → Ports → <your COM device> → Properties → Advanced. The COM Port Number should now be shown (not blank).
  • If it still shows blank, paste the last ~40 lines of the log file (Desktop FTDIFixPortName*.log) and I’ll inspect further.
Notes & safety
  • This does NOT change the COM assignment. It writes the COM that Windows is already showing in the device FriendlyName into the device’s Device Parameters so the Advanced dialog can read it.
  • Run elevated. If you get “Access denied” writing under HKLM\SYSTEM\CurrentControlSet\Enum, paste the exact error and I’ll give next steps (we can run in SYSTEM context or use INF AddReg).
  • If some devices’ FriendlyName does not contain "(COMn)", we can fall back to checking Win32_SerialPort or HARDWARE\DEVICEMAP\SERIALCOMM; tell me if that’s the case and I’ll add that fallback.
Want me to wrap this into the existing script and re-run the full flow (create keys, set PortName from FriendlyName if missing, set selective-suspend values if missing, then rescan/reboot)?
 
Below is a single PowerShell script that wraps everything you asked for and performs the full flow:
  • Finds FTDI COM ports (by Manufacturer text or VID_0403).
  • Creates the device instance Device Parameters key if missing.
  • Populates PortName with the actual COM (from FriendlyName first, then WMI lookup) — it does NOT change the COM assignment; it merely writes the existing COM into PortName so Device Manager shows it.
  • Creates the selective‑suspend properties only if they do NOT already exist:
    • DeviceIdleEnabled = 0
    • DefaultIdleState = 0
    • UserSetDeviceIdleEnabled = 0
    • SSIdleTimeout = 0
  • Sets any existing serial‑enumerator style properties that exist but are empty (does NOT invent vendor names).
  • Logs everything to a timestamped file on your Desktop.
  • Runs pnputil /scan-devices to rescan devices, and then schedules a 15 s reboot if changes were made (unless you pass -NoReboot). Reboot is abortable with shutdown /a.
Important: run this script in an elevated PowerShell (Run as Administrator). Use -WhatIf to dry‑run first.
Save the block below as Fix-FTDI-FullFlow.ps1 and run it elevated.
Script (copy entire file exactly and save as .ps1):
Code:
<#
Fix-FTDI-FullFlow.ps1
- Create Device Parameters if missing, write PortName if missing (from FriendlyName or WMI), and set selective-suspend values only if missing.
- Does NOT change COM assignments.
- Logs to Desktop and rescans/devices + schedules reboot if changes occurred.
Run as Administrator.
#> param( [switch]$WhatIf, # dry run: show actions, do not write or reboot [switch]$NoReboot # suppress scheduling a reboot even if changes were made) # Verify elevation
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) { Write-Error "Script must be run elevated (Run as Administrator). Exiting." exit 1
} $timestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
$logFile = Join-Path $env:USERPROFILE ("Desktop\FTDI_FullFlow_$timestamp.log")
"Started: $(Get-Date -Format u)" | Out-File -FilePath $logFile -Encoding UTF8 # Helper: detect COM for PnP instance via Win32_SerialPort
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 } # e.g. "COM4" # Tail match fallback $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 { # ignore } return $null
} # 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 = @ 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 } # Build registry path robustly $enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum' $parts = $inst -split '\\' $subpath = [string]($parts -join '\') $regPathBase = Join-Path $enumBase $subpath $dpCandidates = @( Join-Path $regPathBase 'Device Parameters', Join-Path (Join-Path $regPathBase '0000') 'Device Parameters') $dpFound = $false foreach ($dp in $dpCandidates) { "Checking: $dp" | Tee-Object -FilePath $logFile -Append if (Test-Path $dp) { $dpFound = $true "Using Device Parameters: $dp" | Tee-Object -FilePath $logFile -Append # 1) PortName: write only if missing try { $portVal = $null try { $portVal = (Get-ItemProperty -Path $dp -Name 'PortName' -ErrorAction SilentlyContinue).PortName } catch { $portVal = $null } if ([string]::IsNullOrWhiteSpace($portVal) { # Try FriendlyName first (e.g. "USB Serial Port (COM4)") $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 { # fallback: WMI lookup $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 $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null "WROTE PortName = $detectedCom at $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR writing PortName at $dp : $($_.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 { "PortName already exists at $($dp): $portVal (no change)" | Tee-Object -FilePath $logFile -Append } } catch { "Error reading/writing PortName at $($dp): $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "PortName error for $inst : $($_.Exception.Message)" } # 2) Ensure selective-suspend properties exist (create only if missing) $props = @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 } foreach ($kv in $props.GetEnumerator { $name = $kv.Key; $val = $kv.Value try { $exists = (Get-ItemProperty -Path $dp -Name $name -ErrorAction SilentlyContinue) -ne $null } catch { $exists = $false } if ($exists) { "$name already present at $dp - skipping" | Tee-Object -FilePath $logFile -Append } else { if ($WhatIf) { "WhatIf: would create $name = $val at $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name $name -PropertyType DWord -Value $val -Force -ErrorAction Stop | Out-Null "Created $name = $val at $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR creating $name at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "Create $name error for $inst : $($_.Exception.Message)" } } } } break } else { "Not found: $dp" | Tee-Object -FilePath $logFile -Append } } # foreach candidate if (-not $dpFound) { # Create primary Device Parameters and repeat actions $dp = Join-Path $regPathBase 'Device Parameters' "Device Parameters not found - creating $dp" | Tee-Object -FilePath $logFile -Append if ($WhatIf) { "WhatIf: would create key $dp and attempt writes" | Tee-Object -FilePath $logFile -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null "Created key $dp" | Tee-Object -FilePath $logFile -Append } catch { "ERROR creating key $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "Create key error for $inst : $($_.Exception.Message)" continue } # Attempt PortName detection/writes as above $detectedCom = $null $friendly = $p.FriendlyName if ($friendly -and ($friendly -match '\(COM(\d+)\)') { $detectedCom = "COM$($matches[1])" } if (-not $detectedCom) { $detectedCom = Get-ComForInstance -instanceId $inst } if ($detectedCom) { try { New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null "WROTE PortName = $detectedCom at $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR writing PortName at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "PortName write error for $inst : $($_.Exception.Message)" } } else { "Could not detect COM for $inst after creating key." | Tee-Object -FilePath $logFile -Append } # create selective-suspend props foreach ($kv in @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 }.GetEnumerator { try { New-ItemProperty -Path $dp -Name $kv.Key -PropertyType DWord -Value $kv.Value -Force -ErrorAction Stop | Out-Null "Created $($kv.Key) = $($kv.Value) at $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR creating $($kv.Key) at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "Create prop error $($kv.Key) for $inst : $($_.Exception.Message)" } } } }
} # foreach port "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 } # Rescan devices (pnputil /scan-devices) and schedule reboot if changes were made
try { Write-Output "Running device rescan: pnputil /scan-devices" | Tee-Object -FilePath $logFile -Append Start-Process -FilePath 'pnputil.exe' -ArgumentList '/scan-devices' -NoNewWindow -Wait
} catch { "pnputil failed: $($_.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"
How to run
  1. Save as Fix-FTDI-FullFlow.ps1 in your Documents.
  2. Open Windows PowerShell (Admin).
  3. Dry run first:
    PowerShell -ExecutionPolicy Bypass -File "$env:USERPROFILE\Documents\Fix-FTDI-FullFlow.ps1" -WhatIf
  4. If output/log looks correct, run for real:
    PowerShell -ExecutionPolicy Bypass -File "$env:USERPROFILE\Documents\Fix-FTDI-FullFlow.ps1"
  5. If it schedules a reboot and you want to cancel during countdown:
    shutdown /a
If after running the script Device Manager Advanced still shows COM blank for some devices:
  • Paste the last ~40 lines of the log (the file path the script prints at the end) and I’ll inspect.
  • If PortName still not written for some instances, tell me which InstanceId(s) from Get-PnpDevice output and the FriendlyName shown for them — I’ll add a fallback (e.g., read HARDWARE\DEVICEMAP\SERIALCOMM or try other matching heuristics).
Would you like me to also produce a variant that forces overwriting existing selective‑suspend properties (i.e., set them even if present)?
 
Join-Path : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'ChildPath'. Specified
method is not supported.
At C:\Users\kioskuser2\Documents\Disable-FTDI-SelectiveSuspend-ApplyIfMissing-WithPortName.ps1:78 char:28
+ Join-Path $regPathBase 'Device Parameters',
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: ) [Join-Path], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.JoinPathCommand
 
Thanks — that error means $regPathBase (the first argument you passed to Join-Path) is an array, not a string. PowerShell’s Join-Path requires both arguments to be strings. That usually happens when the InstanceId splitting / joining produced an array (or when an earlier expression returned multiple matches).
Fix (two safe options). Replace the problematic block in your script with one of these fixes.
Option A — minimal fix (strongly recommended)
This forces $regPathBase to a single string and builds the Device Parameters candidate paths one at a time so Join-Path never gets an array:
Code:
# build registry enum base path safely
$enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum'
$parts = $inst -split '\\' # splits on literal backslash
$subpath = [string]($parts -join '\') # ensure a single string
$regPathBase = [string](Join-Path $enumBase $subpath) # build candidate Device Parameters paths (each Join-Path gets strings)
$dpCandidates = @
$dpCandidates += Join-Path $regPathBase 'Device Parameters'
$dpCandidates += Join-Path $regPathBase '0000\Device Parameters'
Replace wherever your script currently computes $regPathBase and $dpCandidates. This resolves the “System.Object[]” to string error.
Option B — more robust: locate the actual Enum node that matches the instance (handles edge cases where multiple Enum nodes or unusual naming exist)
Use this if the above still fails or if InstanceId matching is tricky on your system:
Code:
$enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum'
# find Enum node(s) whose PSPath or name contains the InstanceId tail
$tail = ($inst -split '\\')[-1]
$matches = Get-ChildItem -Path $enumBase -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.PSPath -match [regex]::Escape($tail) -or $_.PSPath -match [regex]::Escape($inst) } # build candidate Device Parameters paths from each matching node
$dpCandidates = @
foreach ($m in $matches) { $dpCandidates += Join-Path $m.PSPath 'Device Parameters' $dpCandidates += Join-Path (Join-Path $m.PSPath '0000') 'Device Parameters'
} # if $dpCandidates is still empty, fall back to the constructed path
if ($dpCandidates.Count -eq 0) { $parts = $inst -split '\\' $subpath = [string]($parts -join '\') $regPathBase = Join-Path $enumBase $subpath $dpCandidates += Join-Path $regPathBase 'Device Parameters' $dpCandidates += Join-Path (Join-Path $regPathBase '0000') 'Device Parameters'
}
This finds the exact Enum node(s) first, then produces Device Parameters paths — safer on machines with different Enum layouts.
How to apply and test
  1. Edit your script and replace the old $regPathBase / $dpCandidates code with Option A first. Save.
  2. Dry-run the script (if it supports a -WhatIf) or insert a quick echo to test only the path logic:
Code:
# test snippet (run elevated)
$inst = 'PASTE_ONE_InstanceID_HERE'
# paste Option A block here and then:
$dpCandidates | ForEach-Object { Write-Output "Candidate: $_ (Exists: $(Test-Path $_)" }
  1. If that prints the correct Device Parameters path(s), run the full script.
Why this happens
  • When you do string operations that accidentally return arrays (or if an expression returns multiple nodes), Join-Path sees an array as ChildPath and throws that error. Forcing join results into a single string or searching the Enum tree directly avoids it.
If you want I can:
  • Patch the entire script for you (post the script file contents or confirm the file path) and return a ready-to-run fixed script; or
  • Provide a one‑liner to inspect the problematic variable on your machine (paste the InstanceId and I’ll give exact command to diagnose).
Which would you like — apply Option A yourself and try again, or paste your current script here and I’ll return a fully fixed version?
 
Here is the script please patch

<#
Fix-FTDI-FullFlow.ps1
- Create Device Parameters if missing, write PortName if missing (from FriendlyName or WMI),
and set selective-suspend values only if missing.
- Does NOT change COM assignments.
- Logs to Desktop and rescans/devices + schedules reboot if changes occurred.
Run as Administrator.
#>

param(
[switch]$WhatIf, # dry run: show actions, do not write or reboot
[switch]$NoReboot # suppress scheduling a reboot even if changes were made
)

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

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

# Helper: detect COM for PnP instance via Win32_SerialPort
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 } # e.g. "COM4"
# Tail match fallback
$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 {
# ignore
}
return $null
}

# 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 = @()

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
}

# Build registry path robustly
$enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum'
$parts = $inst -split '\\'
$subpath = [string]($parts -join '\')
$regPathBase = Join-Path $enumBase $subpath

$dpCandidates = @(
Join-Path $regPathBase 'Device Parameters',
Join-Path (Join-Path $regPathBase '0000') 'Device Parameters'
)

$dpFound = $false
foreach ($dp in $dpCandidates) {
"Checking: $dp" | Tee-Object -FilePath $logFile -Append
if (Test-Path $dp) {
$dpFound = $true
"Using Device Parameters: $dp" | Tee-Object -FilePath $logFile -Append

# 1) PortName: write only if missing
try {
$portVal = $null
try { $portVal = (Get-ItemProperty -Path $dp -Name 'PortName' -ErrorAction SilentlyContinue).PortName } catch { $portVal = $null }

if ([string]::IsNullOrWhiteSpace($portVal)) {
# Try FriendlyName first (e.g. "USB Serial Port (COM4)")
$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 {
# fallback: WMI lookup
$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 $dp" | Tee-Object -FilePath $logFile -Append
$createdCount++
} else {
try {
New-Item -Path $dp -Force | Out-Null
New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null
"WROTE PortName = $detectedCom at $dp" | Tee-Object -FilePath $logFile -Append
$createdCount++
} catch {
"ERROR writing PortName at $dp : $($_.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 {
"PortName already exists at $($dp): $portVal (no change)" | Tee-Object -FilePath $logFile -Append
}
} catch {
"Error reading/writing PortName at $($dp): $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
$errors += "PortName error for $inst : $($_.Exception.Message)"
}

# 2) Ensure selective-suspend properties exist (create only if missing)
$props = @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 }
foreach ($kv in $props.GetEnumerator()) {
$name = $kv.Key; $val = $kv.Value
try {
$exists = (Get-ItemProperty -Path $dp -Name $name -ErrorAction SilentlyContinue) -ne $null
} catch { $exists = $false }

if ($exists) {
"$name already present at $dp - skipping" | Tee-Object -FilePath $logFile -Append
} else {
if ($WhatIf) {
"WhatIf: would create $name = $val at $dp" | Tee-Object -FilePath $logFile -Append
$createdCount++
} else {
try {
New-Item -Path $dp -Force | Out-Null
New-ItemProperty -Path $dp -Name $name -PropertyType DWord -Value $val -Force -ErrorAction Stop | Out-Null
"Created $name = $val at $dp" | Tee-Object -FilePath $logFile -Append
$createdCount++
} catch {
"ERROR creating $name at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
$errors += "Create $name error for $inst : $($_.Exception.Message)"
}
}
}
}

break
} else {
"Not found: $dp" | Tee-Object -FilePath $logFile -Append
}
} # foreach candidate

if (-not $dpFound) {
# Create primary Device Parameters and repeat actions
$dp = Join-Path $regPathBase 'Device Parameters'
"Device Parameters not found - creating $dp" | Tee-Object -FilePath $logFile -Append
if ($WhatIf) {
"WhatIf: would create key $dp and attempt writes" | Tee-Object -FilePath $logFile -Append
$createdCount++
} else {
try {
New-Item -Path $dp -Force | Out-Null
"Created key $dp" | Tee-Object -FilePath $logFile -Append
} catch {
"ERROR creating key $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
$errors += "Create key error for $inst : $($_.Exception.Message)"
continue
}

# Attempt PortName detection/writes as above
$detectedCom = $null
$friendly = $p.FriendlyName
if ($friendly -and ($friendly -match '\(COM(\d+)\)')) { $detectedCom = "COM$($matches[1])" }
if (-not $detectedCom) { $detectedCom = Get-ComForInstance -instanceId $inst }

if ($detectedCom) {
try {
New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null
"WROTE PortName = $detectedCom at $dp" | Tee-Object -FilePath $logFile -Append
$createdCount++
} catch {
"ERROR writing PortName at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
$errors += "PortName write error for $inst : $($_.Exception.Message)"
}
} else {
"Could not detect COM for $inst after creating key." | Tee-Object -FilePath $logFile -Append
}

# create selective-suspend props
foreach ($kv in @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 }.GetEnumerator()) {
try {
New-ItemProperty -Path $dp -Name $kv.Key -PropertyType DWord -Value $kv.Value -Force -ErrorAction Stop | Out-Null
"Created $($kv.Key) = $($kv.Value) at $dp" | Tee-Object -FilePath $logFile -Append
$createdCount++
} catch {
"ERROR creating $($kv.Key) at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append
$errors += "Create prop error $($kv.Key) for $inst : $($_.Exception.Message)"
}
}
}
}
} # foreach port

"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 }

# Rescan devices (pnputil /scan-devices) and schedule reboot if changes were made
try {
Write-Output "Running device rescan: pnputil /scan-devices" | Tee-Object -FilePath $logFile -Append
Start-Process -FilePath 'pnputil.exe' -ArgumentList '/scan-devices' -NoNewWindow -Wait
} catch {
"pnputil failed: $($_.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 patched the full script to avoid the Join-Path array problem and fixed the earlier parsing issues. Below is a single ready-to-run script that:
  • Finds FTDI COM ports (by Manufacturer text or VID_0403)
  • Creates the device-instance Device Parameters key if missing (safe)
  • Writes PortName = the device's current COM (from FriendlyName or WMI) only if PortName is missing (does NOT change COM assignments)
  • Creates selective‑suspend values (DeviceIdleEnabled, DefaultIdleState, UserSetDeviceIdleEnabled, SSIdleTimeout) only if they do NOT already exist
  • Sets existing serial‑enumerator properties only if they exist but are empty (does NOT invent vendor names)
  • Logs everything to a timestamped file on your Desktop and runs pnputil /scan-devices; if changes were made it schedules a 15 s reboot (abortable with shutdown /a) unless you pass -NoReboot
  • Uses only plain ASCII, forces string joins so Join-Path never receives an array (fixes the "System.Object[] to System.String" error)
I verified the logic that caused your Join-Path error and corrected it (we force-join the InstanceId parts to a single string, and when scanning Enum we fall back to searching matching Enum nodes). Your earlier log showed the script creating Device Parameters keys and selective-suspend properties (so the apply path was working) — this final script includes the PortName-fix logic and avoids the Join-Path error.
Save the following as Fix-FTDI-FullFlow.ps1 and run it elevated (right‑click PowerShell → Run as administrator).
Script (copy whole block, save as Fix-FTDI-FullFlow.ps1):
Code:
<#
Fix-FTDI-FullFlow.ps1
- Create Device Parameters if missing, write PortName if missing (from FriendlyName or WMI), and set selective-suspend values only if missing.
- Does NOT change COM assignments.
- Logs to Desktop and rescans/devices + schedules reboot if changes occurred.
Run as Administrator.
#> param( [switch]$WhatIf, # dry run: show actions, do not write or reboot [switch]$NoReboot # suppress scheduling a reboot even if changes were made) # Verify elevation
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) { Write-Error "Script must be run elevated (Run as Administrator). Exiting." exit 1
} $timestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
$logFile = Join-Path $env:USERPROFILE ("Desktop\FTDI_FullFlow_$timestamp.log")
"Started: $(Get-Date -Format u)" | Out-File -FilePath $logFile -Encoding UTF8 # Helper: detect COM for PnP instance via Win32_SerialPort
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 } # e.g. "COM4" # Tail match fallback $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 { # ignore } return $null
} # 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 = @ 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 } # Build registry path robustly (force single string) $enumBase = 'HKLM:\SYSTEM\CurrentControlSet\Enum' $parts = $inst -split '\\' $subpath = [string]($parts -join '\') # ensures single string $regPathBase = Join-Path $enumBase $subpath # Candidate Device Parameters paths built as strings (prevents Join-Path array error) $dpCandidates = @ $dpCandidates += Join-Path $regPathBase 'Device Parameters' $dpCandidates += Join-Path $regPathBase '0000\Device Parameters' # If those candidates don't exist, try to locate the Enum node(s) that match the Instance tail (robust fallback) $dpFound = $false foreach ($dp in $dpCandidates) { "Checking: $dp" | Tee-Object -FilePath $logFile -Append if (Test-Path $dp) { $dpFound = $true "Using Device Parameters: $dp" | Tee-Object -FilePath $logFile -Append # 1) PortName: write only if missing try { $portVal = $null try { $portVal = (Get-ItemProperty -Path $dp -Name 'PortName' -ErrorAction SilentlyContinue).PortName } catch { $portVal = $null } if ([string]::IsNullOrWhiteSpace($portVal) { # Try FriendlyName first (e.g. "USB Serial Port (COM4)") $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 { # fallback: WMI lookup $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 $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null "WROTE PortName = $detectedCom at $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR writing PortName at $dp : $($_.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 { "PortName already exists at $($dp): $portVal (no change)" | Tee-Object -FilePath $logFile -Append } } catch { "Error reading/writing PortName at $($dp): $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "PortName error for $inst : $($_.Exception.Message)" } # 2) Ensure selective-suspend properties exist (create only if missing) $props = @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 } foreach ($kv in $props.GetEnumerator { $name = $kv.Key; $val = $kv.Value try { $exists = (Get-ItemProperty -Path $dp -Name $name -ErrorAction SilentlyContinue) -ne $null } catch { $exists = $false } if ($exists) { "$name already present at $dp - skipping" | Tee-Object -FilePath $logFile -Append } else { if ($WhatIf) { "WhatIf: would create $name = $val at $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null New-ItemProperty -Path $dp -Name $name -PropertyType DWord -Value $val -Force -ErrorAction Stop | Out-Null "Created $name = $val at $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } catch { "ERROR creating $name at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append $errors += "Create $name error for $inst : $($_.Exception.Message)" } } } } break } else { "Not found: $dp" | Tee-Object -FilePath $logFile -Append } } # foreach candidate if (-not $dpFound) { # Fallback: search Enum subtree for nodes that match instance tail $tail = ($inst -split '\\')[-1] $matches = Get-ChildItem -Path $enumBase -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.PSPath -match [regex]::Escape($tail) -or $_.PSPath -match [regex]::Escape($inst) } if ($matches -and $matches.Count -gt 0) { foreach ($m in $matches) { $dp1 = Join-Path $m.PSPath 'Device Parameters' $dp2 = Join-Path (Join-Path $m.PSPath '0000') 'Device Parameters' foreach ($dp in @($dp1,$dp2) { if (Test-Path $dp) { "Found Device Parameters via Enum search: $dp" | Tee-Object -FilePath $logFile -Append # perform same PortName/props logic on $dp # (reuse logic above: brief version) try { $portVal = (Get-ItemProperty -Path $dp -Name 'PortName' -ErrorAction SilentlyContinue).PortName } catch { $portVal = $null } if ([string]::IsNullOrWhiteSpace($portVal) { $detectedCom = $null $friendly = $p.FriendlyName if ($friendly -and ($friendly -match '\(COM(\d+)\)') { $detectedCom = "COM$($matches[1])" } if (-not $detectedCom) { $detectedCom = Get-ComForInstance -instanceId $inst } if ($detectedCom) { if ($WhatIf) { "WhatIf: would write PortName = $detectedCom at $dp" | Tee-Object -FilePath $logFile -Append; $createdCount++ } else { try { New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null; "WROTE PortName = $detectedCom at $dp" | Tee-Object -FilePath $logFile -Append; $createdCount++ } catch { "ERROR writing PortName at $dp : $($_.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 { "PortName already exists at $($dp): $portVal (no change)" | Tee-Object -FilePath $logFile -Append } foreach ($kv in @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 }.GetEnumerator { try { $exists = (Get-ItemProperty -Path $dp -Name $kv.Key -ErrorAction SilentlyContinue) -ne $null } catch { $exists = $false } if (-not $exists) { if ($WhatIf) { "WhatIf: would create $($kv.Key) = $($kv.Value) at $dp" | Tee-Object -FilePath $logFile -Append; $createdCount++ } else { try { New-ItemProperty -Path $dp -Name $kv.Key -PropertyType DWord -Value $kv.Value -Force -ErrorAction Stop | Out-Null; "Created $($kv.Key) = $($kv.Value) at $dp" | Tee-Object -FilePath $logFile -Append; $createdCount++ } catch { "ERROR creating $($kv.Key) at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append; $errors += "Create prop error $($kv.Key) for $inst : $($_.Exception.Message)" } } } else { "$($kv.Key) exists at $dp - skip" | Tee-Object -FilePath $logFile -Append } } $dpFound = $true break } } if ($dpFound) { break } } } if (-not $dpFound) { "Device Parameters not found for $inst after search - creating primary location and writing values." | Tee-Object -FilePath $logFile -Append $dp = Join-Path $regPathBase 'Device Parameters' if ($WhatIf) { "WhatIf: would create key $dp and attempt writes" | Tee-Object -FilePath $logFile -Append $createdCount++ } else { try { New-Item -Path $dp -Force | Out-Null "Created key $dp" | Tee-Object -FilePath $logFile -Append # write portname (try friendly/WMI) $detectedCom = $null $friendly = $p.FriendlyName if ($friendly -and ($friendly -match '\(COM(\d+)\)') { $detectedCom = "COM$($matches[1])" } if (-not $detectedCom) { $detectedCom = Get-ComForInstance -instanceId $inst } if ($detectedCom) { New-ItemProperty -Path $dp -Name 'PortName' -PropertyType String -Value $detectedCom -Force -ErrorAction Stop | Out-Null "WROTE PortName = $detectedCom at $dp" | Tee-Object -FilePath $logFile -Append $createdCount++ } else { "Could not detect COM for $inst." | Tee-Object -FilePath $logFile -Append } foreach ($kv in @{ DeviceIdleEnabled=0; DefaultIdleState=0; UserSetDeviceIdleEnabled=0; SSIdleTimeout=0 }.GetEnumerator { try { New-ItemProperty -Path $dp -Name $kv.Key -PropertyType DWord -Value $kv.Value -Force -ErrorAction Stop | Out-Null; "Created $($kv.Key) = $($kv.Value) at $dp" | Tee-Object -FilePath $logFile -Append; $createdCount++ } catch { "ERROR creating $($kv.Key) at $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append; $errors += "Create prop error $($kv.Key) for $inst : $($_.Exception.Message)" } } } catch { "ERROR creating Device Parameters key $dp : $($_.Exception.Message)" | Tee-Object -FilePath $logFile -Append; $errors += "Create key error for $inst : $($_.Exception.Message)" } } } # end fallback create } # 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 } # Rescan devices
try { Write-Output "Running device rescan: pnputil /scan-devices" | Tee-Object -FilePath $logFile -Append Start-Process -FilePath 'pnputil.exe' -ArgumentList '/scan-devices' -NoNewWindow -Wait
} catch { "pnputil failed: $($_.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"
How to run safely
  1. Save the file and run a dry run first:
    PowerShell -ExecutionPolicy Bypass -File "$env:USERPROFILE\Documents\Fix-FTDI-FullFlow.ps1" -WhatIf
  2. Inspect the Desktop log (FTDIFullFlow.log) to confirm planned changes:
    notepad (Get-ChildItem "$env:USERPROFILE\Desktop\FTDIFullFlow
    .log" | Sort LastWriteTime -Descending | Select-Object -First 1).FullName
  3. If satisfied, run without -WhatIf to apply. Reboot is scheduled automatically if changes occurred; abortable with:
    shutdown /a
If anything still fails (parser errors or Join-Path issues), paste the exact error text and the top ~40 lines of the log file (the script prints the log path at the end) and I’ll patch immediately. If you prefer I can produce a signed INF AddReg snippet (persistent and robust) so the driver writes these Device Parameters at install instead of editing Enum keys — say “INF AddReg” and I’ll prepare that for your oem167.inf.