How to access class of dynamically loaded clr / cli library in C# - c#

I am novice in clr / cli and C#. I have one clr / cli library in my C# project. I want to load it dynamically and access the function of its class in C# . Can some one provide some example or right way to doing it.
Here is My header file of class declaration in Clr / cli library
namespace ManagedLibDarkClient {
public ref class AccountHandler
{
public:
AccountHandler()
{
}
static bool RegisterAccnt(String^ accountID, String^ authCode);
};
}
Please find below the function of my C# class on which I have tried to access it:--
private void RegisterWindow_ValidateEvent(object sender, ValidateEventArgs e)
{
Assembly assembly = Assembly.Loadfile("C:\\darkmailWindows\\darkmailwindows\\Dependencies\\ManagedLibDarkMail\\Lib\\ManagedLibDarkClient.dll");
if (assembly != null)
{
Type type = assembly.GetType("AccountHandler");
var obj = Activator.CreateInstance(type);
if (obj != null)
{
string[] args = { e.AccntInfo.AccntName, e.AccntInfo.AuthCode };
type.InvokeMember("RegisterAccnt", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, args);
}
else
MessageBox.Show("Unable to laod object");
}
else
MessageBox.Show("unable to load assembly");
}
}
Here in this example I am facing 2 issue :--
1- LoadFile hangs and did not return any thing.
2- I dont know how to get return value of my clr / cli function.
Here I would like to mention one more thing. I can access clr / cli if I link them statically. But I have to load it dynamically. It is crucial requirement for me.

First af all, regarding the loading issue, check that all the native dependencies (dlls) of your C++/CLI library are present in the working directory.
Make a third assembly containing an interface
public interface IAccountHandler
{
bool RegisterAccnt(String accountID, String authCode);
}
Add a reference to this assembly from both your projects, C++/CLI and C#
In C++/CLI:
public ref class AccountHandler : public IAccountHandler
{
public:
AccountHandler()
{
}
bool RegisterAccnt(String^ accountID, String^ authCode);
};
Then, in C#:
string filename = "C:\\darkmailWindows\\darkmailwindows\\Dependencies\\ManagedLibDarkMail\\Lib\\ManagedLibDarkClient.dll";
Assembly asm = Assembly.LoadFrom(filename);
foreach (Type t in asm.GetTypes())
{
if (t.GetInterfaces().Contains(typeof(IAccountHandler)))
{
try
{
IAccountHandler instance = (IAccountHandler)Activator.CreateInstance(t);
if (instance != null)
{
instance.RegisterAccnt(e.AccntInfo.AccntName, e.AccntInfo.AuthCode);
}
}
catch(Exception ex)
{
//manage exception
}
}
}
I think you don't need to make RegisterAccnt static.

You also can add the reference like you would do to link from a GAC registered or side by side assembly and manually handling the ResolveAssembly event when it fails loading. See the answer to this question.

Related

C# Constructors Ambiguity with explicit call - Error CS0012

