C#: Resolving multiple assemblies using the AssemblyResolve function - c#

I'm attempting to make a single EXE app. To that end, I have two DLLs that are tagged as embedded resouces in my app. I can successfully use the AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(myresolve) method to return an assembly when the event fires.
The difficulty that I'm running into is that the methods within my application reference multiple assemblies. When I use the above event handler to load and return my assembly, my application looks for a method in the wrong assembly file, and I get a "type load exception".
Therefore, how can I load my assemblies so that my application looks in the proper places for any methods I might use?
Kind regards.
Code Sample:
DLL Resolution:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveDll)
static Assembly ResolveDll(object sender, ResolveEventArgs args)
{
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyApplication.Resources.MyDll1.dll"))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData); //Obviously resolves the first DLL, but how can I also resolve the second?
}
}
void SomeMethod()
{
MyDll1.Class.CallAMethod()
MyDll2.Class.CallAnotherMethod() //Type Load exception - Assembly Resolution told it to look in MyDll1.
}

As per the suggestion of L.B., I tried a simple If/then block using args.name. This has done the trick.
if (args.Name == "MyDll1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
{
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyApplication.Resources.MyDLL1.dll"))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
else if (args.Name == "MyDll2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
{
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyApplication.Resources.MyDll2.dll"))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}

Related

Unity. Unload DLL at runtime

So, currently I have a dll that is loaded when the application starts. I have both used
using (FileStream fileStream = File.Open(DLLData.LocalDLLFolder() + "DLLSupport.dll", FileMode.Open))
{
using (MemoryStream memoryStream = new MemoryStream())
{
byte[] buffer = new byte[1024];
int read = 0;
while ((read = fileStream.Read(buffer, 0, 1024)) > 0)
{
memoryStream.Write(buffer, 0, read);
}
Assembly assembly = Assembly.Load(memoryStream.ToArray());
Type _main = assembly.GetType("DLLSupport.DebugStart");
dllSupport = new GameObject();
dllSupport.AddComponent(_main);
memoryStream.Close();
fileStream.Close();
}
}
and Assembly.LoadFrom
The thing is that when is running I will download this same DLL again if its updated.
If I try to download it with Assembly.LoadFrom It wont allow me since the file is being used by Unity.
With the other method I can download or delete it when I want, but the thing is that even if I call that method again it will work like the previous one until I restart the application. (Maybe there is any way to unload that that I didnt found?)
I tried it too with AppDomain but I will get FileNotFoundException: Could not load file or assembly or one of its dependencies
Any idea?
You cannot unload an assembly from the curren AppDomain, but you can create an AppDomain, and load assembly into it, execute some codes on that AppDomain, and then Unload the AppDomain when necessary, this will also Unload the assembly on that domain.
Reference : https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/ms173101(v=vs.90)?redirectedfrom=MSDN

C# Include DLL in a Library DLL

I have the following problem:
I'm making a library with few non-windows dependecies.
I would like to make the dll in order to have all the dependecies included without need to have also the other dll together with it.
The idea is to have only the new dll with the project that it'll use it instead of having also all the dependecies that this new dll is using.
I've read about inlcluding them as resource but it appears to not work as expected, using this code (I'm calling it in a static constructor of the class that need that dependecies, all the dependecies are in a subfolder lib of the library project):
internal static void Include()
{
if (!included)
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
String resourceName = "namespaceOfTheLibrary" + ".lib." + 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);
}
};
included = true;
}
This would be very helpfull in order to keep the code updated in many projects without forget any dependecies.
Hope something it's possible.
Thanks

How to embed System.Data.SQLite.dll to my Class Library?

i'm building a class library for SQLite , when i build the project the System.Data.SQLite.dll comes as a standalone dll file and i need it to be merged with my class library in a single dll file
i added System.Data.SQLite.dll as a ressource then , add / existing item , and then adding it as an embedded resource
then i drop this static constractor in the start of my namespace
static SimpleClass()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
if (new AssemblyName(args.Name).Name.ToLowerInvariant() == "System.Data.SQLite")
{
String resourceName = "SQLlite." + 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);
}
}
else
{
return null;
}
};
}
This is giving me errors like error CS1518: Expected class, delegate, enum, interface, or struct
appreciate any help from you guys
If you want to make one assembly from two, you can try ILMerge.
This will ultimately fail. System.Data.SQLite is a PInvoke wrapper for SQLite.Interop.dll, which is an unmanaged dll, which is not resolved in .net.

Including a DLL as an Embedded Resource in a WPF project

