web-dev-qa-db-fra.com

Incorporation de DLL dans .exe dans Visual C # 2010

Je travaille sur un programme C # qui utilise iTextSharp.dll et WebCam_Capture.dll. Lorsque je crée le programme, il crée un fichier exécutable dans le dossier de débogage et copie également ces deux dll dans le dossier de débogage comme prévu. Je veux les fusionner en un seul exécutable, mais j'ai échoué. Ces deux bibliothèques sont visibles dans les références normalement dans l'explorateur de solutions. Je les ajoute aussi comme ressources. La taille de l'exécutable a grossi, ce qui équivaut à la somme de trois fichiers. Néanmoins, l'exécutable a toujours besoin de ces bibliothèques dans son répertoire ... J'ai joué avec la propriété "action de construction" des fichiers de ressources, mais aucun changement. J'ai aussi essayé ILmerge mais cela m'a causé une erreur. donc qu'est ce que je devrais faire?

Mise à jour: Voici ce que je reçois d’ILmerge:

An exception occurred during merging:
Unresolved Assembly reference not allowed: System.Core.
at System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode Assembly)
   at System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)

Soit dit en passant, il s’agit simplement d’une application Windows, un formulaire à remplir et à imprimer au format PDF avec une photo prise par webcam, le cas échéant. Merci a tous!

14
Mehmed

Vous pouvez utiliser ILMerge pour fusionner plusieurs assemblys. Vous avez déjà dit que vous avez fait cela, et vous avez reçu une erreur. Bien que je ne sache pas pourquoi, vous pouvez utiliser une alternative: si les bibliothèques sont open source (et leurs licences sont compatibles avec la vôtre), vous pouvez télécharger le code source, l’ajouter à votre projet et le compiler. Cela aboutira à une seule assemblée.

La page ILMerge répertorie également Le blog de Jeffrey Richter comme une autre alternative pour résoudre votre problème:

De nombreuses applications consistent en un fichier EXE qui dépend de nombreux fichiers DLL . Lors du déploiement de cette application, tous les fichiers doivent être déployés. Cependant, il existe une technique que vous pouvez utiliser pour déployer Juste un fichier EXE unique. Commencez par identifier tous les fichiers DLL dont dépend le fichier EXE Qui ne sont pas fournis avec le cadre Microsoft .NET Proprement dit. Ajoutez ensuite ces DLL à votre projet Visual Studio. Pour chaque fichier DLL que vous ajoutez, affichez ses propriétés et remplacez son "Action de génération" par "Ressource incorporée". force le compilateur C # à incorporer le (s) fichier (s) DLL dans votre fichier EXE, et vous pouvez déployer ce fichier EXE . 

Au moment de l'exécution, le CLR ne pourra pas trouver les assemblys DLL dépendants , Ce qui pose problème. Pour résoudre ce problème, lorsque votre application S’initialise, enregistrez une méthode de rappel avec l’événement ResolveAssembly de AppDomain. Le code devrait ressembler à ceci: 

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { 
   String resourceName = "AssemblyLoadingAndReflection." + 
       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); 
   }   
}; 

Maintenant, la première fois qu'un thread appelle une méthode qui référence un type dans Un fichier DLL dépendant, l'événement AssemblyResolve sera déclenché et le code de rappel Présenté ci-dessus trouvera la ressource DLL incorporée souhaitée et chargez-la en appelant une surcharge de la méthode Load de Assembly qui prend un argument Byte [].

9
foxy
  1. Ajoutez les fichiers DLL à votre projet Visual Studio.
  2. Pour chaque fichier, allez dans "Propriétés" et réglez son action de construction sur "Ressources incorporées".
  3. Sur votre code, récupérez la ressource à l'aide de GetManifestResourceStream ("DLL_Name_Here"), qui renvoie un flux pouvant être chargé.
  4. Ecrivez un gestionnaire d'événement "AssemblyResolve" pour le charger.

Voici le code:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;

namespace WindowsForm
{
    public partial class Form1 : Form
    {
        Dictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();            

        public Form1()
        {
            InitializeComponent();
            AppDomain.CurrentDomain.AssemblyResolve += FindDLL;
        }

