Insert one dll inside another as an embedded resource, and then call it from my code

I have a situation where I have a DLL that I create that uses a different third-party DLL, but I would rather be able to create a third-party DLL in my DLL, instead of holding both of them together if possible.

This is with C # and .NET 3.5.

The way I would like to do this is to save the third-party DLL as an embedded resource, which I then place in the appropriate place during the execution of the first DLL.

The way I originally planned to do this was to write code to put a third-party DLL in the location indicated by System.Reflection.Assembly.GetExecutingAssembly (). Location.ToString () minus the last /nameOfMyAssembly.dll. I can successfully save the third-party .DLL in this place (which ends (C: \ Documents and Settings \ myUserName \ Local Settings \ Application Data \ assembly \ dl3 \ KXPPAX6Y.ZCY \ A1MZ1499.1TR \ e0115d44 \ 91bb86eb_fe18c901), but when I get to the part of my code requiring this dll, it cannot find it.

Does anyone have any ideas what I need to do differently?

+53
c # dll
Sep 18 '08 at 20:42
source share
6 answers

After you have implemented a third-party assembly as a resource, add code to subscribe to the AppDomain.AssemblyResolve event of the current domain during application startup. This event is fired whenever the Fusion CLR subsystem cannot find the assembly in accordance with the current method (policy). In the event handler for AppDomain.AssemblyResolve load the resource using Assembly.GetManifestResourceStream and feed its contents as a byte array to the corresponding Assembly.Load overload. The following is an example of how one such implementation might look like in 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; } }; 

where StreamToBytes can be defined as:

 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(); } } 

Finally, as already noted, ILMerge may be another option, although somewhat more meaningful.

+40
Sep 18 '08 at 21:40
source share

In the end, I did it almost as raboof suggested (and looks like what dgvid suggested), with the exception of some minor changes and some omissions. I chose this method because it was closest to what I was looking for in the first place, and did not require the use of any third-party executables, etc. It works great!

Here is what my code looked like:

EDIT: I decided to move this function to another assembly in order to reuse it in multiple files (I just pass Assembly.GetExecutingAssembly ()).

This is an updated version that allows you to go through an assembly with embedded DLLs.

embeddedResourcePrefix is ​​the string path to the embedded resource, usually the name of the assembly, followed by any folder structure that contains the resource (for example, "MyComapny.MyProduct.MyAssembly.Resources" if the DLL is in the Resources folder in the project). It also suggests that the dll has the .dll.resource extension.

  public static void EnableDynamicLoadingForDlls(Assembly assemblyToLoadFrom, string embeddedResourcePrefix) { AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { // had to add => try { string 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(); } } 
+17
Sep 19 '08 at 17:11
source share

There is an IlMerge tool that can accomplish this: http://research.microsoft.com/~mbarnett/ILMerge.aspx

Then you can just make the build event look like the following.

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

+11
Sep 18 '08 at 20:48
source share

You can easily achieve this using Netz , the .net NET compiler and packer.

+9
Sep 18 '08 at 21:02
source share

I had success doing what you are describing, but since the third-party DLL is also a .NET assembly, I never write it to disk, I just load it from memory.

I get a built-in resource assembly as an array of bytes, for example:

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

Then I load the data using Assembly.Load ().

Finally, I add a handler to AppDomain.CurrentDomain.AssemblyResolve to return the loaded assembly when the type loader looks.

See the .NET Fusion Workshop for more details.

+8
Sep 18 '08 at 21:17
source share

Instead of writing the assembly to disk, you can try running Assembly.Load (byte [] rawAssembly), where you create rawAssembly from the built-in resource.

+2
Sep 18 '08 at 20:56
source share