Generate Emit statements from IL - c#

I want to add method body without going through Emit statements. Similar to:
public static void Replicate<T>(this MethodBuilder methodBuilder, Func<T> func)
{
var body = func.Method.GetMethodBody();
var instructions = body.GetILAsByteArray();
methodBuilder.CreateMethodBody(instructions, instructions.Length);
}
var methodBuilder = typeBuilder.DefineMethod("GetExtensionCollection", MethodAttributes.Public, typeof(Stream), Type.EmptyTypes);
methodBuilder.Replicate(() =>
{
var assembly = Assembly.GetExecutingAssembly();
return assembly.GetManifestResourceStream(assembly.GetName().Name + ".Extensions.xml");
});
When I decompile the generated assembly using dnSpy, the IL code is completely wrong, and the C# code throws exception.
I have also tried the following, based on Create a copy of method from IL and I have modified it to use Mono.Reflection:
public static void Replicate<T>(this MethodBuilder methodBuilder, Func<T> func)
{
var il = methodBuilder.GetILGenerator();
foreach (var local in func.Method.GetMethodBody().LocalVariables)
il.DeclareLocal(local.LocalType);
foreach (var instrcustion in func.Method.GetInstructions())
{
if (instrcustion.OpCode.OperandType == OperandType.InlineBrTarget)
{
il.Emit(instrcustion.OpCode, Convert.ToInt32(instrcustion.Operand));
continue;
}
if (instrcustion.OpCode.OperandType == OperandType.ShortInlineBrTarget)
{
continue;
}
if (instrcustion.OpCode.OperandType == OperandType.InlineString)
{
il.Emit(instrcustion.OpCode, instrcustion.Operand.ToString());
continue;
}
if (instrcustion.OpCode.OperandType == OperandType.InlineType)
{
il.Emit(instrcustion.OpCode, instrcustion.Operand as Type);
continue;
}
if (instrcustion.OpCode.FlowControl == FlowControl.Call)
{
var methodInfo = instrcustion.Operand as MethodInfo;
if (methodInfo == func.Method)
il.Emit(instrcustion.OpCode, methodBuilder);
else
il.Emit(instrcustion.OpCode, methodInfo);
continue;
}
il.Emit(instrcustion.OpCode);
}
}
This one works, but I need:
Preferably get the first Replicate to work
If not successful, build a comprehensive version of the second Replicate

Related

How to use project methods with Roslyn?

I am using Roslyn to run C# code from text.
It works, but I can't figure out how can I use my project class methods, didn't find a way to reference my project methods. I can't use a dll as there too many classes and forms I need to use.
For exmaple, using the Number set method, or the DoMultiAction() method inside the roslyn ExecuteUserCodeTest() text code. How to do that they will be referenced?
Is it even possible? Any explanation or example will be appreciated.
My Code:
namespace bot1
{
class Testing
{
private int number;
public int Number
{
get { return number; }
set { this.number = value; }
}
public int DoMultiAction(int num1, int num2)
{
return num1 * num2 * Number;
}
public void ExecuteUserCodeTest()
{
String text = #"
using System;
using System.Drawing;
using System.Windows.Forms;
namespace RoslynCompileSample
{
public class Writer
{
public void Write(String text)
{
MessageBox.Show(text);
}
}
}";
// define source code, then parse it (to the type used for compilation)
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(text);
// define other necessary objects for compilation
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Drawing.Point).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Windows.Forms.MessageBox).Assembly.Location),
MetadataReference.CreateFromFile(typeof (ScriptManagerHandler.ScriptHandler).Assembly.Location)
};
// analyse and generate IL code from syntax tree
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
// write IL code into memory
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
// handle exceptions
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
String error = "";
foreach (Diagnostic diagnostic in failures)
{
error += "" + diagnostic.Id + ", " + diagnostic.GetMessage() + "\n";
}
if (error != "")
MessageBox.Show(error);
}
else
{
// load this 'virtual' DLL so that we can use
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
// create instance of the desired class and call the desired function
Type type = assembly.GetType("RoslynCompileSample.Writer");
object obj = Activator.CreateInstance(type);
type.InvokeMember("Write",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
obj,
new object[] { "Hello World" });
}
}
}
}
}

