Instantiate class using reflection - c#

I have different class libraries that each implement an interface IImportCharacter. In my main app, the user selects a DLL and the app needs to check if the library implements the interface and then instantiate the class in the library which implements it. I'm trying to use reflection to do this but I keep getting:
Unable to cast object of type 'CustomCharacter.Ogre' to type
'MainGame.IImportCharacter'.
Assembly assembly = assemblyPath;
foreach (Type type in assembly.GetTypes())
{
IImportCharacter instance = null;
if (type.GetInterface("IImportCharacter") != null)
{
//exception thrown at this line
instance = (IImportCharacter)Activator.CreateInstance(type);
}
}
I've copied the same IImportCharacter file into the main project, otherwise the compiler complains it doesn't know what IImportCharacter is. I think this might be causing the problem since it's not the same one being dynamically loaded. How can I fix this?

Yes, I think the interface you're casting to is a different type with the same name. Why don't you move IImportCharacter to a shared assembly that you can reference both from your code and the one you're loading, since it's your interface?
Otherwise, try using Convert.ChangeType() with the dynamically loaded interface on your dynamically loaded type.

Related

Immediate window evaluates to true while debugging evaluates to false? [duplicate]

I have a class (TabControlH60) that both inherits from a base class (UserControl) and implements an interface (IFrameworkClient). I instantiate the object using the .NET Activator class. With the returned instance, I can cast to the UserControl base class, but not to the interface. The exception I get is below the code snipet. How do I cast to the interface?
object obj = Activator.CreateInstance(objType);
Type[] interfaces = obj.GetType().GetInterfaces(); // contains IFrameworkClient
m_Client = (UserControl)obj; // base class cast works
IFrameworkClient fc = (IFrameworkClient)obj; // interface cast fails
// Note: The (IFrameworkClient)obj cast works fine in the debugger Watch window.
{"Unable to cast object of type 'FPG.H60.AFF.TabControlH60' to type
'FPG.AFF.Interfaces.IFrameworkClient'."}
I hat the same problems with a library of mine providing "plugin"-functionality... I got it finally working...
Here was my problem: I had one main assembly using plugins, one assembly with the plugin (Plugin.dll) AND (important) another assembly providing the plugin-functionality (Library.dll).
The Plugin.dll referenced the main assembly (in order to be able to extend it) and the Library.dll with the plugin-func. - it's binaries got to a directory "./Plugins" relative to the main assembly.
The main assembly also referenced the plugin-func. assembly in order to use the "PluginManager" is wrote. This "PluginManager" gets a path and loads all *.dll files via reflection in order to analyze if there is a "IPlugin"-interface (which comes from Library.dll too).
Everytime I called the PluginManager to load the plugins it could not cast them to "IPlugin" although they implemented it.
I nearly got mad - but then I found out the whole problem. By compiling the plugin there was not only the "Plugin.dll" but the "Library.dll" written to the "./Plugins" directory. By accidentally loading the "Library.dll" every time with my PluginManager I now had two types of "IPlugin" - one in the actual "Library.dll" that is used from the main assembly and one that was loaded through my PluginManager - and those were incompatible!
Attention - if you just do not load "./Plugins/Library.dll" you nevertheless encounter the problem - because if you load "Plugin.dll" which references "Library.dll" then it just uses the one in the same directory... TILT...!! My PluginManager now just deletes "Library.dll" where it find it.
The clue is: Be sure that you do not access two assemblies in different contexts!
The most likely cause here is that IFrameworkClient is from a different assembly in the two cases, and is thus a different .NET type. Even if it is the same code, it can be a different type.
Check the AssemblyQualifiedName. Note also that if you are loading this assembly with reflection you can get a different type even with the same AssemblyQualifiedName, thanks to the load-context.
Define IFrameworkClient Interface in independent namespace (must be have namespace) of independent project (class library).Then add refrence of the class library to Control project and main project
When the Interface is in a different assembly and i get my class dynamically at run-time in a different assembly, interface casting will be failed like your sample (C# knows our interface as a different type than which one the class inherited from that).
This is my simple and useful technique in this cases:
When I'm sure my Class has inherited from the mentioned Interface (eq. IFrameworkClient), so i write one magic line of code like this:
dynamic fc = obj as IFrameworkClient ?? (dynamic) obj;
By this technique you can:
Write your codes after this line of code for fc at design time base on Interface members info and vs editor intelligences system.
Prevent any interface casting error at run-time
Notes:
You need C# v4 to use dynamic type
Usually i don't like to use dynamic types in my codes but it can help us in some cases like this
Something tells me your sample code is leaving some stuff out...
class Program
{
static void Main(string[] args)
{
var type = typeof(MyClass);
object obj = Activator.CreateInstance(type);
Type[] interfaces = obj.GetType().GetInterfaces();
var m_Client = (UserControl)obj;
IFrameworkClient fc = (IFrameworkClient)obj;
}
}
public interface IFrameworkClient { }
public class UserControl { }
public class MyClass : UserControl, IFrameworkClient { }
This compiles and runs.
I'm betting that the DLL containing the definition of IFrameworkClient hasn't yet been loaded before you try to cast. This can happen when you're using Activator.CreateInstance.
Try inserting var forceLoad = typeof(IFrameworkClient); before the cast.
If the class FPG.H60.AFF.TabControlH60 actually does implement IFrameworkClient there should be no reason this would fail. The only thing I can think of that causes this exception is if the assembly that contains IFrameworkClient is strongly named and the Tab Control object happens to reference a different version of the containing assembly or your are using a different interface with the name IFrameworkClient.
In my case I had to add a build event to copy the needed DLL since I was creating instances and assigning to interface types at run time. Otherwise the DLL loaded might not be the most up-to-date DLL, and therefore may not cast to the interface.
The reason I used build events in this case (instead of adding the DLL as a reference) is that the architecture is such that the main application should only reference the interface types, and everything else should be loaded dynamically.
TLDR;
In the case of loading types dynamically from another DLL, make sure you copy the most recent version of that DLL to the bin directory using build events, otherwise casting may not work when it appears that it should.
I ran into same issue and I just added the following code
private void LoadAssemblyPlugins(string dll)
Assembly ass = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => new Uri(a.CodeBase).Equals(new Uri(dll)));
if (ass == null)
// Load it here
// use activator here
Although, in production it will never be a problem, in unit test it was but now I don't need to load it again and create a "different type"
The cast isn't working because you're trying to cast from type object to the interface. If you replace the interface cast line with:
IFrameworkClient fc = (IFrameworkClient)m_Client;
It will work.
Alternately, I'm mildly certain that you could do the cast from the object to the interface with the as operator.
See this article for more information:
http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx
One more piece of the puzzle. Interfaces do not derive from object:
http://blogs.msdn.com/ericlippert/archive/2009/08/06/not-everything-derives-from-object.aspx

How to access class with interface from DLL?

I'm developing a program that uses plugins. All plugins are classes that inherits from KrotAPI.IKrotAPI interface. The interface is stored in krotapi.cs file, that is common for both host's csproj and every plugins' *.csproj.
The host loads plugins using this code
dynamic LoadPlugin(Assembly asm)
{
foreach (Type type in asm.GetTypes())
{
if (type.GetInterface("KrotAPI.IKrotPlugin") != null)
{
PluginType = type;
dynamic inst = Activator.CreateInstance(type);
KrotAPI.IKrotPlugin inst2 = inst as KrotAPI.IKrotPlugin;
if (inst2 == null) return inst;
Console.WriteLine("Link to API is set up.");
return inst2;
}
}
throw new Exception("There are no valid plugin(s) in the DLL.");
}
The inst2 is always null, and I am forced to use slow and buggy dynamic calls. How to make it like inst, but with KrotAPI.IKrotPlugin type?
Second question on almost same topic.
Some plugins' functions returns an result of type KrotAPI.FindData (it's an structure, that is stored in above krotapi.cs file). But i cannot access it:
dynamic fd = new KrotAPI.FindData();
if (NextItemInfo != null) //NextItemInfo is an instance of that struct
fd = NextItemInfo;
listBox1.Items.Add(fd.FileName);
On last row NET Framework throws this exception:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
'System.ValueType' does not contain a definition for 'FileName'
however the FileName field is hardcoded in the struct and certainly not a null, or
Microsoft.CSharp.RuntimeBinder.RuntimeBinderInternalCompilerException
An unexpected exception occurred while binding a dynamic operation
if I replace the last line with KrotAPI.FindData fd2 = (KrotAPI.FindData) fd; listBox1.Items.Add(fd2.FileName);.
WTF?
You have your IKrotAPI interface defined in your host and your plugins. This means that they are not the same interface. The containing assembly is part of the interface's full identity. Each of your plugins is actually implementing an interface that is defined within it's own assembly, which is not the same interface that is defined within the host assembly.
There are two ways to solve this:
Have your plugins reference the host assembly for the IKrotAPI interface.
Create a third assembly just for the interfaces and data types (e.g. KrotAPI.FindData) that are common to both host and server.
I personally prefer the second approach. It allows you to upgrade your host assembly without invalidating the existing plugins. To use a common naming convention for this sort of assembly, this third assembly might be called something like KrotAPI. I tend to think of an "API" as the set of interfaces and data types that someone (i.e. a plugin developer) would code against, but all actual runnable code would, in your case, be in either the host or the plugin.

