Loading Byte Array Assembly

sircodesalot picture sircodesalot · Jul 24, 2013 · Viewed 13.2k times · Source

I'm experimenting with loading an assembly using just byte arrays, but I can't figure out how to get it to work properly. Here is the setup:

public static void Main() 
{
    PermissionSet permissions = new PermissionSet(PermissionState.None);
    AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory };
    AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions);

    Byte[] primary = File.ReadAllBytes("Primary.dll_");
    Byte[] dependency = File.ReadAllBytes("Dependency.dll_");

    // Crashes here saying it can't find the file.
    friendlyDomain.Load(dependency);

    AppDomain.Unload(friendlyDomain);

    Console.WriteLine("Stand successful");
    Console.ReadLine();
}

I created two mock dlls, and renamed their extension to '.dll_' intentionally so the system wouldn't be able to find the physical files. Both primary and dependency fill correctly, but when I try to call the AppDomain.Load method with the binary data, it comes back with:

Could not load file or assembly 'Dependency, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

Why would it be searching the system for a file?

UPDATE

This on the other hand seems to work:

public class Program {
    public static void Main() {
        PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted);
        AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory };
        AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions);

        Byte[] primary = File.ReadAllBytes("Primary.dll_");
        Byte[] dependency = File.ReadAllBytes("Dependency.dll_");

        // Crashes here saying it can't find the file.
        // friendlyDomain.Load(primary);

        Stage stage = (Stage)friendlyDomain.CreateInstanceAndUnwrap(typeof(Stage).Assembly.FullName, typeof(Stage).FullName);
        stage.LoadAssembly(dependency);

        Console.WriteLine("Stand successful");
        Console.ReadLine();
    }

}

public class Stage : MarshalByRefObject {
    public void LoadAssembly(Byte[] data) {
        Assembly.Load(data);
    }
}

So it appears there is a difference between AppDomain.Load and Assembly.Load.

Answer

Hans Passant picture Hans Passant · Jul 24, 2013

This is normal, the CLR doesn't consider the "dependency" you loaded to be a suitable assembly when it searches for the assembly that "primary" needs. A problem associated with "loading context", there isn't one for assemblies loaded like this. This is intentional, the CLR cannot ensure that DLL Hell won't be an issue since it has no idea where the assembly came from. Since you opened the door to DLL Hell, you also have to avoid hell yourself.

You'll need to implement the AppDomain.AssemblyResolve event. It will fire when the CLR cannot find "dependency", you can return the assembly you get from Assembly.Load(byte[]). You will however have to do so consistently when it fires more than once for the same assembly, in other words return the exact same Assembly, or you'll have more problems induced by .NET type identity. Producing hard to understand casting exceptions, "can't cast Foo to Foo" style.

There are other problems, it is rather inefficient. The virtual memory for the assembly cannot be backed by a file on disk so it is backed by the paging file. Which increases the commit size for your process.

It is certainly better to not do this.