Resolve assembly references from another folder - c#

I am developing an application which references and uses some third party assemblies from a certain Vendor; in development box I have these 3 assemblies in a reference folder in my source tree and I can reference them and build the application, application builds but does not run because the whole server application is not installed, but this is fine.
On the server where I want to copy this custom application and run all assemblies I am referencing are in folder something like:
D:\ProgramFiles\VendorName\ProductName\Support\API\Bin64
and if I copy my small executable in that folder and run it, it works perfectly, but if I put my .exe in a more appropriate folder like I want:
D:\ProgramFiles\MyCompanyName\MyProduct\bin\...
it does not work because it cannot resolve those assemblies.
I know I can use probing in app.config to specify in which folders my exe has to look for references but imy case the assemblies are not in a subfolder, more in a completely different location.
I don't want to copy all vendor assemblies in my app folder and I cannot put there only the 3 ones I am referencing because they are also loading other assemblies and unless I have all of them (many...), it does not work.
I am not doing anything special, not creating app domains and not loading assemblies via reflection, just want the CLR to resolve the references as they are needed on application start or execution.
Thanks.
Edit: here the final working code
static System.Reflection.Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
Logger logger = new Logger();
try
{
string RMSAssemblyFolder = ConfigurationManager.AppSettings["RMSAssemblyFolder"];
Assembly MyAssembly = null;
string strTempAssmbPath = string.Empty;
Assembly objExecutingAssemblies = Assembly.GetExecutingAssembly();
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
AssemblyName myAssemblyName = Array.Find<AssemblyName>(arrReferencedAssmbNames, a => a.Name == args.Name);
if (myAssemblyName != null)
{
MyAssembly = Assembly.LoadFrom(myAssemblyName.CodeBase);
}
else
{
strTempAssmbPath = Path.Combine(RMSAssemblyFolder, args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll");
if (!string.IsNullOrEmpty(strTempAssmbPath))
{
if (File.Exists(strTempAssmbPath))
{
logger.Information("Assembly to load: {0} - File was found in: {1}", args.Name, strTempAssmbPath);
// Loads the assembly from the specified path.
MyAssembly = Assembly.LoadFrom(strTempAssmbPath);
}
}
}
// Returns the loaded assembly.
return MyAssembly;
}
catch (Exception exc)
{
logger.Error(exc);
return null;
}
}

You should first find the folder where theses dlls are installed then use AppDomain.AssemblyResolve to hook assembly resolution and try to load the requested assemblies from this folder.
It will look something like this (not tested, and you need to check what args.Name contain exactly, could contain the version and the strong name along with type name) :
var otherCompanyDlls = new DirectoryInfo(companyFolder).GetFiles("*.dll");
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var dll = otherCompanyDlls.FirstOrDefault(fi => fi.Name == args.Name);
if (dll == null)
{
return null;
}
return Assembly.Load(dll.FullName);
};

Use SN.exe : SN -T VendorAssembly.dll, this will return a hex number that is the public key token, then, reference the assembly from app.config. To get the version right click your vendor assembly and use that for the codeBase version value, the href=path you mentioned.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="VendorAssembly" culture="neutral" publicKeyToken="307041694a995978"/>
<codeBase version="1.1.1.1" href="FILE://D:/ProgramFiles/VendorName/ProductName/Support/API/Bin64/VendorAssembly.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

Related

Is there an way to add reference(dll) in parent path in C#?

== compile command ==
csc -r:"../Newtonsoft.Json.dll" test.cs
== exec command ==
mono test.exe
== exec result : dependency error ==
System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=11.0.0.0, Culture=neutral,
PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies.
"Newtonsoft.Json.dll" this file is located in parent path. so I added a reference about dll and compile succeeded, but when I executed the exe file, it failed to get dll reference I added.
And when I put cs file and dll file together in the same directory, it worked very well, but that's not what I wanted.
Is there a solution to add a reference from dll file which is located in parent path using command line interface?
I used csc for compiler and mono for execution.
Thanks.
References are pathless. What that means is that wherever the assembly resides, all your program knows is that it has a reference to Newtonsoft.Json, Version=x.x.x.x, Culture=... and so on. You can do some things with the application configuration (application.config or myprogram.exe.config) to organize things into subfolders (using the probing setting) or specify a URL location for the file (using the codebase setting). You can set up the environment to change the search path and so on.
Or you can add some runtime code that allows you to override the default behavior and the call Assembly.LoadFrom to provide a full path to the file. You can either do it as part of your initialization or in a handler for the AppDomain.AssemblyResolve event - which is generally better method since it will only be called when the assembly is actually needed.
For example:
using System.IO;
using System.Reflection;
static class ParentPathResolver
{
public static void Init()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolve);
}
private static Assembly? Resolve(object? sender, ResolveEventArgs args)
{
var filename = new AssemblyName(args.Name).Name + ".dll";
var fullname = Path.GetFullPath(Path.Combine("..", filename));
if (File.Exists(fullname))
return Assembly.LoadFrom(fullname);
return null;
}
}
Of course you can add your own code to the Resolve method to search pretty much anywhere, just as long as you don't get caught in a resolution loop. I've used the AssemblyResolve event to do fun things like loading assemblies from compressed resources.

