Exception on receiving Assembly reference from another AppDomain - c#

I am loading all DLLs from a specific "extensions" directory in a new AppDomain to get some Reflection related information out of those.
This is what I'm trying:
I created a new library AssemblyProxy in my solution which just has this class:
public class AssemblyProxy : MarshalByRefObject
{
public Assembly LoadFile( string assemblyPath )
{
try
{
return Assembly.LoadFile( assemblyPath );
}
catch
{
return null;
}
}
}
I make sure this DLL is present inside my "extensions" directory. Then I use the following code to load all assemblies from "extensions" directory into the new AppDomain.
foreach( string extensionFile in Directory.GetFiles( ExtensionsDirectory, "*.dll" ) )
{
Type type = typeof( AssemblyProxy.AssemblyProxy );
var value = (AssemblyProxy.AssemblyProxy) Domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName );
var extensionAssembly = value.LoadFile( extensionFile );
types.AddRange( extensionAssembly.GetTypes() );
}
Some DLLs do get loaded successfully but on some DLLs an exception is thrown like this:
Could not load file or assembly 'Drivers, Version=2.3.0.77, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
EDIT: The exception is not thrown in the new AppDomain. The DLL gets successfully loaded in the new AppDomain. The exception gets thrown as soon as the assembly reference is returned to the main/calling AppDomain. Does the main/calling AppDomain try to load the assembly on its own on just receiving the reference?
Thanks.

You should not return an Assembly object from the new AppDomain because this will only work if you main AppDomain has access to those assemblies which it does not since the assemblies are located in a directory that:
is not the AppDomain's base directory (AppDomain.BaseDirectory),
is not in the directories specified in the AppDomain's relative search path (AppDomain.PrivateBinPath)
One way to avoid this to follow the leppie's comment and:
Create a serialized type that includes the minimum information you need from the Assembly object.
Add this type to new assembly that both AppDomain's have access to. The simplest way to do this by adding it to the GAC.
Another approach would be to use Mono.Cecil instead of System.Reflection. Mono.Cecil will allow you to inspect assemblies without loading them. For a very simple example look at the second half from this answer.

Related

Is there an way to add reference(dll) in parent path in C#?

== compile command ==
csc -r:"../Newtonsoft.Json.dll" test.cs
== exec command ==
mono test.exe
== exec result : dependency error ==
System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=11.0.0.0, Culture=neutral,
PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies.
"Newtonsoft.Json.dll" this file is located in parent path. so I added a reference about dll and compile succeeded, but when I executed the exe file, it failed to get dll reference I added.
And when I put cs file and dll file together in the same directory, it worked very well, but that's not what I wanted.
Is there a solution to add a reference from dll file which is located in parent path using command line interface?
I used csc for compiler and mono for execution.
Thanks.
References are pathless. What that means is that wherever the assembly resides, all your program knows is that it has a reference to Newtonsoft.Json, Version=x.x.x.x, Culture=... and so on. You can do some things with the application configuration (application.config or myprogram.exe.config) to organize things into subfolders (using the probing setting) or specify a URL location for the file (using the codebase setting). You can set up the environment to change the search path and so on.
Or you can add some runtime code that allows you to override the default behavior and the call Assembly.LoadFrom to provide a full path to the file. You can either do it as part of your initialization or in a handler for the AppDomain.AssemblyResolve event - which is generally better method since it will only be called when the assembly is actually needed.
For example:
using System.IO;
using System.Reflection;
static class ParentPathResolver
{
public static void Init()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolve);
}
private static Assembly? Resolve(object? sender, ResolveEventArgs args)
{
var filename = new AssemblyName(args.Name).Name + ".dll";
var fullname = Path.GetFullPath(Path.Combine("..", filename));
if (File.Exists(fullname))
return Assembly.LoadFrom(fullname);
return null;
}
}
Of course you can add your own code to the Resolve method to search pretty much anywhere, just as long as you don't get caught in a resolution loop. I've used the AssemblyResolve event to do fun things like loading assemblies from compressed resources.

Load assembly to temporary Appdomain from another directory

