| | | 1 | | <# |
| | | 2 | | .SYNOPSIS |
| | | 3 | | Ensures that a .NET assembly is loaded only once. |
| | | 4 | | |
| | | 5 | | .DESCRIPTION |
| | | 6 | | Checks the currently loaded assemblies for the specified path. If the |
| | | 7 | | assembly has not been loaded yet, it is added to the current AppDomain. |
| | | 8 | | .PARAMETER AssemblyPath |
| | | 9 | | Path to the assembly file to load. |
| | | 10 | | .PARAMETER LoadContext |
| | | 11 | | The AssemblyLoadContext to load the assembly into. Default is the Default context. |
| | | 12 | | This helps avoid issues with multiple contexts loading the same assembly. |
| | | 13 | | .OUTPUTS |
| | | 14 | | [bool] - True if the assembly is loaded successfully or already loaded, false otherwise. |
| | | 15 | | #> |
| | | 16 | | function Assert-KrAssemblyLoaded { |
| | | 17 | | [CmdletBinding()] |
| | | 18 | | [OutputType([bool])] |
| | | 19 | | param( |
| | | 20 | | [Parameter(Mandatory)] |
| | | 21 | | [string]$AssemblyPath, |
| | | 22 | | |
| | | 23 | | # Default = require it to be loadable from the Default ALC (fixes VSCode/PSES split-load issue) |
| | | 24 | | [System.Runtime.Loader.AssemblyLoadContext]$LoadContext = [System.Runtime.Loader.AssemblyLoadContext]::Default |
| | | 25 | | ) |
| | | 26 | | |
| | 2 | 27 | | if (-not (Test-Path -LiteralPath $AssemblyPath -PathType Leaf)) { |
| | 0 | 28 | | throw "Assembly not found at path: $AssemblyPath" |
| | | 29 | | } |
| | | 30 | | |
| | 2 | 31 | | $full = (Resolve-Path -LiteralPath $AssemblyPath).Path |
| | 1 | 32 | | $asmName = [System.Reflection.AssemblyName]::GetAssemblyName($full) |
| | | 33 | | |
| | | 34 | | # Find an already-loaded assembly with same simple name *in the desired ALC* |
| | 1 | 35 | | $loadedInContext = [AppDomain]::CurrentDomain.GetAssemblies() | |
| | 1 | 36 | | Where-Object { |
| | 1 | 37 | | $_.GetName().Name -eq $asmName.Name -and |
| | 1 | 38 | | ([System.Runtime.Loader.AssemblyLoadContext]::GetLoadContext($_) -eq $LoadContext) |
| | | 39 | | } | |
| | 1 | 40 | | Select-Object -First 1 |
| | | 41 | | |
| | 1 | 42 | | if ($loadedInContext) { |
| | 2 | 43 | | Write-Verbose ('Assembly already loaded in {0} ALC: {1} {2} from {3}' -f ` |
| | 1 | 44 | | ($LoadContext.Name ?? 'Default'), $loadedInContext.GetName().Name, $loadedInContext.GetName().Version, $load |
| | 1 | 45 | | return $true |
| | | 46 | | } |
| | | 47 | | |
| | 0 | 48 | | Write-Verbose ('Loading assembly into {0} ALC: {1}' -f ($LoadContext.Name ?? 'Default'), $full) |
| | | 49 | | |
| | | 50 | | try { |
| | | 51 | | # Load into the requested context (Default by default) |
| | 0 | 52 | | [void]$LoadContext.LoadFromAssemblyPath($full) |
| | 0 | 53 | | return $true |
| | | 54 | | } catch { |
| | 0 | 55 | | Write-Error "Failed to load assembly: $full" |
| | 0 | 56 | | Write-Error $_ |
| | 0 | 57 | | return $false |
| | | 58 | | } |
| | | 59 | | } |