Incorporation d’une DLL dans une autre en tant que ressource incorporée, puis appel à partir de mon code

J’ai une situation dans laquelle je crée une DLL qui utilise une autre DLL tierce, mais je préférerais pouvoir créer la DLL tierce dans ma DLL au lieu de les conserver ensemble si possible.

Ceci est avec C # et .NET 3.5.

Je voudrais le faire en stockant la DLL tierce en tant que ressource intégrée que je place ensuite à l’endroit approprié lors de l’exécution de la première DLL.

À l’origine, je prévoyais cela en écrivant du code pour placer la DLL tierce à l’emplacement spécifié par System.Reflection.Assembly.GetExecutingAssembly().Location.ToSsortingng() moins le dernier /nameOfMyAssembly.dll . Je peux enregistrer avec succès le tiers .DLL à cet endroit (qui finit par être

C: \ Documents and Settings \ NomUtilisateur \ Paramètres locaux \ Application Data \ assembly \ dl3 \ KXPPAX6Y.ZCY \ A1MZ1499.1TR \ e0115d44 \ 91bb86eb_fe18c901

), mais quand j’obtiens la partie de mon code qui requirejs cette DLL, elle ne peut pas la trouver.

Est-ce que quelqu’un a une idée de ce que je dois faire différemment?

Une fois que vous avez incorporé l’assemblage tiers en tant que ressource, ajoutez du code pour vous abonner à l’événement AppDomain.AssemblyResolve du domaine actuel lors du démarrage de l’application. Cet événement se déclenche chaque fois que le sous-système Fusion du CLR ne parvient pas à localiser un assembly en fonction des règles (stratégies) en vigueur. Dans le gestionnaire d’événements pour AppDomain.AssemblyResolve , chargez la ressource à l’aide de Assembly.GetManifestResourceStream et chargez son contenu sous la forme d’un tableau d’octets dans la surcharge Assembly.Load correspondante. Voici comment une telle implémentation pourrait ressembler à C #:

 AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { var resName = args.Name + ".dll"; var thisAssembly = Assembly.GetExecutingAssembly(); using (var input = thisAssembly.GetManifestResourceStream(resName)) { return input != null ? Assembly.Load(StreamToBytes(input)) : null; } }; 

StreamToBytes pourrait être défini comme:

 static byte[] StreamToBytes(Stream input) { var capacity = input.CanSeek ? (int) input.Length : 0; using (var output = new MemoryStream(capacity)) { int readLength; var buffer = new byte[4096]; do { readLength = input.Read(buffer, 0, buffer.Length); output.Write(buffer, 0, readLength); } while (readLength != 0); return output.ToArray(); } } 

Enfin, comme certains l’ont déjà mentionné, ILMerge peut être une autre option à envisager, quoique un peu plus complexe .

En fin de compte, je l’ai fait presque exactement de la manière suggérée par Raboof (et semblable à celle suggérée par dgvid), sauf quelques modifications mineures et certaines omissions fixes. J’ai choisi cette méthode car elle était la plus proche de ce que je cherchais au départ et n’exigeait pas l’utilisation d’exécutables tiers. Ça marche super bien!

Voici à quoi ressemblait mon code:

EDIT: J’ai décidé de déplacer cette fonction vers un autre assemblage afin de pouvoir le réutiliser dans plusieurs fichiers (je passe juste à Assembly.GetExecutingAssembly ()).

Ceci est la version mise à jour qui vous permet de passer l’assemblage avec les DLL incorporées.

embeddedResourcePrefix est le chemin de la chaîne incorporée, il s’agira généralement du nom de l’assembly suivi de toute structure de dossiers contenant la ressource (par exemple, “MyComapny.MyProduct.MyAssembly.Resources” si la DLL se trouve dans un dossier appelé Resources dans le projet. ). Il suppose également que la DLL a une extension .dll.resource.

  public static void EnableDynamicLoadingForDlls(Assembly assemblyToLoadFrom, ssortingng embeddedResourcePrefix) { AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { // had to add => try { ssortingng resName = embeddedResourcePrefix + "." + args.Name.Split(',')[0] + ".dll.resource"; using (Stream input = assemblyToLoadFrom.GetManifestResourceStream(resName)) { return input != null ? Assembly.Load(StreamToBytes(input)) : null; } } catch (Exception ex) { _log.Error("Error dynamically loading dll: " + args.Name, ex); return null; } }; // Had to add colon } private static byte[] StreamToBytes(Stream input) { int capacity = input.CanSeek ? (int)input.Length : 0; using (MemoryStream output = new MemoryStream(capacity)) { int readLength; byte[] buffer = new byte[4096]; do { readLength = input.Read(buffer, 0, buffer.Length); // had to change to buffer.Length output.Write(buffer, 0, readLength); } while (readLength != 0); return output.ToArray(); } } 

Il existe un outil appelé IlMerge qui peut accomplir ceci: http://research.microsoft.com/~mbarnett/ILMerge.aspx

Ensuite, vous pouvez simplement créer un événement de construction similaire à celui-ci.

Set Path = “C: \ Program Files \ Microsoft \ ILMerge”

ilmerge /out:$(ProjectDir)\Deploy\LevelEditor.exe $ (ProjectDir) \ bin \ Release \ release.exe $ (ProjectDir) \ bin \ Release \ InteractLib.dll $ (ProjectDir) \ bin \ Release \ SpriteLib.dll $ (ProjectDir) \ bin \ Release \ LevelLibrary.dll

J’ai réussi à faire ce que vous décrivez, mais comme la DLL tierce est aussi un assembly .NET, je ne l’écris jamais sur le disque, je le charge simplement depuis la mémoire.

J’obtiens l’assemblage de ressources incorporé comme un tableau d’octets comme ceci:

  Assembly resAssembly = Assembly.LoadFile(assemblyPathName); byte[] assemblyData; using (Stream stream = resAssembly.GetManifestResourceStream(resourceName)) { assemblyData = ReadBytesFromStream(stream); stream.Close(); } 

Ensuite, je charge les données avec Assembly.Load ().

Enfin, j’ajoute un gestionnaire à AppDomain.CurrentDomain.AssemblyResolve pour renvoyer mon assembly chargé lorsque le type loader le regarde.

Voir le .NET Fusion Workshop pour plus de détails.

Vous pouvez y arriver facilement grâce à Netz , un compresseur et packer exécutable .net NET.

Au lieu d’écrire l’assemblage sur le disque, vous pouvez essayer de faire Assembly.Load (byte [] rawAssembly) où vous créez rawAssembly à partir de la ressource incorporée.