我正在尝试将已复制到字节数组中的DLL文件加载到新的AppDomain中。
DLL确实包含对Windows.Forms和其他dll之类的引用。是那些无法加载?如果是这样,您如何为特定域预加载它们?
AppDomainSetup Setup = new AppDomainSetup();
Setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
Setup.ApplicationName = "Plugin_" + DLLName + "" + PluginManager.PluginList.Count;
AppDomain Domain = AppDomain.CreateDomain("Domain_" + DLLName + "" + PluginManager.PluginList.Count, null, Setup);
Assembly Assembly = Domain.Load(buffer);
然而改变
Assembly Assembly = Domain.Load(buffer);
到
Assembly = AppDomain.CurrentDomain.Load(buffer);
使其发挥作用。
我需要它在一个单独的域中,因为我打算卸载这个AppDomain以卸载DLL本身。
我尝试过使用“AssemblyResolve”事件,就像所有人都在暗示,但它没有做任何事情。
我需要这个来自字节数组的原因是因为我希望能够在运行时切换DLL文件并将其重新加载到内存中。
DLL文件与.exe文件位于不同的文件夹中。它位于同一目录中,只有一个文件夹。
有趣的发现:
如果我将DLL文件添加到.exe的文件位置,它将加载它们并锁定它们并成功加载到新域中。当我提供一个字节数组而不是文件位置时,为什么它会以这种方式工作?我实际上必须采取字节数组并写入临时文件?我可以这样做,并在我完成它们时删除它们,但这似乎是浪费时间,没有理由不能从内存中完成所有这些。
答案 0 :(得分:0)
<强> SOLUTION:强>
AppDomains的文档记录很差,无论我在哪里都很难解释。这就像人们试图隐藏它并让它成为公众群众的秘密。显然,AppDomains不会像变量和其他对象引用那样在彼此之间共享数据。您需要在它们之间使用SetData / GetData和DoCallBack。这被含糊地提到,但没有人给出真正的解决方案。
所以我做了这个简单的插件加载器,使用“LoadFrom”而不将其加载到字节数组中并且文件没有被锁定,它将其读入新的AppDomain到内存中并立即解锁文件但是这里没有提到并且是奇怪的行为,因为在主AppDomain中它像癌症一样锁定到文件上。
[Serializable] //This is important
public class TPlugin
{
public bool InitializeImmediately { get; set; }
public AppDomainSetup AppSetup { get; set; }
public Assembly Assembly { get; set; }
public AppDomain AppDomain { get; set; }
public string FilePath { get; set; }
public object ClassInstance { get; set; }
public Type ClassType { get; set; }
public TPlugin(string path, bool Initialize = false)
{
FilePath = path;
InitializeImmediately = Initialize;
AppSetup = new AppDomainSetup();
AppSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
AppDomain = AppDomain.CreateDomain(FilePath, null, AppSetup);
AppDomain.SetData("Plugin", this);
AppDomain.DoCallBack(new CrossAppDomainDelegate(() =>
{
//We are now inside the new AppDomain, every other variable is now invalid since this AppDomain cannot see into the main one
TPlugin plugin = AppDomain.CurrentDomain.GetData("Plugin") as TPlugin;
if (plugin != null)
{
plugin.Assembly = Assembly.LoadFrom(plugin.FilePath);
if(InitializeImmediately) //You cannot use the "Initialize" parameter here, it goes out of scope for this AppDomain
{
plugin.ClassType = plugin.Assembly.GetExportedTypes()[0];
if (plugin.ClassType != null && plugin.ClassType.IsClass)
{
plugin.ClassInstance = Activator.CreateInstance(plugin.ClassType);
MethodInfo info = plugin.ClassType.GetMethod("Initializer");
info.Invoke(plugin.ClassInstance, null);
}
}
}
}));
}
public object Execute(string FunctionName, params object[] args)
{
AppDomain.SetData("FunctionName", FunctionName);
AppDomain.SetData("FunctionArguments", args);
AppDomain.DoCallBack(CallBack);
return AppDomain.GetData("FunctionReturn");
}
public void CallBack()
{
TPlugin plugin = AppDomain.CurrentDomain.GetData("Plugin") as TPlugin;
if (plugin != null)
{
MethodInfo info = plugin.ClassType.GetMethod(AppDomain.CurrentDomain.GetData("FunctionName") as string);
info.Invoke(plugin.ClassInstance, AppDomain.CurrentDomain.GetData("FunctionArgs") as object[]);
}
//This is how to return back since DoCallBack does not support returns.
AppDomain.CurrentDomain.SetData("FunctionReturn", null);
}
}
这是我的DLL模块:
public class GUIModule
{
public bool Initializer()
{
Console.WriteLine("Initialized!");
return true;
}
public bool Deinitializer()
{
Console.WriteLine("Deinitialized");
return true;
}
}
现在一切正常,甚至加载依赖项。 GUIModule在编译时引用了Windows.Forms。