MSDN Magazin > Home > Ausgaben > 2007 > March >  WiX-Tricks: Automatisieren von Freigaben mit MS...
WiX-Tricks
Automatisieren von Freigaben mit MSBuild und Windows Installer XML
Sayed Ibrahim Hashimi

Themen in diesem Artikel:
  • Überblick über Windows Installer XML
  • Erstellen von WiX-Verpackungsanweisungen
  • Integrieren von WiX und MSBuild
  • Automatisieren von Builds und Verpackungsvorgängen
In diesem Artikel werden folgende Technologien verwendet:
Visual Studio, Windows Installer XML (WiX), MSBuild
Laden Sie den Code für diesen Artikel herunter: WiX2007_03.exe (498 KB)
Code online durchsuchen
Im Verlauf der Entwicklung ist es wichtig, einen automatisierten Buildprozess zu besitzen. Gleichermaßen wichtig ist eine automatisierte Methode zur Erstellung von Versionen. Leider ist dies in vielen Organisationen, insbesondere in kleineren Unternehmen, nicht gegeben. In der Regel werden Sie feststellen, dass die Version einfach in letzter Minute zusammengeschustert wird. Wenn Sie sich jedoch die Zeit dafür nehmen, einen automatisierten Build- und Freigabeplan zu entwickeln, sparen Sie zahllose Stunden, die Sie sinnvoller auf andere Aufgaben statt auf die Erstellung und Freigabe Ihres Projekts verwenden können.
In diesem Artikel werde ich Ihnen zeigen, wie Sie mithilfe der Microsoft® Build Engine (MSBuild) und des WiX-Toolsets (Windows® Installer XML) in Ihrer Organisation einen automatisierten und wiederholbaren Build- und Freigabeprozess einrichten können. Dieser Artikel befasst sich mit WiX Version 2. (Beachten Sie bitte, dass bei Veröffentlichung von WiX Version 3 einige der Syntaxbeispiele nicht mehr direkt übertragbar sind.) Die hier beschriebenen Verfahren sind unabhängig davon relevant, ob Sie WiX zum Erstellen von Versionen verwenden, obgleich WiX diesen Prozess vereinfacht. Diese Verfahren können mit einigen Änderungen auch auf Anwendungen übertragen werden, die ohne Verwendung von Microsoft .NET Framework 2.0 entwickelt werden.
Ich gehe davon aus, dass Sie mit MSBuild vertraut sind (wenn Sie Ihre Kenntnisse auffrischen müssen, lesen Sie meinen MSDN®Magazin-Artikel „Inside MSBuild: Compile Apps Your Way with Custom Tasks for the Microsoft Build Engine“ vom Juni 2006). Für alle, die mit dem WiX-Toolset nicht vertraut sind, werde ich einen Überblick über dieses Toolset geben. Verweise auf weitere Artikel und Tools, die mit diesem Themenbereich zusammenhängen, finden Sie in der Randleiste „MSBuild- und WiX-Ressourcen“. In diesem Artikel verwende ich zu Demonstrationszwecken mein MSBuild-Projekt Sedodream. Sie erhalten die neuesten Quellen auf der Website www.codeplex.com/Sedodream.

Einführung in WiX
Beim Erstellen einer Anwendung besteht das Endergebnis in der Regel darin, dass Sie die Anwendung auf Produktionscomputern installieren und ausführen. Das WiX-Toolset kann Ihnen bei dieser Aufgabe helfen. Dieser Abschnitt enthält eine Beschreibung von WiX sowie eine Einführung in das Erstellen von Installationsprogrammen mit WiX.
WiX beschreibt, wie die Installation auf dem Zielcomputer aussieht. In vielen Microsoft-Anwendungen wird Windows Installer verwendet, von Visual Studio® bis zu Microsoft Office. Es wird Sie vielleicht überraschen zu erfahren, dass WiX ein von sourceforge.net gehostetes Open Source-Projekt ist. Sie können die neuesten Binärdateien und Quellen von der Website wix.sourceforge.net herunterladen. Wenn Sie WiX herunterladen und installieren, finden Sie in Ihrer Installation den in Abbildung 1 zusammengefassten Satz von ausführbaren Dateien vor. Ich werde mich auf die Verwendung der Tools Candle.exe und Light.exe konzentrieren.

Name Beschreibung
Candle.exe Wandelt die WiX-Quelldatei in eine Zwischendarstellung um. Dabei handelt es sich zwar um eine weitere XML-Datei, Sie sollten jedoch die generierten Dateien unter keinen Umständen manuell ändern.
Dark.exe Konvertiert eine MSI-Datei in eine geeignete WiX-Quelldatei. Sie können sich diesen Vorgang als „Dekompilierung“ des Installationsprogramms vorstellen.
Light.exe Generiert aus der Zwischendarstellung der WiX-Quelldatei(en) den Windows Installer.
Lit.exe Generiert WiX-Bibliotheken, die zum Erstellen anderer Installationsprogrammpakete verwendet werden können.
Tallow.exe Dient zum Erstellen der WiX-XML-Quelle, um ihre Dateien und Ordner in einem Installationsverzeichnis oder einer Installationsdatei zu replizieren.
WixCop.exe Überprüft ähnlich wie „FxCop“ eine WiX-Quelldatei auf potenzielle Problembereiche.
WiX verwendet keine Prozedursprache, sondern eine Deklarationssprache. Dies bedeutet, dass Sie nicht angeben, welche Schritte ausgeführt werden müssen, um Ihre Installation zu realisieren, sondern beschreiben, wie Ihre Installation aussehen soll. Dies unterscheidet sich eventuell von dem, woran Sie gewohnt sind, ist aber erstaunlich leicht zu erlernen. In der Regel werden Ihre WiX-Quelldateien gefüllt, indem Dateien beschrieben werden, die auf dem Zielcomputer installiert werden. Ich werde mich in diesem Artikel auf diese Komponenten konzentrieren.
In einer WiX-Quelldatei gibt es in Bezug auf die Dateien, die installiert werden sollen, drei Hauptelemente: „File“, „Component“ und „Feature“. Ein „File“-Element ist ein Verweis auf eine einzelne Datei. Dateien müssen in einem „Component“-Element enthalten sein, das die kleinste zu installierende Einheit darstellt. Wenn eine Komponente also 100 Dateien enthält und Sie diese Komponente installieren, werden alle in ihr enthaltenen Dateien installiert. Umgekehrt gilt: Wenn diese Komponente nicht installiert wird, dann wird keine der in ihr enthaltenen Dateien installiert. Es wird davon abgeraten, Komponenten zu erstellen, die eine große Anzahl von Dateien enthalten.
Komponenten sind immer in einem „Feature“-Element enthalten und können Bestandteil mehrerer Features sein. Ein Feature ist ein Satz, der sich aus Komponenten und möglicherweise auch aus Unterfeatures zusammensetzt. Wenn Ihr Installationsprogramm über eine grafische Benutzeroberfläche verfügt, die dem Benutzer die Auswahl der zu installierenden Elemente ermöglicht, wählt der Benutzer in Wirklichkeit Features aus.

