3

Googleドライブ/ドロップボックス/ ftpなどのさまざまなリポジトリからDLLをインポートして、Windowsサービス内にDLLを展開しようとしています...

しかし、新しい DLL をインスタンス化する前に、以前に実行していたインスタンスをシャットダウンする必要があります。

これにはタスクとリフレクションを使用しています。

実行時に DLL をインスタンス化するタスクをキャンセルする方法がわかりません(インスタンス化された dll は長時間実行されるアプリケーション サンプル ファイル ウォッチャーであるため..)

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;

            // instantiate the dll though reflection
        t = Task.Factory.StartNew(() =>
        {
            try
            {
                Assembly assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName);
                Type type = assembly.GetType("myclass.Program");

                MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic |
                          BindingFlags.Static | BindingFlags.Instance);
                minfo.Invoke(Activator.CreateInstance(type), null);

            }
            catch (Exception ex)
            {

                log.Error(ex.ToString());
            }
        }, cts.Token);

質問: アプリケーションが新しい dll を検出し、このタスク コードを使用してそれを実行しようとする前に、タスク t をキャンセルしたいです。

EDIT キャンセルトークンコードが壊れていたので削除しました。キャンセル トークンを含む実際のコードを次に示します。

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;

if (t != null)
        {
            cts.Cancel();
            try
            {
                ct.ThrowIfCancellationRequested();
            }
            catch (Exception ex)
            {

                cts.Dispose();
                t.Dispose();
            }

        }

                // instantiate the dll though reflection
        t = Task.Factory.StartNew(() =>
        {
            try
            {
                Assembly assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName);
                Type type = assembly.GetType("myclass.Program");

                MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic |
                          BindingFlags.Static | BindingFlags.Instance);
                minfo.Invoke(Activator.CreateInstance(type), null);

            }
            catch (Exception ex)
            {

                log.Error(ex.ToString());
            }
        }, cts.Token);

私の考えは、インスタンス化コンテキストを保持していたタスクを何らかの方法でキャンセルして破棄できれば、アセンブリが解放され、アセンブリを更新して、タスクを介して再度インスタンス化できるようになるというものでした。

私はどこかで間違っていることを知っています。親切に説明してください。

編集

私は assemblyDomain.DoCallBack(delegate) に大きな期待を寄せていました。しかし、エラーが発生します。これは、バグをスローするコードのトーンダウン バージョンです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Reflection;


namespace AppDomain
{
    [Serializable]
    class Program
    {
        static System.AppDomain assemblyDomain = null;

        static void Main(string[] args)
        {

            var inp = "go";

            while (inp.ToString().ToLower().Trim() != "stop")
            {
                start();
                inp = Console.ReadLine();
            }

        }

        private static void start()
        {


            //Check if appdomain and assembly is already loaded
            if (assemblyDomain != null)
            {
                //unload appDomain and hence the assembly
                System.AppDomain.Unload(assemblyDomain);

                //Code to download new dll
            }

            string cwd = System.AppDomain.CurrentDomain.BaseDirectory;

            string sourceFileName = @"C:\Users\guest\Documents\visual studio 2010\Projects\DotNetTraining\Lecture 1 - dotNetProgramExecution\bin\Debug\Lecture 1 - dotNetProgramExecution.exe";

            string dllName = "Lecture 1 - dotNetProgramExecution.exe";

            // copy the file
            if (File.Exists(cwd + dllName))
            {
                File.Delete(cwd + dllName);
            }

            File.Copy(sourceFileName, cwd + dllName);

            assemblyDomain = System.AppDomain.CreateDomain("assembly1Domain", null);
            assemblyDomain.DoCallBack(() =>
               {
                   var t = Task.Factory.StartNew(() =>
                   {
                       try
                       {

                           string sss = "";
                           Assembly assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName);
                           Type type = assembly.GetType("Lecture_1___dotNetProgramExecution.Program");

                           MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic |
                                     BindingFlags.Static | BindingFlags.Instance);
                           minfo.Invoke(Activator.CreateInstance(type), null);



                           //        //var pathToDll = @"assembly path";
                           //        //var dllName = "assembly name";
                           //        var assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName);
                           //        var targetAssembly = assembly.CreateInstance("Lecture_1___dotNetProgramExecution.Program");
                           //        Type type = targetAssembly.GetType();
                           //        MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
                           //        minfo.Invoke(targetAssembly, null);

                       }
                       catch (Exception ex)
                       {

                           Console.WriteLine(ex.ToString());
                       }
                   });
               });
        }
    }
}


エラー :
アセンブリ 'AppDomain、Version=1.0.0.0、Culture=neutral、PublicKeyToken=null' の 'AppDomain.Program+<>c__DisplayClass2' と入力すると、シリアル化可能としてマークされていません。

スタックトレース :

at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at AppDomain.Program.start() in c:\users\guest\documents\visual studio 2010\Projects\DotNetTraining\AppDomain\Program.cs:line 58
   at AppDomain.Program.Main(String[] args) in c:\users\guest\documents\visual studio 2010\Projects\DotNetTraining\AppDomain\Program.cs:line 24
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

