I've copied code for an assembly that is used in a solution to create a similar assembly. The GUTS was different, but the shell stayed the same.
These assemblies are used in a project at a client that are add-on's and not part of our core code. Now that I'm finished the assembly does not want to load like it is supposed to.
The code that loads the assembly is
var assemblyName = ((XmlElement)xmlDoc.GetElementsByTagName("AssemblyName")[0]).InnerText;
var qualifiedClass = ((XmlElement)xmlDoc.GetElementsByTagName("QualifiedClass")[0]).InnerText;
IExternalAddOn addOn = (IExternalAddOn)Activator.CreateInstance(assemblyName, qualifiedClass).Unwrap();
var properties = new Dictionary<Type, object>();
properties[typeof(DevExpress.XtraBars.Ribbon.RibbonControl)] = mainForm.ribbon;
var form = addOn.ShowForm(properties);
if (form != null)
{
form.MdiParent = mainForm;
form.Text = pListRow.NAME;
form.Show();
I get the exception on the CreateInstance part.
The interesting thing is that when I use
Assembly ass = Assembly.LoadFrom(assemblyName); // this is test code
Type at = ass.GetType(qualifiedClass);
IExternalAddOn addOn = (IExternalAddOn)Activator.CreateInstance(at);
to load the assembly and get the type, and CreateInstance it works.
Why do you need the Unwrap? Is there a difference in the two different ways of loading? And WHY does the first one not work?
Thanks
J
As soon as Assembly.LoadFrom works, I guess you are passing a file name as an assemblyName parameter, which is wrong in case of Activator.CreateInstance(assemblyName, qualifiedClass).
According to Activator.CreateInstance documentation:
assemblyName can be either of the following:
The simple name of an assembly, without its path or file extension. For example, you would specify TypeExtensions for an assembly whose path and name are .\bin\TypeExtensions.dll.
The full name of a signed assembly, which consists of its simple name, version, culture, and public key token; for example, "TypeExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=181869f2f7435b51".
Related
I've inherited some code that I think is a little odd, and want to make sure if it's a potential issue or not.
First, it gets the name of a loaded assembly based on a type it contains:
private readonly string _assemblyName = Assembly.GetAssembly(typeof(IEntityBase)).FullName;
Then, on each request to process a named entity, it loads the assembly by name, even though the assembly is already loaded:
var assembly = Assembly.Load(_assemblyName);
var type = assembly.GetType(entityFullName);
var instance = Activator.CreateInstance(type);
I'm wondering if the Load calls are potentially causing issues - in this case, since the assembly is already loaded, is the call idempotent and returns the already-loaded assembly, or does it create a new assembly each time?
I had an issue that code Type.GetType(myTypeName) was returning null because assembly with that type is not current executing assembly.
The solution I found for this issue is next:
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
Type myType = assemblies.SelectMany(a => a.GetTypes())
.Single(t => t.FullName == myTypeName);
The problem is that the first run of this code causes exception "Sequence contains no matching element". When I call this part of code again - everything is ok and needed Type is loaded.
Can anyone explain such behavior? Why in the scope of first call no needed assembly/type found?
Problem you are facing is caused by design of GetAssemblies method of AppDomain class - according to documentation this method:
Gets the assemblies that have been loaded into the execution context of this application domain.
So when in your program type fails to be found first time - its assembly isn't obviously loaded by the application yet. And afterwards - when some functionality from assembly that contains type in question had been used - assembly is already loaded, and same code can already find missing type.
Please try loading assemblies directly. Instead of using:
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
you can use:
List<Assembly> assemblies = Assembly.GetEntryAssembly().GetReferencedAssemblies().Select(assembly => Assembly.LoadFrom(assembly.Name)).ToList();
It is possible that the type is in an assembly that has not yet been loaded. Later in your program it then is. If you look at the output window this should give you sn idea of when the assemblies are being loaded.
Another answer shows the best way for obtaining (at runtime) a Type defined in an Assembly that might not be loaded:
var T1 = Type.GetType("System.Web.Configuration.IAssemblyCache, " +
"System.Web, " +
"Version=4.0.0.0, " +
"Culture=neutral, " +
"PublicKeyToken=b03f5f7f11d50a3a");
As you can see, unfortunately that method requires you to supply the full AssemblyQualifiedName of the Type and will not work with any of the abbreviated forms of the assembly name that I tried. This somewhat defeats our main purposes here. If you knew that much detail about the assembly, it wouldn't be that much harder to just load it yourself:
var T2 = Assembly.Load("System.Web, " +
"Version=4.0.0.0, " +
"Culture=neutral, " +
"PublicKeyToken=b03f5f7f11d50a3a")
.GetType("System.Web.Configuration.IAssemblyCache");
Although these two examples look similar, they execute very different code paths; note for example, that the latter version calls an instance overload on Assembly.GetType versus the static call Type.GetType. Because of this, the second version is probably faster or more efficient. Either way, though, you seem to end up at the following CLR internal method, and with the second argument set to false, and this is why neither method goes to the trouble of searching for the required assembly on your behalf.
[System.Runtime.Remoting.RemotingServices]
private static RuntimeType LoadClrTypeWithPartialBindFallback(
String typeName,
bool partialFallback);
A tiny step forward from these inconveniences would be to instead call this CLR method yourself, but with the partialFallback parameter set to true. In this mode, the function will accept a truncated version of the AssemblyQualifiedName and will locate and load the relevant assembly, as necessary:
static Func<String, bool, TypeInfo> LoadClrTypeWithPartialBindFallback =
typeof(RemotingServices)
.GetMethod("LoadClrTypeWithPartialBindFallback", (BindingFlags)0x28)
.CreateDelegate(typeof(Func<String, bool, TypeInfo>))
as Func<String, bool, TypeInfo>;
// ...
var T3 = LoadClrTypeWithPartialBindFallback(
"System.Web.Configuration.IAssemblyCache, System.Web",
true); // <-- enables searching for the assembly
This works as shown, and also continues to support specifying the full AssemblyQualifiedName as in the earlier examples. This is a small improvement, but it's still not a fully unqualified namespace search, because you do still have to specify the short name of the assembly, even if it might be deduced from the namespace that appears in the type name itself.
And if you exlucde mscorlib assembly?? , you can try this:
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(asb=>!asb.FullName.StartsWith("mscorlib")).ToList();
Type myType = assemblies.SelectMany(a => a.GetTypes())
.Single(t => t.FullName == myTypeName);
BTW if you do know the FullName of the assembly containing the type (or an assembly containing a TypeForwardedToAttribute for the type) you can use Type.GetType. Specifically, Type.GetType(Assembly.CreateQualifiedName(assembly.FullName, myTypeName)) which will look something like:
Type.GetType("Some.Complete.Namespace.myTypeName, Some.Assembly.Name, Version=1.2.3.4, Culture=neutral, PublicKeyToken=ffffffffffffffff");
This will load the assembly if it is not already, assuming the framework can resolve the location of the assembly.
Here's my sample LinqPad query confirming the parenthetic TypeForwardedToAttribute remark:
var u = (from a in AppDomain.CurrentDomain.GetAssemblies()
let t = a.GetType("System.Lazy`2")
where t != null
select t).FirstOrDefault();
(u?.AssemblyQualifiedName).Dump();
u = Type.GetType("System.Lazy`2, System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
(u?.AssemblyQualifiedName).Dump();
Output:
null
System.Lazy`2, System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
I am having difficulty with using reflections dynamically eg. query a .exe file without requiring a reference to be added for every assembly which I wish to query against.
So for instance, the code below is the regular way to get a hold of a class to then be checked.
AssemblyName assembly_name = new AssemblyName( "Name" );
The issue is not adding the argument in to the code but the code requirng direct reference to the new assembly to check against.
Any suggestions are welcome.
It sounds like you're really just trying to load an assembly at execution time. Look at Assembly.Load and Assembly.ReflectionOnlyLoad.
Maybe you're looking for something like Cecil. It's a library (available on Windows and other platforms) that allows to query metadata without the need to resolve all references.
I'm not really sure what you mean by "query". If you want to know how to create an instance from an assembly using reflection, here is an example:
// From within the current assembly
public CartesianType CreateInstance(string fullyQualifiedClassName)
{
Assembly assembly = Assembly.GetExecutingAssembly();
Type target = assembly.GetType(fullyQualifiedClassName, true, true);
return (CartesianType)Activator.CreateInstance(target);
}
// From an external assembly already referenced in your project
public SomeClass CreateInstance(string fullyQualifiedClassName)
{
Assembly assembly = Assembly.GetAssembly(typeof(SomeClass));
Type target = assembly.GetType(fullyQualifiedClassName, true, true);
return (SomeClass)Activator.CreateInstance(target);
}
All other methods must use Load or LoadFile, LoadFrom etc.
I'am building a small plugin architecture (unfortunately MEF is not an options because it needs to run on .NET 2.0).
I want to be able to put dll's in a directory without recompiling the main project.
My main project is a winforms application which has some dialogs to pick an implementation of an interface the main program needs.
I have a method that searches a certain directory and gives a List of locations of the dll's I want to search for Types that implement the interface.
public List<Type> GetPluginTypes()
{
List<Type> types = new List<Type>();
foreach (string dll in this.Plugins)
{
Assembly assembly;
try
{
assembly = AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(dll));
}
catch
{
continue;
}
foreach (Type type in assembly.GetExportedTypes())
{
if (type.IsInterface || type.IsAbstract)
continue;
if (typeof(IMyInterface).IsAssignableFrom(type))
types.Add(type);
}
}
return types;
}
Using this method I show the user a list of implementations, one is chosen and it's AssemblyQualifiedName is saved to a settings file.
When I start the main application, I load the AQN from the settings and load all the plugins into the AppDomain by calling the above method.
string typeName = GetSetting("MyPlugin");
GetPluginTypes(); // just to load the plugins into the app domain
Type.GetType(typeName); // allways returns null.
Here is my problem: Type.GetType(typeName), always returns null.
I used Type.GetType(typeName, true), to enforce an exception, which I got:
Could not load file or assembly 'MyImpl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.":"MyImpl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"}
I am clueless. I've already loaded the assembly into the AppDomain, and still Type.GetType(string) can't find it when I specify the AQN.
This can be solved by registering the AppDomain.AssemblyResolve event for the AppDomain which will request the assembly and make the handler return the already loaded assembly.
Here is what the handler looks like in C#:
private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return
AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(Kandidaat => string.Equals(Kandidaat.GetName().FullName, args.Name));
}
a detailed explanation can be found here:
https://msdn.microsoft.com/en-us//library/ff527268(v=vs.110).aspx
appdomain assemblyresolve
Perhaps this is relevant. From the documentation for Type.GetType(String, Boolean):
If typeName includes only the name of
the Type, this method searches in the
calling object's assembly, then in the
mscorlib.dll assembly. If typeName is
fully qualified with the partial or
complete assembly name, this method
searches in the specified assembly.
You said that you try calling with type.AssemblyQualifiedName, but it failed. Did you check the qualified name to see if it was reasonable?
I want to do something like this:
Create a number of assemblies that implement IMyInterface.
In the web.config or app.config of my application, define which one of them to load (they may have different names)
When the application starts, load the assembly as marked in web.config and use its implementation of IMyInterface
what is the best way of doing this?
I am using Framework 3.5 at this time.
(p.s. I know I can just define a variable that contains the assembly name (e.g. key="My assembly", value="myassembly.dll" then dynamically load the assembly, just wanting to know if there is a more correct way of doing this)
Assemblies do not implement interfaces; types do.
For ASP.NET apps, I suggest putting every assembly in the /bin directory and use a web.config configuration option and Activator.CreateInstance to create the actual type:
var typeName = "MyNamespace.MyClass, MyAssembly"; // load from config file
IMyInterface instance =
(IMyInterface)Activator.CreateInstance(Type.GetType(typeName));
I do this as follows (i use base class, with abstract methods instead interfaces):
1 - List the classes implemented with my base class.
Assembly assembly = null;
AssemblyName assemblyName = new AssemblyName();
assemblyName.CodeBase = "FullPathToAssembly";
assembly = Assembly.Load(assemblyName);
Type[] arrayTipo;
arrayTipo = assembly.GetTypes();
var tipos =
from t in arrayTipo
where t.BaseType == typeof(DD.Util.BaseProcesso)
select t;
2 - Call the method of execution in an instance of the class:
Type tipo = engine.GetType("FullClassName");
BaseProcesso baseProcesso = (BaseProcesso)Activaor.CreateInstance(tipo, new object[] { "ConstructorParameter1", "ConstructorParameter1") });
// Event
baseProcesso.IndicarProgresso += new BaseProcesso.IndicarProgressoHandler(baseProcesso_IndicarProgresso);
new Thread(new ThreadStart(baseProcesso.Executar)).Start();
Works for me!