【问题标题】:Shadow copy with AppDomain to overwrite exe at runtime使用 AppDomain 进行卷影复制以在运行时覆盖 exe
【发布时间】:2014-04-20 03:50:07
【问题描述】:

在以下示例应用程序中,我创建了一个新的AppDomain,并在启用卷影复制的情况下执行它。然后从新的AppDomain 尝试删除(替换)原始的主 exe。但是我收到“访问被拒绝错误”。有趣的是,启动程序后,可以从 Windows 资源管理器重命名主 exe(但不能删除它)。

卷影复制能否用于运行时覆盖主 exe?

static void Main(string[] args)
{
    // enable comments if you wanna try to overwrite the original exe (with a 
    // copy of itself made in the default AppDomain) instead of deleting it

    if (AppDomain.CurrentDomain.IsDefaultAppDomain())
    {
        Console.WriteLine("I'm the default domain");
        System.Reflection.Assembly currentAssembly = System.Reflection.Assembly.GetExecutingAssembly();
        string startupPath = currentAssembly.Location;

        //if (!File.Exists(startupPath + ".copy"))
        //    File.Copy(startupPath, startupPath + ".copy");

        AppDomainSetup setup = new AppDomainSetup();
        setup.ApplicationName = Path.GetFileName(startupPath);
        setup.ShadowCopyFiles = "true";

        AppDomain domain = AppDomain.CreateDomain(setup.ApplicationName, AppDomain.CurrentDomain.Evidence, setup);
        domain.SetData("APPPATH", startupPath);

        domain.ExecuteAssembly(setup.ApplicationName, args);

        return;
    }

    Console.WriteLine("I'm the created domain");
    Console.WriteLine("Replacing main exe. Press any key to continue");
    Console.ReadLine();

    string mainExePath = (string)AppDomain.CurrentDomain.GetData("APPPATH");
    //string copyPath = mainExePath + ".copy";
    try
    {
        File.Delete(mainExePath );
        //File.Copy(copyPath, mainExePath );
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error! " + ex.Message);
        Console.ReadLine();
        return;
    }

    Console.WriteLine("Succesfull!");
    Console.ReadLine();
}

【问题讨论】:

  • 你想达到什么目的? 应该拒绝覆盖 exe,因为文件正在使用中。
  • 是的,我想覆盖 exe。来自 MSDN:“影子复制允许在不卸载应用程序域的情况下更新应用程序域中使用的程序集”。这是否仅对 dll 有效,对 exe 无效?
  • 卷影副本是相反的——先复制,然后运行副本,以便替换原件。来自 MSDN:“当应用程序域配置为卷影复制文件时,应用程序路径中的程序集被复制到另一个位置并从该位置加载。副本被锁定,但原始程序集文件被解锁并且可以更新。”了解您是在进行实验、尝试修改运行时行为还是尝试进行就地更新会很有帮助?还是别的什么?
  • 我需要就地更新,并且我正在尝试完全按照 MSDN 中的报告进行操作,即我正在尝试运行副本而不是原始版本。我手动执行的副本只是为了尝试覆盖原始 exe 以证明原始 exe 未锁定。此手动副本与卷影副本无关,您甚至可以将其删除,然后尝试删除原始 exe(如果我正确理解卷影副本的目的)。一旦执行新的 AppDomain,就会创建卷影副本文件(自动在另一个目录中)。
  • 看看 MEF - 你可以很容易地用它来热交换插件代码。更改正在运行的应用程序不会是您可以通过简单地更改源 exe 来完成的事情。看到这个问题:stackoverflow.com/questions/15232228/…

标签: c# appdomain shadow-copy


【解决方案1】:

您可以在具有多个 AppDomain 的单个应用程序中实现自我更新应用程序。诀窍是将应用程序可执行文件移动到临时目录并复制回您的目录,然后将复制的可执行文件加载到新的 AppDomain 中。