As a part of BaaS project, I need to dynamically execute a code from "guest" assembly. I've tried all the examples and answers in other similar questions including AppDomainToolKit approach, but got no luck.
Similar with the plugin approach, I got positive result by copying related assemblies into host application's bin path. However, this is not possible for the current scenario:
permissions and restrictions needs to be applied per request
each request should be evaluated in a temporary appdomain
load the required assembles and referenced types from a path to temporary domain
So far, my latest piece of code is below
// ClassLibrary1.dll and ClassLibrary2.dll are in the same directory, both are marked as Serializable
var binPath = #"C:\AssemblyDemo\ClassLibrary1\bin\Debug\";
var lib1Path = binPath + "ClassLibrary1.dll";
var lib2Path = binPath + "ClassLibrary2.dll";
var setup = new AppDomainSetup();
setup.ApplicationBase = binPath;
AppDomain domain = AppDomain.CreateDomain("domainname", null, setup);
ObjectHandle handle = domain.CreateInstanceFrom(lib1Path, "ClassLibrary1.Class1");
var unwrap = handle.Unwrap();
var m1 = unwrap.GetType().GetMethod("Method1");
var result = m1.Invoke(unwrap, null);
handle.Unwrap() throws exception Type is not resolved for member 'ClassLibrary1.Class1,ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
If you want to load the assembly only in the temporary AppDomain you cannot call Unwrap in the main domain. Unwrap loads the type there as well. You must move all access to objects from the temporary assembly to the temporary AppDomain. You can do this using AppDomain.DoCallBack or a custom MarshalByRef class that you instantiate in the child domain and call from the parent domain.

Getting a list of types in an assembly that inherit from a specific interface

