Searching type in assemblies - c#

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

Related

Dotnet Core, is Assembly.Load(string) idempotent?

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?

dnLib-Generated Assembly - TypeLoadException Thrown at Runtime

I am using dnLib to generate MSIL assemblies dynamically from a custom language I'm writing, named CSASM:
string absolute = Path.Combine(Directory.GetCurrentDirectory(), forceOutput ?? $"{asmName}.exe");
ModuleDefUser mod = new ModuleDefUser(asmName, Guid.NewGuid(), new AssemblyRefUser(new AssemblyNameInfo(typeof(int).Assembly.GetName().FullName))){
Kind = ModuleKind.Console,
RuntimeVersion = "v4.0.30319" //Same runtime version as "CSASM.Core.dll"
};
var asm = new AssemblyDefUser($"CSASM_program_{asmName}", new Version(version));
asm.Modules.Add(mod);
// Adding attribute code omitted for brevity
//Adds types to the module and constructs methods and method bodies for those types based on the CSASM source file in question
Construct(mod, source);
mod.Write(absolute);
The generation of the executable is working as intended.
However, when trying to run this executable, the TypeLoadException below is thrown:
System.TypeLoadException: Could not load type 'CSASM.Core.IntPrimitive' from assembly
'CSASM_program_Example, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' due to
value type mismatch.
at Example.Program.csasm_main()
CSASM_program_Example being the name of assembly for the generated executable, Example.exe.
The type IntPrimitive is actually found in the CSASM.Core.dll assembly, which is also in the same folder as the generated executable.
Due to the extreme lack of documentation surrounding dnLib, I'm essentially stumbling around in the dark here.
In short, is there a reason why the type is trying to be loaded from the wrong assembly?
If so, is there a way that I can remedy this?
Viewing the assembly in dnSpy shows the TypeRefs and MemberRefs referencing correct assemblies, which makes this predicament even more frustrating.
After a very thorough examination of a dnLib DLL, the problem was due to me using Importer.ImportDeclaringType(Type).ToTypeDefOrRef(), which caused value-type TypeSigs to be registered as class-type TypeSigs instead.
Even though the docs say to use Importer.ImportDeclaringType(Type) over Importer.ImportAsTypeSig(Type) for method and field declarations, you really shouldn't be using it.

Activator.CreateInstance throws Exception "The system cannot find the file specified."

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".

How can I dynamically reference an assembly that looks for another assembly?

Apologies for the dodgy question - happy to rephrase if someone has a better suggestion.
I'm trying to create an object by dynamically invoking an assembly belonging to another application.
The following PowerShell code is working nicely for me:
[Reflection.Assembly]::LoadFrom("C:\Program Files\Vendor\Product\ProductAPI.dll")
$bobject = new-object ProductAPI.BasicObject
$bobject.AddName("Some Name")
I'm struggling to do the same thing in C#. Based on other posts on StackOverflow I currently have this:
System.Reflection.Assembly myDllAssembly =
System.Reflection.Assembly.LoadFile("C:\\Program Files\\Vendor\\Product\\ProductAPI.dll");
System.Type BasicObjectType = myDllAssembly.GetType("ProductAPI.BasicObject");
var basicObjectInstance = Activator.CreateInstance(BasicObjectType);
The final line results in a TargetInvocationException.
{"Could not load file or assembly 'AnotherObject, Version=1.2.345.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."
It appears that the BasicObject constructor is trying to invoke AnotherObject (from AnotherObject.dll in the same folder) but can't find it.
Any tips on how to get around this?
If it can't find a dependent assembly in the usual places, you'll need to manually specify how to find them.
The two easiest ways I'm aware of for doing this:
manually load the dependent assemblies in advance with
Assembly.Load.
handle the AssemblyResolve event for the domain which is loading the
assembly with additional assembly dependencies.
Both essentially require you to know the dependencies for the assembly you're trying to load in advance but I don't think that's such a big ask.
If you go with the first option, it would also be worthwhile looking into the difference between a full Load and a reflection-only Load.
If you would rather go with 2 (which I'd recommend), you can try something like this which has the added benefit of working with nested dependency chains (eg MyLib.dll references LocalStorage.dll references Raven.Client.dll references NewtonSoft.Json.dll) and will additionally give you information about what dependencies it can't find:
AppDomain.CurrentDomain.AssemblyResolve += (sender,args) => {
// Change this to wherever the additional dependencies are located
var dllPath = #"C:\Program Files\Vendor\Product\lib";
var assemblyPath = Path.Combine(dllPath,args.Name.Split(',').First() + ".dll");
if(!File.Exists(assemblyPath))
throw new ReflectionTypeLoadException(new[] {args.GetType()},
new[] {new FileNotFoundException(assemblyPath) });
return Assembly.LoadFrom(assemblyPath);
};

Using reflection to dynamically query an assembly

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.

Categories