C#: Custom Assembly Directory

C#: Custom assembly directory

You can add additional search paths to your app.config that it looks in to load assemblies. For example

<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="lib;thirdParty" />
</assemblyBinding>
</runtime>

You can see more details here.

C# Custom Assembly Directory

I have found out how, and what the AssemblyResolve event does. I am now posting this for those of you with the same problem I was having. So what this event does is that if it cannot find the assemblies, this event will tell it to look else where to find them. Here's how to set it up.

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs e)
{
Assembly ass = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "\\Libraries\\" + e.Name.Split(new char[] { ',', ' ' })[0] + ".dll");
return ass;
}

Make sure to add the event out of a static method, or it will not compile. Libraries is the sub-directory in which my executable is located at. You can change that to any name, or even add another sub-directory if you please.

Looking for .NET Assembly in a different place

you can use the AppDomain.AssemblyResolve event to add custom Assembly Resolvers. This allows you to point at other directories or even databases to get assemblies as needed.

I have even used similar code to download assemblies from a database and store in IsolatedStorage. The file name as a hash of the full Assembly name. Then the database would only need to download on the first time you resolve and all future resolutions will be served by the file system. The best about about the AssemblyResolve event is you can use it Type.GetType() and the built in Serializers.

static string lookupPath = @"c:\otherbin";

static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve +=
new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

static Assembly CurrentDomain_AssemblyResolve(object sender,
ResolveEventArgs args)
{
var assemblyname = new AssemblyName(args.Name).Name;
var assemblyFileName = Path.Combine(lookupPath, assemblyname + ".dll");
var assembly = Assembly.LoadFrom(assemblyFileName);
return assembly;
}

Getting custom assembly attributes without loading into current AppDomain

The short answer is, no, there's no way to do what you're asking.

The longer answer is this: There is a special assembly loading method, Assembly.ReflectionOnlyLoad(), which uses a "reflection-only" load context. This lets you load assemblies that cannot be executed, but can have their metadata read.

In your case (and, apparently, in every use case I could come up with myself) it's not really that helpful. You cannot get typed attributes from this kind of assembly, only CustomAttributeData. That class doesn't provide any good way to filter for a specific attribute (the best I could come up with was to cast it to a string and use StartsWith("[System.Diagnostics.Debuggable");

Even worse, a reflection-only load does not load any dependency assemblies, but it forces you to do it manually. That makes it objectively worse than what you're doing now; at least now you get the dependency loading automatically.

(Also, my previous answer made reference to MEF; I was wrong, it appears that MEF includes a whole ton of custom reflection code to make this work.)

Ultimately, you cannot unload an assembly once it has been loaded. You need to unload the entire app domain, as described in this MSDN article.

UPDATE:

As noted in the comments, I was able to get the attribute information you needed via the reflection-only load (and a normal load) but the lack of typed attribute metadata makes it a serious pain.

If loaded into a normal assembly context, you can get the information you need easily enough:

var d = a.GetCustomAttributes(typeof(DebuggableAttribute), false) as DebuggableAttribute;
var tracking = d.IsJITTrackingEnabled;
var optimized = !d.IsJITOptimizerDisabled;

If loaded into a reflection-only context, you get to do some work; you have to figure out the form that the attribute constructor took, know what the default values are, and combine that information to come up with the final values of each property. You get the information you need like this:

var d2 = a.GetCustomAttributesData()
.SingleOrDefault(x => x.ToString()
.StartsWith("[System.Diagnostics.DebuggableAttribute"));

From there, you need to check the ConstructorArguments to see which constructor was called: this one with one argument or this one with two arguments. You can then use the values of the appropriate parameters to figure out what values the two properties you are interested in would have taken:

if (d2.ConstructorArguments.Count == 1)
{
var mode = d2.ConstructorArguments[0].Value as DebuggableAttribute.DebuggingModes;
// Parse the modes enumeration and figure out the values.
}
else
{
var tracking = (bool)d2.ConstructorArguments[0].Value;
var optimized = !((bool)d2.ConstructorArguments[1].Value);
}

Finally, you need to check for NamedArguments that might override those set on the constructor, using for example:

var arg = NamedArguments.SingleOrDefault(x => x.MemberInfo.Name.Equals("IsJITOptimizerDisabled"));
var optimized = (arg == null || !((bool)arg.TypedValue.Value));

On one final note, if you are running this under .NET 2.0 or higher, and haven't already seen in, MSDN points this out in the DebuggingModes documentation:

In the .NET Framework version 2.0, JIT tracking information is always generated, and this flag has the same effect as Default with the exception of the IsJITTrackingEnabled property being false, which has no meaning in version 2.0.

Resolve assembly references from another folder

You should first find the folder where theses dlls are installed then use AppDomain.AssemblyResolve to hook assembly resolution and try to load the requested assemblies from this folder.

It will look something like this (not tested, and you need to check what args.Name contain exactly, could contain the version and the strong name along with type name) :

var otherCompanyDlls = new DirectoryInfo(companyFolder).GetFiles("*.dll");

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var dll = otherCompanyDlls.FirstOrDefault(fi => fi.Name == args.Name);
if (dll == null)
{
return null;
}

return Assembly.Load(dll.FullName);
};

