Остановить / избавиться / отменить задачу C#

Я пытаюсь развернуть библиотеки DLL внутри службы Windows, импортируя библиотеки DLL из различных репозиториев, таких как Google Drive/ Dropbox/ FTP и т.д....

Но прежде чем создавать новую 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);

Вопросы: Я хочу отменить задачу t до того, как мое приложение обнаружит новую DLL и попытается выполнить ее с помощью этого кода задачи.

РЕДАКТИРОВАТЬ Я удалил код токена отмены, поскольку он ломался. Вот фактический код с токеном отмены.

    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 (делегат). Но я получаю ошибку. Вот уменьшенная версия кода, который выдает ошибку.

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.Program+<>c__DisplayClass2" в сборке "AppDomain, версия =1.0.0.0, Culture= нейтральный, PublicKeyToken=null" не помечен как сериализуемый.

Трассировки стека:

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 в сборке, которую я импортирую, как Serializable

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();
        }


    }
}

1 ответ

Решение

Из вашего вопроса видно, что вы хотите выгрузить динамически загруженную сборку, если доступно какое-либо обновление, а затем перезагрузить последнюю сборку. Принятая отмена не поможет в этом случае. На самом деле я не вижу, чтобы вы использовали токен отмены где-либо.

Единственный способ выгрузить динамически загруженную сборку - это сначала загрузить сборку в отдельный домен приложения, а затем выгрузить сам домен приложения, если сборка больше не нужна. Итак, вы должны сделать следующее:

  1. Создайте новый домен приложения. Сохраните ссылку на домен приложения, она понадобится вам позже для выгрузки домена и, следовательно, сборки.
  2. Загрузите сборку во вновь созданный домен приложения.
  3. При необходимости создайте экземпляр типа из вновь загруженной сборки и выполните его метод.
  4. Когда новая версия DLL будет доступна, выгрузите ранее созданный домен приложения. Это также автоматически выгрузит сборку.
  5. Загрузите новую сборку и начните с шага 1 снова.

Посмотрите, как загрузить / выгрузить домен приложения и сборку в нем: Использование AppDomain в C# для динамической загрузки и выгрузки 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);
        }
    }
}
}
Другие вопросы по тегам