Erstellen von WiX-Dateien
Sie können WiX-Quelldateien in jedem beliebigen Text- oder XML-Editor erstellen. Sie können auch Visual Studio zum Erstellen von WiX-Quelldateien mit IntelliSense® verwenden. Wenn Sie eine WiX-Quelldatei mit Visual Studio bearbeiten und IntelliSense nicht aktiviert ist, müssen Sie lediglich die Datei wix.xsd in das Schemaverzeichnis von Visual Studio kopieren. Dieses Verzeichnis befindet sich in der Regel unter „%Programme%\Microsoft Visual Studio 8\Xml\Schemas“. Beim Erstellen von WiX-Quelldateien müssen Sie auch zahlreiche GUIDs generieren. Visual Studio bietet ein Tool, das Sie für diesen Zweck verwenden können, und es stehen zudem Visual Studio-Makros zur Verfügung, mit denen Sie eine Verknüpfung zuweisen können, um eine neue GUID direkt einzufügen.
Beginnen wir nun mit der Erstellung einer neuen WiX-Quelldatei. Das erste Element ist immer ein Wix-Element. Untergeordnete Elemente des Wix-Elements sind die Elemente „Product“, „Fragment“, „Module“ und „PatchCreation“. Die Art der gewünschten Ausgabe bestimmt, welches dieser untergeordneten Elemente Sie verwenden. In unserem Fall soll das Endergebnis eine Installer-Datenbank (MSI) für mein Projekt sein. Daher verwende ich das Element „Product“. Hier ist der Anfang der WiX 2-Quelldatei Sedodream.wxs für das Beispielprojekt zu sehen:
<?xml version='1.0' encoding='UTF-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2003/01/wi'>   
   <Product Name='Sedodream MSBuild Project' 
        Id='C9D25926-FCE0-4EB6-8FF5-4686EE5AB089'
        Language='1033' Codepage='1252' Version='1.0.0' 
        Manufacturer='SedoTech'
        UpgradeCode='9C5E4073-EFDE-419B-935D-CE2632BC560E'>

      <Package Id='????????-????-????-????-????????????' 
         Keywords='Installer'
         Description='Sedodream MSBuild Project Installer'
         InstallerVersion='100' Languages='1031' 
         Compressed='yes' SummaryCodepage='1252' />
...
Wie Sie sehen können, verfügt das Element „Product“ über zahlreiche Attribute, von denen „Id“ und „Name“ die wichtigsten sind. Das Attribut „Id“ dient dazu, das Produkt eindeutig zu identifizieren. Bei einer neuen Hauptversion müssen Sie diesen Attributwert ändern. Sie sollten sich den Wert auch für spätere Referenzzwecke notieren. Der Wert des Attributs „Name“ bestimmt, was später im Dialogfeld „Software“ angezeigt wird, und falls Ihr Installationsprogramm eine Benutzeroberfläche besitzt, wird er auf der Einführungsseite angezeigt.
An dieser Stelle wird auch das Element „Package“, das erste untergeordnete Element des Elements „Product“, angezeigt. Beachten Sie, dass das Attribut „Id“ eine Folge von Fragezeichen („?“) im Format einer GUID enthält. Durch diese Syntax gibt der Code an, dass die ID zum Zeitpunkt der Builderstellung generiert werden soll. Wenn Sie Installationsprogramme erstellen, sollte die Paket-ID bei jedem erstellten Installationsprogramm anders lauten, selbst in den verschiedenen Builds. Dies ist die einzige GUID, die Sie automatisch generieren lassen können. Alle anderen GUIDs müssen gleich bleiben und sollten für spätere Referenzzwecke notiert werden.
Fast jedes Installationsprogramm legt mindestens eine Datei auf dem Zielcomputer ab. Dies ist bei diesem Installationsprogramm nicht anders. Da WiX mit einem deklarativen Ansatz arbeitet, deklariere ich, wie die Verzeichnisstruktur beschaffen ist, und diese Verzeichnisstruktur wird dann auf dem Zielcomputer neu angelegt. Ich verwende hierzu eine Folge von „Directory“-Elementen:
<Media Id='1' Cabinet='Sedodream.cab' EmbedCab='yes'/>

<Directory Id='TARGETDIR' Name='SourceDir'>
   <Directory Id='ProgramFilesFolder' Name='PFiles'>
      <Directory Id='MSBuildDir' Name='MSBuild'>
         <Directory Id='INSTALLDIR' Name='Sedodrea' 
          LongName='Sedodream'>
