Indirectly accessing an assembly through reflection - c#

OK, I have the following bit of code:
Assembly assembly = Assembly.LoadFile("W:\\AssemblyFoo.dll");
foreach (Type type in assembly.GetExportedTypes())
{
foreach (object attribute in type.GetCustomAttributes(false)) //Exceptio on that line
{
string attributeString = attribute.ToString();
}
}
The code throws the following exception: Could not load file or assembly 'AssemblyBar, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
The problem is that one of the attribute is in AssemblyBar, which is referenced by AssemblyFoo, but not directly by the current's project (ProjectBaz) assembly. I'd rather avoid referencing AssemblyBar directly in ProjectBaz, since AssemblyFoo is selected by the user. What's the correct way to go about this ? I'm pretty sure I'm missing something easy.
I know it's possible since Reflector does it.

Mono's Cecil
You may circumvent the problem by not loading the assemblies through the built-in reflection
facilities, using a tool like Mono.Cecil instead. I've had good experiences in applying it for analysis tasks.
From the Cecil site:
with Cecil, you can load existing
managed assemblies, browse all the
contained types, modify them on the
fly and save back to the disk the
modified assembly.
CCI Metadata
As an alternative to Cecil, you might consider CCI Metadata by Microsoft Research. I have not used that tool, so I can not comment on how it stacks up to Cecil.
Hope this helps.

You could include AssemblyBar in you bin directory so that the clr can load it.

MemberInfo.GetCustomAttributesData() was introduced in .NET 4 so that you could examine custom attributes without having to instantiate them.
UPDATE: Taking closer look at MemberInfo.GetCustomAttributesData(), although it doesn't instantiate the attributes, it still need to load the assembly. My apologies.
Another possible route to investigate would be Mono Cecil. You can load the Mono.Cecil assembly and use it with the .NET Framework, not just Mono. It is much more powerful than System.Reflection and Reflection.Emit. According to its homepage:
Cecil does not need to load the assembly or have compatible assemblies to introspect the images.
This sounds exactly like what you're trying to do.

Related

Mono.Cecil - Wrong mscorlib version

I am having issues trying to get Mono.Cecil to use the correct "mscorlib" version when using a TypeReference. I am adding a new variable to a Method and doing something along the following. However, it is adding a reference to the 4.0 mscorlib instead of the 2.0 mscorlib. I am having to run this application using the .NET Framework 4.5 so I am not able to use a lower version unless I re-write other people's code that I don't have access to.
TypeReference typeReference = CSharpModule.Import(Type.GetType("System.Nullable`1[[System.Boolean, mscorlib, Version=2.0.0.0]], mscorlib, Version=2.0.0.0"));
methodBody.Variables.Add(new VariableDefinition(typeReference));
I have tried to search around for an answer on how to get the correct version and I apparently have to use the Cecil Type System, but I am not able to find any examples of how to do this.
You should attempt to load the type by using TypeReference constructor,
https://github.com/lextm/obfuscar/blob/master/Obfuscar/Obfuscator.cs#L1204
My project Obfuscar shows an example of how to do so. You need to pay much attention to the library.MainModule.TypeSystem.Corlib part, so that the type is loaded from the proper mscorlib assembly.
So I was able to fix this by doing the following. Create a TypeReference using what is available to the MainModule.
assemblyDefinition.MainModule.GetType("System.Nullable`1[[System.Boolean]]", true)

Could not load file or assembly Not exact version

I'm using MigratorDotNet to do migrations. I give it a string to my Assembly My_Assembly which is built on Sharp Arch v2. Sharp Arch v2 is built on Fluent Nhibernate 1.3.0.727.
My project is using 1.3.0.737
I'm getting the following error when MigratorDotNet calls Assembly.GetExportedTypes:
System.IO.FileNotFoundException: Could not load file or assembly 'FluentNHibernate, Version=1.3.0.727, Culture=neutral, PublicKeyToken=8aa435e3cb308880' or one of its dependencies. The system cannot find the file specified.
I'm familiar with BindingRedirects but would prefer to not change source code for MigratorDotNet (it does't pull in external config).
Is there a way I can force the new version and prevent the FileNotfoundException?
P.S. I know there a lot of similar-phrased questions but those are usually resolved by binding redirects, ensuring Copy Local = true, adding assemblies to the GAC, etc. I think this question is more generic.
I hate to say it, but I think you are going to have to live with binding redirects. I've not found a way to do what you are asking yet myself, but would love to see if there is one.

