3

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

//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();
    }
}

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

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

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

4

3 回答 3

6

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

这是来自单声道的异常调用堆栈:

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

于 2011-01-21T17:21:53.897 回答
3

因此,您尝试从 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();
      }
}  
于 2011-01-21T18:07:11.263 回答
0

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

于 2011-01-21T18:00:11.347 回答