c# hinzugefügt - Einbetten von DLLs in eine kompilierte ausführbare Datei




es csc (17)

Im Allgemeinen benötigen Sie eine Art Post-Build-Tool, um eine Assembly Merge durchzuführen, wie Sie es beschreiben. Es gibt ein kostenloses Tool namens Eazfuscator (eazfuscator.blogspot.com/), das für das Bytecode Mangling entwickelt wurde und auch das Zusammenfügen von Assemblys übernimmt. Sie können dies in einer Post-Build-Befehlszeile mit Visual Studio hinzufügen, um Ihre Assemblys zusammenzuführen, aber Ihre Laufleistung variiert aufgrund von Problemen, die bei nicht zusammenhängenden Zusammenführungsszenarien auftreten.

Sie könnten auch überprüfen, ob der Build die Möglichkeit bietet, NANT nach dem Erstellen Assemblys zusammenzufügen, aber ich bin nicht genug vertraut mit NANT, um zu sagen, ob die Funktionalität eingebaut ist oder nicht.

Es gibt auch viele Visual Studio-Plugins, die das Zusammenführen von Assemblys als Teil des Erstellens der Anwendung durchführen.

Alternativ, wenn Sie dies nicht automatisch durchführen müssen, gibt es eine Reihe von Tools wie ILMerge, die .net Assemblies in eine einzige Datei zusammenführen.

Das größte Problem, das ich beim Zusammenführen von Assemblys hatte, ist, wenn sie ähnliche Namespaces verwenden. Oder schlimmer, verweisen Sie auf verschiedene Versionen der gleichen DLL (meine Probleme waren in der Regel mit den NUnit DLL-Dateien).

Weißt du, ich habe nirgendwo eine gute Antwort dafür gefunden. Ist es möglich, eine bereits vorhandene DLL in eine kompilierte C # -Datei einzubetten (so dass Sie nur eine Datei verteilen müssen)? Wenn es möglich ist, wie würde man es tun?

Normalerweise bin ich cool, wenn ich die DLLs einfach draußen lasse und das Setup-Programm alles erledigt, aber es gab ein paar Leute bei der Arbeit, die mich das gefragt haben und ich weiß es ehrlich gesagt nicht.


ILMerge kann Assemblys zu einer einzelnen Assembly kombinieren, vorausgesetzt, die Assembly hat nur verwalteten Code. Sie können die Befehlszeilen-App verwenden oder einen Verweis auf die exe-Datei hinzufügen und sie programmgesteuert zusammenführen. Für eine GUI-Version gibt es Eazfuscator und auch .Netz beide kostenlos sind. Bezahlte Apps enthalten BoxedApp und SmartAssembly .

Wenn Sie Assemblys mit nicht verwaltetem Code zusammenführen müssen, würde ich SmartAssembly vorschlagen. Ich hatte nie SmartAssembly mit SmartAssembly aber mit allen anderen. Hier können Sie die erforderlichen Abhängigkeiten als Ressourcen in Ihre Haupt-Exe einbetten.

Sie können all dies manuell tun, ohne sich Sorgen zu machen, wenn die Assembly verwaltet wird oder im gemischten Modus, indem Sie dll in Ihre Ressourcen einbetten und sich dann auf den Assembly ResolveHandler von ResolveHandler . Dies ist eine One-Stop-Lösung, indem der schlechteste Fall angenommen wird, dh 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 hier ist, die Bytes in eine Datei zu schreiben und von ihrem Ort zu laden. Um das Henne-und-Ei-Problem zu vermeiden, müssen Sie sicherstellen, dass Sie den Handler vor dem Zugriff auf die Assembly deklarieren und nicht auf die Assembly-Member (oder Instanzen, die mit der Assembly instanziieren müssen) innerhalb des Lade- (Assembly Resolving) Teils zugreifen. GetMyApplicationSpecificPath() sicher, dass GetMyApplicationSpecificPath() kein GetMyApplicationSpecificPath() Verzeichnis ist, da temporäre Dateien möglicherweise von anderen Programmen oder von Ihnen selbst gelöscht werden (nicht, dass es gelöscht wird, während Ihr Programm auf die DLL zugreift, aber es ist ein Ärgernis). AppData ist ein guter Standort). Beachten Sie auch, dass Sie die Bytes jedes Mal schreiben müssen, Sie können nicht von Ort laden, nur weil die DLL bereits dort residiert.

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:

    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 Assembly vollständig nicht verwaltet wird, können Sie diesen oder this link zum Laden solcher DLLs sehen.



BoxedApp Sie die BoxedApp

Es kann eine DLL in jede App einbetten. Natürlich auch in C # geschrieben :)

Ich hoffe es hilft.



Ich empfehle, Costura.Fody zu verwenden - bei weitem der beste und einfachste Weg, Ressourcen in Ihre Baugruppe 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 wurden, automatisch in Ihre Hauptbaugruppe eingebettet. Möglicherweise möchten Sie die eingebetteten Dateien bereinigen, indem Sie Ihrem Projekt ein Ziel hinzufügen:

