Load multiple methods from dll at runtime C# - c#

I am working on "plugin" system to my program. I want to redistribute multiple dll's for example: "myAwesomePlugin1.dll", "myAwesomePlugin2.dll". Each of these files have method called for example: "doSmthAwesome(string msg)".
Now, when I have all prepared I want to load these two methods and run them:
1st doSmthAwesome from Plugin1
2nd doSmthAwesome from Plugin2
Unfortunatelly, I don't know why, but when I load these dll's, second one copies method from first dll. How I can do it in proper way, to have both these methods?
Heres my code:
string doSmthAwesomeFrom1(string msg)
{
string value = "";
try
{
Assembly a = null;
a = Assembly.LoadFrom(pluginDirectory + "/" + IkarosConfiguration.getValueFromKey("FIRST_PLUGIN"));
Type classType = a.GetType("awesomePlugin.awesomePlugin");
MethodInfo mi = classType.GetMethod("doSmthAwesome");
object obj = Activator.CreateInstance(classType);
value = mi.Invoke(obj, new object[] { msg});
}
catch
{
}
return value;
}
string doSmthAwesomeFrom2(string msg)
{
string value = "";
try
{
Assembly a = null;
a = Assembly.LoadFrom(pluginDirectory + "/" + IkarosConfiguration.getValueFromKey("SECOND_PLUGIN"));
Type classType = a.GetType("awesomePlugin.awesomePlugin");
MethodInfo mi = classType.GetMethod("doSmthAwesome");
object obj = Activator.CreateInstance(classType);
value = mi.Invoke(obj, new object[] { msg});
}
catch
{
}
return value;
}

Related

error reflection and Abstract class, call AppDomain.CurrentDomain.GetAssemblies() [duplicate]