        private Assembly FindDLL(object sender, ResolveEventArgs args)
        {
            string keyName = new AssemblyName(args.Name).Name;

            // If DLL is loaded then don't load it again just return
            if (_libs.ContainsKey(keyName)) return _libs[keyName];


            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("YourNamespaceGoesHere." + keyName + ".dll"))  // <-- To find out the Namespace name go to Your Project >> Properties >> Application >> Default namespace
            {
                byte[] buffer = new BinaryReader(stream).ReadBytes((int)stream.Length);
                Assembly assembly = Assembly.Load(buffer);
                _libs[keyName] = Assembly;
                return Assembly;
            }
        }

        //
        // Your Methods here
        //

    }
}

J'espère que ça aide, Pablo

7
Pabinator

J'ai modifié un peu le code de Pablo et cela a fonctionné pour moi.
Le nom de la ressource de la DLL n’est pas entré correctement.

IDictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();

public Form1()
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    InitializeComponent();
}

// dll handler
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string keyName = new AssemblyName(args.Name).Name;

    // If DLL is loaded then don't load it again just return
    if (_libs.ContainsKey(keyName)) return _libs[keyName];

    using (Stream stream = Assembly.GetExecutingAssembly()
           .GetManifestResourceStream(GetDllResourceName("itextsharp.dll")))  // <-- To find out the Namespace name go to Your Project >> Properties >> Application >> Default namespace
    {
        byte[] buffer = new BinaryReader(stream).ReadBytes((int)stream.Length);
        Assembly assembly = Assembly.Load(buffer);
        _libs[keyName] = Assembly;
        return Assembly;
    }
}

private string GetDllResourceName(string dllName)
{
    string resourceName = string.Empty;
    foreach (string name in Assembly.GetExecutingAssembly().GetManifestResourceNames())
    {
        if (name.EndsWith(dllName))
        {
            resourceName = name;
            break;
        }
    }

    return resourceName;
}
3
Amit Bhagat

La réponse que vous recherchez:

// To embed a dll in a compiled exe:
// 1 - Change the properties of the dll in References so that Copy Local=false
// 2 - Add the dll file to the project as an additional file not just a reference
// 3 - Change the properties of the file so that Build Action=Embedded Resource
// 4 - Paste this code before Application.Run in the main exe
AppDomain.CurrentDomain.AssemblyResolve += (Object sender, ResolveEventArgs args) =>
    {
        String thisExe = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
        System.Reflection.AssemblyName embeddedAssembly = new System.Reflection.AssemblyName(args.Name);
        String resourceName = thisExe + "." + embeddedAssembly.Name + ".dll";

        using (var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            return System.Reflection.Assembly.Load(assemblyData);
        }
    };
3
userSteve

Ajoutez ce code de fonction anonyme au sommet de notre constructeur d’application. Cela ajoutera des dll à partir de ressources incorporées dans le même projet.

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    string resourceName = new AssemblyName(args.Name).Name + ".dll";
    string resource = Array.Find(this.GetType().Assembly.GetManifestResourceNames(), element => element.EndsWith(resourceName));

    using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource))
    {
        Byte[] assemblyData = new Byte[stream.Length];
        stream.Read(assemblyData, 0, assemblyData.Length);
        return Assembly.Load(assemblyData);
    }
};
1
Mahsh Nikam

Je sais que ce sujet est ancien, mais je l’écrirai pour les futures personnes qui voudront l’utiliser.

je me base sur le code de userSteve .

je suggère de changer cela.

String thisExe = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;

dans cette 

String thisExe = System.Reflection.Assembly.GetExecutingAssembly().EntryPoint.DeclaringType.Namespace;

de cette façon, cela fonctionnerait même si l'espace de noms est différent du nom de l'Assemblée

aussi, si vous voulez utiliser DLL depuis le répertoire, vous pouvez l'utiliser comme ça (le répertoire Resources as Example)

String resourceName = thisExe + ".Resources." + embeddedAssembly.Name + ".dll";

si vous ne parvenez toujours pas à trouver où placer ce code dans l'application de formulaire C #, collez-le dans le fichier "Program.cs" au-dessus de la ligne:

Application.Run(new Form_1());

et en dessous des lignes:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
0
Asmendorius

Découvrez l'événement AssemblyResolve sur le domaine d'application.

Je n'ai pas d'exemple, mais vous vérifiez essentiellement ce qui est demandé et retransmettez la DLL de ressources. Je pense que LinqPAD le fait bien - vous pouvez jeter un coup d'œil à l'implémentation de Joseph Albahari avec un décompilateur, etc.

0
Paul Kohler