6

我正在尝试使用 Wix 编辑 XML 文件。我正在使用与 Wix 3.7 捆绑的 WixUtilExtension。xml 文件是在 Visual Studio 2010 中为 C# 应用程序创建的设置文件。在这个文件中,我使用了一个用于在数组中存储多个字符串值的元素。这是未更改的设置文件的内容:

<configuration>
    <applicationSettings>
        <AppName.Properties.Settings>
            <setting name="StringArray" serializeAs="Xml">
                <value>
                    <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                    </ArrayOfString>
                </value>
            </setting>
        </AppName.Properties.Settings>
    </applicationSettings>
</configuration>

我想向此文件<string>中的元素添加元素<ArrayOfString>。一种方法是使用<XmlConfig>wix/UtilExtension 命名空间中的元素。我已将此元素添加到包含如下配置文件的组件中:

<Component Id="ProductComponent" Guid="$(var.ConfigGuid)">
    <File Source="SettingsFile.exe.config" KeyPath="yes" Id="FILE_config" />
    <util:XmlConfig
      Name="string"
      Value="My value"
      File="[INSTALLFOLDER]SettingsFile.exe.config"
      Id="String1"
      On="install"
      Action="create"
      Node="element"
      ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString"
      Sequence="100"
      />
</Component>

这导致向元素添加一个<string>元素<ArrayOfString>。要将另一个<string>元素添加到设置文件,必须将另一个 XmlConfig 元素添加到<Component>设置项目的元素中,该元素具有不同的 Id 属性和更高的 Sequence 属性值,如下所示:

<util:XmlConfig
    Name="string"
    Value="My second value"
    File="[INSTALLFOLDER]SettingsFile.exe.config"
    Id="String2"
    On="install"
    Action="create"
    Node="element"
    ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString"
    Sequence="101"
/>

安装 msi 后,<ArrayOfString>设置文件中的元素如下所示:

<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>My value</string><string>My second value</string></ArrayOfString>

我发现可以将属性的 Value 属性设置为<XmlConfig>属性的值,如下所示:

<Property Id="STRING1VALUE" Value="My value" />
<util:XmlConfig Value="[STRING1VALUE]" ... />

这很好。我希望用户能够在安装过程中动态添加多个值,以便可以将可变数量的<string>元素添加到设置文件中。我的第一种方法是使用这样的<?foreach?>语句:

<?define values="My value;My second value"?>
<?foreach value in $(var.values)?>
    <util:XmlConfig
        Name="string"
        Value="$(var.value)"
        File="[INSTALLFOLDER]SettingsFile.exe.config"
        Id="String$(var.value)"
        On="install"
        Action="create"
        Node="element"
        ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString"
        Sequence="101"
    />
<?endforeach?>

这种方法存在一些问题:

  1. foreach 语句使用无法设置为属性值的预处理器变量。
  2. Sequence 属性的值保持不变。

我希望用户将字符串元素的值存储在属性中,该属性用分号分隔值,然后在 foreach 语句中解析它们,如下所示:

<Property Id="VALUES" Value="My value;My second value" />
<?foreach value in [VALUES]?>
    <util:XmlConfig
        Name="string"
        Value="$(var.value)"
        File="[INSTALLFOLDER]SettingsFile.exe.config"
        Id="String$(var.value)"
        On="install"
        Action="create"
        Node="element"
        ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString"
        Sequence="101"
    />
<?endforeach?>

这会引发以下错误:

The util:XmlConfig/@Id attribute's value, 'String[VALUES]', is not a legal identifier. 
Identifiers may contain ASCII characters A-Z, a-z, digits, underscores (_), or periods (.). 
Every identifier must begin with either a letter or an underscore.

有什么方法可以使用 XmlFile 或 XmlConfig 元素创建可变数量的元素?这个问题的唯一解决方案是 CustomAction 吗?

4

2 回答 2

3

根据 Rob 的回答,这是我使用 Wix 将多个元素添加到 XML 配置文件的新方法。我不想编写 C++ 代码,这就是我在 CustomAction 中使用DTF的原因。

我将描述如何使用分隔符将包含多个元素的字符串转换为多个 XML 元素。

首先,安装文件中需要有一个包含分隔字符串的属性。

<Property Id="STRINGARRAY" Value="string1;string2;string3" />

当然,这个属性可以由用户在对话框中填充。

