Microsoft Installer (MSI)

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.

Microsoft Installer (MSI)

Beitragvon Krishty » 01.09.2018, 00:42

(Ich benutze das hier einfach mal als Sammelthread, damit ich die ganzen Nachschlagwerke wiederfinde. Die Information ist unglaublich weit verstreut.)

„Warum nicht einfach eine Batch-Datei?“ – Einführung Roaming Profiles

In Firmennetzwerken ist es üblich, dass sich jeder Benutzer an jedem beliebigen Computer einloggen kann. Ihr geht also ins Nebenbüro, loggt euch am Rechner eures Kollegen an (mit eurem eigenen Namen und Passwort natürlich) und wie von Zauberhand sind eure gewohnten Anwendungen da und eure Dateien in den gewohnten Ordnern. Habt ihr euch schonmal gefragt, wie das geht? Dafür sind Roaming Profiles da.

Im Grunde synchronisiert Windows euer Benutzerprofil bei jedem Abmelden mit eurem Domain Server. Diese Synchronisierung beinhaltet auch die Registry, Startmenüs, usw. Bei der Anmeldung werden diese Informationen wieder heruntergeladen.

Das ist ein Grund, warum man sich beim Speichern von Benutzerdaten entscheiden muss, ob sie nun nach %APPDATA% (C:\Users\Krishty\AppData\Roaming) oder nach %LOCALAPPDATA% (C:\Users\Krishty\AppData\Local) gehen: Ersteres wird mit dem Domain Server synchronisiert, dort sollten also rein benutzerspezifische Einstellungen landen. Letzteres wird nicht synchronisiert; dort sollten also maschinenabhängige Benutzereinstellungen landen. Am Beispiel eines Spieles, weil wir ja Spiele entwickeln: Nach %APPDATA% schreibt ihr den Schwierigkeitsgrad und die aktuellen Spielstände / Achievements (sollen auf jedem Rechner, auf dem der Benutzer zockt, identisch sein). Nach %LOCALAPPDATA% schreibt ihr die Grafikeinstellungen, Lautstärke, und die Lizenz (falls sie von der Maschine abhängt), denn auf dem langsamen Laptop möchte der Benutzer sicher andere Grafikeinstellungen haben als auf dem starken Desktoprechner und ffffuuuu falls die automatisch synchronisiert würden.

Roaming User Profiles haben jede Menge Probleme und Details, und sie werden langsam durch Cloud Computing verdrängt. Aber das ist hier egal. Kann man in der MSDN nachlesen.

Bis hier hin können mir die meisten wahrscheinlich noch folgen. Die viel interessantere Frage ist aber: Wenn nur ausgewählte Benutzerordner und die Registry synchronisiert werden, sind dann die Shortcuts im Startmenü nicht kaputt? Das Spiel ist doch sicher nicht installiert, wenn ich mich auf einem anderen Rechner einlogge? Oder werden die Gigabyte-großen Programmdateien beim Login ebenfalls synchronisiert?

Die Antwort ist das eigentliche Kernthema: Advertising.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 01.09.2018, 00:59

Einführung in Advertising und on-Demand-Setup

Wie kann es sein, dass auf jedem Rechner im Firmennetzwerk jede Software verfügbar ist?

Microsoft hat sich überlegt, dass es wohl nicht so schlau wäre, jede Software komplett auf jeden Computer zu kopieren. Die langsamen Rechner in der Buchhaltung könnten SAP nicht starten, weil die Festplatte vom Visual Studio der Entwickler zugemüllt ist, das sie aber gar nicht benutzen.

Stattdessen hat Microsoft sich für Advertising und on Demand-Setups entschieden. Hier wird es abgefahren. Das ist eine Möglichkeit, wie das System vorgaukeln kann, eine Anwendung wäre installiert – aber tatsächlich findet das Setup erst statt, sobald man auf die Anwendung zugreift.

Es funktioniert folgendermaßen: Wenn Office auf einen Rechner advertised wird, installiert das Setup alle Registry-Schlüssel. Es legt außerdem alle Verknüpfungen im Startmenü an und trägt Word als Standardprogramm für .docx-Dateien ein. Es werden aber keine Programmdateien von Office auf das Zielsystem kopiert. Stattdessen wird dort nur vermerkt, wo das Office-Setup zu finden ist (typischerweise auf dem Domain Server).

Erst, wenn ein Benutzer tatsächlich einen der Einsprungspunkte – Verknüpfung im Startmenü, Doppelklick auf .docx, usw – auswählt, springt das Setup an und installiert die benötigte Komponente. Programme, die nicht ausgeführt werden, verbrauchen also kaum Platz auf dem Rechner. Und wenn mein Kollege aus der Buchhaltung an meinen Rechner kommt, sich auf sein Konto einloggt und mir eine Word-Datei zeigt, müssen wir halt erst einmal ein Bisschen warten, bis es startet – dafür ist es aber exakt gleich eingestellt wie auf seinem eigenen Rechner (denn Registry & Co. sind synchronisiert). (Beim nächsten Start ist es sofort da, denn die neue Installation wird auch nach dem Logout auf meinem Rechner behalten.)

Beachtet auch, dass hier keine Admin-Rechte nötig waren, denn theoretisch war Office ja schon die ganze Zeit auf dem Rechner installiert!

Ebenso werden Features on Demand nachinstalliert, die man beim Setup von Office abgewählt hat. Wer kein Excel wollte, aber auf eine .xlsx-Datei klickt, bei dem startet dann automatisch das Excel-Setup.

Genau da kommt auch viel Hass auf das Feature her: Man öffnet irgendwas, und plötzlich springt ein Setup an und installiert irgendwas. Oder – noch schlimmer – fragt nach einem Datenträger, von dem man nichts weiß. Wo diese Fehler herkommen und wie man sie vermeidet, das schreibe ich später.

Trotz dieser Unannehmlichkeiten ist das Feature für große Unternehmensnetzwerke unbezahlbar.

Nun sollte auch jedem klar sein: Mit Batch-Dateien geht das nicht. Die Komplexität, mit der z.B. Icons für Verknüpfungen verwaltet werden müssen, deren Ziele noch gar nicht auf dem System existieren, ist in dieser Kategorie nicht meisterbar. Hier kommt der Microsoft Installer ins Spiel.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 01.09.2018, 01:16

Microsoft Installer: Eierlegende Wollmilchsau

Um Roaming, Advertising, und on Demand-Setups zu realisieren, hat Microsoft eine neue Systemebene geschaffen: Den Microsoft Installer, kurz MSI. Das erste Produkt, das damit installiert wurde, war Office 2000.

Weitere große Ziele, die damit verfolgt wurden:

    Transaktionales Verhalten. Eine Installation soll ein einziger, atomarer Vorgang sein. Schlägt sie fehl, muss alles wie vorher sein. Wird das Produkt deinstalliert, muss alles wie vorher sein. Keine ungültigen Systemzustände; keine Dateileichen.

    Self Repair. Wird eine Anwendung beschädigt, kann das Setup sie automatisch reparieren (aber nicht immer; Details später).

    Weiterverwertbarkeit. Admins sollen alle Anwendungen auf ihre Firma zuschneiden können (indem sie z.B. Setups mit Patches kombinieren, Logos ersetzen, Sprachen übersetzen). Das war für mich die größte Überraschung: Ihr könnt tatsächlich jede MSI-Datei jedes Programms öffnen und nach eurem Willen verändern. Wie das geht, dazu später mehr.
Alle diese Punkte übernimmt MSI ohne Zutun des Packagers (Berufsbezeichnung für die Person, die ein Setup für ein Programm entwickelt oder weiterverwertet). Man muss sich bei MSI nicht um Rollback kümmern (vollautomatisch erzeugt). Man muss sich nicht darum kümmern, ob die Deinstallation Komponenten entfernen soll, die mit anderen Programmen geteilt sind (automatisches Reference Counting). Man muss keine Dateien testen (MSI berechnet Prüfsummen und vergleicht sie mit dem Sollzustand). Advertising und on Demand-Setup sind nicht viel mehr als Flags beim Erstellen des Setups.

MSI läuft als Hintergrunddienst auf jeder Windows-Version seit der Jahrtausendwende. Klickt der Anwender auf eine Verknüpfung, prüft MSI, ob das Ziel advertised ist, und startet die Installation, falls benötigt. (Das ist die Art von Verknüpfungen, für die in den Eigenschaften kein Pfad eingetragen ist!) Fragt eine Anwendung via CoCreateInstance() nach einer COM-Komponente, die auf dem System vorhanden sein sollte, prüft MSI die Integrität und startet bei Bedarf automatisch die Reparatur, bevor sie geladen wird. Und, nicht zuletzt: Klickt der Anwender auf ein Setup, führt MSI es aus.

