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
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.
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;
}
I'm creating a method to convert an enum to a friendly string. The friendly names are stored in a resource file and are subject to globalization. So I created two resource file: Enums.resx and Enums.pt-BR.resx whose keys are the name of the enum followed by it's value (i.e DeliveryStatus_WaitingForPayment).
This is the code I'm using to load the resource and get the corresponding friendly name for the enum:
public static string EnumToString<T>(object obj)
{
string key = String.Empty;
Type type = typeof(T);
key += type.Name + "_" + obj.ToString();
Assembly assembly = Assembly.Load("EnumResources");
string[] resourceNames = assembly.GetManifestResourceNames();
ResourceManager = null;
for(int i = 0; i < resourceNames.Length; i++)
{
if(resourceNames[i].Contains("Enums.resources"))
{
rm = new ResourceManager(resourceNames[i], Assembly.GetExecutingAssembly());
Stream resStream = assembly.GetManifestResourceStream(resourceNames[i]);
ResourceReader reader = new ResourceReader(resStream);
IDictionaryEnumerator dict = reader.GetEnumerator();
while (dict.MoveNext())
{
string keyToCompare = dict.Key.ToString();
if (keyToCompare == key)
return dict.Value.ToString();
}
}
return obj.ToString();
}
}
This method works almost perfectly except that it ignores the CurrentUICulture and always returns the values from the default resource, that is, even when I'm using pt-BR as my CurrentUICulture it will load the value from Enum.resx and not Enum.pt-BR.resx.
What am I doing wrong?
As it turns out I was taking the wrong approach to read the resource file. Not only I didn't need to work my way through a stream it was preventing me from getting the result based on the CurrentUICulture.
The solution is much easier than that of my first attempt:
public static string EnumToString<T>(object obj)
{
string key = String.Empty;
Type type = typeof(T);
key += type.Name + "_" + obj.ToString();
Assembly assembly = Assembly.Load("EnumResources");
string[] resourceNames = assembly.GetManifestResourceNames();
ResourceManager = null;
for(int i = 0; i < resourceNames.Length; i++)
{
if(resourceNames[i].Contains("Enums.resources"))
{
//The substring is necessary cause the ResourceManager is already expecting the '.resurces'
rm = new ResourceManager(resourceNames[i].Substring(0, resourceNames[i].Length - 10), assembly);
return rm.GetString(key);
}
return obj.ToString();
}
}
I hope this helps anyone trying something similar in the future!
Instead of
if (somecondition == 1)
{
int result = new myDelegate(MyClass.myMethod1);
}
else
{
int result = new myDelegate(MyClass.myMethod2);
}
Is it possible to do something like this
int result = new myDelegate("MyClass.myMethod" + i.ToString()); }
myDelegate dlg = (myDelegate)Delegate.CreateDelegate(typeof(myDelegate), this, "myMethod" + i);
You can do this via Reflection (but I don't necessarily recommend it):
string MethodName = "myMethod" + i.ToString();
Type type = MyClass.GetType();
MethodInfo methodInfo = type.GetMethod(MethodName);
int result = (int) methodInfo.Invoke(MyClass, null);
Well, needed just too long for this, but after i've done this, I'm gonna post this too ;-)
BEWARE: Reflektion is far far slower than using delegates!
Type t = typeof(MainClass);
MethodInfo mi = null;
int i = 2;
if (i==1)
{
mi = t.GetMethod("myMethod" + i.ToString());
}
else
{
mi = t.GetMethod("myMethod" + i.ToString());
}
if(mi != null)
{
mi.Invoke(new object(), new object[] {});
}
Yes, you can dynamically invoke methods using reflection. Small sample:
public class MyClass {
public delegate string MyDelegate();
public string MyMethod1() {
return "Hello";
}
public string MyMethod2() {
return "Bye";
}
}
int i;
MyClass myInstance = new MyClass();
MethodInfo method = typeof(MyClass).GetMethod("MyMethod" + i.ToString());
Delegate del = Delegate.CreateDelegate(typeof(MyClass.MyDelegate), myInstance, method);
Console.WriteLine(del()); // prints "Hello" or "Bye" contingent on value of i