Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AssemblyResolve not fired for dependencies

Tags:

I'm struggling with AssenblyResolve event for a while now. I've searched stackoverflow and did other googling and tried all that I think was relevant. Here are the links being the closer to my problem (in my opinion) :

  1. AssemblyResolve is not invoked and FileNotFoundException is thrown during serialization

  2. Where to handle AssemblyResolve event in a class library?

I have a Bootstrapper class with to static method (I will remove the thread safe code that we have, just for the sake of clarity :

public static void Initialize()
{
    AppDomain.CurrentDomain.AssemblyResolve += CustomResolve;
}

private static Assembly CustomResolve(object sender, ResolveEventArgs args)
{
    // There is a lot code here but basicall what it does.
    // Is determining which architecture the computer is running on and
    // extract the correct embedded dll (x86 or x64). The code was based
    // on milang on GitHub (https://github.com/milang/P4.net). And it's the same
    // purpose we want to be able to load the x86 or x64 version of the perforce dll
    // but this time with the officially Perforce supported p4api.net.
    // Once the dll is extracted we assign it to the boostrapper
    Bootstrapper._p4dnAssembly = Assembly.LoadFile(targetFileName);

    // Make sure we can satisfy the requested reference with the embedded assembly (now extracted).
    AssemblyName reference = new AssemblyName(args.Name);
    if (AssemblyName.ReferenceMatchesDefinition(reference, Bootstrapper._p4dnAssembly.GetName()))
    {
        return Bootstrapper._p4dnAssembly;
    }
}

I was able to make the code work if the I have simple class with a main method and static constructor. The static constructor is simply calling the Boostrapper.Initialize() method. After that, I could use my library and it was working as expected :

public static class Test
{
    static Test()
    {
        Bootstrapper.Initialize();
    }

    public static void Main()
    {
         // Using the library here is working fine. The AssemblyResolve event was
         // fired (confirmed by a breakpoint in Visual Studio)
    }
}

The problem I have is if there is at least one layer of dependency. Basically the code stays the same but this time my the code of the library is inside another library :

public static class Test
{
    static Test()
    {
        Bootstrapper.Initialize();
    }

    public static void Main()
    {
        Class1 myClass = new Class1();

        // The following line is using the code of the extracted library, but
        // The AssemblyResolve event is not fired (or fired before I register the  
        // callback) and therefore the library is not found : result  
        // BadImageFormatException() error could not load libary because one
        myClass.Connect();
    }
}

Sounds like #2 of the links I've previously stated explain what I'm seeing but it does not work. Visual Studio break point on the AssemblyResove callback is never being hit.

Any idea of what's going on?

Francis

like image 979
Francis P Avatar asked Oct 26 '12 13:10

Francis P


People also ask

What is AssemblyResolve?

AssemblyResolve event can be used to load referenced assemblies at runtime. This can be useful in multiple scenarios: you can use it to load a library from a different location or load a library based on bitness or load a library which is embedded in the executable file.

Which event allows you to intervene and manually load an assembly that the CLR Cannot find?

AssemblyResolve Event (System)


1 Answers

I know it's been a while since this question was asked and answered, but I wanted to add my take on the issue anyway (since I just wasted a few hours over this, maybe thanks to this someone else wouldn't have to)

The problem is basically the fact, that the application is trying to resolve all assemblies needed to execute the method at the beginning of that method:

static void main(string[] args)
{
    // <-- here the app tries to resolve MyAssembly
    // and as MyAssembly.Class1 is not found, the app crashes

    // this next line is never called:
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);

    // class contained in an assemnly that we need to resolve
    MyAssembly.Class1 myClass = new MyAssembly.Class1();
}

That's why the above will crash: the ResolveAssembly event handler will never be called because it was never hooked up.

And that's also why the below solution works (as posted by the OP):

static void main(string[] args)
{
    Initialize();
    RealMain();
}

static void Initialize()
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);
}

static void RealMain()
{
   // <-- here the app tries to resolve MyAssembly
   // class contained in an assemnly that we need to resolve      
   MyAssembly.Class1 myClass = new MyAssembly.Class1();
   // and everything is OK
}
like image 182
Arie Avatar answered Sep 19 '22 15:09

Arie