Incorporation de DLL dans un exécutable compilé

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

Normalement, je suis cool de laisser les DLL à l’extérieur 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.

Je recommande fortement d’utiliser Costura.Fody – de loin le moyen le plus simple et le plus simple pour intégrer des ressources dans votre assembly. Il est disponible sous forme de package NuGet.

Install-Package Costura.Fody 

Après l’ajout au projet, il intégrera automatiquement toutes les références copiées dans le répertoire de sortie dans votre assembly principal . Vous souhaiterez peut-être nettoyer les fichiers incorporés en ajoutant une cible à votre projet:

 Install-CleanReferencesTarget 

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

Mettre à jour

Actuellement, certaines personnes essaient d’append un support pour DNX .

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

Voir aussi: Comment une DLL C ++ peut-elle être fusionnée dans un exe d’application C #?

Cliquez simplement 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 dans votre App.xaml.cs ou équivalent.

 public App() { AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve); } System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { ssortingng dllName = args.Name.Contains(',') ? args.Name.Subssortingng(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/

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 en un seul paquet binary.
  • IL-Repack est une alternative FLOSS à ILMerge, avec quelques fonctionnalités supplémentaires.

En outre, cela peut être combiné avec le Mono Linker , qui supprime le code inutilisé et réduit donc l’assemblage résultant.

Une autre possibilité consiste à utiliser .NETZ , qui permet non seulement de compresser un assemblage, mais peut également intégrer les DLL directement dans l’exécutable. La différence avec les solutions mentionnées ci-dessus est que .NETZ ne les fusionne pas, ils restnt des assemblys séparés mais sont regroupés dans un seul package.

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

ILMerge peut combiner des assemblys en un seul ensemble à condition que l’assembly n’ait que du code géré. Vous pouvez utiliser l’application en ligne de commande ou append une référence à l’exécutable et fusionner par programme. Pour une version GUI, il y a Eazfuscator , et aussi .Netz, qui sont tous deux gratuits. Les applications payantes incluent BoxedApp et SmartAssembly .

Si vous devez fusionner des assemblys avec du code non géré, je suggère SmartAssembly . Je n’ai jamais eu de hoquet avec SmartAssembly mais avec tous les autres. Ici, il peut intégrer les dépendances requirejses 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 incorporant dll à vos ressources et en vous appuyant sur Assembly 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) => { ssortingng assemblyName = new AssemblyName(args.Name).Name; if (assemblyName.EndsWith(".resources")) return null; ssortingng dllName = assemblyName + ".dll"; ssortingng 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 à 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 à ce que GetMyApplicationSpecificPath() ne soit 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. AppData est un bon emplacement). 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 totalement non géré, vous pouvez voir ce lien ou ceci pour savoir comment charger ces DLL.

L’ extrait de Jeffrey Richter est très bon. En bref, ajoutez la bibliothèque en tant que ressources incorporées et ajoutez un rappel avant tout. 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) => { Ssortingng dllName = new AssemblyName(bargs.Name).Name + ".dll"; var assem = Assembly.GetExecutingAssembly(); Ssortingng 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); } }; 

Pour élargir la réponse à @ Bobby ci- dessus. Vous pouvez modifier votre fichier .csproj pour utiliser IL-Repack afin de regrouper automatiquement tous les fichiers dans un seul assemblage lorsque vous les comstackz.

  1. Installez le package 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.

         

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

Vous pouvez append les DLL en tant que ressources incorporées, puis faire en sorte que votre programme les décompresse dans le répertoire de l’application au démarrage (après avoir vérifié si elles existent déjà).

Les fichiers d’installation sont si faciles à faire, cependant, que je ne pense pas que cela en vaudrait la peine.

EDIT: Cette technique serait facile avec les assemblages .NET. Avec les DLL non .NET, cela demanderait beaucoup plus de travail (il faudrait trouver où décompresser les fichiers et les enregistrer, etc.).

Vérifiez boxedapp

Il peut intégrer une DLL dans n’importe quelle application. Écrit en C # aussi, bien sûr 🙂

J’espère que cela aide.