Calling functions from string with parameters [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

"Couldn't find an IPlatformOperations. This should never happen, your dependency resolver is broken" on WPF

I'm writing a program that is container for other, smaller programs. It loads it's modules via Assembly.Load, finds types implementing IModule and makes instances of them.
In WPF MainWindow I have a RoutedViewHost, which will display everything.
In my AppBoostrapper, I have the following:
private ReactiveList<IModule> LoadModules(IMutableDependencyResolver resolver)
{
var modules = ModuleLoader.Load(ModulesDirectory);
// var modules = new IModule[] { new SampleModuleClass(), }; // this works perftectly!
foreach (var module in modules)
{
try
{
module.RegisterDependencies(this, resolver);
}
catch (Exception e)
{
Log.Error(e, "Could not register dependecies for module " + module.Name);
}
}
Log.Debug("Modules loaded: " + string.Join(", ", modules.Select(x => x.Name)));
return new ReactiveList<IModule>(modules);
}
Then, in my sample module:
public void RegisterDependencies(IMainScreen screen, IMutableDependencyResolver resolver)
{
_screen = screen;
_resolver = resolver;
resolver.Register(() => new SampleView(), typeof(IViewFor<SampleViewModel>));
resolver.Register(() => new GetNameDialogView(), typeof(IViewFor<GetNameDialogViewModel>));
Log.Debug("Dependecies registered");
}
Also, each module has its MainViewModel, which is displayed by RoutedViewHost when module is chosen.
Unfortunately, this does not work. I get the following error:
ReactiveUI.RoutedViewHost ERROR Couldn't find an IPlatformOperations.
This should never happen, your dependency resolver is broken
The ModuleLoader.Load goes like this:
public static IModule[] Load(string path)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
Log.Error("No modules directory found - creating");
return new IModule[0];
}
var moduleTypes = GetTypes(path);
return moduleTypes.Select(MakeInstance).Where(x => x != null).ToArray();
}
private static IModule MakeInstance(Type type)
{
try
{
var module = type.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as IModule;
if (module != null)
{
Log.Info("{0} module succesfully instatiated", module.Name);
return module;
}
Log.Error("Could not instantiate {0}", type.FullName);
return null;
}
catch (Exception exception)
{
Log.Error(exception, "Exception during instatiating {0}", type.FullName);
return null;
}
}
private static List<Type> GetTypes(string path)
{
var di = new DirectoryInfo(path);
var moduleTypes = new List<Type>();
foreach (var dir in di.GetDirectories())
{
FileInfo[] files = dir.GetFiles("*.dll");
foreach (var file in files)
{
Assembly newAssembly = Assembly.LoadFile(file.FullName);
Type[] types = newAssembly.GetExportedTypes();
foreach (var type in types)
{
if (type.IsClass && !type.IsAbstract && (type.GetInterface(typeof(IModule).FullName) != null))
{
moduleTypes.Add(type);
Log.Debug("Loaded {0} type", type.Name);
}
}
}
}
return moduleTypes;
}
There is no error if I just create the instance instead of loading the assembly. If this is important, SampleModule also uses ReactiveUI.
I've tried adding Locator.CurrentMutable.InitializeReactiveUI();
in various places (MainWindow constructor, App constructor, module static constructor), but nothing helped. Any ideas?
EDIT: if this is important, MainWindow is a MetroWindow from mahapps.metro
EDIT2:
I tried to register PlatformOperations, as #Wouter suggested with:
` var iPlatformOperations = Type.GetType("ReactiveUI.IPlatformOperations, ReactiveUI, Version=7.4.0.0, Culture=neutral, PublicKeyToken=null");
resolver.Register(() => new PlatformOperations(), iPlatformOperations);`
both before module loading and after, but nothing changes.
Well, it turns out that simply calling Assembly.Load("ReactiveUI"); breaks the whole thing, so I just added filtering do loaded DLLs to load only the ones with "Module" in name.
Then I encountered new problem: View location does not work with dynamically loaded assemblies, so I created my own ViewLocator with the following ResolveView implementation:
public IViewFor ResolveView<T>(T viewModel, string contract = null)
where T : class
{
var typeToFind = ViewModelToViewFunc(viewModel.GetType().AssemblyQualifiedName);
var ret = attemptToResolveView(Reflection.ReallyFindType(typeToFind, false), contract);
if (ret != null) return ret;
// IViewFor<FooBarViewModel> (the original behavior in RxUI 3.1)
var viewType = typeof(IViewFor<>);
ret = attemptToResolveView(viewType.MakeGenericType(viewModel.GetType()), contract);
if (ret != null) return ret;
// check ViewModel's assembly
var typeAssembly = viewModel.GetType().Assembly;
var types = typeAssembly.GetExportedTypes()
.SelectMany(x => x.GetInterfaces()
.Where(i => i.Name.Contains("IViewFor")))
.ToArray(); // all IViewFor from assembly
types = types.Where(x => x.FullName.Contains(viewModel.GetType().Name.Replace("ViewModel", "View"))).ToArray(); // only IViewFor<ViewModel>
foreach (var type in types) // in case there is more than one registration - contracts
{
ret = attemptToResolveView(type, contract);
if (ret != null) return ret;
}
return null;
}

Npgsql locks thread indefinitely in async environment

