Fremde DLLs in eigenes .Net-Projekt einbinden

Manchmal ist es wünschenswert, wenn man mit dem eigenen Programm nicht alle verwendeten DLLs, seien es eigene oder fremde, ausliefern muss. Um die Auslieferung zu erleichtern würde es sich anbieten, diese fremden DLLs in das eigene Programm, also die EXE, einzupacken. Alternativ funktioniert das Ganze auch mit eigenen Klassenbibliotheken.

Für diese Aufgabe gibt es aus dem Microsoft Research Center das Werkzeug ILMerge. Mit Hilfe von ILMerge ist es möglich, fertige Assemblies zu mischen.

 

Voraussetzungen

Um die DLLs in das eigene Programm/die eigene Klassenbibliothek (im folgenden spreche ich nur noch von „eigenem Projekt“) aufzunehmen, müssen diese bereits vollständig kompiliert sein. ILMerge nimmt diese Assemblies und verpackt sie zu einer neuen. Dabei gehen alle Informationen, die an der Assembly hängen, wie etwa der Name oder interne IDs verloren.

Aus diesem Grund funktioniert ILMerge für WPF nicht. WPF verwendet interne Ressourcen, welche die IDs der Assembly verwenden.  Durch das Erstellen einer neuen Assembly gehen diese Informationen verloren und funktionieren hinterher nicht mehr.

Für ASP.Net gibt es ein eigenes Werkzeug, ASP.Net Merge. Link, siehe unten.

Verwenden von ILMerge

ILMerge ist in erster Linie ein Kommandozeilenwerkzeug. Über Kommandozeilenparameter wird das Verhalten gesteuert. Folgende Parameter stehen zur Verfügung:

Parameter Beschreibung
[/lib:directory]* Verzeichnisse in denen die benötigten Assemblies zu finden sind. Etwa das Debug-Verzeichnis des Projekts. (Optional)
[/log[:filename]] Protokolldatei von ILMerge. Hier werden die Fehler protokolliert. (Optional)
[/keyfile:filename Eventuell vorhandene Keyfile,etwa zur Erstellung einer strongly named Assembly. Angabe erfolgt mit vollständiger Pfadangabe. (Optional)
[/delaysign]] Sollte verwendet werden, wenn eine strongly named Assembly erstellt werden soll (Optional)
[/internalize[:filename]] Wird verwendet, um die eingebundenen DLLs als internal zu kennzeichnen, statt public. Dies verhindert, dass die eingebundene DLL nach außen hin sichtbar und nutzbar ist. (Optional)
[/t[arget]:(library|exe|winexe)] Hierüber wird gesteuert, welchen Typ die Ziel-Assembly  haben soll. Wird nichts angegeben, entspricht sie dem Typ der <primary assembly> (siehe unten). (Optional)
[/closed] Dient der Auflösung von transitiven (indirekten) Abhängigkeiten. Wenn etwa eine Assembly A.Exe eine Assembly B.dll referenziert, die wiederum eine Assembly C.dll referenziert, so wird über /closed sichergestellt, dass C.dll ebenfalls in die Zielassembly gemischt wird.

Dies ist nicht erforderlich, wenn die Lister der Assemblies am Ende alle relevanten Assemblies enthält. (Optional)

[/ndebug] Bei Angabe dieses Schalters werden die Debuginformationen (*.pdb-Dateien) nicht mit erstellt. Wird der Schalter nicht angegeben, werden die Debuginformationen aller Assemblies in eine zentrale .pdb-Datei eingebunden. (Optional)
[/ver:version] Bietet die Möglichkeit, die Version der Assembly festzulegen. Wird dies nicht angegeben, so wird die Version der <primary assembly> (siehe unten) verwendet. (Optional)
[/copyattrs [/allowMultiple]] Übernimmt alle Assembly-Attribute in die neue Assembly. Mehrfach angegebene Attribute werden dabei überschrieben. Zum Schluss gewinnen die Attribute der <primary assembly>. (Optional)

Wird zusätzlich der Schalter allowMultiple gesetzt, so werden Attribute, die als „AllowMultiple“ markiert sind, aus allen DLLs übernommen. (Optional)

[/xmldocs] Bei Angabe dieses Schalters werden die XML-Dokumentationen aller Assemblies in eine zentrale .XML-Datei eingebunden. (Optional)
[/attr:filename] Über diesen Parameter lässt sich eine Attributs-Assembly angeben, aus der alle Attribute ausgelesen werden (wie etwa Culture, Version, usw.). (Optional)
[/targetplatform:<version>[,<platformdir>]]|v1|v1.1|v2|v4 Hierüber kann gesteuert werden, welches .Net-Framework als Basis verwendet werden soll. Mit V1 und V1.1 gibt es ggf. Probleme. V2 wird vollständig unterstützt. Soll V3, V3.5 verwendet werden, oder gibt es bei der Verwendung von V4 Probleme, so kann über den weiteren Parameter „platformdir“ ein Verzeichnis angegeben werden, aus dem die Assemblies verwendet werden sollen.

Beispiel:

/targetplatform:v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319

Hierbei wird das .Net-Framework 4 aus dem genannten Ordner verwendet. zwischen dem Komma nach V4 und dem Pfad darf kein Leerzeichen angegeben werden. (Optional)