Ni l’approche ILMerge ni celle de Lars Holm Jensen qui gère l’événement AssemblyResolve ne fonctionneront pour un hôte de plug-in. Dites que l’exécutable H charge dynamicment l’assemblage P et y accède via l’interface IP définie dans un assemblage distinct. Pour intégrer IP dans H , il faut modifier légèrement le code de Lars:

 Dictionary loaded = new Dictionary(); AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { Assembly resAssembly; ssortingng dllName = args.Name.Contains(",") ? args.Name.Subssortingng(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; }; 

Astuce pour gérer les tentatives répétées de résolution du même assembly et renvoyer le fichier existant au lieu de créer une nouvelle instance.

EDIT: Ne gâchez pas la sérialisation de .NET, assurez-vous de renvoyer null pour tous les assemblys qui ne sont pas incorporés dans le vôtre, par défaut pour le comportement standard. Vous pouvez obtenir une liste de ces bibliothèques en:

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

et retournez simplement null si l'assembly transmis n'appartient pas à IncludedAssemblies .

SmartAssembly, à SmartAssembly.com , est un autre produit capable de gérer cela avec élégance. Ce produit, en plus de fusionner toutes les dépendances en une seule DLL, (éventuellement) masquer votre code, supprimer les métadonnées supplémentaires pour réduire la taille du fichier et peut également optimiser l’IL pour augmenter les performances d’exécution. Il y a aussi une sorte de fonctionnalité globale de gestion des exceptions et de génération de rapports qu’elle ajoute à votre logiciel (si vous le souhaitez), que je n’ai pas pris le temps de comprendre, mais qui pourrait être utile. Je crois qu’il a aussi une API de ligne de commande pour que vous puissiez l’intégrer à votre processus de construction.

ILMerge fait exactement ce que vous voulez.

Outre ILMerge , si vous ne voulez pas vous soucier des commutateurs de ligne de commande, je vous recommande vraiment ILMerge-Gui . C’est un projet open source, vraiment bien!

Cela peut paraître simpliste, mais WinRar permet de compresser un tas de fichiers sur un exécutable auto-extractible.
Il a beaucoup d’options configurables: icône finale, extraire des fichiers vers un chemin donné, fichier à exécuter après extraction, logo / texte personnalisé pour la fenêtre contextuelle affichée lors de l’extraction, pas de fenêtre contextuelle, texte du contrat de licence, etc.
Peut être utile dans certains cas.

J’utilise le compilateur csc.exe appelé à partir d’un script .vbs.

Dans votre script xyz.cs, ajoutez les lignes suivantes après les directives (mon exemple concerne le SSH Renci):

 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" 

Les balises ref, res et ico seront récupérées par le script .vbs ci-dessous pour former la commande csc.

Ensuite, ajoutez l’appelant résolveur d’assemblage dans la principale:

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

… et append le résolveur quelque part dans la classe:

     static Assembly CurrentDomain_AssemblyResolve (expéditeur d'object, arguments ResolveEventArgs)
     {
         Ssortingng resourceName = new AssemblyName (args.Name) .Name + ".dll";

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

     }

Je nomme le script vbs pour correspondre au nom de fichier .cs (par exemple, ssh.vbs recherche ssh.cs); Cela rend l’exécution du script beaucoup plus facile, mais si vous n’êtes pas un idiot comme moi, un script générique pourrait récupérer le fichier .cs cible à partir d’un glisser-déposer:

     Dim nom_, oShell, fso
     Définissez oShell = CreateObject ("Shell.Application")
     Définissez fso = CreateObject ("Scripting.fileSystemObject")

     'PRENEZ LE NOM DE SCRIPT VBS COMME NOM DE FICHIER CIBLE
     '################################################
     name_ = Split (wscript.ScriptName, ".") (0)

     'OBTENIR LES NOMS DE LA DLL EXTERNE ET ICÔNE DU FICHIER .CS
     '################################################# ######
     Constante OPEN_FILE_FOR_READING = 1
     Définissez objInputFile = fso.OpenTextFile (name_ & ".cs", 1)

     'LISEZ TOUT DANS UN ENSEMBLE
     '#############################
     inputData = Split (objInputFile.ReadAll, vbNewline)

     Pour chaque strData In inputData

         si laissé (strData, 7) = "// + ref>" alors 
             csc_references = csc_references & "/ reference:" & sortingm (remplacez (strData, "// + ref>", "")) & ""
         fin si

         si laissé (strData, 7) = "// + res>" alors 
             csc_resources = csc_resources & "/ resource:" & sortingm (remplacez (strData, "// + res>", "")) & ""
         fin si

         si laissé (strData, 7) = "// + ico>" alors 
             csc_icon = "/ win32icon:" & sortingm (remplacez (strData, "// + ico>", "")) & ""
         fin si
     Prochain

     objInputFile.Close


     'COMPILER LE FICHIER
     '################
     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)

Il est possible mais pas facile de créer un assemblage hybride natif / géré en C #. Si vous utilisiez C ++ à la place, cela serait beaucoup plus facile, car le compilateur Visual C ++ peut créer des assemblages hybrides aussi facilement que tout le rest.

À moins que vous ayez une exigence ssortingcte de produire un assemblage hybride, je suis d’accord avec MusiGenesis pour dire que cela ne vaut pas vraiment la peine de faire de C #. Si vous avez besoin de le faire, envisagez peut-être de passer à C ++ / CLI à la place.

En général, vous avez besoin d’un outil de post-construction pour effectuer une fusion d’assemblage comme vous le décrivez. Il existe un outil gratuit appelé Eazfuscator (eazfuscator.blogspot.com/) conçu pour la gestion des bytecodes qui gère également la fusion des assemblages. Vous pouvez append ceci dans une ligne de commande post-build avec Visual Studio pour fusionner vos assemblys, mais votre kilométrage variera en raison de problèmes qui surviendront dans les scénarios de fusion d’assemblages non sortingvalents.

Vous pouvez également vérifier si la construction permet à NANT de fusionner des assemblys après la construction, mais je ne suis pas suffisamment familiarisé avec NANT pour savoir si la fonctionnalité est intégrée ou non.

Il existe également de nombreux plug-ins Visual Studio qui effectueront la fusion d’assemblage dans le cadre de la création de l’application.

Si vous n’avez pas besoin de cela automatiquement, il existe un certain nombre d’outils comme ILMerge qui fusionnent les assemblages .net en un seul fichier.

Le plus gros problème que j’ai rencontré lors de la fusion des assemblys est qu’ils utilisent des espaces de noms similaires. Ou pire, faites référence à différentes versions de la même DLL (mes problèmes étaient généralement avec les fichiers dll NUnit).

J’ai essayé cette solution à code-project qui intègre la DLL: http://www.codeproject.com/Articles/528178/Load-DLL-From-Embedded-Resource

Et ça a bien fonctionné.