MEF Composition Error Because of Assembly Names?

Thanks in advance for reviewing this question!
I am using MEF to load some assemblies in a project. Everything was working great until we changed the name of the file that contains the interface.
To make it clearer, I'll summarize the scenario where things worked, then the scenario where things did not work, then specifically show the exception and the code that caused the exception in the scenario that didn't work.
Here's the working scenario:
We've got an interface called IPlugin that is defined in an assembly called Common-1-0.dll.
We have some plugin assemblies that are compiled against Common-1-0.dll.
The application that loads the plugin is compiled against Common-1-0.dll.
Here's the non working scenario:
We've got this an called IPlugin that is defined in an assembly called Common-1-1.dll. The interface has not changed from Common-1-0.dll.
We have some plugin assemblies that are compiled against Common-1-0.dll.
The application that loads the plugin is compiled against Common-1-1.dll.
Now the issue:
When I go to run this code below in the second scenario, I get a CompositionException (shown under the code below). It appears to be caused because the the plugin was compiled against Common-1-0.dll while the application trying to do the composition was compiled against Common-1-1.dll. Nothing within the code has changed between the two files, just the name.
So we'd like to be able to load plugins built against whatever assembly so long as that assembly exports the right interface but I'm not sure if I can do that with MEF or not. That is what I'd like to know as a result of this question.
Code :
private void LoadPlugins(string directory, string searchPattern = "", bool recursive = false)
{
Trace.Agent.Status("Loading plugin(s) from {0}{1}{2}", directory, Path.DirectorySeparatorChar, searchPattern);
try
{
var directoryCatalog = string.IsNullOrEmpty(searchPattern)
? new DirectoryCatalog(directory)
: new DirectoryCatalog(directory, searchPattern);
_container = new CompositionContainer(new AggregateCatalog(directoryCatalog));
_container.ComposeParts(this);
}
catch (CompositionException exc)
{
Trace.Agent.Exception(exc);
}
if (recursive)
{
foreach (string dir in Directory.GetDirectories(directory))
{
LoadPlugins(Path.Combine(directory, dir), recursive:true);
}
}
}
CompositionException :
The export 'TestPlugin.TestPlugin (ContractName="Common.IPlugin")' is not assignable to type 'Common.IPlugin'.
I think I found a solution for your issue, it is a little bit hacked but anyways.
So if you have named your assemblies differently, e.g. assembly1.0 and assembly1.1 this causes issues because you cannot simply redirect the assembly to a new version.
Usually you would just keep the same assembly name and increase the version number. This way you can redirect without any issues (as long as the code supports it).
The solution is to hack into the assembly resolve mechanism by attaching to the AssemblyResolve event of the current AppDomain.
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
LoadPlugins(currentDomain.BaseDirectory);
var x = _container.GetExport<IPlugin>().Value;
within the event handler you can simply return the "new" assembly
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
if (args.Name.StartsWith("PluginCore1.0"))
{
return typeof(IPlugin).Assembly;
}
return null;
}
This works, also without signed assemblies (without public key tokens).
To trigger the resolve event you still have to define the assembly redirection within your app.config though:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="PluginCore1.0"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
</assemblyBinding>
Again, I would strongly recommend to use the same assembly name (without the version suffix) and simply use the assembly redirection mechanism which should work just fine.

Why loading dll file from embedded resource is not working?

In the past, I used to dynamically load the file System.Data.SQLite.dll by calling this in the main method :
static void Main(string[] args)
{
try
{
string resource1 = "System.Data.SQLite.dll"; //
string resource2 = "Ionic.Zip.Reduced.dll";
EmbeddedAssembly.Load(resource1, "System.Data.SQLite.dll");
EmbeddedAssembly.Load(resource2, "Ionic.Zip.Reduced.dll");
//
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
catch (Exception E) { File.AppendAllText("myExcept.txt",E.Message); }
}
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return EmbeddedAssembly.Get(args.Name);
}
Where EmbeddedAssembly.cs is a class that I have found on this link in codeproject
I have already used this method before and it worked for me, but now I need to compile a cs file dynamically from c# code : So I added the file System.Data.SQLite.dll as an embedded resource and as a referencedassembly, something like this :
...
String exeName = "myFile.exe"
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = true;
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Data.dll");
// add reference
cp.ReferencedAssemblies.Add("System.Data.SQLite.dll");
// add embedded resources
cp.EmbeddedResources.Add(#"System.Data.SQLite.dll");
...
But after generating the executable file It still depends on System.Data.SQLite.dll file, so if I do not put this file in the same folder with the Exe, the file won't work !
The other thing that Confused me is that the file Ionic.Zip.Reduced.dll is loaded correctly whereas System.Data.SQLite.dll is throwing an exception :
Cannot load file or assembly 'System.Data.SQLite, Version=1.0.86.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139' or one of its dependencies. The specified file cannot be found
What I'm doing wrong ?
Why do you load the assemblies manually. You can do this by referencing them in your project file?
As an alternative SQLite has nuget package. Using this is very handy on loading an assembly for AnyCPU/x86 or x64).

