Embedding One Dll Inside Another as an Embedded Resource and Then Calling It from My Code

Embedding one dll inside another as an embedded resource and then calling it from my code

Once you've embedded the third-party assembly as a resource, add code to subscribe to the AppDomain.AssemblyResolve event of the current domain during application start-up. This event fires whenever the Fusion sub-system of the CLR fails to locate an assembly according to the probing (policies) in effect. In the event handler for AppDomain.AssemblyResolve, load the resource using Assembly.GetManifestResourceStream and feed its content as a byte array into the corresponding Assembly.Load overload. Below is how one such implementation could 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 could 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 a few have already mentioned, ILMerge may be another option to consider, albeit somewhat more involved.

Put a dll inside a dll in C#

Embedding as a resource and ILMerge was already mentioned in the comments. I'd like to give another alternative: ILRepack. It works like ILMerge but unlike ILMerge is still actively developed.

VB.NET embedded DLL in another DLL as embedded resource?

We use this technique in VB.NET in Visual Studio 2008...

First, the project needs to know to include the "other" dll as an Embedded Resource. In the Solution Explorer, add the dll as a file to your project (not as a Reference). Then, open the Properties for the file and set the Build Action to "Embedded Resource." It is recommended that you create a local copy of the dll file in the structure of your project rather than linking to some other location. Once the project includes the dll file, you can then add the reference to that copy of the dll so that you can use its contents at design-time.

That ensures that the "other" dll is included in your compiled dll, but it doesn't make it automatically load when needed. That's where the following code comes in:

Public Module Core

Private _initialized As Boolean

Public Sub EnsureInitialized()
If Not _initialized Then
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf AssemblyResolve
_initialized = True
End If
End Sub

Private Function AssemblyResolve(ByVal sender As Object, ByVal e As ResolveEventArgs) As Assembly
Dim resourceFullName As String = String.Format("[CONTAINER ASSEMBLY].{0}.dll", e.Name.Split(","c)(0))
Dim thisAssembly As Assembly = Assembly.GetExecutingAssembly()
Using resource As Stream = thisAssembly.GetManifestResourceStream(resourceFullName)
If resource IsNot Nothing Then Return Assembly.Load(ToBytes(resource))
Return Nothing
End Using
End Function

Private Function ToBytes(ByVal instance As Stream) As Byte()
Dim capacity As Integer = If(instance.CanSeek, Convert.ToInt32(instance.Length), 0)

Using result As New MemoryStream(capacity)
Dim readLength As Integer
Dim buffer(4096) As Byte

Do
readLength = instance.Read(buffer, 0, buffer.Length)
result.Write(buffer, 0, readLength)
Loop While readLength > 0

Return result.ToArray()
End Using
End Function

End Module

Place this Module somewhere in your project and be sure to call the EnsureInitialized method to attach the AssemblyResolve handler before calling any other code in your dll.

NOTE: You'll need to replace [CONTAINER ASSEMBLY] with the name of your dll.

Also note that the above code is a stripped-down version of what we actually use because ours includes log4net logging messages at strategic places. The logging messages aren't necessary for the true functionality, so I removed them for brevity and clarity.

The major caveat to this approach is that the AssemblyResolve handler has to be attached manually. Even if you can't set things up so that EnsureInitialized is called only once during the initialization of the consuming code, you can call EnsureInitialized within any of your own modules that require the "other" dll for execution. This makes the code a little more delicate because you have to remember to make that initialization call, but it does allow you to sleep at night knowing that the dll will be available when you need it.

In my experience, some "other" dlls don't play well when they are provided as embedded resources, so you might need to play around a bit to get things working.

Final Note: I've never used ArcMap Component, so Your Mileage May Vary!

getting an embedded resource in a single dll made up of multiple class libraries

I would not use Assembly.GetCallingAssembly(). I would use typeof(SomeClassNextToXmlFile).Assembly that way if you are calling the dll with the embedded resource from a exe file it won't go looking in the exe for the resource. Also you may want to try using Reflector and make sure the resource you are looking for is where you think it is.

Embedding assemblies inside another assembly

Take a look at ILMerge for merging assemblies.

I'm also a little confused about why .NET assemblies are .dll files. Didn't this format exist before .NET?

Yes.

Are all .NET assemblies DLLs,

Either DLLs or EXE normally - but can also be netmodule.

but not all DLLs are .NET assemblies?

Correct.

Why do they use the same file format and/or file extension?

Why should it be any different - it serves the same purpose!

Embedding DLLs in a compiled executable

I highly recommend to use Costura.Fody - by far the best and easiest way to embed resources in your assembly. It's available as NuGet package.

Install-Package Costura.Fody

After adding it to the project, it will automatically embed all references that are copied to the output directory into your main assembly. You might want to clean the embedded files by adding a target to your project:

Install-CleanReferencesTarget

You'll also be able to specify whether to include the pdb's, exclude certain assemblies, or extracting the assemblies on the fly. As far as I know, also unmanaged assemblies are supported.

Update

Currently, some people are trying to add support for DNX.

Update 2

For the lastest Fody version, you will need to have MSBuild 16 (so Visual Studio 2019). Fody version 4.2.1 will do MSBuild 15. (reference: Fody is only supported on MSBuild 16 and above. Current version: 15)



Related Topics



Leave a reply



Submit