Install-CleanReferencesTarget

Sie können außerdem angeben, ob die PDBs eingeschlossen, bestimmte Assemblys ausgeschlossen oder die Assemblys im laufenden Betrieb extrahiert werden sollen. Soweit ich weiß, werden auch nicht verwaltete Assemblys unterstützt.

Aktualisieren

Derzeit versuchen einige Benutzer , Unterstützung für DNX hinzuzufügen .


Wenn Sie ILMerge nicht mit Befehlszeilenschaltern belästigen wollen, empfehle ich ILMerge-Gui . Es ist ein Open-Source-Projekt, wirklich gut!


Es ist möglich, aber nicht so einfach, eine hybride native / verwaltete Assembly in C # zu erstellen. Wenn Sie C ++ stattdessen verwenden, wäre es viel einfacher, da der Visual C ++ - Compiler so einfach wie jedes andere Hybrid-Assemblys erstellen kann.

Sofern Sie keine strikte Anforderung haben, eine hybride Baugruppe zu produzieren, stimme ich MusiGenesis zu, dass dies die Mühe mit C # nicht wirklich wert ist. Wenn Sie es tun müssen, schauen Sie sich vielleicht stattdessen den Wechsel zu C ++ / CLI an.


ILMerge macht genau das, was Sie wollen.


Ich verwende den csc.exe-Compiler, der von einem .vbs-Skript aufgerufen wird.

Fügen Sie in Ihrem xyz.cs-Skript die folgenden Zeilen nach den Anweisungen hinzu (mein Beispiel ist für den Renci-SSH):

using System;
using Renci;//FOR THE SSH
using System.Net;//FOR THE ADDRESS TRANSLATION
using System.Reflection;//FOR THE Assembly

//+ref>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+res>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+ico>"C:\Program Files (x86)\Microsoft CAPICOM 2.1.0.2 SDK\Samples\c_sharp\xmldsig\resources\Traffic.ico"

Die ref-, res- und ico-Tags werden vom unten stehenden .vbs-Skript zur Bildung des csc-Befehls aufgenommen.

Fügen Sie dann den Aufruf des Assembly-Resolvers in den Hauptordner ein:

public static void Main(string[] args)
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    .

... und fügen Sie den Resolver selbst irgendwo in der Klasse hinzu:

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        String resourceName = new AssemblyName(args.Name).Name + ".dll";

        using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            return Assembly.Load(assemblyData);
        }

    }

Ich benenne das vbs-Skript so, dass es dem .cs-Dateinamen entspricht (z. B. ssh.vbs sucht nach ssh.cs); Das macht das Ausführen des Skripts um ein Vielfaches einfacher, aber wenn Sie kein Idiot wie ich sind, könnte ein generisches Skript die CS-Zieldatei per Drag & Drop aufnehmen:

    Dim name_,oShell,fso
    Set oShell = CreateObject("Shell.Application")
    Set fso = CreateObject("Scripting.fileSystemObject")

    'TAKE THE VBS SCRIPT NAME AS THE TARGET FILE NAME
    '################################################
    name_ = Split(wscript.ScriptName, ".")(0)

    'GET THE EXTERNAL DLL's AND ICON NAMES FROM THE .CS FILE
    '#######################################################
    Const OPEN_FILE_FOR_READING = 1
    Set objInputFile = fso.OpenTextFile(name_ & ".cs", 1)

    'READ EVERYTHING INTO AN ARRAY
    '#############################
    inputData = Split(objInputFile.ReadAll, vbNewline)

    For each strData In inputData

        if left(strData,7)="//+ref>" then 
            csc_references = csc_references & " /reference:" &         trim(replace(strData,"//+ref>","")) & " "
        end if

        if left(strData,7)="//+res>" then 
            csc_resources = csc_resources & " /resource:" & trim(replace(strData,"//+res>","")) & " "
        end if

        if left(strData,7)="//+ico>" then 
            csc_icon = " /win32icon:" & trim(replace(strData,"//+ico>","")) & " "
        end if
    Next

    objInputFile.Close


    'COMPILE THE FILE
    '################
    oShell.ShellExecute "c:\windows\microsoft.net\framework\v3.5\csc.exe", "/warn:1 /target:exe " & csc_references & csc_resources & csc_icon & " " & name_ & ".cs", "", "runas", 2


    WScript.Quit(0)

Um auf @ Bobbys asnwer oben zu erweitern. Sie können Ihre .csproj bearbeiten, um IL-Repack zu verwenden, um beim Erstellen automatisch alle Dateien in einer einzigen Assembly zu packen.

  1. Installieren Sie das Paket nugget ILRepack.MSBuild.Task mit Install-Package ILRepack.MSBuild.Task
  2. Bearbeiten Sie den AfterBuild-Abschnitt Ihres .csproj

Hier ist ein einfaches Beispiel, das ExampleAssemblyToMerge.dll in Ihre Projektausgabe zusammenführt.