Wie müssen Setups aufgebaut sein, damit das funktioniert?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 01.09.2018, 01:44

Grundlegende Konzepte

MSI ist deklarativ, also komplett data-driven. Alles wird beschrieben. Es wird nichts programmiert. Man sagt gewissermaßen aus, wie das System aussehen soll, wenn die Anwendung installiert ist – wo welche Datei liegt, welchen Wert welcher Registry-Schlüssel hat – und wie dieses Ziel erreicht wird, darum kümmert MSI sich dann. Dadurch fällt es MSI auch so einfach, Rollbacks durchzuführen. Übrigens ist auch die Benutzeroberfläche, die während des Setups angezeigt wird, deklarativ beschrieben.

Ein offensichtlicher Vorteil ist Transparenz. Man kann einem MSI-Paket ansehen, welche Dateien auf der Platte landen – und es ist unmöglich, dass es die Festplatte formatiert oder meine Dateien auf einen Server hochlädt. (Abgesehen von einer Einschränkung: Custom Actions. Die sind aber geächtet. So sehr, dass das Installer-Team elf Punkte aufzählt, warum man sie niemals einsetzen sollte: Windows Installer Team Blog, Integration Hurdles for EXE Custom Actions (scrollt über die leere Seite hinaus).)

MSI speichert diese Daten in einer SQL-ähnlichen Datenbank. Zu meinem Bedauern, denn das war nicht die beste Wahl. Das Datenbankschema (also, wie die Tabellen aufgebaut sind) ist, wie wir später sehen werden, leider eher maschinen- als menschenlesbar. Aber man muss eingestehen, dass es so zumindest eine standardisierte Schnittstelle garantiert. Tatsächlich kann man MSIs fröhlich mergen und alles ist immer wohldefiniert.

Was wir Anwendung nennen, ist im MSI-Slang gewissermaßen ein Package.

Packages bestehen aus Features. Ein Spiel könnte beispielsweise aus den Features Spiel, Online-Client und Desktop-Verknüpfung bestehen. Features können optional sein, oder auch nicht.

Das war einfach. Aber Features bestehen aus Components. Die Definitionen gehen weit auseinander, aber zumindest hat Microsoft einen Leitfaden veröffentlicht, in welche Components man seine Features unterteilen sollte (Microsoft Docs, Organizing Applications into Components und Defining Installer Components):
• Define a new component for every .exe, .dll, and .ocx file. […]
• Define a new component for every file that serves as a target of a shortcut. […]
• Group all of the remaining resources into folders. All resources in each folder must ship together. […] Define a new component for every folder. […]
• […] Any registry key that points to a file should be included in that file's component.


… diese Components bestehen aus einzelnen Files, Registry Keys, und Shortcuts.

Jede Component wird durch eine GUID identifiziert. Bei dieser Aufteilung müssen wir aber noch etwas extrem wichtiges im Hinterkopf behalten: Updates und das Teilen von Components zwischen verschiedenen Packages.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 01.09.2018, 02:44

Versionierung und Shared Components

Besprechen wir, dass MSI alle Components mit GUID adressiert.

Ihr führt eine Firma ContosoGames, die eine Batterie erfolgreicher Videospiele entwickelt hat. Dabei nutzt ihr den Ogg Vorbis-Codec für Klangdateien.

Wie legt ihr vorbis.dll den Spielen bei?

  • Ins Hauptverzeichnis und ab damit! – Ist eigentlich nur eine Option für Billigspiele, die nicht gewartet werden müssen. Sollte ein Patch für eine der Bibliotheken nötig werden, müsst ihr für jedes veröffentlichte Spiel ein eigenes Patch-Paket bauen. Schließlich hat jedes Spiel eine private Kopie der Bibliothek!

  • Wir liefern die in einem eigenen Setup (also als gesonderte Packages) aus! – Gute Idee, denn so fallen Patches wirklich minimal aus. Was passiert aber, wenn der Anwender Platz auf der Platte schaffen möchte, durch die Programmliste stöbert, und Ogg Vorbis deinstalliert (er weiß ja nicht, dass es von euren Spielen benötigt wird)? Nun haben eure Spiele keinen Klang mehr!

  • Wir installieren sie in ein gesondertes Verzeichnis (Program Files\ContosoGames\soundlibs), und wenn ein Spiel aktualisiert wird, ist die Sache erledigt! – Das wäre die MSI-konforme Lösung, aber dort gibt es einiges zu beachten.

Components, die von unterschiedlichen Packages geteilt werden, nennt man Shared Components. Der große Fallstrick hierbei ist, die Identifikation zu vergeigen und sich eine Reparaturschleife einzuhandeln. Man muss folgendes unbedingt einhalten:

  1. Wenn man GUIDs an Components verteilt, müssen Shared Components in jedem Package die selbe GUID haben. Alle eure Spiele müssen also vorbis.dll mit den selben GUIDs ansprechen.

  2. Alle Updates müssen die selben GUIDs für die Components verwenden. Ausnahme: Wenn eine der Bibliotheken durch ein Update inkompatibel zu früheren Versionen wird, müsst ihr sie als neue Component mit neuem Namen an einem anderen Ort installieren (z. B. als vorbis2.dll).

  3. Shared Components müssen versioniert sein. Beim Bauen des Setups müsst ihr also eine Version angeben, von Anfang an!
Was passiert, wenn ihr das vergesst?
  1. Der Anwender installiert Space Contoso 1. Program Files\ContosoGames\soundlibs\vorbis.dll hat Version 1.0 und die GUID {AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA}.
  2. Der Anwender spielt Space Contoso 1. Alles funktioniert gut.
  3. Der Anwender installiert Space Contoso 2. Program Files\ContosoGames\soundlibs\vorbis.dll ist identisch (auch Version 1.0), ihr habt aber vergessen, die GUID zu synchronisieren – sie ist nun {BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB}.
  4. Der Anwender spielt Space Contoso 2. Alles funktioniert gut.
  5. Hey, hatte Captain Conto auch im ersten Teil schon so eine komische Frisur? Der Anwender startet Space Contoso 1, um nachzusehen.
  6. Wie wir wissen, prüft MSI vor dem Laden jeder Komponente, ob sie korrekt installiert ist. Für Program Files\ContosoGames\soundlibs\vorbis.dll ist aber nun eine GUID {BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB} von Teil 2 vermerkt!
  7. Das Setup von Space Contoso 1 startet im Hintergrund, um eine Reparatur durchzuführen. Da die CD noch im Laufwerk liegt, wird der Anwender nicht nach dem Datenträger gefragt. Aber irgendwie dauert das Starten länger als sonst!
  8. Program Files\ContosoGames\soundlibs\vorbis.dll hat nun wieder die GUID von Teil 1: {AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA}.
  9. Beim Start von Space Contoso 2 wird wieder eine Reparatur angestoßen werden, weil die GUID nicht {BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB} ist!
Das war das GUID-Beispiel. Ein Versionsbeispiel lässt sich analog herbeiführen, wenn eine Version von vorbis.dll ohne Version ausgeliefert wurde.

Falls die GUIDs identisch sind und die Components korrekt versioniert wurden, löst MSI kein Repair aus, wenn es eine neue Version vorfindet. Nach Installation eures Sound-Patches hat vorbis.dll die Version 1.1 und beide Spiele starten damit normal.

Solche Repair Loops sind berüchtigt. Unter anderem hatte Visual Studio 2010 Beta 1 eine, weil eine Visual Basic-Komponente mit Office 2007 geteilt war, und beim Advertising auf dem falschen Laufwerk landete: Setup & Install by Heath Stewart, How to work around the issue when opening Office applications repairs Visual Studio

Quintessenz: Komponenten teilen ist gut für die Wartbarkeit. Es sei denn, man weist ihnen unterschiedliche GUIDs zu. Oder vergisst, sie zu versionieren. Oder vergisst, für inkompatible Änderungen eine neue Komponente mit neuer GUID anzulegen.

Die Signifikanz der GUID für Updates sollte damit auch klar sein: Verlegt man die GUID, können Komponenten nicht mehr sauber aktualisiert werden.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 01.09.2018, 02:56

Ausflug: Warum ist C:\Windows\Installer so groß?! Kann ich das löschen?!

