I have a library project (SamplePlugin) that is meant to be used as an MEF module and loaded at runtime. Recently, I decided to support internationalization. To that end, I created Properties/Resource.resx and respective Properties/Resource.xx-yy.resx for each supported locale. I then added string resources with locale specific translations to each resource file. However at runtime, I cannot seem to get the culture-correct resource. Here is the relavent code:
Loading the module:
var di = new DirectoryInfo("Plugins/");
var dlls = di.GetFileSystemInfos("*.dll", SearchOption.AllDirectories);
var catalog = new AggregateCatalog();
foreach (var fi in dlls)
{
var ac = new AssemblyCatalog(Assembly.LoadFile(fi.FullName));
catalog.Catalogs.Add(ac);
}
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
Method inside of MEF module (SamplePlugin):
string GetFrenchString()
{
var resourceManager = new ResourceManager(
"SamplePlugin.Properties.Resources",
System.Reflection.Assembly.GetExecutingAssembly());
var culture = new System.Globalization.CultureInfo("fr-FR");
return (string)resourceManager.GetObject("SampleText", culture);
}
File structure of the compiled application:
app.exe
Plugins/SamplePlugin/SamplePlugin.dll
Plugins/SamplePlugin/zh-CN/SamplePlugin.resources.dll
Plugins/SamplePlugin/de-DE/SamplePlugin.resources.dll
Plugins/SamplePlugin/fr-FR/SamplePlugin.resources.dll
...
When I run GetFrenchString() I get "Sample Text" (from resource.resx) instead of "exemple de texte" (from resource.fr-FR.resx). I actually found similar question but the poster answered his own question and provided very little detail in both his question and answer.
Additional details:
The access modifiers of all my resource files are "public"
Related
Module Creation process
var file = Console.ReadLine();
var moduleContext = ModuleDef.CreateModuleContext();
var moduleCreationOptions = new ModuleCreationOptions(moduleContext, CLRRuntimeReaderKind.Mono);
var buffer = File.ReadAllBytes(file);
var moduleDefMD = ModuleDefMD.Load(buffer, moduleCreationOptions);
var moduleWriterOptions = new ModuleWriterOptions(moduleDefMD);
moduleWriterOptions.MetadataLogger = DummyLogger.NoThrowInstance;
moduleWriterOptions.MetadataOptions.Flags |=
MetadataFlags.PreserveStringsOffsets
| MetadataFlags.PreserveUSOffsets
| MetadataFlags.PreserveBlobOffsets
| MetadataFlags.PreserveExtraSignatureData;
moduleWriterOptions.Cor20HeaderOptions.Flags = ComImageFlags.ILOnly;
Add Static Method with single instruction ret and call it in Module cctor
var methodDef = new MethodDefUser(Guid.NewGuid().ToString(), MethodSig.CreateStatic(moduleDefMD.CorLibTypes.Void));
methodDef.IsStatic = true;
methodDef.Body = new CilBody();
methodDef.Body.Instructions.Add(new Instruction(OpCodes.Ret));
moduleDefMD.GlobalType.Methods.Add(methodDef);
var cctorInstructions = moduleDefMD.GlobalType.FindOrCreateStaticConstructor().Body.Instructions;
cctorInstructions.Insert(0, new Instruction(OpCodes.Call, methodDef));
Write ModuleDefMD
var output = Path.Combine(Path.GetDirectoryName(file), "output." + Path.GetExtension(file));
moduleDefMD.Write(output);
Result
assembly attributes are deleted, only single one is left is [assembly: AssemblyVersion("1.0.0.0")], nothing else, no Assembly Name, no Assembly Company etc it causes problems with Assembly.GetAssembly().GetName
I tried to create another project with the same parameters and conditions i.e same .NET Framework versions, same ModuleDefMD writing process, I tried to do everything the same but nothing is help me, this is just like a curse, I'm deeply learing dnlib sources and I didn't found anything interesting that can help me to solve this problem. I open issue on dnlib GitHub
The problem is solved, when I was resolving CustomAttributes via reflection I used to be removing them
I would like to use resourcefiles to get some text.These resourcefiles will be in dll.It's nothing todo with localization just in case you ask.
I want to have the ability to choose which rexfile to use based on a configsettings.
Sample
MyCompany.RexFiles.dll
RexFileA
RexFileB
RexFileC
My question
Given that in a config file I have a settings that decide which rexfile to use eg CurrentRexfile="RexFileB"
How can I default to right rexFile depending on the configSettings.
Any suggestions
You can use the ResourceManager Class to retrieve resources:
System.Reflection.Assembly myAssembly = this.GetType().Assembly;
string rexFile = ConfigurationManager.AppSettings["CuurentRexfile"];
System.Reflection.Assembly otherAssembly = System.Reflection.Assembly.Load(rexFile);
System.Resources.ResourceManager resManager = new System.Resources.ResourceManager("ResourceNamespace.myResources", otherAssembly);
string test = resManager.GetString("resourceString");
more read here
I am currently developing client-server application.
It should load interfaces from modules and show them inside of its own window.
But sometimes I need to plugin remote module.
Can I run form from module (with all actions working) without loading module file on disk?
Thank you.
Yes, you can load an assembly sent from a remote computer (I will not discuss here security implications of this, I'd - at least - check for a signature):
var data = new WebClient.DownloadData(url); // For example...
var assembly = Assembly.Load(data);
In C++/CLI (it's not clear in your question what's language you're using):
array<Byte>^ data = (gcnew WebClient())->DownloadData(url);
Assembly^ assembly = Assembly::Load(data);
Now you have assembly and you can load something from it, for example (just for illustration):
var plugins = assembly.GetExportedTypes()
.Where(x => typeof(IYourContract).IsAssignableFrom(x) && !x.IsAbstract)
.Select(x => (IYourContract)Activator.CreateInstance(x));
Please note that this is a very naive implementation because each instance will be different (if you load same plugin multiple times) and it's also expansive in terms of resources (primary memory). You should keep an assembly cache:
private static Dictionary<string, Assembly> _cachedAssemblies =
new Dictionary<string, Assembly>();
public static Assembly LoadRemoteAssembly(string url)
{
lock (_cachedAssemblies)
{
if (_cachedAssemblies.ContainsKey(url))
return _cachedAssemblies[url];
var data = new WebClient.DownloadData(url); // For example...
var assembly = Assembly.Load(data);
_cachedAssemblies.Add(url, assembly);
return assembly;
}
}
I'm completely new to resource files, but there is a need for me to deploy my application as click-once, and that seems to ignore all of my external files (images, .ini files etc...) - Rather than spend time trying to figure it out, I thought I'd learn how to use Resource files properly.
After searching through SO, I've found much code and I've built my resource file. So far it only contains strings, which I thought would be simpler!? Alas...
So I've got my DLL (ValhallaLib.dll) and these two functions for dealing with resources (these functions are held within a static Helper Class, where all my random but useful functions live):
public static Bitmap getImageByName(string imgName)
{
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
string resName = asm.GetName().Name + ".Properties.Resources";
var rm = new System.Resources.ResourceManager(resName, asm);
return (Bitmap)rm.GetObject(imgName);
}
public static string getStringByName(string var)
{
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
string resName = asm.GetName().Name + ".Properties.Resources";
var rm = new ResourceManager(resName, asm);
return rm.GetString(var);
}
And I'm attempting to call them with a simple:
CHelpers.getStringByName("db_host_address");
Now, other than getting a MissingManifestException... My problem is, that I have no idea (and can't seem to find a straight answer!!) what the resName should be. My resources file is called: StringResource.resx - Yet, its not a case of saying Assembly.ValhallaLib.StringResource.
Can anyone offer some guidance, please?
Update
I've tried global::ValhallaLib.StringResource - but that's not really what I'm after either.
Update 2
Solved it. I've managed to get it to work with:
public static string getStringByName(string var)
{
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
string resName = asm.GetName().Name + ".Properties.Resources";
var rm = new ResourceManager("ValhallaLib.StringResource", asm);
return rm.GetString(var);
}
I don't know why I found that so overly complicated. Possibly because I've had about 2 hrs sleep.
Cheers to those who tried though :)
facepalm I've figured it out. I can access the resources file using
public static string getStringByName(string var)
{
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
string resName = asm.GetName().Name + ".Properties.Resources";
var rm = new ResourceManager("ValhallaLib.StringResource", asm);
return rm.GetString(var);
}
Ignore me :)
resName is a base name for resources. In your case you can simple use full type name of resource class.
var resName = typeof(StringResource).FullName;
I have a situation and I need to know how to deal with it in the best way.
I have an application (MVC3) and I have couple of integrations for it. I have an interface "IntegrationInterface" and every integration implements it.
I want to load the dlls of the integrations, create a list of them, and run a loop that runs a method for every integration in the list.
For example - let's say I have integrations for facebook, myspace and twitter (for my appliction), and every time the user post a message in my application I want to post a message on his\her facebook, myspace and twitter.
I don't want that the code will know which integrations I have, so if tomorrow I'll create a new integration for google+, I'll just need to add a new DLL without changing the code of my application.
How can I do that?
First, you'll have to find all relevant dlls and classes:
loadedIntegrations.Clear();
if (!Directory.Exists(path))
return;
DirectoryInfo di = new DirectoryInfo(path);
FileInfo[] files = di.GetFiles("*.dll");
foreach (var file in files)
{
Assembly newAssembly = Assembly.LoadFile(file.FullName);
Type[] types = newAssembly.GetExportedTypes();
foreach (var type in types)
{
//If Type is a class and implements the IntegrationInterface interface
if (type.IsClass && (type.GetInterface(typeof(IntegrationInterface).FullName) != null))
loadedIntegrations.Add(type);
}
}
loadedIntegrations is of type List<Type>. Then you can instantiate each integration and call its methods:
foreach(var integrationType in loadedIntegrations)
{
var ctor = integrationType.GetConstructor(new Type[] { });
var integration = ctor.Invoke(new object[] { }) as IntegrationInterface;
//call methods on integration
}
I am doing something similar to what you described in an import utility that wrote. My issue was that I didn't want to load ALL the assemblies. I only wanted to load assemblies that contained types that were requested.
To accomplish this I've used the AppDomain.CurrentDomain.AssemblyResolve event handler.
This event handler is raised just before the AppDomain throw an exception notifying that an assembly is not found. I execute similar code to what Nico suggested in that handler and return the requested assembly.
NOTE: I have a sub-directory called 'Tasks' (think Import Tasks) where I store all of my assemblies I want to load at runtime.
Here is the code:
var tasks = GetTasks();
var basedir = AppDomain.CurrentDomain.BaseDirectory; // Get AppDomain Path
var tasksPath = Path.Combine(basedir, "Tasks"); // append 'Tasks' subdir
// NOTE: Cannot be factored, relies on 'tasksPath' variable (above).
AppDomain.CurrentDomain.AssemblyResolve += (s, e) => // defined 'AssemblyResolve' handler
{
var assemblyname = e.Name + ".dll"; // append DLL to assembly prefix
// *expected* assembly path
var assemblyPath = Path.Combine(tasksPath, assemblyname); // create full path to assembly
if (File.Exists(assemblyPath)) return Assembly.LoadFile(assemblyPath); // Load Assembly as file.
return null; // return Null if assembly was not found. (throws Exception)
};
foreach (var task in tasks.OrderBy(q => q.ExecutionOrder)) // enumerate Tasks by ExecutionOrder
{
Type importTaskType = Type.GetType(task.TaskType); // load task Type (may cause AssemblyResolve event to fire)
if (importTaskType == null)
{
log.Warning("Task Assembly not found");
continue;
}
ConstructorInfo ctorInfo = importTaskType.GetConstructor(Type.EmptyTypes); // get constructor info
IImportTask taskInstance = (IImportTask)ctorInfo.Invoke(new object[0]); // invoke constructor and cast as IImportTask
taskInstances.Add(taskInstance);
}
// rest of import logic omitted...
If u know MEF (Managed extensibility framework) this might help you I personally use it but have to say that using MEF with MVC is not trivial i think for more information please visit
http://msdn.microsoft.com/en-us/library/dd460648.aspx