Mixed assembly won't load from ILMerge-ed result - c#

I have a simple program that references The open-source AlphaVSS library. It contains 3 dlls:
AlphaFS.dll (IL)
AlphaVSS.Common.dll (IL)
AlphaVSS.x64.dll (Mixed)
Now I want to merge my main.exe with AlphaFS.dll and AlphaVSS.Common.dll, leave only AlphaVSS.x64.dll on the disk.
So I added an AssemblyResolve event to load AlphaVSS.x64.dll from system32:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args2) =>
{
Console.WriteLine("Loading " + args2.Name);
string name = args2.Name.Split(',')[0];
string path = "c:\\windows\\system32\\" + name + ".dll";
if (args2.Name.StartsWith("AlphaVSS"))
{
if (File.Exists(path))
{
return Assembly.LoadFrom(path);
}
}
return null;
};
And I merged the rest successfully:
%ILMerge% /v4 /ndebug /out:"$(TargetDir)all-in-one.exe" "$(TargetDir)$(TargetFileName)" "$(TargetDir)AlphaVSS.Common.dll" "$(TargetDir)AlphaFS.dll"
Then I copied only all-in-one.exe and AlphaVSS.x64.dll to another computer, and launched all-in-one.exe. The first DLL to load is AlphaVSS.x64.dll and it's loaded. Then it tried to load AlphaVSS.Common.dll and it's not found.
From ILSpy, The AlphaVSS.Common.dll is already merged into the all-in-one.exe and loaded. But the mixed assembly AlphaVSS.x64.dll is still referencing AlphaVSS.Common.dll, so it tried to load it again.
Does anyone know how to resolve this?

Based on your answers in comments, when you load AlphaVSS.x64.dll from the drive, it tries to load AlphaVSS.Common.dll (since it references it).
But you already catching it with your custom resolver. And since AlphaVSS.Common.dll contains AlphaVSS this condition will be true if (args2.Name.StartsWith("AlphaVSS")) and your resolver'll try to load AlphaVSS.Common.dll from the drive.
What you need to do is modify your resolver to load only AlphaVSS.x64.dll from the drive and load AlphaVSS.Common.dll from contents of current assembly.
In case of AlphaVSS.Common.dll try doing something like this:
Assembly assembly = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetName().Name == "AlphaVSS.Common")
.Single();

Related

Loading an assembly from file using AppDomain.Load() instead of Assembly.LoadFile() returns file not found