<!-- ILRepack -->
<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">

   <ItemGroup>
    <InputAssemblies Include="$(OutputPath)\$(AssemblyName).exe" />
    <InputAssemblies Include="$(OutputPath)\ExampleAssemblyToMerge.dll" />
   </ItemGroup>

   <ILRepack 
    Parallel="true"
    Internalize="true"
    InputAssemblies="@(InputAssemblies)"
    TargetKind="Exe"
    OutputFile="$(OutputPath)\$(AssemblyName).exe"
   />
</Target>

Sie könnten die DLLs als eingebettete Ressourcen hinzufügen und dann von Ihrem Programm beim Start in das Anwendungsverzeichnis entpacken lassen (nachdem überprüft wurde, ob sie bereits vorhanden sind).

Setup-Dateien sind so einfach zu machen, dass ich denke, dass es das nicht wert wäre.

EDIT: Diese Technik wäre mit .NET-Assemblies einfach. Mit Nicht-.NET-DLLs wäre es viel mehr Arbeit (Sie müssten herausfinden, wo Sie die Dateien entpacken und sie registrieren und so weiter).



Ja, es ist möglich, ausführbare .NET-Dateien mit Bibliotheken zusammenzuführen. Es stehen mehrere Tools zur Verfügung, um den Job zu erledigen:

  • ILMerge ist ein Dienstprogramm, mit dem mehrere .NET-Assemblies zu einer einzelnen Assembly zusammengeführt werden können.
  • Mono mkbundle , packt 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.

Zusätzlich kann dies mit dem Mono-Linker kombiniert werden, der nicht verwendeten Code entfernt und dadurch die resultierende Baugruppe verkleinert.

Eine andere Möglichkeit ist die Verwendung von .NETZ , das nicht nur das Komprimieren einer Assembly ermöglicht, sondern auch die dlls direkt in die exe packen kann. Der Unterschied zu den oben genannten Lösungen ist, dass .NETZ sie nicht zusammenführt, sie bleiben separate Assemblies, sind aber in einem Paket verpackt.

.NETZ ist ein Open-Source-Tool, das die ausführbaren Dateien von Microsoft .NET Framework (EXE, DLL) komprimiert und komprimiert, um sie zu verkleinern.


Ein weiteres Produkt, das elegant damit umgehen kann, ist SmartAssembly, auf SmartAssembly.com . Dieses Produkt wird nicht nur alle Abhängigkeiten in eine einzelne DLL zusammenführen, sondern (optional) Ihren Code verschleiern, zusätzliche Metadaten entfernen, um die resultierende Dateigröße zu reduzieren, und kann auch die IL optimieren, um die Laufzeitleistung zu erhöhen. Es gibt auch eine Art globales Ausnahmebehandlungs- / Berichtsmerkmal, das es Ihrer Software hinzufügt (falls gewünscht), das ich mir nicht die Zeit genommen habe, es zu verstehen, aber es könnte nützlich sein. Ich glaube, es hat auch eine Befehlszeilen-API, so dass Sie es Teil Ihres Build-Prozesses machen können.


Weder der ILMerge-Ansatz noch Lars Holm Jensen, der das AssemblyResolve-Ereignis behandelt, funktionieren für einen Plugin-Host. Die ausführbare Datei H lädt die Baugruppe P dynamisch und greift auf sie über die in einer separaten Baugruppe definierte Schnittstelle IP zu . Um IP in H einzubetten, benötigt man eine kleine Änderung an Lars's Code:

Dictionary<string, Assembly> loaded = new Dictionary<string,Assembly>();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{   Assembly resAssembly;
    string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
    dllName = dllName.Replace(".", "_");
    if ( !loaded.ContainsKey( dllName ) )
    {   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);
        resAssembly = System.Reflection.Assembly.Load(bytes);
        loaded.Add(dllName, resAssembly);
    }
    else
    {   resAssembly = loaded[dllName];  }
    return resAssembly;
};  

Der Trick bei wiederholten Versuchen, dieselbe Assembly aufzulösen und die vorhandene Assembly zurückzugeben, anstatt eine neue Instanz zu erstellen.

BEARBEITEN: Damit es die Serialisierung von .NET nicht stört, stellen Sie sicher, dass für alle Assemblys, die nicht in Ihr eingebettet sind, null zurückgegeben wird, wodurch das Standardverhalten standardmäßig beibehalten wird. Sie können eine Liste dieser Bibliotheken erhalten nach:

static HashSet<string> IncludedAssemblies = new HashSet<string>();
string[] resources = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
for(int i = 0; i < resources.Length; i++)
{   IncludedAssemblies.Add(resources[i]);  }

und geben Sie nur null zurück, wenn die übergebene Assembly nicht zu IncludedAssemblies .


Zu diesem Zweck können Sie eine LINQ-Abfrage verwenden. Dies fragt alles auf dem Formular ab, das den Typ TextBox hat

var c = from controls in this.Controls.OfType<TextBox>()
              select controls;




c# .net dll merge linker