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