Please read before marking as duplicate, already answered, on hold, or off topic.
First of all, I know there are SIMILAR question here, with very good answers. Unfortunately they do not answer my question.
My goal: to create a list of all of the types within an assembly that inherit from a specific interface. This assembly may be located at a local path or a UNC path.
My problem: First, I know how to load an assembly, both from a UNC and a local path, and I know how to use GetTypes(). Many of you may know that GetTypes() has it's own issue. Specifically, it will raise an exception if any of the Types inherit from another type that cannot be loaded. How to prevent ReflectionTypeLoadException when calling Assembly.GetTypes() for instance. The problem is, I don't need to load any of the dependencies. I only need to see if a type declared in the assembly inherits from an other specific type.
some code as an example. This is just the first step, to retrieve the types in the assembly:
Assembly asm = Assembly.Load(File.ReadAllBytes(assemblyPath));
Type[] myTypes = GetLoadableTypes(asm);
public static Type[] GetLoadableTypes(Assembly assembly)
{
// TODO: Argument validation
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}
If any of the Types inside of the assembly inherit from a type that cannot be loaded, a ReflectionTypeLoadException exception will be thrown and they will show up as null inside of e.Types, so there is still no information on them. So I guess I can't use GetTypes still.
For completeness, here is a reference to my original question.
How to get Type information when ReflectionTypeLoadException is thrown from Assembly.GetType()
OK, made some headway with this and, unless I want to write my own metadata parser, it looks like this is what I am stuck with so I thought I'd share. I'll try to give the pros and cons.
This answer all revolves around using an event handler for the
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve
event. It should be pointed out that this event is only raised when an assembly's dependencies are being resolved. I didn't understand that at first but what it simply means is that the event is not fired for the assembly you are trying to load, but any dependencies in that assembly, which makes sense. Why raise that event when you have to have a starting point in the first place. For my solution, this event will be raised when I call .GetTypes().
Before I post the code let me explain the idea and what's good and bad about it. The idea is that, if I have a dependency, the event will be raised. In the event, I will first check a dictionary, created before hand, of all of the assemblies found in the same folder (including subfolders) as the original assembly. If the args.Name is found in that dictionary, then I read in that assembly and return it from the event. If it's not in the list, I attempt to load the assembly by name only, meaning the dependency assembly must either be part of the project or installed in the local GAC.
I already hinted at some of the cons here, so let's start with those.
Since I'm not just looking at metadata to tell me what dependencies are in the main assembly (in other words those dependencies must be loaded), if a dependency's assembly cannot be found with the original assembly (in the same folder or subfolders), and the dependency is not installed in the local GAC, or referenced in the project, GetTypes() will still throw a ReflectionTypeLoadException exception. It may seem like a rare case to run in to a situation where an dependency assembly is not located with the original assembly or installed in the GAC but I would give PRISM as an example of a situation where that may happen. PRISMs assemblies do not necessarily get installed into the GAC and are not always going to be copied local.
So what's good about this method? Well mainly that it gives you at least some way to handle 99% of the situations where you don't have a dependency's assembly in hand.
One or two final thoughts (actually gotchas) before the code. I use AssemblyName to create a dictionary bases on the assembly's full name without actually loading (either straight out or by ReflectionOnlyLoad) an assembly. There are two ways you can create an instance of AssemblyName, either by the constructor where you pass in the path to the assembly, or with a static method, GetAssemblyName(path). The constructor seems to only handle local paths where GetAssemblyName() can handle a UNC path. Those are the gotchas: don't load the assembly just to get the name and make sure you don't limit yourself to local paths.
Finally, some code:
public class TestClass
{
//This dictionary will hold a list of the full path for an assembly, indexed by the assembly's full name
private Dictionary<string, string> _allAsms = new Dictionary<string, string>();
/// <summary>
/// Tries to list all of the Types inside an assembly and any interfaces those types inherit from.
/// </summary>
/// <param name="pathName">The path of the original assembly, without the assembly file</param>
/// <param name="fileName">The name of the assembly file as it is found on disk.</param>
public void Search(string pathName, string fileName)
{
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
//Getting a list of all possible assembly files that exist in the same location as the original assembly
string[] filePaths = Directory.GetFiles(pathName, "*.dll", SearchOption.AllDirectories);
Assembly asm;
AssemblyName name;
if (!pathName.EndsWith("\\"))
pathName += "\\";
foreach (string path in filePaths)
{
name = AssemblyName.GetAssemblyName(path);
if (!_allAsms.ContainsKey(name.FullName))
_allAsms.Add(name.FullName, path);
}
//This is where we are loading the originaly assembly
asm = System.Reflection.Assembly.ReflectionOnlyLoad(File.ReadAllBytes(pathName + fileName));
Console.WriteLine("Opened assembly:{0}", fileName);
//And this is where the ReflectionOnlyAssemblyResolve will start to be raised
foreach (Type t in asm.GetTypes())
{
Console.WriteLine(" " + t.FullName);
//Get the interfaces for the type;
foreach (Type dep in t.GetInterfaces())
{
Console.WriteLine(" " + dep.FullName);
}
}
}
private Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
if (_allAsms.ContainsKey(args.Name))
return Assembly.ReflectionOnlyLoad(File.ReadAllBytes(_allAsms[args.Name]));
else
return System.Reflection.Assembly.ReflectionOnlyLoad(args.Name);
}
}
To check if a type implements a specific interface you can use this:
typeof(yourInterface).IsAssignableFrom(type)
However, if the type could not be loaded there is no information available on it, and you have no chance to figure out whether it implements a specific interface or not.

How can I dynamically reference an assembly that looks for another assembly?