[/useFullPublicKeyForReferences] Wird dieser Schalter angegeben, so werden bei externen Assembly-Referencen die vollständigen Public Keys verwendet. Wird der Schalter nicht angegeben, so werden nur die Public Key Token benutzt. (Optional)
[/zeroPeKind] Dieser Schalter wird benutzt, um C++ Assemblies in die Ziel-Assembly zu übernehmen. Wichtig dabei ist, dass alle Assemblies IL-konform sind und nur managed code verwenden. (Optional)
[/wildcards] Über diesen Schalter werden Wildcards bei der Angabe von Dateinamen berücksichtigt und alle Dateien werden als Eingabe verwendet. (Optional)
[/allowDup[:typename]]* Diese Option erlaubt es ILMerge, öffentliche Typen aus allen DLLs, die den gleichen Namen haben, selbständig umzubenennen. Für private Typen macht ILMerge das bereits immer, für öffentliche (also public) Typen ist das aber in den wenigsten Fällen sinnvoll. (Optional, nicht empfohlen)
[/allowDuplicateResources] Diese Option erlaubt es ILMerge, Ressource-Dateien aus allen DLLs, die den gleichen Namen haben, selbständig umzubenennen. (Optional)
[/union] Wird dieser Schalter angegeben, werden Typen,die den gleichen Namen haben, in einen einzigen Typ in der Ziel-Assembly zusammengeführt werden. Kann nicht zusammen mit der Option „allowDup“ verwendet werden. (Optional)
[/align:n] Über diese Option kann das [http://msdn.microsoft.com/de-de/library/aa983894%28v=vs.71%29.aspx File-Alignment] gesteuert werden. (Optional, Angabe nicht empfohlen)
/out:filename Hier wird der Name der Ziel-Assembly angegeben. Es kann ein vollständiger Pfad angegeben werden.
<primary assembly> [<other assemblies>…]  Zum Schluss wird die Liste der Assemblies angegeben, die zusammengemischt werden soll. Als erstes wird die Basis-Assembly angegeben, also das eigene Projekt. Anschließend, mit Leerzeichen getrennt, alle weiteren Assemblies, die in die Ziel-Assembly gemischt werden sollen.

 

Verwendung als Post-Build in Visual Studio

Da ILMerge ein Kommandozeilenwerkzeug ist, kann es auch einfach in einer Post-Build Aktion im Visual Studio verwendet werden. Dabei bietet es sich an, die Makro-Parameter des Visual Studios zu verwenden.

Hier ein Beispiel, wie ein solches Post-Build-Event aussehen kann:

 

  • In diesem Beispiel ist die Exe-Datei von ILMerge relativ zum Ordner mit der .sln-Datei des eigenen Projekts (Solution-Ordner) zu finden. Das ermöglicht, dass man ILMerge nur einmal ablegen und von allen Projekten aus verwenden kann.
  • Die Protokoll-Datei wird Solution-Ordner abgelegt.
  • Als Ausgabe soll eine DLL erstellt werden, die Zielplattworm ist .Net-Framework 4. Die gemischte DLL- wird unter dem Namen „MyProjectMerged.dll“ im Ordner FinalLibrary im Solution-Ordner abgelegt.
  • Die primary assembly ist hier die MyProject.dll, welche aus dem Verzeichnis /bin/Debug/ genommen wird. Genauso die 3rdParty.dll. Diese wird während des Kompiliervorgangs ja genau dorthin kopiert.
  • Die Bedeutungen der weiteren Schalter sind der obigen Tabelle zu entnehmen.

 

Was die einzelnen Makro-Variablen bedeuten, schaut man sich am besten im Visual Studio selbst an, da dort auch gleich die Auflösung bezogen auf das eigene Projekt durchgeführt wird. Dazu im Solution Explorer das Projekt markieren, aus dem Kontext-Menü „Eigenschaften“ wählen und den 3. Reiter („Build Events“) auswählen.

Im Bereich der „Post-Build events“ auf den Button zum Editieren des Events klicken.

Über den Schalter Makros werden nun alle zur Verfügung stehenden Variablen angezeigt.

 

Einbinden von Projekten, die XNA verwenden

Mir war es nur über einen Workaround möglich, ILMerge dazu zu bringen, DLLs, die das XNA-Framework als Abhängigkeit haben, zu mischen. Obwohl die XNA-DLLs im GAC registriert sind, kann ILMerge diese nicht finden.

Das in der Option /lib angegebene Verzeichnis muss zwingend eine der zu mischenden Assemblies enthalten. Da ich das XNA-Framework selbst aber nicht in die DLL mischen möchte, scheidet diese Möglichkeit aus.

Die „Lösung“ ist nun, alle benötigten DLLs, die nicht gefunden werden, in ein temporäres Verzeichnis zu kopieren und dieses mit der Option /lib anzugeben.

Folgende Batch-Datei übernimmt diese Aufgabe:

Als Parameter werden das Target-Verzeichnis und das Solution-Verzeichnis reingegeben (siehe Beispiel der Post-Build-Events oben). Der XNA-Ordner kann ebenfalls als Parameter reingegeben werden. Ich habe ihn hier fest eingetragen, der ändert sich ja nicht so oft.

  • Als erstes wird ein Verzeichnis „temp“ in dem Ordner erstellt, in dem die Batch-Datei liegt (typischerweise das Solution-Verzeichnis).
  • Danach werden alle benötigten DLLs in das temp-Verzeichnis kopiert.
  • Anschließend wird dann ILMerge aufgerufen, allerdings jetzt mit dem temp-Verzeichnis als /lib und als Basisverzeichnis für die zu mischenden DLLs.
  • Zum Schluss wird das temp-Verzeichnis geleert und gelöscht

 

Über diesen Workaround lassen sich alle möglichen DLLs mischen.

Download: http://www.microsoft.com/en-us/download/details.aspx?id=17630

Quellen:

  • http://www.codeproject.com/Articles/9364/Merging-NET-assemblies-using-ILMerge
  • http://www.bjoernrochel.de/2009/07/07/how-to-shoot-yourself-in-the-foot-with-ilmerge/

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.