Wir wissen nun, dass MSI höllisch viele Aufgaben übernimmt:

  1. Aus der Deklaration eines Packages berechnet es, was am System zu ändern ist, um den Zielzustand herzustellen (Installation).

  2. Während dieser Installation führt es Buch über ein mögliches Rollback, um bei Fehlschlag den Ausgangszustand wiederherzustellen.

  3. Es überwacht Components auf Veränderungen und repariert sie nötigenfalls.

  4. Es überwacht Entry Points für Advertisement und installiert die angeforderte Anwendung nötigenfalls.

  5. Bisher nicht erwähnt: Falls während einer Installation Dateien ersetzt werden, legt MSI Sicherheitskopien davon an. Nach der Deinstallation werden sie wiederhergestellt (das Rollback aus 1. wird also dauerhaft behalten).

Alle diese Dinge müssen irgendwo gespeichert werden. Dieses Irgendwo ist … *Trommelwirbel* … C:\Windows\Installer.

Wenn ihr euch also fragt, warum das Verzeichnis so riesig ist: Weil jedes Setup, das ihr jemals ausgeführt habt, dort beschrieben ist, samt Rollback-Informationen. Falls ihr das löscht, könnt ihr nicht einmal mehr eine Deinstallation der betroffenen Packages durchführen.

Pikantes Detail: Bis Windows 7 wurden dort nur reduzierte Packages gespeichert – also nur die Deklarationen ohne tatsächliche Dateidaten. Dummerweise lassen sich die Packages dann nicht mehr digital zertifizieren, denn beim Entfernen der Dateidaten ändert sich ja die Prüfsumme! Bedeutet: Wenn ihr eine 1-GiB-MSI installiert, dann ja, wird euer C:\Windows\Installer-Verzeichnis tatsächlich um einen Gigabyte größer! Dass diese Änderung nicht auf signierte Packages eingeschränkt wurde, und dass MSI diese Packages nicht einmal vollständig zur Reparatur einsetzen kann, wurde breit kritisiert. Microsoft begründete es damit, dass wahrscheinlich nicht genügend Ressourcen zur Verfügung standen, diese Komplexität breit zu testen. Um ein funktionsfähiges System zu garantieren, hat man eben den einfachen Algorithmus gewählt und massig Plattenspeicher geopfert (Setup & Install by Heath Stewart, Changes to Package Caching in Windows Installer 5.0).

Wenn ich mehr über MSI erzählt habe, spendiere ich eine Tour durch die verschiedenen Tools, mit denen man das Verzeichnis säubern kann. Nun haben wir aber erstmal Wichtigeres zu tun – es wird Zeit für einen Blick in die Datenbanken selber, also in MSI-Dateien. Gebt euch damit zufrieden, dass die Festplattenbereinigung ab Windows 8.1 auch dieses Verzeichnis mit bereinigt.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon MasterQ32 » 01.09.2018, 11:33

Sehr awesome, danke für die Ausführungen!
Wer checkt diese Shaderprogrammierung denn?
JCL: Kein Mensch zwingt Sie jedoch, mit Shadern oder ueberhaupt mit Gamestudio zu arbeiten. Es gibt schliesslich auch andere schoene Hobbies, wie zum Beispiel das Sammeln von Bierdeckeln – JCL quotes
Benutzeravatar
MasterQ32
Felix Queißner
Establishment
 
Beiträge: 1197
Registriert: 07.10.2012, 14:56

Re: Microsoft Installer (MSI)

Beitragvon Schrompf » 01.09.2018, 13:04

Tolle Übersicht! Vielen Dank.
Häuptling von Dreamworlds. Baut an was Neuem. Hilft nebenbei nur höchst selten an der Open Asset Import Library mit.
Benutzeravatar
Schrompf
Thomas Ziegenhagen
Moderator
 
Beiträge: 3768
Registriert: 26.02.2009, 00:44
Wohnort: Dresden
Benutzertext: Lernt nur selten dazu

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 02.09.2018, 02:05

Freut mich, wenn es nützt!

Das .msi-Dateiformat

Wie gesagt arbeitet der MSI deklarativ. Dabei können wir direkt ein häufiges Missverständnis aus dem Weg räumen: MSI-Packages sind nicht ausführbar. Es befindet sich üblicherweise kein Code in .msi-Dateien. Sie werden vom MSI ausgeführt.

Das ausführende Tool ist msiexec.exe; es prüft kurz die Integrität des Packages und dirigiert dann einen Großteil der Arbeit an einen Systemdienst. MSI muss aus zwei Hauptgründen als Systemdienst arbeiten:

  1. Nebenläufige Installationen wären nicht transaktional. Wenn zwei Packages parallel die selbe Datei installieren würden, würde das die Rollback-Logik schwer verkomplizieren. MSI darf deshalb nur als eine globale Instanz laufen.

  2. on-Demand-Installationen dürfen keine Admin Prompts auslösen, weil sie sonst nicht mehr transparent für den Anwender wären. (Der Admin müsste jedes Mal zum Anwender rennen, wenn der ein Programm zum ersten Mal benutzt.) Dafür braucht MSI ständige Admin-Rechte.
Ein unschätzbarer Vorteil des Weges über msiexec.exe ist die einheitliche Schnittstelle. Alle MSIs aller Produkte aller Hersteller können über exakt die selben Parameter stummgeschaltet, advertised, installiert, repariert, oder deinstalliert werden. Das Logging erfolgt auf identische Weise mit identischen Steuerparametern.

MSI nutzt eine SQL-ähnliche Datenbank. Neben dieser Datenbank braucht ein Package aber noch weitere Daten:

  • Die eigentlichen Dateidaten, die ans Ziel kopiert werden sollen. MSI nutzt Windows’ traditionelle .cab-Archive und gruppiert die Dateien in Media.

    Diese Gruppierung kann beliebig erfolgen – heutzutage gruppiert man normalerweise logisch (32-Bit-Dateien in ein Medium; 64-Bit-Dateien in ein Medium; plattformunabhängige Dateien in ein drittes). Früher hat man meist physisch gruppiert – die ersten 600 (komprimierten) MiB auf das Medium der ersten CD, die nächsten 600 auf das Medium der zweiten CD, usw (im Ergebnis mit je einer .cab pro CD). MSI sucht im Ausführungsverzeichnis der .msi nach den .cabs; falls es sie nicht findet, fragt es den Anwender.

    .cab-Archive können auch in .msi eingebettet werden. So bekommt der Anwender nur eine Datei statt eines ganzen Satzes. In der Datenbank wird dem Archivnamen dafür eine Raute # vorangestellt.

    Die maximale Größe eines .cab-Archives liegt bei 2 GiB. Ich glaube, auch einzelne Dateien dürfen nicht größer werden. Mehr muss man halt auf mehrere Medien verteilen. Einzelne Dateien dürfen größer als 2 GiB sein, und können bei Bedarf mehrere Archive übergreifen: Microsoft Docs, Cabinet Files.

    Die Kompression ist ganz gut – es gibt einen völlig veralteten MSzip-Algorithmus, der gerade mal DEFLATE-Kompression erreicht. Es gibt aber auch LZX, das Microsoft in den 90ern eingekauft hat, und das „nur“ 15–25 % hinter modernen Algorithmen liegt.

  • Für Advertising und on-Demand-Installationen werden Symbole benötigt, die den Dateitypen und Verknüpfungen mitgegeben werden können. Sie werden als binärer Datenstrom direkt in der Datenbank abgelegt.

  • Die Benutzeroberfläche der Installation kann mit Bildern und Logos angepasst werden. Auch sie landen als Binärströme in der Datenbank.

  • .msi-Dateien können digital signiert werden. Die Signatur landet als Pseudo-Tabelle in der Datenbank.

  • Der Explorer zeigt Package-Eigenschaften an, etwa Version und Namen. Zu dieser Summary Information mit ihren ganz besonderen Eigenschaften werden wir später noch kommen.
Das Ganze kann man sich in 7-Zip recht gut ansehen; hier z. B. mit dem Setup des Application Verifiers:

AppVerifMSI7z.png

Die Einträge mit den Ausrufzeichen sind Datenbanktabellen; appverifier.cab ist das Haupt-Medium; Binärdatenströme und das Icon kommen danach. Die Datei endet mit der digitalen Signatur und der Summary Information.

Nun können wir uns die Tabellen genauer ansehen.
Zuletzt geändert von Krishty am 02.09.2018, 12:05, insgesamt 1-mal geändert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 02.09.2018, 03:10

Die MSI-Datenbank

Die MSI-Datenbank unterstützt eingeschränkt SQL-Befehle (Microsoft Docs, SQL Syntax).

