3

我正在尝试通过从各种存储库(如 google drive/dropbox/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。

编辑 我删除了取消令牌代码,因为它正在破坏。这是带有取消令牌的实际代码。

    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 回答