Open Shyam-Gupta opened 3 years ago
Tagging subscribers to this area: @vitek-karas, @agocke, @coffeeflux, @vsadov See info in area-owners.md if you want to be subscribed.
Author: | Shyam-Gupta |
---|---|
Assignees: | - |
Labels: | `area-AssemblyLoader-coreclr`, `untriaged` |
Milestone: | - |
FYI @merriemcgaw
@vitek-karas, could you look at this?
Chiming in here to say that this winds up blocking a key scenario in the WinForms designer, something our customers certainly expect to work. We would greatly appreciate the prioritization here 😄
Good to know, let's see if we can get to the bottom of this ASAP. Do you know if you'll need fixes for .NET 5 or earlier, or just .NET 6?
I'm looking, but I doubt it's a recent bug.
That said - why do you need to load the ref assembly by the runtime? If it's only for "reflection-only-load" it would be much cleaner to use MetadataLoadContext
which has no interactions with the runtime and will avoid the issue for sure.
The WinForms designer needs to load project output assembly to resolve custom user types which show up as controls in the Toolbox. To do this, the designer's child process (.NET Core process) loads all assemblies present in project output folder. In case there is an easy way to differentiate between ref assemblies and implementation assemblies then we can skip loading ref assemblies. Let us know.
I believe there's an attribute, System.Runtime.CompilerServices.ReferenceAssemblyAttribute
, that should be on every reference assembly.
Is there any way to read assembly attributes without loading the assembly ? Else we will need to load each assembly into MetadataLoadContext
first which will likely impact perf.
MetadataLoadContext should be pretty fast – it’s lazy – meaning that it will only parse the bare headers and almost nothing else. You can go even lower level and use System.Reflection.Metadata and use the low level readers. It should be reasonably easy to read just assembly level attributes with that approach as well. @MichalStrehovsky for details on the low-level APIs if you're interested in that approach.
Note about MetadataLoadContext
- it's purely managed code library, it has no interaction with loading in the runtime. So among other things, if you get rid of all managed references to the returned objects, the loaded stuff will simply disappear (via GC, just like any other managed objects).
I checked what happens in the runtime in this case:
ReferencedAssemblyAttribute
- if it's there it will fail with the exception you're seeingThe failure happens basically during the "execution" phase - so it's similar to things like corrupted IL and so on. So the runtime treats the assembly as already loaded.
That said the runtime does basically the same thing you would do with MetadataLoadContext
and it you would not be able to unload these, unlike the MetadataLoadContext
where for assemblies which you don't want, they will simply go away.
Checking using System.Reflection.Metadata is pretty easy and should be cheap -- I wrote up a quick version here https://gist.github.com/agocke/f6a80c71cc62074379e2c9c4025505b7
Description
FileLoadException
is thrown during assembly load when in a previous attempt assembly load for a reference assembly has failed withBadImageFormatException
. Both implementation assembly and reference assemblies have same name.Repro Steps: Consider following code snippet:
Both the implementation assembly and reference assembly have same name. We first try to load reference assembly which fails with
BadImageFormatException
and is expected. However later when we try to load correct implementation assembly it fails withSystem.IO.FileLoadException
message: "Assembly with same name is already loaded". Since the ref assembly load failed in previous attempt, it seems weird that implementation assembly load fails with this error.Configuration
Scenario affected
This bug is causing issues with WinForms designer, which tries to load project output dlls to resolve custom types. In .NET 5.0 and .NET 6.0, project output folder contains ref binaries. When the designer's child process tries to load ref binaries, it fails with
BadImageFormatException
. It ignores those exceptions and then tries to load implementation assemblies which also fails.