Powershell - Assembly binding redirect NOT found in application configuration file

Not 100% related to 32/64-bit issue, however if someone is interested in working assembly redirect solution please have a look here Powershell config assembly redirect.

You can do custom assembly redirect using PowerShell code like

$FSharpCore = [reflection.assembly]::LoadFrom($PSScriptRoot + "\bin\LIBRARY\FSharp.Core.dll") 

$OnAssemblyResolve = [System.ResolveEventHandler] {
  param($sender, $e)

  # from:FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
  # to:  FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
  if ($e.Name -eq "FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") { return $FSharpCore }

  foreach($a in [System.AppDomain]::CurrentDomain.GetAssemblies())
  {
    if ($a.FullName -eq $e.Name)
    {
      return $a
    }
  }
  return $null
}

[System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve)

I am first loading the correct version of FSharp.Core from somewhere as the version in the GAC is old (I guess this might be your case too)


Based on @davidpodhola's extremely helpful answer, I started putting something like this in my psm1 module files. If your newer assemblies are already loaded (by Import-Module for instance), this should work:

if (!("Redirector" -as [type]))
{
$source = 
@'
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

public class Redirector
{
    public readonly string[] ExcludeList;

    public Redirector(string[] ExcludeList = null)
    {
        this.ExcludeList  = ExcludeList;
        this.EventHandler = new ResolveEventHandler(AssemblyResolve);
    }

    public readonly ResolveEventHandler EventHandler;

    protected Assembly AssemblyResolve(object sender, ResolveEventArgs resolveEventArgs)
    {
        Console.WriteLine("Attempting to resolve: " + resolveEventArgs.Name); // remove this after its verified to work
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            var pattern  = "PublicKeyToken=(.*)$";
            var info     = assembly.GetName();
            var included = ExcludeList == null || !ExcludeList.Contains(resolveEventArgs.Name.Split(',')[0], StringComparer.InvariantCultureIgnoreCase);

            if (included && resolveEventArgs.Name.StartsWith(info.Name, StringComparison.InvariantCultureIgnoreCase))
            {
                if (Regex.IsMatch(info.FullName, pattern))
                {
                    var Matches        = Regex.Matches(info.FullName, pattern);
                    var publicKeyToken = Matches[0].Groups[1];

                    if (resolveEventArgs.Name.EndsWith("PublicKeyToken=" + publicKeyToken, StringComparison.InvariantCultureIgnoreCase))
                    {
                        Console.WriteLine("Redirecting lib to: " + info.FullName); // remove this after its verified to work
                        return assembly;
                    }
                }
            }
        }

        return null;
    }
}
'@

    $type = Add-Type -TypeDefinition $source -PassThru 
}

#exclude all powershell related stuff, not sure this strictly necessary
$redirectExcludes = 
    @(
        "System.Management.Automation", 
        "Microsoft.PowerShell.Commands.Utility",
        "Microsoft.PowerShell.Commands.Management",
        "Microsoft.PowerShell.Security",
        "Microsoft.WSMan.Management",    
        "Microsoft.PowerShell.ConsoleHost",
        "Microsoft.Management.Infrastructure",
        "Microsoft.Powershell.PSReadline",
        "Microsoft.PowerShell.GraphicalHost"
        "System.Management.Automation.HostUtilities",

        "System.Management.Automation.resources",
        "Microsoft.PowerShell.Commands.Management.resources",
        "Microsoft.PowerShell.Commands.Utility.resources",
        "Microsoft.PowerShell.Security.resources",
        "Microsoft.WSMan.Management.resources",
        "Microsoft.PowerShell.ConsoleHost.resources",
        "Microsoft.Management.Infrastructure.resources",
        "Microsoft.Powershell.PSReadline.resources",
        "Microsoft.PowerShell.GraphicalHost.resources",
        "System.Management.Automation.HostUtilities.resources"
    )
try
{
    $redirector = [Redirector]::new($redirectExcludes)
    [System.AppDomain]::CurrentDomain.add_AssemblyResolve($redirector.EventHandler)
}
catch
{
    #.net core uses a different redirect method
    write-warning "Unable to register assembly redirect(s). Are you on ARM (.Net Core)?"
}

Update: Powershell appears to have a bug where simply registering an assembly resolve scriptblock can cause a StackOverflowException when calling some commands like Out-GridView. I updated the code to use a version compiled with Add-Type that seems to resolve the issue.


On a 64-bit machine there are two configuration files:

C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe.Config
C:\Windows\syswow64\Windowspowershell\v1.0\powershell.exe.Config

Have you edited both of them on the 64-bit machine?

On 64-bit versions of Windows. 32 bit processes (like notepad++) are transparently redirected from C:\WINDOWS\System32 to C:\WINDOWS\SysWOW64 by the OS.

You will need to make sure you edit both files using a 64-bit text editor like the builtin notepad.exe. This will guarantee you do not suffer from this subtle issue, which can cause confusion.