Copilot and I created this script. This script has 3 parameters, none is required.

- -TargetDir is the path of a folder you want to examine. Or it will use the working dir.
- -BindingRedirectsFile is a path to a file that you want to write redirects for all the assembly versions it finds in -TargetDir. It will replace all the contents so do not point it at a working .config file. If this is not present it will just print to the output.
- -ExcludeNoPublicKeyToken just a flag to ignore assemblies with no public key token. Because those are probably your related project files.
Save this script to a file named assemblyversions.ps1, open a PowerShell command prompt to the dir so you can call
.\assemblyversions -TargetDir "C:\temp\bin" -BindingRedirectsFile "C:\temp\bindingsredirectfile.txt" -ExcludeNoPublicKeyToken
It is kinda slow because it calls PowerShell in a loop so that the locks on the file are released. It was the only way. The read-only version of loadfile didn't fully release.
Here is the script to save:
param(
[string]$TargetDir = $PWD,
[string]$BindingRedirectsFile = $null,
[switch]$ExcludeNoPublicKeyToken
)
function Get-BindingRedirect {
param($AssemblyName, $Version, $PublicKeyToken)
@"
<dependentAssembly>
<assemblyIdentity name="$AssemblyName" publicKeyToken="$PublicKeyToken" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-$Version" newVersion="$Version" />
</dependentAssembly>
"@
}
# Write the child script to a temp file
$childScriptPath = [System.IO.Path]::GetTempFileName() + ".ps1"
@'
param([string]$dll, [string]$dllName)
try {
$a = [System.Reflection.Assembly]::LoadFile($dll)
$n = $a.GetName()
$v = $n.Version
$pktBytes = $n.GetPublicKeyToken()
if ($pktBytes -eq $null -or $pktBytes.Length -eq 0) { $pkt = 'null' } else { $pkt = ($pktBytes | ForEach-Object { $_.ToString('x2') }) -join '' }
Write-Output ($n.Name + "|" + $v + "|" + $pkt)
} catch {
Write-Output ("ERROR|" + $dllName + "|Could not load assembly")
}
'@ | Set-Content -Encoding UTF8 $childScriptPath
$results = @()
$redirects = @()
Get-ChildItem -Path $TargetDir -Filter *.dll | ForEach-Object {
$dllPath = $_.FullName
$dllName = $_.Name
$output = powershell -NoProfile -File $childScriptPath -dll $dllPath -dllName $dllName
$output = $output -replace "`r?`n", ""
if ($output -like "ERROR|*") {
$parts = $output -split "\|"
$results += "${dllName}: Could not load assembly"
return
}
$parts = $output -split "\|"
if ($parts.Count -lt 3) {
$results += "${dllName}: Could not parse assembly info"
return
}
$asmName = $parts[0]
$version = $parts[1]
$pkt = $parts[2]
$results += "${dllName}: $version (PublicKeyToken=$pkt)"
if ($BindingRedirectsFile) {
if ($ExcludeNoPublicKeyToken) {
if ($pkt -ne "null") {
$redirects += Get-BindingRedirect $asmName $version $pkt
}
} else {
$redirects += Get-BindingRedirect $asmName $version $pkt
}
}
}
Remove-Item $childScriptPath -ErrorAction SilentlyContinue
$results | Write-Output
if ($BindingRedirectsFile) {
$header = @"
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
"@
$footer = @"
</assemblyBinding>
</runtime>
</configuration>
"@
$content = $header + "`r`n" + ($redirects -join "`r`n") + "`r`n" + $footer
Set-Content -Path $BindingRedirectsFile -Value $content
Write-Output "Binding redirects written to $BindingRedirectsFile"
}