-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Do not resolve types from assemblies that are loaded in separate AssemblyLoadContext #11088
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…emblyLoadContext` (#11088)
|
🎉 Handy links: |
|
This PR prevents PowerShell from finding types in assemblies loaded from memory using This has worked in PowerShell 5.1, and PowerShell 7 up to rc2 but doesn't work in rc3. A workaround for PowerShell 7 rc3 is to use However the above code is not compatible with PowerShell 5.1 as the type doesn't exist. |
|
Are you able to expand on your scenario for using that API? I haven't seen any use cases in .NET Framework for loading an assembly from bytes other than to get around assembly resolution issues, which .NET Core has effectively resolved with the addition of assembly load contexts. Really this change is us catching up to deeper changes in .NET assembly handling, and given that these changes have occurred in the platform and something fundamentally different is happening (i.e. the loaded assembly really isn't in the current load context), it's probably appropriate to handle the loading differently on different platforms: if ($PSVersionTable.PSVersion.Major -ge 7)
{
$ms = ...
[System.Runtime.Loader.AssemblyLoadContext]::Default.LoadFromStream($ms)
}
else
{
[System.Reflection.Assembly]::Load($bytes)
} |
|
The core use case is when you don't want the DLL to be locked on the file system in Windows, preventing rebuilds/reinstalls etc. I typically load my more complex DLLs using add-type, but I have a native interop DLL that checks file locking status on startup and handles upgrading of assemblies and native DLLs by inspecting the install folder for locked files. I don't want loading this DLL to also cause file locking so I load it from memory - it has no external dependencies so this works fine. I used something like the above code, but I tested for the presence of the AssemblyLoadContext type, rather than tying it to the PSVersion. |
Yes, this is something an ALC can solve today, since they can be collectible/unloadable. Meaning the strategy for handling the problem will probably change significantly moving into the future. We've been discussing how best to support this going forward and there's no simple solution available by default in PowerShell yet, but it's certainly an area of interest. |
|
I don't think being able to unload from a specific PowerShell will solve my issue, which is users may have multiple PowerShell windows open, each of which would have locked the DLLs when they upgrade. I currently resolve it by checking for locked files and creating a new sequence number folder for the new version of DLLs on upgrade, which is what the in-memory DLL helps with. Most of my users are on PS 5.1 as well, but I'm on both. |
|
@stevenebutler Please open new issue with repo steps.
It is not PowerShell, it is .Net Core method. You could create simple C# application and check with different .Net Core 3 versions. |
The preliminary issue is that PowerShell stopped resolving types outside the default ALC with the changes in this PR. However, Assembly.Load effectively made a breaking change first, since it loads byte assemblies into an anonymous ALC. Given that ALCs are designed partially to replace AppDomains, I think not resolving from them is the right way to go (and I should note that fully qualified names still work). |
|
@rjmholt Thanks! In the case I see another additional problem https://source.dot.net/#System.Private.CoreLib/Assembly.cs,242 |
|
I'm not sure I want to create an issue for this as I understand this area is changing and PowerShell has to adapt. It's obviously a change in behaviour that may break existing code, so I wanted to let people know how they could adapt. I am happy with the change I have made and don't have any issues with it now. In case anyone does feel strongly about this and wants to raise an issue with a repro, the below pester test will repro the issue (fail) on PowerShell 7.0.0 rc3 (and release), but will pass on versions prior. For completeness, I have included a second test that shows how to load types from memory in PowerShell, which passes on PowerShell 7.0.0rc3 and above.. import-module Pester
Describe "PowerShell C# type visibility" {
It "[Assembly]::Load(`$bytes) types are visible to PowerShell" {
$testdll = "$env:TEMP/visibility.dll"
if (test-path $testdll) {
remove-item $testdll -force
}
Add-Type @"
public class Visibility {
public static string Test = "Found";
}
"@ -Language CSharp -OutputAssembly $testdll
$bytes = [System.IO.File]::ReadAllBytes($testdll)
[System.Reflection.Assembly]::Load($bytes)
[Visibility]::Test | should be "Found"
}
It "System.Runtime.Loader.AssemblyLoadContext]::Default.LoadFromStream(`$memoryStream) types are visible to PowerShell" {
$testdll = "$env:TEMP/visibility2.dll"
if (test-path $testdll) {
remove-item $testdll -force
}
Add-Type @"
public class Visibility2 {
public static string Test = "Found";
}
"@ -Language CSharp -OutputAssembly $testdll
$bytes = [System.IO.File]::ReadAllBytes($testdll)
$ms = new-object System.IO.MemoryStream
try {
$ms.Write($bytes, 0, $bytes.Length)
$ms.Seek(0, [System.IO.SeekOrigin]::Begin)
# This will fail on Windows PowerShell 5.1
[System.Runtime.Loader.AssemblyLoadContext]::Default.LoadFromStream($ms)
}
finally {
$ms.Dispose()
}
[Visibility2]::Test | should be "Found"
}
}Output on 7.0.0 rc3: |
|
@stevenebutler In cast you don't know yet, #12052 is tracking the issue you reported, and the PR #12203 was submitted to fix it. |
PR Summary
In .NET Core 2.1,
[System.AppDomain]::CurrentDomain.GetAssemblies()only returns the assemblies loaded in the default load context.In .NET Core 3.0,
[System.AppDomain]::CurrentDomain.GetAssemblies()is changed to return all assemblies loaded in theAppDomain, including the assemblies loaded into other AssemblyLoadContext (ALC) instances.This results in our type resolution to behave inconsistently:
This PR changes to use
AssemblyLoadContext.Default.Assemblies(new API added in .NET Core 3) to make sure assemblies loaded in other ALC are invisible to PowerShell type resolution.PR Context
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.