I am trying to invoke a method via reflection with parameters and I get:
object does not match target type
If I invoke a method without parameters, it works fine. Based on the following code if I call the method Test("TestNoParameters"), it works fine. However if I call Test("Run"), I get an exception. Is something wrong with my code?
My initial purpose was to pass an array of objects e.g. public void Run(object[] options) but this did not work and I tried something simpler e.g. string without success.
// Assembly1.dll
namespace TestAssembly
{
public class Main
{
public void Run(string parameters)
{
// Do something...
}
public void TestNoParameters()
{
// Do something...
}
}
}
// Executing Assembly.exe
public class TestReflection
{
public void Test(string methodName)
{
Assembly assembly = Assembly.LoadFile("...Assembly1.dll");
Type type = assembly.GetType("TestAssembly.Main");
if (type != null)
{
MethodInfo methodInfo = type.GetMethod(methodName);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(methodInfo, parametersArray);
}
}
}
}
}
Change "methodInfo" to "classInstance", just like in the call with the null parameter array.
result = methodInfo.Invoke(classInstance, parametersArray);
You have a bug right there
result = methodInfo.Invoke(methodInfo, parametersArray);
it should be
result = methodInfo.Invoke(classInstance, parametersArray);
A fundamental mistake is here:
result = methodInfo.Invoke(methodInfo, parametersArray);
You are invoking the method on an instance of MethodInfo. You need to pass in an instance of the type of object that you want to invoke on.
result = methodInfo.Invoke(classInstance, parametersArray);
The provided solution does not work for instances of types loaded from a remote assembly. To do that, here is a solution that works in all situations, which involves an explicit type re-mapping of the type returned through the CreateInstance call.
This is how I need to create my classInstance, as it was located in a remote assembly.
// sample of my CreateInstance call with an explicit assembly reference
object classInstance = Activator.CreateInstance(assemblyName, type.FullName);
However, even with the answer provided above, you'd still get the same error. Here is how to go about:
// first, create a handle instead of the actual object
ObjectHandle classInstanceHandle = Activator.CreateInstance(assemblyName, type.FullName);
// unwrap the real slim-shady
object classInstance = classInstanceHandle.Unwrap();
// re-map the type to that of the object we retrieved
type = classInstace.GetType();
Then do as the other users mentioned here.
I tried to work with all the suggested answers above but nothing seems to work for me. So i am trying to explain what worked for me here.
I believe if you are calling some method like the Main below or even with a single parameter as in your question, you just have to change the type of parameter from string to object for this to work. I have a class like below
//Assembly.dll
namespace TestAssembly{
public class Main{
public void Hello()
{
var name = Console.ReadLine();
Console.WriteLine("Hello() called");
Console.WriteLine("Hello" + name + " at " + DateTime.Now);
}
public void Run(string parameters)
{
Console.WriteLine("Run() called");
Console.Write("You typed:" + parameters);
}
public static string StaticString()
{
return "static string example";
}
public string TestNoParameters()
{
Console.WriteLine("TestNoParameters() called");
return ("TestNoParameters() called");
}
public void Execute(object[] parameters)
{
Console.WriteLine("Execute() called");
Console.WriteLine("Number of parameters received: " + parameters.Length);
for(int i=0;i<parameters.Length;i++){
Console.WriteLine(parameters[i]);
}
}
}
}
Then you have to pass the parameterArray inside an object array like below while invoking it. The following method is what you need to work
private object ExecuteWithReflection(string methodName,object parameterObject = null)
{
Assembly assembly = Assembly.LoadFile("Assembly.dll");
Type typeInstance = assembly.GetType("TestAssembly.Main");
MethodInfo methodInfo = typeInstance.GetMethod(methodName);
ParameterInfo[] parameterInfo = methodInfo.GetParameters();
object result = null;
if (typeInstance != null) //non static
{
if(methodInfo.IsStatic == false)
{
//instance is needed to invoke the method
object classInstance = Activator.CreateInstance(typeInstance, null);
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(classInstance, null);
}
else
{
result = methodInfo.Invoke(classInstance,new object[] { parameterObject } );
}
}
else //handle static
{
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(null, null);
}
else
{
result = methodInfo.Invoke(null,new object[] { parameterObject } );
}
}
}
return result;
}
This method makes it easy to invoke the method, it can be called as following
ExecuteWithReflection("Hello");
ExecuteWithReflection("Run","Vinod");
ExecuteWithReflection("TestNoParameters");
ExecuteWithReflection("Execute",new object[]{"Vinod","Srivastav"});
ExecuteWithReflection("StaticString");
I'am posting this answer because many visitors enter here from google for this problem.
string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );
when external .dll -instead of this.GetType(), you might use typeof(YourClass).
I would use it like this, its way shorter and it won't give any problems
dynamic result = null;
if (methodInfo != null)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
result = methodInfo.Invoke(classInstance, parameters.Length == 0 ? null : parametersArray);
}
Assembly assembly = Assembly.LoadFile(#"....bin\Debug\TestCases.dll");
//get all types
var testTypes = from t in assembly.GetTypes()
let attributes = t.GetCustomAttributes(typeof(NUnit.Framework.TestFixtureAttribute), true)
where attributes != null && attributes.Length > 0
orderby t.Name
select t;
foreach (var type in testTypes)
{
//get test method in types.
var testMethods = from m in type.GetMethods()
let attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true)
where attributes != null && attributes.Length > 0
orderby m.Name
select m;
foreach (var method in testMethods)
{
MethodInfo methodInfo = type.GetMethod(method.Name);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(classInstance, parametersArray);
}
}
}
}
I m invoking the weighted average through reflection. And had used method with more than one parameter.
Class cls = Class.forName(propFile.getProperty(formulaTyp));// reading class name from file
Object weightedobj = cls.newInstance(); // invoke empty constructor
Class<?>[] paramTypes = { String.class, BigDecimal[].class, BigDecimal[].class }; // 3 parameter having first is method name and other two are values and their weight
Method printDogMethod = weightedobj.getClass().getMethod("applyFormula", paramTypes); // created the object
return BigDecimal.valueOf((Double) printDogMethod.invoke(weightedobj, formulaTyp, decimalnumber, weight)); calling the method
On .Net 4.7.2 to invoke a method inside a class loaded from an external assembly you can use the following code in VB.net
Dim assembly As Reflection.Assembly = Nothing
Try
assembly = Reflection.Assembly.LoadFile(basePath & AssemblyFileName)
Dim typeIni = assembly.[GetType](AssemblyNameSpace & "." & "nameOfClass")
Dim iniClass = Activator.CreateInstance(typeIni, True)
Dim methodInfo = typeIni.GetMethod("nameOfMethod")
'replace nothing by a parameter array if you need to pass var. paramenters
Dim parametersArray As Object() = New Object() {...}
'without parameters is like this
Dim result = methodInfo.Invoke(iniClass, Nothing)
Catch ex As Exception
MsgBox("Error initializing main layout:" & ex.Message)
Application.Exit()
Exit Sub
End Try

