Why loading dll file from embedded resource is not working? - c#

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).

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.

SSIS Script Task cant find reference to assembly

I have an SSIS package that uses a script task to populate a datatable with data from a variety of different file types including excel.
I am using NPOI to read in the data from Excel and have put the NPOI.dll file in the same folder as the SSIS package and added it as a reference in the script task. I'm a Noob when it comes to NPOI so I'm only tinkering at the moment, but even still I have fallen at the first hurdle!
My script contains the code below (which i copied from this SA answer):
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
HSSFWorkbook wb;
using (FileStream file = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
{
wb = new HSSFWorkbook(file);
}
but fails with the following error message: Could not load file or assembly 'NPOI, Version=2.1.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1' or one of its dependencies. The system cannot find the file specified
But when I go into the script task, the reference is there and there are no errors.
If I comment out everything except the first line where I declare a HSSFWorkBook called wb it runs fine.
Have I added the references incorrectly or is adding references to a SSIS script task notoriously difficult?
As always any help is greatly appreciated.
For custom assemblies to be referenced and executed in Script Task, you have to add them to GAC. Here is an article with workflow.
Alternative approach - provide your own AssemblyResolver in Script task code.
Here is an example for the custom AssemblyResolver Approach, mentioned by Ferdipux.
The given solution was not working with user variables, so you would have to deal with the comments in the documentation to find a "non static" approach.
Additionally the posted solutions would not be working anymore when you deploy to a SQL Server 2017 instance and try to read the assemblies from a network share (System.NotSupportedException).
Therefore I replaced the LoadFile(path) with the UnsafeLoadFrom(path) call as workaround. Please use it only for your own or other wellknown assemblies, not downloaded assemblies from unknown authors, because this would be a security issue.
Here is the working code, the referenced DLL is "System.Web.Helpers.dll" and the network share path gets configured in the user variable "LibPath" (VS 2015,SQL Server 2017):
public System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string path = Variables.LibPath.ToString();
if (args.Name.Contains("System.Web.Helpers"))
{
return System.Reflection.Assembly.UnsafeLoadFrom(System.IO.Path.Combine(path, "System.Web.Helpers.dll"));
}
return null;
}
/// <summary>
/// This method is called once, before rows begin to be processed in the data flow.
/// </summary>
public override void PreExecute()
{
base.PreExecute();
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
...
static ScriptMain()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//(string)Dts.Variables["User::CustomDLL"].Value;
if (args.Name.Contains("HtmlAgilityPack"))
{
string path = #"C:\Temp\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "HtmlAgilityPack.dll"));
//return System.Reflection.Assembly.UnsafeLoadFrom(System.IO.Path.Combine(path, "HtmlAgilityPack.dll"));
}
return null;
}

Reading web.config with WebConfigurationManager that contains custom sections, can't load

I'm trying to put together a small helper tool to encrypt web.config files.
Linqpad example:
var path = #"F:\project\";
var wcfm = new WebConfigurationFileMap();
wcfm.VirtualDirectories.Add("/",new VirtualDirectoryMapping(path,true));
var config = Configuration.WebConfigurationManager.OpenMappedWebConfiguration(wcfm,"/");
var c = config.Sections["customGroup"];
c.SectionInformation.ProtectSection(null);
config.Save();
This throws the exception:
Could not load file or assembly 'customGroupAssembly' or one of its dependencies.
The system cannot find the file specified
Adding the assembly to LinqPad fixes the errpr. I would assume this is because it's now a compile time reference.
Moving the code to a winforms application, reintroduces the issue.
I'm trying to load the needed assembly at runtime with this:
if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
{
byte[] assemblyBuffer = File.ReadAllBytes(this.openFileDialog1.FileName);
AppDomain.CurrentDomain.Load(assemblyBuffer);
MessageBox.Show("Assembly loaded!");
}
However it still doesn't seem to find the file.
Is there a way to load the custom assembly into the runtime app domain so that the web config can be loaded properly?
Try attaching assembly resolve listener, I've had such a problem where runtime-loaded assembly was not resolved properly and needed to be manually resolved:
AppDomain.CurrentDomain.AssemblyResolve += (s,arg)=>{ ... }
with your code at the (...) returning the assembly you want resolved based the name you got in arg

How do package managed C# dlls with a managed C# application without leaving behind files?

I've read through the two other threads that extract the dll from the application at run time. One of these methods used the current Windows temporary directory to save the dll in, but it was an unmanaged dll and had to be imported at runtime with DllImport. Assuming that my managed dll exported to the temporary directory, how can I properly link that managed assembly to my current MSVC# project?
You dont need to save to a temp directory at all. Just put the managed dll as an 'Embedded Resource' in your project. Then hook the Appdomain.AssemblyResolve event and in the event, load the resource as byte stream and load the assembly from the stream and return it.
Sample code:
// Windows Forms:
// C#: The static contructor of the 'Program' class in Program.cs
// VB.Net: 'MyApplication' class in ApplicationEvents.vb (Project Settings-->Application Tab-->View Application Events)
// WPF:
// The 'App' class in WPF applications (app.xaml.cs/vb)
static Program() // Or MyApplication or App as mentioned above
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.Contains("Mydll"))
{
// Looking for the Mydll.dll assembly, load it from our own embedded resource
foreach (string res in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
if(res.EndsWith("Mydll.dll"))
{
Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream(res);
byte[] buff = new byte[s.Length];
s.Read(buff, 0, buff.Length);
return Assembly.Load(buff);
}
}
}
return null;
}

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