6

この質問のさまざまなバージョンと、スタック オーバーフローにあるのと同じくらい多くのバージョンを読みました。また、チュートリアルの 3 つの異なる Google 検索のフロント ページにあるすべての青いリンクと、MSDN (アセンブリの実行を超えた浅いものです) を読みました。 . Tao を動作させるための私の努力は、良いテスト ケースとしてしか考えられませんが、単純な文字列 return、double、パラメーターを持つ関数も試してみました。私の問題が何であれ、それはタオではありません。

testLibraryDomain.CreateInstance()基本的に、GLPlugin 名前空間に Draw クラスを作成したいと考えています。

        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 )(もちろん、残念ながら、アセンブリをロックして、置換/再コンパイルを防ぎます)。

私が試したさまざまな方法のいずれかを使用すると、常に「指定されたアセンブリ名またはそのコード ベースが無効です」というメッセージが表示されます。私は約束します、それは有効です。私がロードしようとしている方法の何かがそうではありません。

私が欠けていることを知っていることの1つは、testLibraryDomain.CreateInstance( string assemblyName , string typeName);

私が知る限り、assemblyName 引数はアセンブリ ファイルへのファイル パスではありません。それは名前空間ですか、それとも単にアセンブリ名GLPluginですか? その場合、実際のファイルはどこで参照できますか? someAppDomain.LoadFrom( someFilename ) はありませんが、あれば便利です。さらに、その Type と文字列 typeName は一体何なのでしょうか? "Object"オブジェクトのインスタンス以外の型を作成していないので、ここに入れたくありませんか? 私も試してみましたCreateInstanceAndUnwrap( ... , ... )AppDomain の基本的な理解が不足しています。「なぜ?」を理解できないことがよくありますが、通常はチュートリアルを混乱させて物事を機能させることができます...ここではそうではありません. 通常、6 つの異なるチュートリアルを参照することは役に立ちます。ここではそうではありませんが、すべてのチュートリアルが基本的な (またはそう思われる) アプローチを取っているためです。

だからELI5してください...別のAppDomainのdllからクラスのインスタンスをロードし、おそらくいくつかの関数を実行してアンロードしたいと思います。最終的に、これらの関数のリストをリストとして作成し、必要に応じて削除/更新します...引数もそれらに渡すことができるようにしたいのですが、それはステップ2になります.StackOverflowによると、serializableどのもう一日延期します。(私の例から、私がやろうとしていることを理解できると思います。)

4

1 に答える 1

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% わかりません。

そして、それは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();
    }
}

}

シャドウコピーはとても便利そうです。

于 2012-11-19T21:47:27.523 に答える