Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>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.</p> <p>This answer all revolves around using an event handler for the <code>AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve</code> 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(). </p> <p>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.</p> <p>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.</p> <p>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.</p> <p>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.</p> <p>Finally, some code:</p> <pre><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&lt;string, string&gt; _allAsms = new Dictionary&lt;string, string&gt;(); /// &lt;summary&gt; /// Tries to list all of the Types inside an assembly and any interfaces those types inherit from. /// &lt;/summary&gt; /// &lt;param name="pathName"&gt;The path of the original assembly, without the assembly file&lt;/param&gt; /// &lt;param name="fileName"&gt;The name of the assembly file as it is found on disk.&lt;/param&gt; 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); } } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload