Our code uses a "Plugin" model, that remotely loads dll's conforming to a predefined structure IPluginModel that is defined in the main program. The main program itself has several localized forms where the text and placement of the labels in the UI are all changed based on the different localization Thread.CurrentThread.CurrentUICulture.
One thing that we noticed is that any forms or reports from the remotely loaded dll's will never be properly localized. It does not seem to matter where the localization dll's containing the different resources for the plugin are located, either next to the main form, next to the plugin dll, or anywhere else. How would one cause the Assembly to correctly locate its localized resources when the assembly itself is loaded during RunTime of the main program?
I do have code that rather resembles the method used Here, but dont want to have to manually implement the resx against the form itself if at all possible. The code I have is located directly inside the plugin itself and is called whenever the CurrentUICulture is NOT "en-US".
Ideally speaking, what Im looking for is a way to load the pluginName.resources.dll which is directly associated with the plugin I just loaded. I do see the different folders in my main projects bin folder, the es folder contains a main.resources.dll, but simply placing the plugin dll's in that folder did not seem to work last time I tried it, although in theory that could have changed...or I may have done it wrong at the time...
Loading the assembly [code snippet]
private void LoadPlugin(string filePath)
{
bool isValidPlugin = false;
Assembly asm = null;
try
{
asm = Assembly.LoadFrom(Path.Combine(Environment.CurrentDirectory, filePath));
//Do some type checking to make certain this is in fact one of OUR plugins
var p = (IFTLPlugin)Activator.CreateInstance(types[x]);
_plugins.Add(p.Prefix, p);
}
}
Edit: More thoughts on the subject
Is there some way to intercept the loading structure that attempts to resolve the satellite assemblies, an event that I can implement or a function I can override, where I can manually point the code to the correct resource assembly?. I found some things about assembly resolution, but that was for direct loading, not for satellite resources.
As you know perfectly well already, the runtime loads localized resources using a convention, by looking for satellite assemblies (e.g. fr/pluginName.resources.dll).
I think you may be right to think that this issue may be due to a failure to find those satellite assemblies . You could try confirming this using the .NET Framework diagnostics tool fuslogvw.exe. You can also try to deploy your satellite assemblies to the Global Assembly Cache (GAC) as a quick fix (see Installing a Satellite Assembly in the Global Assembly Cache on this page).
If that works, and to help figure out other suitable locations (other than the GAC), the locations inspected by the runtime to look for satellite assemblies are documented as the fallback process here:
http://msdn.microsoft.com/en-us/library/sb6a8618%28v=vs.71%29.aspx
The .NET system, when loading resource assemblies, uses the Thread.CurrentThread.CurrentUICulture, as you pointed out in your question background.
The problem is that each new thread defaults to CultureInfo.InstalledCulture until it is told otherwise.
Have your main code pass the desired culture to the library in your initialization call.
Then have the library set its Thread.CurrentThread.CurrentUICulture (and .CurrentCulture if appropriate) before you load any resources. That would be before any call to InitializeComponent() or TryFindResource(), for example.
This gets the library's thread's cultures aligned with the main program's thread's cultures, regardless of what the OS's ideas about it are.
Related
I'm writing a plug-in for a 3rd party application (.NET). This application lets me choose the plug-in (as a .dll library file) to load. However, if I have two versions of the same library---they have the same name but are in different directories---and try to load one after the other, it only loads the first plug-in and treats the second as if it was the first. In other words, if the first plug-in is supposed to show a message box saying "First plug-in" and the second plug-in is supposed to show a message box saying "Second plug-in," then loading the second plug-in after the first will actually show a message saying "First plug-in" (i.e., the second plug-in was actually never loaded).
After searching and reading online, I believe that the problem is that the 3rd party application loads its plug-ins into its primary AppDomain. Therefore, plug-in libraries are never unloaded (become locked?) and subsequent attempts to load a plug-in with the same name simply uses the library that's already been loaded. I thought perhaps signing my plug-in libraries would fix the problem, but unfortunately, I'm unable to sign them because I depend on a .dll provided by the 3rd party application and it is not signed. Also, I cannot change the 3rd party application's config file, so I cannot play around with probing.
Our current solution is to re-name the assembly for every version of the plug-in library we have (for example, "PlugIn-1.0.dll" and "PlugIn-2.0.dll"), including re-naming all their dependent assemblies. I don't mean just changing their file name, but changing the AssemblyName property and re-compiling. This works, but I'd like to see if there's a cleaner solution. It wouldn't be so bad if it was just the plug-in assembly name we had to change, but we are also forced to change all their dependent .dll's (because different plug-ins may use different versions of these .dll's as well). I tried creating a config file for the plug-in library to change the probing directory, but this doesn't work. It looks like it is the application itself that does the probing, not the library that depends on the .dll's (am I correct in inferring this?)
Finally, I tried having my plug-in create an AppDomain and load its dependent .dll's into it, but unfortunately my plug-in directory location (and dependent .dll's) must be on a remote location relative to the 3rd party app. There are security/permission issues with loading assemblies over network locations in .NET 4.0 (which I'm using) that I haven't been able to solve.
What are my options? Thanks in advance.
I think you're knocking on the right door with spinning up your own AppDomain. That is the only way I know of to have different versions of the same assembly loaded into the same process.
And yes - as soon as a .NET appdomain has loaded an assembly, it will not load another version of that assembly. Whoever is first wins. And of course, there is no way to unload an assembly once it has been loaded.
As for your permission issues.. Can you copy the assemblies from that network location to a user folder (where the user has write permissions) and load them from there? I've been able to do this successfully for an auto-updating application.
I'm creating some tool what performs several operations like NUnit.
Inside this tool I open .dll assembly and invoke methods form it to run some test.
Everything is going OK till the time I need to reload .dll withour program restart. The idea is that when tool is run we copy required assembly to some temporary folder and invoke from there. If I need to reload I copy another one to another temporary folder and try to load newly copied from another folder and load to previous assembly object
ExecutingAssembly = Assembly.LoadFrom(AssemblyFullPath);
But my problem is that after I change AssemblyFullPath to new one and call Assembly.LoadFrom it returns just old assembly what was loaded first time but not the second one!
Maybe the problem is that we cannot load several assemblies with different versions? What is the solution?
The CLR does support loading multiple versions of strongly named assemblies into the same AppDomain. This only works though if your assemblies are strongly named and each one has a different version than the other.
I'm guessing it's more likely that you are dealing with unsigned assemblies. If that is the case then what you're asking for isn't really possible. Once a given assembly is loaded into an AppDomain it will remain there until the AppDomain is unloaded. To get this to work you will have to abstract out all of the work around the loaded assemblies into a separate AppDomain and use a new AppDomain for every assembly
To expand on JaredPar's answer, you will need to create a new AppDomain and use Remoting to communicate between the two.
Check out http://msdn.microsoft.com/en-us/library/kwdt6w2k(v=vs.85).aspx to help get you started.
Try like this:
string dllFile = "C:\\sample.dll";
Assembly asmLoader = Assembly.LoadFile(dllFile);
Type[] types = asmLoader.GetTypes();
Since all resources from the assembly cannot be reloaded/replaced it's assembly resources while application is still running. It will only be replaced/removed when application is unloaded or the that Assembly that holds it.
Use LoadFile() method. Hope it helps.
The background here is, I am developing a plugin for a CMS which is loaded via reflection from an external assembly. I want to embed some javascript and CSS files into my assembly so that I can load those files inside the CMS.
Ordinarily, you'd use Page.ClientScript.GetWebResourceUrl(Type type, string resourceName) and the resulting URL points to WebResource.axd, whose job it is to find the correct assembly, locate the correct resource and stream it back to the client.
This works fine if the assembly is already loaded as part of your application (e.g. it exists in your bin folder), but in this case it won't work because my assembly is only ever loaded via reflection (i.e. Assembly.Load()) from an external directory. The WebResource handler won't be able to find the assembly and so requests of this type will always result in a 404.
My question is, is there an accepted and well-trodden way of doing what I'm trying to achieve here or am I going to have to roll my own? Bear in mind, simply putting the assembly in the bin folder is not an option here.
I've tried google and searching here and I can find people with the same problem I'm experiencing, but no solutions as yet.
Oh, I should add, I'm running on .NET 4.0
You should load all of your plugin assemblies eagerly using BuildManager's AddReferencedAssembly method.
This can be achieved at application start by utilizing the PreApplicationStartMethodAttribute.
Then whenever you need to discover your types use GetReferencedAssemblies.
I have a little bit of code that loops through the types currently loaded into an AppDomain that runs in an ASP.NET application. Here's how I get the assemblies:
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
When the application first starts up there is no problem and all the types I expect are present. But when I update the Web.config or kill the w3p.exe process (or the process gets recycled for whatever reason) only some of the types I'm expecting are available. When I step through with a debugger I notice that certain assemblies from the private search path (the bin directory of my application) haven't been loaded. I was under the assumption that all assemblies were loaded at application start and restart whether or not they were immediately required. But in the case of restarting this doesn't seem to be happening unless those assembly files have been updated.
What I require is to collect type information at start-up for use later. But since during a restart the types aren't available it reeks havoc later on when the type information needs to be used. So with that in mind how can I solve or work around this deficiency?
Assemblies are loaded on demand, so it could be that you did not used any type contained in these assemblies yet.
You can use
AssemblyName[] assemblies = Assembly.GetCallingAssembly().GetReferencedAssemblies();
This way, you get all assemblies that are referenced from the assembly from which you are calling these method.
As a part of startup, can you explicitly load the assemblies you care about?
You would have to know ahead of time which assemblies you would need.
Scanning the filesystem to find out which assemblies have been shipped along with your app may be a useful idea, but it won't help for GAC loaded assemblies...
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!)