接下来,必须编写一个 CustomAction。要使用 DTF,必须将 Microsoft.Deployment.WindowsInstaller.dll 的引用添加到 C# CustomAction 项目中。命名空间 Microsoft.Deployment.WindowsInstaller 应包含在该项目中的 using 指令中。我的 CustomAction 看起来像这样:

[CustomAction]
public static ActionResult Insert(Session session)
{
    string strings = session["STRINGARRAY"];
    string[] stringArray = strings.Split(';');
    Database db = session.Database;
    View view = db.OpenView("select * from `XmlConfig`");
    string xpath = "/configuration/applicationSettings/AppName.Properties.Settings/setting[\\[]@name='StringArray'[\\]]/value/ArrayOfString";
    for (int i = 0; i < stringArray.Length; i++)
    {
        string id = String.Format("String{0}", i);
        int sequence = 100 + i;
        string value = stringArray[i].Trim();
        Record rec = new Record(
            id,
            "[INSTALLFOLDER]SettingsFile.exe.config",
            xpath,
            null,
            "string",
            value,
            273,
            "ProductComponent",
            sequence);
        view.InsertTemporary(rec);
    }
    db.Close();
    return ActionResult.Success;
}

在这里,首先将 Property StringArray 读入一个局部变量,该变量被转换为一个字符串数组。以下行建立到安装程序使用的当前数据库的连接。在表 XmlConfig 上创建了一个句柄,该表是添加 XML 元素的表。要将正确的值插入到该表中,最好创建一个包含此类表的安装程序文件,然后在 orca 或 InstEd 等编辑器中查看该表。

在 xpath 中,必须使用双反斜杠来转义反斜杠。id 变量保存临时记录的名称,使用简单的字符串和数字可以完美地工作。序列必须为每个元素递增。我找不到关于 flags 列值的任何文档,但我发现它的值设置为 273 用于创建的元素和 289 用于删除的元素。

一旦记录填充了正确的值,就会使用视图对象的 InsertTemporary 方法将其添加到 XmlConfig 表中。这是针对在分隔字符串中找到的每个元素完成的。

我遇到的一个问题是,如果 XmlConfig 表不存在,此 CustomAction 将失败。为了解决这个问题,我在安装文件中添加了以下代码,该代码将一个元素添加到 XML 文件并立即删除该元素。我想可能有一个更清洁的解决方案,但这对我来说是最简单的。

<util:XmlConfig
    Name="string"
    Value="Dummy"
    File="[INSTALLFOLDER]SettingsFile.exe.config"
    Id="DummyEntry"
    On="install"
    Action="create"
    Node="element"
    ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString"
    Sequence="1" />
<util:XmlConfig
    On="install"
    Action="delete"
    Id="DeleteDummyEntry"
    Node="element"
    File="[INSTALLFOLDER]SettingsFile.exe.config"
    VerifyPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString/string"
    ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString"
    Sequence="2" />

最后,必须将 CustomAction 添加到安装项目中。通过在 setup 项目中添加对 CustomAction 项目的引用,可以像这样指定二进制文件的位置:

<Binary Id="XmlCustomActionDLL" SourceFile="$(var.XmlCustomAction.TargetDir)XmlCustomAction.CA.dll" />

CustomAction 必须立即执行,否则将无法访问会话变量:

<CustomAction Id="CA_XmlCustomAction" BinaryKey="XmlCustomActionDLL" DllEntry="Insert" Execute="immediate" Return="check" />
<InstallExecuteSequence>
  <Custom Action="CA_XmlCustomAction" Before="RemoveRegistryValues" />
</InstallExecuteSequence>

为了确定安装顺序中 CustomAction 的正确位置,我参考了 Bob Arnson 的这篇文章

于 2013-04-04T10:17:42.887 回答
0

是的,这是可能的,但如果您想在安装时确定这一点,那么预处理器不是一个选项。预处理器在构建过程中执行。

要获得所需的内容,您需要编写另一个自定义操作,该操作采用任意长的用户数据集并将临时行添加到XmlConfig表中。中的WcaAddTempRecord()功能src\ca\wcautil\wcawrap.cpp可以完成工作。这是使用向表中添加行src\ca\wixca\dll\RemoveFoldersEx.cpp的一个很好的例子。你会想做类似的事情。WcaAddTempRecord()RemoveFile

于 2013-04-03T12:58:17.413 回答