Diese Syntax ist wichtig, wenn man mit der Win32-API auf Packages zugreifen möchte (Microsoft Docs, Installer Function Reference). Die Funktionsfülle ist überwältigend, aber sie deckt zwei große Anwendungsfälle ab: Das Verwalten von Packages genau so wie Laufzeitveränderungen der Packages von den geächteten Custom Actions aus. Im Augenblick lassen wir die API beiseite und merken uns schlicht, dass man MSIs von Win32 aus erzeugen und verändern kann.

Um sich die eigentliche Datenbank anzusehen, gibt es eine Vielzahl Tools:

  • Der „offizielle“ Weg ist Orca, Microsofts .msi-Editor. Es lag von Anfang an dem Microsoft Installer SDK bei, das es nun schon seit einem Jahrzehnt nicht mehr gibt. Obwohl es wie aus der Steinzeit aussieht, wird es ständig weiterentwickelt. Ihr findet die aktuelle Version im Windows 10 SDK. Falls euch der Download zu groß für das winzige Tool ist, habe ich eine Kopie auf meinem Webspace.

  • Pantaray SuperOrca ist eine Neuentwicklung mit viel größerem Funktionsumfang. Sie passt zum Beispiel Verknüpfungen zwischen Tabellen an, die man bei Orca händisch synchronisieren muss. Ich habe sie nicht getestet.

  • InstEd It! ist eine weitere Drittentwicklung. Auch die habe ich nicht getestet, aber viele Profis in den Packaging-Foren nutzen sie.
Ich werde hier alles mit Orca illustrieren.

Sehen wir uns Application Verifiers MSI-Datenbank an. Ich werde ungefähr die gleiche Reihenfolge einhalten wie in der Erklärung grundlegender Konzepte.

Jede Datenbank besteht aus Tabellen. Tabellen enthalten Werte, die in Spalten sortiert sind. Diese Organisation wird durch ein Schema vorgegeben. Ob Null-Werte (leere Werte) erlaubt sind oder nicht, hängt bei MSI vom Spaltentyp ab.

Die Features, die ein MSI installieren kann, stehen in der Feature-Tabelle:
1 Feature.png

Das Package besteht also nur aus einem Feature – dem Application Verifier selber. Der Name in der ersten Spalte sollte eigentlich menschenlesbar sein – dieses Package wurde aber augenscheinlich maschinell generiert.

Um zu wissen, welche Components für ein Feature installiert werden, kann man sich die FeatureComponents-Tabelle ansehen:
2 FeatureComponents.png

Ein einfaches Mapping von Name zu Name: Alle Komponenten hängen am selben Feature (es gibt ja nur eins).

In der Component-Tabelle wird es interessanter:
3 Component.png
  • Der Name einer Komponente wurde eindeutig aus dessen GUID erzeugt. In eurem Setup solltet ihr natürlich lesbare Namen verwenden.
  • Directory_ gibt an, in welches Verzeichnis die Komponente erzeugt wird. Dabei ist TARGETDIR symbolisch und wird bei der Installation durch den tatsächlichen Ort ersetzt. Falls die Komponente keine Datei ist, sondern ein Registry-Schlüssel, ist der Pfad logischerweise ein Registry-Pfad. Alle Pfade kann man sich in der Directory-Tabelle ansehen.
  • Die Komponente im ProgramMenuFolder hat die Condition, dass sie nur unter der NT-Linie (also nicht unter Windows 95/98/ME) installiert wird. Auf die Syntax solcher Einschränkungen kommen wir später zurück; da sie auch selbstdefinierte Variablen nutzen kann, ist sie recht mächtig.
  • KeyPath gibt eine Datei oder einen Registry-Schlüssel an, den MSI prüfen soll, wenn die entsprechende Komponente angefordert wird. Damit wird Self Repair (im Fall einer beschädigten oder fehlenden Datei) oder das Setup (im Fall von Advertising) ausgelöst.
  • Attributes ist eine hässliche Sammlung kryptischer Flags für Sonderfälle – etwa, ob die Datei nach der Deinstallation doch auf dem System verbleiben soll.

Hier ist die File-Tabelle mit der Zuordnung einzelner Dateien zu Komponenten:
4 File.png
  • Die erste Spalte ist mal wieder der symbolische Name; in diesem Fall maschinell erzeugt.
  • Die Werte der Component_-Spalte kennen wir aus der vorherigen Tabelle.
  • Dahinter der tatsächliche Name, mit dem die Datei im Dateisystem angelegt wird.
  • Die Dateigröße in FileSize ist nötig, damit MSI bei der Installation prüfen kann, ob ausreichend Platz auf der Festplatte zur Verfügung steht.
  • Wir haben gelernt, dass Komponenten möglichst immer versioniert sein sollten, um Updates zu erleichtern. Dafür die Version- und Language-Spalte.
  • Die Sequence Number ist gewissermaßen der eindeutige Index der Datei. Er wirkt sich auf die Installationsreihenfolge aus – weil Zugriff auf komprimierte Dateien in .cab-Archiven nicht immer wahlfrei ist. Der Installer wird sich bemühen, die zu installierenden Dateien nach dieser Zahl zu ordnen.
Eine fast identische Zuordnung der Registry-Schlüssel findet sich in der Registry-Tabelle.

Wo die Dateien dann tatsächlich zu finden sind, bestimmt die Media-Tabelle:
5 Media.png
Dabei ordnet LastSequence die Dateien einem Medium zu: Die Dateien 1–13 stammen aus 838060235bcd28bf40ef7532c50ee032.cab; die Datei 13 aus a35cd6c9233b6ba3da66eecaa9190436.cab, Nummer 14 aus fe38b2fd0d440e3c6740b626f51a22fc.cab. Warum sie so verstreut sind? Wahrscheinlich sind die Komponenten mit anderen Packages aus dem Windows SDK geteilt, und das Überspringen von Dateien in einem Archiv ist mangels wahlfreiem Zugriff teuer. Die DiskId am Anfang ist eher kosmetisch: Wenn der Benutzer aufgefordert wird, einen Datenträger einzulegen, wird diese Id angezeigt.

Mit diesen Informationen könntet ihr exakt rekonstruieren, wohin welche Datei installiert wird, aus welchem Archiv sie stammt, und welcher Registry-Schlüssel mit welchem Wert angelegt wird. Die Dateidaten könntet ihr, wie oben gezeigt, aus den .cab-Dateien extrahieren.

Es gibt noch viel mehr Tabellen – vor allem für die UI. Die sind aber erstmal nicht so wichtig.

Glückwunsch – ihr könnt nun beliebige MSIs extrahieren! Gönnt euch zum Stöbern ruhig einen Blick in LibreOffice & Co …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 02.09.2018, 03:41

MSI und Unicode

Wegen der Lokalisierung ist das ein wichtiges Thema.

Machen wir es kurz: MSI-Datenbanken arbeiten leider mit Byte-orientierten Strings. Sie erfordern eine Codepage, die für das gesamte MSI identisch sein muss – anders als die Win32-API, die auf UTF-16 aufbaut. Grund ist die Kompatibilität zu Windows 95/98/ME, auf dem MSI ursprünglich eingeführt wurde.

Aber es gibt Hoffnung: Die Codepage 65001 – allgemein bekannt als UTF-8 – wird akzeptiert. Das ist nicht offiziell dokumentiert, aber der Trick ist mittlerweile zu weit verbreitet, als dass Windows irgendwann inkompatibel dazu werden könnte. Auch das MSI-Team hat das 2005 in die Welt hinaus gebloggt (Setup & Install by Heath Stewart, MSI Databases and Code Pages).

Schauen wir uns das Hauptproblem mit Unicode an:
Encoded strings will store correctly and will be converted correctly when the W functions are called, but they may not display properly because the correct font for wide characters is not chosen.
Tatsächlich gibt es Berichte, wonach z.B. eine japanische Übersetzung an der Schriftart gescheitert ist (microsoft.public.platformsdk.msi, Japanese MSI with UTF8 65001 codepage issues?).

Andererseits hat Microsoft durch den rasanten Aufstieg von Emojis viel investiert, um alle Windows-Schriftarten komplett Unicode-tauglich zu kriegen. Ich habe jedenfalls schnell ein UTF-8-Setup auf Windows 10 getestet, und ASCII-Buchstaben plus traditionelles Chinesisch und Japanisch zeigten makellos an. Ich werde es einfach drauf ankommen lassen. Wenn ich was feststelle, schreibe ich es hier.

Vorläufiges Fazit: Mit dem inoffiziellen Trick, die Codepage auf 65001 zu setzen, dürfte MSI allerspätestens ab Windows 10 Unicode-kompatibel sein.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon mrz » 02.09.2018, 14:18

Auch von mit erstmal Danke für die Infos.