Can Unity Container Load Assemblies Dynamically?

I was developing an application. I want unity to resolve my types WITHOUT having to reference the assemblies in the main project. I tought it loaded assemblies automatically by configuring the type registration by using but doesn't seem to work unless I add a reference to the assembly containing dependencies.
Is there a wat to load types from the assemblies in the current directory?
Thanks!
Sounds like you want MEF and not Unity. MEF is designed for dynamic discovery.
Read the answer to this question: What is different between and purpose of MEF and Unity?
I know this was asked a while ago, but for anyone looking for an answer, you have to make sure that Unity can locate the assemblies at run-time. So you either need to put them in the GAC or drop your assembly dll in the same directory as your executable. If you are using a different directory for dependencies and have a web app, then you have to set the probing element in your web.config:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Dependencies" />
</assemblyBinding>
</runtime>
And for anyone out there looking for a piece of code on how to solve this, the following code will look for all assemblies on your defined directory and register them with Unity:
string DependenciesPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Dependencies");
string[] Dependencies = Directory.GetFiles(DependenciesPath, DLL_PATTERN);
Dictionary<Type, Type> pluginMappings = new Dictionary<Type, Type>();
//Load Dependency Assemblies
foreach (string fileName in Dependencies)
{
string assemblyName = Path.GetFileNameWithoutExtension(fileName);
if (assemblyName != null)
{
Assembly pluginAssembly = Assembly.Load(assemblyName);
foreach (Type pluginType in pluginAssembly.GetTypes())
{
if (pluginType.IsPublic) //Only look at public types
{
if (!pluginType.IsAbstract) //Only look at non-abstract types
{
//Gets a type object of the interface we need the plugins to match
Type[] typeInterfaces = pluginType.GetInterfaces();
foreach (Type typeInterface in typeInterfaces)
{
if (pluginMappings.ContainsKey(typeInterface))
{
throw new DuplicateTypeMappingException(typeInterface.Name, typeInterface, pluginMappings[typeInterface], pluginType);
}
pluginMappings.Add(typeInterface, pluginType);
}
}
}
}
}
}
//Web API resolve dependencies with Unity
IUnityContainer container = new UnityContainer();
foreach (var mapping in pluginMappings)
{
container.RegisterType(mapping.Key, mapping.Value);
}
It might be a bit late to help, but Unity can dynamically load assemblies if you use the XML configuration approach, register each assembly, and then register the types accordingly. I've been using this process for minor extensions to an otherwise very DI-heavy framework for awhile now.
If you're encountering an issue where Unity fails to resolve a type that's registered in the main application but defined in another, unreferenced assembly and referencing said assembly resolves the issue, that most likely means that it just wasn't copied to the application's output directory. By default, only assemblies that are directly referenced are automatically copied. If they're copied manually or by a post-build event, or if you redirect your build paths so that the unreferenced assemblies build to the application's output directory, it should work.

Custom config section: Could not load file or assembly

