705 votes

Incorporation de DLL dans un exécutable compilé

Est-il possible d'intégrer une DLL préexistante dans un exécutable C# compilé (afin de n'avoir qu'un seul fichier à distribuer) ? Si c'est possible, comment s'y prendre ?

Normalement, je n'ai pas de problème à laisser les DLL en dehors et à laisser le programme d'installation s'occuper de tout, mais quelques personnes au travail m'ont posé la question et, honnêtement, je ne sais pas.

0 votes

Je vous recommande de consulter l'utilitaire .NETZ, qui compresse également l'assemblage avec un schéma de votre choix : http://madebits.com/netz/help.php#single

2 votes

C'est possible, mais vous vous retrouverez avec un exécutable volumineux (Base64 sera utilisé pour encoder votre dll).

0 votes

Outre ILMerge si vous ne voulez pas vous embêter avec les commutateurs de ligne de commande, je recommande vraiment ILMerge-Gui . C'est un projet open source, vraiment bon !

827voto

Matthias Points 2423

Je recommande vivement d'utiliser Costura.Fody - est de loin le meilleur moyen et le plus facile d'intégrer des ressources dans votre assemblage. Il est disponible sous forme de paquet NuGet.

Install-Package Costura.Fody

Après l'avoir ajouté au projet, il incorporera automatiquement toutes les références qui sont copiées dans le répertoire de sortie dans le fichier principal assemblage. Vous pourriez vouloir nettoyer les fichiers incorporés en ajoutant une cible à votre projet :

Install-CleanReferencesTarget

Vous pourrez également spécifier s'il faut inclure les pdb, exclure certains assemblages, ou extraire les assemblages à la volée. Pour autant que je sache, les assemblages non gérés sont également pris en charge.

Mise à jour

Actuellement, certaines personnes essaient d'ajouter support pour le DNX .

Mise à jour 2

Pour la dernière version de Fody, vous devrez disposer de MSBuild 16 (donc Visual Studio 2019). La version 4.2.1 de Fody fera MSBuild 15 (référence : Fody n'est pris en charge que sur MSBuild 16 et plus. Version actuelle : 15 )

86 votes

Merci pour cette suggestion géniale. Installez le paquet et vous avez terminé. Il compresse même les assemblages par défaut.

11 votes

Je déteste être un "moi aussi", mais moi aussi - cela m'a épargné beaucoup de maux de tête ! Merci pour la recommandation ! Cela m'a permis d'empaqueter tout ce dont j'ai besoin pour redistribuer dans un seul exe et il est maintenant plus petit que l'exe original et les dlls étaient combinés... Je ne l'utilise que depuis quelques jours, je ne peux donc pas dire que je l'ai mis à l'épreuve, mais si rien de mauvais ne survient, je peux voir que cela devient un outil régulier dans ma boîte à outils. Ça marche !

23 votes

C'est cool. Mais il y a un inconvénient : l'assembleur généré sous Windows n'est plus compatible binairement avec mono Linux. Cela signifie que vous ne pouvez pas déployer l'assemblage sur mono Linux directement.

102voto

Lars Holm Jensen Points 699

Il suffit de cliquer avec le bouton droit de la souris sur votre projet dans Visual Studio, de choisir Project Properties -> Resources -> Add Resource -> Add Existing File 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 l'article original de mon blog : http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/

6 votes

Vous pouvez avoir ce comportement dès la sortie de la boîte. Consultez ma réponse stackoverflow.com/a/20306095/568266

4 votes

Il est également important de noter un commentaire INCROYABLEMENT utile d'AshRowe sur votre blog : si vous avez un thème personnalisé installé, il essaiera de résoudre l'assemblage PresentationFramework.Theme qui se plante et brûle ! Conformément à la suggestion d'AshRowe, vous pouvez simplement vérifier si le dllName contient PresentationFramework comme suit : if (dllName.ToLower().Contains("presentationframework")) return null ;

4 votes

Deux commentaires à ce sujet. Un : vous devriez vérifier si bytes est nul, et si c'est le cas, renvoyer null à cet endroit. Il est possible que la dll soit pas dans les ressources, après tout. Deuxièmement : cela ne fonctionne que si la classe elle-même n'a pas un "using" pour quoi que ce soit de cette assemblée. Pour les outils de ligne de commande, j'ai dû déplacer le code de mon programme actuel dans un nouveau fichier, et créer un petit programme principal qui ne fait que cela et appelle ensuite le programme principal original dans l'ancienne classe.

89voto

Shog9 Points 82052

S'il s'agit en fait d'assemblages gérés, vous pouvez utiliser ILMerge . Pour les DLL natives, vous aurez un peu plus de travail à faire.

Voir aussi : Comment fusionner une dll Windows C++ avec un exe d'application C# ?

0 votes

Je suis intéressé par la fusion de DLL natifs, existe-t-il des matériaux ?

5 votes

0 votes

@BaiyanHuang regarde github.com/boxedapp/bxilmerge L'idée est de faire "ILMerge" pour les Dlls natives.

21voto

nawfal Points 13500

ILMerge peut combiner des assemblages en un seul assemblage à condition que l'assemblage ne contienne que du code géré. Vous pouvez utiliser l'application en ligne de commande, ou ajouter une référence à l'exe et fusionner de manière programmatique. Pour une version GUI, il existe Eazfuscator et aussi .netz qui sont toutes deux gratuites. Les applications payantes comprennent BoxedApp et SmartAssembly .

Si vous devez fusionner des assemblages avec du code non géré, je suggérerais SmartAssembly . Je n'ai jamais eu le 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 soucier de savoir si l'assemblage est géré ou en mode mixte en intégrant la dll à vos ressources et en vous appuyant sur l'assemblage d'AppDomain. ResolveHandler . Il s'agit d'une solution unique qui adopte 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 de les charger depuis son emplacement. Pour éviter le problème de la poule et de l'œuf, vous devez vous assurer que vous déclarez le gestionnaire avant d'accéder à l'assemblage et que vous n'accédez pas aux membres de l'assemblage (ou n'instanciez rien qui ait à voir avec l'assemblage) pendant la partie chargement (résolution de l'assemblage). Veillez également à ce que 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 soient effacés pendant que votre programme accède à la dll, mais au moins c'est une nuisance. AppData est un bon emplacement). Notez également que vous devez écrire les octets à chaque fois, vous ne pouvez pas charger à partir d'un emplacement juste parce que la dll y réside déjà.

Pour les dlls 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'assemblage depuis la mémoire. Comme ceci ou ainsi :

    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 totalement non géré, vous pouvez voir ceci lien ou ce sur la façon de charger de telles dlls.

0 votes

Notez que l'"Action de construction" de la ressource doit être définie sur "Ressource intégrée".

0 votes

@Mavamaarten Pas nécessairement. S'il est ajouté au Resources.resx du projet à l'avance, vous n'avez pas besoin de le faire.

2 votes

EAZfuscator est maintenant commercial.

7voto

Nathan Baulch Points 7994

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X