Krishty hat geschrieben:Hier ist die File-Tabelle mit der Zuordnung einzelner Dateien zu Komponenten:
[...]
[*]Dahinter der tatsächliche Name, mit dem die Datei im Dateisystem angelegt wird.

Bei deinem Screen sieht man aber dass die unterersten beiden Einträge ein | drin haben, entsprechend kann das nicht der finale Dateiname sein.
Denke mal das ist ein "Sondefall" für irgend einen OR Bastel?
mrz
 
Beiträge: 42
Registriert: 07.08.2008, 14:34

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 03.09.2018, 08:07

Sehr gut beobachtet! Das Zeichen | trennt den langen vom kurzen 8.3-Dateinamen. Aus Kompatibilitätsgründen legen viele Setups beides an (zumindest tut’s WiX bei mir standardmäßig).

Ich denke nicht, dass wir uns 2018 noch damit beschäftigen sollten, deshalb werde ich das hier ignorieren. Davon ab ist die Syntax nicht sehr menschenfreundlich (Microsoft Docs, Filename):
No space is allowed preceding the vertical bar (|) separator for the short file name/long file name syntax. Short file names may not include a space, although a long file name may. A space can exist after the separator only if the long file name of the file name begins with the space. No full-path syntax is allowed.
… und wie die Logik damit umgeht, dass Short und Long Name stark unterschiedlich sind, habe ich selber noch nicht verstanden. Wie gesagt, einfach ignorieren.

Nachtrag zur Klärung: Die MSDN gibt an, dass man einen Short Name eintragen muss. Tatsächlich wird der aber nur verwendet, wenn man die Setups via SHORTFILENAMES-Property oder durch ein Registry-Setting in Windows erzwingt. Wer sowas tut, ist IMHO selber schuld, wenn nichts mehr funktioniert.

Nachtrag 2: Für Short Filenames gelten andere Regeln als für Long Filenames – bswp. ist das Pluszeichen + nicht erlaubt. Short Filenames sind immer der erste Fall, erst danach kommt das Trennzeichen und dann der Long Filename. Es könnte also sein, dass MSI einen Dateinamen mit Pluszeichen nicht akzeptiert, wenn nicht explizit auch ein Short Filename angegeben wurde. Das muss ich nachher testen; wäre ein ziemlicher Kracher …

Nachtrag 3: Wenn man .cab-Dateien mit dem Windows-Standard-Utility makecab erzeugt, unterstützt es nur 8.3-Dateinamen. Ich werde euch aber später zeigen, wie man das direkt mit der Win32-API und langen Namen hinkriegt.

Nachtrag 4: Dateiname mit Pluszeichen wirft einen Fehler in Orcas Überprüfung des Pakets; Installation trotzdem problemlos (sogar ohne Warnungen) auf Windows 10 x64.

(Ihr seht, es ist ein ziemliches Rabbit Hole. Ich muss hier auch alles aufschreiben, damit ich es nicht selber sofort wieder vergesse.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 07.09.2018, 20:24

Ein Setup auspacken

MSI ist standardisiert und datengesteuert. Packages liefern also keinen eigenen Code für die Installation, und jedes Package lässt sich auf die selbe Art entpacken. (Ausnahmen am Ende des Beitrags.)

Das Auspacken eines Setups nennt sich in MSI Administrative Installation. Fragt mich nicht, wo der Name herkommt oder was er bedeutet – ist halt so.

Die Befehlszeile

  msiexec /a foo.msi TARGETDIR=C:\foo

entpackt das Package foo.msi in das Verzeichnis C:\foo. Davon abgesehen wird nichts am Package oder an eurem System geändert. Den Parameter TARGETDIR=C:\foo könnt ihr bei vernünftig gebauten Packages weglassen; sie extrahieren dann ins aktuelle Verzeichnis. Jedoch haben gerade mit WiX gebaute Packages die Unart, zu C:\ zu extrahieren, wenn man kein TARGETDIR angibt. Sicher ist sicher.

Nach dem Aufruf sollte das Zielverzeichnis eine (geschrumpfte) .msi und ein neues Unterverzeichnis enthalten. Die geschrumpfte .msi speichert weiterhin alle Registry-Einträge, UI, usw.; die eingebetteten Binärdaten und Archive sind jedoch alle in das neue Unterverzeichnis extrahiert. Wenn ihr darin rumstöbert, findet ihr alle Dateien, die das Package auf dem System installieren kann. Wenn ihr das mit Word o. ä. macht, könnt ihr euch z. B. alle Schriftarten ansehen oder in Ruhe welche rauspicken.


Dieses entpackte Package ist vollständig und kann problemlos zur Installation benutzt werden. Ihr könnt das ursprüngliche Package löschen, wenn euch das entpackte besser gefällt. Diese Eigenschaft ist enorm nützlich – einerseits für die Archivierung, aber zu einem viel wichtigeren Anwendungsfalle komme ich im nächsten Beitrag.


Die versprochene Ausnahme von der Regel: Office 2003 lässt sich nicht extrahieren, weil Microsoft dort die entsprechenden Tabellen aus der Datenbank gelöscht hat. Ich weiß nicht, was hinter der Entscheidung stand. Mit etwas Eifer kann man sie wiederherstellen, aber das spare ich mir für einen späteren Beitrag.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 07.09.2018, 21:12

Setups, Patches, und Service Packs zusammenfassen

Falls ihr euch für Office & Co. die Patches und Service Packs selber herunterladet statt durch einen Web Installer, habt ihr ein ursprüngliches Setup und eine Sammlung von .msp-Dateien (MSI-Patches). Hier seht ihr meinen Ordner für den PowerPoint Viewer (im Gegensatz zu Libre Office & Co. 100 % kompatibel; dabei viel leichter als eine echte Office-Installation):

PPV folder.png

Ihr merkt vielleicht: Möchte ich den PowerPoint Viewer installieren, muss ich 35 Installationen starten. Alternativ installiere ich ein Mal und Windows Update installiert 34 Updates nach.

Beides kein Zustand.

Nun zu dem irren Vorteil der entpackten Setups, die ich im Beitrag zuvor erwähnt habe: Man kann Patches direkt auf sie anwenden.

Legen wir los. Das Grund-Setup ist kein MSI, sondern eine EXE. Sowas nennt man Bootstrapper und oft ist es bloß ein selbstextrahierendes Archiv, um einen netten Splash Screen anzuzeigen und die Dateigröße weiter zu drücken. Wir kommen an das eigentliche Setup, indem wir das .msi-Package via 7-Zip aus der EXE extrahieren (und sein zugehöriges .cab-Archiv, denn das ist – aus welchem Grund auch immer – nicht ins MSI eingebettet). Rechtsklick auf die EXE7-ZipOpen Archive:

PPV actual MSI.png

Dann extrahieren wir das MSI mit der Methode, die wir im vorherigen Beitrag gelernt haben:

PPV MSI extracted.png
PPV MSI extracted.png (2.22 KiB) 1147-mal betrachtet

Nun wenden wir via /p-Parameter die gesammelten Patches auf das extrahierte Package an:
Code: Ansicht erweitern :: Alles auswählen
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2553347 Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2589298 Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2589318 Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2589352 Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2597087 Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2880522 Update for PowerPoint Viewer 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2881030 Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2889841 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2920812 Security Update for PowerPoint 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB2956076 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3054886 Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3085528 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3101520 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3115197 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3118380 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3118382 Security Update for PowerPoint Viewer 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3128030 Security Update for PowerPoint Viewer 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3141538 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3178688 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3191844 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3191899 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3203460 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3213624 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB3213631 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB4011055 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB4011191 Update for PowerPoint Viewer 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB4011611 Security Update for Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB4011707 Security Update for Microsoft Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB4018311 Security Update for Microsoft Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB4018312 Update for PowerPoint Viewer 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB4022136 Update for Microsoft PowerPoint 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB4022137 Security Update for Microsoft Office 2010.msp" TARGETDIR=C:\foo
msiexec /a "%~dp0ppviewer.msi" /p "%~dp0KB4022198 Security Update for Microsoft Office 2010.msp" TARGETDIR=C:\foo

Die Liste zu erzeugen sieht nach PITA aus, aber der geneigte Windows-User weiß, dass er via dir >> list.txt alle Dateinamen in eine Textdatei ausspucken lassen kann, und von da an ist die Sache quasi erledigt :)

Nun befinden sich unter C:\foo eine .msi-Datei und ein Ordner, die ein vollständig gepatchtes Setup speichern. Die könnt ihr wegspeichern, archivieren, oder sonstwas – und wenn die .msi aufgerufen wird, installiert sie in einem Rutsch den PowerPoint Viewer mitsamt allen Patches. Wenn neue Patches erscheinen, könnt ihr das Verzeichnis auf die selbe Art erweitern.

