我已经阅读了Stack Overflow上这个问题的不同版本,以及3个不同Google搜索教程的首页上的每个蓝色链接,以及MSDN(这是一种浅薄的执行程序集)。我只能想到我努力让Tao作为一个好的测试用例,但请相信我,我尝试过一个简单的字符串返回,一个double,一个带参数的函数。无论我的问题是什么,都不是道。
基本上我想在GLPlugin命名空间中创建一个我的Draw类的testLibraryDomain.CreateInstance()
。
if( usePlugin )
{
AppDomain testLibraryDomain = AppDomain.CreateDomain( "TestGLDomain2" );
//What the heck goes here so that I can simply call
//the default constructor and maybe a function or two?
AppDomain.Unload( testLibraryDomain );
}
Gl.glBegin( Gl.GL_TRIANGLES );
我知道一个事实:
namespace GLPlugin
{
public class DrawingControl : MarshalByRefObject
{
public DrawingControl()
{
Gl.glColor3f( 1.0f , 0.0f , 0.0f );
//this is a test to make sure it passes
//to the GL Rendering context... success
}
}
}
确实改变了笔的颜色。当我给它一个static void Main( string args[] )
入口点并且我调用testLibraryDomain.ExecuteAssembly( thePluginFilePath )
时它是否正常工作是否直接的ExecuteAssembly会起作用关注我,因为我不确定GL调用是否会使它进入“顶级” “AppDomain的OpenGL上下文。它甚至可以让我覆盖组件并再次更换笔颜色。不幸的是,给它一个可执行的入口点意味着弹出控制台会打断我然后消失。它也适用于我只是在项目中给它一个引用并创建一个常规GLPlugin.DrawingTool tool = new GLPlugin.DrawingControl()
,甚至创建一个someAssembly = Assembly.LoadFrom( thePluginFilePath )
(当然,不幸的是,锁定程序集,阻止替换/重新编译)。 / p>
当使用我尝试的各种方法时,我总是得到“给定的程序集名称或其代码库无效”。我保证,这是有效的。我试图加载它的方式不是。
我知道我缺少的一件事是testLibraryDomain.CreateInstance( string assemblyName , string typeName);
据我所知,assemblyName参数不是程序集文件的文件路径。它是名称空间,还是只是程序集名称,即:GLPlugin
?如果是这样,我在哪里引用实际文件?没有someAppDomain.LoadFrom(someFilename),虽然如果有的话会很方便。另外,什么是Type,以及字符串typeName呢?我不想把"Object"
放在这里,因为除了对象的实例之外不创建类型吗?我也试过CreateInstanceAndUnwrap( ... , ... )
同样缺乏对AppDomain的基本理解。通常我可以混淆教程并让事情发挥作用,即使我经常不理解“为什么?”......在这里不是这样。通常,对我来说,查找六个不同的教程是有帮助的......在这里不再如此,但因为每个教程都采用了一种基本的(或似乎是这样的)方法。
请ELI5 ...我想在一个单独的AppDomain中从一个dll加载一个类的实例,可能运行一些函数,然后卸载它。最终创建一个列表,列出这些函数作为List,删除/更新必要......我也希望能够将参数传递给它们,但这将是第2步。根据StackOverflow,我必须了解{ {1}}我将推迟一天。 (我想你可以从我的例子中找出我想要做的事情。)
答案 0 :(得分:11)
好的,我们必须澄清一些事情。首先,如果您希望能够在不锁定文件iteslf的情况下将dll加载和卸载到不同的AppDomain,也许您可以使用这样的方法:
AppDomain apd = AppDomain.CreateDomain("newdomain");
using(var fs = new FileStream("myDll.dll", FileMode.Open))
{
var bytes = new byte[fs.Length];
fs.Read(bytes, 0, bytes .Length);
Assembly loadedAssembly = apd.Load(bytes);
}
这样,您就不会锁定该文件,您应该可以稍后卸载域,重新编译该文件并在以后使用较新版本加载它。但我不能100%确定这是否会破坏您的申请。
那是因为第二件事。如果您将使用CreateInstanceAndUnwrap
方法,根据MSDN,您必须在两个appdomains中加载程序集 - 正在调用的程序集和您正在调用的程序集。当您在AppDomains中加载两个不同的dll时,这可能会在某种情况下结束。
我现在不记得了,但我认为当你打电话给CreateInstanceAndUnwrap
时,两个应用领域中对象创建的行为会有所不同,但我不记得细节。
对于您的插件架构,您可能需要阅读此博文。 About how to handle Dynamic Plugins using the AppDomain Class to Load and Unload Code
修改强>
我忘记了这个AppDomains是如何工作的,我可能会引入一些混乱。我准备了“插件”架构如何工作的简短示例。它与我之前提到的博客中描述的内容非常相似,这里是我使用Shadow Copying的示例。如果由于某些原因您不想使用它,可以很容易地更改为使用AppDomain.Load(byte[] bytes)
我们有3个程序集,第一个是基本插件程序集,它将作为代理使用,并将加载到所有AppDomains(在我们的例子中 - 在主应用程序域和插件应用程序域中)。
namespace PluginBaseLib
{
//Base class for plugins. It has to be delivered from MarshalByRefObject,
//cause we will want to get it's proxy in our main domain.
public abstract class MyPluginBase : MarshalByRefObject
{
protected MyPluginBase ()
{ }
public abstract void DrawingControl();
}
//Helper class which instance will exist in destination AppDomain, and which
//TransparentProxy object will be used in home AppDomain
public class MyPluginFactory : MarshalByRefObject
{
//This method will be executed in destination AppDomain and proxy object
//will be returned to home AppDomain.
public MyPluginBase CreatePlugin(string assembly, string typeName)
{
Console.WriteLine("Current domain: {0}", AppDomain.CurrentDomain.FriendlyName);
return (MyPluginBase) Activator.CreateInstance(assembly, typeName).Unwrap();
}
}
//Small helper class which will show how to call method in another AppDomain.
//But it can be easly deleted.
public class MyPluginsHelper
{
public static void LoadMyPlugins()
{
Console.WriteLine("----------------------");
Console.WriteLine("Loading plugins in following app domain: {0}", AppDomain.CurrentDomain.FriendlyName);
AppDomain.CurrentDomain.Load("SamplePlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
Console.WriteLine("----------------------");
}
}
}
这里我们将有另一个带有我们的虚拟插件的程序集,名为SamplePlugin.dll,存储在“Plugins”文件夹下。它引用了PluginBaseLib.dll
namespace SamplePlugin
{
public class MySamplePlugin : MyPluginBase
{
public MySamplePlugin()
{ }
public override void DrawingControl()
{
var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("----------------------");
Console.WriteLine("This was called from app domian {0}", AppDomain.CurrentDomain.FriendlyName );
Console.WriteLine("I have following assamblies loaded:");
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Console.WriteLine("\t{0}", assembly.GetName().Name);
}
Console.WriteLine("----------------------");
Console.ForegroundColor = color;
}
}
}
最后一个程序集(简单的控制台应用程序),它只引用PluginBaseLib.dll和
namespace ConsoleApplication1
{
//'Default implementation' which doesn't use any plugins. In this sample
//it just lists the assemblies loaded in AppDomain and AppDomain name itself.
public static void DrawControlsDefault()
{
Console.WriteLine("----------------------");
Console.WriteLine("No custom plugin, default app domain {0}", AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine("I have following assamblies loaded:");
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Console.WriteLine("\t{0}", assembly.GetName().Name);
}
Console.WriteLine("----------------------");
}
class Program
{
static void Main(string[] args)
{
//Showing that we don't have any additional plugins loaded in app domain.
DrawControlsDefault();
var appDir = AppDomain.CurrentDomain.BaseDirectory;
//We have to create AppDomain setup for shadow copying
var appDomainSetup = new AppDomainSetup
{
ApplicationName = "", //with MSDN: If the ApplicationName property is not set, the CachePath property is ignored and the download cache is used. No exception is thrown.
ShadowCopyFiles = "true",//Enabling ShadowCopy - yes, it's string value
ApplicationBase = Path.Combine(appDir,"Plugins"),//Base path for new app domain - our plugins folder
CachePath = "VSSCache"//Path, where we want to have our copied dlls store.
};
var apd = AppDomain.CreateDomain("My new app domain", null, appDomainSetup);
//Loading dlls in new appdomain - when using shadow copying it can be skipped,
//in CreatePlugin method all required assemblies will be loaded internaly,
//Im using this just to show how method can be called in another app domain.
//but it has it limits - method cannot return any values and take any parameters.
//apd.DoCallBack(new CrossAppDomainDelegate(MyPluginsHelper.LoadMyPlugins));
//We are creating our plugin proxy/factory which will exist in another app domain
//and will create for us objects and return their remote 'copies'.
var proxy = (MyPluginFactory) apd.CreateInstance("PluginBaseLib", "PluginBaseLib.MyPluginFactory").Unwrap();
//if we would use here method (MyPluginBase) apd.CreateInstance("SamplePlugin", "SamplePlugin.MySamplePlugin").Unwrap();
//we would have to load "SamplePlugin.dll" into our app domain. We may not want that, to not waste memory for example
//with loading endless number of types.
var instance = proxy.CreatePlugin("SamplePlugin", "SamplePlugin.MySamplePlugin");
instance.DrawingControl();
Console.WriteLine("Now we can recompile our SamplePlugin dll, replace it in Plugin directory and load in another AppDomain. Click Enter when you ready");
Console.ReadKey();
var apd2 = AppDomain.CreateDomain("My second domain", null, appDomainSetup);
var proxy2 = (MyPluginFactory)apd2.CreateInstance("PluginBaseLib", "PluginBaseLib.MyPluginFactory").Unwrap();
var instance2 = proxy2.CreatePlugin("SamplePlugin", "SamplePlugin.MySamplePlugin");
instance2.DrawingControl();
//Now we want to prove, that this additional assembly was not loaded to prmiary app domain.
DrawControlsDefault();
//And that we still have the old assembly loaded in previous AppDomain.
instance.DrawingControl();
//App domain is unloaded so, we will get exception if we try to call any of this object method.
AppDomain.Unload(apd);
try
{
instance.DrawingControl();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadKey();
}
}
}
阴影复制似乎非常方便。