I'm having a very hard time trying to access a custom configuration section in my config file.
The config file is being read from a .dll that is loaded as a plug-in. I created the Configuration and necessary code using the Configuration Section Designer VS addin.
The namespace is 'ImportConfiguration'. The ConfigurationSection class is 'ImportWorkflows'. The assembly is ImportEPDMAddin.
The xml:
<configSections>
<section name="importWorkflows" type="ImportConfiguration.ImportWorkflows, ImportEPDMAddin"/>
</configSections>
Whenever I try to read in the config, I get the error:
An error occurred creating the configuration section handler for importWorkflows: Could not load file or assembly 'ImportEPDMAddin.dll' or one of its dependencies. The system cannot find the file specified.
The dll will not reside in the same directory as the executable as the software that loads the plugin places the dll and it's dependencies in it's own directory. (I can't control that.)
I edited the code for the singleton instance to the following:
string path = System.Reflection.Assembly.GetCallingAssembly().CodeBase;
path = path.Replace("file:///", "");
System.Configuration.Configuration configuration = System.Configuration.ConfigurationManager.OpenExeConfiguration(path);
return configuration.GetSection(ImportWorkflowsSectionName) as ImportConfiguration.ImportWorkflows;
I have also tried using a simple NameValueFileSectionHandler as well, but I get an exception saying that it can't load file or assembly 'System'.
I have read numerous blog posts and articles and it sounds like it is possible to read a config file in for a dll, but I just can't get it to work. Any ideas? Thanks.
Unfortunately, you will need to either have the ImportEPDMAddin assembly residing in the same folder as your executable, residing in the .Net framework folder related to the .Net framework you are using (i.e., C:\Windows\Microsoft.NET\Framework\v2.0.50727), or registered in the Global Assembly Cache.
The only other option is, if you know the path to the assembly that contains the configuration handler's defining class, you can load it without a reference with something like this:
//Class global
private Assembly configurationDefiningAssembly;
protected TConfig GetCustomConfig<TConfig>(string configDefiningAssemblyPath,
string configFilePath, string sectionName) where TConfig : ConfigurationSection
{
AppDomain.CurrentDomain.AssemblyResolve += new
ResolveEventHandler(ConfigResolveEventHandler);
configurationDefiningAssembly = Assembly.LoadFrom(configDefiningAssemblyPath);
var exeFileMap = new ExeConfigurationFileMap();
exeFileMap.ExeConfigFilename = configFilePath;
var customConfig = ConfigurationManager.OpenMappedExeConfiguration(exeFileMap,
ConfigurationUserLevel.None);
var returnConfig = customConfig.GetSection(sectionName) as TConfig;
AppDomain.CurrentDomain.AssemblyResolve -= ConfigResolveEventHandler;
return returnConfig;
}
protected Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args)
{
return configurationDefiningAssembly;
}
Make sure you handle the AssemblyResolve event, as this will throw an exception without it.
In your main applications config file, add the following (where plugins is the folder for your assembly to load from. You can use multiple paths semi-colon separated.
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath=".;.\Plugins"/>
</assemblyBinding>
</runtime>
Taken from http://msdn.microsoft.com/en-us/library/823z9h8w%28v=vs.90%29.aspx
To expand on AJ's excellent answer, here is a custom class to assist with the overhead of registering and removing the global event.
public sealed class AddinCustomConfigResolveHelper : IDisposable
{
public AddinCustomConfigResolveHelper(
Assembly addinAssemblyContainingConfigSectionDefinition)
{
Contract.Assert(addinAssemblyContainingConfigSectionDefinition != null);
this.AddinAssemblyContainingConfigSectionDefinition =
addinAssemblyContainingConfigSectionDefinition;
AppDomain.CurrentDomain.AssemblyResolve +=
this.ConfigResolveEventHandler;
}
~AddinCustomConfigResolveHelper()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool isDisposing)
{
AppDomain.CurrentDomain.AssemblyResolve -= this.ConfigResolveEventHandler;
}
private Assembly AddinAssemblyContainingConfigSectionDefinition { get; set; }
private Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args)
{
// often the name provided is partial...this will match full or partial naming
if (this.AddinAssemblyContainingConfigSectionDefinition.FullName.Contains(args.Name))
{
return this.AddinAssemblyContainingConfigSectionDefinition;
}
return null;
}
}
I would suggest creating an instance in a using statement, like so:
// you'll need to populate these two variables
var configuration = GetConfiguration();
var assembly = GetAssemblyContainingConfig();
using(new AddinCustomConfigResolveHelper(assembly))
{
return (MyConfigSection)configuration.GetSection("myConfigSection");
}
Have you made sure that DLL is loaded first? Perhaps with Assembly.LoadFile("PATH")?
If you can't get the classes in System.Configuration to work properly, you can always fall back on using XmlDocument to manually parse the configuration file. Use XPaths to make getting the data easier. For example (assuming your path variable above):
var document = new XmlDocument();
document.Load(path);
var node = document.SelectSingleNode("configuration/importWorkflows/add[#name='KEY']");
// Do whatever with node
Could you verify that the probing paths are setup correctly in your Host application's config file? It is possible that a needed reference is not being loaded in your current application domain.
Assembly Binding ->Probing
Had to use the fully qualified type string of my module/plugin assembly, which is in a probing directory, so it could be located. Using EntityFramework as an example...
Incorrect:
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework"
Correct
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
I tried AJ's answer, with rileywhite's supplement but I found that did not work for me.
In my scenario, the custom ConfigurationSection class was already in the currently-executing assembly, and trying to load it causes a stack overflow. I also did not want to put it in GAC even though it did resolve the problem as reported by the OP.
In end, I found this to work well enough for my purpose. Maybe others will find it useful:
public class CustomConfigurationSection : ConfigurationSection {
public CustomConfigurationSection()
{
var reader = XmlReader.Create(<path to my dll.config>);
reader.ReadToDescendant("CustomConfigurationSection");
base.DeserializeElement(reader,false);
}
// <rest of code>
}

Categories