Protip für die Archivierung: Setup und Patches als gesonderte .exe und .msp waren 266 MiB groß, denn wenn eine Datei durch zehn Updates ersetzt wurde, lag sie auch elf Mal komprimiert vor. Das extrahierte, gepatchte Setup ist nach 7-Zip nur noch 42 MiB groß. Man kann also auch richtig, richtig viel Platz sparen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 06.11.2018, 00:09

Installation Context

Hiermit habe ich mir ziemlich in den Fuß geschossen, darum ein extra Post.

Wenn ihr Setups mit WiX erstellt, braucht ihr Admin-Rechte zur Installation. Andererseits haben einige Setups aber auch den klassischen Auswahldialog, ob man für alle Benutzer oder nur für sich selber installieren möchte. Und letztlich strebt Microsoft eine pro-User-Installation an. Hier ist, wie das Ganze zusammenpasst:

Der Windows Installer unterscheidet zwei Installation Contexts: per-User und per-Machine. Eine per-User-Installation erfordert keine Admin-Rechte; eine per-Machine-Installation schon.

Ursprünglich gab es eine einzige Property-Variable, die das Ganze steuerte: ALLUSERS. Wenn dieser Wert auf 1 gesetzt ist, wird per-Machine installiert; sonst per-User. Der Wert kann an UI-Elemente gebunden werden (ControlEvent-Tabelle) um dem Anwender eine Auswahl zu ermöglichen. Entwickler müssen basierend auf diesem Wert z. B. unterscheiden, ob sie nach Program Files installieren oder nach AppData\Local\Programs.

Um es dem Anwender (und den Entwicklern) noch bequemer zu machen, wurde mit Windows 7 und dem Windows Installer 5.0 der Wert 2 eingeführt. Sobald der gesetzt ist, erfolgt die Steuerung durch eine neue Property-Variable MSIINSTALLPERUSER, und die wird von Windows automatisch initialisiert – startet ein Administrator das Package, hält sie 0 und die Installation wird per-Machine durchgeführt. Startet ein Standardbenutzer das Package, wird die Installation per-User durchgeführt. Die Pfade werden automatisch umgeleitet; der Entwickler muss keine Unterscheidung mehr einbauen – das nennt sich Single Package Authoring. Die Variable lässt sich ebenso durch die UI ändern, um dem Anwender eine Wahl zu ermöglichen (das Single Package Authoring Example führt es sehr gut vor). Beim Reparieren und Deinstallieren startet das Package automatisch wieder im richtigen Kontext.

Übrigens dürft ihr das UAC-Bit im Summary nur setzen, wenn das Package ausschließlich per-User installiert werden kann. (Die Namen sind irreführend!) Sonst fragt Windows nicht nach Admin-Rechten bei einer per-Machine-Installation, und sie schlägt fehl.

Damit ist es sehr einfach geworden, Packages zu entwickeln, die sich ohne UAC-Prompts bedienen lassen. So weit, so gut. Kommen wir nun zu dem Punkt, an dem sie alles verkackt haben: Upgrades.

Die Dokumentation sagt zwar „Applications that have been installed per-user therefore receive all updates or repairs on a per-user basis and applications installed per-machine receive updates or repairs on a per-machine basis“, und wörtlich stimmt das auch. Allerdings versteht Windows Installer unter Update nur minimale Patches; nicht komplette Packages – die heißen Upgrades. Und man kommt fast gar nicht um sie herum.

Wenn nun ein Upgrade startet, indem man eine neue Version über die alte installiert, erfolgt die Suche nach älteren Packages ausschließlich im Installation Context, in dem das neue Package gestartet wird. Mit per-User-Installationen klappt das perfekt:
  1. User X startet Package 1.0. Er ist Standardbenutzer, darum startet es per-User.
  2. Er installiert das Package 1.0 ohne UAC-Prompts; Windows Installer leitet alles nach C:\Users\X\AppData\Local\Programs um.
  3. Installation erfolgreich; X benutzt das Programm.
  4. Package 1.1 mit kritischen Sicherheitsupdates wird verteilt. Es ist als vollständiges Package mit identischem Upgrade-Code und unterschiedlichem Product Code realisiert (Upgrade).
  5. Beim Start erkennt Windows-Installer, dass bereits eine frühere Version installiert ist. Der Lizenzdialog wird übersprungen; die alten Einstellungen werden übernommen; die Installation wird aktualisiert.
  6. Alles ist perfekt.
Nun spielen wir das mit einem Auswahlbildschirm für den Kontext durch – Wollen Sie das Programm für alle Benutzer dieses Computers installieren? – und der Benutzer sagt immer Ja:
  1. User X startet Package 1.0. Er ist Standardbenutzer, darum startet es per-User.
  2. Im Auswahlbildschirm wählt er per-Machine-Kontext aus.
  3. Die Installation erfordert einen UAC-Prompt; Windows Installer leitet alles nach C:\Program Files um.
  4. Installation erfolgreich; X benutzt das Programm.
  5. Package 1.1 mit kritischen Sicherheitsupdates wird verteilt.
  6. X startet das neue Package. Er ist Standardbenutzer, darum startet es per-User.
  7. Die Suche nach vorherigen Installationen schlägt fehl, weil die vorherige Installation per-Machine durchgeführt wurde. Der Lizenzdialog taucht erneut auf. Die alten Einstellungen werden nicht übernommen.
  8. Im Auswahlbildschirm wählt X wieder per-Machine-Kontext aus.
  9. Die Installation erfordert einen UAC-Prompt; Windows Installer leitet alles nach C:\Program Files um.
  10. Installation erfolgreich … und alles ist Schrott.
Nun hat Windows Installer nämlich nicht das alte Package deinstalliert (es wurde beim Start der UI ja nicht gefunden). Nun sind zwei Packages mit unterschiedlichen Versionen am selben Ort installiert. Das Programm taucht unter Programme und Features doppelt mit unterschiedlichen Versionsnummern auf. Falls noch irgendwo eine Verknüpfung zur alten Version besteht, erkennt Windows Installer beim Start, dass die Zieldatei geändert wurde und startet eine automatische Reparatur auf Version 1.0. Die beiden Installationen bekämpfen sich.

Ich habe für dieses Dilemma keine andere Lösung gefunden, als …
  1. … entweder den Auswahlbildschirm für den Kontext zu vermeiden
  2. … oder das Paket von vornherein auf einen bestimmten Kontext festzulegen.
Keine freie Wahl also. Ich finde, dass das eine üble Fehlplanung ist, zumal die direkte API solche Einschränkungen nicht hat.

Das dürfte erklären, warum WiX standardmäßig als per-Machine-Installation startet.

Hier ist übrigens noch eine gute Zusammenfassung der per-User-Nachteile:
  • Installing to locations the user has the ability to alter might reduce the confidence the package producer has for the integrity of the install. This can affect support costs as well as computational correctness under a regulatory environment (lawyers, accounts, food and drug companies, government agencies, etc)
  • Multiple instances of an install means there is duplicate copies of binaries on the machine which wastes disk space. A “Per-Machine” install creates a single copy of common binaries for all users thus saving space.
  • Software is less secure because updating behavior has to be done for each user on the machine. In other words, the occasional user on the machine can made the machine vulnerable because they are not on the machine often enough to keep the software they use up to date.
  • IT departments want programs in locations users can’t tamper with. User tampering is a major source of support costs.
  • Centralized install, servicing, and uninstall from a central IT department are all more challenging when the apps are just in the users profile. There are numerous conditions where it is known not to work at all
Andererseits weiß jeder, der auf der Arbeit nur einen eingeschränkten Benutzer-Account zur Verfügung hat, die Vorteile von per-User-Installationen zu schätzen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon mrz » 07.11.2018, 22:01

Wieder mal sehr informativ, Danke!
Fühle richtig mit, erinnert mich an die Zeit wo ich das Vergnügen mit Sysprep hatte
und mit WAIK ("Windows Automated Installation Kit") resp. WADK ("Windows Assessment and Deployment Kit").
Bis vor deinen letzten Post hat für mich MSI, abgesehen von der Unschönheit mit dem Deinstaller vonwegen Diskspace,
einen brauchbaren Eindruck gemacht. Aber die Problematik mit per-User und per-Machine ist ja ein übler Killer.
Weil die freie Wahl dieser Option im Setup finde ich eine gute Sache.
mrz
 
Beiträge: 42
Registriert: 07.08.2008, 14:34

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 08.11.2018, 11:31

