Fody / Costura

Embed references as resources
MIT License
2.4k stars 274 forks source link

Costura fails to load library when extending class #104

Closed lukasholcik closed 9 years ago

lukasholcik commented 9 years ago

Hello folks, I have a case that makes Costura fail to find dependency when extending a class from an embedded library. Maybe I'm doing something wrong, but I already contacted the author of LibZ with the same issue, because LibZ can't make it either.

I prepared a demo solution to demostrate this (.NET 3.5). It contains 3 projects

  1. Console app that runs Assembly.LoadFile on MainLibrary.dll
  2. MainLibrary - a project with one class that extends class from ReferencedLibrary.dll, which is embedded using Costura
  3. ReferencedLibrary

MainClass (MainLibrary.dll) goes like this:

    public class MainClass : ReferencedClass
    {
        public void SayHelloReference()
        {
            SayHello();
        }
    }

ReferencedClass (ReferencedLibrary.dll) goes like this

    public class ReferencedClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello!");
        }
    }

Console app goes like this:

var mainLibrary = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\MainLibrary.dll");
mainLibrary.GetTypes(); // KABOOOM!

The test project I created is here: https://dl.dropboxusercontent.com/u/36622208/Costura/Costura_DependencyFail.zip

Hope this helps to improve Costura. Thanks a lot. Lukas

distantcam commented 9 years ago

Yeah, this is because the costura code is not being loaded from the MainLibrary.

Costura isn't really supported for libraries. This is one of the reasons why. Sorry.

lukasholcik commented 9 years ago

That's strange, because costura code is loaded when the class is not extended ... then the packed ReferenceLibrary is loaded correctly from MainLibrary embedded resource... Pity:(

And concerning the "Costura isn't really supported for libraries" I recently found your answer on SO: http://stackoverflow.com/questions/10959893/net-merge-class-library-references

As one of the contributors to Costura I can now confirm that it works for libraries as well as applications.

In fact all the unit tests for Costura are on a library, so it definitely works.

answered Aug 19 '13 at 15:18
Cameron MacFarland

So I don't really know now what to take from this.

distantcam commented 9 years ago

Yeah, the fact is, it never fully worked with libraries, it only sorta works and I was tricked into thinking it did.

An assembly can contain one or more modules, although there's typically only ever one. And modules can contain a single module level class, which is guaranteed to execute before any other code in the module. This is what we use to hook up Costura.

The problem is, with libraries you can still "use" a library without any of it's code executing. The example you found is getting a type via reflection. Getting the type doesn't execute any of the code in the assembly, so it's only when the type is created does Costura kick in. In the case where it works, the referenced library isn't needed to get the type because the type doesn't inherit from a type that's not loaded. When you inherit from the reference type then it has to be loaded into the appdomain and that's when you get an error.

You can see this for yourself by opening the MainLibrary/obj/Release/ILTemplate.cs and putting a breakpoint in the Attach method.

lukasholcik commented 9 years ago

Ah, so if I get it right - if I'm able to execute any code from the loaded library .. e.g. create instance of some dummy class, then it will work, because then the ModuleInitializer will fire and register the assembly resolve handlers. ... and as I've just tried, it really works. Wow, thanks anyway, at least I managed to make it work somehow:)

GeertvanHorrik commented 8 years ago

I think combining Costura with LoadAssembliesOnStartup solves this issue.

impoetk commented 7 years ago

86 has the workaround, you just need to add this code in AppDomain.CurrentDomain.AssemblyLoad event

var assembly = Assembly.LoadFile(@"<path to your dll>");
var assemblyLoaderType = assembly.GetType("Costura.AssemblyLoader", false);
var attachMethod = assemblyLoaderType?.GetMethod("Attach", BindingFlags.Static | BindingFlags.Public);
attachMethod?.Invoke(null, new object[] { });