I'm following http://blogs.msdn.com/b/microsoft_press/archive/2010/02/03/jeffrey-richter-excerpt-2-from-clr-via-c-third-edition.aspx
I've added WPFToolkit.Extended.dll to my solution, and set its Build Action to Embedded Resource.
In App.OnStartup(StartupEventArgs e) I have the following code:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
String resourceName = "AssemblyLoadingAndReflection." + new AssemblyName(args.Name).Name + ".dll";
String assemblyName = Assembly.GetExecutingAssembly().FullName;
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
using (stream)
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
The debugger hits this block of code twice.
First time:
resourceName is "AssemblyLoadingAndReflection.StatusUtil.resources.dll"
assemblyName is "StatusUtil, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
stream is null
Second time:
resourceName is "AssemblyLoadingAndReflection.WPFToolkit.Extended.resources.dll"
assemblyName is "StatusUtil, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
stream is null
The code throws an exception when it hits stream.Length, since it's null.
I can't use ILMerge because it's a WPF project.
You have to change the string "AssemblyLoadingAndReflection" to the name of your application assembly.
One thing you can do to make this code more generic by using some more reflection:
Assembly.GetExecutingAssembly().FullName.Split(',').First()
Don't forget to append a dot. This will of course not work if the dll is not in the resources of the application assembly.
The answer by H.B. is actually not quite right. The prefix we need is not the name of the assembly, but the default namespace of the project. This is probably impossible to get entirely reliably, but the following code would be much more reliable than assuming it's the same as the assembly name.
Assembly thisAssembly = Assembly.GetEntryAssembly();
String resourceName = string.Format("{0}.{1}.dll",
thisAssembly.EntryPoint.DeclaringType.Namespace,
new AssemblyName(args.Name).Name);
Nathan Philip's Answer worked like a charm for me..
This is the entire method that worked for me
(and it works for multiple dlls also)
public MainWindow()
{
InitializeComponent();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
Assembly thisAssembly = Assembly.GetEntryAssembly();
String resourceName = string.Format("{0}.{1}.dll",
thisAssembly.EntryPoint.DeclaringType.Namespace,
new AssemblyName(args.Name).Name);
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
}
I tried all of the above answers and they did not work for me. I found this post and it worked like a charm.
Post: http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application
I found that the .csproj file also needs to be edited. The post explains how to do everything.
I have a full explanation of how to dynamically load embedded assemblies in StackOverflow question VB.NET embedded DLL in another DLL as embedded resource?

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

I've got a situation where I have a DLL I'm creating that uses another third party DLL, but I would prefer to be able to build the third party DLL into my DLL instead of having to keep them both together if possible.
This with is C# and .NET 3.5.
The way I would like to do this is by storing the third party DLL as an embedded resource which I then place in the appropriate place during execution of the first DLL.
The way I originally planned to do this is by writing code to put the third party DLL in the location specified by System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()
minus the last /nameOfMyAssembly.dll. I can successfully save the third party .DLL in this location (which ends up being
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 can't find it.
Does anybody have any idea as to what I need to be doing differently?
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.
In the end I did it almost exactly the way raboof suggested (and similar to what dgvid suggested), except with some minor changes and some omissions fixed. I chose this method because it was closest to what I was looking for in the first place and didn't require using any third party executables and such. It works great!
This is what my code ended up looking like:
EDIT: I decided to move this function to another assembly so I could reuse it in multiple files (I just pass in Assembly.GetExecutingAssembly()).
This is the updated version which allows you to pass in the assembly with the embedded dlls.
embeddedResourcePrefix is the string path to the embedded resource, it will usually be the name of the assembly followed by any folder structure containing the resource (e.g. "MyComapny.MyProduct.MyAssembly.Resources" if the dll is in a folder called Resources in the project). It also assumes that the dll has a .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();
}
}
There's a tool called IlMerge that can accomplish this: http://research.microsoft.com/~mbarnett/ILMerge.aspx
Then you can just make a build event similar to 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
I've had success doing what you are describing, but because the third-party DLL is also a .NET assembly, I never write it out to disk, I just load it from memory.
I get the embedded resource assembly as a byte array like so:
Assembly resAssembly = Assembly.LoadFile(assemblyPathName);
byte[] assemblyData;
using (Stream stream = resAssembly.GetManifestResourceStream(resourceName))
{
assemblyData = ReadBytesFromStream(stream);
stream.Close();
}
Then I load the data with Assembly.Load().
Finally, I add a handler to AppDomain.CurrentDomain.AssemblyResolve to return my loaded assembly when the type loader looks it.
See the .NET Fusion Workshop for additional details.
You can achieve this remarkably easily using Netz, a .net NET Executables Compressor & Packer.
Instead of writing the assembly to disk you can try to do Assembly.Load(byte[] rawAssembly) where you create rawAssembly from the embedded resource.

Categories