Mit dem Element „Media“ wird in diesem Beispiel festgelegt, dass eine CAB-Datei generiert und in der Installer-Datenbank abgelegt wird. Hierbei handelt es sich um ein obligatorisches Element, und im Fall von Installationen, die mehrere Medien umfassen, benötigen Sie eventuell mehrere dieser Elemente. Dies kann beispielsweise dann der Fall sein, wenn Sie Ihre Anwendung auf CD verteilen, die Anwendung aber mehrere CDs umfasst. In den meisten Fällen müssen Sie sich um dieses Problem nicht kümmern.
Nach „Media“ folgt eine Reihe von „Directory“-Elementen. Das erste „Directory“-Element ist ein virtuelles Element, in dem lediglich die anderen Einträge gekapselt werden. Der Eintrag unter dem „Directory“-Element „TARGETDIR“ hat den „Id“-Wert „ProgramFilesFolder“. Wie Sie vielleicht schon erraten haben, ist dies ein bekannter Speicherort, und Windows Installer legt seinen Wert beim Start fest. Es gibt weitere Systemordner, die Sie verwenden können. Eine vollständige Liste finden Sie unter msdn.microsoft.com/library/en-us/msi/setup/system_folder_properties.asp. Diese Systemeigenschaften werden stets in ihre vollständigen Pfade aufgelöst.
Die nächsten Elemente stellen Namen benutzerdefinierter Verzeichnisse bereit. Dem Verzeichnis wird sein Name mithilfe des Attributs „LongName“ zugewiesen, sofern dies möglich ist. Andernfalls wird das Attribut „Name“ verwendet. Wenn diese Verzeichnisse nicht existieren, erstellt Windows Installer das entsprechende Verzeichnis bei Installation. Die vollständige Verzeichnisstruktur der im vorherigen Codefragment deklarierten Elemente lautet am Ende „C:\Programme\MSBuild\Sedodream“.
Nachdem Sie Ihre Verzeichnisstruktur deklariert haben, ist es an der Zeit, mit dem Erstellen von „Component“-Deklarationen zu beginnen. Wie bereits erwähnt, ist eine Komponente die kleinste Einheit, die installiert werden kann. Sie kann aus vielen verschiedenen Elementen wie beispielsweise Dateien, Verknüpfungen, Registrierungsschlüsseln und Zertifikaten bestehen. Entwerfen Sie Ihre Komponenten so, dass sie voneinander unabhängig sind. Dies bedeutet, dass die Installation oder Deinstallation einer Komponente keine negativen Auswirkungen auf andere Komponenten haben sollte. Daher ist es nur logisch, dass Elemente lediglich in einer einzigen Komponente enthalten sein sollten. Ist dies nicht der Fall, sollten Sie die Organisation Ihrer Komponenten überdenken. Es ist nicht ungewöhnlich, dass in einer Komponente nur eine einzige Datei enthalten ist.
Jede „Component“-Deklaration muss ein „Id“-Attribut und ein „GUID“-Attribut aufweisen. Der Wert des Attributs „Id“ ist der Name, den Sie verwenden, um auf die Komponente zu verweisen, und der Wert von „GUID“ ist ein eindeutiger Bezeichner, den Windows Installer verwenden soll. Stellen Sie unbedingt sicher, dass keine Ihrer Komponenten-GUIDs wiederholt wird.
Abbildung 2 zeigt eine Komponente aus der WiX-Beispielquelldatei. Wie Sie sehen können, enthält sie vier „File“-Elemente und zwei „XmlFile“-Elemente. Das „File“-Element ist lediglich ein Verweis auf eine Datei, die auf dem Zielcomputer abgelegt werden muss. Das „File“-Element kann viele verschiedene Attribute aufweisen, Sie werden jedoch am häufigsten die in Abbildung 3 beschriebenen Attribute „Id“, „Name“, „LongName“, „Source“ und „DiskId“ verwenden.

Attributname Beschreibung
Id Ein Bezeichner, der verwendet werden kann, um auf die Datei zu verweisen.
Name Der Kurzname der Datei im 8.3-Format.
LongName Der lange Name der Datei. Dies ist der Name, der auf dem Zielcomputer verwendet wird, falls dieser lange Dateinamen unterstützt. Ist dies nicht der Fall, wird der Wert von „Name“ verwendet.
Source Der relative Pfad zur Datei auf dem Computer, der die Installer-Datenbank erstellt.
DiskId Der Bezeichner des Mediums, auf dem diese Datei enthalten sein wird.
<Component Id='TaskBinFiles' Guid='38736E2E-BEB7-48A9-A2B3-138A57A69D45'>
   <File Id='MSBCommonDLL' Name='CommoDLL' 
      LongName='Sedodream.MSBuild.Common.dll'
      Source='Sedodream.MSBuild.Common.dll' Vital='yes' DiskId='1'/>
   <File Id='SedodreamLoggersDLL' Name='Logger' 
      LongName='Sedodream.MSBuild.Loggers.dll'
      Source='Sedodream.MSBuild.Loggers.dll' Vital='yes' DiskId='1'/>
   <File Id='SedodreamTasksDLL' Name='Tasks' 
      LongName='Sedodream.MSBuild.Tasks.dll'
      Source='Sedodream.MSBuild.Tasks.dll' Vital='yes' DiskId='1'/>
   
   <File Id='SedodreamTASKS' Name='SeTasks' LongName='Sedodream.tasks'
      Source='Sedodream.tasks' Vital='yes' DiskId='1'/>
   <!-- Merge in the Xsd for the custom tasks we have created to have 
      IntelliSense -->
   <XmlFile Id='SedoSche' 
      File='C:\Program Files\Microsoft Visual Studio 8\
               Xml\Schemas\1033\Microsoft.Build.xsd' 
      Action='createElement' ElementPath='//xs:schema' 
      Name='xs:include' Permanent='no' Sequence='0'/>
   <XmlFile Id='SedoSch2' 
      File='C:\Program Files\Microsoft Visual Studio 8\
               Xml\Schemas\1033\Microsoft.Build.xsd'
      Action='setValue' 
      ElementPath='//xs:schema/xs:include[\[]not(@schemaLocation)[\]]'
      Name='schemaLocation' Value='MSBuild\Sedodream.MSBuild.xsd' 
      Permanent='no' Sequence='1' />
</Component>

Das „XmlFile“-Element ändert eine auf dem Zielcomputer vorhandene XML-Datei. Es fügt ein neues Element in die Datei Microsoft.Build.xsd ein, das es ermöglicht, für die im Beispielprojekt enthaltenen benutzerdefinierten MSBuild-Tasks IntelliSense zu aktivieren.
Features sind die Elemente, die der Benutzer für die Installation auswählt, wenn Ihr Installationsprogramm eine Benutzeroberfläche aufweist. Im Beispielprojekt werden die folgenden „Feature“-Deklarationen verwendet:
<Feature Id='Complete' 
   Title='Sedodrem Core' Description='MSBuild libraries'
   Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR'>
   <ComponentRef Id='TaskBinFiles'/>
   <ComponentRef Id='NUnitFiles'/>

   <Feature Id='Samples' Title='Samples' 
      Description='Contains samples of Sedodream usage'
      Display='expand' Level='100' ConfigurableDirectory='INSTALLDIR'>
      <ComponentRef Id='Samples'/>
   </Feature>
</Feature>
Wie viele andere WiX-Elemente kann das „Feature“-Element zahlreiche Attribute aufweisen, von denen die am häufigsten verwendeten Attribute in Abbildung 4 beschrieben werden.