This works fine:
Assembly MyAssembly = Assembly.LoadFile(Directory.GetParent(Application.LocalUserAppDataPath) + "\\MyLib.dll");
Type MyAssemblyType = MyAssembly .GetType("MyLib.someType");
object MyAssemblyObject = Activator.CreateInstance(deviceControlType);
This returns file not found exception:
AppDomain MyDomain = AppDomain.CreateDomain("MyDomain");
Assembly MyAssembly = MyDomain.Load(File.ReadAllBytes(Directory.GetParent(Application.LocalUserAppDataPath) + "\\MyLib.dll");
I've implemented an AssemblyResolve event with no luck, I've tried various methods to solve this but it seems loading from file isn't at all the same when using AppDomain. My initial issue is I need to use an appdomain so that I can unload the .dll to change the file, then reload it when the changes ahve been made.

c# using Assembly.Load to load multiply assembly from memory

I have 2 assembly should be loaded from memory, I use the following code to implement it but it can't work. Help me, Thank you!
Assembly.Load(File.ReadAllBytes("b.dll"));
var assembly = Assembly.Load(File.ReadAllBytes("a.dll"));//a.dll referenced b.dll
var type = assembly.GetTypes().First(p => p.FullName == "Namespace1.Type1");
type.GetMethod("StaticMethod1", BindingFlags.Static | BindingFlags.Public).Invoke(null, new object[] { });//it throw an exception, can't load file or assembly b.dll
Using Assembly.Load to load an assembly from a byte array, loads the assembly but does not register it under its name in the Application Domain. In order to provide your own assembly from a byte array when the runtime is looking to load a specific assembly with a specific name, you implement the AppDomain.AssemblyResolve event.
See this question and the answer for example code.

`Assembly.Load` in a separate folder

As a continuation of my previous question.
I load DLL through this code.
Example 1:
var assembly = Assembly.LoadFile("C:\\Temp\\PROCESSOR\\SKM.dll");
And that's work fine.
But I use serialization that internally use this code, example 2:
var ass1 = Assembly.Load("SKM, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
And this code throws an exception: System.Runtime.Serialization.SerializationException: Unable to find assembly "SKM, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null". - that's because the DLL in a separate folder.
How to force CLR to see DLL in separate directory (not in subfolders of main application)?
I tried this:
<codeBase version="1.0.0.0" href="C:\\Temp\\PROCESSOR\\SKM.dll"/> - do not work because it works only for subfolders.
<probing privatePath="paths"/> - do not work because it works only for subfolders.
First run first example, and then run second example. But even if the SKM.dll already loaded, CLR does not see my assembly.
I found resolution here.
Just adding an event to AssemblyResolve:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string fileName = new AssemblyName(args.Name).Name + ".dll";
string assemblyPath = Path.Combine("C:\\Temp\\PROCESSOR", fileName);
var assembly = Assembly.LoadFile(assemblyPath);
return assembly;
};
And if DLL can not be found standard way, the event fired and load DLL from my folder.
Is there any particular reason you don't want to go with example 1 if you know where the DLL is?
If you really don't then one option would be to register the DLL in the GAC.
https://msdn.microsoft.com/en-us/library/dkkx7f79%28v=vs.110%29.aspx

How to unloaded a loaded Assembly

I'm trying to load an assembly, use Reflection to get all the class' inside that .dll, and then delete the .dll. However I am getting access denied exception when trying to delete the .dll. This is not due to access rights as I can delete the .dll if I do not load it first.
I've looked on MSDN, and apparently there is no way to "unload", but I'm hoping that there might be another way.
Assembly assembly;
assembly = Assembly.LoadFrom(filepath);
Type[] listOfAllClassInDll = assembly.GetTypes();
List<string> listOfAllClassNamesInDll = new List<string>();
foreach (Type classInDll in listOfAllClassInDll)
{
listOfAllClassNamesInDll.Add(classInDll.Name);
}
File.Delete(filepath);
Actually you can't do it straightforward..
There is why: http://blogs.msdn.com/b/jasonz/archive/2004/05/31/145105.aspx
But you can dynamically load your assembly in another AppDomain. Then when you don't need your assembly - you have to unload you AppDomain with loaded assembly.
Example there: https://bookie.io/bmark/readable/9503538d6bab80
Instead of using LoadFrom/LoadFile you can use Load with File.ReadAllBytes. Here you do not use assembly file directly but read it's content and use the read data. So, you are free to rename/delete the file.
Your code will then look like:
Assembly assembly;
assembly = Assembly.Load(File.ReadAllBytes(filepath));
Type[] listOfAllClassInDll = assembly.GetTypes();
List<string> listOfAllClassNamesInDll = new List<string>();
foreach (Type classInDll in listOfAllClassInDll)
{
listOfAllClassNamesInDll.Add(classInDll.Name);
}
File.Delete(filepath);
Hope this helps :)

C# Reflection Utility

I have written the following Utility class to get an instance of any class of name "className".
public class AssemblyUtils
{
private AssemblyUtils()
{
}
public static T GetInstance<T>(string assemblyName, string className)
{
T classInstance = default(T);
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assemblyName);
object o = assembly.CreateInstance(className);
if (o is T)
{
classInstance = (T)o;
}
else
{
o = null;
}
return classInstance;
}
I have called this like:
IMyInterface ins = AssemblyUtils.GetInstance<IMyInterface>(#"MyNamespace.DA.dll", "MyClassDA");
But I am getting the following error message:
Could not load file or assembly 'MyNamespace.DA.dll' or one of its dependencies. The system cannot find the file specified.
Note that, I called the AssemblyUtils.GetInstance() from separate assemblies which are in the same sln.
How can I resolve the assembly path???
My guess is that it cannot find assembly because it's not in the same folder and not in the GAC, or other directories where the system is looking for.
You need either move them in the same folder where an executing assembly. You can change the folder where assembly is loaded from by using AppDomainSetup.PrivateBinPath Property.
I think, the assembly you want to load (MyNamespace.DA.dll) is dependent to another assembly which is not located in you folder you're looking for. Copy the dependent assemblies into the folder where you located MyNamespace.DA.dll assembly.
Check your bin\Debug folder, is the MyNamespace.DA.dll in that folder? If not you're going to have to move it in there manually. Maybe add a postcondition so that its copied in automatically. Also try using the full assembly strong name.
Also JMSA, how about some upvotes and accepting answers on your other thread?
As Vadim mentioned Assembly.Load will only look in a limited set of places. Assembly.LoadFrom might be a better bet for you. It takes a path (with filename) to the assembly.
Assembly.Load works off the assembly name, not the path.

Categories

Resources