GetCustomAttributes from dynamically loaded Assembly - c#

I essentially want to do this:
AppDomain domain = AppDomain.CreateDomain("DataSpecDomain");
Assembly dataSpecAssembly = domain.Load(fullAssemblyFilePath);
object[] dataSpecAttrCollection = dataSpecAssembly.GetCustomAttributes(typeof(DataSpecificationAttribute), true);
Actually, previously I didn't have the AppDomain portion and was using Assembly.LoadFrom() which evidently is not so recommended these days in lieu of security. The problem with this code is the assemblies I am loading into this new domain are built with their own references to a third-party API. In fact, the third-party API references are only shell-representation of the actuals which lie in the third-party exe directory. When referencing these, Copy Local is set to False. The end result is that I get a FileLoadException. If I add the shell references to the Debug folder, it works. Is there a way I can get the attribute instances without having to deal with the loading assembly's dependencies? Or is there a way to resolve the loading assembly's dependencies without having to place the third-party API references in the same folder.
Just for reference, the third-party product is designed with a special directory used for auto-loading, meaning that the typical development shouldn't need their references in the same folder.

Related

What happens to the references when a code is Compied in .NET Framework?

I never understand , What actually happens to the the external DLLs when code is compiled by the compiler and converted to Intermediate code to run on CLR.
Does DLL code added to the Intermediate code and the references are not longer needed on the new machine or just the path of added DLLs are stored and we need those DLLs on our drive to run the program.
Generally (ie. there are some exceptions) the referenced assembly needs to be on the deployment system. All that is included in your assembly is the assembly name (this includes version and possibly signature).
Assembly binding (involving the GAC, config overrides etc.) is applied at runtime to get the right assembly. The rules depend on how the app domain was set up (eg. extra folders can be added, which is why ASP.NET web apps apply different rules).
There are various cases where the reference can be embedded (including referencing ActiveX when the right options are set and use of the assembly binding tool).

How to use a newer version of an assembly that Orchard uses

I want to implement a custom module in Orchard CMS. the problem is that I want to reference libraries used by various Orchard CMS modules, which are referencing older versions of these libraries
I want my plugin/module to be self sustaining - I don't want to require modifications be made to orchard.web's web.config file. i want users who are running orchard without a source enlistment to be able to utilize my modules.
How can I achieve this? This page describes my own module's web.config as more or less meaningless in terms of binding redirects... I tried just putting these references (the libraries in question are System.IdentityModel.Tokens.Jwt and Newtonsoft.Json) in the bin directory of my module and reference from there, but when I attempt to install the module I get an error:
Could not load file or assembly 'Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference.
I have read a little bit about Orchard's App_Data/dependencies folder and the orchard.web/bin folder but I am having trouble understanding how it all comes together. How can I allow my module to use a new version of a library while still allowing orchard modules to utilize an old (incompatible) version of that library?
Orchard does a good job of giving the illusion of a composed application with very few global, application-level entities that can't be overridden at the module level. Assembly loading is one of those places where it breaks down however. When the application is compiled and loaded, including when dynamic compilation enters the mix, the assemblies generated and depended upon by all the modules are gathered and copied into the global application's bin folder. Module's bin folders are actually just places assemblies are copied from, but the actual loading happens from the app's bin. So in the end, if you want to know what exactly is loaded into the app, you need to look in the top-level bin folder. If you find the wrong version in here, it's time to dig further and look at what versions modules have, and in what order they are contributing. If I'm not mistaken, that would depend on their dependencies on one another, the outermost dependencies getting their assemblies copied last, and thus winning. That means that you may have some power to tweak what makes it by tweaking module dependency order in their manifest.
The second thing that is global, because that's just the way ASP.NET is, is assembly binding configuration. There are ASP.NET settings that can be set at the directory level, but assembly binding needs to happen at the top, because the app is just one app domain in the end, and only one copy of each assembly will be loaded. Of course, that's a problem in any modular application, because modules had different expectations of versions of their dependencies at the times they were written. .NET deals with this with binding redirects, which basically are a way of instructing the runtime to just assume that everything will be fine if it loads that specific version. It will also tell assemblies that depend on those redirected dependencies a lie, in that it pretends that the version they expected was present, but will actually use the version specified in the redirect. It often works, but not always (breaking changes happen). And if that specific version that you redirect to isn't actually available, either in the GAC or in the app's bin, it will fail.
So in summary:
There's nothing modules can do at their level to unify the version of an assembly that the app will use, it needs to be a binding redirect set at the application level.
The assembly being redirected to must end up in the app's bin.
A module can affect, to a degree, what assemblies get deployed to the app's bin by changing its dependency order in its manifest (but that requires knowledge of those other modules).
Of course, ideally, all third-party modules, as well as Orchard core modules, would get updated to use the latest, but that's clearly rarely an option.