Attributname Beschreibung
Id Ein Bezeichner, der verwendet werden kann, um auf das Feature zu verweisen.
ConfigurableDirectory Ermöglicht Ihnen, anhand einer Eigenschaft das Installationsverzeichnis einzustellen, das von einem Benutzer mit einer Benutzeroberfläche festgelegt werden kann.
Description Eine Beschreibung des Features, die für einen Benutzer mit einer Benutzeroberfläche angezeigt wird.
Level Legt die Installationsebene für das Feature fest. Der Wert Null bedeutet, dass das Feature nicht installiert wird. Werte, die ungleich Null und kleiner oder gleich dem Wert von „INSTALLLEVEL“ sind, bedeuten, dass das Feature installiert wird.
Title Ein Titel für das Feature, der in der Liste der Features angezeigt wird, wenn eine Benutzeroberfläche verwendet wird.
In diesem WiX-Codeausschnitt enthält das Hauptfeature „Complete“ einen Verweis auf zwei Komponenten und ein Unterfeature. Die Unterfeature-Verweise werden durch ein untergeordnetes „Feature“-Element eingerichtet. Die WiX-Quelldatei kann so viele untergeordnete „Feature“-Elemente wie nötig enthalten. Für den Komponentenverweis wird ein „ComponentRef“-Element verwendet. Wenn Sie das „ComponentRef“-Element verwenden, müssen Sie denselben ID-Wert wie in dem „Component“-Element angeben, auf das Sie verweisen möchten.

WiX und MSBuild
In den neuesten Versionen für WiX 2 finden Sie eine Datei namens wix.targets. Diese enthält Definitionen, die Ihnen dabei helfen, in MSBuild WiX-Versionen zu erstellen. Das WiX-Team hat einen Satz von MSBuild-Tasks erstellt, auf die in der Datei wix.targets verwiesen wird. Diese Tasks sind alle in der Assembly WiXTasks.dll enthalten und werden in Abbildung 5 zusammengefasst.

Name Beschreibung
Candle Erstellt durch Aufrufen des WiX-Compilers die Zwischendarstellung der WiX-Quelldateien.
Lit Erstellt aus der Zwischendarstellung eine WiX-Bibliothek.
Light Erstellt durch Aufrufen des WiX-Linkers aus der Zwischendarstellung das endgültige Installationsprogramm.
Das Verfahren, mit dem Sie Installationsprogramme erstellen, ähnelt weitgehend der Methode, mit der heute verwaltete Projekte erstellt werden. Wenn Sie beispielsweise ein C#-Projekt erstellen, enthält Ihre Projektdatei alle zum Erstellen des Projekts erforderlichen Eigenschaften und Elemente. Alle Schritte zum Erstellen des Projekts sind jedoch in der Datei Microsoft.CSharp.targets enthalten. Diese Datei wird mit dem „MSBuild Import“-Element in die Projektdatei eingebunden. Bei WiX definiert Ihre Projektdatei alle notwendigen Eigenschaften und Elemente und schließt dann die Datei wix.targets ein, um die Logistik für die Erstellung der endgültigen Pakete zu definieren. Abbildung 6 zeigt eine Liste der Mindestdeklarationen, die benötigt werden, um erfolgreich mit wix.targets ein Installationsprogramm zu erstellen.

Name Beschreibung
ToolPath Enthält den Pfad zum WiX-Installationsverzeichnis. Dies ist das Verzeichnis, in dem sich die Datei wix.targets befindet.
OutputName Pfad und Name der Ausgabe, die erstellt wird. Der Name sollte keine Erweiterung umfassen, weil er sowohl für die Zwischendarstellung als auch für die endgültige Paketdatei verwendet wird. Diese Eigenschaft entspricht dem Argument „OutputFile“ von Candle.exe und Light.exe.
OutputType Gibt an, worum es sich bei der endgültigen Datei handeln soll. Die möglichen Werte sind „package“, „module“, „library“ und „object“. Dadurch wird bestimmt, ob der Light- oder Lit-Task aufgerufen wird und welche Erweiterung die endgültige Datei haben wird. Da wir ein Installationsprogramm erstellen möchten, geben wir für diese Eigenschaft den Wert „package“ an.
Compile MSBuild-Element, das die zu kompilierenden WiX-Quelldateien enthält.
BaseInputPath Dient als Eingabepfad für Quelldateien. Obwohl diese Deklaration technisch nicht unbedingt erforderlich ist, sollte sie in den meisten Fällen verwendet werden. Andernfalls müssen Sie sicherstellen, dass msbuild.exe im richtigen Verzeichnis aufgerufen wird.
Zum Erstellen des Installationsprogramms müssen Sie eine MSBuild-Datei erstellen, um die erforderlichen Eigenschaften und Elemente zu definieren. In diesem Fall gebe ich der Datei den Namen „SedodreamMSI.wproj“. Diese Datei wird in Abbildung 7 gezeigt.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
  <!--===============================================================
    These must be declared BEFORE the statement 
    that imports the wix.targets file
   ==================================================================-->
  <PropertyGroup>
    <!-- The location pointing where WiX is installed -->
    <ToolPath>C:\Data\Development\WiX\</ToolPath>
    <!-- Required Property by WiX -->
    <OutputName Condition="$(OutputName)==''" >
      $(Configuration)Sedodream</OutputName>
    <!-- Required property by WiX -->
    <OutputType 
      Condition="$(OutputType)==''" >package</OutputType>
    <!-- Input path to source files  -->
    <BaseInputPath  Condition="$(BaseInputPath)==''">
      $(PackageRoot)$(Configuration)</BaseInputPath>
  </PropertyGroup>

  <ItemGroup>
    <!-- Required WiX item.
      Files in this item are sent to the Candle tool.
    -->
    <Compile Include="$(BaseInputPath)\Sedodream.wxs"/>
  </ItemGroup>

  <Import Project="$(ToolPath)wix.targets"/>
</Project>

