DLL’s insluiten in een gecompileerd uitvoerbaar bestand

Is het mogelijk om een ​​reeds bestaande DLL in een gecompileerd C#-uitvoerbaar bestand in te sluiten (zodat u maar één bestand hoeft te distribueren)? Als het mogelijk is, hoe zou je het dan doen?

Normaal gesproken vind ik het prima om de DLL’s gewoon buiten te laten en het installatieprogramma alles te laten regelen, maar er zijn een paar mensen op het werk die me dit hebben gevraagd en ik weet het eerlijk gezegd niet.


Antwoord 1, autoriteit 100%

Ik raad ten zeerste aan om Costura.Fodyte gebruiken – verreweg de beste en gemakkelijkste manier om bronnen in te sluiten in uw vergadering. Het is beschikbaar als NuGet-pakket.

Install-Package Costura.Fody

Nadat het aan het project is toegevoegd, zal het automatisch alle verwijzingen die naar de uitvoermap zijn gekopieerd, insluiten in je hoofd-assembly. Misschien wilt u de ingesloten bestanden opschonen door een doel aan uw project toe te voegen:

Install-CleanReferencesTarget

Je kunt ook aangeven of je de pdb’s wilt opnemen, bepaalde assemblages wilt uitsluiten of de assemblages on-the-fly wilt extraheren. Voor zover ik weet worden ook unmanaged assemblies ondersteund.

Bijwerken

Momenteel proberen sommige mensen ondersteuning voor DNXtoe te voegen.

Update 2

Voor de nieuwste Fody-versie heb je MSBuild 16 nodig (dus Visual Studio 2019). Fody versie 4.2.1 zal MSBuild 15 doen. (referentie: Fody wordt alleen ondersteund op MSBuild 16 en hoger. Huidige versie: 15)


Antwoord 2, autoriteit 12%

Klik met de rechtermuisknop op uw project in Visual Studio, kies Projecteigenschappen -> Bronnen -> Bron toevoegen -> Bestaand bestand toevoegen…
En voeg de onderstaande code toe aan uw App.xaml.cs of equivalent.

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 is mijn oorspronkelijke blogbericht:
http://codeblog.larsholm.net/ 2011/06/embed-dlls-easily-in-a-net-assembly/


Antwoord 3, autoriteit 11%

Als het daadwerkelijk beheerde assemblages zijn, kunt u ILMergegebruiken. Voor native DLL’s heb je wat meer werk te doen.

Zie ook:Hoe kan een C++ Windows-dll worden samengevoegd tot een C#-toepassings-exe?


Antwoord 4, autoriteit 3%

Ja, het is mogelijk om uitvoerbare .NET-bestanden samen te voegen met bibliotheken. Er zijn meerdere tools beschikbaar om de klus te klaren:

  • ILMergeis een hulpprogramma dat kan worden gebruikt om meerdere .NET-assemblies samen te voegen tot een enkele assembly.
  • Mono mkbundle, verpakt een exe en alle assemblages met libmono in een enkel binair pakket.
  • IL-Repackis een FLOSS-alternatief voor ILMerge, met enkele extra functies.

Bovendien kan dit worden gecombineerd met de Mono Linker, die ongebruikte code verwijdert en daardoor maakt de resulterende assembly kleiner.

Een andere mogelijkheid is om .NETZte gebruiken, waarmee niet alleen het comprimeren van een assembly, maar kan de dll’s ook rechtstreeks in de exe inpakken. Het verschil met de bovengenoemde oplossingen is dat .NETZ ze niet samenvoegt, ze blijven afzonderlijke assemblages maar zijn verpakt in één pakket.

.NETZ is een open source-tool die de uitvoerbare bestanden (EXE, DLL) van Microsoft .NET Framework comprimeert en verpakt om ze kleiner te maken.


Antwoord 5, autoriteit 3%

ILMergekan assemblages combineren tot één enkele assemblage op voorwaarde dat de assembly alleen beheerde code heeft. U kunt de opdrachtregel-app gebruiken of een verwijzing naar de exe toevoegen en programmatisch samenvoegen. Voor een GUI-versie is er Eazfuscator, en ook .Netzdie beide gratis zijn. Betaalde apps zijn onder andere BoxedAppen SmartAssembly.

Als u assembly’s moet samenvoegen met onbeheerde code, raad ik u SmartAssembly. Ik heb nooit problemen gehad met SmartAssemblymaar met alle anderen. Hier kan het de vereiste afhankelijkheden insluiten als bronnen voor uw hoofdexe.

Je kunt dit allemaal handmatig doen, je hoeft je geen zorgen te maken als assembly wordt beheerd of in gemengde modus door dll in je bronnen in te sluiten en vervolgens te vertrouwen op AppDomain’s Assembly ResolveHandler. Dit is een alles-in-één oplossing door in het slechtste geval te werken, dat wil zeggen assemblages met onbeheerde 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);
    };
}

De sleutel hier is om de bytes naar een bestand te schrijven en vanaf zijn locatie te laden. Om kip en ei problemen te voorkomen, moet u ervoor zorgen dat u de handler declareert voordat u de assembly opent en dat u geen toegang heeft tot de assembly-leden (of iets instantiëren dat met de assembly te maken heeft) in het ladende (montage-oplossende) deel. Zorg er ook voor dat GetMyApplicationSpecificPath()geen tijdelijke map is, aangezien tijdelijke bestanden kunnen worden gewist door andere programma’s of door uzelf (niet dat het wordt verwijderd terwijl uw programma toegang heeft tot de dll, maar het is in ieder geval vervelend. AppData is een goede locatie). Houd er ook rekening mee dat u elke keer de bytes moet schrijven, u kunt niet vanaf de locatie laden, alleen omdat de dll zich daar al bevindt.