Die Namen sagen mir was :D

Das mit per-User und per-Machine ist ein derart übler Killer, dass ich fast eine Woche alles auf den Kopf gestellt habe um zu prüfen, ob ich nicht irgendwo was falsch mache – aber das MS-Sample zeigt gleiches Verhalten. Ich liefere das Programm nun mit dem Hinweis aus, dass man das Setup händisch per-User starten kann (msiexec /i setup.msi ALLUSERS=2 MSIINSTALLPERUSER=1) und dass man dann darauf achten muss, alle zukünftigen Updates auch auf gleichem Wege zu starten.

Was ich nicht ausprobiert habe, ist: ALLUSERS=1, FindRelatedProducts-Action; falls nichts gefunden, das selbe nochmal mit ALLUSERS=0. Nach einer Woche Herumprobieren wäre mir das zu viel Aufwand gewesen und ich weiß nicht, ob man Standard-Aktionen doppelt einreihen kann.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 10.11.2018, 01:59

Ich bin bei diesem Thema in eine weitere Falle getappt.

Ich kann mein altes, mit WiX erstelltes Setup nicht mit meinem neuen, selber erzeugten Setup upgraden: Obwohl das WiX-Setup während der Installation Admin-Rechte einfordert, und nach C:\Program Files installiert, ist das Produkt nur per-User registriert! Irgendwas ist da total abgefuckt.

Sobald ich mein Setup zu per-User ändere, erkennt er es als Upgrade des alten WiX-Setups. Aber dann installiert es natürlich zu LocalAppData statt nach Program Files, weil … es ja per-User ist. Total kirre. Aber ändern kann ich nichts mehr; ist halt schon lange ausgeliefert.

Also, falls ihr bestehende Setups mit WiX habt: Testet den Upgrade-Pfad gut und überlegt euch den Installation Context ganz genau. Irgendwas ist da richtig, richtig faul.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 11.11.2018, 01:06

.CAB-Archive packen

Cabinet-Archive sind ja quasi der Nukleus von Installationen unter Windows (werden schon seit Urzeiten für Treiber benutzt), aber sie zu erzeugen ist nicht so einfach.

  • Windows bringt makecab mit. Über die Befehlszeile könnt ihr damit nur eine einzelne Datei packen, also ist es quasi nutzlos. Für mehrere Dateien müsst ihr eine kryptische Direktivendatei anlegen, deren Syntax in einem SDK beschrieben ist, das ihr im Internet Archive suchen müsst.

  • cabarc war der Vorgänger von makecab, ist schwer aufzutreiben, und unterstützt gemäß Doku nicht einmal die begehrte LZX-Kompression. Unbrauchbar.

  • cabcomp, auch bekannt als HeleCAB, ist eine schmale GUI über Microsofts Cabinet.dll. Steinalt, aber ganz brauchbar, falls man mal was packen muss.

  • CabPack soll gemäß encode.ru „a couple bytes better“ sein, aber bei mir stürzt es immer nur ab. Unbrauchbar.

  • wimlib hat die einzige nicht-Microsoft-Implementierung der LZX-Kompression, die ich auftreiben konnte. Es ist aber halt blankes LZX ohne CAB-Container, ohne Sliding Windows. Und es soll der Kompression in Windows’ Cabinet.dll unterlegen sein.

  • Das Windows SDK enthält in fci.h die komplette API der Cabinet.dll. Obwohl das irre kryptisch aussieht, gibt es in der MSDN sogar quasi-fertigen Quelltext, den man nur zusammenkopieren muss! (Einfach durch die Liste der Makros klicken, jedes Code-Beispiel abkopieren.) Benutzen lässt er sich folgendermaßen:

    Code: Ansicht erweitern :: Alles auswählen
    // TODO: _A_NAME_IS_UTF!!!!!

    ::CCAB cab = { };
    cab.cb             = 0x7FFF'FFFF; // allow up to 2 GiB per CAB
    cab.cbFolderThresh = 0x7FFF'
    FFFF; // allow up to 2 GiB per compression block
    cab.setID          = 555;         // TODO: ??!? Cabinet set ID
    cab.iCab           = 0;           // Number of this cabinet in a set
    cab.iDisk          = 0;           // Disk number
    cab.szDisk[0]      = '\0';
    strcpy_s(cab.szCabPath, (char const*)path);
    cab.szCab[0]       = '\0';

    ::ERF da_staet;
    auto builder = ::FCICreate(
            &da_staet,
            &onFilePlaced,
            &onReserveMemory, &onReleaseMemory,
            &openFile, &readFile, &writeFile, &closeFile, &seekFile, &deleteFile,
            &generateTempFileName,
            &cab,
            nullptr
    );
    if(builder) {

            auto       toJob           = jobs;
            auto const toEndOfJobs     = toJob + numberOfJobs;
            auto       lastCompression = compression(jobs[0].type, jobs[0].compressionLevel, jobs[0].memoryLevel);
            do {

                    // The memory window can span multiple files, which improves compression drastically (for the sake of random access
                    //  latency). This is controlled for via “folders” (each folder being a solid block of compressed data, if not
                    //  interrupted by CAB size limit).
                    // Start a new folder when the compression settings change.
                    // TODO: Doensn’t the CAB API do that automatically?! Test it!
                    auto const newCompression = compression(toJob->type, toJob->compressionLevel, toJob->memoryLevel);
                    if(newCompression != lastCompression) {
                            if(0 == ::FCIFlushFolder(builder, &onNeedNextCAB, &onStatus)) {
                                    __debugbreak(); // error
                            }
                            lastCompression = newCompression;
                    }

                    if(0 == ::FCIAddFile(
                            builder, (char *)toJob->sourcePath, (char *)toJob->nameInArchive,
                            0, // don’t execute
                            &onNeedNextCAB, &onStatus, &fnGetOpenInfo,
                            newCompression
                    )) {
                            __debugbreak();
                    }

            } while(++toJob < toEndOfJobs);

            if(0 == ::FCIFlushCabinet(builder, 0, onNeedNextCAB, onStatus)) { // TODO: necessary?!
                    __debugbreak();
            }
            ::FCIDestroy(builder);
    }

    (Dabei sind Compression Type die Werte tcompTYPE_NONE, tcompTYPE_MSZIP, tcompTYPE_QUANTUM, tcompTYPE_LZX aus den Windows-Headern und Compression Level / Memory Level sind in den Headern direkt daneben erklärt. Beste Kompression ist immer tcompTYPE_LZX mit Level 21.)

    Nachtrag: Microsoft hat auch ein Aufrufbeispiel; nutzt besser das: https://docs.microsoft.com/en-us/window ... -a-cabinet

… und das ist dann auch schon alles, was mein Setup-Generator braucht.

Abschließende Trivia: Für Mitte der 90er war LZX wirklich bahnbrechend. Der Typ, den MS damals dafür eingestellt hat, hatte sogar extra-Filter für ausführbare Dateien entwickelt. Heute ist es zwar abgeschlagen hinter LZMA, Oodle, und Konsorten – Microsoft setzt es aber nichtsdestotrotz für komprimierte Windows 10-Installationen (nicht -Setups!) ein, bei denen das Betriebssystem komprimiert installiert wird. Raymond Chen hatte letztens was dazu.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Tiles » 11.11.2018, 09:11

Ich finde es Top wie du dich hier durchwühlst :)

Hast du eigentlich geplant mit dem Fachwissen unterm Arm einen eigenen Installmaker zu bauen? Mir scheint das Nutzen/ Aufwandsverhältnis ein wenig gross um nur eine einzige Setupdatei zu bauen ...
Free Gamegraphics, Freeware Games http://www.reinerstilesets.de
Die deutsche 3D Community: http://www.3d-ring.de
Benutzeravatar
Tiles
Establishment
 
Beiträge: 1238
Registriert: 11.01.2003, 14:21

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 11.11.2018, 09:55

Ich habe schon einen gebaut, aber da sind noch nicht alle Schwächen ausgebügelt. Insbesondere ist die EULA noch hard-coded, weil sie für alle meine Projekte identisch ist … mit der Zeit wird sich das schrittweise verbessern, und dann kann ich den Generator auch veröffentlichen.

Tatsächlich sollte man das meiste hier aber auch wissen, wenn man nur ein einziges Setup bauen will. Leider. Das hätte Microsoft echt viel, viel einfacher entwerfen können.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 16.11.2018, 22:35

.CAB-Archive in MSI einbetten

.CAB-Archive können in MSIs eingebettet werden oder nicht; Windows Installer kann mit beiden Fällen umgehen.

