712 Stimmen

Einbetten von DLLs in eine kompilierte ausführbare Datei

Ist es möglich, eine bereits existierende DLL in eine kompilierte C#-Datei einzubetten (so dass Sie nur eine Datei zu verteilen haben)? Wenn es möglich ist, wie würde man dabei vorgehen?

Normalerweise bin ich damit einverstanden, die DLLs einfach draußen zu lassen und das Setup-Programm alles erledigen zu lassen, aber es gab ein paar Leute bei der Arbeit, die mich danach gefragt haben, und ich weiß es ehrlich gesagt nicht.

835voto

Matthias Punkte 15299

Ich empfehle dringend die Verwendung von Costura.Fody - ist bei weitem der beste und einfachste Weg, um Ressourcen in Ihre Assembly einzubetten. Es ist als NuGet-Paket verfügbar.

Install-Package Costura.Fody

Nach dem Hinzufügen zum Projekt werden alle Referenzen, die in das Ausgabeverzeichnis kopiert werden, automatisch in Ihre Haupt Montage. Sie können die eingebetteten Dateien bereinigen, indem Sie ein Ziel zu Ihrem Projekt hinzufügen:

Install-CleanReferencesTarget

Sie können auch angeben, ob Sie die pdb-Dateien einschließen, bestimmte Baugruppen ausschließen oder die Baugruppen im laufenden Betrieb extrahieren möchten. Soweit ich weiß, werden auch nicht verwaltete Assemblies unterstützt.

Update

Derzeit versuchen einige Leute, die Unterstützung für DNX .

Aktualisierung 2

Für die aktuellste Fody-Version benötigen Sie MSBuild 16 (also Visual Studio 2019). Fody Version 4.2.1 wird mit MSBuild 15 funktionieren (Referenz: Fody wird nur von MSBuild 16 und höher unterstützt. Aktuelle Version: 15 )

103voto

Lars Holm Jensen Punkte 1594

Klicken Sie einfach mit der rechten Maustaste auf Ihr Projekt in Visual Studio und wählen Sie Projekteigenschaften -> Ressourcen -> Ressource hinzufügen -> Vorhandene Datei hinzufügen Und fügen Sie den nachstehenden Code in Ihre App.xaml.cs oder eine entsprechende Datei ein.

public App()
{
    AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");

    dllName = dllName.Replace(".", "_");

    if (dllName.EndsWith("_resources")) return null;

    System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());

    byte[] bytes = (byte[])rm.GetObject(dllName);

    return System.Reflection.Assembly.Load(bytes);
}

Hier ist mein ursprünglicher Blogbeitrag: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/

90voto

Shog9 Punkte 151504

Wenn es sich tatsächlich um verwaltete Assemblies handelt, können Sie ILMerge . Bei nativen DLLs haben Sie etwas mehr Arbeit zu tun.

Siehe auch: Wie kann eine C++-Windows-DLL in eine C#-Anwendungs-Exe eingebunden werden?

27voto

Bobby Punkte 11139

Ja, es ist möglich, ausführbare .NET-Dateien mit Bibliotheken zusammenzuführen. Es gibt mehrere Tools, um diese Aufgabe zu bewältigen:

  • ILMerge ist ein Dienstprogramm, mit dem mehrere .NET-Assemblies zu einer einzigen Assembly zusammengeführt werden können.
  • Mono mkbundle verpackt eine Exe und alle Assemblies mit libmono in ein einziges Binärpaket.
  • IL-Repack ist eine FLOSS-Alternative zu ILMerge, mit einigen zusätzlichen Funktionen.

Darüber hinaus kann dies mit dem Mono-Linker wodurch ungenutzter Code entfernt wird und die resultierende Baugruppe somit kleiner wird.

Eine weitere Möglichkeit ist die Verwendung von .NETZ das nicht nur die Komprimierung einer Assembly erlaubt, sondern auch die dlls direkt in die Exe packen kann. Der Unterschied zu den oben genannten Lösungen besteht darin, dass .NETZ sie nicht zusammenführt, sondern sie bleiben separate Assemblies, werden aber in ein Paket gepackt.

.NETZ ist ein Open-Source-Tool, das die ausführbaren Dateien (EXE, DLL) des Microsoft .NET Frameworks komprimiert und packt, um sie kleiner zu machen.

22voto

nawfal Punkte 65966

ILMerge kann Baugruppen zu einer einzigen Baugruppe kombinieren, sofern die Baugruppe nur verwalteten Code enthält. Sie können die Befehlszeilenanwendung verwenden oder einen Verweis auf die Exe hinzufügen und programmatisch zusammenführen. Für eine GUI-Version gibt es Eazfuscator und auch .netz die beide kostenlos sind. Kostenpflichtige Apps umfassen BoxedApp et SmartAssembly .

Wenn Sie Assemblies mit nicht verwaltetem Code zusammenführen müssen, würde ich vorschlagen SmartAssembly . Ich hatte nie Schluckauf mit SmartAssembly sondern mit allen anderen. Hier kann es die erforderlichen Abhängigkeiten als Ressourcen in Ihre Haupt-Exe einbetten.

Sie können all dies manuell tun, ohne sich darum kümmern zu müssen, ob die Assembly verwaltet oder im gemischten Modus ist, indem Sie die DLL in Ihre Ressourcen einbetten und sich dann auf die Assembly von AppDomain verlassen ResolveHandler . Dies ist eine Lösung aus einem Guss, indem der schlimmste Fall angenommen wird, d.h. Baugruppen mit nicht verwaltetem Code.

static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
    {
        string assemblyName = new AssemblyName(args.Name).Name;
        if (assemblyName.EndsWith(".resources"))
            return null;

        string dllName = assemblyName + ".dll";
        string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);

        using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
        {
            byte[] data = new byte[stream.Length];
            s.Read(data, 0, data.Length);

            //or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);

            File.WriteAllBytes(dllFullPath, data);
        }

        return Assembly.LoadFrom(dllFullPath);
    };
}

Der Schlüssel dazu ist, die Bytes in eine Datei zu schreiben und von dort zu laden. Um das Henne-Ei-Problem zu vermeiden, müssen Sie sicherstellen, dass Sie den Handler deklarieren, bevor Sie auf die Assembly zugreifen, und dass Sie nicht auf die Assembly-Mitglieder zugreifen (oder irgendetwas instanziieren, das mit der Assembly zu tun hat) innerhalb des Ladeteils (Assembly-Auflösung). Achten Sie auch darauf, dass GetMyApplicationSpecificPath() ist kein temporäres Verzeichnis, da temporäre Dateien von anderen Programmen oder von Ihnen selbst gelöscht werden könnten (nicht, dass sie gelöscht werden, während Ihr Programm auf die dll zugreift, aber es ist zumindest ein Ärgernis). AppData ist ein guter Ort). Beachten Sie auch, dass Sie die Bytes jedes Mal neu schreiben müssen, Sie können nicht von einem Ort laden, nur weil die Dll dort bereits vorhanden ist.

Bei verwalteten DLLs müssen Sie keine Bytes schreiben, sondern direkt vom Speicherort der DLL laden oder nur die Bytes lesen und die Assembly aus dem Speicher laden. So oder so ähnlich:

    using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
    {
        byte[] data = new byte[stream.Length];
        s.Read(data, 0, data.Length);
        return Assembly.Load(data);
    }

    //or just

    return Assembly.LoadFrom(dllFullPath); //if location is known.

Wenn die Baugruppe vollständig unverwaltet ist, können Sie Folgendes sehen Link o ce wie man solche DLLs lädt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X