Hosted CLR: how to set base directory of default AppDomain?

If I host CLR in my C++ executable, is there any way to set base directory of default AppDomain to something other than location of the executable?
Here's why I need it. I have a rather complex application that loads .NET plugins using a plugin loader executable, PluginBox.exe. The plugins are located outside of the main application folder. We run one instance of PluginBox.exe per plugin.
Currently PluginBox.exe is written in C++. I want to convert it to a managed app. PluginBox uses unmanaged CLR hosting (ICLRMetaHost, ICLRRuntimeInfo, etc.), and locates plugin assemblies by implementing IHostAssemblyStore interface. There is only one AppDomain, and its base directory is the application directory. When searching for assemblies, CLR invokes the assembly store before looking at the application base directory. So, if the plugin and the main app contain an assembly with the same name, the assembly store can load the plugin-specific version.
As a first step towards making PluginBox.exe managed, I would like to get rid of the assembly store and replace it with an assembly resolver. The trouble is, unlike assembly store, assembly resolver is called after the application base directory has been considered. So, if the plugin and the main app contain an assembly with the same name, main app's assembly wins. This disrupts plugin execution.
I would like to switch base directory of the hosted CLR to where the plugin is located. So far, I found only two ways to do that, both of them unacceptabe: move PluginBox.exe to the plugin directory, or create a second AppDomain, which is problematic for a variety of internal reasons. This is a huge application with a lot of history, and any drastic moves are bound to cause problems.
Any thoughts and ideas are appreciated.
PS. Current CLR version is 4.0.

Can I get all assemblies referenced by a website

Basically, I want to print a list on a page inside my MVC site that shows all the assemblies that are used by the application and there version information.
I don't want to just include those in the bin directory, I want to use a more accurate way.
So examples would be
I want to know which dll's within the bin directory are being used. For each of these, I need to see what their dependent assemblies are (with their version information).
I want to know which dll's are referenced from GAC, etc.
I want as much information on this as possible, so information like what version the assembly is expecting, and what the application is picking up would be useful.
Are there any standard approaches to this?
Thanks,
Martin
Consider using BuildManager.GetReferencedAssemblies which:
Returns a list of assembly references that all page compilations must reference.
Example usage:
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
Note also an important difference between this method and AppDomain.GetAssemblies(). BuildManager.GetReferencedAssemblies returns:
assemblies specified in the assemblies element of the Web.config file, assemblies built from custom code in the App_Code directory, and assemblies in other top-level folders.
while AppDomain.GetAssemblies() returns only assemblies currently loaded.
Try this:
var assemblies = AppDomain.CurrentDomain.GetAssemblies();

Embedding assemblies inside another assembly