Externe CABs sind besser, wenn:
  • Sich mehrere Installer die selben Daten teilen. Etwa zwei MSIs – einmal die 32-Bit-Version, einmal die 64-Bit-Version – die sich gemeinsame Programmdaten teilen.
  • Wenn man aus irgendeinem Grund mehrere Installationsmedien braucht (CD 1, CD 2, …) – stirbt aber heute aus.
Eingebettete Tabs haben die Vorteile:
  • Nur eine Datei für den Kunden – also die beste Option für Downloads.
  • Beim Kunden ist die Hürde, versehentlich was kaputtzumachen, viel höher (er kann nicht versehentlich das CAB löschen).
Wenn man das CAB im MSI haben möchte, muss es als separater Stream hinzugefügt werden. (MSIs sind intern die selben Stream-Container wie sie auch von frühen Office-Versionen genutzt wurden.) Nativ in Win32 geht das so (Quelle):

  • Dem echten CAB-Namen muss im MSI eine Raute vorangestellt werden – das bedeutet, dass Windows Installer es sich aus einem Stream holen soll statt aus einer Datei. Gilt vor allem für die Cabinet-Spalte in der Media-Tabelle.

  • Neues MSI erzeugen via

      MSIHANDLE msi;
      if(ERROR_SUCCESS == MsiOpenDatabaseW(L"foo.msi", MSIDBOPEN_CREATEDIRECT, &msi)) {
        // foo.msi
    erfolgreich erzeugt

  • Eine Ansicht aller Streams im MSI öffnen via

      MSIHANDLE hView;
      if(ERROR_SUCCESS == ::MsiDatabaseOpenViewA(msi, "select * from `_Streams`", &hView)) {
        // hView
    ist nun eine Ansicht aller Streams

  • Einen neuen Eintrag für den Stream erzeugen – erste Spalte ist der Zielname, zweite Spalte ist der Pfad zum CAB, das hineinkopiert werden soll:

      if(auto hRec = ::MsiCreateRecord(2)) {
        ::MsiRecordSetStringA(hRec, 1, "#data.cab");
        ::MsiRecordSetStreamA(hRec, 2, "C:\\Users\\Krishty\\AppData\\Local\\Temp\\data.cab");
        ::MsiViewModify(hView, MSIMODIFY_INSERT, hRec);
      }
… und so kommt das CAB ins MSI. Nun haben wir fast alle Zutaten, um eigene (sehr primitive) Packages mit der Win32-API zu erzeugen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Microsoft Installer (MSI)

Beitragvon Krishty » 16.11.2018, 22:42

Ein mehrsprachiges Setup ausliefern

Optimalerweise möchte man eine .msi-Datei haben, die beim Anwender automatisch in der richtigen Sprache startet. Das ist aber leider schwierig und setzt einiges voraus; darunter zwei inoffizielle Features.


1. Transformationen

Lokalisierung ist bei MSI nur als nachträgliche Transformation möglich: Man ersetzt dabei Datenbankspalten, die Strings enthalten, durch lokalisierte Versionen der Strings.

(Theoretisch könnte man Properties und Bedingungen nutzen; praktisch werden aber auch einige Dialoge – bspw. das Laden des Setups – vom MSI verwaltet, und nutzen deshalb keine Properties für ihre Texte, sondern die Sprache des aufgerufenen Paketes. Von der exponentiellen Code-Explosion mal ganz abgesehen!)


2. UTF-8

MSI-Datenbanken sind Codepage-basiert – weil das 1997 eben so gemacht wurde. Die Transformationen müssen also nicht nur die Strings ersetzen, sondern auch die Codepage der Datenbank ändern! Das kann eigentlich nur in zwei Fällen gelingen:

  1. Die ursprüngliche Datenbank enthält nur ASCII-Zeichen. Das könnt ihr euch abschmieren, sobald ihr einen Ordner, Registry-Schlüssel, oder eine Datei mit Sonderzeichen drin habt. Mit Sonderzeichen im Produktnamen sowieso.

  2. Die Codepage der ursprünglichen Datenbank ist identisch mit der Codepage eurer Übersetzung.
Der letzte Punkt ist eigentlich kontraintuitiv: Die Codepages können doch nicht bei allen Übersetzungen gleich sein; außer vielleicht für die British English-Übersetzung eines American English-Programms! Protip: MSI unterstützt inoffiziell, aber stabil die Codepage 65001 UTF-8.

Zweite Voraussetzung für eine praktikable Übersetzung eines Packages ist also, dass es komplett in UTF-8 vorliegt. Die Transformationen der Übersetzungen können ebenfalls in UTF-8 vorliegen. Dann kann nichts kaputtgemacht werden.


3. Namen eingebetteter Transformationen

Auf die dritte Voraussetzung kommt man nicht alleine, denn sie ist ein Implementierungsdetail: Damit MSI die passende Transformation für die Sprache des Benutzers auswählt, muss sie mit einem bestimmten Namen in der MSI eingebettet sein, nämlich unter der Language/Region Decimal ID.

Wenn ihr also eine Transformation habt, die euer Package ins Chinesische (Volksrepublik) übersetzt, müsst ihr eure Transformation unter dem Namen 2052 (ohne .mst-Erweiterung!) ins MSI einbetten.

Auch dieses Feature ist undokumentiert. Allerdings ist es seit über fünfzehn Jahren bekannt, und mittlerweile basieren derart viele Packages darauf (u. a. einige Versionen von Microsoft Office und Libre Office), dass Microsoft es nicht mehr entfernen wird. Unter Windows 7 und Windows 10 funktioniert es garantiert; unter älteren Versionen habe ich es nicht getestet.

Glaubt nicht den Gerüchten, Windows würde die Sprache anhand des Tastaturschemas aussuchen. Unter Windows 10 ist das garantiert falsch.


4. Languages in Summary Page

Die letzte Voraussetzung ist, dass in der Summary Page des Packages alle eingebetteten Language/Region Decimal IDs aufgelistet werden. Für Deutsch, Englisch, und Chinesisch sollte dort z. B. unter Languages eingetragen sein: 1031,1033,2052.

––––

Was bedeutet das praktisch? Falls ihr WiX nutzt, gibt es eine schöne Anleitung, wie ihr Transformationen für Sprachen erzeugen könnt:

  1. Nutzt <WixLocalization>, um Strings zu lokalisieren.

  2. Stellt dabei sicher, dass ihr immer in UTF-8 arbeitet.

  3. Kompiliert dann für jede Sprache ein komplettes Setup. (Ja ich weiß – aber WiX kann es wohl einfach nicht anders.)

  4. Nutzt Torch.exe, um ein Diff der einzelnen Versionen zu machen. Im Resultat erhaltet ihr pro Lokalisierung eine .mst-Transformation.

  5. Benennt die Transformationen zu ihren entsprechenden Language/Region Decimal IDs um (ohne Dateierweiterung!).

  6. Ladet euch die WiX-Ergänzung EmbedTransform herunter.

  7. Nutzt sie, um die Transformationen in die .msi einzubetten.

  8. Achtung: Hierfür habe ich noch keine saubere Lösung gefunden: Ihr müsstet nun eigentlich die IDs ins Summary eures Packages eintragen, etwa

    <Package
    Languages='1033,1031'


    aber dann meckert der Compiler, dass diese Sprachen deklariert, aber nicht unterstützt sind. Klar – die Sprachen landen ja auch erst nach der Kompilierung im Package! Ihr müsst also entweder die Validierung ausschalten (-sval-Parameter bei Light.exe) oder die Languages undefiniert lassen und im fertigen Paket händisch nachtragen, etwa via Orca.

    Ich habe aber auch nicht wirklich lange nach einer Lösung gesucht, weil ich eh meinen eigenen MSI-Compiler gebaut habe.
Bedenkt, dass sich das alles automatisieren lässt (vielleicht 20 Zeilen Batch). Es ist also nicht so schlimm, wie es klingt.

––––

Eine Frage, die mich beschäftigt: Sollte das sprachneutrale Setup überhaupt keine Strings enthalten? Im Augenblick ist das sprachneutrale Setup bei mir Englisch; dafür gibt es dann aber keine explizite englische Transformation. Hätte es Vorteile, das anders zu machen? Im Hinblick auf Merge Modules? Mal ausprobieren.


Setup in einer bestimmten Sprache starten

Falls ihr den Erfolg eurer Arbeit prüfen wollt: msiexec /i foo.msi TRANSFORMS=":2052" ruft die chinesische Version von foo.msi auf. (Der Doppelpunkt markiert, wie wir wissen, eingebettete Dateien statt externer.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6663
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy


Zurück zu Algorithmen und Datenstrukturen

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast