Get methods and attributes from dll assembly - c#

I'm trying to get a small plugin mechanism running by reflecting an dll file providing my class Plugin (implementing my Plugin-Interface shared among dll and main project / sorry for naming both the same) offering an attribute of type string and a main-method activate:
Interface:
public interface Plugin
{
string pluginName{get;set;}
void activate(System.Windows.Forms.Form main);
}
dll class:
public class Plugin : WhiteA.Plugin
{
public string pluginName{get;set;}
public void activate(System.Windows.Forms.Form main){
//find the right form to modify it
IEnumerable<System.Windows.Forms.ComboBox> ie= GetControlsOfType<System.Windows.Forms.ComboBox>(main);
System.Windows.Forms.ComboBox cb=GetControlsOfType<System.Windows.Forms.ComboBox>(main).FirstOrDefault();
cb.Items.Add("Modification");
System.Windows.Forms.MessageBox.Show(cb.SelectedItem.ToString());
}
public static IEnumerable<T> GetControlsOfType<T>(System.Windows.Forms.Control root)
where T : System.Windows.Forms.Control
{
var t = root as T;
if (t != null)
yield return t;
var container = root as System.Windows.Forms.ContainerControl;
if (container != null)
foreach (System.Windows.Forms.Control c in container.Controls)
foreach (var i in GetControlsOfType<T>(c))
yield return i;
}
}
So here comes the problem, there is no type named "Plugin" to be found in the assembly. Tried to get all types from all assemblies in the directory, get all methods/members/custom attributes from them, have them logged etc, but there is nothing of my class Plugin to be found, while the dll definitely is being found, as it doesn't return the MessageBox.
string[] files=new string[]{};
string path="Error retrieving path";
try{
path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
files = System.IO.Directory.GetFiles(path, "*.dll");
}catch(Exception exF){
}
if(files.Length>0){
foreach (string dll in files){
try{
System.Reflection.Assembly sampleAssembly = System.Reflection.Assembly.LoadFrom(dll);
Type myType = sampleAssembly.GetType("Plugin");
System.Reflection.MethodInfo method = myType.GetMethod("activate");
object myInstance = Activator.CreateInstance(myType);
method.Invoke(myInstance, new object[]{this});
}catch(Exception exL){
}
}
}else{
MessageBox.Show("No working plugins detected in " + path.ToString(), "Nothing to activate", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
}
I know my code probably looks really messy to you all - I think the last try-block is the only thing relevant here, wanted to put in the class itself and the interface for a little bit transparency though - and my english isn't perfect, but I hope someone can help me out finding my attribute+method in the assembly.
EDIT:
try{
System.Reflection.Assembly sampleAssembly = System.Reflection.Assembly.LoadFrom(dll);
List<Type> list= sampleAssembly.GetTypes().Where(p =>
p.Namespace == dll &&
p.Name.Contains("Plugin")
).ToList();
Type myType=list.FirstOrDefault();
//Type myType = sampleAssembly.GetType("Plugin");
System.Reflection.MethodInfo method = myType.GetMethod("activate");
object myInstance = Activator.CreateInstance(myType);
method.Invoke(myInstance, new object[]{this});
}
I did change it according to Getting all types in a namespace via reflection
Still the same result, what did I do wrong?

As pointed out by #stuartd:
Type myType = sampleAssembly.GetType("WhiteA_Plugin_PausedVideo.Plugin");
is the solution, missed the namespace
cb.Items.Add("Modification");
doesn't work though...any suggestions?
Got it to work getting the form's children by Controls["nameOfChild"] directly, that help method to fetch all objects by class seems to be wrong here.
Plugin works now, thanks!

Related

How to instantiate the instance of a interface you plugged in? [duplicate]

In my project I need to use plugins. But to use these in my project I need to import an reference of the plugin. Since I can't know how many or which plugins the project uses beforehand I would like to import them dynamically in my project.
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = new IPlugin[pluginFiles.Length];
Assembly asm;
for (int i = 0; i < pluginFiles.Length; i++)
{
string args = pluginFiles[i].Substring(
pluginFiles[i].LastIndexOf("\\") + 1,
pluginFiles[i].IndexOf(".dll") -
pluginFiles[i].LastIndexOf("\\") - 1);
asm = Assembly.LoadFile(pluginFiles[i]);
Type[] types = asm.GetTypes();
In this code example I searched all the .dll files and put them into a string list.
But how can I now load all these .dll files? Or is there a way to use these .dll files without really importing them?
The MEF (Managed Extensibility Framework) Method:
You'll want to add references to System.ComponentModel.Composition to your projects that utilize the import/export functionality of MEF.
First, the bootstrapper/loader (in my case, I just added it to the Main class).
Program.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using MEFContract;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
var prgm = new Program();
// Search the "Plugins" subdirectory for assemblies that match the imports.
var catalog = new DirectoryCatalog("Plugins");
using (var container = new CompositionContainer(catalog))
{
// Match Imports in "prgm" object with corresponding exports in all catalogs in the container
container.ComposeParts(prgm);
}
prgm.DoStuff();
Console.Read();
}
private void DoStuff()
{
foreach (var plugin in Plugins)
plugin.DoPluginStuff();
}
[ImportMany] // This is a signal to the MEF framework to load all matching exported assemblies.
private IEnumerable<IPlugin> Plugins { get; set; }
}
}
The IPlugin interface is the contract between the imports & exports. All plugins will implement this interface. The contract is pretty simple:
IPlugin.cs:
namespace MEFContract
{
public interface IPlugin
{
void DoPluginStuff();
}
}
Finally, you can create as many plugins as you like in different assemblies. They must implement the contract interface and also be decorated with the "Export" attribute to indicate to MEF that they should be matched up with any corresponding imports. Then drop the dlls in a "Plugins" folder (this folder should reside in the same location as the executable). Here's a sample plugin:
Plugin.cs:
using System;
using System.ComponentModel.Composition;
using MEFContract;
namespace Plugin
{
[Export(typeof(IPlugin))]
public class Plugin : IPlugin
{
public void DoPluginStuff()
{
Console.WriteLine("Doing my thing!");
}
}
}
Let's assume for the sake of simplicity that all of the implementations of IPlugin have default constructors (public and no parameters).
That said, you really want to find all types that implement this interface and create an instance of them. You're on the right track somewhat, but you can simplify this tremendously with a little LINQ:
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = (
// From each file in the files.
from file in pluginFiles
// Load the assembly.
let asm = Assembly.LoadFile(file)
// For every type in the assembly that is visible outside of
// the assembly.
from type in asm.GetExportedTypes()
// Where the type implements the interface.
where typeof(IPlugin).IsAssignableFrom(type)
// Create the instance.
select (IPlugin) Activator.CreateInstance(type)
// Materialize to an array.
).ToArray();
That said, you might be better off using a dependency injection framework; they usually allow for dynamic loading and binding to interface implementations in assemblies not referenced at compile time.
Also, while a bit convoluted (in my opinion), you might want to look at the System.AddIn namespaces, as they are built specifically for this purpose. However, the dependency injection route is usually much easier if you don't have to worry about version control of contracts and the like.
I have an application which can not only load plugin at runtime, but also hot-load and unload them as user drop them in the folder, take them out or erase them. So, when I need to recompile my plugin, I don't need to re-launch my application. In my case, all plugin derive from the Plugin abstract, so they are easy to find in .DLL.
Here's my loading method:
private static void LoadPlugins(FileInfo file)
{
try
{
Assembly assembly = Assembly.LoadFrom(file.FullName);
foreach (Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(Plugin)) && type.IsAbstract == false)
{
Plugin b = type.InvokeMember(null,
BindingFlags.CreateInstance,
null, null, null) as Plugin;
plugins.Add(new PluginWrapper(b, file));
b.Register();
}
}
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
if (exSub is FileNotFoundException)
{
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
Log.Error("Plugins Manager", errorMessage);
}
}

