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/
Related
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 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
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 ^^
Using MSDN I got the class to write a wrapper for my command line tool.
I now am facing a problem, if I execute the exe through the command line with arguments, it works perfect without any errors.
But when I try to pass the arguments from the Wrapper it crashes the program.
Wanted to know if I am passing the arguments properly and if I am wrong, could somebody point out please.
This is the LaunchEXE class from MSDN
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
namespace SPDB
{
/// <summary>
/// Class to run any external command line tool with arguments
/// </summary>
public class LaunchEXE
{
internal static string Run(string exeName, string argsLine, int timeoutSeconds)
{
StreamReader outputStream = StreamReader.Null;
string output = "";
bool success = false;
try
{
Process newProcess = new Process();
newProcess.StartInfo.FileName = exeName;
newProcess.StartInfo.Arguments = argsLine;
newProcess.StartInfo.UseShellExecute = false;
newProcess.StartInfo.CreateNoWindow = true; //The command line is supressed to keep the process in the background
newProcess.StartInfo.RedirectStandardOutput = true;
newProcess.Start();
if (0 == timeoutSeconds)
{
outputStream = newProcess.StandardOutput;
output = outputStream.ReadToEnd();
newProcess.WaitForExit();
}
else
{
success = newProcess.WaitForExit(timeoutSeconds * 1000);
if (success)
{
outputStream = newProcess.StandardOutput;
output = outputStream.ReadToEnd();
}
else
{
output = "Timed out at " + timeoutSeconds + " seconds waiting for " + exeName + " to exit.";
}
}
}
catch (Exception e)
{
throw (new Exception("An error occurred running " + exeName + ".", e));
}
finally
{
outputStream.Close();
}
return "\t" + output;
}
}
}
This is the way I am passing arguments from my main program (Form1.cs)
private void button1_Click(object sender, EventArgs e)
{
string output;
output = LaunchEXE.Run(#"C:\Program Files (x86)\MyFolder\MyConsole.exe", "/BACKUP C:\\MyBackupProfile.txt", 100);
System.Windows.Forms.MessageBox.Show(output);
}
The command line tool accepts the following command and works perfectly:
C:\Program Files (x86)\MyFolder>MyConsole.exe /BACKUP C:\MyBackupProfile.txt
I have two options for you -
1) Please try running your Visual Studio on "administrator mode".
or 2) Try to implement this instead. https://github.com/commandlineparser/commandline.
"The Command Line Parser Library offers to CLR applications a clean and concise API for manipulating command line arguments and related tasks. It allows you to display an help screen with an high degree of customization and a simple way to report syntax errors to the user. Everything that is boring and repetitive to be programmed stands up on library shoulders, letting you concentrate yourself on core logic. This library provides hassle free command line parsing with a constantly updated API since 2005."
Worked great for me.
I have a feeling it doesn't like the spaces in your path. See this post: C# How to use Directory White Spaces into process.arguements?
Current best practice is to use Environment.NewLine in your code to, well, start a new line. I would like to be able to use an alias or overloaded operator in my code so that it is more concise.
Instead of this:
MessageBox.Show("My first line here" + Environment.NewLine + "My second line here");
I would like to have something like this:
MessageBox.Show("My first line here" + NL + "My second line here");
How can I easily set this up one time as an IDE setting, or for a whole project/namespace?
An alias or overloaded operator is that comes to mind, but not sure if there is a good way of doing a global alias that is more concise than Environment.NewLine, and I've never done an overloaded operator before, so not familiar with the ins and outs of that.
Simple shortening method. Pop this class in one of your utility assemblies:
namespace MyCompany
{
public static class E
{
public static readonly string NL = System.Environment.NewLine;
}
}
then you can happily use it as such:
using MyCompany;
MessageBox.Show("My first line here" + E.NL + "My second line here");
Might I suggest that you use an extension method instead?
public static class StringExtensions
{
public static string NextLine(this string s, string next)
{
return s + Environment.NewLine + next;
}
public static string NextLine(this string s)
{
// just add a new line with no text
return s + Environment.NewLine;
}
}
Usage:
var lines = "My first line here".NextLine("My second line here.")
.NextLine("third line").NextLine();
Of course, you can call it NL if you wish -- might not be clear, though.
use StringBuilder.AppendLine() in cases with few Environment.NewLine:
var sb = new StringBuilder();
sb.AppendLine("My first line here");
sb.AppendLine("My second line here");
MessageBox.Show(sb.ToString());
Write a class to provide the value of Environment.NewLine as a member, as Jesse C. Slicer has already suggested:
namespace MyNamespace
{
public static class Env
{
public static readonly string NL = Environment.NewLine;
}
}
Then write the following using directive:
using E = MyNamespace.Env;
You can add this using directive to your default new class template and any other templates you use (new struct, new interface, etc.).
Here's where the new class template is on my machine, as an example to get you started:
C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplates\CSharp\Code\1033
Once this is done, you should be able to write E.NL in place of Environment.NewLine everywhere you want.
using static System.Environment;
Then you can just use it as NewLine
Alias won't work - you can alias a namespace or a type, but not a property of a type. So this works:
using NL = System.Environment;
class Program
{
static void Main(string[] args)
{
var s = NL.NewLine;
}
}
But this doesn't:
// returns: The type name 'NewLine' does not
// exist in the type 'System.Environment' error
using NL = System.Environment.NewLine;
Overloaded operator is an interesting idea, but then you'll have to use something other than a String. Usually people create a struct which can take a base string value and then overload the operators. Not worth the pain if all you want to do is replace the Environment.NewLine. You're better off to use a static extension as suggested by others.
Another alternative (if you're dead set on using NL) is to descend all the classes in your framework off of a common parent class which can have the following property:
public class BaseParentClass
{
public string NL
{
get { return System.Environment.NewLine; }
}
}
Then in the code for all the descendant classes, your code will look simply like:
public class ChildOfBaseParent
{
public void Show_A_Message()
{
MessageBox.Show("My first line here" + NL + "My second line here");
}
}
Of course if your classes do not descend off of a common parent, you will have to refactor the code base for this piece of convenience. You will need to create a parallel System.Windows.Forms.Form parent for winform classes to have this same behavior.
But definitely worth the pain if you have a lot of string concatenations involving NL...
Adding to #abatishchev response you can do nice things with the StringBuilder Class.
StringBuilder builder = new StringBuilder();
builder.Append("List:");
builder.AppendLine();
builder.Append("1. Boat")
builder.Append("2. Car").AppendLine();
builder.Replace("Boat", "Jet");