【问题标题】:How to start an exe from a .NET Windows Service for updating the service如何从 .NET Windows 服务启动 exe 以更新服务
【发布时间】:2015-11-28 14:36:14
【问题描述】:

我有一个 Windows 服务,我想自动且无提示地更新它。我开始使用 wyBuild 来实现它,但遇到了一些问题,并决定尝试构建自己的。我编写了一个独立的 exe,可以调用它来执行更新过程:检查带有更新的新 zip 文件,下载它,解压缩,停止 windows 服务,从 zip 复制文件,然后重新启动服务。当我从命令行运行它时,这个 exe 运行良好,而且编写起来并不难。

但是,现在我希望服务(正在更新的同一个)向更新程序 exe 进行自我更新。我首先尝试了 Process.Start:

var proc = Process.Start(pathToUpdaterExe);
proc.WaitForExit(60000);

这调用了更新程序,但是当更新程序停止服务时,进程被杀死并且更新停止。我做了一些搜索,听起来解决方案是使用单独的 AppDomain。这就是我现在拥有的:

Evidence baseEvidence = AppDomain.CurrentDomain.Evidence;
Evidence objEvidence = new System.Security.Policy.Evidence(baseEvidence);
AppDomainSetup setup = new AppDomainSetup();
var updateDomain = AppDomain.CreateDomain("updateDomain", objEvidence, setup);
updateDomain.ExecuteAssembly(updater);
AppDomain.Unload(updateDomain);

但是,现在我收到错误 System.IO.IOException:“该进程无法访问文件 'C:\Program Files (x86)\Company\Service\Service.dll' 因为它正被另一个进程使用”尝试复制新的 Service.dll 时

同样,此时我已经停止该服务。我已经通过记录确认了这一点。我无法想象 Service.dll 仍然被锁定的是什么,所以我添加了代码来检查是什么锁定了它:

 public static IEnumerable<Process> GetProcessesLocking(string filePath)
    {
        var result = new List<Process>();

        result.Clear();
        var processes = Process.GetProcesses();
        foreach (Process proc in processes)
        {
            try
            {
                if (proc.HasExited) continue;
                foreach (ProcessModule module in proc.Modules)
                {
                    if ((module.FileName.ToLower().CompareTo(filePath.ToLower()) == 0))
                    {
                        result.Add(proc);
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Log(ex.ToString());
                Log("There was an error checking " + proc.ProcessName );
            }
        }
        return result;
    }

但是,此代码表明 dll 上没有任何锁定(结果为空,并且没有记录任何内容表示错误)。

我怀疑我遇到了一些 UAC 问题,这是 IOException 的真正原因。 Windows 服务作为 LocalSystem 运行。所有这些要问:应该我如何从 Windows 服务运行更新 exe,以便它有权复制 c:\Program Files 中的文件?

更新

正如 cmets 和答案所暗示的,Process.Start 可以工作,但有一些细微差别。您必须启动 cmd.exe 并使用 it 来启动更新程序。我还发现我无法为更新程序 exe 使用完整路径,并且我需要设置 UseShellExecute=false。这是我从 .NET 服务启动更新程序的最终工作代码:

 var cmd = "/c start updater.exe";     
 var startInfo = new ProcessStartInfo("cmd.exe");
 startInfo.Arguments = cmd;
 startInfo.WorkingDirectory = AssemblyDirectory;
 startInfo.UseShellExecute = false;
 var proc = Process.Start(startInfo);

【问题讨论】:

  • 完全没想到,但为什么不让服务创建一个计划任务以在 5 秒内启动呢?这样启动更新的过程和更新的实际处理就完全脱节了
  • 发生这种情况时,可以在进程资源管理器中查找 DLL 名称(“查找句柄”),看看谁还有打开 DLL 的句柄?或者在 Process Monitor 中记录这样的会话。只要找到真正的罪魁祸首。不知道5秒后开始会不会更成功。
  • 您的第一个解决方案可以工作。阅读stackoverflow.com/questions/1035213/… 了解如何让更新程序保持活跃。

标签: c# windows uac


【解决方案1】:

我做了这件事——使用更简单(有些人可能会说笨拙)的方法。服务:

  1. 产生一个批处理命令,
  2. 将新的可执行文件下载到暂存位置,
  3. 启动一个进程:cmd.exe,然后运行批处理脚本而不等待它完成,然后
  4. 立即自行终止。

批处理命令:

  1. Ping 127.0.0.1 五次,
  2. 将可执行文件复制到最终位置,并且,
  3. 重新启动服务。

像发条一样工作。 ping 是一个可靠的 5 秒延迟 - 让服务在复制文件之前关闭。

编辑

为了完整起见 - 我意识到批量 cmd 正在 ping 127.0.0.1 而不是 128.0.0.1,所以我编辑了这个答案以反映这一点。我想这两种方法都可以——但是 128.0.0.1 ping 超时,其中 127.0.0.1 解析为“我”。由于我只是将其用作穷人的延迟,因此无论哪种方式都可以达到目的。

【讨论】:

  • 看起来使用 Process.Start 和 cmd.exe 是关键。 Process.Start("cmd.exe", "/c updater.exe") 做我想要的。我的过程有点不同 - 它使用 .net 控制台应用程序而不是批处理,控制台应用程序检查更新、停止/启动服务和应用更新。我遇到了其他一些细微差别 - 我将使用我正在使用的代码更新我的问题。
  • 非常好 - 很高兴这对你有用。将其从您的流程中彻底清除会更容易。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-13
  • 1970-01-01
相关资源
最近更新 更多