web-dev-qa-db-fra.com

Fusion de dll dans un seul fichier .exe avec wpf

Je travaille actuellement sur un projet dans lequel nous avons beaucoup de dépendances. Je voudrais compiler toutes les dll référencées dans le fichier .exe de la même manière que vous le feriez avec des ressources incorporées. J'ai essayé ILMerge mais il ne peut pas gérer les ressources .xaml.

Ma question est donc la suivante: existe-t-il un moyen de fusionner un projet WPF avec plusieurs dépendances en un seul fichier .exe?

49
Farawin

Réacteur .NET a la particularité de fusionner les assemblages et qu’il n’est pas très coûteux.

7
Hemant

http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a--wpf-application

Cela a fonctionné comme un charme pour moi :) et c'est totalement gratuit.

Ajouter du code au cas où le blog disparaîtrait jamais.

1) Ajoutez ceci à votre fichier .csproj:

<Target Name="AfterResolveReferences">
  <ItemGroup>
    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>

2) Faites votre Program.cs principal ressembler à ceci:

[STAThreadAttribute]
public static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
    App.Main();
}

3) Ajoutez la méthode OnResolveAssembly:

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    AssemblyName assemblyName = new AssemblyName(args.Name);

    var path = assemblyName.Name + ".dll";
    if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);

    using (Stream stream = executingAssembly.GetManifestResourceStream(path))
    {
        if (stream == null) return null;

        var assemblyRawBytes = new byte[stream.Length];
        stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
        return Assembly.Load(assemblyRawBytes);
    }
}
53
Wegged

Costura Fody est un outil open source conçu pour gérer la fusion des assemblages wpf.

https://github.com/Fody/Costura#how-it-works

38
Simon

{smartassembly} est l'un de ces produits. Il peut obsfucate ou incorporer vos dll.

Essayez ceci: http://www.smartassembly.com/

Vous pouvez également apporter de nombreuses améliorations à votre application afin qu'elle s'exécute plus rapidement.

Et oui. Vous pouvez l'utiliser pour WPF.

Mise à jour du 8/06/2015: ILRepack 2.0.0 (une alternative open source à ILMerge) prend désormais en charge la plupart des cas WPF fusionnés: https://Twitter.com/Gluckies/status/607680149157462016

12
Timotei

Comme indiqué sur Site Web ILMerge , traitez ces dll comme des ressources, de Jeffrey Richter ici

De nombreuses applications consistent en un fichier EXE qui dépend de nombreuses DLL des dossiers. Lors du déploiement de cette application, tous les fichiers doivent être déployé. Cependant, il existe une technique que vous pouvez utiliser pour déployer juste un seul fichier EXE. Commencez par identifier tous les fichiers DLL que votre fichier Le fichier EXE qui en dépend ne fait pas partie de Microsoft .NET Cadre lui-même. Ajoutez ensuite ces DLL à votre projet Visual Studio . Pour chaque fichier DLL que vous ajoutez, affichez ses propriétés et modifiez-le «Action de compilation» sur «Ressource incorporée». Le compilateur C # devient alors incorporez le (s) fichier (s) DLL dans votre fichier EXE, et vous pourrez déployer celui-ci Fichier EXE. Au moment de l'exécution, le CLR ne pourra pas trouver la personne à charge DLL assemblages, ce qui pose problème. Pour résoudre ce problème, lorsque votre application initialise, enregistre une méthode de rappel avec le .__ d'AppDomain. Événement ResolveAssembly. 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 fichier Le code de rappel indiqué ci-dessus trouvera la ressource DLL incorporée souhaitée et chargez-le en appelant une surcharge de la méthode Load de Assembly qui prend un Byte [] comme argument.

9
Matthieu

Utilisez Costura.Fody - Il est disponible en tant que Nuget Pkg pour le meilleur moyen le plus simple d’incorporer des ressources à votre Assemblée.

Install-Package Costura.Fody

Une fois ajouté au projet, il intégrera automatiquement toutes les références ajoutées à votre assemblage principal.

6
Dark Knight

Essayez .Netz ( http://madebits.com/netz/ ) - c'est gratuit (comme dans la bière) et fait de bonnes choses si votre cible est un exe.

2
Nils

Voici une version modifiée du code cité par Matthieu qui n'exige pas de connaître l'espace de noms pour extraire le code. Pour WPF, mettez ceci dans le code d'événement de démarrage de l'application.

AppDomain.CurrentDomain.AssemblyResolve += (s, args) =>
{
    // Note: Requires a using statement for System.Reflection and System.Diagnostics.
    Assembly assembly = Assembly.GetExecutingAssembly();
    List<string> embeddedResources = new List<string>(Assembly.GetManifestResourceNames());
    string assemblyName = new AssemblyName(args.Name).Name;
    string fileName = string.Format("{0}.dll", assemblyName);
    string resourceName = embeddedResources.Where(ern => ern.EndsWith(fileName)).FirstOrDefault();
    if (!string.IsNullOrWhiteSpace(resourceName))
    {
        using (var stream = Assembly.GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            var test = Assembly.Load(assemblyData);
            string namespace_ = test.GetTypes().Where(t => t.Name == assemblyName).Select(t => t.Namespace).FirstOrDefault();
#if DEBUG
            Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", fileName, namespace_));
#endif
            return Assembly.Load(assemblyData);
        }
    }

    return null;
}; 

Pour les rendre disponibles au moment de la compilation, je crée un dossier nommé ExternalDLLs, y copie les dll et les définit sur EmbeddedResource comme indiqué ci-dessus. Pour les utiliser dans votre code, vous devez toujours leur attribuer une référence, mais définissez l'option Copie locale sur Faux. Pour que le code soit compilé proprement, sans erreur, vous devez également définir, à l'aide d'instructions dans votre code, les espaces de noms des dll.

Voici un petit utilitaire qui parcourt les noms des ressources incorporées et affiche leurs espaces de noms dans la fenêtre de sortie.

private void getEmbeddedResourceNamespaces()
{
    // Note: Requires a using statement for System.Reflection and System.Diagnostics.
    Assembly assembly = Assembly.GetExecutingAssembly();
    List<string> embeddedResourceNames = new List<string>(Assembly.GetManifestResourceNames());
    foreach (string resourceName in embeddedResourceNames)
    {
        using (var stream = Assembly.GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            try
            {
                var test = Assembly.Load(assemblyData);
                foreach (Type type in test.GetTypes())
                {
                    Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", type.Name, type.Namespace));
                }
            }
            catch 
            {
            }
        }
    }
}
1
j2associates
  1. ajoutez ceci au fichier .csprofj:

>

<Target Name="AfterResolveReferences">
  <ItemGroup>
    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>
  1. clic droit projet/propriétés/application/objet starup/sélectionnez Sinhro.Program

  2. ajoutez ceci à votre fichier program.cs:

    using System.Reflection; en utilisant System.IO; using System.Globalization;

    [STAThreadAttribute]
    static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
        ...
    
    
    private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
    {
        Assembly executingAssembly = Assembly.GetExecutingAssembly();
        AssemblyName assemblyName = new AssemblyName(args.Name);
        string path = assemblyName.Name + ".dll";
        if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
        {
            path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
        }
        using (Stream stream = executingAssembly.GetManifestResourceStream(path))
        {
            if (stream == null)
                return null;
            byte[] assemblyRawBytes = new byte[stream.Length];
            stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
            return Assembly.Load(assemblyRawBytes);
        }
    }   
    

source: http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wwpf-application

0
Tone Škoda