Question Incorporation de DLL dans un exécutable compilé


Vous savez, je n'ai pas vu une bonne réponse pour ça n'importe où. Est-il possible d'incorporer une DLL préexistante dans un exécutable C # compilé (pour que vous n'ayez qu'un seul fichier à distribuer)? Si c'est possible, comment s'y prendrait-il?

Normalement, je suis cool avec le fait de laisser les DLL dehors et de faire en sorte que le programme d'installation gère tout, mais quelques personnes au travail m'ont posé la question et honnêtement, je ne le sais pas.


478
2017-10-09 23:11


origine


Réponses:


Je recommande fortement d'utiliser Costura.Fody - de loin la meilleure et la plus simple façon d'intégrer des ressources dans votre assemblage. Il est disponible sous forme de package NuGet.

Install-Package Costura.Fody

Après l'avoir ajouté au projet, il intégrera automatiquement toutes les références copiées dans le répertoire de sortie dans votre principale Assemblée. Vous pouvez nettoyer les fichiers incorporés en ajoutant une cible à votre projet:

Install-CleanReferencesTarget

Vous serez également en mesure de spécifier si vous souhaitez inclure les pdb, exclure certains assemblages ou extraire les assemblages à la volée. Autant que je sache, des assemblées non gérées sont également soutenues.

Mettre à jour

Actuellement, certaines personnes essaient d'ajouter prise en charge de DNX.


611
2017-11-30 21:53



Si ce sont des assemblages gérés, vous pouvez utiliser ILMerge. Pour les DLL natives, vous aurez un peu plus de travail à faire.

Voir également:  Comment une DLL Windows C ++ peut-elle être fusionnée dans une application C # exe? 


74
2017-10-09 23:17



Cliquez avec le bouton droit sur votre projet dans Visual Studio, choisissez Propriétés du projet -> Ressources -> Ajouter une ressource -> Ajouter un fichier existant ... Et incluez le code ci-dessous à votre App.xaml.cs ou équivalent.

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);
}

Voici mon article de blog original: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/


72
2018-06-15 18:20



Oui, il est possible de fusionner des exécutables .NET avec des bibliothèques. Il existe plusieurs outils pour faire le travail:

  • ILMerge est un utilitaire permettant de fusionner plusieurs assemblys .NET en un seul assemblage.
  • Mono mkbundle, empaquette un exe et tous les assemblys avec libmono dans un seul paquet binaire.
  • IL-Repack est une alternative à ILMerge, avec quelques fonctionnalités supplémentaires.

En outre, cela peut être combiné avec Mono Linker, qui supprime le code inutilisé et, par conséquent, rend l'ensemble résultant plus petit.

Une autre possibilité est d'utiliser .NETZ, ce qui permet non seulement de compresser un assemblage, mais aussi d'emballer les dll directement dans l'exe. La différence avec les solutions mentionnées ci-dessus est que .NETZ ne les fusionne pas, ils restent des assemblages séparés mais sont emballés dans un seul paquet.

.NETZ est un outil open source qui compresse et emballe les fichiers exécutables (EXE, DLL) de Microsoft .NET Framework afin de les réduire.


24
2017-10-28 14:06



ILMerge peut combiner des assemblages à un seul assemblage à condition que l'assemblage ait seulement du code managé. Vous pouvez utiliser l'application en ligne de commande ou ajouter une référence à l'exécutable et fusionner par programme. Pour une version graphique, il y a Eazfuscator, et aussi .Netz les deux sont gratuits. Les applications payantes comprennent BoxedApp et SmartAssembly.

Si vous devez fusionner des assemblys avec du code non managé, je suggérerais SmartAssembly. Je n'ai jamais eu de hoquet avec SmartAssembly mais avec tous les autres. Ici, il peut intégrer les dépendances requises en tant que ressources à votre exe principal.

Vous pouvez faire tout cela manuellement sans avoir à vous inquiéter si l'assemblage est géré ou en mode mixte en intégrant dll à vos ressources, puis en s'appuyant sur l'Assemblée AppDomain ResolveHandler. Ceci est une solution unique en adoptant le pire des cas, c'est-à-dire des assemblages avec du code non géré.

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);
    };
}

La clé ici est d'écrire les octets dans un fichier et charger à partir de son emplacement. Pour éviter le problème de la poule et de l'œuf, vous devez vous assurer de déclarer le gestionnaire avant d'accéder à l'assembly et de ne pas accéder aux membres de l'assembly (ou d'instancier quoi que ce soit) dans la partie chargement (résolution de l'assembly). Veillez également à assurer GetMyApplicationSpecificPath() n'est pas un répertoire temporaire car les fichiers temporaires pourraient être effacés par d'autres programmes ou par vous-même (non pas qu'ils seront supprimés pendant que votre programme accède à la DLL, mais au moins c'est une nuisance. AppData est bien situé). Notez également que vous devez écrire les octets à chaque fois, vous ne pouvez pas charger à partir de l'emplacement juste parce que la DLL y réside déjà.

Pour les DLL gérées, vous n'avez pas besoin d'écrire des octets, mais de charger directement depuis l'emplacement de la DLL ou simplement de lire les octets et de charger l'assembly depuis la mémoire. Comme ceci ou cela:

    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.

Si l'assemblage est entièrement non géré, vous pouvez le voir lien ou ce quant à la façon de charger ces DLL.


16
2018-05-15 11:46



le extrait de Jeffrey Richter c'est très bien. En bref, ajoutez les bibliothèques en tant que ressources incorporées et ajoutez un rappel avant toute autre chose. Voici une version du code (trouvée dans les commentaires de sa page) que je mets au début de la méthode Main pour une application console (assurez-vous simplement que tous les appels utilisant la bibliothèque utilisent une méthode différente de celle de 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);
            }
        };

11
2017-11-06 07:04



Pour développer @ Bobby Asnwer au dessus. Vous pouvez éditer votre fichier .csproj pour l'utiliser IL-Repack pour empaqueter automatiquement tous les fichiers dans un seul assemblage lorsque vous construisez.

  1. Installez le paquet nuget ILRepack.MSBuild.Task avec Install-Package ILRepack.MSBuild.Task
  2. Editez la section AfterBuild de votre fichier .csproj

Voici un exemple simple qui fusionne ExampleAssemblyToMerge.dll dans la sortie de votre projet.

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

10
2018-03-07 20:40



Je vous recommande de vérifier l'utilitaire .NETZ, qui compresse également l'assemblage avec un schéma de votre choix:

http://madebits.com/netz/help.php#single


7
2017-10-09 23:28