Wie Sie sehen können, ist diese MSBuild-Datei nicht sehr umfangreich. In ihr werden lediglich einige Eigenschaften und ein einziges Element definiert. In dieser Datei wird davon ausgegangen, dass eine WiX-Quelldatei namens „Sedodream.wxs“ erstellt und im Verzeichnis „BaseInputPath“ abgelegt wurde. Darüber hinaus werden in dieser WiX-Datei einige Speicherorte vorausgesetzt, an denen Dateien abgelegt wurden. Gehen wir einfach davon aus, dass sich alle diese Dateien dort befinden, wo sie hingehören.
Zum Erstellen des Installationsprogramms muss ich für die Projektdatei MSBuild aufrufen. Dazu öffne ich die Eingabeaufforderung von Visual Studio 2005, wechsle zum Speicherort der Datei und führe folgenden Befehl aus:
msbuild Sedodream.wproj
Die Ausgabe ist in Abbildung 8 zu sehen.
Da ich für die Ausführung kein Ziel angegeben habe, wurden die Standardziele (DefaultTargets) ausgeführt. Dies wurde in der Datei Sedodream.wproj als Build definiert. Anhand der Ausgabe können Sie sehen, dass im Ordner „bin\Release“ eine Datei namens „ReleaseSedodream.msi“ erstellt wurde. Jetzt können Sie das Installationsprogramm ausführen, um sicherzustellen, dass es wie erwartet funktioniert. Bevor wir mit unserem Hauptziel fortfahren, gehe ich im nächsten Abschnitt auf das fortgeschrittene MSBuild-Thema der Batchverarbeitung ein. Dies ist notwendig, weil die Batchverarbeitung während des gesamten Prozesses der Erstellung und Freigabe des Produkts verwendet wird.
Abbildung 8 Ausgabe bei der Erstellung des WiX-Installationsprogramms mit MSBuild (Klicken Sie zum Vergrößern auf das Bild)

MSBuild-Batchverarbeitung
Bei der Verwendung von MSBuild werden Sie feststellen, dass keine Schleifenkonstrukte vorkommen. Statt einer Schleife können Sie die Batchverarbeitung nutzen, bei der Elemente mithilfe von Elementmetadaten in verschiedene Kategorien aufgeteilt werden, die aus einem oder mehreren Elementen bestehen und als „Batches“ oder „Buckets“ bezeichnet werden. Sobald die Elemente getrennt wurden, können Sie jeden Batch durchlaufen. Diese Vorgehensweise wird während des gesamten Buildprozesses verwalteter Projekte verwendet. Es gibt zwei weit gefasste Kategorien der Batchverarbeitung: Task-Batchverarbeitung und Ziel-Batchverarbeitung. Wenn Sie bei der Task-Batchverarbeitung einen Task ausführen, bestimmt das MSBuild-Modul, welche Buckets erstellt werden müssen, und führt den Task für diese Buckets aus. Um die Task-Batchverarbeitung zu verwenden, stellen Sie dem Task die Metadaten eines Elements zur Verfügung.
Zur Veranschaulichung der Batchverarbeitung stellen Sie sich das folgende Szenario vor. Sie müssen einen Satz von Dateien von einem Speicherort an einen oder mehrere andere Speicherorte kopieren und haben dazu zwei Möglichkeiten: Sie können die Dateien mithilfe einzelner Copy-Tasks an jeden Speicherort kopieren oder die Batchverarbeitung verwenden, um dies mit einem einzigen Copy-Element zu bewerkstelligen. Das erste Verfahren ist zwar einfach, stellt aber keine gute Lösung dar, weil es schwierig zu verwalten ist. Zur Veranschaulichung des zweiten Verfahrens betrachten Sie diese sehr einfache MSBuild-Datei namens batching01.proj:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" 
         DefaultTargets="CopyFiles">
  <ItemGroup>
    <SourceFiles Include="*.txt"/>
    <Dest Include="One;Two;Three;Four;Five"/>
  </ItemGroup>

  <Target Name="CopyFiles">
    <Copy SourceFiles ="@(SourceFiles)" 
         DestinationFolder="%(Dest.FullPath)"/>
    <Message Text="Fullpath: %(Dest.FullPath)"/>
  </Target>
</Project>
In dieser Datei deklariere ich ein „SourceFiles“-Element, das alle Dateien mit der Erweiterung „.txt“ enthalten soll, sowie ein „Dest“-Element, in dem fünf Speicherorte angegeben sind. Im „CopyFiles“-Ziel verwende ich den Copy-Task, um das „SourceFiles“-Element an jeden Bestimmungsort zu kopieren. Da ich die „FullPath“-Metadaten an den Copy-Task übergebe, erstellt das MSBuild-Modul einen Batch aller „Dest“-Elemente, die unterschiedliche „FullPath“-Werte besitzen. Anschließend wird der Copy-Task für jeden Batch aufgerufen. Dies ist die Task-Batchverarbeitung. Zur Ausführung kann ich in dem Verzeichnis, das die Datei batching01.proj enthält, den folgenden Befehl aufrufen:
msbuild.exe batching01.proj
Nun zeige ich Ihnen, wie dasselbe Ergebnis mit der Ziel-Batchverarbeitung erreicht wird. Da bei der Ziel-Batchverarbeitung die Batches auf der Grundlage der Werte von „Inputs“ und „Outputs“ des Ziels erstellt werden, muss ich für diese Werte die Metadaten eines Elements übergeben:
<Project xmlns="http://schemas.
  microsoft.com/developer/msbuild/2003" 
         DefaultTargets="CopyFiles">
  <ItemGroup>
    <SourceFiles Include="*.txt"/>
    <Dest Include="One;Two;Three;Four;
                   Five"/>
  </ItemGroup>

  <!-- These targets demonstrate target 
       batching -->
  <Target Name="CopyFiles" 
          Inputs="@(SourceFiles)" 
          Outputs="%(Dest.FullPath)">
    <Copy SourceFiles="@(SourceFiles)" 
          DestinationFolder=
          "%(Dest.FullPath)"/>
  </Target>
</Project>
Die Ziel-Batchverarbeitung wird von den Werten von „Inputs“ und „Outputs“ der Ziele gesteuert. Da „Outputs“ die „Dest.FullPath“-Deklaration enthält, wird das Ziel einmal pro Bucket aufgerufen. Dieser Vorgang ähnelt der Task-Batchverarbeitung, nur wird hier der Inhalt des gesamten Ziels einmal pro Batch wiederholt, wie in Abbildung 9 dargestellt.
Abbildung 9 MSBuild-Ausgabe bei der Ziel-Batchverarbeitung (Klicken Sie zum Vergrößern auf das Bild)
An dieser Ausgabe können Sie erkennen, dass das Ziel „CopyFiles“ einmal pro Batch für das „Dest“-Element aufgerufen wurde. Im Beispiel zur Automatisierung des Build-/Verpackungsprozesses werde ich die Batchverarbeitung verwenden, um für jede Kombination von Konfigurationsplattform und -typ ein Installationsprogramm zu erstellen.