How to get memory base address of a c# assembly loaded in a different AppDomain

You can get the memory base address of the modules loaded on your process with some code like this one below:
ProcessModuleCollection pc = Process.GetCurrentProcess().Modules;
foreach(ProcessModule pm in pc)
{
Console.WriteLine("The moduleName is " + pm.ModuleName);
Console.WriteLine("The " + pm.ModuleName + "'s base address is: " + pm.BaseAddress);
}
Unfortunately, this is not working with the scenario that I have. What I'm doing is to create a new and separated AppDomain (called Temporal) and using it to load an assembly from disk. Then, I just execute some function contained in the loaded assembly. This would be the code of the class that contains all this logic:
public class Loader : MarshalByRefObject
{
object CallInternal(string dll, string method, string[] parameters)
{
byte[] buffer = File.ReadAllBytes(dll);
Assembly a = Assembly.Load(buffer);
Type[] types = a.GetTypes();
MethodInfo m = null;
Type myType = null;
foreach (var type in types)
{
m = type.GetMethod(method);
if (m != null)
{
myType = type;
break;
}
}
if (m != null && myType != null)
{
var myInstance = Activator.CreateInstance(myType);
return m.Invoke(myInstance, new object[] { parameters });
}
else
{
return null;
}
}
public static object Call(string dll, string method, params string[] parameters)
{
AppDomain dom = AppDomain.CreateDomain("Temporal");
Loader ld = (Loader)dom.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName);
object result = ld.CallInternal(dll, typename, method, parameters);
return result;
}
The question is, is there any way to get the memory base address of the loaded assembly without executing some sort of memory scan? The ProcessModuleCollection that you get from Process.GetCurrentProcess().Modules is not showing the assembly loaded in the AppDomain Temporal. Unsafe and unmanaged code allowed.

Dynamic Compilation NullReferenceException error