Is there a better way to check if a project is NUnit or MSTest based?

I want to check if a c# project is NUnit or MSTest based. Currently, I read csproj's file and look for a specific string like below.
const string MSTEST_ELEMENT = "<TestProjectType>UnitTest</TestProjectType>";
const string NUNIT_ELEMENT = #"<Reference Include=""nunit.framework"">";
var file = File.ReadAllText("C:\myfile.csproj");
if (file.Contains(NUNIT_ELEMENT))
{
result = TestProjectType.NUnit;
}
else if (file.Contains(MSTEST_ELEMENT))
{
result = TestProjectType.MSTest;
}
It works as I expected but looking for a specific text in a file is ugly for me. Is there a better way to do this?
Check the solution for dll reference "NUnit.framework.dll" . For NUnit, it is neccesary to provide reference of that dll.
You could use a reflection-based approach - load the DLL from the test project, get all of the public types in it, and check for [TestClass] attributes to indicate if it's MSTest, etc.
This sample (works but not really tested) gives an example. You could make it better by referencing the test attribute types in whatever runs this code so you could do proper type comparisons instead of strings.
class Program
{
static void Main(string[] args)
{
var path = #"Path\To\Your\Test\Dll";
//load assembly:
var assembly = Assembly.LoadFile(path);
//get all public types:
var types = assembly.GetExportedTypes();
foreach (var t in types)
{
Console.WriteLine(t.Name);
//check for [TestClass] attribute:
var attributes = t.GetCustomAttributes();
foreach (var attr in attributes)
{
var typeName = attr.TypeId.ToString();
Console.WriteLine(attr.TypeId);
if (typeName== "Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute")
{
Console.WriteLine("It's MSTest");
}
else if (typeName == "Nunit.Tests.TestFixture") //not sure if that's the right type id :)
{
Console.WriteLine("It's NUnit");
}
else
{
Console.WriteLine("I Have no idea what it is");
}
}
}
Console.ReadLine();
}
}