Automatisieren von Builds und Verpackungsvorgängen
In jeder Organisation sind die Schritte zum Erstellen und Verpacken ein wenig verschieden. In einigen Fällen liegt dies an den verwendeten Technologien und in anderen an den organisatorischen Anforderungen. Ich habe einen Satz der Schritte zusammengestellt, von denen ich glaube, dass sie vielen Organisationen gute Dienste leisten werden, auch wenn Ihr spezielles Szenario möglicherweise eine Feinabstimmung erfordert. Ein automatisierter Build- und Verpackungsprozess dient zwei Zielen: der Entwicklung eines wiederholbaren öffentlichen Buildprozesses und der Erstellung eines automatisierten wiederholbaren Verpackungsprozesses. Es gibt drei grundlegende Schritte: Abrufen der neuesten Quellen, Erstellen und Verpacken. Diese bestehen jedoch aus vielen kleineren Schritten, wie in Abbildung 10 dargestellt.
Abbildung 10 Buildprozess (Klicken Sie zum Vergrößern auf das Bild)
Ein sehr wichtiger Schritt ist das Bezeichnen der Quellen. Dies ist eine Voraussetzung, weil Sie ohne diesen Schritt den Prozess nicht wiederholen können. Wenn Sie einen Build erstellen, der an die Produktion gesendet werden soll, müssen Sie in der Lage sein, genau denselben Build erneut zu erstellen, damit der bereitgestellte Code beibehalten wird. Dieser Schritt ist je nach dem Tool, das Sie als Quellcode-Verwaltungsprogramm verwenden, anders beschaffen. Es sind viele verschiedene MSBuild-Tasks zum Download verfügbar, mit denen Sie dies für verschiedene Quellcode-Verwaltungsprogramme erreichen können. Ausführlichere Informationen hierzu finden Sie in der Randleiste „MSBuild- und WiX-Ressourcen“.

Anatomie des MSBuild-Skripts
In diesem Abschnitt stelle ich das MSBuild-Skript vor, das zum Erstellen und Verpacken der Beispielanwendung verwendet werden kann. Die Gesamtstruktur ist der Vorgehensweise zum Erstellen verwalteter Projekte sehr ähnlich. Sie definieren hierbei eine Projektdatei, die die zu verarbeitenden Elemente enthält, und importieren eine andere Projektdatei namens „Sedodream.Package.targets“, die den Prozessablauf definiert. Stellen Sie sich diese Prozesse als Sätze vor. Ihre Verpackungsprojektdatei definiert die Substantive und die importierte Datei die Verben. Ich erstelle das Projekt, indem ich den MSBuild-Task auf die Projektmappendatei anwende. Zur Erstellung des Installationsprogramms wird die vorherige Projektdatei SedodreamMSI.wproj verwendet.
Wenn Sie über einen Prozess verfügen, der in separate Elemente unterteilt ist, müssen diese Elemente irgendwie miteinander verbunden werden. Dies wird erreicht, indem ein gemeinsamer Satz von Eigenschaften, Elementen und Zielen definiert wird (siehe Abbildung 11).

Name Beschreibung
SolutionFilePath Eigenschaft, die den Speicherort der Projektmappendatei definiert, die zum Erstellen des Produkts verwendet wird.
PackageRoot Eigenschaft, die definiert, wo die Projekte erstellt werden. Der angegebene Speicherort dient als temporärer Buildspeicherort für WiX.
WiXSourceFiles Element, das alle WiX-Dateien enthält, die zum Kompilieren an das Candle-Tool gesendet werden sollen.
AllConfigurations Element, das alle Konfigurationen enthält, für die dieser Prozess erstellt werden soll. Jede Konfiguration sollte Metadatenwerte sowohl für „FlavorToBuild“ als auch für „PlatformToBuild“ enthalten. Hierzu eine Beispieldeklaration:
<AllConfigurations Include="Release|Any CPU"> <FlavorToBuild>Release</FlavorToBuild> <PlatformToBuild>Any CPU</PlatformToBuild> </AllConfigurations>
OtherFiles Optionales Element, das andere Dateien enthält, die in das Paketverzeichnis kopiert werden sollen. Diese Elemente sollten „Destination“-Metadaten enthalten. Diese Metadaten definieren den relativen Pfad des Ziels zum Paketstamm. Hierzu eine Beispieldeklaration:
<OtherFiles Include= "..\Sedodream.MSBuild.Tasks\SampleTargets\**\*"> <Destination>Samples\</Destination> </OtherFiles>
In diesem Prozess wird die Projektmappe erstellt, und die WiX-Quelldateien werden in dasselbe Verzeichnis kopiert. Sie sollten Ihre WiX-Quelldateien unter Berücksichtigung dieses Pfads definieren. Unabhängig davon, wo sich Ihre WiX-Dateien in der Quellcodeverwaltung befinden, werden sie aus demselben Verzeichnis erstellt, in dem das Produkt erstellt wird. Andere erforderliche Dateien werden vom „CopyFilesForPackaging“-Ziel ebenfalls in dieses Verzeichnis kopiert.
In diesem Prozess definiert zudem die Eigenschaft „PackageRoot“, wo die Projekte erstellt werden und wo WiX die Installationsprogramme erstellt. In unserem Beispiel liegt das Verzeichnis auf derselben Ebene wie die Projektmappendatei, es könnte sich aber auf dem Buildcomputer an jeder beliebigen Stelle befinden. Ich rate allerdings davon ab, als Speicherort eine Netzwerkfreigabe zu verwenden.
Die Hauptziele, die in der Datei Sedodream.Package.targets deklariert werden, sind in Abbildung 12 dargestellt. Die Abhängigkeiten aller dieser Ziele (und vieler anderer in der Zieldatei) werden in Eigenschaften definiert, damit Sie die Folge der Ereignisse im Prozess vollständig ändern können. Wenn Sie beispielsweise einen Schritt einfügen müssen, bevor das „Build“-Ziel Ihre Projektdatei ausführt, könnten Sie einfach nach der Import-Anweisung für die Datei Sedodream.Package.targets den folgenden Code einfügen:
<BuildDependsOn>
    CustomBeforeBuild;
    $(BuildDependsOn);
