werden - visual studio dll einbinden c#




Einbetten von DLLs in eine kompilierte ausführbare Datei (13)

Der Auszug von Jeffrey Richter ist sehr gut. Kurz gesagt, fügen Sie die Bibliothek als eingebettete Ressourcen hinzu und fügen Sie vor allem anderen einen Rückruf hinzu. Hier ist eine Version des Codes (in den Kommentaren auf seiner Seite gefunden), die ich am Anfang der Main-Methode für eine Konsole App (nur stellen Sie sicher, dass alle Aufrufe, die die Bibliothek verwenden, sind in einer anderen Methode zu Main).

AppDomain.CurrentDomain.AssemblyResolve += (sender, bargs) =>
        {
            String dllName = new AssemblyName(bargs.Name).Name + ".dll";
            var assem = Assembly.GetExecutingAssembly();
            String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
            if (resourceName == null) return null; // Not found, maybe another handler will find it
            using (var stream = assem.GetManifestResourceStream(resourceName))
            {
                Byte[] assemblyData = new Byte[stream.Length];
                stream.Read(assemblyData, 0, assemblyData.Length);
                return Assembly.Load(assemblyData);
            }
        };

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.


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.


Es mag simpel klingen, aber WinRar bietet die Option, eine Reihe von Dateien zu einer selbstextrahierenden ausführbaren Datei zu komprimieren.
Es hat viele konfigurierbare Optionen: letztes Icon, Extrahieren von Dateien in den angegebenen Pfad, Datei, die nach der Extraktion ausgeführt werden soll, benutzerdefiniertes Logo / Texte für Popup während der Extraktion, kein Popup-Fenster, Lizenzvereinbarungstext, etc.
Kann in einigen Fällen nützlich sein.


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 .


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)


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.


Klicken Sie einfach mit der rechten Maustaste auf Ihr Projekt in Visual Studio, wählen Sie Projekteigenschaften -> Ressourcen -> Ressource hinzufügen -> Vorhandene Datei hinzufügen ... und fügen Sie den folgenden Code in Ihre App.xaml.cs oder gleichwertig 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 Blog-Beitrag: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/


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>

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 .



BoxedApp Sie die BoxedApp

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

Ich hoffe es hilft.


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.





linker