Recently I faced with interesting problem with assembly binding redirect in PowerShell: in my script I needed to use the following versions of the assemblies (these exact versions were used in other components and I couldn’t add another versions because of number of reasons):
OfficeDevPnP.Core 2.22.1801.0
Microsoft.Graph 1.7.0.0
Microsoft.Graph.Core 1.7.0.0
The problem is that OfficeDevPnP.Core 2.22.1801.0 references different versions of Microsoft.Graph and Microsoft.Grap.Core:
Microsoft.Graph 1.1.1.0Microsoft.Graph.Core 1.2.1.0
So I needed to use assembly binding redirect. At first I tried approach described in the following post: Use specific version of Sharepoint client object model in PowerShell via assembly binding redirection:
$currentDir = Convert-Path(Get-Location)
$pnpCoreDir = resolve-path($currentDir + "\..\packages\SharePointPnPCoreOnline.2.22.1801.0\lib\net45\")
$graphDir = resolve-path($currentDir + "\..\packages\Microsoft.Graph.1.7.0\lib\net45\")
$graphCoreDir = resolve-path($currentDir + "\..\packages\Microsoft.Graph.Core.1.7.0\lib\net45\")
$AssemblyGraphCore = [System.Reflection.Assembly]::LoadFile([System.IO.Path]::Combine($graphCoreDir, "Microsoft.Graph.Core.dll"))
$AssemblyGraph = [System.Reflection.Assembly]::LoadFile([System.IO.Path]::Combine($graphDir, "Microsoft.Graph.dll"))
[System.Reflection.Assembly]::LoadFile([System.IO.Path]::Combine($pnpCoreDir, "OfficeDevPnP.Core.dll"))
<#$OnAssemblyResolve = [System.ResolveEventHandler] {
param($sender, $e)
if ($e.Name.StartsWith("Microsoft.Graph,"))
{
$AssemblyGraph = [System.Reflection.Assembly]::LoadFile([System.IO.Path]::Combine($graphDir, "Microsoft.Graph.dll"))
return $AssemblyGraph
}
if ($e.Name.StartsWith("Microsoft.Graph.Core,"))
{
$AssemblyGraphCore = [System.Reflection.Assembly]::LoadFile([System.IO.Path]::Combine($graphCoreDir, "Microsoft.Graph.Core.dll"))
return $AssemblyGraphCore
}
foreach($a in [System.AppDomain]::CurrentDomain.GetAssemblies())
{
if ($a.FullName -eq $e.Name)
{
return $a
}
}
Write-Host "Return null" -foregroundcolor red
return $null
}
[System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve)
However attempt to call method from loaded assemblies caused StackOverflowException and closing PowerShell session.
After that I tried C#-based assembly redirector (found it in the following forum thread: Powershell - Assembly binding redirect NOT found in application configuration file):
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
}
try
{
$redirector = [Redirector]::new($null)
[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)?"
}
And surprisingly this approach worked. It looks like a bug in PowerShell script-based assembly binding redirect. Hope it will help someone.
No comments:
Post a Comment