issue with loading .dll lib in C# code - c#

I have found a few posts about loading .dll libraries directly into C# projects through code. Here is the current code I am using:
if (System.IO.File.Exists(dir + lib + ".dll"))
{
Assembly type = Assembly.LoadFrom(dir + lib + ".dll");
object obj1 = type.CreateInstance(_namespace);
assm.Add(obj1);
return assm.Count - 1;
}
else
{
Console.WriteLine(dir + lib + ".dll: file does not exist");
return -1;
}
The code with:
Assembly type = Assembly.LoadFrom(dir + lib + ".dll");
object obj1 = type.CreateInstance(_namespace);
is suppose to locate the .dll file and load it as an instance then grabbing the class and creating an object from it. So I could make a library that has one function to print hello and I then load it into the code, I could then access that function by creating an Instance of the class. The problem is when I do the code:
object obj1 = type.CreateInstance(_namespace);
obj1 is null, I know to give the namspace to the function (which I double checked and is correct), but no matter what it is always null. I checked to see if the Library is being loaded in as an assembly and it is! Here is the details of the assembly when I do a breakpoint (hovering over the object type):
{ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null}
Thank you in advance for your help, I am not sure what is wrong with my code.

What is _namespace? It must contents full name of class (with namespace), for example System.Collections.ArrayList

Related

How to instantiate IModule-inheriting class from an AppDomain?

I'm a bit confused here and haven't gotten much help from google. Here's what I'm trying to do:
public Boolean LoadModule(String moduleHandle)//name of module MUST match its .dll name. Name of AppDomain is the same as the Handle.
{
try
{
AppDomain moduleDomain = AppDomain.CreateDomain(moduleHandle);
String pathToDll = #"C:\IModules.dll"; //Full path to dll you want to load
Type moduleType = typeof(IModule);
IModule loadedModule = (IModule)moduleDomain.CreateInstanceFromAndUnwrap(pathToDll, moduleType.FullName);
ModuleList.Add(loadedModule, moduleDomain);
Broadcast("Module loaded: " + moduleHandle, ModuleManagerHandle);
return true;
}
catch (Exception e)
{
//console writeline the error? probably cant
OutputBox.AppendText(e.ToString() + Environment.NewLine);
return false;
}
}
I thought I finally had this figured out but when I try to instantiate the IModule (ConsoleModule, in this case), I get the following error:
System.MissingMethodException: Constructor on type 'IModules.IModule' not found.
I take this to mean that I need to have a constructor, as if this were a class object instantiating itself on this function call, but I cannot make an interface have a constructor.
I have seen other threads suggesting ways to solve this problem, but they use assembly instead of appdomain, which will mess up the ability to unload modules. I'm concerned that without the ability to unload modules the application will suffer memory bloating over time.
The end goal is to be able to write a module, leave the program running and load/unload the modules during runtime without any changes to the core program, and add functionality on the go.
Anyone know of a workaround or maybe a better way to deal with dynamic module loading and unloading?
This is fixed with .NET 5.0 AssemblyLoadContext:
var basePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
AssemblyLoadContext moduleAssemblyLoadContext = new AssemblyLoadContext(moduleHandle, true);
Assembly moduleAssembly = moduleAssemblyLoadContext.LoadFromAssemblyPath($"{basePath}\\{moduleHandle}.dll");
Type[] types = moduleAssembly.GetTypes();
foreach (Type type in types)
{
// Does this class support the transport interface?
Type typeModule = type.GetInterface("IModule");
if (typeModule == null)
{
// Not supported.
continue;
}
// This class supports the interface. Instantiate it.
IModule loadedModule = moduleAssembly.CreateInstance(type.FullName) as IModule;
if (loadedModule != null)
{
loadedModule.LoadedModule(this);
ModuleList.Add(loadedModule, moduleAssemblyLoadContext);
Broadcast("Module loaded: " + moduleHandle, ModuleManagerHandle);
OutputTextBox.AppendText(moduleHandle + " was loaded." + Environment.NewLine);
// Successfully created the interface. We are done.
return true;
}
}
return false;
Can't find the source anymore but found it looking for a related problem (you can find it on MSDN anyways). This successfully loads and unloads assemblies into their context. User must set the isCollectible value to TRUE to enable full unloading.
Only issue I had is that .NET 5.0 is not compatible -with itself- yet and libraries loaded as .NET 5.0 into .NET 5.0 programs will give a BadImageFormatException when trying to load the assembly. To fix, set the LIBRARY to the next most recent target framework (in my case, .NET Core 3.1) and move the newly compiled dll to wherever it goes and the application should run using the new dll.
The error tells you that there is no default (empty) constructor found for the type IModule. Since IModule is an interface, the message seems to make some sense.
Resulotion: Instantiate a class that implements IModule. An interface can never be intantiated on its own.
To instantiate the class, just one line neds to be changed:
Type moduleType = typeof(ClassThatImplementIModule);
You can still cast the instance to IModule

