わかりました、いくつか明確にする必要があります。まず、ファイル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% わかりません。
そして、それは2番目の理由です。MSDN によると、メソッドを使用する場合CreateInstanceAndUnwrap
は、呼び出し元と呼び出し元の両方の appdomain にアセンブリを読み込む必要があります。これは、AppDomains に 2 つの異なる dll がロードされている場合に終了する可能性があります。
アンラップされたクラスを含むアセンブリは、両方のアプリケーション ドメインに読み込む必要がありますが、新しいアプリケーション ドメインにのみ存在する他のアセンブリを読み込むことができます。
今は覚えていませんが、 を呼び出すと、両方のアプリ ドメインでのオブジェクト作成の動作が異なると思いますがCreateInstanceAndUnwrap
、詳細は覚えていません。
プラグイン アーキテクチャについては、このブログ投稿をお読みください。AppDomain クラスを使用して動的プラグインを処理し、コードをロードおよびアンロードする方法について
編集
この AppDomains がどのように機能するかを忘れてしまったので、混乱が生じる可能性があります。「プラグイン」アーキテクチャがどのように機能するかの短い例を用意しました。これは、以前に投稿したブログで説明したものと非常に似ています。シャドウ コピーを使用するサンプルを次に示します。何らかの理由で使用したくない場合は、非常に簡単に使用できるように変更できますAppDomain.Load(byte[] bytes)
3 つのアセンブリがあります。最初の 1 つはベース プラグイン アセンブリで、プロキシとして機能し、すべての 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();
}
}
}
シャドウコピーはとても便利そうです。