I want to load a COM dll dynamically in c# console application.
Till now I have tried the following code:
// load exe with args as EXE DllName classNameTobeLoaded
try
{
// load assembly
Assembly Dll = Assembly.LoadFile(#"C:\My_Dir\TestComDll.dll");
// get the type names
foreach(Type t in Dll.GetExportedTypes())
{
dynamic vClassLoaded = Activator.CreateInstance(t,"Test");
Console.WriteLine("Type Loaded");
}
Console.WriteLine("DLL is loaded");
}
catch (Exception ex)
{
Console.WriteLine("Unable to load a DLL because \n" + ex.Message);
}
But while loading the dll I am getting error as:
{System.BadImageFormatException: The module was expected to contain an assembly manifest. (Exception from HRESULT: 0x80131018)
at System.Reflection.RuntimeAssembly.nLoadFile(String path, Evidence evidence)
at System.Reflection.Assembly.LoadFile(String path)
at ThirdPartyDLLLoader.Program.Main(String[] args) in h:\Test Exe\ThirdPartyDLLLoader\ThirdPartyDLLLoader\Program.cs:line 18}
The same code is working fine for .NET DLL.
Can any one tell me why the code is not able to load a COM dll dynamically?
and if it is not can you please tell me how I can do the same.
Thanks for any suggestion and help.
This cannot be done. The Assembly.LoadFrom method is for loading .NET assemblies. COM library must be registered and then you can instantiate classes using Activator.CreateInstance method.
This is how I access MS Word:
Type type = Type.GetTypeFromProgID("Word.Application");
object obj = Activator.CreateInstance(type);
I have a function load a class library dll
public ICollection<Assembly> GetAssemblies(string pathOfDLL)
{
List<Assembly> baseAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
var controllersAssembly = Assembly.LoadFrom(pathOfDLL);
baseAssemblies.Add(controllersAssembly);
return baseAssemblies;
}
Hope it helps!
Related
This question already has an answer here:
Could not load file or assembly error on GetTypes method
(1 answer)
Closed 1 year ago.
The structure of my solution is as follows:
I got 2 projects. Independent of each other at compile time (no cross references). The second one is a class library that is loaded at runtime from the first one.
That library has its own dll dependencies, which I guess is what causes the exception thrown when I'm trying to do execute code using these dlls. I supposed that these referenced dlls are probably not loaded at runtime, except from the class library dll. Therefore, I added some code to load the referenced assemblies before executing any code of the loaded assembly.
To my surprise the "problematic" dll is already included in the loaded assemblies when the following code is executed, its location is correct, but the error still occurs and I have no idea how to further troubleshoot the issue.
Any help is highly appreciated.
try
{
Assembly a = Assembly.LoadFile(Path.GetFullPath(filename));
//Try to find the type the derived plugin class
foreach (Type t in a.GetTypes())
{
if (t.IsSubclassOf(typeof(PluginBase)))
{
Console.WriteLine("Plugin class detected! {0}", t.Name);
//Get Referenced Assemblies
AssemblyName[] l = a.GetReferencedAssemblies()
//Get Loaded Assemblies
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (AssemblyName a2 in l)
{
var asm = loadedAssemblies.FirstOrDefault(a => a.FullName == a2.FullName);
if (asm == null)
{
Assembly test = null;
try
{
//First try to load using the assembly name just in case its a system dll
test = Assembly.Load(a2);
//Try to load the assemly from the plugin directory
if (test == null)
{
test = Assembly.LoadFrom(Path.Join(a.Location, a2.Name + ".dll"));
}
}
catch
{
Callbacks.Log($"Unable to load assembly {a2.Name}", LogVerbosityLevel.WARNING);
}
if (test != null)
{
Callbacks.Log($"Loaded Assembly {a2.Name}", LogVerbosityLevel.WARNING);
AppDomain.CurrentDomain.Load(test.GetName());
}
}
}
object c = Activator.CreateInstance(t, new object[] { this });
Plugins[Path.GetFileName(filename)] = c as PluginBase;
//Call Dll initializers
t.GetMethod("OnLoad").Invoke(c, new object[] { });
break;
}
}
}
catch (Exception ex)
{
Log("Error during loading of plugin " + filename, LogVerbosityLevel.INFO);
Log("Exception type " + ex.Data, LogVerbosityLevel.INFO);
}
Aight, I think I figured it out. So before the reason why I saw the referenced assemblies loaded was because I iteratively loaded them by browsing in a dll folder that contains the class library + its references. The main problem is that I am so stupid that I forgot to load them to the AppDomain calling AppDomain.CurrentDomain.Load(assemblyname). However when I did try to fix that I realized that trying to just fetch the related AssemblyName object does not work. I get the same FileNotFoundException.
What fixed the issue was to use Assembly.LoadFrom instead of Assembly.LoadFile. I read through the documentation that it states that LoadFile treats the loaded assemblies differently regardless if they are exactly the same dll file just in different location. In my case there is only a single path that I tried to load the assembly from, but I guess that LoadFile also differentiates the loaded assemblies in this case as well. However, I am still not sure why trying to use an AssemblyName coming from LoadFile crashes compared to what comes out of LoadFrom. I would expect that they would be identical...
I also added a failsafe mechanism to try and load only the desired dll files. I just expect that all dlls that will be loaded will be prepended with a text identifier. So at first the desired dll is loaded and before invoking any code, its references are loaded using the AssemblyName object or the actual path if the first fails.
Everything seems to be running nicely till now so hopefully this solves it.
foreach (string filename in Directory.GetFiles("Plugins"))
{
if (!filename.EndsWith(("dll")))
continue;
if (!Path.GetFileName(filename).StartsWith(("Test")))
continue;
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
//Load Assembly
try
{
Assembly a = Assembly.LoadFrom(Path.GetFullPath(filename));
AppDomain.CurrentDomain.Load(a.GetName());
//Try to find the type the derived plugin class
foreach (Type t in a.GetTypes())
{
if (t.IsSubclassOf(typeof(PluginBase)))
{
Console.WriteLine("Plugin class detected! {0}", t.Name);
//Load Referenced Assemblies
AssemblyName[] l = a.GetReferencedAssemblies();
loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (AssemblyName a2 in l)
{
var asm = loadedAssemblies.FirstOrDefault(a => a.FullName == a2.FullName);
if (asm == null)
{
Assembly test = null;
try
{
//First try to load using the assembly name just in case its a system dll
test = Assembly.Load(a2);
}
catch (FileNotFoundException ex)
{
try
{
Callbacks.Log($"Unable to load assembly {a2.Name}, Looking in plugin directory...", LogVerbosityLevel.WARNING);
test = Assembly.LoadFrom(Path.Join(Path.GetDirectoryName(a.Location), a2.Name + ".dll"));
} catch (Exception ex2)
{
Callbacks.Log($"Unable to load assembly {a2.Name}, Error: {ex2.Message}", LogVerbosityLevel.WARNING);
}
}
if (test != null)
{
Callbacks.Log($"Loaded Assembly {a2.Name}", LogVerbosityLevel.WARNING);
AppDomain.CurrentDomain.Load(test.GetName());
}
}
}
object c = Activator.CreateInstance(t, new object[] { this });
Plugins[Path.GetFileName(filename)] = c as PluginBase;
//Call Dll initializers
t.GetMethod("OnLoad").Invoke(c, new object[] { });
break;
}
}
}
catch (Exception ex)
{
Log("Error during loading of plugin " + filename, LogVerbosityLevel.INFO);
Log("Exception type " + ex.Data, LogVerbosityLevel.INFO);
}
}
I recently followed an example for pre-loading several DLLs from the bin folder as described here:
https://stackoverflow.com/a/5599581/1099519
This actually did work as expected, but I am using SonarLint for VisualStudio and in the following line of code is underlinded and marked as a "Code Smell":
Assembly.LoadFile(dll);
Stating the following S3885 (https://rules.sonarsource.com/csharp/RSPEC-3885):
The parameter to Assembly.Load includes the full specification of the
dll to be loaded. Use another method, and you might end up with a dll
other than the one you expected.
This rule raises an issue when Assembly.LoadFrom, Assembly.LoadFile,
or Assembly.LoadWithPartialName is called.
So I gave it a try and changed it into Assembly.Load(dll); as recommended:
private const string BinFolderName = "bin";
public static void LoadAllBinDirectoryAssemblies(string fileMask)
{
string binPath = System.AppDomain.CurrentDomain.BaseDirectory;
if(Directory.Exists(Path.Combine(binPath, BinFolderName)))
{
binPath = Path.Combine(binPath, BinFolderName);
}
foreach (string dll in Directory.GetFiles(binPath, fileMask, SearchOption.AllDirectories))
{
try
{
Assembly.Load(dll); // Before: Assembly.LoadFile
}
catch (FileLoadException ex)
{
// The Assembly has already been loaded.
}
catch (BadImageFormatException)
{
// If a BadImageFormatException exception is thrown, the file is not an assembly.
}
}
}
But using the recommended method an FileLoadException is thrown:
Could not load file or assembly 'C:\SomePath\SomeDll.dll' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
The reason: The string of Assembly.Load is not a file path, it is actually a class name such as "SampleAssembly, Version=1.0.2004.0, Culture=neutral, PublicKeyToken=8744b20f8da049e3".
Is this simply a false-positive from SonarLint or does a "compliant" way exist?
Here is how my application folders looks like:
Application:
+ App.exe
+ App.exe.config
Application/Plugins:
+ Plugin1 (folder)
Application/Plugins/Plugin1:
+ Plugin1.dll
+ SomeDll.dll
So main application App.exe looking for plugins folder and load {PluginName}.dll into memory to run it. This plugin usually uses it's own dependant assemblies which must be loaded (like SomeDll.dll). It appears that it make serious troubles sometimes. I receive exception that for example dependant assembly of dependant assembly cannot be found and I don't know why.
For example, My plugin must load lots of additional dlls becouse plugin runs OwinSelfHost service.
So it must load for example:
System.Web.Http.Owin
Owin
Microsoft.Owin
Microsoft.Owin.Host.HttpListener
Microsoft.Owin.Hosting
and when load Microsoft.Owin.Hosting then throw exception that cannot load Microsoft.Owin
Exception looks like:
Could not load file or assembly 'Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of it's dependencies. File not found.
I wrote this method to resolve assemblies. It is tweaked to fit my needs.
It basically hooks a AssemblyResolve event to the current application domain to retrieve an requested assembly from a list of directories.
There is no easy way to find where the assembly file that match the namespace to resolve, except by loading an assembly file and check to which namespace it belongs to.
Plus, it discards some unwanted assemblies (like serializers, resources...) and detects dlls or exes that are not .NET assemblies.
A better approach would consist in using the Global Assembly Cache, but we want our plugins to be fully moveable. So here it is.
public static class AssemblyResolver
{
internal static void Hook(params string[] folders)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
// Check if the requested assembly is part of the loaded assemblies
var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
if (loadedAssembly != null)
return loadedAssembly;
// This resolver is called when an loaded control tries to load a generated XmlSerializer - We need to discard it.
// http://connect.microsoft.com/VisualStudio/feedback/details/88566/bindingfailure-an-assembly-failed-to-load-while-using-xmlserialization
var n = new AssemblyName(args.Name);
if (n.Name.EndsWith(".xmlserializers", StringComparison.OrdinalIgnoreCase))
return null;
// http://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl
if (n.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
return null;
string assy = null;
// Find the corresponding assembly file
foreach (var dir in folders)
{
assy = new[] { "*.dll", "*.exe" }.SelectMany(g => Directory.EnumerateFiles(dir, g)).FirstOrDefault(f =>
{
try { return n.Name.Equals(AssemblyName.GetAssemblyName(f).Name, StringComparison.OrdinalIgnoreCase); }
catch (BadImageFormatException) { return false; /* Bypass assembly is not a .net exe */ }
catch (Exception ex) { throw new ApplicationException("Error loading assembly " + f, ex); }
});
if (assy != null)
return Assembly.LoadFrom(assy);
}
throw new ApplicationException("Assembly " + args.Name + " not found");
};
}
}
Here is how it works:
AssemblyResolver.Hook("\Plugins", "\CommonReferences");
Everytime some assemblies needs to be resolved, it will get the one that is loaded in memory, otherwise it will search in any given folders.
You can use "AssemblyResolve Event" (Method 3):
https://support.microsoft.com/en-us/kb/837908
I have a huge application where one project of my solution makes reports.
I want to add new report (update report) without building my project, just add .dll files. I read about Assembly and
AppDomain, but I don't know is it really good way to add new dll for new report and how to update old report in runtime?
Here's my example, it takes my first dll, but second time it doesn't. First dll - sum, second - deducted.
static void Main(string[] args)
{
try
{
//first domain
AppDomain domain = AppDomain.CreateDomain("MyDomain");
AssemblyDll asb1 = new AssemblyDll();
Console.WriteLine(asb1.AssemblyMethod(1));
AppDomain.Unload(domain);
Console.ReadKey();
//second domain
AppDomain newDomain = AppDomain.CreateDomain("myNewDomain");
AssemblyDll asb2 = new AssemblyDll();
Console.WriteLine(asb2.AssemblyMethod(2));
AppDomain.Unload(newDomain);
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
public class AssemblyDll
{
public string AssemblyMethod(int version)
{
//loading .dll
Assembly assembly = Assembly.LoadFrom(#"../../../../Assembly/DynamicDLL" + version + ".dll");
Type type = assembly.GetType("DynamicDLL.Dynamic");
object instance = Activator.CreateInstance(type);
MethodInfo[] methods = type.GetMethods();
//invoke method
object result = methods[0].Invoke(instance, new object[] { 5, 3 });
return result.ToString();
}
}
My .dll file comes from:
namespace DynamicDLL
{
public class Dynamic
{
public int DynamicMethod(int a, int b)
{
return a + b;
//return a - b;
}
}
}
If you want to write something like plugins and like the plugin approach, you should take a look at MEF http://msdn.microsoft.com/en/library/vstudio/dd460648.aspx
MEF allows you to use any assembly dynamically and even drop dlls into a folder and build a MEF catalog out of it.
Actually Visual Studio and uses MEF internally for extensiblility (Plugins...)
Assemblies are generally loaded into an AppDomain once and you cannot unload them once loaded.
You can create a new AppDomain and load your assemblies into this and when you release this the assemblies will be unloaded. However the caveat here is you cannot directly communicate between two AppDomain you have to marshal between the two using some other method like remoting.
There's been much wrote on this in terms of plugins and making plugins unloadable, a quick Google search presented these:
http://www.brad-smith.info/blog/archives/500
http://adrianvintu.com/blogengine/post/Unloadable-plugins.aspx
Hopefully these will aid you.
System.Data.SQLite.DLL is a mixed code DLL. It contains C and C#.
I know how to add it as Embedded Resource, write it to a temp file and use Assembly.LoadFile() to load it.
My question is there any alternative way to load it without writing it to a temp file?
I wish to combine it with EXE into single assembly.
thans for any advice
Responses to Answers:
To: Oleg Ignatov
Hi, I modified it to load like this:
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly asm = null;
AppDomain domain = (AppDomain)sender;
if (args.Name.Contains("System.Data.SQLite"))
{
try
{
asm = domain.Load(WindowsFormsApplication24.Properties.Resources.System_Data_SQLite);
}
catch (Exception ex)
{
Form f = new Form();
TextBox t = new TextBox(); t.Dock = DockStyle.Fill;
t.Multiline = true; t.ScrollBars = ScrollBars.Both;
f.Controls.Add(t);
t.Text = ex.ToString();
f.ShowDialog();
}
}
return asm;
}
It generates the exception message of this:
System.IO.FileLoadException: Could not load file or assembly 'System.Data.SQLite, Version=1.0.82.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139' or one of its dependencies. Attempt to load an unverifiable executable with fixups (IAT with more than 2 sections or a TLS section.) (Exception from HRESULT: 0x80131019)
File name: 'System.Data.SQLite, Version=1.0.82.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139' ---> System.IO.FileLoadException: Attempt to load an unverifiable executable with fixups (IAT with more than 2 sections or a TLS section.) (Exception from HRESULT: 0x80131019)
at System.Reflection.RuntimeAssembly.nLoadImage(Byte[] rawAssembly, Byte[] rawSymbolStore, Evidence evidence, StackCrawlMark& stackMark, Boolean fIntrospection, SecurityContextSource securityContextSource)
at System.AppDomain.Load(Byte[] rawAssembly)
at WindowsFormsApplication24.Program.CurrentDomain_AssemblyResolve(Object sender, ResolveEventArgs args)
This has the same result as I do it previously like this:
byte[] ba = null;
Assembly asm = null;
Assembly curAsm = Assembly.GetExecutingAssembly();
string embeddedResource = "WindowsFormsApplication24.System.Data.SQLite.dll";
using (Stream stm = curAsm.GetManifestResourceStream(embeddedResource))
{
ba = new byte[(int)stm.Length];
stm.Read(ba, 0, (int)stm.Length);
asm = Assembly.Load(ba);
}
return asm;
You can handle AssemblyResolve event and use AppDomain.Load method:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AppDomain domain = (AppDomain)sender;
if (args.Name.Contains("YourDll"))
{
return domain.Load(WindowsFormsApplication1.Properties.Resources.YourDll);
}
return null;
}
Did you check on your targeted machine is MS Visual C++ runtime redistributable installed?if not, you can dynamic link Visual C++ runtime redistributable to your project, as System.Data.SQLite is depend on Visual C++ runtime redistributable.
.Net 2 - MS Visual C++ 2005 redistributable
.Net 3 - MS Visual C++ 2008 redistributable
.Net 4 - MS Visual C++ 2010 redistributable
.Net 4.5 - MS Visual C++ 2012 redistributable