If you create a class library that uses things from other assemblies, is it possible to embed those other assemblies inside the class library as some kind of resource?
I.e. instead of having MyAssembly.dll, SomeAssembly1.dll and SomeAssembly2.dll sitting on the file system, those other two files get bundled in to MyAssembly.dll and are usable in its code.
I'm also a little confused about why .NET assemblies are .dll files. Didn't this format exist before .NET? Are all .NET assemblies DLLs, but not all DLLs are .NET assemblies? Why do they use the same file format and/or file extension?
ILMerge does merge assemblies, which is nice, but sometimes not quite what you want. For example, when the assembly in question is a strongly-named assembly, and you don't have the key for it, then you cannot do ILMerge without breaking that signature. Which means you have to deploy multiple assemblies.
As an alternative to ilmerge, you can embed one or more assemblies as resources into your exe or DLL. Then, at runtime, when the assemblies are being loaded, you can extract the embedded assembly programmatically, and load and run it. It sounds tricky but there's just a little bit of boilerplate code.
To do it, embed an assembly, just as you would embed any other resource (image, translation file, data, etc). Then, set up an AssemblyResolver that gets called at runtime. It should be set up in the static constructor of the startup class. The code is very simple.
static NameOfStartupClassHere()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolver);
}
static System.Reflection.Assembly Resolver(object sender, ResolveEventArgs args)
{
Assembly a1 = Assembly.GetExecutingAssembly();
Stream s = a1.GetManifestResourceStream(args.Name);
byte[] block = new byte[s.Length];
s.Read(block, 0, block.Length);
Assembly a2 = Assembly.Load(block);
return a2;
}
The Name property on the ResolveEventArgs parameter is the name of the assembly to be resolved. This name refers to the resource, not to the filename. If you embed the file named "MyAssembly.dll", and call the embedded resource "Foo", then the name you want here is "Foo". But that would be confusing, so I suggest using the filename of the assembly for the name of the resource. If you have embedded and named your assembly properly, you can just call GetManifestResourceStream() with the assembly name and load the assembly that way. Very simple.
This works with multiple assemblies, just as nicely as with a single embedded assembly.
In a real app you're gonna want better error handling in that routine - like what if there is no stream by the given name? What happens if the Read fails? etc. But that's left for you to do.
In the rest of the application code, you use types from the assembly as normal.
When you build the app, you need to add a reference to the assembly in question, as you would normally. If you use the command-line tools, use the /r option in csc.exe; if you use Visual Studio, you'll need to "Add Reference..." in the popup menu on the project.
At runtime, assembly version-checking and verification works as usual.
The only difference is in distribution. When you deploy or distribute your app, you need not distribute the DLL for the embedded (and referenced) assembly. Just deploy the main assembly; there's no need to distribute the other assemblies because they're embedded into the main DLL or EXE.
Take a look at ILMerge for merging assemblies.
I'm also a little confused about why .NET assemblies are .dll files. Didn't this format exist before .NET?
Yes.
Are all .NET assemblies DLLs,
Either DLLs or EXE normally - but can also be netmodule.
but not all DLLs are .NET assemblies?
Correct.
Why do they use the same file format and/or file extension?
Why should it be any different - it serves the same purpose!
You can embed an assembly (or any file, actually) as a resource (and then use the ResourceManager class to access them), but if you just want to combine assemblies, you're better off using a tool like ILMerge.
EXE and DLL files are Windows portable executables, which are generic enough to accomodate future types of code, including any .NET code (they can also run in DOS but only display a message saying that they're not supposed to run in DOS). They include instructions to fire up the .NET runtime if it isn't already running. It's also possible for a single assembly to span across multiple files, though this is hardly ever the case.
Note ILMerge doesn't work with embedded resources like XAML, so WPF apps etc will need to use Cheeso's method.
There's also the mkbundle utility offered by the Mono project
Why do they use the same file format and/or file extension?
Why should it be any different - it serves the same purpose!
My 2ยข bit of clarification here: DLL is Dynamic Link Library. Both the old style .dll (C-code) and .net style .dll are by definition "dynamic link" libraries. So .dll is a proper description for both.
With respect to Cheeso's answer of embedding the assemblies as resources and loading them dynamically using the Load(byte[]) overload using an AssemblyResolve event handler, you need to modify the resolver to check the AppDomain for an existing instance of the Assembly to load and return the existing assembly instance if it's already loaded.
Assemblies loaded using that overload do not have a context, which can cause the framework to try and reload the assembly multiple times. Without returning an already loaded instance, you can end up with multiple instances of the same assembly code and types that should be equal but won't be, because the framework considers them to be from two different assemblies.
At least one way that multiple AssemblyResolve events will be made for the same assembly loaded into the "No context" is when you have references to types it exposes from multiple assemblies loaded into your AppDomain, as code executes that needs those types resolved.
https://msdn.microsoft.com/en-us/library/dd153782%28v=vs.110%29.aspx
A couple of salient points from the link:
"Other assemblies cannot bind to assemblies that are loaded without context, unless you handle the AppDomain.AssemblyResolve event"
"Loading multiple assemblies with the same identity without context can cause type identity problems similar to those caused by loading assemblies with the same identity into multiple contexts. See Avoid Loading an Assembly into Multiple Contexts."
I would suggest you to try Costura.Fody. Just don't forget to Install-Package Fody before Costura.Fody (in order to get the newest Fody!)

Categories

Resources