How to load assemblies located in a folder in .NET Core console app

Not sure if it's the best way to do it but here's what I came up with:

(Only tested on .Net Core RC2 - Windows)

using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.DependencyModel;

namespace AssemblyLoadingDynamic
{
public class Program
{
public static void Main(string[] args)
{
var asl = new AssemblyLoader();
var asm = asl.LoadFromAssemblyPath(@"C:\Location\Of\" + "SampleClassLib.dll");

var type = asm.GetType("MyClassLib.SampleClasses.Sample");
dynamic obj = Activator.CreateInstance(type);
Console.WriteLine(obj.SayHello("John Doe"));
}

public class AssemblyLoader : AssemblyLoadContext
{
// Not exactly sure about this
protected override Assembly Load(AssemblyName assemblyName)
{
var deps = DependencyContext.Default;
var res = deps.CompileLibraries.Where(d => d.Name.Contains(assemblyName.Name)).ToList();
var assembly = Assembly.Load(new AssemblyName(res.First().Name));
return assembly;
}
}
}
}

MyClassLib.SampleClasses is the namespace and Sample is the type aka class name.

When executed, it will try to load the SampleClassLib.dll compiled class library in the memory and gives my console app access to MyClassLib.SampleClasses.Sample (take a look at the question) then my app calls the method SayHello() and passes "John Doe" as name to it, Therefore the program prints:

"Hello John Doe"

Quick note:
The override for the method Load doesn't matter so you might as well just replace its content with throw new NotImplementedException() and it shouldn't affect anything we care about.

.Net 4 - Include custom information in assembly

EDIT: I've updated the answer with some more detailed information.

Here is an example how you might accomplish what you want to do.

Start by defining a enum for your different types of plugin types.

public enum AssemblyPluginType
{
Skins,
Browser
}

Add two attributes that will be used to describe the plugins (assembly plugin type and potential conflicts).

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class AssemblyPluginAttribute : Attribute
{
private readonly AssemblyPluginType _type;

public AssemblyPluginType PluginType
{
get { return _type; }
}

public AssemblyPluginAttribute(AssemblyPluginType type)
{
_type = type;
}
}

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class AssemblyPluginConflictAttribute : Attribute
{
private readonly AssemblyPluginType[] _conflicts;

public AssemblyPluginType[] Conflicts
{
get { return _conflicts; }
}

public AssemblyPluginConflictAttribute(params AssemblyPluginType[] conflicts)
{
_conflicts = conflicts;
}
}

Now you can add these attributes to your assembly.

The following two lines can be added anywhere in the assembly as long as they're outside a namespace. I usually put assembly attributes in the AssemblyInfo.cs file that can be found in the Properties folder.

[assembly: AssemblyPluginAttribute(AssemblyPluginType.Browser)]
[assembly: AssemblyPluginConflictAttribute(AssemblyPluginType.Skins, AssemblyPluginType.Browser)]

Now you can use the following code to examine an assembly for specific attributes:

using System;
using System.Reflection;

namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
// Get the assembly we're going to check for attributes.
// You will want to load the assemblies you want to check at runtime.
Assembly assembly = typeof(Program).Assembly;

// Get all assembly plugin attributes that the assembly contains.
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyPluginAttribute), false);
if (attributes.Length == 1)
{
// Cast the attribute and get the assembly plugin type from it.
var attribute = attributes[0] as AssemblyPluginAttribute;
AssemblyPluginType pluginType = attribute.PluginType;
}
}
}
}


Related Topics



Leave a reply



Submit