Find if interface is used in my project C#

I need to find if specific interface is used in project, I just found something like
Type IType = Type.GetType("iInterfaceName"); // I want to look in whole project, not in one file
if (IType == null)
{
Text = "Interface Not Exist";
}
else
{
Text = "Interface Exist";
}
I am not sure if this is correct but this is the latest thing I found and in doesn't work,
any help greatly appreciated...
Use Assembly.Load before you go for GetType as follows:
Assembly.Load("YourProjectName")
.GetType("iInterfaceName");
Assume that you have the following interface:
public interface IFoo
{
}
You can find out if there's any type implementing it this way:
var isImplemented = Assembly.GetExecutingAssembly().
GetTypes().
Any(t => t.IsAssignableFrom(typeof (IFoo)));
To use the above, add to your using directives:
using System.Linq;
For .NET 2.0:
var isImplemented = false;
foreach (var t in Assembly.GetExecutingAssembly().GetTypes())
{
if (!t.IsAssignableFrom(typeof (IFoo))) continue;
isImplemented = true;
break;
}
//Operate

Dynamically load DLL files in C# project - how?

In my project I need to use plugins. But to use these in my project I need to import an reference of the plugin. Since I can't know how many or which plugins the project uses beforehand I would like to import them dynamically in my project.
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = new IPlugin[pluginFiles.Length];
Assembly asm;
for (int i = 0; i < pluginFiles.Length; i++)
{
string args = pluginFiles[i].Substring(
pluginFiles[i].LastIndexOf("\\") + 1,
pluginFiles[i].IndexOf(".dll") -
pluginFiles[i].LastIndexOf("\\") - 1);
asm = Assembly.LoadFile(pluginFiles[i]);
Type[] types = asm.GetTypes();
In this code example I searched all the .dll files and put them into a string list.
But how can I now load all these .dll files? Or is there a way to use these .dll files without really importing them?
The MEF (Managed Extensibility Framework) Method:
You'll want to add references to System.ComponentModel.Composition to your projects that utilize the import/export functionality of MEF.
First, the bootstrapper/loader (in my case, I just added it to the Main class).
Program.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using MEFContract;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
var prgm = new Program();
// Search the "Plugins" subdirectory for assemblies that match the imports.
var catalog = new DirectoryCatalog("Plugins");
using (var container = new CompositionContainer(catalog))
{
// Match Imports in "prgm" object with corresponding exports in all catalogs in the container
container.ComposeParts(prgm);
}
prgm.DoStuff();
Console.Read();
}
private void DoStuff()
{
foreach (var plugin in Plugins)
plugin.DoPluginStuff();
}
[ImportMany] // This is a signal to the MEF framework to load all matching exported assemblies.
private IEnumerable<IPlugin> Plugins { get; set; }
}
}
The IPlugin interface is the contract between the imports & exports. All plugins will implement this interface. The contract is pretty simple:
IPlugin.cs:
namespace MEFContract
{
public interface IPlugin
{
void DoPluginStuff();
}
}
Finally, you can create as many plugins as you like in different assemblies. They must implement the contract interface and also be decorated with the "Export" attribute to indicate to MEF that they should be matched up with any corresponding imports. Then drop the dlls in a "Plugins" folder (this folder should reside in the same location as the executable). Here's a sample plugin:
Plugin.cs:
using System;
using System.ComponentModel.Composition;
using MEFContract;
namespace Plugin
{
[Export(typeof(IPlugin))]
public class Plugin : IPlugin
{
public void DoPluginStuff()
{
Console.WriteLine("Doing my thing!");
}
}
}
Let's assume for the sake of simplicity that all of the implementations of IPlugin have default constructors (public and no parameters).
That said, you really want to find all types that implement this interface and create an instance of them. You're on the right track somewhat, but you can simplify this tremendously with a little LINQ:
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = (
// From each file in the files.
from file in pluginFiles
// Load the assembly.
let asm = Assembly.LoadFile(file)
// For every type in the assembly that is visible outside of
// the assembly.
from type in asm.GetExportedTypes()
// Where the type implements the interface.
where typeof(IPlugin).IsAssignableFrom(type)
// Create the instance.
select (IPlugin) Activator.CreateInstance(type)
// Materialize to an array.
).ToArray();
That said, you might be better off using a dependency injection framework; they usually allow for dynamic loading and binding to interface implementations in assemblies not referenced at compile time.
Also, while a bit convoluted (in my opinion), you might want to look at the System.AddIn namespaces, as they are built specifically for this purpose. However, the dependency injection route is usually much easier if you don't have to worry about version control of contracts and the like.
I have an application which can not only load plugin at runtime, but also hot-load and unload them as user drop them in the folder, take them out or erase them. So, when I need to recompile my plugin, I don't need to re-launch my application. In my case, all plugin derive from the Plugin abstract, so they are easy to find in .DLL.
Here's my loading method:
private static void LoadPlugins(FileInfo file)
{
try
{
Assembly assembly = Assembly.LoadFrom(file.FullName);
foreach (Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(Plugin)) && type.IsAbstract == false)
{
Plugin b = type.InvokeMember(null,
BindingFlags.CreateInstance,
null, null, null) as Plugin;
plugins.Add(new PluginWrapper(b, file));
b.Register();
}
}
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
if (exSub is FileNotFoundException)
{
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
Log.Error("Plugins Manager", errorMessage);
}
}

