是什么阻止我的 Windows 服务运行 MSI 安装程序?

问题描述 投票:0回答:1

我创建了一个 Windows 服务,它可以简单地轮询 git 存储库、检查新版本并自动安装我正在构建的应用程序的新版本。

当我尝试从 Windows 服务内部运行下载的 MSI 安装程序时,它无法打开。我尝试过使用 msiexec 并从调用 msiexec 的批处理文件运行安装程序,但我没有运气。我希望能够从批处理文件中调用 MSI,这样我就可以在其中粘贴一个 taskkill 命令来退出应用程序(如果应用程序当前打开)。这是我完成所有这些工作的代码。

// Build a new batch file to run for our installer routines here
FileInfo InstallerInfo = new FileInfo(InstallerPath);
string InstallerName = InstallerInfo.Name;
string InstallerDirectory = InstallerInfo.DirectoryName;
string UpdateBatchName = $"InvokeUpdate_{Path.GetFileNameWithoutExtension(InstallerName)}.bat";
string UpdateBatchPath = Path.Combine(InstallerDirectory, UpdateBatchName);
string InstallerFileContent =
    "@echo OFF\n" +                                        // Disable echo for the script
    "taskkill /F /IM FulcrumInjector*\n" +                 // Attempt to kill instances of the FulcrumInjector application
    $"msiexec /passive /i \"{InstallerPath}\"";            // Boot the new MSI installer in a passive mode to update the injector

// Write out our installer file content and ensure it exists once done
File.WriteAllText(UpdateBatchPath, InstallerFileContent);
if (!File.Exists(UpdateBatchPath))
    throw new FileNotFoundException($"Error! Failed to build batch file to invoke update for installer \"{InstallerPath}\"!");

// Build a new process to boot our batch file and invoke an update here
ProcessStartInfo UpdaterStartInfo = new ProcessStartInfo
{
    Verb = "runas",                            // Forces the process to run as the user who invoked it
    FileName = "cmd.exe",                      // File to invoke (CMD for booting the batch file)
    CreateNoWindow = false,                    // Specifies if we should make a window for this process
    UseShellExecute = false,                   // Uses shell execution or not
    RedirectStandardError = true,              // Redirects standard output
    RedirectStandardOutput = true,             // Redirects standard error
    WorkingDirectory = InstallerDirectory,     // Sets the working directory of the process to our installer folder
    Arguments = $"/C \"{UpdateBatchPath}\"",   // Arguments to pass into the CMD window when booted
};

// Build our process and store the standard output and error information
Process UpdaterProcess = Process.Start(UpdaterStartInfo);
UpdaterProcess.ErrorDataReceived += (_, ErrorArgs) =>
{
    // Only log out our data for the output if it's not empty
    if (string.IsNullOrWhiteSpace(ErrorArgs.Data)) return; 
    this._serviceLogger.WriteLog("[BAT ERROR] >> " + ErrorArgs.Data);
};
UpdaterProcess.OutputDataReceived += (_, OutputArgs) =>
{
    // Only log out our data for the output if it's not empty
    if (string.IsNullOrWhiteSpace(OutputArgs.Data)) return;
    this._serviceLogger.WriteLog("[BAT OUTPUT] >> " + OutputArgs.Data);
};

// Begin reading the output and error streams for our process
UpdaterProcess.BeginErrorReadLine();
UpdaterProcess.BeginOutputReadLine();

// Wait for the process to exit out and return based on status
UpdaterProcess.WaitForExit();
return UpdaterProcess.ExitCode == 0;

当我找到我正在写的批处理文件时,它的内容是正确的,当我通过双击它手动运行它时,批处理文件会正确执行并打开我尝试运行的安装程序。但是当我在代码中使用 Process.Start 时,它不会启动 MSI 文件。我已重定向批处理文件的输出,并且可以看到它正在执行,但安装程序从未显示。当脚本运行时,我看到任务管理器中打开了一个新的 msiexec 实例,但没有任何结果。谁能解释一下这是为什么吗?

c# batch-file windows-services
1个回答
0
投票

我的解决方案

又花了几个小时挖掘文档并摆弄

MSIEXEC
,我找到了解决方案。修复方法非常简单。我只需使用
MSIEXEC
标志而不是
/A
重写对
/I
的调用。完成此操作后,我就能够让服务直接从 C# 代码调用
MSIEXEC
并执行安装程序。我还放弃了使用批处理文件,并在我正在构建的方法中添加了一些 C# 代码,以便在安装程序运行之前终止应用程序的现有实例。

这就是我的最终结果。此解决方案不需要使用 Wix 添加用户帐户或使用不同的用户帐户调用

MSIEXEC
!我的 Windows 服务仍在
Local Service
帐户下运行,到目前为止我还没有遇到问题。希望这对将来的人有用。

注意: 我唯一要提到的是,出于某种原因,我的 MSI 尝试默认将应用程序安装在我的

X
驱动器上,而不是我的
C
驱动器上。我的解决方案是当我调用
TARGETDIR
时,强制安装程序上的
C:\
属性为
MSIEXEC

// Configure an installer log file here
FileInfo InstallerInfo = new FileInfo(InstallerPath);
string InstallerDirectory = InstallerInfo.DirectoryName;
string InstallerName = Path.GetFileNameWithoutExtension(InstallerInfo.Name);
string InstallerLogFile = Path.Combine(InstallerDirectory, $"InstallerLog_{InstallerName}.log");

// Build our argument string for the msiexec process 
string UpdaterArguments =
    $"/A \"{InstallerPath}\" " +       // Install the package as an administrator
    $"TARGETDIR=\"C:\\\" " +           // Specify our target install directory
    $"/L*V \"{InstallerLogFile}\"";    // Sets logging output to the log file name given

// Build a new process to invoke our installer msi file here
ProcessStartInfo UpdaterStartInfo = new ProcessStartInfo
{
    // Configuration for process bootup
    Verb = "runas",                          // Forces the process to run as the user who invoked it
    UseShellExecute = true,                  // Uses shell execution or not
    FileName = "msiexec.exe",                // File to invoke. MSIEXEC for installing MSI files
    Arguments = UpdaterArguments,            // Arguments to pass into the MSIEXEC when booted
};

// Build our process and and store the start info built above
Process UpdaterProcess = new Process();
UpdaterProcess.StartInfo = UpdaterStartInfo;

// Start the process and wait for it to exit
UpdaterProcess.Start();
UpdaterProcess.WaitForExit();

// Return out if we've got an exit code of 0 or not 
return UpdaterProcess.ExitCode == 0;
© www.soinside.com 2019 - 2024. All rights reserved.