注意してください:私はシリアライズ可能としてインポートするアセンブリのクラスProgramをマークしました

namespace Lecture_1___dotNetProgramExecution
{
    [Serializable]
    class Program
    {
        static void Main()
        {

更新しました :

動的にプルされたアセンブリのコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;

namespace Lecture_1___dotNetProgramExecution
{
    [Serializable]
    class Program
    {
        static void Main()
        {
            try
            {

                Task.Factory.StartNew(() =>
                {

                    while (true)
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append("log something new yippe ");
                        // flush every 20 seconds as you do it
                        File.AppendAllText(@"C:\logs.txt", sb.ToString());
                        sb.Clear();
                        Thread.Sleep(3000);
                    }

                });



                FileSystemWatcher fsw = new FileSystemWatcher();

                fsw.Path = @"c:\watched";
                //fsw.filter = ".dll";
                fsw.Created += new FileSystemEventHandler(fsw_Created);
                fsw.BeginInit();
                //throw new FileNotFoundException();
                Console.ReadLine();

            }
            catch (Exception ex)
            {

                Task.Factory.StartNew(() =>
                {

                    while (true)
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append("loggind froom exception log something");
                        // flush every 20 seconds as you do it
                        File.AppendAllText(@"C:\logs.txt", sb.ToString());
                        sb.Clear();
                        Thread.Sleep(1000);
                    }

                });
                Console.ReadLine();
            }
        }

        static void fsw_Created(object sender, FileSystemEventArgs e)
        {
            throw new NotImplementedException();
        }


    }
}
4

1 に答える 1

4

あなたの質問から、アップグレードが利用可能な場合は動的にロードされたアセンブリをアンロードしてから、最新のアセンブリをリロードしたいようです。この場合のキャンセルは役に立ちません。実際、どこでもキャンセルトークンを使用しているとは思いません。

動的に読み込まれたアセンブリをアンロードする唯一の方法は、最初に別のアプリ ドメインにアセンブリを読み込み、アセンブリが不要になったらアプリ ドメイン自体をアンロードすることです。したがって、次のようにする必要があります。

  1. 新しいアプリ ドメインを作成します。アプリ ドメインの参照を保持します。後でドメインをアンロードしてアセンブリするために必要になります。
  2. 新しく作成されたアプリ ドメインにアセンブリを読み込みます。
  3. 必要に応じて、新しく読み込まれたアセンブリから型のインスタンスを作成し、そのメソッドを実行します。
  4. 新しいバージョンの dll が利用可能になったら、以前に作成したアプリ ドメインをアンロードします。これにより、アセンブリも自動的にアンロードされます。
  5. 新しいアセンブリをダウンロードして、手順 1 からやり直してください。

アプリ ドメインとそのアセンブリをロード/アンロードする方法については、こちらを参照してください: Using AppDomain in C# to dynamic load and unload dll

編集: 以下は、AppDomain.DoCallback を使用したコード スニペットです。

using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;


namespace AppDomain
{
[Serializable]
class Program
{
    static System.AppDomain assemblyDomain = null;

    static void Main(string[] args)
    {

        var inp = "go";

        while (inp.ToString().ToLower().Trim() != "stop")
        {
            start();
            inp = Console.ReadLine();
        }

    }

    private static void start()
    {


        //Check if appdomain and assembly is already loaded
        if (assemblyDomain != null)
        {
            //unload appDomain and hence the assembly
            System.AppDomain.Unload(assemblyDomain);

            //Code to download new dll
        }

        string cwd = System.AppDomain.CurrentDomain.BaseDirectory;

        string sourceFileName = @"C:\Users\deepak\Documents\visual studio 2010\Projects\ConsoleApplication1\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe";


        string dllName = "ConsoleApplication2.exe";

        // copy the file
        if (File.Exists(cwd + dllName))
        {
            File.Delete(cwd + dllName);
        }

        File.Copy(sourceFileName, cwd + dllName);

        assemblyDomain = System.AppDomain.CreateDomain("assembly1Domain", null);
        assemblyDomain.DoCallBack(() =>
        {
            var t = Task.Factory.StartNew(() =>
            {
                try
                {

                    string sss = "";
                    string dllName1 = "ConsoleApplication2.exe";
                    Assembly assembly = Assembly.LoadFile(Directory.GetCurrentDirectory() + @"\" + dllName1);
                    Type type = assembly.GetType("Lecture_1___dotNetProgramExecution.Program");

                    MethodInfo minfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.NonPublic |
                              BindingFlags.Static | BindingFlags.Instance);
                    minfo.Invoke(Activator.CreateInstance(type), null);
                }
                catch (Exception ex)
                {

                    Console.WriteLine(ex.ToString());
                }
            });
        });
    }
}
}


using System;
using System.Text;
using System.Threading;

namespace Lecture_1___dotNetProgramExecution
{
[Serializable]
class Program
{
    static void Main()
    {
        while (true)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("log something new yippe ");
            // flush every 20 seconds as you do it
            //File.AppendAllText(@"C:\logs.txt", sb.ToString());
            Console.WriteLine(sb.ToString());
            sb.Clear();
            Thread.Sleep(3000);
        }
    }
}
}
于 2015-08-01T13:46:43.763 に答える