My code:
//App, Core.cs
using System;
using System.IO;
using System.Reflection;
namespace Game
{
public static void Main(string[] args)
{
Assembly a = Assembly.LoadFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mods\\ExampleMod.dll"));
var x1 = a.GetType("PTF_Mod.Mod_Main");
var x2 = x1.GetMethod("OnStart");
var x3 = x2.Invoke(null, new object[] { });
while(true);
}
}
//App, ModCrew.cs
using System;
using System.Reflection;
namespace Engine
{
public static class ModCrew
{
public class Mod
{
public void ItWorks()
{
Console.WriteLine("It works!");
}
}
}
}
//DLL, Mod_Main.cs
using System;
using System.Reflection;
namespace PTF_Mod
{
public static class Mod_Main
{
public static void OnStart()
{
var exe = Assembly.GetCallingAssembly();
Console.WriteLine(exe.Location); //Location is valid
var x = exe.GetType("Engine.ModCrew.Mod", true); //But here I get exception
var y = Activator.CreateInstance(x);
x.GetMethod("ItWorks", BindingFlags.Instance).Invoke(null, object[] { });
}
}
}
Exception:
An exception of type 'System.TypeLoadException' occurred in mscorlib.dll but was not handled in user code
Additional information: Nie można załadować typu 'Engine.ModCrew.Mod' z zestawu 'Game, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
You should always use BindingFlags when getting methods via reflection.
Invoking an instance with MethodInfo.Invoke requires the instance as the first parameter MethodInfo.Invoke(MyInstance,...)
Changes based on comments:
public static void Main(string[] args)
{
Assembly a = Assembly.LoadFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mods\\ExampleMod.dll"));
var x1 = a.GetType("PTF_Mod.Mod_Main");
var x2 = x1.GetMethod("OnStart", BindingFlags.Static | BindingFlags.Public);
var x3 = x2.Invoke(null, null);
while(true);
}
Mod_Main:
public static void OnStart()
{
var exe = Assembly.GetCallingAssembly();
Console.WriteLine(exe.Location); //Location is valid
var x = exe.GetType("Engine.ModCrew+Mod", true); //But here I get exception
var y = Activator.CreateInstance(x);
x.GetMethod("ItWorks", BindingFlags.Instance | BindingFlags.Public).Invoke(y, null);
}
Also, consider if reflection is even necessary, it can make programs overly complicated. If it's necessary you should look into Dynamic to avoid a lot of the troubles of invoking methods with reflection
Related
When I'm getting IInstanceReferenceExpression operation for type Instance() => this; instance kind is Explicit, but I expect that kind would be This. Am I missing something or this is a bug in Roslyn?
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Semantics;
using System;
using System.Linq;
internal static class so39495857
{
private static void Main()
{
var tree = CSharpSyntaxTree.ParseText(#"
class c
{
c Instance() => this;
static void Main() {}
}
");
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create(null, new[] { tree }, new[] { mscorlib });
var model = compilation.GetSemanticModel(tree);
var node = tree.GetRoot().DescendantNodes().OfType<ArrowExpressionClauseSyntax>().First();
var operation = model.GetOperation(node);
var block = (IBlockStatement)operation;
var #return = (IReturnStatement)block.Statements.First();
var #this = (IInstanceReferenceExpression)#return.ReturnedValue;
Console.WriteLine(#this.InstanceReferenceKind);
}
}
Project on github - https://github.com/isanych/so-39495857
That's what Explicit means.
ThisClass is only for VB's MyClass keyword.
I am trying to understand Reflection in c# and I am trying to get the following code to work. The first method (GetUserName) works but the second method (AddGivenNumbers) is giving me an exception error of "parameter type mismatch".
I created a class library with the 2 methods and am trying to use reflection in main console application.
Class Library Code:
namespace ClassLibraryDELETE
{
public class Class1
{
public string GetUserName(string account)
{
return "My name is " + account;
}
public int AddGivenNumbers(int num1, int num2)
{
return num1 + num2;
}
}
}
Code in my main console application:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace ConsoleApplicationDELETE_REFLECTION
{
class Program
{
static void Main(string[] args)
{
Assembly assemblyInstance = Assembly.LoadFrom(#"C:\Users\xyz\Desktop\Debug\ClassLibraryDELETE.dll");
Type Class1Type = assemblyInstance.GetType("ClassLibraryDELETE.Class1");
object class1Instance = Activator.CreateInstance(Class1Type);
MethodInfo getMethodFullName = Class1Type.GetMethod("GetUserName");
string[] parameter = new string[1];
parameter[0] = "John Doe";
string userName = (string)getMethodFullName.Invoke(class1Instance, parameter);
Console.WriteLine("User Name = {0}", userName);
Assembly assemblyInstance2 = Assembly.LoadFrom(#"C:\Users\xyz\Desktop\Debug\ClassLibraryDELETE.dll");
Type ClassType2 = assemblyInstance.GetType("ClassLibraryDELETE.Class1");
object class1Instance2 = Activator.CreateInstance(ClassType2);
MethodInfo getMethodFullName2 = ClassType2.GetMethod("AddGivenNumbers");
//object[] parameters = new object[2];
//parameters[0] = 8;
//parameters[1] = 4;
object[] args2 = new object[] { 1, 2 };
object result = getMethodFullName.Invoke(class1Instance2, args2);
Console.WriteLine("Sum of the two numbers is {0}", result);
Console.ReadLine();
}
}
}
You have a typo
object result = getMethodFullName.Invoke(class1Instance2, args2);
You should have been targeting getMethodFullName2, otherwise you're trying to execute the first function (which takes 1 argument) with 2 arguments.
Working example: http://rextester.com/WBFYB30834
i have working CompileAssemblyFromSource code. But when i use any code protector like RedGate SmartAssembly or Themida it's stop working and i get error "Could not load file or assembly or one of its dependencies". Can you please help me with that?
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
public class Program
{
public static int q = 0;
static void Main(string[] args)
{
try
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{ }
}
var res = foo.CompileAssemblyFromSource(parameters, source);
var type = res.CompiledAssembly.GetType("stringToCode.FooClass");
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);;
}
Console.ReadLine();
}
}
}
sorry for my question. I just understand why it happens)
This is code is example. The main problem i have with code that i get from server.
So when i obfuscate my vars i don't obfuscare them at my "online" code that i use with CompileAssemblyFromSource. So this just can't work. Because vars don't have same names.
i try to use CompileAssemblyFromSource to change 1 value at my main class.
But when i compile i get error "Could not load file or assembly or one of its dependencies" and this only happens when i try change static value of other class. But if i return some output or wrote anything at Console from this FooClass than all work's fine. But how can i change value of other class?
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}
You can't find the type because you have compilation error in your code.You can't access the classes in your current code in this manner. You should at least reference the current assembly in your in-memory assembly.
UPDATE
You have two issues in your code. First, you have to make the class Program public. Then you should specify the full name of type in GetType method.
This code works fine:
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
public class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("stringToCode.FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
public int xx = 1; // variable I want to access from the runtime code
static void Main(string[] args)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add("System.dll");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode());
var cls = results.CompiledAssembly.GetType("ConsoleApplication1.Program");
var method = cls.GetMethod("DynamicMethod", BindingFlags.Static | BindingFlags.Public);
method.Invoke(null, null);
int a = int.Parse(Console.ReadLine()); // pause console
}
static string[] GetCode()
{
return new string[]
{
#"using System;
namespace ConsoleApplication1
{
class Program
{
public static void DynamicMethod()
{
Console.WriteLine(""Hello, world!"");
}
}
}"
};
}
}
}
I would like to know if It's possible to access variable int xx from the runetime code (eg. putting xx = 2; line after "hello world". That would be awesome
Thanks :)
Yes, you can make that available, but you need to:
add a reference to the assembly that contains xx (I'm calling this assembly StaticAssembly, for convenience - so it is probably StaticAssembly.exe, the console exe that is running)
make Program in StaticAssembly a public type so that it is accessible
either make xx into a static field, or instantiate an instance of Program and pass it to the dynamic code somehow
The following works, for example (I'm using the "instantiate an instance and pass it" approach, so I've added a parameter to DynamicMethod):
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace StaticAssembly
{
public class Program
{
public int xx = 1; // variable I want to access from the runtime code
static void Main(string[] args)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("StaticAssembly.exe");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode());
var cls = results.CompiledAssembly.GetType("GeneratedAssembly.Program");
var method = cls.GetMethod("DynamicMethod", BindingFlags.Static | BindingFlags.Public);
var p = new Program();
p.xx = 42;
method.Invoke(null, new object[] {p});
int a = int.Parse(Console.ReadLine()); // pause console
}
static string[] GetCode()
{
return new string[]
{
#"using System;
namespace GeneratedAssembly
{
class Program
{
public static void DynamicMethod(StaticAssembly.Program p)
{
Console.WriteLine(p.xx);
Console.WriteLine(""Hello, world!"");
}
}
}"
};
}
}
}