How do I create and use a .NET metadata-only 'Reference Assembly'?

Since version 3.0, .NET installs a bunch of different 'reference assemblies' under C:\Program Files\Reference Assemblies\Microsoft...., to support different profiles (say .NET 3.5 client profile, Silverlight profile). Each of these is a proper .NET assembly that contains only metadata - no IL code - and each assembly is marked with the ReferenceAssemblyAttribute. The metadata is restricted to those types and member available under the applicable profile - that's how intellisense shows a restricted set of types and members. The reference assemblies are not used at runtime.
I learnt a bit about it from this blog post.
I'd like to create and use such a reference assembly for my library.
How do I create a metadata-only assembly - is there some compiler flag or ildasm post-processor?
Are there attributes that control which types are exported to different 'profiles'?
How does the reference assembly resolution at runtime - if I had the reference assembly present in my application directory instead of the 'real' assembly, and not in the GAC at all, would probing continue and my AssemblyResolve event fire so that I can supply the actual assembly at runtime?
Any ideas or pointers to where I could learn more about this would be greatly appreciated.
Update: Looking around a bit, I see the .NET 3.0 'reference assemblies' do seem to have some code, and the Reference Assembly attribute was only added in .NET 4.0. So the behaviour might have changed a bit with the new runtime.
Why? For my Excel-DNA ( http://exceldna.codeplex.com ) add-in library, I create single-file .xll add-in by packing the referenced assemblies into the .xll file as resources. The packed assemblies include the user's add-in code, as well as the Excel-DNA managed library (which might be referenced by the user's assembly).
It sounds rather complicated, but works wonderfully well most of the time - the add-in is a single small file, so no installation of distribution issues. I run into (not unexpected) problems because of different versions - if there is an old version of the Excel-DNA managed library as a file, the runtime will load that instead of the packed one (I never get a chance to interfere with the loading).
I hope to make a reference assembly for my Excel-DNA managed part that users can point to when compiling their add-ins. But if they mistakenly have a version of this assembly at runtime, the runtime should fail to load it, and give me a chance to load the real assembly from resources.
To create a reference assembly, you would add this line to your AssemblyInfo.cs file:
[assembly: ReferenceAssembly]
To load others, you can reference them as usual from your VisualStudio project references, or dynamically at runtime using:
Assembly.ReflectionOnlyLoad()
or
Assembly.ReflectionOnlyLoadFrom()
If you have added a reference to a metadata/reference assembly using VisualStudio, then intellisense and building your project will work just fine, however if you try to execute your application against one, you will get an error:
System.BadImageFormatException: Cannot load a reference assembly for execution.
So the expectation is that at runtime you would substitute in a real assembly that has the same metadata signature.
If you have loaded an assembly dynamically with Assembly.ReflectionOnlyLoad() then you can only do all the reflection operations against it (read the types, methods, properties, attributes, etc, but can not dynamically invoke any of them).
I am curious as to what your use case is for creating a metadata-only assembly. I've never had to do that before, and would love to know if you have found some interesting use for them...
If you are still interested in this possibility, I've made a fork of the il-repack project based on Mono.Cecil which accepts a "/meta" command line argument to generate a metadata only assembly for the public and protected types.
https://github.com/KarimLUCCIN/il-repack/tree/xna
(I tried it on the full XNA Framework and its working afaik ...)
Yes, this is new for .NET 4.0. I'm fairly sure this was done to avoid the nasty versioning problems in the .NET 2.0 service packs. Best example is the WaitHandle.WaitOne(int) overload, added and documented in SP2. A popular overload because it avoids having to guess at the proper value for *exitContext" in the WaitOne(int, bool) overload. Problem is, the program bombs when it is run on a version of 2.0 that's older than SP2. Not a happy diagnostic either. Isolating the reference assemblies ensures that this can't happen again.
I think those reference assemblies were created by starting from a copy of the compiled assemblies (like it was done in previous versions) and running them through a tool that strips the IL from the assembly. That tool is however not available to us, nothing in the bin/netfx 4.0 tools Windows 7.1 SDK subdirectory that could do this. Not exactly a tool that gets used often so it is probably not production quality :)
You might have luck with the Cecil Library (from Mono); I think the implementation allows ILMerge functionality, it might just as well write metadata only assemblies.
I have scanned the code base (documentation is sparse), but haven't found any obvious clues yet...
YYMV

