web-dev-qa-db-fra.com

Comment une DLL C++ Windows peut-elle être fusionnée dans un fichier exe d'application C #?

J'ai un programme Windows C # qui utilise une DLL C++ pour les entrées/sorties de données. Mon objectif est de déployer l'application en tant que fichier EXE unique.

Quelles sont les étapes pour créer un tel exécutable?

32
Noah

Déploiement unique des codes gérés et non gérés Dimanche, 4 février 2007

Les développeurs .NET adorent le déploiement XCOPY. Et ils aiment les composants d'assemblage simples. Au moins, je me sens toujours un peu mal à l'aise si je dois utiliser un composant et retenir une liste de fichiers à inclure également dans l'assembly principal de ce composant. Alors, récemment, lorsque j'ai eu à développer un composant de code managé et à le compléter avec du code non managé à partir d'un C DLL (merci à Marcus Heege pour son aide!), J'ai réfléchi à la manière de déployer les deux DLL. S'il ne s'agissait que de deux assemblages, j'aurais pu utiliser ILmerge pour les regrouper dans un seul fichier. Mais cela ne fonctionne pas pour les composants de code mixtes avec des DLL gérées et non gérées.

Alors voici ce que je suis venu avec une solution:

J'inclus toutes les DLL que je souhaite déployer avec l'assembly principal de mon composant en tant que ressources incorporées. Ensuite, j'ai configuré un constructeur de classe pour extraire ces DLL comme ci-dessous. Le ctor de classe est appelé une seule fois dans chaque AppDomain, donc je pense que les frais généraux sont négligeables.

namespace MyLib
{
    public class MyClass
    {
        static MyClass()
        {
            ResourceExtractor.ExtractResourceToFile("MyLib.ManagedService.dll", "managedservice.dll");
            ResourceExtractor.ExtractResourceToFile("MyLib.UnmanagedService.dll", "unmanagedservice.dll");
        }

        ...

Dans cet exemple, j'ai inclus deux DLL en tant que ressources, l'une étant une DLL de code non géré et l'autre, un code géré DLL (uniquement à des fins de démonstration), pour montrer comment cette technique fonctionne pour les deux types de code.

Le code pour extraire les DLL dans des fichiers qui leur sont propres est simple:

public static class ResourceExtractor
{
    public static void ExtractResourceToFile(string resourceName, string filename)
    {
        if (!System.IO.File.Exists(filename))
            using (System.IO.Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Create))
                {
                    byte[] b = new byte[s.Length];
                    s.Read(b, 0, b.Length);
                    fs.Write(b, 0, b.Length);
                }
    }
}

Travailler avec un code géré Un tel assemblage est identique, comme d'habitude. Vous y faites référence (ici: ManagedService.dll) dans le projet principal de votre composant (ici: MyLib), mais définissez la propriété Copy Local sur false. De plus, vous créez un lien dans l'assemblage en tant qu'élément existant et définissez l'action de génération sur une ressource incorporée.

Pour le code non managé (ici: UnmanagedService.dll), il vous suffit de créer un lien dans la DLL en tant qu'élément existant et de définir l'action de génération sur une ressource incorporée. Pour accéder à ses fonctions, utilisez l'attribut DllImport comme d'habitude, par exemple.

[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);

C'est tout! Dès que vous créez la première instance de la classe avec le ctor statique, les DLL incorporées sont extraites dans des fichiers qui leur sont propres et sont prêtes à être utilisées comme si vous les aviez déployées en tant que fichiers séparés. Tant que vous avez les autorisations d'écriture pour le répertoire d'exécution, cela devrait fonctionner correctement pour vous. Au moins pour le code prototypique, cette méthode de déploiement à un seul assembleur est très pratique.

Prendre plaisir!

http://weblogs.asp.net/ralfw/archive/2007/02/04/single-Assembly-deployment-of-managed-and-unmanaged-code.aspx

16
Nick

Essayez boxedapp ; il permet de charger toutes les DLL de la mémoire. En outre, il semble que vous puissiez même intégrer le runtime .net. Bon pour créer des applications vraiment autonomes ...

3
Art

Faites un clic 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)
{
    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/

1
Lars Holm Jensen

Utilisez Fody.Costura nuget

  1. Ouvrez votre solution -> Projet -> Gérer les packages Nuget
  2. Rechercher Fody.Costura
  3. Compilez votre projet

C'est tout !

Source: http://www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/

1
automan

Avez-vous essayé ILMerge? http://research.Microsoft.com/~mbarnett/ILMerge.aspx

ILMerge est un utilitaire pouvant être utilisé pour fusionner plusieurs assemblys .NET en un seul assemblage. Il est disponible gratuitement à partir de la page Outils et utilitaires du centre des développeurs Microsoft .NET Framework. 

Si vous construisez le C++ DLL avec l'indicateur /clr (tout ou partiellement C++/CLI), cela devrait alors fonctionner:

ilmerge /out:Composite.exe MyMainApp.exe Utility.dll

Cependant, cela ne fonctionnera pas avec un Windows DLL ordinaire (natif). 

1
Raithlin

Smart Assembly peut le faire et plus encore. Si votre dll a un code non managé, il ne vous laissera pas fusionner les dll avec un seul assembly, mais il pourra plutôt intégrer les dépendances requises en tant que ressources à votre exe principal. Son revers, ce n'est pas gratuit.

Vous pouvez le faire manuellement en incorporant des dll à vos ressources, puis en vous appuyant sur la variable d'assembly ResolveHandler de AppDomain. En ce qui concerne les dll en mode mixte, j'ai trouvé de nombreuses variantes et saveurs de l'approche ResolveHandler qui ne fonctionnaient pas pour moi (toutes les méthodes qui lisent les octets dll en mémoire et les lisent). Ils ont tous travaillé pour les dll gérés. Voici ce qui a fonctionné pour moi:

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 à partir de leur emplacement. Pour éviter le problème poulet et œuf, vous devez vous assurer de déclarer le gestionnaire avant d'accéder à Assembly et de ne pas accéder aux membres de Assembly (ni instancier quoi que ce soit qui doit traiter avec Assembly) à l'intérieur de la partie de chargement (résolution de l'Assemblée). 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 (cela ne veut pas dire qu’ils seront supprimés lorsque votre programme accède à la dll, mais au moins c’est une nuisance. AppData est bon emplacement). Notez également que vous devez écrire les octets à chaque fois, vous ne pouvez pas charger depuis l'emplacement juste parce que la dll y réside déjà.

Si l'assembly est complètement non géré, vous pouvez voir ceci link ou this quant à la façon de charger ces dll.

0
nawfal

Thinstall est une solution. Pour une application Windows native, je vous suggérerais d'incorporer DLL en tant qu'objet ressource binaire, puis de l'extraire au moment de l'exécution avant que vous n'en ayez besoin.

0
titanae