How can I reference external project DLL with ScriptOptions?

I have a project A in which I am writing code to be compiled internally using C# Script objects.
I have created another project in a different solution (external project), project B. I have already added a reference to the ProjectB.DLL inside Project A in the "references" list. What I want is to add Project B's reference on the internal Script code that I want to compile. To better explain, my code is as follows:
My "using statements" are:
APPROVED_USING_STATEMENTS = new HashSet<string>();
APPROVED_USING_STATEMENTS.Add("System.Text");
APPROVED_USING_STATEMENTS.Add("System.Data");
APPROVED_USING_STATEMENTS.Add("System.Text.RegularExpressions");
APPROVED_USING_STATEMENTS.Add("System");
APPROVED_USING_STATEMENTS.Add("System.Linq");
APPROVED_USING_STATEMENTS.Add("System.Collections");
APPROVED_USING_STATEMENTS.Add("System.Security.Cryptography");
APPROVED_USING_STATEMENTS.Add("Project.ProjectB");
The compiler code is as follows:
foreach (var apprvUsingStatements in DataTransformationScript.GetApprovedUsingStatements())
{
usingStatements.Append("using " + apprvUsingStatements + "; \n");
}
string transformCode = usingStatements.ToString();
transformCode += "object returnObject = currentData;";
transformCode += schemaMap.transform_script;
transformCode += ";returnObject";
ScriptOptions references = ScriptOptions.Default.AddReferences(DataTransformationScript.GetApprovedUsingStatements());
//Adding my own personal references from Connector
var asm = typeof(Project.ProjectB.TestTransformation).Assembly;
references.AddReferences(asm);
//HTMLEncodeLogMessage.Info(Logger, "Assmebly added: " + asm.FullName + " Location " + asm.Location + " Given Name " + asm.GetName().Name);
//===============================================//
var transformScript = CSharpScript.Create(transformCode, references, globalsType: typeof(DataTransformationScript.Globals));
transformScript.Compile();
As you can see, I add the reference to Project B using the following code extract:
var asm = typeof(Project.ProjectB.TestTransformation).Assembly;
references.AddReferences(asm);
And I already have the using statement above for project as "using Project.ProjectB". (TestTransformation is just a method inside ProjectB. It seems I had to use the method name as well to get the reference dll.
I am still getting an error: The type or namespace name 'Project' could not be found (are you missing a using directive or an assembly reference?).
Am I missing any other kind of reference that I need to add? I noticed I am not getting this error for all the "System" references that I have. Is it possible that C# Script objects only get data from the folders inside C:\Windows\assembly?
I have looked at the following stackoverflow question and it DID NOT solve my problem:
Link
I would really appreciate any kind of guidance and assistance. I am still learning.
Try using costura fody/ fody library

Activator.CreateInstance throws Exception "The system cannot find the file specified."

I've copied code for an assembly that is used in a solution to create a similar assembly. The GUTS was different, but the shell stayed the same.
These assemblies are used in a project at a client that are add-on's and not part of our core code. Now that I'm finished the assembly does not want to load like it is supposed to.
The code that loads the assembly is
var assemblyName = ((XmlElement)xmlDoc.GetElementsByTagName("AssemblyName")[0]).InnerText;
var qualifiedClass = ((XmlElement)xmlDoc.GetElementsByTagName("QualifiedClass")[0]).InnerText;
IExternalAddOn addOn = (IExternalAddOn)Activator.CreateInstance(assemblyName, qualifiedClass).Unwrap();
var properties = new Dictionary<Type, object>();
properties[typeof(DevExpress.XtraBars.Ribbon.RibbonControl)] = mainForm.ribbon;
var form = addOn.ShowForm(properties);
if (form != null)
{
form.MdiParent = mainForm;
form.Text = pListRow.NAME;
form.Show();
I get the exception on the CreateInstance part.
The interesting thing is that when I use
Assembly ass = Assembly.LoadFrom(assemblyName); // this is test code
Type at = ass.GetType(qualifiedClass);
IExternalAddOn addOn = (IExternalAddOn)Activator.CreateInstance(at);
to load the assembly and get the type, and CreateInstance it works.
Why do you need the Unwrap? Is there a difference in the two different ways of loading? And WHY does the first one not work?
Thanks
J
As soon as Assembly.LoadFrom works, I guess you are passing a file name as an assemblyName parameter, which is wrong in case of Activator.CreateInstance(assemblyName, qualifiedClass).
According to Activator.CreateInstance documentation:
assemblyName can be either of the following:
The simple name of an assembly, without its path or file extension. For example, you would specify TypeExtensions for an assembly whose path and name are .\bin\TypeExtensions.dll.
The full name of a signed assembly, which consists of its simple name, version, culture, and public key token; for example, "TypeExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=181869f2f7435b51".

GetExportedTypes() FileNotFoundException: Assembly couldn't be found

My task: Find all Forms (WindowsForm or WPF, doesn't matter) in a dll or exe file and return that list. In theory that works (meaning: if I've an assembly with a WPF or WindowsForm my code manages to get all Forms, TextBoxes, Labels etc. ). When it comes to "real" assemblies it fails. I get FileNotFound exceptions when calling GetExportedTypes() for every "custom" assembly (.NET assemblies are found, no problems there). I already use GetReferencedAssemblies() to load the referenced assemblies (Reflection.Assembly.LoadFrom) and yes it does work (all assemblies are found and loaded into the AppDomain) but it doesn't help.
I checked the version numbers (they match), I copied my executable and the assembly into one directory with all referenced assemblies, doesn't work.
Here is my code, maybe someone figures out what I'm (obviously) doing wrong:
foreach (AssemblyName reference in selectedAssembly.GetReferencedAssemblies())
{
if (System.IO.File.Exists(
System.IO.Path.GetDirectoryName(selectedAssembly.Location) +
#"\" + reference.Name + ".dll"))
{
System.Reflection.Assembly.LoadFrom(
System.IO.Path.GetDirectoryName(selectedAssembly.Location) +
#"\" + reference.Name + ".dll");
}
else if (System.IO.File.Exists(#"C:\dll\" + reference.Name + ".dll"))
{
System.Reflection.Assembly.LoadFrom(#"C:\dll\" + reference.Name + ".dll");
}
else
{
System.Reflection.Assembly.ReflectionOnlyLoad(reference.FullName);
}
selectedAssembly.GetExportedTypes();
}
at first check if the referenced dll exists in the directory where the assembly is, if not check if it exists in C:\dll and if it's not there try and use the GAC. it does work and I've no errors from there but as soon as I come to GetExportedTypes it fails with a FileNotFound exception on the first custom library.
*edit 1 what do I mean by "real assemblies": I mean assemblies which are more complex and have references to non-standard-.NET libraries/assemblies
Thanks for the hint to fuslogvw.exe Hans Passant but what do you mean by "with code like this"?
okay I used fuslogvw.exe and I get two exceptions for every single dll that is referenced by the "selectedAssembly".
The first one says something like
"The binding starts in LoadFrom-context
The image owned by the system isn't searched in LoadFrom-Context"
the other logentry says that the dll referenced by the selectedAssembly couldn't be found and it tried to download it from application's base path and all directories below...but not from it's actual location...so, key question: how do I change the Load-context to LoadFrom? And why is .NET so stubborn on this? I mean the assemblies are loaded in the AppDomain, it shouldn't care about the actual location of the assembly.
okay problem solved. Here is the solution:
http://ayende.com/blog/1376/solving-the-assembly-load-context-problem
I implemented that into my existing class (removed the static-keyword and put the body of the Init method into my method), compiled it and it worked.
Thanks for your help guys.
okay problem solved. Here is the solution: http://ayende.com/blog/1376/solving-the-assembly-load-context-problem
I implemented that into my existing class (removed the static-keyword and put the body of the Init method into my method), compiled it and it worked.
Thanks for your help guys.
just in case the website will someday be unavailable, here is the sourcecode from ayende
static Dictionary<string, Assembly>assemblies;
public static void Init()
{
assemblies = new Dictionary<string, Assembly>();
AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly assembly = null;
assemblies.TryGetValue(args.Name, out assembly);
return assembly;
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Assembly assembly = args.LoadedAssembly;
assemblies[assembly.FullName] = assembly;
}
I would recommend using Reflector to see which references you may not have loaded. For instance, you are only loading the referenced assemblies that the current assembly is looking at. Do you step down through each child to find their referenced assemblies as well? The FileNotFound error is probably pointing you in the direction of a type that is declared in another assembly that isn't loaded.

could not load dll or one of its dependency

a. My C# program will load a dll (which is dynamic), for now let's take a.dll (similarly my program will load more dll like b.dll, c.dll, etc....).
b. My program will invoke a method "Onstart" inside a.dll (it's constant for all the dll).
I am able to achieve the above 2 cases by reflection mechanism.
The problem is
a. If my a.dll have any reference say xx.dll or yy.dll, then when I try to Invoke
OnStart method of a.dll from my program. I am getting "could not load dll or one of its dependency".
See the code snippet
Assembly assm = Assembly.LoadFrom(#"C:\Balaji\Test\a.dll");
foreach (Type tp in assm.GetTypes())
{
if (tp.IsClass)
{
MethodInfo mi = tp.GetMethod("OnStart");
if (mi != null)
{
object obj = Activator.CreateInstance(tp);
mi.Invoke(obj,null);
break;
}
}
}
typically i am getting error on the line "object obj = Activator.CreateInstance(tp);" this is because a.dll has reference of xx.dll, but in my program i don't have the reference of xx.dll. Also, I cannot have the reference of xx.dll in my program because a.dll is a external assembly and can have any reference on it's own.
Kinldy help!!!
Have a look at this: http://bytes.com/topic/c-sharp/answers/232691-how-dynamically-load-assembly-w-dependencies. Basically, in the AssemblyResolve event, you need to load the referenced assemblies manually.
private Assembly AssemblyResolveHandler(object sender,ResolveEventArgs e)
{
try
{
string[] assemblyDetail = e.Name.Split(',');
string assemblyBasePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
Assembly assembly = Assembly.LoadFrom(assemblyBasePath + #"\" + assemblyDetail[0] + ".dll");
return assembly;
}
catch (Exception ex)
{
throw new ApplicationException("Failed resolving assembly", ex);
}
}
Not the best code, but should give you a general idea, I hope.
I do, however, agree that plugin-dlls should be packaged for complete, dependancy-less use. If they are allowed to load assemblies you don't have control over, then who knows what might happen.
Perhaps the second DLL reference isn't available to your application?
Make sure the second DLL is in the same directory as the first DLL or that the application is configured to look in a directory that does have the second DLL.
I think you can not do anything other than adding all references which are used .
ps : usually an external assembly should be complete for use (or the package will contain all needed assemblies so you can add them)
I think, it needs more explanation. Let me explain....
a. My C# program will load a dll (which is dynamic), for now let's take a.dll (similarly more dll like b.dll, c.dll, etc....).
b. My program will invoke a method "Onstart" (it's constant for all the dll) inside a.dll.
I am able to achieve the above 2 cases by reflection mechanism.
The problem is
a. If my a.dll have any reference say xx.dll or yy.dll, then when I try to Invoke
OnStart method of a.dll from my progra. I am getting "could not load dll or one of its dependency".
See the code snippet
Assembly assm = Assembly.LoadFrom(#"C:\Balaji\Test\a.dll");
foreach (Type tp in assm.GetTypes())
{
if (tp.IsClass)
{
MethodInfo mi = tp.GetMethod("OnStart");
if (mi != null)
{
object obj = Activator.CreateInstance(tp);
mi.Invoke(obj,null);
break;
}
}
}
Typically I am getting error on the line "object obj = Activator.CreateInstance(tp);"
this is because a.dll has reference of xx.dll, but I cannot have the same.
Also, I cannot have the reference of xx.dll in my program, because a.dll is dynamic and can have any reference on it's own.

Categories