Using Global Assembly Cache (GAC) - in the way it was designed to

Is the following solution the only possibility to use libraries from GAC in code?
Assembly lib = Assembly.Load("MyLibrary, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31f5625abd53197f");
Console.WriteLine(lib.GetType("MyClass").GetMethod("Start").Invoke(obj, null));
I am a little bit confused - I've read quite much about GAC and I know how to sign an assembly, intall and uninstall assembly in GAC, but have no idea how to use it, and how does it help programmers (except that it stores different versions of same library safely). I wish I could normally create classes, not beeing forced to invoke methods presented above.
I don't want any work-arounds such as: "change windows registry" because I don't think GAC was designed for such manipulations. I want a simple answer: what the GAC is for, does runtime environment use it somehow?
What is the point of using GAC, when the code gets really ugly and difficult to manage? Or maybe I am missing something? Maybe I should manually copy an assembly into my local folder? But I heard it's also hard to do.
The GAC is there to help you have multiple versions of your (that's what the public key is for - preventing name clashes) assemblies installed side-by-side, and available to the whole machine, not just your application directory.
So yes, accessing assemblies from the GAC using the version number is pretty much exactly the way the GAC was designed for. ;-)
btw: You should not dynamically load stuff when you don't have to.
IOW: If you already know the class name and assembly version, you could just reference that assembly and skip the rather dramatic performance penalty of not only loading an assembly dynamically, but also invoking methods through reflection. (Instead of e.g. reusing them via interfaces or delegates)
If your assembly has been ngen'ed or GAC'd, then the application will use that automatically, if everything (hashes, etc) matches.
If you reference the assembly in your project, you can use it like a normally referenced DLL that is not in the GAC.

Reflection.Net: how to load dependencies?

I try to add an addons system to my Windows.Net application using Reflection; but it fails when there is addon with dependencie.
Addon class have to implement an interface 'IAddon' and to have an empty constructor.
Main program load the addon using Reflection:
Assembly assembly = Assembly.LoadFile(#"C:\Temp\TestAddon\Addon.dll");
Type t = assembly.GetType("Test.MyAddon");
ConstructorInfo ctor = t.GetConstructor(new Type[] { });
IAddon addon= (IAddon) ctor.Invoke(new object[] { });
addon.StartAddon();
It works great when addon do not use dependencie.
But if my addon reference and use an another DLL (C:\Temp\TestAddon\MyTools.dll) that is saved near the addon in disk, it fails:
System.IO.FileNotFoundException: Could not load file or assembly 'MyTools.dll' or one of its dependencies.
I do not wants to copy the addons DLL near my executable, how can i do to tell .Net runtime to search in "C:\Temp\TestAddon\" for any dependency?
Note that adding
Assembly assembly = Assembly.LoadFile(#"C:\Temp\TestAddon\MyTools.dll");
do not change anything.
If MyTools.dll is located in the same directory as Addon.dll, all you need to do is call Assembly.LoadFrom instead of Assembly.LoadFile to make your code work. Otherwise, handling the AppDomain.AssemblyResolve event is the way to go.
Have you looked into using an Inversion Of Control container? I use Castle Windsor with an external Boo file that lets me easily extend the applcation without having to recompile or worry about supplying dependencies
You can use reflection to access the private Assembly._GetReferencedAssemblies().
Although, the method could change in a future version of the .NET framework, it doesn't seem likely—ASP.NET heavily depends on it, though it's possible they could move it from mscorlib to System.Web which is the only assembly that I know of from where the method is referred to.
Assembly.LoadFrom works well until I try to use a webService in my addon, I had had a "Unable to cast object of type 'X' to type 'X'" exception.
It's ugly, but i will use Assembly.LoadFile with the AppDomain.AssemblyResolve.
Thanks guys.
Couple of options:
You can attach to AppDomain.AssemblyResolve to help the CLR resolve the assembly.
You could look into isolating add-ins into their own AppDomain (see System.AddIn namespace and this website).

Categories

Resources