I'll try to define as mush as possible my problem and forget nothing.
For my project, which use a webRequest, I would like to compile dynamically my webRequest.
For this I used the CodeDomProvider in a Private Assembly and a public MethodInfo who gives me back a " method" that I can use in my main program.
So the main problem is that in my CompileCode, my MethodInfo method = type.getMethod(functionname); gives me a NullReferenceException error. I know it's because my type.getMethod(functionname) can't work on a type which is null. I tried to modify the fact that my object instance and Type type are not null, but I can't give them values because of their gender and I get stuck in the fact that they stay null and gives me no values...
I also saw that lot of people used Linq, but as I am compiling a whole .cs file, I can't write it all like this with the #"using System.Linq;";
So here are the partial code were the problem is :
Thank you
namespace testCodeCompiler
{
public class CodeCompiler
{
public CodeCompiler()
{
}
public MethodInfo CompileCode(string code, string namespacename, string classname,string functionname, bool isstatic, params object[] args)
{
Assembly asm = BuildAssembly(code);
object instance = null;
Type type = null;
if (isstatic)
{
type = asm.GetType(namespacename + "." + classname);
}
else
{
instance = asm.CreateInstance(namespacename + "." + classname);
type = instance.GetType();
}
MethodInfo method = type.GetMethod(functionname); // here is the error
return method;
}
private Assembly BuildAssembly(string code)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters compilerparams = new CompilerParameters();
compilerparams.GenerateExecutable = false;
compilerparams.GenerateInMemory = true;
compilerparams.ReferencedAssemblies.Add("System.dll");
compilerparams.ReferencedAssemblies.Add("System.Xml.dll");
System.Reflection.Assembly currentAssembly = System.Reflection.Assembly.GetExecutingAssembly();
compilerparams.ReferencedAssemblies.Add(currentAssembly.Location);
CompilerResults results = provider.CompileAssemblyFromSource(compilerparams, code);
if (results.Errors.HasErrors)
{
StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
foreach (CompilerError error in results.Errors )
{
errors.AppendFormat("Line {0},{1}\t: {2}\n", error.Line, error.Column, error.ErrorText);
}
throw new Exception(errors.ToString());
}
else
{
return results.CompiledAssembly;
}
}
}
And a little part of the Main() :
static void Main(string[] args)
{
MethodInfo method;
[// Little bit of code ]
StreamReader sr = new StreamReader(#"c:\pathtothefileIwant\File.cs", System.Text.Encoding.Default);
string file = sr.ReadToEnd();
sr.Close();
CodeCompiler cc = new CodeCompiler();
object[] arguments = { popup }; // here are the args which are in another class
[...little bit of code...]
method = cc.CompileCode(file, "testNamespace", "Class1", "webRequest", true, arguments);
List<Test> li = (List<Test>)method.Invoke(null, arguments); // Here I invoke the method to put it in a List<Compte> I made before.
}
The problem was in the class Program
Main(){ method = cc.CompileCode(file, "testNamespace", "Class1", "webRequest", true, arguments);}
The string classname was not the good one and didn't pointed to the real document I wanted to compile. The good path was method = cc.CompileCode(file,"testNamespace", "WebRequest","webRequest", true, arguments);}
That's why the Type type; couldn't get something instead of null.

Obtaining method signatures types and names

I'm writing a 'quick' c# application that will allow me to see all the Methods avaliable under an Assembly DLL, so far all is well and i can output the class, access modifier and name :
private void btnListMethodName()
{
string sAssemblyFileName = assemblyLocation.Text;
if (sAssemblyFileName.Length != 0)
{
Assembly assem = Assembly.LoadFrom(sAssemblyFileName);
Type[] types = assem.GetTypes();
ArrayList arrl = new ArrayList();
foreach (Type cls in types)
{
try
{
//Add Class Name
arrl.Add(cls.FullName);
if (cls.IsAbstract)
arrl.Add("Abstract Class:" + cls.Name.ToString());
else if (cls.IsPublic)
arrl.Add("Public Class:" + cls.Name.ToString());
else if (cls.IsSealed)
arrl.Add("Sealed Class:" + cls.Name.ToString());
MemberInfo[] methodName = cls.GetMethods();
foreach (MemberInfo method in methodName)
{
method.ReflectedType.GetProperties();
if (method.ReflectedType.IsPublic)
arrl.Add("\tPublic - " + method.Name.ToString());
else
arrl.Add("\tNon-Public - " + method.Name.ToString());
}
}
catch (System.NullReferenceException)
{
Console.WriteLine("Error msg");
}
}
olvMain.SetObjects(arrl);
for (int i = 0; i < arrl.Count; i++)
{
AssemblyList.Items.Add(arrl[i].ToString());
}
}
}
What i now need to output is the datatype and name of the methods signature values. For example, at the moment i output 'Public - methodName' what i would like to output is 'Public - methodName(string methodData, int methodData2)'
Is this possible?
You can obtain a method's parameters like this;
ParameterInfo[] pars = method.GetParameters();
To do that you need to change;
MemberInfo[] methodName = cls.GetMethods();
To
MethodInfo[] methodName = cls.GetMethods();
Refer MethodBase.GetParameters
Well, you can use
method.GetParameters()
Wich will return an array of ParameterInfo.
Then, ParameterInfo has a Name and a ParameterType (which has a Name) property.

ICSharpCode.Decompiler + Mono.Cecil -> How to generate code for a single method?

I'm able to use Mono.Cecil and ICSharpCode.Decompiler to generate the code for a type or an assembly.
But if I try to generate the code for a single method I'll get an error "Object reference not set to an instance of an object."
Can you guys give me any hints about this? Thanks ahead for all the help.
Code to generate code for all the types inside an assembly:
DirectoryInfo di = new DirectoryInfo(appPath);
FileInfo[] allAssemblies = di.GetFiles("*.dll");
foreach (var assemblyFile in allAssemblies)
{
string pathToAssembly = assemblyFile.FullName;
System.Reflection.Assembly assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(pathToAssembly);
Mono.Cecil.AssemblyDefinition assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly,parameters);
AstBuilder astBuilder = null;
foreach (var typeInAssembly in assemblyDefinition.MainModule.Types)
{
if (typeInAssembly.IsPublic)
{
Console.WriteLine("T:{0}", typeInAssembly.Name);
//just reset the builder to include only code for a single type
astBuilder = new AstBuilder(new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
astBuilder.AddType(typeInAssembly);
StringWriter output = new StringWriter();
astBuilder.GenerateCode(new PlainTextOutput(output));
string result = output.ToString();
output.Dispose();
}
}
}
Code to generate code for all the public methods inside an assembly:
DirectoryInfo di = new DirectoryInfo(appPath);
FileInfo[] allAssemblies = di.GetFiles("*.dll");
foreach (var assemblyFile in allAssemblies)
{
string pathToAssembly = assemblyFile.FullName;
System.Reflection.Assembly assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(pathToAssembly);
Mono.Cecil.AssemblyDefinition assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly,parameters);
AstBuilder astBuilder = null;
foreach (var typeInAssembly in assemblyDefinition.MainModule.Types)
{
if (typeInAssembly.IsPublic)
{
Console.WriteLine("T:{0}", typeInAssembly.Name);
foreach (var method in typeInAssembly.Methods)
{
//just reset the builder to include only code for a single method
astBuilder = new AstBuilder(new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
astBuilder.AddMethod(method);
if (method.IsPublic && !method.IsGetter && !method.IsSetter && !method.IsConstructor)
{
Console.WriteLine("M:{0}", method.Name);
StringWriter output = new StringWriter();
astBuilder.GenerateCode(new PlainTextOutput(output));
string result = output.ToString();
output.Dispose();
}
}
}
}
}
I had the same problem. You should set the property CurrentType of the DecompilerContext. Change your code to
astBuilder = new AstBuilder(new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule) { CurrentType = typeInAssembly } );
When I recently implemented a quick C# decompiler (MonoDecompiler based), I used the ILSpy methods :)
public string getSourceCode(MethodDefinition methodDefinition)
{
try
{
var csharpLanguage = new CSharpLanguage();
var textOutput = new PlainTextOutput();
var decompilationOptions = new DecompilationOptions();
decompilationOptions.FullDecompilation = true;
csharpLanguage.DecompileMethod(methodDefinition, textOutput, decompilationOptions);
return textOutput.ToString();
}
catch (Exception exception)
{
PublicDI.log.error("in getSourceCode: {0}", new object[] { exception.Message });
return ("Error in creating source code from IL: " + exception.Message);
}
}
For this and other examples see:
https://github.com/o2platform/O2.Platform.Scripts/blob/master/3rdParty/MonoCecil/CecilDecompiler/CecilDecompiler.cs
The stand-alone mini C# decompilation tool is created by this script https://github.com/o2platform/O2.Platform.Scripts/blob/master/3rdParty/MonoCecil/Utils/Tool%20-%20C%23%20Quick%20Decompiler.h2

Categories

Resources