如何从C#中的内存中启动程序?

时间:2009-02-27 16:33:29

标签: c# assemblies assembly-resolution

我有一些UI应用程序存在于用C#编写的用户任务栏中。该工具的EXE会在许多使用它的项目中签入我们的源代码管理系统,因此我们可以通过签入更新的EXE来更新它们运行的​​版本。

问题是,当用户获得最新版本的exe时,程序经常运行,并且同步在他们的机器上失败。我想修复它,以便程序在运行时不会锁定exe和任何相关DLL,这样它们就可以同步而无需关闭程序。

目前,我有一个程序将可执行文件作为参数,并通过提前将程序集内容读入内存来从内存中启动它。不幸的是,当涉及程序所需的DLL时,这完全失败了。

我现在的代码看起来像这样:

public class ExecuteFromMemory
{
    public static void Main(string[] args)
    {
        //Figure out the name of the EXE to launch and the arguments to forward to it
        string fileName = args[0];
        string[] realArgs = new string[args.Length - 1];
        Array.Copy(args, 1, realArgs, 0, args.Length - 1);

        //Read the assembly from the disk
        byte[] binary = File.ReadAllBytes(fileName);

        //Execute the loaded assembly using reflection
        Assembly memoryAssembly = null;
        try
        {
            memoryAssembly = Assembly.Load(binary);
        }
        catch (Exception ex)
        {
            //Print error message and exit
        }

        MethodInfo method = memoryAssembly.EntryPoint;
        if (method != null && method.IsStatic)
        {
            try
            {
                method.Invoke(null, new object[] { realArgs });
            }
            catch(Exception ex)
            {
                //Print error message and exit
            }
        }
        else
        {
            //Print error message and exit
        }
    }
}

我的问题是,我做的事情是完全愚蠢的吗?有没有更好的方法来处理这个?如果没有,我该怎么做才能支持处理外部依赖?

例如,如果您尝试运行使用“Bar.dll”中的函数的“Foo.exe”,上面的代码无法加载任何相关文件,“Foo.exe”将被覆盖,但是'Bar.dll '仍然被锁定,无法覆盖。

我尝试从加载的'GetReferencedAssemblies()'方法获取引用的程序集列表,但是这似乎没有给出任何指示应该从哪里加载程序集......我是否需要搜索他们自己?如果是这样,最好的方法是什么?

似乎其他人之前可能会遇到这种情况,我不想重新发明轮子。

- 更新: EXE已签入,因为我们将内部工具分发给使用它们的团队。它不是这个用例的最佳选择,但我没有机会改变这个政策。

3 个答案:

答案 0 :(得分:4)

免责声明:我不使用Windows,但我熟悉其锁定内容的奇怪方式。

为了在应用程序运行时更新它,您可能需要有两个进程:可执行文件本身,以及将完成更新过程的更新“帮助程序”应用程序。假设您的应用程序是ProcessA.exe,您的更新帮助程序是Updater.exe。您的主程序将下载可执行文件的新副本,并使用随机名称进行保存。然后运行更新程序,监视当前进程的终止。当您的进程终止时,它会显示一个快速窗口,显示更新的状态,将新的可执行文件移动到旧的可执行文件的位置,然后重新启动该程序。

能够模拟POSIX文件系统语义并能够删除当前正在运行的进程磁盘映像并将其替换为新文件会更优雅,但我不知道在Windows上是否可以实现。在POSIX系统上,您可以删除正在使用的文件,但在关闭任何剩余文件句柄之前,它实际上不会被删除,但您可以重复使用该文件名。

您可能希望查看CodeProject talks about this处撰写的文章。它还有一个follow-up article

祝你好运!

答案 1 :(得分:1)

程序需要在更新时是否继续运行?

通常,要更新正在运行的程序,您可以将要替换的任何文件复制到临时文件夹。然后关闭旧实例,删除它并将新文件移动到正确的位置,然后重新启动它。

这样可以最大限度地缩短应用程序的停机时间,因为最长的部分通常是副本,如果临时文件夹位于同一逻辑驱动器上,则文件移动速度非常快。

答案 2 :(得分:0)

虽然迈克尔的答案是这样做的一种方式,但有一些工具明确用于管理桌面上安装的内容。

您正在对正在检查源代码管理的exe执行的操作不正常。如果您有Windows域控制器,则可以使用组策略将程序推送到客户端。或者,您可以使用像Altiris这样的东西来处理它。

如果你必须继续前进的方式,那么你有两种选择。一,使用帮助器/加载器应用程序,在启动时进行版本检查。这类似于firefox的工作方式。

第二种方法是构建一个位于内存中的帮助程序服务,并经常轮询以进行更新。这就是谷歌Chrome,Adobe等的工作方式。