在安装程序的 UI 阶段访问自定义操作中打包的 XML 文件的内容

Access content of packaged XML file within custom action during installer's UI phase

我正在维护一个 WiX 安装程序,它已经有一个工作 UI 并且在 UI 阶段执行几个自定义操作。当前和工作状态取决于 .wxs 文件中硬编码的许多 属性 值。我想更改此设置:我不想对值进行硬编码,而是想在安装时动态收集它们。

我需要在显示 UI 时发生这种情况,因为 属性 值会影响 UI 逻辑。现在的问题是 属性 值的片段应该来自安装程序中打包的 XML 文件。 XML 文件也将在 UI 阶段完成后安装到目标机器上。

到目前为止,我发现如何从自定义操作中访问安装程序中打包的文件的唯一方法是使用 Session.GetTargetPath() 获取其路径,如以下 C# 片段所示:

[CustomAction]
public static ActionResult GetInfoFromXml(Session session)
{
    string xmlFolderPath = session.GetTargetPath("XmlFolder");

    [...]  // deserialize and process the file
}

安装程序日志中显示以下消息失败:

Exception thrown by custom action:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentException: The directory name is invalid. XmlFolder
   at Microsoft.Deployment.WindowsInstaller.Session.GetTargetPath(String directory)

经过一番思考,失败似乎是可以预见的:自定义操作是在安装程序的 UI 阶段执行的,因此还没有安装任何东西,因此 GetTargetPath 还不能 return 任何有用的东西.

所以问题是:如何从在安装程序 UI 阶段执行的自定义操作中访问 XML 文件的内容?

如果我可以通过两次打包 XML 文件来实现我的目标,假设一次是将其安装到目标机器上,第二次只是通过自定义操作对其进行处理,那将没事的。我不知道该怎么做,或者如果可能的话,我仍然是一个 WiX 初学者。

注意:我还在构建时研究了 "harvesting" XML 文件的内容,即如何在构建时从 XML 文件生成 .wxs 片段,但 WiX 中似乎没有简单的方法来做到这一点。

我的问题的解决方案是

  1. 将 XML 文件添加到安装程序的二进制 table。
  2. 从自定义操作中的二进制 table 中获取数据,将数据转换为字符串,然后从该字符串反序列化。

这是 .wxs 中的二进制 table 条目:

<Binary Id="XmlFile" SourceFile="foo.xml" />

这是自定义操作部分:

[CustomAction]
public static ActionResult GetInfoFromXml(Session session)
{
    string xmlFileContent = GetStringDataFromBinaryTable(
        session,
        "XmlFile");

    [...]  // deserialize and process the XML file content
}

这是我编写的一个通用的可重用方法,它从二进制文件中提取字符串数据 table:

using Microsoft.Deployment.WindowsInstaller;
using System.IO;

public static string GetStringDataFromBinaryTable(Session session, string binaryTableEntryId)
{
  const string BinaryTableName = "Binary";
  const string NameColumnName = "Name";
  const string DataColumnName = "Data";

  Database db = session.Database;

  string selectQuery = string.Format(
    "SELECT `{2}` FROM `{0}` WHERE `{0}`.`{1}` = '{3}'",
    BinaryTableName,
    NameColumnName,
    DataColumnName,
    binaryTableEntryId);
  View view = db.OpenView(selectQuery);
  view.Execute();

  Record record = view.Fetch();

  Stream stream = record.GetStream(DataColumnName);
  StreamReader streamReader = new StreamReader(stream);
  string dataAsString = streamReader.ReadToEnd();

  view.Close();

  return dataAsString;
}