Apologies for the dodgy question - happy to rephrase if someone has a better suggestion.
I'm trying to create an object by dynamically invoking an assembly belonging to another application.
The following PowerShell code is working nicely for me:
[Reflection.Assembly]::LoadFrom("C:\Program Files\Vendor\Product\ProductAPI.dll")
$bobject = new-object ProductAPI.BasicObject
$bobject.AddName("Some Name")
I'm struggling to do the same thing in C#. Based on other posts on StackOverflow I currently have this:
System.Reflection.Assembly myDllAssembly =
System.Reflection.Assembly.LoadFile("C:\\Program Files\\Vendor\\Product\\ProductAPI.dll");
System.Type BasicObjectType = myDllAssembly.GetType("ProductAPI.BasicObject");
var basicObjectInstance = Activator.CreateInstance(BasicObjectType);
The final line results in a TargetInvocationException.
{"Could not load file or assembly 'AnotherObject, Version=1.2.345.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."
It appears that the BasicObject constructor is trying to invoke AnotherObject (from AnotherObject.dll in the same folder) but can't find it.
Any tips on how to get around this?
If it can't find a dependent assembly in the usual places, you'll need to manually specify how to find them.
The two easiest ways I'm aware of for doing this:
manually load the dependent assemblies in advance with
Assembly.Load.
handle the AssemblyResolve event for the domain which is loading the
assembly with additional assembly dependencies.
Both essentially require you to know the dependencies for the assembly you're trying to load in advance but I don't think that's such a big ask.
If you go with the first option, it would also be worthwhile looking into the difference between a full Load and a reflection-only Load.
If you would rather go with 2 (which I'd recommend), you can try something like this which has the added benefit of working with nested dependency chains (eg MyLib.dll references LocalStorage.dll references Raven.Client.dll references NewtonSoft.Json.dll) and will additionally give you information about what dependencies it can't find:
AppDomain.CurrentDomain.AssemblyResolve += (sender,args) => {
// Change this to wherever the additional dependencies are located
var dllPath = #"C:\Program Files\Vendor\Product\lib";
var assemblyPath = Path.Combine(dllPath,args.Name.Split(',').First() + ".dll");
if(!File.Exists(assemblyPath))
throw new ReflectionTypeLoadException(new[] {args.GetType()},
new[] {new FileNotFoundException(assemblyPath) });
return Assembly.LoadFrom(assemblyPath);
};

GetType(string) returns null after AppDomain.CurrentDomain.Load

I'am building a small plugin architecture (unfortunately MEF is not an options because it needs to run on .NET 2.0).
I want to be able to put dll's in a directory without recompiling the main project.
My main project is a winforms application which has some dialogs to pick an implementation of an interface the main program needs.
I have a method that searches a certain directory and gives a List of locations of the dll's I want to search for Types that implement the interface.
public List<Type> GetPluginTypes()
{
List<Type> types = new List<Type>();
foreach (string dll in this.Plugins)
{
Assembly assembly;
try
{
assembly = AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(dll));
}
catch
{
continue;
}
foreach (Type type in assembly.GetExportedTypes())
{
if (type.IsInterface || type.IsAbstract)
continue;
if (typeof(IMyInterface).IsAssignableFrom(type))
types.Add(type);
}
}
return types;
}
Using this method I show the user a list of implementations, one is chosen and it's AssemblyQualifiedName is saved to a settings file.
When I start the main application, I load the AQN from the settings and load all the plugins into the AppDomain by calling the above method.
string typeName = GetSetting("MyPlugin");
GetPluginTypes(); // just to load the plugins into the app domain
Type.GetType(typeName); // allways returns null.
Here is my problem: Type.GetType(typeName), always returns null.
I used Type.GetType(typeName, true), to enforce an exception, which I got:
Could not load file or assembly 'MyImpl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.":"MyImpl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"}
I am clueless. I've already loaded the assembly into the AppDomain, and still Type.GetType(string) can't find it when I specify the AQN.
This can be solved by registering the AppDomain.AssemblyResolve event for the AppDomain which will request the assembly and make the handler return the already loaded assembly.
Here is what the handler looks like in C#:
private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return
AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(Kandidaat => string.Equals(Kandidaat.GetName().FullName, args.Name));
}
a detailed explanation can be found here:
https://msdn.microsoft.com/en-us//library/ff527268(v=vs.110).aspx
appdomain assemblyresolve
Perhaps this is relevant. From the documentation for Type.GetType(String, Boolean):
If typeName includes only the name of
the Type, this method searches in the
calling object's assembly, then in the
mscorlib.dll assembly. If typeName is
fully qualified with the partial or
complete assembly name, this method
searches in the specified assembly.
You said that you try calling with type.AssemblyQualifiedName, but it failed. Did you check the qualified name to see if it was reasonable?

Categories