</BuildDependsOn>

Name Beschreibung
Package Einziges Ziel in der „DefaultTargets“-Liste – wird aufgerufen, um den gesamten Prozess auszuführen.
Build Erstellt die Projektmappendatei. Die Ausgabe dieses Builds wird in das richtige Verzeichnis weitergeleitet, indem beim Aufrufen des MSBuild-Tasks die Eigenschaft „OutputPath“ überschrieben wird.
CopyFilesForPackaging Kopiert alle benötigten Dateien, die nicht erstellt werden, in die Buildverzeichnisse.
DeployPackage Wird gegen Ende des Prozesses aufgerufen. In unserem Beispiel ist dieses Ziel leer, aber bei Ihrer Implementierung könnten Sie beschließen, das Ziel zu überschreiben, um Ihre Pakete zur Qualitätssicherung oder an Ihr Bereitstellungsteam zu senden.
Clean Beseitigt das Durcheinander, das von den anderen Zielen hinterlassen wurde.
Weil die Eigenschaft „BuildDependsOn“ neu definiert wurde, fügen Sie dadurch den Schritt „CustomBeforeBuild“ effektiv in den aktuellen Prozess ein, ohne die vorhandene Zieldatei zu ändern. Ausführlichere Informationen zu diesem Thema finden Sie in meinem Artikel vom Juni 2006, auf den ich bereits weiter oben hingewiesen habe.
Bevor wir das Installationsprogramm erstellen, lassen Sie uns einen kurzen Blick auf das „CoreBuild“-Ziel werfen, das die Projektmappe erstellt (siehe Abbildung 13). Dadurch erfahren Sie, wie die Ziel-Batchverarbeitung zum Erstellen der Projektmappe für jede definierte Konfiguration verwendet wird.
<Target Name="CoreBuild"
   Inputs="%(AllConfigurations.PlatformToBuild);
           %(AllConfigurations.FlavorToBuild)"
   Outputs="%(AllConfigurations.PlatformToBuild);
            %(AllConfigurations.FlavorToBuild)" >
   <Message Text="Building for Flavor/Platform:
       %(AllConfigurations.FlavorToBuild)/
       %(AllConfigurations.PlatformToBuild)"/>
   <MSBuild Projects="@(SolutionFile)"
      Targets="Build"
      Properties="OutputPath=$(PackageRoot)
         %(AllConfigurations.FlavorToBuild);
      Configuration=%(AllConfigurations.FlavorToBuild);
      Platform=%(AllConfigurations.PlatformToBuild)">
      <Output ItemName="OutputFiles" TaskParameter="TargetOutputs"/>
   </MSBuild>
</Target>

Als Erstes werden Ihnen „Inputs“ und „Outputs“ auffallen. Hierbei handelt es sich um die Metadatenwerte für das Element „AllConfigurations“, das bewirkt, dass das Ziel einmal pro Kombination aus Plattform und Typ ausgeführt wird. Danach wird der MSBuild-Task aufgerufen, um die Projektmappendatei mit allen entsprechenden Eigenschaften zu erstellen. Mit „OutputPath“ wird ein Ausgabepfad bereitgestellt, weil ich die Ausgabe in ein anderes Verzeichnis umleiten möchte.

Letzte Schritte
Bisher habe ich beschrieben, wie mit WiX ein Installationsprogramm erstellt wird, und einige fortgeschrittene MSBuild-Konzepte sowie die Struktur des MSBuild-Verpackungsskripts behandelt. Das einzige Thema, das in Bezug auf die Erstellung des Installationsprogramms für das Beispielprojekt noch nicht angesprochen wurde, ist das Erstellen der MSBuild-Projektdatei, um die erforderlichen Eigenschaften zu definieren. Dies ist erstaunlich einfach. Zuletzt werde ich Ihnen zeigen, wie Sie den Prozess an Ihre individuellen Anforderungen anpassen können.
Abbildung 14 zeigt die MSBuild-Datei Sedodream.Package.dproj, die den Prozess für das Beispielprojekt steuert. Wie bereits erwähnt, besteht die Hauptfunktion dieser Projektdatei in der Beschreibung der zu erstellenden Elemente. Die importierte Datei Sedodream.Package.targets enthält alle Details dazu, wie das Endprodukt zu erstellen ist, und Sie können den Prozess auch nach Bedarf anpassen, indem Sie Schritte in den Buildprozess einfügen.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" 
         DefaultTargets="Package">

   <!-- Required properties by the deployment.targets file -->
   <PropertyGroup>
      <SolutionFilePath>..\Sedodream.MSBuild.sln</SolutionFilePath>
      <PackageRoot>..\Package\</PackageRoot>
   </PropertyGroup>

   <ItemGroup>
      <WiXSourceFiles Include="Sedodream.wxs"/>
      <OtherFiles Include="..\Sedodream.MSBuild.Tasks\
         SampleTargets\**\*">
         <Destination>Samples\</Destination>
      </OtherFiles>
      <!-- Copy the license to the BaseSearchPath so it can 
         be included into the installer -->
      <OtherFiles Include="License.rtf">
         <Destination></Destination>
      </OtherFiles>
      <!-- Copy the custom installer bitmaps -->
      <OtherFiles Include="bitmaps\dlgbmp.bmp">
         <Destination>bitmaps\</Destination>
      </OtherFiles>
   </ItemGroup>

   <!-- Define all the configurations that you want to build here -->
   <ItemGroup>
      <AllConfigurations Include="Debug|x86">
         <FlavorToBuild>Debug</FlavorToBuild>
         <PlatformToBuild>Any CPU</PlatformToBuild>
      </AllConfigurations>
      <AllConfigurations Include="Release|Any CPU">
         <FlavorToBuild>Release</FlavorToBuild>
         <PlatformToBuild>Any CPU</PlatformToBuild>
      </AllConfigurations>
   </ItemGroup>

   <PropertyGroup>
      <DropLocation>C:\Data\Drops\MSBuild-Wix\</DropLocation>
   </PropertyGroup>
   
   <!-- Import the deployment target to do all the work for us -->
   <Import Project="Sedodream.Package.targets"/>
</Project>