static class Program
{
    private const string DELETED_FILES_SUBFOLDER = "__delete";

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [LoaderOptimization(LoaderOptimization.MultiDomainHost)]
    [STAThread]
    static int Main()
    {
        // Check if shadow copying is already enabled
        if (AppDomain.CurrentDomain.IsDefaultAppDomain())
        {
            // Get the startup path.
            string assemblyPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
            string assemblyDirectory = Path.GetDirectoryName(assemblyPath);
            string assemblyFile = Path.GetFileName(assemblyPath);

            // Check deleted files folders existance
            string deletionDirectory = Path.Combine(assemblyDirectory, DELETED_FILES_SUBFOLDER);
            if (Directory.Exists(deletionDirectory))
            {
                // Delete old files from this folder
                foreach (var oldFile in Directory.EnumerateFiles(deletionDirectory, String.Format("{0}_*{1}", Path.GetFileNameWithoutExtension(assemblyFile), Path.GetExtension(assemblyFile))))
                {
                    File.Delete(Path.Combine(deletionDirectory, oldFile));
                }
            }
            else
            {
                Directory.CreateDirectory(deletionDirectory);
            }
            // Move the current assembly to the deletion folder.
            string movedFileName = String.Format("{0}_{1:yyyyMMddHHmmss}{2}", Path.GetFileNameWithoutExtension(assemblyFile), DateTime.Now, Path.GetExtension(assemblyFile));
            string movedFilePath = Path.Combine(assemblyDirectory, DELETED_FILES_SUBFOLDER, movedFileName);
            File.Move(assemblyPath, movedFilePath);
            // Copy the file back
            File.Copy(movedFilePath, assemblyPath);

            bool reload = true;
            while (reload)
            {
                // Create the setup for the new domain
                AppDomainSetup setup = new AppDomainSetup();
                setup.ApplicationName = assemblyFile;
                setup.ShadowCopyFiles = true.ToString().ToLowerInvariant();

                // Create an application domain. Run 
                AppDomain domain = AppDomain.CreateDomain(setup.ApplicationName, AppDomain.CurrentDomain.Evidence, setup);

                // Start application by executing the assembly.
                int exitCode = domain.ExecuteAssembly(setup.ApplicationName);
                reload = !(exitCode == 0);
                AppDomain.Unload(domain);
            }
            return 2;
        }
        else
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            MainForm mainForm = new MainForm();
            Application.Run(mainForm);
            return mainForm.ExitCode;
        }
    }
}

【讨论】:

  • 那你是把新文件复制到删除目录还是汇编路径
  • 您可以复制新文件并覆盖程序集路径中的现有二进制文件。
【解决方案2】:

由于它是 MEF 的一个有趣用例,因此我快速演示了如何在 C# 中热交换正在运行的代码。这非常简单,并且省去了很多边缘情况。

https://github.com/i-e-b/MefExperiments

著名课程:

  • src/PluginWatcher/PluginWatcher.cs -- 监视一个文件夹以查找合同的新实现
  • src/HotSwap.Contracts/IHotSwap.cs -- 热交换的最小基础合约
  • src/HotSwapDemo.App/Program.cs -- 实时代码是否交换

这不会锁定插件文件夹中的任务.dlls,因此您可以在部署新版本后删除旧版本。 希望对您有所帮助。

【讨论】:

    【解决方案3】:

    您专门要求在更新过程中使用 ShadowCopy。如果那(为什么会这样?)不是一个固定的要求,那么这些对我来说真是大开眼界:

    https://visualstudiomagazine.com/articles/2017/12/15/replace-running-app.aspx

    https://www.codeproject.com/Articles/731954/Simple-Auto-Update-Let-your-application-update-i

    这归结为您重命名目标文件(这是允许的,即使它在运行时被锁定),然后将所需文件移动/复制到现在释放的目标。

    vs-magazine 的文章非常详细,包括一些漂亮的技巧,例如找出当前应用程序是否正在使用文件(尽管只有 exe、.dll 和其他人必须想出解决方案)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-04
      • 2015-02-07
      • 2023-03-09
      • 2020-04-15
      • 1970-01-01
      • 1970-01-01
      • 2019-02-05
      • 2022-11-10
      相关资源
      最近更新 更多