I'm trying to load an assembly into the appdomain.
Failed to load assembly "Test.dll". System.IO.FileNotFoundException: Could not load file or assembly 'Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
I'm using bytes to load the assembly, instead of paths.
I looked at an example which also specifies a Proxy class down below.
AppDomainSetup domaininfo = new AppDomainSetup();
domaininfo.ApplicationBase = System.Environment.CurrentDirectory;
Evidence adevidence = AppDomain.CurrentDomain.Evidence;
dmname = AppDomain.CreateDomain("test", adevidence, domaininfo);
Loading:
Assembly = Store.dmname.Load(bytes);
Using the Proxy class:
internal class Proxy : MarshalByRefObject
{
internal Assembly GetAssembly(byte[] assemblyPath)
{
try
{
return Assembly.Load(assemblyPath);
}
catch (Exception)
{
return null;
// throw new InvalidOperationException(ex);
}
}
}
Type type = typeof(Store.Proxy);
var value = (Store.Proxy)Store.dmname.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
Assembly = value.GetAssembly(bytes);
I tried both ways and I end up at the same thing. Is there a way to load up the assembly to the appdomain using byte[]? I do not want to save It.
Related
I have a class library in which I load dynamic assemblies. How can I load these assemblies using the new MetaDataLoadContext and MetaDataAssemblyResolver?
Code:
// Retrieve the domain assembly used by the compilation
Assembly baseAssembly = typeof(MyType).Assembly;
// Retrieve the location of the assembly and the referenced assemblies used by the domain
AssemblyName[] baseAssemblyReferences = baseAssembly.GetReferencedAssemblies();
var paths = new List<string>();
foreach (var item in baseAssemblyReferences)
{
paths.Add(item.Name + ".dll");
}
paths.Remove("mscorlib.dll");
paths.Remove("System.Runtime.dll");
paths.Remove("netstandard.dll");
paths.Remove("System.Core.dll");
paths.Remove("System.dll");
// Create PathAssemblyResolver that can resolve assemblies using the created list.
var resolver = new PathAssemblyResolver(paths);
var mlc = new MetadataLoadContext(resolver, baseAssmebly.GetName().Name);
I am getting an error:
Could not find assembly 'MYAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Either explicitly load this assembly using a method such as LoadFromAssemblyPath() or use a Metadata AssemblyResolver that returns a valid assembly
I need to create a new AppDomain for my solution so that I can invoke the method from it's own AppDomain. All of the dependent .dll files for my solution have been embedded into my.exe using Fody/Costura. I have created a Loader : MarshalByRefObject class to invoke my method but when I execute the loader at runtime I get an exception of:
Could not load file or assembly 'my.Assembly, Version=19.1.7242.23931, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
The code is looking for the .dll files in my AppDomain.CurrentDomain.BaseDirectory to execute my method via the loader but they are not there but are embedded in the executable. Is there a way for my new AppDomain to know about the assemblies that have been embedded into my executable without having to load them from the BaseDirectory?
I have looked for ways to load the other assemblies into my new AppDomain but haven't found a way to do it. I also suspect there might be Fody/Costura API to do it but haven't found anything that worked for this scenario.
internal class Loader : MarshalByRefObject
{
object CallInternal(string dll, string typename, string method, object[] parameters)
{
Assembly a = Assembly.LoadFile(dll);
Type t = a.GetType(typename);
MethodInfo m = t.GetMethod(method);
Console.WriteLine("Invoking " + m.Name);
return m.Invoke(null, parameters);
}
public static object Call(string dll, string typename, string method, params object[] parameters)
{
object result = null;
try
{
AppDomain newAppDomain = AppDomain.CreateDomain("myNewDomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
Loader ld = (Loader)newAppDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName); //DLL file not found exception thrown from here
result = (Loader)ld.CallInternal(dll, typename, method, parameters);
AppDomain.Unload(newAppDomain);
}
catch (Exception ee)
{
Console.WriteLine("Exception Message [" + ee.Message + "]");
PDFUtil.Report.ErrSilent(ee);
}
return result;
}
}
I am looking to have my Loader class be instantiated without a need to load the .dlls from disk during runtime. Is this possible with Fody/Costura embedded .dlls?
I added my .dlls to my Visual Studio Project and set Build-Action to be Embedded Resource
I am running this bit of code to get the assembly into my new AppDomain:
string resName = null;
foreach (string resourceName in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
Console.WriteLine("Assembly.GetExecutingAssembly().GetManifestResourceNames() [" + resourceName + "] ");
if (resourceName.Contains("my.Assembly"))
{
resName = resourceName;
Console.WriteLine("CurrentDomain Assembly my.Assembly [" + "] Resource name [" + resName + "]");
}
}
Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream(resName);
if (s != null)
{
byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
Assembly a = Assembly.Load(data);
newAppDomain.Load(data);
foreach (Assembly cdAssem in newAppDomain.GetAssemblies())
{
Console.WriteLine("newAppDomain Assembly 2 [" + cdAssem.GetName().FullName + "]");
}
}
I can see that the assembly is part of my new AppDomain:
newAppDomain Assembly 2 [mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]
newAppDomain Assembly 2 my.Assembly Version=19.1.7243.19014, Culture=neutral, PublicKeyToken=null]
But using CreateInstanceAndUnwrap() still fails saying it can't find my.Assembly.dll. I am wondering if the only way one can load an assembly into the AppDomain is by loading it from the Base Directory?
I use Assembly.Load(byte[]) to load an assembly. This loaded assembly internally uses codeDomProvider.CompileAssemblyFromSource() to compile code which is failing with error 'The type or namespace name '<ClassName>' could not be found (are you missing a using directive or an assembly reference?)'.
AppDomain.AssemblyResolve event is not fired for the references present in the code to compile.
FYI: I am trying to load assembly which uses FileHelpers.dll and it calls on ClassBuilder.ClassFromString function. Assembly load fails with RunTimeCompilationException.
How should I resolve such dependencies?
UPDATE:
Here is code I am using
ctor()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
}
private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{
AssemblyName assemblyName = new AssemblyName(args.Name);
return LoadAssembly(assemblyName);
}
public Assembly GetAssembly(string assemblyFullName)
{
AssemblyName assemblyName = new AssemblyName(assemblyFullName);
Assembly assembly = LoadAssembly(assemblyName) ?? Assembly.Load(assemblyName);
}
private Assembly LoadAssembly(AssemblyName assemblyName)
{
byte[] assemblyBytes = GetAssemblyBytes(); // Get assembly bytes from external source
return Assembly.Load(assemblyBytes);
}
Thanks
Yash
You need to handle the CurrentDomain.ResolveEventHandler event as below, this event will be triggered whenever a reference is required:
code to load assemblies:
private void LoadAssem(){
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
//use File.ReadAllBytes to avoid assembly locking
Assembly asm2 = Assembly.Load(File.ReadAllBytes("AssemblyPath"));
}
Once a reference is needed it will call the below event:
private static string asmBase;
//asmBase contains the folder where all your assemblies found.
public static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//This handler is called only when the common language runtime tries to bind to the assembly and fails.
//Retrieve the list of referenced assemblies in an array of AssemblyName.
Assembly MyAssembly, objExecutingAssemblies;
string strTempAssmbPath = "";
objExecutingAssemblies = args.RequestingAssembly;
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
//Loop through the array of referenced assembly names.
foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
{
//Check for the assembly names that have raised the "AssemblyResolve" event.
if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
{
//Build the path of the assembly from where it has to be loaded.
strTempAssmbPath = asmBase + "\\" + args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
break;
}
}
//Load the assembly from the specified path.
MyAssembly = Assembly.Load(File.ReadAllBytes(strTempAssmbPath));
//Return the loaded assembly.
return MyAssembly;
}
How can I create an appdomain, add assemblies to it, then destroy that app domain? This is what I have tried:
static void Main(string[] args)
{
string pathToExe = #"A:\Users\Tono\Desktop\ConsoleApplication1.exe";
AppDomain myDomain = AppDomain.CreateDomain("MyDomain");
Assembly a = Assembly.Load(System.IO.File.ReadAllBytes(pathToExe));
myDomain.Load(a.FullName); // Crashes here!
}
I have also tried:
myDomain.Load(File.ReadAllBytes(pathToExe));
how can I add an assembly to the appdomain. Once I do that I can find the method via reflection execute it and then destroy the appdomain
The exception that I get is:
Could not load file or assembly 'ConsoleApplication1, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null' or one of its dependencies. The
system cannot find the file specified.
Two quick points:
AppDomain.Load loads an assembly in the current AppDomain and not on myDomain (weird, I know).
AppDomain.Load loads an assembly in the Load context, which resolves assemblies from the apps base-dir, the private-bin-paths and the GAC. Most probably, the assembly you try to load is not located in any of these locations which explains the exception message.
For more info have a look at this answer.
One way to load assemblies to an AppDomain is by creating a MarshalByRef-derived loader. You need something like this to avoid leaking types (and assemblies) to your main AppDomain. Here's a simple one:
public class SimpleAssemblyLoader : MarshalByRefObject
{
public void Load(string path)
{
ValidatePath(path);
Assembly.Load(path);
}
public void LoadFrom(string path)
{
ValidatePath(path);
Assembly.LoadFrom(path);
}
private void ValidatePath(string path)
{
if (path == null) throw new ArgumentNullException("path");
if (!System.IO.File.Exists(path))
throw new ArgumentException(String.Format("path \"{0}\" does not exist", path));
}
}
And use it like that:
//Create the loader (a proxy).
var assemblyLoader = (SimpleAssemblyLoader)myDomain.CreateInstanceAndUnwrap(typeof(SimpleAssemblyLoader).Assembly.FullName, typeof(SimpleAssemblyLoader).FullName);
//Load an assembly in the LoadFrom context. Note that the Load context will
//not work unless you correctly set the AppDomain base-dir and private-bin-paths.
assemblyLoader.LoadFrom(pathToExe);
//Do whatever you want to do.
//Finally unload the AppDomain.
AppDomain.Unload(myDomain);
i really wonder why assemblyResolver not working? Also i can not use
foreach (byte[] binary in deCompressBinaries)
ApplicationHost.Load(binary);
how to fire AssemblyResolve? please look my reference question: http://stackoverflow.com/questions/9721686/how-to-use-appdomain-createdomain-with-assemblyresolve
protected void LoadApplication()
{
AppDomainSetup domainSetup = new AppDomainSetup();
domainSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
domainSetup.DisallowBindingRedirects = false;
domainSetup.DisallowCodeDownload = true;
domainSetup.LoaderOptimization = LoaderOptimization.SingleDomain;
//domainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
ApplicationHost = AppDomain.CreateDomain("Test.Service", null, domainSetup);
object obj = ApplicationHost.CreateInstanceAndUnwrap("Test.Process", "Test.ApplicationLoader");
Assembly objExecutingAssemblies = Assembly.GetExecutingAssembly();
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
foreach (AssemblyName assName in arrReferencedAssmbNames)
{
ApplicationHost.Load(assName);
}
ApplicationHost.AssemblyResolve += new ResolveEventHandler(OnAssemblyResolve);
List<byte[]> deCompressBinaries = new List<byte[]>();
foreach (var item in AppPackage.Item.AssemblyPackage)
deCompressBinaries.Add(item.Buffer);
var decompressvalues = DeCompress(deCompressBinaries);
deCompressBinaries.Clear();
deCompressBinaries = decompressvalues.ToList();
foreach (byte[] binary in deCompressBinaries)
ApplicationHost.Load(binary);
Assembly[] assAfter = AppDomain.CurrentDomain.GetAssemblies();
}
Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
{
return Assembly.Load(args.Name);
}
The AssemblyResolve will never fire, because you're loading up all the assemblies in the LoadApplication method - AssemblyResolve will only be called if the required assembly reference fails to be resolved.
I would suggest running fuslogvw.exe so that you can visualize what is going on.
If you want to load the assemblies on an as-needed basis, the block of code:
List<byte[]> deCompressBinaries = new List<byte[]>();
foreach (var item in AppPackage.Item.AssemblyPackage)
deCompressBinaries.Add(item.Buffer);
var decompressvalues = DeCompress(deCompressBinaries);
deCompressBinaries.Clear();
deCompressBinaries = decompressvalues.ToList();
foreach (byte[] binary in deCompressBinaries)
ApplicationHost.Load(binary);
Assembly[] assAfter = AppDomain.CurrentDomain.GetAssemblies();
will need to be worked into the AssemblyResolve. The block currently loads EVERYTHING into the AppDomain, so you'd have to rework that piece of logic.
Of course, the other thing you might want do is not to re-invent the wheel, and use ILMerge.
AssemblyResolve fill fire only of CLR is failed to load an assembly by one of the reasons. This event will not fire just because you loading your assemblies from byte array.
Here are some examples.
This code will fire AssemblyResolve event, because "System.Drawing" as an assembly name is not sufficient.
object obj2 = ApplicationHost.CreateInstanceAndUnwrap("System.Drawing", "System.Drawing.Rectangle");
This code will not fire AssemblyResolve event, because "System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" is a fully qualified assembly name.
object obj2 = ApplicationHost.CreateInstanceAndUnwrap("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Rectangle");