Class instantiated from DLL does not seem to be implementing interface properly

I have a class I am attempting to instantiate through the use of Assembly and Activator, this class implements an interface, however when I run the instance of the class through a conditional that checks that the class implements it, I am getting false. What could be the problem?
My code where I am checking for implementation:
string className = MyClass;
Type type = null;
Assembly assembly = Assembly.LoadFile("#C:\\MyDLL", new Evidence(AppDomain.CurrentDomain.Evidence));
type = assembly.GetType(className);
object instance = Activator.CreateInstance(type);
//never makes it past this conditional
if (!(instance is MyInterface)
{
//It always endsup in here, when it shouldn't.
System.Writeline("ERROR");
}
else{
//This is what needs to happen
}
Code for the class MyClass that is outside the scope of all of this, and in MyDLL
public class MyClass: MyInterface
{
//Contents irrelevent to my problem
}
I am at a loss as to why this is not passing the conditional. Could I be instantiation the class wrong? Also to note I am a huge rookie when it comes to using Assembly/Activator and using interfaces.
Most likely reason - both DLL and your code have own version of MyInterface. This could happen either because
one did not want to spend time to come up with good unique name for interface,
someone decided to share interface as source instead of via assembly reference,
good named interfaces in the different namespaces and you are using the wrong one.
You may be referencing your assembly directly. If so, the types you load dynamically will have the identical name and namespace, but are considered different by the runtime.

myInterface.GetGenericTypeDefinition() not equals to myType but myInterface.GetGenericTypeDefinition().FullName equals to myType.FullName

I'm looking for all implementations of IHandle<> in my assembly.
public interface IHandle<T> where T : class, IEvent, new()
Those classes looks like
public class IHandleEventX : IHandle<EventX>{...}
public class IHandleAnotherEvent : IHandle<AnotherEvent>{...}
Currently, I got the following code.
aLoader.LoadImplementationOf(typeof(IHandle<>));
// my aLoader class method
public void LoadImplementationOf(Type genericInterface)
{
// theres another foreach here, to iterate over all assemblies
foreach (Type aType in allMyAssemblies.GetTypes())
{
var interfaces = aType.GetInterfaces();
foreach (var currentInterface in interfaces)
{
if (!currentInterface.IsGenericType) { continue; }
// the statement below always return false
if (currentInterface.GetGenericTypeDefinition() == genericInterface)
{}
// those two statement (FullName and AssemblyQualifiedName), works as expected
if (currentInterface.GetGenericTypeDefinition().FullName == genericInterface.FullName)
{}
if (currentInterface.GetGenericTypeDefinition().AssemblyQualifiedName == genericInterface.AssemblyQualifiedName)
{}
// those below also fail
if (currentInterface.GetGenericTypeDefinition().IsAssignableFrom(genericInterface))
{}
if (genericInterface.IsAssignableFrom(currentInterface.GetGenericTypeDefinition()))
{}
// can't do currentInterface.GetGenericTypeDefinition() is genericInterface compiler error
}
}
Why comparing types fail, but comparing types fullname property works ?
Also, whats the best way to do this?
Edit: I rewrote the sample, using only one assembly and IsAssignableFrom worked. I'll investigate and update here later to see why isn't working using more than one assembly - as #HansPassant pointed out
It's working now, but I'm not sure why...
I was working with 3 assemblies.
Asm Loader, there was only de ALoader class there
Asm Domain, with interfaces IHandle<>, IEvent<> and classes IHandleEventX, IHandleAnotherEvent
Asm Test, my test project, I was calling aLoader.LoadImplementationOf(typeof(IHandle<>)); from here.
Projects References.
Loader doesnt references any project.
Domain references Loader (there was another IInterface on Loader, implemented on Domain)
Test references both Loader and Domain.
So I moved my interfaces IHandle<> and IEvent<> from Domain to Loader and removed the reference from Domain to Loader, now, its works.
Its still not clear why the IsAssignableFrom failed when IHandle<> was in the domain. It only worked when my IHandle<> and ALoader are in the same assembly.
Well, in the end, that was my fault, I was loading de assembly in two load contexts.
I think it's about reference comparison, considering that Type is a reference type.
Use:
Type.IsAssignableFrom
Good luck.
That's because Type.FullName doesn't fully describe a type. You only get the namespace name and the type name. But .NET also includes the properties of the assembly in which the type resides into the type identity. Display name, [AssemblyVersion], Culture, PublicKeyToken and (indirectly) ProcessorArchitecture. You want to compare Type.AssemblyQualifiedName instead.
The diagnostic for failure like this is that the source code that contains the interface definition is getting compiled into multiple assemblies. Which in itself is almost always a mistake, you want to make sure that such an interface type only exists in one assembly. And any assembly that uses the interface type has a reference to it so they all use an identical type.
The rules for type identity were loosened somewhat in .NET 4, a type can be identical if it has the same GUID. The underlying core for the Embed Interop Types feature. But that only applies to [ComImport] types, I doubt that's the case here.

Load a .DLL file and access methods from class within?

I'm completely new to loading in libraries like this, but here's where I stand:
I have a homemade DLL file it's about as simple as it gets, the class itself and a method. In the home program that loads this library, I have:
Assembly testDLL = Assembly.LoadFile("C:\\dll\\test.dll");
From here, I'm kind of stuck. As far as I know, it's loading it correctly because it gives me errors when I change the name.
What do I do from here? How exactly do I load the class & methods within it?
Thanks.
Use Assembly.GetTypes() to get a collection of all the types, or Assembly.GetType(name) to get a particular type.
You can then create an instance of the type with a parameterless constructor using Activator.CreateInstance(type) or get the constructors using Type.GetConstructors and invoke them to create instances.
Likewise you can get methods with Type.GetMethods() etc.
Basically, once you've got a type there are loads of things you can do - look at the member list for more information. If you get stuck trying to perform a particular task (generics can be tricky) just ask a specific question an I'm sure we'll be able to help.
This is how you can get the classes if you know the type.
Assembly assembly = Assembly.LoadFrom("C:\\dll\\test.dll");
// Load the object
string fullTypeName = "MyNamespace.YourType";
YourType myType = assembly.CreateInstance(fullTypeName);
The full type name is important. Since you aren't adding the .dll you can't do a Using because it is not in your project.
If you want all I would just Jon Skeet answer.
If you want to dynamically load an assembly, and then invoke methods from classes therein, you need to perform some form of dynamic invoke.
Check here for basic advice on that.
The only bit missing is how to get the type itself, which can easily be retrieved wth code like this:
foreach (Type t in assemblyToScan.GetTypes())
{
if(condition)
//do stuff
}
And if you simply want to use the assembly statically (by having the assembly available at compile time), then the answer fom Launcy here on this page is the way to go.

Categories

Resources