How to get Names of DLLs used by application

I'm looking the way to read all assemblies (.dlls) used by my app.
In a standard C# project there is "References" folder, when it is expanded I can read all libraries used.
My goal is programatically read all assemblies which are used by each project in my solution.
Finally I'd like to see what libraries are used by my compiled *.exe application.
Have you looked at Assembly.GetReferencedAssemblies?
Note that any references you don't use won't end up being emitted into the metadata, so you won't see them at execution time.
I've used GetReferencedAssemblies recursively before now to find a named type without having to specify the assembly.
To do this properly, you need to walk the assemblies, picking up the dependencies... if your exe needs Dll_A, and Dll_A needs Dll_B (even if the exe doesn't reference it), then your exe also needs Dll_B.
You can query this (on any assembly) via reflection; it takes a little work (especially to guard against circular references, which do happen; here's an example that starts at the "entry assembly", but this could just as easily be any assembly:
List<string> refs = new List<string>();
Queue<AssemblyName> pending = new Queue<AssemblyName>();
pending.Enqueue(Assembly.GetEntryAssembly().GetName());
while(pending.Count > 0)
{
AssemblyName an = pending.Dequeue();
string s = an.ToString();
if(refs.Contains(s)) continue; // done already
refs.Add(s);
try
{
Assembly asm = Assembly.Load(an);
if(asm != null)
{
foreach(AssemblyName sub in asm.GetReferencedAssemblies())
{
pending.Enqueue(sub);
}
foreach (Type type in asm.GetTypes())
{
foreach (MethodInfo method in type.GetMethods(
BindingFlags.Static | BindingFlags.Public |
BindingFlags.NonPublic))
{
DllImportAttribute attrib = (DllImportAttribute)
Attribute.GetCustomAttribute(method,
typeof(DllImportAttribute));
if (attrib != null && !refs.Contains(attrib.Value))
{
refs.Add(attrib.Value);
}
}
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
}
refs.Sort();
foreach (string name in refs)
{
Console.WriteLine(name);
}
System.Reflection.Assembly []ar=AppDomain.CurrentDomain.GetAssemblies();
foreach (System.Reflection.Assembly a in ar)
{
Console.WriteLine("{0}", a.FullName);
}
You can use AppDomain.GetAssemblies.
But this will give ALL assemblies used explicitly or implicitly in your application.
If you have an Assembly object, you can call GetReferencedAssemblies() on it to get any references that assembly uses. To get a list of assemblies the currently running project uses, you can use:
System.Reflection.Assembly.GetExecutingAssembly().GetReferencedAssemblies()
I guess you can use:
AssemblyName[] assemblies = this.GetType().Assembly.GetReferencedAssemblies();

Categories