影子复制与AppDomain覆盖exe在运行时



在下面的示例应用程序中,我创建了一个新的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();
}

您可以在具有多个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;
        }
    }
}

因为这是一个有趣的MEF用例,所以我已经快速演示了如何在c#中热插拔运行的代码。这非常简单,省去了很多边缘情况。

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

显著的类:

  • src/PluginWatcher/PluginWatcher.cs—监视一个文件夹,以查看合同的新实现
  • src/HotSwap.Contracts/IHotSwap.cs—热插拔的最小基本契约
  • src/HotSwapDemo.App/Program.cs—是否实时代码交换

这不会锁定Plugins文件夹中的 .dlls任务,因此一旦部署了新版本,您可以删除旧版本。

您特别要求在更新过程中使用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和其他必须想出一个解决方案)。

相关内容

  • 没有找到相关文章

最新更新