2013-04-03 15 views
6

Próbuję edytować plik XML za pomocą Wix. Używam WixUtilExtension w pakiecie z Wix 3.7. Plik xml jest plikiem ustawień utworzonym w Visual Studio 2010 dla aplikacji C#. W tym pliku używam elementu, który służy do przechowywania wielu wartości ciągu w tablicy. To jest zawartość pliku ustawień niezmienionym:W jaki sposób można dodać wiele elementów do pliku konfiguracyjnego XML za pomocą wix?

<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> 

chcę dodać <string> elementy do elementu <ArrayOfString> w tym pliku. Jednym ze sposobów na to jest użycie elementu <XmlConfig> z przestrzeni nazw wix/UtilExtension. Dodałem ten element do elementu, który posiada plik konfiguracyjny tak:

<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> 

Powoduje dodanie jednego <string> elementu do elementu <ArrayOfString>. Aby dodać kolejny element <string> do pliku ustawień, kolejnym elementem XmlConfig musi zostać dodany do elementu <Component> projektu konfiguracji z innym atrybutem id i wyższej wartości dla sekwencji atrybut tak:

<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" 
/> 

Po instalacji z msi element w pliku ustawień <ArrayOfString> wygląda następująco:

<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> 

I okazało się, że jest możliwe, aby ustawić atrybut wartość atrybutu <XmlConfig> na wartość nieruchomości tak:

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

To dobrze. Chciałbym, aby użytkownik mógł dynamicznie dodawać wiele wartości w procesie instalacji, aby zmienna ilość elementów <string> mogła zostać dodana do pliku ustawień. Moje pierwsze podejście było użyć <?foreach?> oświadczenie takiego:

<?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?> 

Istnieje kilka problemów z tym podejściem:

  1. Oświadczenie foreach używa zmiennej preprocesora, które nie mogą być ustawione na wartości własność.
  2. Wartość atrybutu Sekwencja pozostaje taka sama.

Chciałbym użytkownikowi przechowywać wartości dla elementów strunowych w nieruchomości, która oddziela wartości średnikami, a następnie analizować je w foreach jak ten:

<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?> 

zgłasza to następujące błąd:

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. 

Czy istnieje sposób na utworzenie zmiennej ilości elementów za pomocą elementu XmlFile lub XmlConfig? Czy jedynym rozwiązaniem tego problemu jest CustomAction?

Odpowiedz

2

Na podstawie odpowiedzi Roba, oto moje nowe podejście do dodawania wielu elementów do pliku konfiguracyjnego XML za pomocą Wix. Nie chciałem pisać kodu C++, dlatego użyłem DTF w mojej CustomAction.

Zamierzam opisać, jak zamienić łańcuch zawierający wiele elementów za pomocą ogranicznika na wiele elementów XML.

Najpierw musi istnieć właściwość w pliku instalacyjnym zawierającym rozdzielany ciąg.

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

Ta właściwość może być wypełniona przez użytkownika w oknie dialogowym, oczywiście.

Następnie należy zapisać CustomAction. Aby skorzystać z DTF, do projektu C# CustomAction należy dodać odwołanie do pliku Microsoft.Deployment.WindowsInstaller.dll. Przestrzeń nazw Microsoft.Deployment.WindowsInstaller powinna zostać dołączona do dyrektywy using w tym projekcie. Moja CustomAction wygląda następująco:

[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; 
} 

Tutaj na pierwszy StringArray Nieruchomość jest odczytywany w zmiennej lokalnej, który jest przekształcany do tablicy ciągów. Poniższy wiersz ustanawia połączenie z bieżącą bazą danych używaną przez instalatora. Zostanie utworzony uchwyt tabeli XmlConfig, który jest tabelą, do której dodawane są elementy XML. Aby wstawić odpowiednie wartości do tej tabeli, najlepiej jest utworzyć plik instalatora, który zawiera taką tabelę, a następnie spójrz na tę tabelę w edytorze takim jak orca lub InstEd.

W ścieżce x należy odwrócić ukośniki odwrócone za pomocą podwójnych ukośników odwrotnych. Zmienna id zawiera nazwę rekordu tymczasowego, używając prostego łańcucha, a numer działa bezbłędnie. Sekwencja musi zostać zwiększona dla każdego elementu. Nie mogłem znaleźć żadnej dokumentacji dotyczącej wartości kolumny flags, ale odkryłem, że jej wartość jest ustawiona na 273 dla elementów, które są tworzone, a 289 dla elementów, które zostaną usunięte.

Po wypełnieniu rekordu poprawnymi wartościami zostanie on dodany do tabeli XmlConfig za pomocą metody InsertTemporary obiektu widoku. Odbywa się to dla każdego elementu znalezionego w rozdzielanym łańcuchu znaków.

Wystąpił problem polegający na tym, że ta niestandardowa funkcja nie działa, jeśli tabela XmlConfig nie istnieje. Aby rozwiązać ten problem, dodałem następujący kod do pliku instalacyjnego, który dodaje element do pliku XML i natychmiast usuwa ten element. Sądzę, że może istnieć czystsze rozwiązanie, ale to było dla mnie najłatwiejsze.

<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" /> 

Wreszcie, CustomAction musi zostać dodana do projektu instalacji.Dodając odwołanie do projektu CustomAction w projekcie instalacji, położenie pliku binarnego można określić tak:

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

CustomAction musi być wykonywane natychmiast, w przeciwnym razie nie będzie w stanie uzyskać dostęp do sesji zmienna:

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

Aby ustalić właściwą pozycję dla CustomAction w kolejności instalacji, I powoływać się na this article Bob Arnson.

0

Tak, jest to możliwe, ale jeśli chcesz to ustalić w czasie instalacji, wtedy preprocesor nie jest opcją. Preprocesor wykonuje się podczas procesu kompilacji.

Aby uzyskać to, co chcesz, musisz napisać kolejną akcję niestandardową, która pobiera arbitralnie długi zestaw danych użytkownika i dodaje tymczasowe wiersze do tabeli XmlConfig. Funkcja WcaAddTempRecord() w src\ca\wcautil\wcawrap.cpp może wykonać pracę. Model src\ca\wixca\dll\RemoveFoldersEx.cpp jest całkiem dobrym przykładem użycia WcaAddTempRecord() do dodawania wierszy do tabeli RemoveFile. Będziesz chciał robić podobnie.

3

Jako dodatek do odpowiedzi BdN3504 za ... zamiast całego

<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" /> 

rzeczy. Sugerowałbym użycie tego obiektu, aby zapewnić, że tabela XmlConfig zostanie uwzględniona w wyjściowym pliku MSI, nawet jeśli jest pusta. (Powiedziałbym to jako komentarz .. ale nie mam podobnej reputacji)

Powiązane problemy