AppDomain.Load()的有趣错误

时间:2011-01-21 16:42:54

标签: c# .net load appdomain assembly-loading

我试图找到一种在运行时编译程序集并加载它们的方法。基本意图是将它们存储在不在光盘上的数据库中。所以我写了一些代码,但看到了一个有趣的情况。这是我的代码:

//SumLib
namespace SumLib
{
    public class SumClass
    {
        public static int Sum(int a, int b)
        {
            return a + b;
        }
    }
}


// Console app
class Program
{

    public static void AssemblyLoadEvent(object sender, AssemblyLoadEventArgs args)
    {

        object[] tt = { 3, 6 };
        Type typ = args.LoadedAssembly.GetType("SumLib.SumClass");
        MethodInfo minfo = typ.GetMethod("Sum");
        int x = (int)minfo.Invoke(null, tt);
        Console.WriteLine(x);
    }

    static void Main(string[] args)
    {

        AppDomain apd = AppDomain.CreateDomain("newdomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
        apd.AssemblyLoad += new AssemblyLoadEventHandler(AssemblyLoadEvent);

        FileStream fs = new FileStream("Sumlib.dll", FileMode.Open);
        byte[] asbyte = new byte[fs.Length];
        fs.Read(asbyte, 0, asbyte.Length);
        fs.Close();
        fs.Dispose();

//      File.Delete("Sumlib.dll");

        apd.Load(asbyte);

        Console.ReadLine();
    }
}

代码运行完美, delete 行已注释掉,如果我取消注释,应用程序域加载程序集,AssemblyLoadEvent()方法运行,我在控制台上看到一个数字9 ,但是当方法结束apd.Load()时会抛出错误:“无法加载文件或程序集。”这是完全合理的。

问题是:如果没有光盘上的汇编文件,AssemblyLoadEvent()方法如何运行?

如果该方法以某种方式在原始二进制数据的帮助下运行,那么appdomain是否有任何成功完成Load()方法的方法?

3 个答案:

答案 0 :(得分:6)

它将程序集正常加载到“newdomain”并调用仍然在newdomain中的事件处理程序 (如果在事件处理程序中打印当前域,则可以验证这一点)。最后,它创建了要传回的返回值。您忽略示例代码中的返回值,但仍然创建它。在跨域编组期间发生异常,因为反序列化也希望将程序集加载到默认域中。

以下是mono:

的异常调用堆栈
  at System.AppDomain.Load (System.String assemblyString, System.Security.Policy.Evidence assemblySecurity, Boolean refonly) [0x00000] in <filename unknown>:0
  at System.AppDomain.Load (System.String assemblyString) [0x00000] in <filename unknown>:0
  at (wrapper remoting-invoke-with-check) System.AppDomain:Load (string)
  at System.Reflection.Assembly.Load (System.String assemblyString) [0x00000] in <filename unknown>:0
  at System.UnitySerializationHolder.GetRealObject (StreamingContext context) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.ObjectRecord.LoadData (System.Runtime.Serialization.ObjectManager manager, ISurrogateSelector selector, StreamingContext context) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.ObjectManager.DoFixups () [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (System.IO.BinaryReader reader) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders, System.Object& result, System.Runtime.Remoting.Messaging.Header[]& headers) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) [0x00000] in <filename unknown>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) [0x00000] in <filename unknown>:0
  at System.Runtime.Remoting.RemotingServices.DeserializeCallData (System.Byte[] array) [0x00000] in <filename unknown>:0
  at (wrapper xdomain-invoke) System.AppDomain:Load (byte[])
  at (wrapper remoting-invoke-with-check) System.AppDomain:Load (byte[])
  at Program.Main (System.String[] args) [0x00000] in <filename unknown>:0

修改:此处是来自MSDN的确认:

  

尝试在不是当前应用程序域的目标应用程序域上调用Load将导致在目标应用程序域中成功加载程序集。由于程序集不是MarshalByRefObject,因此当此方法尝试将已加载程序集的程序集返回到当前应用程序域时,公共语言运行库将尝试将程序集加载到当前应用程序域中,并且加载可能会失败。如果两个应用程序域的路径设置不同,则加载到当前应用程序域的程序集可能与首先加载的程序集不同。

答案 1 :(得分:2)

所以你试图从byte []加载一个程序集并调用一个方法。我不建议您采用的方式(使用AssemblyLoad事件),因为它将针对每个依赖项进行调用。

@Jester是关于使用来自父域的Load()加载程序集的。为了纠正这个问题,我建议使用这样的包装类:

// Console app 
class Program 
{  
    public class AssemblyLoader : MarshalByRefObject
    {
        public void LoadAndCall(byte[] binary)
        {
            Assembly loadedAssembly = AppDomain.CurrentDomain.Load(binary);
            object[] tt = { 3, 6 };
            Type typ = loadedAssembly.GetType("SumLib.SumClass");
            MethodInfo minfo = typ.GetMethod("Sum", BindingFlags.Static | BindingFlags.Public);
            int x = (int)minfo.Invoke(null, tt);
            Console.WriteLine(x);
        }
    }

    static void Main()
    {
        AppDomain apd = AppDomain.CreateDomain("newdomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
        FileStream fs = new FileStream("Sumlib.dll", FileMode.Open);
        byte[] asbyte = new byte[fs.Length];
        fs.Read(asbyte, 0, asbyte.Length);
        fs.Close();
        fs.Dispose();
        File.Delete("Sumlib.dll");    

        AssemblyLoader loader = (AssemblyLoader)apd.CreateInstanceAndUnwrap(typeof(AssemblyLoader).Assembly.FullName, typeof(AssemblyLoader).FullName);
        loader.LoadAndCall(asbyte);
        Console.ReadLine();
      }
}  

答案 2 :(得分:0)

为什么不使用Shadow Copy参数?它可能会对你有帮助。