I have a folder with many dlls. One of them contains nunit tests (functions marked with [Test] attribute). I want to run nunit test from c# code. Is there any way to locate the right dll?
thank you
You can use Assembly.LoadFile method to load a DLL into an Assembly object. Then use the Assembly.GetTypes method to get all the types defined in the assembly. Then using the GetCustomAttributes method you can check if the type is decorated with the [TestFixture] attribute. If you want it quick 'n dirty, you could just call .GetType().ToString() on each attribute and check if the string contains "TestFixtureAttribute".
You can also check for the methods inside each type. Use the method Type.GetMethods to retrieve them, and use GetCustomAttributes on each of them, this time searching for "TestAttribute".
Just in case somebody needs working solution. As you can't unload assemblies, which were loaded this way, it's better to load them in another AppDomain.
public class ProxyDomain : MarshalByRefObject
{
public bool IsTestAssembly(string assemblyPath)
{
Assembly testDLL = Assembly.LoadFile(assemblyPath);
foreach (Type type in testDLL.GetTypes())
{
if (type.GetCustomAttributes(typeof(NUnit.Framework.TestFixtureAttribute), true).Length > 0)
{
return true;
}
}
return false;
}
}
AppDomainSetup ads = new AppDomainSetup();
ads.PrivateBinPath = Path.GetDirectoryName("C:\\some.dll");
AppDomain ad2 = AppDomain.CreateDomain("AD2", null, ads);
ProxyDomain proxy = (ProxyDomain)ad2.CreateInstanceAndUnwrap(typeof(ProxyDomain).Assembly.FullName, typeof(ProxyDomain).FullName);
bool isTdll = proxy.IsTestAssembly("C:\\some.dll");
AppDomain.Unload(ad2);
Related
I have consulted code on the website http://codeproject.com with the title "Loading Assemblies from Anywhere into a New AppDomain" of Marius Bancila, but I tested the error as in the attached picture, currently, I don't know resolve, hope you help, thank you.
Link Code
https://www.codeproject.com/Articles/453778/Loading-Assemblies-from-Anywhere-into-a-New-AppDom#_articleTop
Test
public class Program
{
[STAThread]
public static void Main()
{
var project = #"D:\Github\BeyConsPlugin\BeyConsProject\bin\x64\Debug\BeyConsRevitProject.dll";//Path to assembly
var manager = new AssemblyReflectionManager();
var success = manager.LoadAssembly(project, Guid.NewGuid().ToString());
if (success)
{
var result = manager.Reflect(project, (a) =>
{
return a.GetTypes();
});
Console.WriteLine(string.Join("\n", result.Select(x => x.Name)));
}
Console.ReadKey();
manager.UnloadAssembly(project);
}
}
Error
Assembly Resolver not Set
Marius's code has a bug in AssemblyReflectionProxy with regards that the Assembly Resolver is not set if you call LoadAssembly unlike Reflect<> which does.
Depending on how a child app domain is created, when loading assemblies it might only have access to the folder as specified during creation. If you need to assembly probe elsewhere for the assembly or its dependencies you need a Assembly Resolver. When .NET is looking for an assembly for a domain, it will call your handler as specified in the assemblie's ReflectionOnlyAssemblyResolve event. If not specified or if the resolver fails to locate the assembly, it will bubble up and throw a load fail exception.
I suggest you change the code from:
public class AssemblyReflectionProxy : MarshalByRefObject
{
private string _assemblyPath;
public void LoadAssembly(String assemblyPath)
{
try
{
_assemblyPath = assemblyPath;
Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
catch (FileNotFoundException)
{
// Continue loading assemblies even if an assembly
// cannot be loaded in the new AppDomain.
}
}
...to:
public class AssemblyReflectionProxy : MarshalByRefObject
{
private string _assemblyPath;
public void LoadAssembly(String assemblyPath)
{
try
{
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve // <---- add me
+= OnReflectionOnlyResolve;
_assemblyPath = assemblyPath;
Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
catch (FileNotFoundException)
{
// Continue loading assemblies even if an assembly
// cannot be loaded in the new AppDomain.
}
}
You can see this in Sacha's original code that on which Marius based his.
Add Provision for Resolve Paths
The other problem with the code is that both assume that when loading one assembly, any dependent assemblies are in the same folder something that may not always be the case.
Alter AssemblyReflectionProxy to include a list of paths in which to probe:
public List<string> ResolvePaths { get; set; }
Then modify OnReflectionOnlyResolve to resemble the following:
private Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
{
Assembly loadedAssembly =
AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies()
.FirstOrDefault(
asm => string.Equals(asm.FullName, args.Name,
StringComparison.OrdinalIgnoreCase));
if (loadedAssembly != null)
{
return loadedAssembly;
}
foreach (var tryFolder in ResolvePaths)
{
var asmName = args.Name.Split(',');
var assemblyPath = Path.Combine(tryFolder, asmName[0] + ".dll");
if (!File.Exists(assemblyPath))
return null;
return Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
}
What's in a name?
Both articles neglected to point out the fine print when using ReflectionOnlyLoad. Though Sacha did at least mention his code was for a "code generator" I can't help but wonder that both articles with their "Loading Assemblies....into a New AppDomain" are perhaps somewhat subject to interpretation.
The aim of ReflectionOnlyLoad is just that - for reflection. If you load an assembly via this method you cannot execute any code in it. Additionally and somewhat surprising at first to most assembly reflector programmers including me is that calling GetCustomAttributes will also fail (because it attempts to "instantiate" types in the assembly).
If you are writng your own plug-in system in which each plug-in has its own App Domain, the Assembly reflection and load methods are useful for different stages of the plug-in system loading pipeline:
first pass - use ReflectionOnlyLoad as a way to inspect the plug-in to see if it is valid; perhaps you want to run some security checks safe in the knowledge that none of the plug-ins code can run during this phase
second pass - after verifying the plug-in, you can safely Load/LoadFrom the assembly and execute the code
I read this article, but it doesn't help me.
My goal is to find the class that has custom attributes.
since I don't need any instance or use codes, is there a way to load DLL file and search what I want in code, without solving dependencies problem? to lookup codes.
if you going to get all app domain assemblies types you can avoid that issue by catching the ReflectionTypeLoadException:
public static class AssemblyExtension
{
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
{
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}
}
you can use it like so:
var types = (from domainAssembly in AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic)
from assemblyType in domainAssembly.GetLoadableTypes()
select assemblyType);
if you targeting an assembly you want to load it without loading its dependencies use one of overloads:
as documented here: How to: Load Assemblies into the Reflection-Only Context
Assembly.ReflectionOnlyLoad(/*your appropriate parameter assembly name or something else*/);
I'm trying to load third party assemblies dynamically to the project and use reflection to create instance of their types.
I used:
Assembly.LoadFrom("Assembly1.dll")
Assembly.LoadFrom("Assembly2.dll")
Assembly.LoadFrom("Assembly3.dll")
Also, tried:
AppDomain.CurrentDomain.Load("Assembly1.dll")
AppDomain.CurrentDomain.Load("Assembly2.dll")
AppDomain.CurrentDomain.Load("Assembly3.dll")
However, I keep getting The method is not implemented exception while I try to create instance of one of their type as follow:
Assembly.LoadFrom("Assembly1.dll")
Assembly.LoadFrom("Assembly2.dll")
Assembly assembly= Assembly.LoadFrom("Assembly3.dll")
Type type=assembly.GetType("Assembly3.Class1")
object instance=Activator.CreateInstance(type); //throws exception at this point
However, if I directly add reference to Assembly1, Assembly2 and Assembly3 in the project and do:
Assembly3.Class1 testClass=new Assembly3.Class1();
I get no exception
I just wanted to know what I'm doing wrong? How do I load assemblies to the project dynamically. I'm guessing since creation of Class1 instance depends on another assembly Assembly1 and Assembly2, so it's failing. So, how do I load all the dependent assemblies dynamically to the appdomain/loadcontext.
Greatly appreciate your answers.
For resolve dependencies you need to handle AppDomain.AssemblyResolve Event
using System;
using System.Reflection;
class ExampleClass
{
static void Main()
{
AppDomain ad = AppDomain.CurrentDomain;
ad.AssemblyResolve += MyAssemblyResolveHandler;
Assembly assembly = ad.Load("Assembly3.dll");
Type type = assembly.GetType("Assembly3.Class1");
try
{
object instance = Activator.CreateInstance(type);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static Assembly MyAssemblyResolveHandler(object source, ResolveEventArgs e)
{
// Assembly.LoadFrom("Assembly1.dll")
// Assembly.LoadFrom("Assembly2.dll")
return Assembly.Load(e.Name);
}
}
The MyAssemblyResolveHandler triggered for each assembly than not loaded, including dependencies.
When using "ad.AssemblyResolve += MyAssemblyResolveHandler", I got the infinite loop described by 'cdie'.
So I tried a couple of thing. The following was via the MSDNs LoadFrom link
public object InitClassFromExternalAssembly(string dllPath, string className)
{
try
{
Assembly assembly = Assembly.LoadFrom(dllPath);
Type type = assembly.GetType(className);
var instance = Activator.CreateInstance(type);
return instance;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
Apparently, the Assembly.LoadFrom method requires the full path of the DLL.
Please note the possible problems loading an assembly via LoadFrom in the link.
Also, the link included by 'ArcangelZith' above has some interesting ideas.
If assembly is referenced, you could do like this to load it by name
AppDomain.CurrentDomain.Load(new AssemblyName("Assembly1.Core"))
I have a simple challenge. I dynamically need to figure out all methods with a specific attribute in C#. I'm going to load the assemblies dynamically from another application and need to find out the exact methods. The assemblies look like the followings:
Base.dll:
Class Base
{
[testmethod]
public void method1()
...
}
Derived.dll:
Class Derived:Base
{
[testmethod]
public void method2()
}
Now in 3rd application I dynamically like to load the above mentioned dlls and find out testmethods.
If I load Base.dll, I need to get testmethod1. If I load Drived.dll, should I get testmethod1 and testmethod2.
I found some code online which helped me to load dlls dynamically:
List<Assembly> a = new List<Assembly>();
string bin = #"Bin-Folder";
DirectoryInfo oDirectoryInfo = new DirectoryInfo(bin);
//Check the directory exists
if (oDirectoryInfo.Exists)
{
//Foreach Assembly with dll as the extension
foreach (FileInfo oFileInfo in oDirectoryInfo.GetFiles("*.dll", SearchOption.AllDirectories))
{
Assembly tempAssembly = null;
//Before loading the assembly, check all current loaded assemblies in case talready loaded
//has already been loaded as a reference to another assembly
//Loading the assembly twice can cause major issues
foreach (Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
{
//Check the assembly is not dynamically generated as we are not interested in these
if (loadedAssembly.ManifestModule.GetType().Namespace != "System.Reflection.Emit")
{
//Get the loaded assembly filename
string sLoadedFilename =
loadedAssembly.CodeBase.Substring(loadedAssembly.CodeBase.LastIndexOf('/') + 1);
//If the filenames match, set the assembly to the one that is already loaded
if (sLoadedFilename.ToUpper() == oFileInfo.Name.ToUpper())
{
tempAssembly = loadedAssembly;
break;
}
}
}
//If the assembly is not aleady loaded, load it manually
if (tempAssembly == null)
{
tempAssembly = Assembly.LoadFrom(oFileInfo.FullName);
}
a.Add(tempAssembly);
}
The above code is working fine and I can load the DLLs. However when I use the following code to find out the right method, it doesn't return any desired results. I'm wondering which part is not correct. The following code lists about 145 methods but non of them is one which I'm looking for.
public static List<string> GetTests(Type testClass)
{
MethodInfo[] methodInfos = testClass.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
Array.Sort(methodInfos,
delegate(MethodInfo methodInfo1, MethodInfo methodInfo2)
{ return methodInfo1.Name.CompareTo(methodInfo2.Name); });
foreach (MethodInfo mi in methodInfos)
{
foreach (var item in mi.GetCustomAttributes(false))
{
if
(item.ToString().CompareTo("Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute") == 0)
result.Add(mi.Name);
}
}
return result;
}
Can any one help me with this issue?
I'm not sure why but I tried to instantiate objects from above mentioned classes (Base and Derived) and the above mentioned code returns the right results. However as mentioned above if I don't have an object from base and derived classes and try to figure out the methods based on the types, it doesn't return the desired results.
Thx
The simplest approach is to use MethodInfo.IsDefined - quite possibly with LINQ as well:
var testMethods = from assembly in assemblies
from type in assembly.GetTypes()
from method in type.GetMethods()
where method.IsDefined(typeof(TestMethodAttribute))
select method;
foreach (var method in testMethods)
{
Console.WriteLine(method);
}
(I'd do all the sorting with LINQ as well. Obviously you can tune the GetMethods call etc to only return instance methods, for example.)
It's not entirely clear to me why your current approach doesn't work or why it does work when you've created instances - but without a short but complete example demonstrating the problem, it would be hard to diagnose it further. I'd definitely start with the code above :)
Is it possible to enumerate every function present in a DLL ? How about getting its signature ?
Can I do this in C# ? Or do I have to go low level to do this?
Regards and tks,
Jose
If it's a .NET DLL RedGate's Reflector can list the methods and even attempt to disassemble the code. It's a great item for any developer's toolbox and it's free
Edit: If you are trying to read the types and methods at runtime you'll want to use Reflection. You would have to load the Assembly and GetExportedTypes. Then, iterate over the Members to the the Methods and Properties. Here is an article from MSDN that has an example of iterating over the MemberInfo information. Also, here is an MSDN Magazine article, Extracting Data from .NET Assemblies.
Finally, Here is a little test method I wrote for executing a method on a loaded object.
In this example ClassLibrary1 has one class of Class1:
public class Class1
{
public bool WasWorkDone { get; set; }
public void DoWork()
{
WasWorkDone = true;
}
}
And here is the test:
[TestMethod]
public void CanExecute_On_LoadedClass1()
{
// Load Assembly and Types
var assm = Assembly.LoadFile(#"C:\Lib\ClassLibrary1.dll");
var types = assm.GetExportedTypes();
// Get object type informaiton
var class1 = types.FirstOrDefault(t => t.Name == "Class1");
Assert.IsNotNull(class1);
var wasWorkDone = class1.GetProperty("WasWorkDone");
Assert.IsNotNull(wasWorkDone);
var doWork = class1.GetMethod("DoWork");
Assert.IsNotNull(doWork);
// Create Object
var class1Instance = Activator.CreateInstance(class1.UnderlyingSystemType);
// Do Work
bool wasDoneBeforeInvoking =
(bool)wasWorkDone.GetValue(class1Instance, null);
doWork.Invoke(class1Instance, null);
bool wasDoneAfterInvoking =
(bool)wasWorkDone.GetValue(class1Instance, null);
// Assert
Assert.IsFalse(wasDoneBeforeInvoking);
Assert.IsTrue(wasDoneAfterInvoking);
}
If its a managed dll: Use reflection
If its unmanaged: You need to enumerate the DLL export table
You can see all of the exports in a dll by using Dependency Walker, which is a free program from Microsoft: http://en.wikipedia.org/wiki/Dependency_walker
For regular win32 DLLs, see the Dumpbin utility. It is included with Visual-C++ (including the free "express" version I believe).
example:
c:\vc9\bin\dumpbin.exe /exports c:\windows\system32\kernel32.dll