I was wondering if there is any way to pass a variable value in a code that will be compiled One Time using CSharpCodeProvider .
for example :
string code = #"
using System;
namespace First
{
public class Program
{
public int Value; // pass this value
public static void Main()
{
" +
"Console.WriteLine(\"Hello + Value\");"
+ #"
}
}
}
";
Compile Method :
public void Compile(String Code)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("System.Drawing.dll");
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = false;
CompilerResults results = provider.CompileAssemblyFromSource(parameters, Code);
}
So i want to be able to pass a value of the Value example 2
and what i meant by ONE TIME is like compile time so the compiled code will always in its run-time will display the value : 2 whenever i executed the application .
I hope its clear !
Solved Using Mono.Cecil reference details Documentatin
Related
I use the CSharpCodeProvider to execute C# code from a string. But of course it creates a class for the code to run. Is there a way to call a method from the current app it is called from? For eg to call CallTest() from a parsed script in this (pseudo) example code
public class Test {
// lot of code
private void CallTest() {
Console.WriteLine("CallTest()");
}
public object OnMethodInvoke(string functionName, List<object> parameters) {
// lot of if statements
if(functionName == "exec") {
CSharpCodeProvider c = new CSharpCodeProvider();
ICodeCompiler icc = c.CreateCompiler();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
cp.CompilerOptions = "/t:library";
cp.GenerateInMemory = true;
System.Text.StringBuilder sb = new System.Text.StringBuilder("");
sb.Append("using System;\n");
sb.Append("namespace ScriptStack.Dynamic {\n");
sb.Append("public class Code {\n");
sb.Append("public object EvalCode(){\n");
sb.Append((string)parameters[0]);
sb.Append("}\n");
sb.Append("}\n");
sb.Append("}\n");
CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
if (cr.Errors.Count > 0) return null;
System.Reflection.Assembly a = cr.CompiledAssembly;
object o = a.CreateInstance("ScriptStack.Dynamic.Code");
MethodInfo mi = o.GetType().GetMethod("EvalCode");
return mi.Invoke(o, null);
}
}
}
If I got your problem statement correctly, you want to be able to compile a statement like
t.CallTest(); //where t is an instance of your class
and run it.
As I hinted in the comments, Reflection might be way easier to do. But for a second, let's imagine you need to keep pressing on and generate some fancy code that you will then need to run (which is where LINQ Expression trees might be a better answer, but never mind - suppose we're still sticking to CodeDom).
There are a couple of issues I see to overcome here:
The unit of compilation in CodeDom is assembly, I don't believe you can have assembly nested in a class. Therefore you need to define at least a namespace and a class (which you seem to have done already)
I also don't believe CodeDom allows you to compile partial classes, therefore your generated code with direct references to Test will not compile even if you manage to overcome point #1
With the above in mind, I believe one option will be to basically have your generated code invoke reflection to obtain a reference to your desired method and invoke it dynamically (which kinda gets reduced to my suggestion #1):
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;
namespace ConsoleApp9
{
public class MainTestClass
{
private void CallTest(string value) => Console.WriteLine($"CallTest({value})");
public object OnMethodInvoke(string functionName, List<object> parameters)
{
if (functionName == "exec")
{
CSharpCodeProvider c = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("ConsoleApp9.exe"); // note a reference to your main assembly here
cp.CompilerOptions = "/t:library";
cp.GenerateInMemory = true;
StringBuilder sb = new StringBuilder("");
sb.Append("using System;\n");
sb.Append("using System.Reflection;\n");
sb.Append("using ConsoleApp9;\n"); // reference your main assembly ConsoleApp9. this is my test console app
sb.Append("namespace ScriptStack.Dynamic\n");
sb.Append("{\n");
sb.Append(" public class Code\n");
sb.Append(" {\n");
sb.Append(" private MainTestClass t;\n"); // reference your
sb.Append(" public Code(MainTestClass t) { this.t = t; }\n"); // we're going to capture reference to calling MainTestClass instance and use it for reflection
sb.Append(" public object EvalCode()\n");
sb.Append(" {\n");
sb.Append(" // this is a very standard method of invoking private methods. see this SO answer: https://stackoverflow.com/a/135482/12339804 \n");
sb.Append(" var mi = typeof(MainTestClass).GetMethod(\"CallTest\", BindingFlags.NonPublic | BindingFlags.Instance);\n");
sb.Append(" return mi.Invoke(t, new object[]{ \"" + (string)parameters[0] + "\" });\n"); //suppose you want to pass a string to your generated C# source.
sb.Append(" }\n");
sb.Append(" }\n");
sb.Append("}\n");
var cr = c.CompileAssemblyFromSource(cp, sb.ToString());
if (cr.Errors.Count > 0) return null;
var a = cr.CompiledAssembly;
object o = a.CreateInstance("ScriptStack.Dynamic.Code", false, BindingFlags.Default, null, new object[] { this }, CultureInfo.InvariantCulture, null); //we have to opt for a way more overloaded CreateInstance method due to us requiring to pass a this into the Code class we're instantiating
var mi = o.GetType().GetMethod("EvalCode");
return mi.Invoke(o, null);
}
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
var t = new MainTestClass();
t.OnMethodInvoke("exec", new List<object> { "parameter 1" });
Console.ReadKey();
}
}
}
Recently I try to compile and run C# code stored somewhere else. My goal is to import a .txt file, compile it and run it. I followed this article on Simeon's blog about compiling and running C# code within the program, and everything work well.
Then I try making something a bit more complex by importing the C# code from my computer, so I created a .txt file with the following lines that is store for instance at "C:\program.txt" :
(the text file)
using System;
namespace Test
{
public class DynaCore
{
static public int Main(string str)
{
Console.WriteLine("Cool it work !");
return str.Length;
}
}
}
I do some coding based on the same article and that is my code :
(the C# program)
using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.CSharp;
using System.Reflection;
namespace DynaCode
{
class Program
{
static void Main(string[] args)
{
string[] lines = System.IO.File.ReadAllLines(#"C:\program.txt");
string bigLine = string.Empty;
foreach(string s in lines)
{
bigLine += s;
}
string[] finalLine = new string[1] { bigLine };
CompileAndRun(finalLine);
Console.ReadKey();
}
static void CompileAndRun(string[] code)
{
CompilerParameters CompilerParams = new CompilerParameters();
string outputDirectory = Directory.GetCurrentDirectory();
CompilerParams.GenerateInMemory = true;
CompilerParams.TreatWarningsAsErrors = false;
CompilerParams.GenerateExecutable = false;
CompilerParams.CompilerOptions = "/optimize";
string[] references = { "System.dll" };
CompilerParams.ReferencedAssemblies.AddRange(references);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults compile = provider.CompileAssemblyFromSource(CompilerParams, code);
if (compile.Errors.HasErrors)
{
string text = "Compile error: ";
foreach (CompilerError ce in compile.Errors)
{
text += "rn" + ce.ToString();
}
throw new Exception(text);
}
Module module = compile.CompiledAssembly.GetModules()[0];
Type mt = null;
MethodInfo methInfo = null;
if (module != null)
{
mt = module.GetType("Test.DynaCore");
}
if (mt != null)
{
methInfo = mt.GetMethod("Main");
}
if (methInfo != null)
{
Console.WriteLine(methInfo.Invoke(null, new object[] { "here in dyna code. Yes it work !!" }));
}
}
}
}
This work well, and I got the following output as expected :
Cool it work !
33
Note that I put all the code of the .txt file in one big line that I do myseft, because as Simeon said :
CompileAssemblyFromSource consumes is a single string for each block (file) worth of C# code, not for each line.
Even now this sentence still a bit obscure for me.
( I tried CompileAndRun(new string[1] { lines.ToString() }); before but there was an error when compiling the .txt file, that's why I do the big line myself. )
And here is my problem : I ask myself : "What if I add a comment in my .txt file ?", so I edit it and that how it look : (the text file)
using System;
namespace Test
{
//This is a super simple test
public class DynaCore
{
static public int Main(string str)
{
Console.WriteLine("Cool it work !");
return str.Length;
}
}
}
And of course I got an error (CS1513) because I convert the .txt file in one big string, so everything after the // is ignored. So how can I use comment using // inside my .txt file and got the program work ?
I also try CompileAndRun(lines);, but after launching the program it crash when compiling the .txt file because of the exception.
I do some search about it and I didn't find anythings about comment. I guess there is somethings wrong about passing only one big line in the CompileAndRun method, but passing several lines don't work as I say upper.
(Another note : Comment using /* insert comment */ works.)
Each element given to CompileAssemblyFromSource is supposed to be a file, not a single line of code. So read the whole file into a single string and give it to the method and it'll work just fine.
static void Main(string[] args)
{
var code = System.IO.File.ReadAllText(#"C:\program.txt");
CompileAndRun(code);
Console.ReadKey();
}
static void CompileAndRun(string code)
{
CompilerParameters CompilerParams = new CompilerParameters();
string outputDirectory = Directory.GetCurrentDirectory();
CompilerParams.GenerateInMemory = true;
CompilerParams.TreatWarningsAsErrors = false;
CompilerParams.GenerateExecutable = false;
CompilerParams.CompilerOptions = "/optimize";
string[] references = { "System.dll" };
CompilerParams.ReferencedAssemblies.AddRange(references);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults compile = provider.CompileAssemblyFromSource(CompilerParams, code);
// ...
}
I am trying to make a KI or something which is more like a Bot which can learn a little bit. For example I want to give him new commands. Therefor the Bot has to create new Methods due run-time so it can react on my inputs with the right Method. I wanted to know if and how it is possible to add a method into my existing class due run-time.
I have found some links already and examples like the CodeDomProvider, CSharpCodeProvider and the DynamicMethod but it seems like they can only create new runables (exe files) or create a preset which one can execute with new parameters.
What I need is a way to create a new Method in my existing class or a way to interact with my existing class. I was already thinking about Plugins but in my opinion it would be much work to create a plugin for each method and also not efficient am I right?
You may also know a better way then creating Methods for each command?
Edit 1:
With Assembly.CreateInstane("path"); I could "clone" my running program and together with a CSharpCodeProvider I could create a new exe with these Methods. But there is a problem. When I use a Method where is no Reference to in the Class such as using System.Windows.Forms gives me the error:
Line number 3, error number: CS0234, 'The type or namespace name' Windows' does not exist in the namespace 'System'. (Is an assembly reference missing?);
That would have been my Testcode right now:
//The String I am going to Add through my textfield
using System;
using System.Reflection;
using System.Windows.Forms;
public class Example
{
public static void Main()
{
Assembly assem = typeof(View).Assembly;
View v = (View ) assem.CreateInstance("Usopis");
if (! (v == null)) {
v.Height = 300;
MessageBox.Show("Instantiated a {0} object whose value is '{1}'",
v.GetType().Name, v);
}
else {
MessageBox.Show("Unable to instantiate a View object.");
}
}
}
//Code which should compile my String to a exe
private void Button_Click(object sender, EventArgs e) {
textBox2.Text = "";
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, textBox1.Text);
if(results.Errors.Count > 0) {
textBox2.ForeColor = Color.Red;
foreach(CompilerError CompErr in results.Errors) {
textBox2.Text = textBox2.Text +
"Line number " + CompErr.Line +
", Error Number: " + CompErr.ErrorNumber +
", '" + CompErr.ErrorText + ";" +
Environment.NewLine + Environment.NewLine;
}
} else {
//Successful Compile
textBox2.ForeColor = Color.Blue;
textBox2.Text = "Success!";
}
}
To fix the missing namespace error you have to add the missing reference:
parameters.ReferencedAssemblies.Add("System.Web.dll");
But that won`t solve all of your problems.
Maybe look into Lamdas...
That could be something for you:
https://www.strathweb.com/2018/01/easy-way-to-create-a-c-lambda-expression-from-a-string-with-roslyn/
I can't get it to work to cast an object to a class...
I need that because i dynamically load a Dll and give it the class as parameter of a method. The class is linked in both projects (same file).
My Class I want to give to the Dll:
public class CParams
{
public int m_iFunctionCode = -1;
public STTestDll pTestDll;
public struct STTestDll
{
public int m_iSleepTime;
public int m_iCount;
}
public string GetDescriptionText()
{
return "Starte Dll: Sleeptime=" + pTestDll.m_iSleepTime.ToString() + "; Count=" + pTestDll.m_iCount.ToString() + "; Solldauer=" +
(pTestDll.m_iSleepTime * pTestDll.m_iCount / 1000).ToString() + " Sekunden";
}
public CParams(int iFunctionCode_)
{
m_iFunctionCode = iFunctionCode_;
}
}
The call of the DLL:
Type typeDll = asmDLL.GetType(strClassName + "." + strClassName);
object activator = Activator.CreateInstance(typeDll);
MethodInfo miRun = typeDll.GetMethod("Run");
if (miRun != null)
{
CParams pParam = new CParams(0);
pParam.pTestDll.m_iCount = 200;
pParam.pTestDll.m_iSleepTime = 25;
object[] args = new object[1];
args[0] = pParam;
miRun.Invoke(activator, args);
}
The Code which tries to cast the class:
public void Run(object objParams)
{
CParams pParams = (CParams)objParams;
MessageBox.Show(pParams.pTestDll.m_iCount.ToString() + " - " + pParams.pTestDll.m_iSleepTime.ToString());
}
The Error is: InvalidCastException.
In a similar scenario I had the same problem. In my case I had two assemblys:
The executable
Dll
The implementation of the refference I wanted to pass over was in the DLL and my Executable had the DLL added as reference. Bot assemblys knew about the Implementation and the code would compile. But when executed I got that invalidCastException. The solution to my problem was an additional DLL, in which to put the implementation of my reference class (in your case CParams) and then link refer to that DLL in both other projects.
Just because both assemblies use the same file content, doesn't mean it is in the Type.
The runtime sees two CParams classes are two totally different types. You cannot cast between them.
You need to put it in an assembly that is referenced by both.
I'm trying to build a GUI app that has an interactive console, much like the one found in SublimeText.
I hope it is a valid question because it seems to be "a practical, answerable problem that is unique to software development".
In short, I see huge benefits having an interactive console inside a GUI app for
debugging, probing internal variables at runtime
logging
quick configuration changes
However, I have not come across any existing open-source applications that uses such a design.
I'm hoping someone has done it before and can share his/her design approach.
While I do have a semi-working solution using reflection and invoke in .NET, it is limited to only function calls and I'm not able to probe into nested internal variables (e.g. object.property.property).
To make the question more specific, these are the problems I'm facing:
Not easily extensible (Need to wire every new GUI command to a console command, vice-versa), any design tips? Routed commands (I could not find a useful example either)?
How to execute dynamic code that can access all existing object instances in the entire .NET app?
Thank you.
So here comes the code which worked for me:
namespace ReflectionsTest
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
//Events excluded
private void ExecuteCommand(string command)
{
string cmd = "";
cmd += #"using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Linq;
using Microsoft.CSharp;
using System.Reflection;
using ReflectionsTest;";
// i included a using statement for every namespace i want to adress direct
cmd += #"namespace ReflectionConsole
{
public class RuntimeExecution
{
public static void Main(MainForm parent, TextBox output, FieldInfo[] privateFields)
{
try {";
//the code in a trycatch because i can send every error to a specific output defined as output parameter
cmd += command;
cmd += "}catch (Exception ex) { if(output != null){" +
"output.Text += ex.Message + \"\\n\\r\";"
+"}else{MessageBox.Show(ex.Message);}}}}}";
try {
ExecuteCSharp(cmd);
}
catch (Exception ex) {
textBox2.Text += ex.Message + "\n\r";
}
}
private void ExecuteCSharp(string code)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
List<AssemblyName> assemblys = (Assembly.GetExecutingAssembly().GetReferencedAssemblies()).ToList<AssemblyName>();
foreach (var item in assemblys) {
parameters.ReferencedAssemblies.Add(item.Name + ".dll");
}
string t = Assembly.GetExecutingAssembly().GetName().Name;
parameters.ReferencedAssemblies.Add(t + ".exe");
//Here you have to reference every assembly the console wants access
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = false;
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.HasErrors) {
StringBuilder sb = new StringBuilder();
foreach (CompilerError error in results.Errors) {
sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}
throw new InvalidOperationException(sb.ToString());
}
else {
Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("ReflectionConsole.RuntimeExecution");
MethodInfo main = program.GetMethod("Main");
FieldInfo[] fields = this.GetType().GetFields(
BindingFlags.NonPublic |
BindingFlags.Instance);
//if everything is correct start the method with some arguments:
// containing class, output, private fields of the containing class for easier access
main.Invoke(null, new object[]{this, textBox2, fields});
}
}
}
}
Some Explanations:
You have pass the highest class of your program which contains everything else, because it is easier to access members than parent objects.
public objects you can access like parent.obect1.Text = "textXYZ";
private objects you can access by name. These objects are listed in privateFields.
for the subclasses you have two options:
change the first and third parameter when calling main.Invoke([...])
or
recollect the private fields.
as Suggestion you could include a .dll in the command which already gives you methods to achieve this much faster.
For example GetValueFromFieldByName(object class, string name, Type resultType)
I hope that is what you've hoped for ^^