Voor beheerde dll’s hoeft u geen bytes te schrijven, maar rechtstreeks te laden vanaf de locatie van de dll, of u kunt de bytes lezen en de assembly uit het geheugen laden. Zoals dit of zo:

   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.

Als de assembly volledig onbeheerd is, zie je deze linkof ditover hoe dergelijke dll’s moeten worden geladen.


Antwoord 6, autoriteit 2%

De uittreksel van Jeffrey Richteris erg goed. Kortom, voeg de bibliotheek toe als ingebedde bronnen en voeg voor alles een callback toe. Hier is een versie van de code (te vinden in de opmerkingen van zijn pagina) die ik aan het begin van de Main-methode voor een console-app heb geplaatst (zorg er wel voor dat alle aanroepen die de bibliotheek gebruiken een andere methode hebben dan 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);
            }
        };

Antwoord 7, autoriteit 2%

Om uit te breiden op @Bobby’s antwoordhierboven. U kunt uw .csproj bewerken om IL-Repackte gebruiken om automatisch alle bestanden in één montage tijdens het bouwen.

  1. Installeer het nuget ILRepack.MSBuild.Task pakket met Install-Package ILRepack.MSBuild.Task
  2. Bewerk de AfterBuild-sectie van uw .csproj

Hier is een eenvoudig voorbeeld dat VoorbeeldAssemblyToMerge.dll samenvoegt met uw projectuitvoer.

<!-- 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>

Antwoord 8

.NET Core 3.0 ondersteunt native compileren naar een enkele .exe

De functie wordt ingeschakeld door het gebruik van de volgende eigenschap in uw projectbestand (.csproj):

   <PropertyGroup>
        <PublishSingleFile>true</PublishSingleFile>
    </PropertyGroup>

Dit wordt gedaan zonder enige externe tool.

Zie mijn antwoord voor deze vraagvoor meer informatie.


Antwoord 9

U kunt de DLL’s als ingebedde bronnen toevoegen en uw programma deze bij het opstarten laten uitpakken in de toepassingsmap (na te hebben gecontroleerd of ze er al zijn).

Instellingsbestanden zijn echter zo gemakkelijk te maken dat ik niet denk dat dit de moeite waard zou zijn.

EDIT: Deze techniek zou gemakkelijk zijn met NET assemblies. Met non-.NET DLL’s zou het een veel meer werk te zijn (je zou moeten uitzoeken waar de bestanden uit te pakken en te registreren en ga zo maar door).


10

Het kan simplistisch klinken, maar WinRar geeft de mogelijkheid om een ​​stel bestanden te comprimeren naar een zelfextractieberwijs.
Het heeft veel configureerbare opties: definitief pictogram, extract-bestanden naar het gegeven pad, bestand om uit te voeren na extractie, aangepaste logo / teksten voor pop-up getoond tijdens extractie, geen pop-upvenster, überhaupt tekst, enz.
Kan in sommige gevallen nuttig zijn.


Antwoord 11

Het is mogelijk, maar niet zo eenvoudig, om een ​​hybride native/managed assembly in C# te maken. Als je in plaats daarvan C++ zou gebruiken, zou het een stuk eenvoudiger zijn, omdat de Visual C++-compiler net zo gemakkelijk hybride assemblages kan maken als al het andere.

Tenzij je een strikte vereiste hebt om een ​​hybride assemblage te produceren, ben ik het met MusiGenesis eens dat dit niet echt de moeite waard is om met C# te maken. Als je het moet doen, kun je in plaats daarvan kijken naar C++/CLI.


Antwoord 12

Over het algemeen hebt u een of andere vorm van post-build-tool nodig om een ​​assembly-samenvoeging uit te voeren, zoals u beschrijft. Er is een gratis tool genaamd Eazfuscator (eazfuscator.blogspot.com/) die is ontworpen voor het mangelen van bytecodes die ook het samenvoegen van assemblages afhandelt. U kunt dit toevoegen aan een post-build-opdrachtregel met Visual Studio om uw assemblages samen te voegen, maar uw kilometerstand zal variëren als gevolg van problemen die zich voordoen in scenario’s voor het samenvoegen van niet-trivale assemblages.

Je zou ook kunnen controleren of de build-make-up NANT de mogelijkheid heeft om assembly’s samen te voegen na het bouwen, maar ik ben zelf niet bekend genoeg met NANT om te zeggen of de functionaliteit is ingebouwd of niet.

Er zijn ook veel Visual Studio-plug-ins die het samenvoegen van assemblages uitvoeren als onderdeel van het bouwen van de applicatie.

Als u dit niet automatisch wilt laten gebeuren, zijn er een aantal tools zoals ILMerge die .net-assemblies samenvoegen tot één enkel bestand.

Het grootste probleem dat ik heb gehad met het samenvoegen van assembly’s, is of ze vergelijkbare naamruimten gebruiken. Of erger nog, verwijs naar verschillende versies van dezelfde dll (mijn problemen waren over het algemeen met de NUnit dll-bestanden).

Other episodes