I'm looking for an explanation for the following -
I have an assembly I'm loading using
Assembly assembly = Assembly.LoadFrom(filename);
I then loop on all the types in the assembly, and wish to try and find out if a type implements a particular interface and if so I want an instance of that type, I've tried several things which did not work, but when I fell back to the most basic (and probably inefficient) way, I realised there's something more fundamental I don't understand -
foreach (Type t in assembly.GetTypes())
{
foreach (Type i in t.GetInterfaces())
{
if (i.FullName == pluginInterfaceType.FullName)
{
object o = assembly.CreateInstance(t.ToString());
IInterface plugin = (IInterface)o;
That last line causes an InvalidCastException, despite the fact that the type created definitely implements that interface.
Further more - if I use Activator.CreateInstance instead of Assembly.CreateInstance (which I don't want to do), casting to the interface works just fine.
This is most probably because the interface you are casting to is not the same you find in the class.
Either because there is more the one interface with the same name, or because you loaded it more then once. For instance, because it is defined in the assembly you are dynamically loaded, and you try to cast it to the one that is statically bound.
The InvalidCastException should contain more details, like 'cannot cast x to y'.
My guess is that the assembly containing IInterface that you are loading is not exactly the same as the one your code was built against, maybe it's a local copy of a non strongly-named assembly.
Related
My issue is that I am getting different results to our older DLLs from history, when very little has changed and I don't under how the changes are relevant to the error shown.
This is part of a method, in our SecurityPluginServices.dll module, which essentially added plugins to a list so they can be utilised by the main program.
It gets all the DLLs in a set folder, then for each it runs through the code below.
private void AddPlugin(string FileName, LoggingUtilities.Source source)
{
//Create a new assembly from the plugin file we're adding..
Assembly pluginAssembly = Assembly.LoadFrom(FileName);
try
{
//Next we'll loop through all the Types found in the assembly
foreach (Type pluginType in pluginAssembly.GetTypes())
{
if (pluginType.IsPublic) //Only look at public types
{
if (!pluginType.IsAbstract) //Only look at non-abstract types
{
//Gets a type object of the interface we need the plugins to match
Type typeInterface = pluginType.GetInterface("SecurityInterface.ISecurityPlugin", true);
//Make sure the interface we want to use actually exists
if (typeInterface != null)
{
// Do work here
}
typeInterface = null; //Mr. Clean
}
}
}
pluginAssembly = null; //more cleanup
}
catch (ReflectionTypeLoadException ex1)
{
Console.WriteLine(ex1.Message);
}
catch (Exception ex2)
{
Console.WriteLine(ex2.Message);
}
}
The issue I am having is that everytime it gets to Type typeInterface = pluginType.GetInterface("SecurityInterface.ISecurityPlugin", true); it always returns null.
The plugins I need to load are for NTLM and LDAP and they have changed very little in many versions and only a couple of extra properties and methods have been added, nothing to do with interfaces that the implement.
I've opened up a copy of the newer plugin DLL and one of the older ones in ILDASM and they both seem to contain the same information regarding the SecurityInterface.ISecurityPlugin that the .GetInterface method is seeking.
Newer LdapSecurity
Older LdapSecurity
Suggestion:
Type typeInterface = pluginType.GetInterface("SecurityInterface.ISecurityPlugin", true);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Change this to a fully-qualified type name, i.e. one that includes the assembly containing your interface type.
(If you're now saying that you've got two different types by the same name, in two different assemblies, and therefore cannot include one definite assembly name, this may well be the likely cause of your issue.)
Explanation:
My other answer led me to a suspicion of what could cause your issue. Generally speaking, typeof(T) would force you to add an assembly reference to your project for the assembly that contains T. Otherwise, compilation will fail. On the other hand, textual mentions of type names such as Type.GetType("T") do not enforce a compile-time assembly reference... but they still have to be resolved to a type instance at run-time. So if you omit the assembly name, you need to keep in mind how .NET will map T to a specific assembly.
Back to your issue. First, let's note that you are not using a fully-qualified type name:
Type typeInterface = pluginType.GetInterface("SecurityInterface.ISecurityPlugin", true);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You specify only the namespace and the type name, but not the assembly containing your interface type. Could it be that the interface type is defined more than once, i.e. in different assemblies, and your mention of that type name is resolved to type in the wrong assembly?
Let's briefly look at the MSDN documentation for Type.GetType(string) method, which describes the string parameter as follows:
"The assembly-qualified name of the type to get. […] If the type is in the currently executing assembly or in Mscorlib.dll, it is sufficient to supply the type name qualified by its namespace."
What if the same applies to the Type.GetInterface(string, bool) method which you're using? And what if your currently executing assembly does contain a type of that name? Then that is the type that pluginType will be checked against... but it might be a different ISecurityPlugin interface type (albeit equal in name) than the one your plug-in class actually implements.
Update: Please see my other answer first, which is likely more relevant.
If you want to check whether pluginType implements the ISecurityPlugin interface, you could instead perform this check:
typeof(SecurityInterface.ISecurityPlugin).IsAssignableFrom(pluginType)
If you don't reference the assembly containing ISecurityPlugin, you can replace the first part by Type.GetType("SecurityInterface.ISecurityPlugin").
I wanted to know what are the benefits, if any, to calling either of these methods, and why?
Are they functionally equivalent, or should I always be choosing one over the other?
AFAIK they're functionally equivalent, however Assembly.GetAssembly(Type) is not available in the core-clr (Silverlight and the like)
You would get different errors based on whether or not your Type is null. Type.Assembly will throw a NullReferenceException (naturally) whereas Assembly.GetAssembly(Type) would throw an ArgumentNullException. Based on your code you might prefer one or the other.
EDIT: Looking at the source, for Assembly.GetAssembly:
public static Assembly GetAssembly(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
Contract.EndContractBlock();
Module m = type.Module;
if (m == null)
return null;
else
return m.Assembly;
}
Whereas System.Type.Assembly:
public abstract Assembly Assembly {
get;
}
If I recall correctly, the System.Type for runtime types are their own subclasses (or something like that) So I guess each one maybe overrides and simply directly returns an assembly reference. Beyond that, I don't think there's any significant difference for you to worry about with your code beyond null handling which is up to you. (Or if you're running the Core-CLR in which case you don't have Assembly.GetAssembly anyway so you must use Type.Assembly)
From the documentation for Type.Assembly
If the current Type object represents a constructed generic type, this
property returns the assembly that contains the generic type
definition.
For example, suppose you create an assembly named
MyGenerics.dll that contains the generic type definition
MyGenericStack<T>. If you create an instance of
MyGenericStack<int> in
another assembly, the Assembly property for the constructed type
returns an Assembly object that represents MyGenerics.dll
From the documentation for Assembly.GetAssembly:
In order to call this method, you must have a Type object, which means
that the assembly in which the class is defined must already be
loaded.
At the moment, I have this code :
var shellViewLibrary = Assembly.LoadFrom(Path.Combine(_DllsPath, _DllShellView));
IEnumerable<Type> types = shellViewLibrary.GetTypes();
foreach (Type type in types)
{
var typeIShellViewInterface = type.GetInterface(_NamespaceIShellView, false);
if (typeIShellViewInterface != null)
{
//here
}
}
The thing is that where I got //here I want to use Activator.CreateInstance to create an object whose type is type in a specific folder (that is outside the build folder)
I tried about 20 different things, most of them with this : http://msdn.microsoft.com/en-us/library/d133hta4.aspx
but none works...
The typical thing I tried is :
object MyObj = Activator.CreateInstance(shellViewLibrary.FullName, type.FullName);
or
object MyObj = Activator.CreateInstance(Path.Combine(_DllsPath, _DllShellView), type.FullName);
I always got different exception, the most common being :
XamlParseException
I feel like that I'm not using Activator.CreateInstance in the right way with 2 parameters. What should I do ?
This is an example of "Dynamically Loading a .dll from a Specific Folder" at runtime.
// Check if user has access to requested .dll.
string strDllPath = Path.GetFullPath(strSomePath);
if (File.Exists(strDllPath))
{
// Execute the method from the requested .dll using reflection (System.Reflection).
Assembly DLL = Assembly.LoadFrom(strDllPath);
Type classType = DLL.GetType(String.Format("{0}.{1}", strNmSpaceNm, strClassNm));
if (classType != null)
{
// Create class instance.
classInst = Activator.CreateInstance(classType);
// Invoke required method.
MethodInfo methodInfo = classType.GetMethod(strMethodName);
if (methodInfo != null)
{
object result = null;
result = methodInfo.Invoke(classInst, new object[] { dllParams });
return result.ToString();
}
}
}
This took me a while to work out so I hope it is of some use...
Once you call this line
var shellViewLibrary = Assembly.LoadFrom(Path.Combine(_DllsPath, _DllShellView));
The assembly has been loaded in to memory. So long as you specify types correctly from this then you will be able to use Activator.CreateInstance to create the types. ie: It is not necessary to further specify where the type is.
Regarding Activator, from MSDN the CreateInstance method can accept a System.Type. I would just use this method inside your if-statement:
Activator.CreateInstance(Type type);
What I would try to do to debug this is first create the type and then pass it in to CreateInstance. You may find that the Type creation itself is failing (due to unresolved assembly) or the instantiation of that type (due to exception in the constructor). At first glance your code here appears to be correct:
foreach (Type type in types)
{
var typeIShellViewInterface = type.GetInterface(_NamespaceIShellView, false);
if (typeIShellViewInterface != null)
{
try
{
// I assume you are calling this line at the point marked 'here'.
// To debug the creation wrap in a try-catch and view the inner exceptions
var result = Activator.CreateInstance(type);
}
catch(Exception caught)
{
// When you hit this line, look at caught inner exceptions
// I suspect you have a broken Xaml file inside WPF usercontrol
// or Xaml resource dictionary used by type
Debugger.Break();
}
}
}
In your question you specify that you are getting a XamlParseException. It sounds to me like the type in question is a UserControl (or otherwise refers to a WPF Xaml resource file) and there is an error in that Xaml file, i.e. nothing to do with your usage of Assembly.Load or Activator.CreateInstance.
Could you try posting the inner exception(s) to get a better idea on what the problem is?
Check out MEF and Prism. MEF is a dependency injection library that helps with this. You can load all of your dependencies from a specific folder and make dynamically load them.
Prism is a pattern that leverages dependency injection and works great with MEF to load libraries dynamically
if you're trying to create a type with the Activator class from a DLL that's outside your application, you need to first load this DLL inside your application domain.
The easiest and quickest way to do this is by using the Assembly.LoadFile method.
more information about that method can be found here : http://msdn.microsoft.com/en-us/library/system.reflection.assembly.loadfile.aspx
When the assembly is properly loaded you can use the Activator to create an instance from the type inside the DLL.
We use this mechanic to run custom code in our application.
I'm not sure what you mean by this:
create an object whose type is type in a specific folder (that is outside the build folder)
A type only exists in an assembly. Path is entirely irrelevant to types in .NET
I assume that by "path" you really mean "namespace", meaning that you don't know in which namespace the type exists. You have already loaded the assembly in your code. Inspect the assembly using reflection to find the type you're looking for. Then pass the Type object that represents the type you're looking for to Activator.CreateInstance
I've been trying to make a module, command-line-like application. Each module can hold multiple commands. For example, "Module A" can have commands like "exit" and "echo". I'm using the following code to load and initialize my modules...
foreach (string Filename in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Modules"), "*.dll"))
{
Assembly Asm = Assembly.LoadFrom(Filename);
foreach (Type AsmType in Asm.GetTypes())
{
if (AsmType.GetInterface("ICommandModule") != null)
{
object CommandObject = Activator.CreateInstance(AsmType);
ICommandModule CommandModule;
if (CommandObject is ICommandModule)
{
CommandModule = (ICommandModule)CommandObject;
}
else
{
throw new Exception("CommandObject is not a valid ICommandModule.");
}
...
I know for a fact that the module which it is loading (Core.dll) is perfectly in compliance to the ICommandModule interface. When I make sure with if (CommandObject is ICommandModule) it throws the exception. When I removed the if statement alltogether, it told me it was unable to cast CommandObject to ICommandModule.
Your application may be finding the assembly with ICommandModule in two different directories. The assemblies may be exactly the same, but because they are in different directories the type system sees them as different.
Take Jakub's suggestion and make this change:
ICommandModule CommandModule = Activator.CreateInstance(AsmType) as ICommandModule;
You may have run into a situation where Activator.CreateInstance(AsmType) is returning null. If so, it may be that it found the declaration for the Interface itself and there is no instance creation possible for that particular AsmType. So, when debugging, make sure you know exactly which type you're trying to instantiate.
You can use Reflection to check whether an object implements a specific interface.
Here's an example:
bool isCommandModule = typeof(ICommandModule).IsAssignableFrom(commandObject);
Related resources:
Type.IsAssignableFrom Method
Try:
CommandModule = CommandObject as ICommandModule;
I am trying to simply load a dll written in C# at run time and create an instance of a class in that dll.
Assembly a = Assembly.LoadFrom(#"C:\Development\DaDll.dll");
Type type = a.GetType("FileReleaseHandler", true);
TestInterface.INeeedHelp handler = Activator.CreateInstance(type) as TestInterface.INeeedHelp;
No errors are thrown, and if I step through the code I can walk though the FileReleaseHandler Class as it executes the constructor but the value of handler is always null.
What am I missing here? or even is there a better way I should be going about this?
Where is TestInterface.INeedHelp defined? One common problem is if you've got the same interface in multiple assemblies. If both the caller and the dynamically loaded assembly reference the same interface in the same assembly, it should be okay.
One subtlety is that if the assembly is in a different directory to the calling assembly, it may end up loading a different copy of the same assembly, which can be very irritating :(
Try setting the result of Activator.CreateInstance to an object directly, and not casting.
It's possible FileReleaseHandler does not implement TestInterface.INeeedHelp, in which case, this will be set to null via the "as TestInterface.INeeedHelp".
Check the assemblies that are loaded into the application domain. Are there two assemblies with the TestInterface.INeedHelp interface in it? My suspicion is that the LoadFrom is binding this created object to a different TestInterface.INeedHelp than the one to which you are trying to cast. Try doing a normal downcast rather than a so-called "safe" cast and see what error you get.
The problem is that the LoadFrom method loads the dll into "LoadFrom" context which is different from the default context. The rules of type resolution for the types in this context differ from the ones you are used to. It is possible that the INeedHelp interface as defined in your main module from the standpoint of the runtime is different from the interface with the same name as coming from the LoadFrom context. This will cause the cast (as TestINterface) return null.
To see if this is the case try to assign the result to an object.
I would do something like :
Assembly a = Assembly.LoadFrom(#"C:\Development\DaDll.dll");
object obj = a.CreateInstance("fullClassName",other params) as IMyType
//(if it implements an interface you referenced)