Wie Sie sehen, wird in dieser Projektdatei die erforderliche Eigenschaft „SolutionFilePath“ definiert. Hierbei handelt es sich um den Pfad zur Projektmappendatei, die zum Erstellen des Produkts verwendet wird. Sie können auch die Deklaration des Elements „WixSourceFiles“ sehen, das den Satz der Dateien umfasst, die zum Kompilieren Ihres Installationsprogramms verwendet werden. Hierbei handelt es sich um die Dateien, die zum Kompilieren an das Tool Candle.exe gesendet werden.
Außerdem finden Sie hier die Deklaration des Elements „OtherFiles“, das Dateien enthält, die an den Speicherort kopiert werden sollen, der zum Erstellen des Installationsprogramms verwendet wird. Diese Dateien werden in den mit „BaseInputPath“ angegebenen Pfad kopiert, der später verwendet wird, um das Installationsprogramm zu erstellen. Sie können sie mithilfe des Metadatenwerts von „Destination“ an einen beliebigen Speicherort unterhalb dieses Pfads kopieren. In diesem Beispiel kopiere ich benutzerdefinierte Bitmaps, die zum Generieren des Installationsprogramms verwendet werden sollen, in den Ordner „bitmaps“. Wenn das Tool Light.exe aufgerufen wird, um die MSI-Datei zu erstellen, erfasst es zuerst die in diesem Verzeichnis enthaltenen Dateien, bevor es auf Standarddateien im lokalen Verzeichnis zugreift.
Beachten Sie die Deklaration der Eigenschaft „DropLocation“. Durch Deklarieren dieser Eigenschaft werden alle in dem mit „PackageRoot“ angegebenen Verzeichnis in ein Verzeichnis kopiert, das durch „DropLocation“ festgelegt wird. Es ist nützlich, die Binärdateien und die Installationsprogrammdateien am selben Speicherort abzulegen, falls Sie schnell einige Änderungen vornehmen müssen.
Um den Build und das Paket anzuzeigen, die für das Beispielprojekt ausgeführt werden, öffnen Sie eine Eingabeaufforderung von Visual Studio 2005, und navigieren Sie zum Projektbereitstellungsordner. Führen Sie danach den folgenden Befehl aus:
msbuild SedodreamPackage.dproj
Ein großer Teil der Ausgabe wird in der Eingabeaufforderung generiert, und Sie finden unter dem Verzeichnis „Package“ zwei Verzeichnisse namens „Debug“ und „Release“. Jedes dieser Verzeichnisse enthält das generierte Installationsprogramm und die zugehörigen Dateien.

Anpassen des Prozesses
Wenn Sie diesen Prozess für Ihr Produkt einrichten, müssen Sie eine ähnliche Datei wie die Datei SedodreamPackage.dproj erstellen. Sie können Ihre Anpassungen direkt in diese Datei einfügen. Es sollte nicht erforderlich sein, die Datei Sedodream.Package.targets selbst zu ändern. Wenn Sie Schritte einfügen möchten, können Sie nach dem zuvor beschriebenen Verfahren vorgehen. Wenn Sie Ziele neu definieren möchten, können Sie sie einfach überschreiben, indem Sie nach der Import-Anweisung das Ziel neu deklarieren. So wird beispielsweise in der Datei Sedodream.Package.targets das Ziel „GetLatest“ definiert, aber dieses Ziel ist leer. In diesem Ziel sollten Sie die erforderlichen Tasks zum Abrufen der neuesten Quellen aus Ihrem Repository ablegen. Im Folgenden wird kurz skizziert, wie dies bei Ihrem Projekt aussehen könnte:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" 
       DefaultTargets="Package">
   <!-- Your properties and items defined here -->

   <!-- Import the deployment target to do all the work for us -->
   <Import Project="Sedodream.Package.targets"/>

   <!-- Place overriding customizations after this point -->
   
   <Target Name="GetLatest">
      <Message Text="Getting latest sources"/>
      <!-- Insert tasks to get latest from source control -->
   </Target>
</Project>
Abbildung 15 enthält eine Liste wichtiger Ziele, die in der Datei Sedodream.Package.targets definiert sind. Sie können diese Ziele an Ihre Bedürfnisse anpassen.
Als letztes wichtiges Thema bleibt jetzt noch zu behandeln, wie dieser Prozess automatisiert werden kann – was jetzt äußerst einfach ist. Sie müssen lediglich die Ausführung der Datei MSBuild.exe für die Bereitstellungsdatei (in diesem Fall die Datei SedodreamPackage.dproj) automatisieren. Das entsprechende Verfahren hängt davon ab, wie Sie derzeit öffentliche Builds generieren. Wenn Sie mit Visual Studio Team Foundation Server arbeiten, können Sie zu diesem Zweck Team Build verwenden. Sie können den MSBuild-Task in Ihrer Datei TFSBuild.proj verwenden. Wenn Sie andere Technologien einsetzen, ist es wahrscheinlich, dass diese systemeigene Unterstützung für MSBuild bieten. Wenn Sie keine der genannten Technologien nutzen, können Sie den Windows-Planer für die Ausführung auf Terminbasis verwenden.

Schlussbemerkung
In diesem Artikel haben Sie erfahren, wie Sie mithilfe des WiX-Toolsets einen automatisierten Build- und Verpackungsprozess erstellen können. Wenn Sie diese Integration abgeschlossen haben, können Sie Ihre Produkte auf zuverlässige und wiederholbare Weise erstellen und verpacken. Dies ist bei Anwendungen, die zur Bereitstellung gesendet werden, sehr wichtig.
Wenn Sie als Installationstechnologie nicht WiX verwenden, können Sie dennoch die hier beschriebenen Konzepte und Dateien mit einigen Änderungen verwenden. Alle mir bekannten Installationstechnologien unterstützen eine bestimmte Art der Befehlszeilenausführung. Wenn Sie Ihre Installationsprogramme mit diesen anderen Technologien erstellen, arbeiten Sie mit der entsprechenden Befehlszeile. Es gibt auch einige Installationstechnologien von Drittanbietern, in deren Lieferumfang MSBuild-Tasks und -Ziele enthalten sind, die Ihnen beim Einrichten dieses Prozesses helfen können.

Sayed Ibrahim Hashimi besitzt einen Abschluss der University of Florida in Computer Engineering. Er ist Entwickler und Architekt und lebt in Jacksonville, Florida. Sayed ist Experte in der Finanz-, Schulungs- und Inkassobranche. Er arbeitet bei Latitude Software mit .NET-Technologien.

Page view tracker