There is an unexplained ambiguity in C#, where I explicitly try to call a constructor but the compiler thinks it is a different constructor. I will start with showing a short C# architecture we use. Then show a small "working" example I created, and the possible solution to this, but still I like to understand why this happens.
The Architecture:
CLR DLL which bridges the C++ API.
C# API which uses the bridge level.
C# Client applications that use the C# API.
Note that the C# Clients are not allowed to use the CLR level.
Example I created
A class in the CLR DLL:
#pragma once
#include <string>
using namespace System;
namespace Inner {
public ref class AInner
{
public:
AInner() : _data(new std::wstring(L"")) {}
~AInner() {
delete _data;
}
property String^ Val
{
String^ get()
{
return gcnew String((*_data).data());
}
void set(String^ value) {
System::IntPtr pVal = System::Runtime::InteropServices::Marshal::StringToHGlobalUni(value);
*_data = (const wchar_t*)pVal.ToPointer();
System::Runtime::InteropServices::Marshal::FreeHGlobal(pVal);
}
}
private:
std::wstring* _data;
};
}
Class wrapping the CLR level, in a DLL:
using System;
using Inner;
namespace Outer
{
public class A
{
public A()
{
_inner.Val = String.Empty;
}
public A(string val)
{
init(val);
}
public string Val
{
get
{
return _inner.Val;
}
set
{
_inner.Val = value;
}
}
internal A(AInner inner)
{
_inner = inner;
}
private void init(string Val)
{
_inner = new AInner();
_inner.Val = String.Empty;
}
private AInner _inner;
}
}
Note that there is an internal constructor and a public constructor.
Executable Client using the C# API DLL:
using Outer;
namespace OneClient
{
class Program
{
static void Main(string[] args)
{
string myString = "Some String";
A testA = new A(myString);
}
}
}
Twist in the story:
In the DLL wrapping the CLR level, not ALL API should be used by external clients, but can be used by internal clients, thus the internals are exposed to the internal clients by adding [assembly: InternalsVisibleTo("OneClient")] to the 'AssemblyInfo.cs' of the DLL wrapping the CLR level.
The issue
When compiling the Client code I get the following error:
error CS0012: The type 'AInner' is defined in an assembly that is not referenced. You must add a reference to assembly 'InnerOne, Version=1.0.7600.28169, Culture=neutral, PublicKeyToken=null'.
I cannot use InnerOne because clients are not allowed to use this level.
The client is exposed to both A(string val) and A(AInner inner) constructors.
Possible Workarounds:
Remove the [assembly: InternalsVisibleTo("OneClient")] - This is unacceptable due to other classes internals that the specific client needs to use.
Change the A(string val) constructor to A(string val, bool unique=true) and use it A testA = new A(myString, true) - Not a nice solution.
Use default constructor A() and call testA.Val = myString; - This is actually OK but to much code.
Change the client code from A testA = new A(myString) to A testA = new A(val:myString); - This is actually the chosen solution.
Question
Why does this ambiguity happen?
I call the A(string val) with the myString which is actually a string value
This is very strange.
Is this a bug in Microsoft compiler?
Example Sources:
Source Code One.zip
Why does this ambiguity happen?
Because to satisfy the constructor overload resolution, the compiler needs to know what all the argument types are, and it doesn't know what an AInner is.
Why not expose the AInner version as a factory method:
static internal A Create(AInner inner)
{
return new A { _inner = inner };
}
I don't see any issue in this, the problem is we are used to do the things in a wrong/briefly way.
The correct answer fot this is:
A testA = new A(val:myString);
Furthermore, all your calls (in this way is a call to a constructor/initializer but it's a call anyway) should be with the parameter name. No one (even me) writes them, but...

Two Dlls Loaded with Assembly.LoadFrom Define the Same Class and Work Fine?

This is freaking me out, and I'm guessing it's because I'm severely misunderstanding something basic about how assemblies get loaded. I was not expecting this to work, can someone explain why it does?
Projects:
Plugins has the definition of a plugin class
Lib1 References Plugins and defines a plugin class
Lib2 References Plugins and defines a plugin class
Console references Plugins and looks for dlls near itself to load
Lib1 and Lib2 share a code file via symlink:
namespace Shared
{
public class SharedClass
{
public static string Key { get; set; }
}
}
Lib1 Plugin:
namespace Lib1
{
public class Lib1Plugin : Plugin
{
public override void Load()
{
SharedClass.Key = "Lib1 Key";
Console.WriteLine(SharedClass.Key);
}
public override void Run()
{
Console.WriteLine(SharedClass.Key);
}
}
}
Lib2 Plugin:
namespace Lib2
{
public class Lib2Plugin : Plugin
{
public override void Load()
{
SharedClass.Key = "Lib2 Key";
Console.WriteLine(SharedClass.Key);
}
public override void Run()
{
Console.WriteLine(SharedClass.Key);
}
}
}
Console:
static class Functions
{
public static IEnumerable<Type> FindDerivied(Assembly asm, Type baseType)
{
try
{
return asm.GetTypes().Where(t => baseType.IsAssignableFrom(t) && t != baseType);
}
catch (Exception e)
{
return new List<Type>();
}
}
}
class Program
{
static void Main(string[] args)
{
var di = new DirectoryInfo("Plugins");
var bos = new List<Plugin>();
if (di.Exists)
{
var dlls = di.EnumerateFiles();
foreach (var dll in dlls)
{
var asm = Assembly.LoadFrom(dll.FullName);
var builders = Functions.FindDerivied(asm, typeof(Plugin));
foreach (var builder in builders)
{
var bo = (Plugin)Activator.CreateInstance(builder);
bo.Load();
bos.Add(bo);
}
}
foreach (var bo in bos)
{
bo.Run();
}
var asms = AppDomain.CurrentDomain.GetAssemblies();
foreach (var asm in asms)
{
var exports =
asm.GetExportedTypes().Where(type => type.Name == "SharedClass")
.ToList();
foreach (var export in exports)
{
Console.WriteLine(export.FullName);
}
}
}
}
}
Output:
Lib1 Key
Lib2 Key
Lib1 Key
Lib2 Key
Shared.SharedClass
Shared.SharedClass
How does it know the difference!?
There is nothing preventing two assemblies from declaring types with identical fully-qualified names. Whether those types are similar or completely different (or here, are actually defined in the same source file) is irrelevant.
Although the page discussing extern alias uses "two versions of the same assembly" as it's motivating example, it's describing the general mechanism that would allow any consuming application to consume two (or more) libraries that declare types with identical fully-qualified type names.
You might have to reference two versions of assemblies that have the same fully-qualified type names. For example, you might have to use two or more versions of an assembly in the same application. By using an external assembly alias, the namespaces from each assembly can be wrapped inside root-level namespaces named by the alias, which enables them to be used in the same file.
And further, what this comes down to is that a fully-qualified type name, by itself, does not uniquely identify a specific type. A type's identity includes not just its name but also its assembly.
You did not share compiled code, but the file SharedClass.cs file. So the libraries don't know about each other's SharedClass, and hence there is nothing which should "know the difference". At compile time, each plugin gets linked to the SharedClass contained in the same assembly, and at runtime there are two SharedClasses which do not know anything about each other.

Is it possible to import another C# project with a main method and set it as the entry point?

Is it possible to create a project in C# without a main method, and import another project that has one, and set the entry point to be that main method of the imported project?
The purpose of this is to provide a library complete with its main method and all startup code, requiring only a couple of "plugin" methods. This will minimize boiler-plate (in particular, start-up) code.
Abstract example:
Consider Project 1 with Program.cs:
namespace Project1 {
public class Program {
public static void Main() {
Console.WriteLine("All your Main are belong to us");
Plugin pluginClass = MagicallyGetInstanceOfPluginClassProbablyThroughInjection();
pluginClass.DoSomethingSpecificDependingOnPluginClassDefinition();
}
private Plugin MagicallyGetInstanceOfPluginClassProbablyThroughInjection(){
/*...*/
}
}
public interface Plugin {
void DoSomethingSpecificDependingOnPluginClassDefinition();
}
}
Now consider Project 2 with only class MyPlugin.cs:
namespace Project2 {
using Project1;
public class MyPlugin: Plugin {
public void DoSomethingSpecificDependingOnPluginClassDefinition() {
Console.WriteLine("I'm doing something specific!");
}
}
}
Things to point out:
Project 1 is just a library, possibly nuget'ed
It's Project 2 that imports Project 1, not the other way around
The MyPlugin.cs class above is the only class/file in the project (excluding manifests, app configs, etc)
Aim:
Project 2 should compile into an executable, running Project 1's Main function without writing any more code (no boiler-plate start-up/set-up code). There can then be Project 3, 4, 5, ... that all implement their Plugin-specific code, import Project 1 and run as independent instances.
Is this possible to do? Or do I still have to make a main method in each project that calls the imported project's start-up code? Many thanks in advance!
You could create a plugin container that scans the directory for assemblies and tries to load them. For this you would need a shared interface (interface known to your program and the plugins.
You could then add the DLLs into a defined plugin directory or you could reference the projects inside your main running project.
An example of the interface could be:
public interface IStandAlone
{
void Run();
}
And 1 or to simple implementations could be
public class Program1 : IStandAlone
{
public void Run()
{
Console.WriteLine("Program1");
}
}
public class Program2 : IStandAlone
{
public void Run()
{
Console.WriteLine("Program 2");
}
}
Then you would need to load the possible assemblies, either from the current assemblies (as is done in this example), or by scanning a directory for dlls that might have your type.
An example that scans the current assemblies for any implementations of the a definite type:
public class PluginContainer<T>
{
Type targetType = typeof(T);
public virtual IList<Type> GetMatchingTypes()
{
Assembly[] currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();
IList<Type> items = new List<Type>();
if (currentAssemblies == null || currentAssemblies.Length == 0)
{
Console.WriteLine("No assemblies found!");
return items;
}
foreach (Assembly ass in currentAssemblies)
{
try
{
var types = ass.GetTypes();
foreach (var t in types)
{
if (t.IsInterface)
{
continue;
}
if (!(targetType.IsAssignableFrom(t)))
{
continue;
}
items.Add(t);
}
}
catch (ReflectionTypeLoadException rtle)
{
/* In case the loading failed, scan the types that it was able to load */
Console.WriteLine(rtle.Message);
if (rtle.Types != null)
{
foreach (var t in rtle.Types)
{
if (t.IsInterface)
{
continue;
}
if (!(targetType.IsAssignableFrom(t)))
{
continue;
}
items.Add(t);
}
}
}
catch (Exception ex)
{
/* General exception */
Console.WriteLine(ex.Message);
}
}
return items;
}
public IList<T> GetPlugins()
{
IList<Type> matchingTypes = GetMatchingTypes();
IList<T> items = new List<T>();
if (matchingTypes == null || matchingTypes.Count == 0)
{
Console.WriteLine("No matching types of {0} found", typeof(T).FullName);
return null;
}
foreach (Type type in matchingTypes)
{
try
{
T nObj = (T)Activator.CreateInstance(type);
items.Add(nObj);
}
catch (Exception ex)
{
Console.WriteLine("Error occured trying to run {0}\r\n{1}", type.FullName, ex.Message);
}
}
return items;
}
}
which can then be used inside a main method to scan for any available plugins, and to execute them:
static void Main(string[] args)
{
PluginContainer<IStandAlone> container = new PluginContainer<IStandAlone>();
var plugins = container.GetPlugins();
foreach (var plugin in plugins)
{
plugin.Run();
}
Console.ReadLine();
}
which eventually gives as an output:
Program1
Program 2
Please keep in mind, that this is a very basic example, and that a well thought out interface should be in place, that really only contains the basics, and might give some feedback to the program running the plugins (though this shouldn't be a requirement). Also offering a versions for plugins, maybe an update Url, and such things could be handy in case your plugins can be maintained or implemented by 3th party providers...
I believe the requirement for a start-up method is that it's signature needs to be public static void and it needs to have a single string[] parameter. It may also need to be named "Main", but I doubt it. If a method matches those requirements it should be available to pick as the start-up method in a project's properties.
However the start-up method is what's used to run a stand-alone executable program when it's launched. I believe what you are looking for is more of a plug-in architecture. You can create an attribute and tag your entry point method(s) with that attribute. Then, in your service, you would need to reflect over the classes in the plug-in assembly which you are loading and find methods marked with your custom attribute, and invoke the appropriate one.
Sorry if this sounds a bit vague but a "plugin architecture" is not a trivial topic.
An alternative would be to use the System.Diagnostics.Process.Start(string) method to just launch your "plug-in" as a stand-alone program.
I'm not really sure what you're asking for. Every C# project is either a .exe or a .dll. A .dll does not have a main method, but an .exe needs one. Here's the link describing what it should look like.
If you have many very similar applications then you can move all the common stuff in a .dll project and reference it in all the applications. Then you can call the methods from each .exe. You'll still have a Main() method in each .exe, but it will only contain one line which calls the common implementation.
Or you can do something like a plugin architecture, where you have one .exe and all the other applications are .dll projects, which are loaded and executed by the .exe as needed.
Six of one, half a dozen of the other, it's all the same in the end.

Correct Way to Load Assembly, Find Class and Call Run() Method

Sample console program.
class Program
{
static void Main(string[] args)
{
// ... code to build dll ... not written yet ...
Assembly assembly = Assembly.LoadFile(#"C:\dyn.dll");
// don't know what or how to cast here
// looking for a better way to do next 3 lines
IRunnable r = assembly.CreateInstance("TestRunner");
if (r == null) throw new Exception("broke");
r.Run();
}
}
I want to dynamically build an assembly (.dll), and then load the assembly, instantiate a class, and call the Run() method of that class. Should I try casting the TestRunner class to something? Not sure how the types in one assembly (dynamic code) would know about my types in my (static assembly / shell app). Is it better to just use a few lines of reflection code to call Run() on just an object? What should that code look like?
UPDATE:
William Edmondson - see comment
Use an AppDomain
It is safer and more flexible to load the assembly into its own AppDomain first.
So instead of the answer given previously:
var asm = Assembly.LoadFile(#"C:\myDll.dll");
var type = asm.GetType("TestRunner");
var runnable = Activator.CreateInstance(type) as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();
I would suggest the following (adapted from this answer to a related question):
var domain = AppDomain.CreateDomain("NewDomainName");
var t = typeof(TypeIWantToLoad);
var runnable = domain.CreateInstanceFromAndUnwrap(#"C:\myDll.dll", t.Name) as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();
Now you can unload the assembly and have different security settings.
If you want even more flexibility and power for dynamic loading and unloading of assemblies, you should look at the Managed Add-ins Framework (i.e. the System.AddIn namespace). For more information, see this article on Add-ins and Extensibility on MSDN.
If you do not have access to the TestRunner type information in the calling assembly (it sounds like you may not), you can call the method like this:
Assembly assembly = Assembly.LoadFile(#"C:\dyn.dll");
Type type = assembly.GetType("TestRunner");
var obj = Activator.CreateInstance(type);
// Alternately you could get the MethodInfo for the TestRunner.Run method
type.InvokeMember("Run",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
obj,
null);
If you have access to the IRunnable interface type, you can cast your instance to that (rather than the TestRunner type, which is implemented in the dynamically created or loaded assembly, right?):
Assembly assembly = Assembly.LoadFile(#"C:\dyn.dll");
Type type = assembly.GetType("TestRunner");
IRunnable runnable = Activator.CreateInstance(type) as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();
I'm doing exactly what you're looking for in my rules engine, which uses CS-Script for dynamically compiling, loading, and running C#. It should be easily translatable into what you're looking for, and I'll give an example. First, the code (stripped-down):
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using CSScriptLibrary;
namespace RulesEngine
{
/// <summary>
/// Make sure <typeparamref name="T"/> is an interface, not just any type of class.
///
/// Should be enforced by the compiler, but just in case it's not, here's your warning.
/// </summary>
/// <typeparam name="T"></typeparam>
public class RulesEngine<T> where T : class
{
public RulesEngine(string rulesScriptFileName, string classToInstantiate)
: this()
{
if (rulesScriptFileName == null) throw new ArgumentNullException("rulesScriptFileName");
if (classToInstantiate == null) throw new ArgumentNullException("classToInstantiate");
if (!File.Exists(rulesScriptFileName))
{
throw new FileNotFoundException("Unable to find rules script", rulesScriptFileName);
}
RulesScriptFileName = rulesScriptFileName;
ClassToInstantiate = classToInstantiate;
LoadRules();
}
public T #Interface;
public string RulesScriptFileName { get; private set; }
public string ClassToInstantiate { get; private set; }
public DateTime RulesLastModified { get; private set; }
private RulesEngine()
{
#Interface = null;
}
private void LoadRules()
{
if (!File.Exists(RulesScriptFileName))
{
throw new FileNotFoundException("Unable to find rules script", RulesScriptFileName);
}
FileInfo file = new FileInfo(RulesScriptFileName);
DateTime lastModified = file.LastWriteTime;
if (lastModified == RulesLastModified)
{
// No need to load the same rules twice.
return;
}
string rulesScript = File.ReadAllText(RulesScriptFileName);
Assembly compiledAssembly = CSScript.LoadCode(rulesScript, null, true);
#Interface = compiledAssembly.CreateInstance(ClassToInstantiate).AlignToInterface<T>();
RulesLastModified = lastModified;
}
}
}
This will take an interface of type T, compile a .cs file into an assembly, instantiate a class of a given type, and align that instantiated class to the T interface. Basically, you just have to make sure the instantiated class implements that interface. I use properties to setup and access everything, like so:
private RulesEngine<IRulesEngine> rulesEngine;
public RulesEngine<IRulesEngine> RulesEngine
{
get
{
if (null == rulesEngine)
{
string rulesPath = Path.Combine(Application.StartupPath, "Rules.cs");
rulesEngine = new RulesEngine<IRulesEngine>(rulesPath, typeof(Rules).FullName);
}
return rulesEngine;
}
}
public IRulesEngine RulesEngineInterface
{
get { return RulesEngine.Interface; }
}
For your example, you want to call Run(), so I'd make an interface that defines the Run() method, like this:
public interface ITestRunner
{
void Run();
}
Then make a class that implements it, like this:
public class TestRunner : ITestRunner
{
public void Run()
{
// implementation goes here
}
}
Change the name of RulesEngine to something like TestHarness, and set your properties:
private TestHarness<ITestRunner> testHarness;
public TestHarness<ITestRunner> TestHarness
{
get
{
if (null == testHarness)
{
string sourcePath = Path.Combine(Application.StartupPath, "TestRunner.cs");
testHarness = new TestHarness<ITestRunner>(sourcePath , typeof(TestRunner).FullName);
}
return testHarness;
}
}
public ITestRunner TestHarnessInterface
{
get { return TestHarness.Interface; }
}
Then, anywhere you want to call it, you can just run:
ITestRunner testRunner = TestHarnessInterface;
if (null != testRunner)
{
testRunner.Run();
}
It would probably work great for a plugin system, but my code as-is is limited to loading and running one file, since all of our rules are in one C# source file. I would think it'd be pretty easy to modify it to just pass in the type/source file for each one you wanted to run, though. You'd just have to move the code from the getter into a method that took those two parameters.
Also, use your IRunnable in place of ITestRunner.
You will need to use reflection to get the type "TestRunner". Use the Assembly.GetType method.
class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.LoadFile(#"C:\dyn.dll");
Type type = assembly.GetType("TestRunner");
var obj = (TestRunner)Activator.CreateInstance(type);
obj.Run();
}
}
When you build your assembly, you can call AssemblyBuilder.SetEntryPoint, and then get it back from the Assembly.EntryPoint property to invoke it.
Keep in mind you'll want to use this signature, and note that it doesn't have to be named Main:
static void Run(string[] args)

Writing C# Plugin System

I'm trying to write a plugin system to provide some extensibility to an application of mine so someone can write a plugin(s) for the application without touching the main application's code (and risk breaking something).
I've got the base "IPlugin" interface written (atm, nothing is implemented yet)
Here is how I'm loading:
public static void Load()
{
// rawr: http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx
String[] pluginFiles = Directory.GetFiles(Plugins.PluginsDirectory, "*.dll");
foreach (var plugin in pluginFiles)
{
Type objType = null;
try
{
//Assembly.GetExecutingAssembly().GetName().Name
MessageBox.Show(Directory.GetCurrentDirectory());
Assembly asm = Assembly.Load(plugin);
if (asm != null)
{
objType = asm.GetType(asm.FullName);
if (objType != null)
{
if (typeof(IPlugin).IsAssignableFrom(objType))
{
MessageBox.Show(Directory.GetCurrentDirectory());
IPlugin ipi = (IPlugin)Activator.CreateInstance(objType);
ipi.Host = Plugins.m_PluginsHost;
ipi.Assembly = asm;
}
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString(), "Unhandled Exception! (Please Report!)", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);
}
}
}
A friend tried to help but I really didn't understand what was wrong.
The folder structure for plugins is the following:
\
\Plugins\
All plugins reference a .dll called "Lab.Core.dll" in the [root] directory and it is not present in the Plugins directory because of duplicate references being loaded.
The plugin system is loaded from Lab.Core.dll which is also referenced by my executable. Type "IPlugin" is in Lab.Core.dll as well. Lab.Core.dll is, exactly as named, the core of my application.
EDIT:
Question: Why/What is that exception I'm getting and how could I go about fixing it?
FINAL EDIT:
Ok so I decided to re-write it after looking at some source code a friend wrote for a TF2 regulator.
Here's what I got and it works:
public class TestPlugin : IPlugin {
#region Constructor
public TestPlugin() {
//
}
#endregion
#region IPlugin Members
public String Name {
get {
return "Test Plugin";
}
}
public String Version {
get {
return "1.0.0";
}
}
public String Author {
get {
return "Zack";
}
}
public Boolean OnLoad() {
MessageBox.Show("Loaded!");
return true;
}
public Boolean OnAllLoaded() {
MessageBox.Show("All loaded!");
return true;
}
#endregion
}
public static void Load(String file) {
if (!File.Exists(file) || !file.EndsWith(".dll", true, null))
return;
Assembly asm = null;
try {
asm = Assembly.LoadFile(file);
} catch (Exception) {
// unable to load
return;
}
Type pluginInfo = null;
try {
Type[] types = asm.GetTypes();
Assembly core = AppDomain.CurrentDomain.GetAssemblies().Single(x => x.GetName().Name.Equals("Lab.Core"));
Type type = core.GetType("Lab.Core.IPlugin");
foreach (var t in types)
if (type.IsAssignableFrom((Type)t)) {
pluginInfo = t;
break;
}
if (pluginInfo != null) {
Object o = Activator.CreateInstance(pluginInfo);
IPlugin plugin = (IPlugin)o;
Plugins.Register(plugin);
}
} catch (Exception) {
}
}
public static void LoadAll() {
String[] files = Directory.GetFiles("./Plugins/", "*.dll");
foreach (var s in files)
Load(Path.Combine(Environment.CurrentDirectory, s));
for (Int32 i = 0; i < Plugins.List.Count; ++i) {
IPlugin p = Plugins.List.ElementAt(i);
try {
if (!p.OnAllLoaded()) {
Plugins.List.RemoveAt(i);
--i;
}
} catch (Exception) {
Plugins.List.RemoveAt(i);
--i;
}
}
}
The Managed Extensibility Framework (MEF) is a new library in .NET that enables greater reuse of applications and components. Using MEF, .NET applications can make the shift from being statically compiled to dynamically composed. If you are building extensible applications, extensible frameworks and application extensions, then MEF is for you.
http://www.codeplex.com/MEF
Edit: CodePlex is going away - the code has been moved to Github for archival purposes only: https://github.com/MicrosoftArchive/mef
MEF is now a part of the Microsoft .NET Framework, with types primarily under the System.Composition. namespaces. There are two versions of MEF
System.ComponentModel.Composition, which has shipped with .NET 4.0 and higher. This provides the standard extension model that has been used in Visual Studio. The documentation for this version of MEF can be found here
System.Compostion is a lightweight version of MEF, which has been optimized for static composition scenarios and provides faster compositions. It is also the only version of MEF that is a portable class library and can be used on phone, store, desktop and web applications. This version of MEF is available via NuGet and is documentation is available here
It sounds like you have a circular reference. You said your plugins reference Lab.Core.DLL, but you also say the plugins are loaded from Lab.Core.DLL.
Am I misunderstanding what is happening here?
EDIT: OK now that you have added your question to the question...
You need to have Lab.Core.DLL accessible to the plugin being loaded since it is a dependency. Normally that would mean having it in the same directory or in the GAC.
I suspect there are deeper design issues at play here, but this is your immediate problem.
As a side answer, i use these 2 interfaces for implementing that
public interface IPlugin {
string Name { get; }
string Description { get; }
string Author { get; }
string Version { get; }
IPluginHost Host { get; set; }
void Init();
void Unload();
IDictionary<int, string> GetOptions();
void ExecuteOption(int option);
}
public interface IPluginHost {
IDictionary<string, object> Variables { get; }
void Register(IPlugin plugin);
}

Categories

Resources