Loading Byte Array Assembly

2019-02-09 22:55发布

问题:

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.

回答1:

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.



回答2:

There is no difference between these two methods (you can check the official source code if you want).

In the MSDN page for AppDomain.Load Method (Byte[]) it is remarked that this method is loading the assembly in the current application domain:

This method should be used only to load an assembly into the current application domain. This method is provided as a convenience for interoperability callers who cannot call the static Assembly.Load method. To load assemblies into other application domains, use a method such as CreateInstanceAndUnwrap.

the line:

friendlyDomain.Load(dependency);

behaves exactly the same with:

Assembly.Load(dependency);

The reason it works in your updated sample code, is because the Stage object is actually calling Assembly.Load inside the child AppDomain.

Note: This answer complements the answers by Hans Passant and colinsmith.



回答3:

If you use FusionLogViewer you can see more details about the particular problem the CLR is having in loading an assembly .... it can show you which locations it's trying to probe to give you a clue, etc.

  • http://msdn.microsoft.com/en-us/library/e74a18c4(v=vs.71).aspx
  • http://www.shujaat.net/2012/04/fusion-log-viewer-fuslogvw-for-assembly.html

You could also handle the AssemblyLoad / AssemblyResolve / ResourceResolve events on your AppDomain in your code, to trace through the sequence.

  • http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve.aspx

This is a handy example that uses a custom MSBuild step to embed any of your project dependent assemblies as Resources into your EXE program, and then use AssemblyResolve to load them from a ResourceStream (doing Assembly.Load() on the byte[] array).

  • http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application


标签: c# appdomain