I'm struggling to find an answer to a problem I've been experiencing with C#'s popular Postgres library, Npgsql. I'm not sure if it's wholly a problem with Npgsql or not, though I suspect it is because my code is very straight forward. The issue I'm seeing is: when I call an async method on Npgsql sometimes, not all the time, the thread becomes locked. This issue occurs randomly from what I can tell. The result, being that I'm running in a Microsoft Orleans environment (which may be relevant to finding the solution), is that the thread locks indefinitely, thus making one of Orleans' worker threads unable to process work. As I make more Npgsql calls, these locked threads stack up and eventually the Orleans system is choked by thread exhaustion.
So I'm really at a loss for what the problem could be, but because the locking always happens in the same method, and because it appears to be happening in some subroutine of Npgsql, I think it's fair to investigate Npgsql further.
Here's my code which is used in an Orleans storage provider (special class which handles the system's persistence layer.)
var sql = $"SELECT * FROM objects WHERE id = #id";
using (var connection = new NpgsqlConnection(connectionString))
using (var cmd = new NpgsqlCommand(sql, connection))
{
try
{
await connection.OpenAsync();
cmd.Parameters.AddWithValue("id", id);
using(var reader = await connection.ExecuteReaderAsync(cmd))
{
if (reader.HasRows)
{
var objects = await ProtobufSQL.DataReaderToType(modelType, reader);
var data = objects[0];
state.Data = data;
}
}
catch (Exception e)
{
Log.Error(1, e.Message, e);
}
}
This is the source for the ProtobufSQL class:
public class ProtobufSQL
{
public static List<Tuple<string, object>> FlattenToSQLColumns(IMessage message, MessageDescriptor descriptor, string prefix = null)
{
var fields = descriptor.Fields.InDeclarationOrder();
var columns = new List<Tuple<string, object>>();
for (var i = 0; i < fields.Count; i++)
{
var field = fields[i];
var columnName = field.Name.ToLower();
if (field.Name == "id")
{
ByteString bytes = (ByteString)field.Accessor.GetValue(message);
var uuid = new Guid(bytes.ToByteArray());
columns.Add(new Tuple<string, object>("id", uuid));
}
else if (field.FieldType == FieldType.Message)
{
var embeddedDescriptor = field.MessageType;
var embeddedMessage = field.Accessor.GetValue(message);
if (field.IsRepeated)
{
throw new Exception("Repeated complex types are not supported, create a foreign key reference in a new object instead.");
}
else
{
columns.AddRange(FlattenToSQLColumns((IMessage)embeddedMessage, embeddedDescriptor, $"{columnName}."));
}
}
else if (field.FieldType == FieldType.Group)
{
throw new Exception("Groups are not supported by ProtobufSQL.");
}
else
{
var columnValue = field.Accessor.GetValue(message);
var key = prefix + columnName;
if (field.IsRepeated)
{
var enumerableColumnValue = columnValue as IEnumerable;
Type listTypeOf = enumerableColumnValue.GetType().GetGenericArguments()[0];
Type listType = typeof(List<>).MakeGenericType(listTypeOf);
dynamic valueList = Activator.CreateInstance(listType);
foreach (var item in enumerableColumnValue)
{
valueList.Add((dynamic)item);
}
columns.Add(new Tuple<string, object>(key, valueList.ToArray()));
}
else
{
columns.Add(new Tuple<string, object>(key, columnValue));
}
}
}
return columns;
}
public static async Task<IMessage[]> DataReaderToType(Type type, DbDataReader reader)
{
var descriptor = (MessageDescriptor)type.GetProperty("Descriptor").GetValue(null);
IList<IMessage> objects = new List<IMessage>();
while (await reader.ReadAsync())
{
var obj = Activator.CreateInstance(type);
TraverseDbRow(reader, descriptor, obj);
objects.Add((IMessage)obj);
}
return objects.ToArray();
}
private static void TraverseDbRow(DbDataReader reader, MessageDescriptor descriptor, object obj, string prefix = null)
{
var fields = descriptor.Fields.InFieldNumberOrder();
for (var i = 0; i < fields.Count; i++)
{
var field = fields[i];
if (field.FieldType == FieldType.Message)
{
if (field.IsRepeated)
{
// Repeated embedded types will be broken out into a separate table,
// so there's no need to handle them here.
}
else if (field.IsMap)
{
throw new Exception("Maps are not yet supported by ProtobufSQL.");
}
else
{
var embeddedDescriptor = field.MessageType;
var embeddedObj = Activator.CreateInstance(embeddedDescriptor.ClrType);
TraverseDbRow(reader, embeddedDescriptor, embeddedObj, $"{prefix}{field.Name}.");
}
}
else if (field.FieldType == FieldType.Group)
{
throw new Exception("Groups are not supported by ProtobufSQL.");
}
else
{
var columnName = prefix + field.Name;
try
{
var columnValue = reader[columnName];
var propertyInfo = obj.GetType().GetProperty(field.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (field.Name == "id")
{
var guid = (Guid)columnValue;
ByteString bytes = ByteString.CopyFrom(guid.ToByteArray());
propertyInfo.SetValue(obj, bytes);
}
else if (field.IsRepeated)
{
var repeated = propertyInfo.GetValue(obj);
var addRange = repeated.GetType().GetMethod("AddRange");
addRange.Invoke(repeated, new object[] { columnValue });
}
else if (field.IsMap)
{
throw new Exception("Maps are not yet supported by ProtobufSQL.");
}
else
{
propertyInfo.SetValue(obj, Convert.ChangeType(columnValue, propertyInfo.PropertyType));
}
}
catch (IndexOutOfRangeException e)
{
// columnName was not present in the response
}
}
}
}
}
I also have this screenshot of the thread's stack when it got locked:
I'm really unsure what to make of all this. Hopefully someone is out there with a bit of knowledge that can help me proceed! Thanks.

Reflection: How to Invoke Method with parameters

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

Categories